diff --git a/lib/_tls_wrap.js b/lib/_tls_wrap.js index 57eb3e72872232..4d98f3fa991e1e 100644 --- a/lib/_tls_wrap.js +++ b/lib/_tls_wrap.js @@ -35,6 +35,7 @@ const debug = util.debuglog('tls'); const tls_wrap = process.binding('tls_wrap'); const { TCP, constants: TCPConstants } = process.binding('tcp_wrap'); const { Pipe, constants: PipeConstants } = process.binding('pipe_wrap'); +const { owner_symbol } = require('internal/async_hooks').symbols; const { SecureContext: NativeSecureContext } = process.binding('crypto'); @@ -77,7 +78,7 @@ function onhandshakestart(now) { else this.handshakes++; - const { owner } = this; + const owner = this[owner_symbol]; if (this.handshakes > tls.CLIENT_RENEG_LIMIT) { owner._emitTLSError(new ERR_TLS_SESSION_ATTACK()); return; @@ -90,7 +91,7 @@ function onhandshakestart(now) { function onhandshakedone() { debug('onhandshakedone'); - const owner = this.owner; + const owner = this[owner_symbol]; // `newSession` callback wasn't called yet if (owner._newSessionPending) { @@ -103,7 +104,7 @@ function onhandshakedone() { function loadSession(hello) { - const owner = this.owner; + const owner = this[owner_symbol]; var once = false; function onSession(err, session) { @@ -131,7 +132,7 @@ function loadSession(hello) { function loadSNI(info) { - const owner = this.owner; + const owner = this[owner_symbol]; const servername = info.servername; if (!servername || !owner._SNICallback) return requestOCSP(owner, info); @@ -213,7 +214,7 @@ function requestOCSPDone(socket) { function onnewsession(key, session) { - const owner = this.owner; + const owner = this[owner_symbol]; if (!owner.server) return; @@ -242,11 +243,11 @@ function onnewsession(key, session) { function onocspresponse(resp) { - this.owner.emit('OCSPResponse', resp); + this[owner_symbol].emit('OCSPResponse', resp); } function onerror(err) { - const owner = this.owner; + const owner = this[owner_symbol]; if (owner._writableState.errorEmitted) return; @@ -365,9 +366,9 @@ for (var n = 0; n < proxiedMethods.length; n++) { tls_wrap.TLSWrap.prototype.close = function close(cb) { let ssl; - if (this.owner) { - ssl = this.owner.ssl; - this.owner.ssl = null; + if (this[owner_symbol]) { + ssl = this[owner_symbol].ssl; + this[owner_symbol].ssl = null; } // Invoke `destroySSL` on close to clean up possibly pending write requests @@ -406,7 +407,7 @@ TLSSocket.prototype._wrapHandle = function(wrap) { handle = options.pipe ? new Pipe(PipeConstants.SOCKET) : new TCP(TCPConstants.SOCKET); - handle.owner = this; + handle[owner_symbol] = this; } // Wrap socket's handle diff --git a/lib/dgram.js b/lib/dgram.js index 1e151edad0c78c..063bb7b9d28326 100644 --- a/lib/dgram.js +++ b/lib/dgram.js @@ -47,7 +47,7 @@ const { isUint8Array } = require('internal/util/types'); const EventEmitter = require('events'); const { defaultTriggerAsyncIdScope, - symbols: { async_id_symbol } + symbols: { async_id_symbol, owner_symbol } } = require('internal/async_hooks'); const { UV_UDP_REUSEADDR } = process.binding('constants').os; @@ -82,7 +82,7 @@ function Socket(type, listener) { } var handle = newHandle(type, lookup); - handle.owner = this; + handle[owner_symbol] = this; this[async_id_symbol] = handle.getAsyncId(); this.type = type; @@ -136,7 +136,7 @@ function replaceHandle(self, newHandle) { newHandle.lookup = oldHandle.lookup; newHandle.bind = oldHandle.bind; newHandle.send = oldHandle.send; - newHandle.owner = self; + newHandle[owner_symbol] = self; // Replace the existing handle by the handle we got from master. oldHandle.close(); @@ -620,7 +620,7 @@ function stopReceiving(socket) { function onMessage(nread, handle, buf, rinfo) { - var self = handle.owner; + var self = handle[owner_symbol]; if (nread < 0) { return self.emit('error', errnoException(nread, 'recvmsg')); } @@ -730,6 +730,14 @@ Socket.prototype._stopReceiving = function() { }; +// Legacy alias on the C++ wrapper object. This is not public API, so we may +// want to runtime-deprecate it at some point. There's no hurry, though. +Object.defineProperty(UDP.prototype, 'owner', { + get() { return this[owner_symbol]; }, + set(v) { return this[owner_symbol] = v; } +}); + + module.exports = { _createSocketHandle, createSocket, diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 90155fa3c773dc..9edb23f8a6bdee 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -30,7 +30,7 @@ const async_wrap = process.binding('async_wrap'); * It has a fixed size, so if that is exceeded, calls to the native * side are used instead in pushAsyncIds() and popAsyncIds(). */ -const { async_hook_fields, async_id_fields } = async_wrap; +const { async_hook_fields, async_id_fields, owner_symbol } = async_wrap; // Store the pair executionAsyncId and triggerAsyncId in a std::stack on // Environment::AsyncHooks::async_ids_stack_ tracks the resource responsible for // the current execution stack. This is unwound as each resource exits. In the @@ -434,7 +434,7 @@ module.exports = { symbols: { async_id_symbol, trigger_async_id_symbol, init_symbol, before_symbol, after_symbol, destroy_symbol, - promise_resolve_symbol + promise_resolve_symbol, owner_symbol }, constants: { kInit, kBefore, kAfter, kDestroy, kTotals, kPromiseResolve diff --git a/lib/internal/child_process.js b/lib/internal/child_process.js index f0a54ab6a18a74..32568aa31128a6 100644 --- a/lib/internal/child_process.js +++ b/lib/internal/child_process.js @@ -28,6 +28,7 @@ const { TTY } = process.binding('tty_wrap'); const { TCP } = process.binding('tcp_wrap'); const { UDP } = process.binding('udp_wrap'); const SocketList = require('internal/socket_list'); +const { owner_symbol } = require('internal/async_hooks').symbols; const { convertToValidSignal } = require('internal/util'); const { isUint8Array } = require('internal/util/types'); const spawn_sync = process.binding('spawn_sync'); @@ -210,7 +211,7 @@ function ChildProcess() { this.spawnfile = null; this._handle = new Process(); - this._handle.owner = this; + this._handle[owner_symbol] = this; this._handle.onexit = (exitCode, signalCode) => { if (signalCode) { diff --git a/lib/internal/cluster/child.js b/lib/internal/cluster/child.js index 40c1a12327558f..39e891214ff787 100644 --- a/lib/internal/cluster/child.js +++ b/lib/internal/cluster/child.js @@ -3,6 +3,7 @@ const assert = require('assert'); const util = require('util'); const path = require('path'); const EventEmitter = require('events'); +const { owner_symbol } = require('internal/async_hooks').symbols; const Worker = require('internal/cluster/worker'); const { internal, sendHelper } = require('internal/cluster/utils'); const cluster = new EventEmitter(); @@ -207,8 +208,8 @@ function _disconnect(masterInitiated) { delete handles[key]; waitingCount++; - if (handle.owner) - handle.owner.close(checkWaitingCount); + if (handle[owner_symbol]) + handle[owner_symbol].close(checkWaitingCount); else handle.close(checkWaitingCount); } diff --git a/lib/internal/fs/watchers.js b/lib/internal/fs/watchers.js index e7edc1c5acd91f..90ea3971aae6f5 100644 --- a/lib/internal/fs/watchers.js +++ b/lib/internal/fs/watchers.js @@ -11,7 +11,10 @@ const { getStatsFromBinding, validatePath } = require('internal/fs/utils'); -const { defaultTriggerAsyncIdScope } = require('internal/async_hooks'); +const { + defaultTriggerAsyncIdScope, + symbols: { owner_symbol } +} = require('internal/async_hooks'); const { toNamespacedPath } = require('path'); const { validateUint32 } = require('internal/validators'); const { getPathFromURL } = require('internal/url'); @@ -20,7 +23,6 @@ const assert = require('assert'); const kOldStatus = Symbol('kOldStatus'); const kUseBigint = Symbol('kUseBigint'); -const kOwner = Symbol('kOwner'); function emitStop(self) { self.emit('stop'); @@ -36,7 +38,7 @@ function StatWatcher(bigint) { util.inherits(StatWatcher, EventEmitter); function onchange(newStatus, stats) { - const self = this[kOwner]; + const self = this[owner_symbol]; if (self[kOldStatus] === -1 && newStatus === -1 && stats[2/* new nlink */] === stats[16/* old nlink */]) { @@ -59,7 +61,7 @@ StatWatcher.prototype.start = function(filename, persistent, interval) { return; this._handle = new _StatWatcher(this[kUseBigint]); - this._handle[kOwner] = this; + this._handle[owner_symbol] = this; this._handle.onchange = onchange; if (!persistent) this._handle.unref(); @@ -104,7 +106,7 @@ function FSWatcher() { EventEmitter.call(this); this._handle = new FSEvent(); - this._handle.owner = this; + this._handle[owner_symbol] = this; this._handle.onchange = (status, eventType, filename) => { // TODO(joyeecheung): we may check self._handle.initialized here @@ -131,6 +133,7 @@ function FSWatcher() { } util.inherits(FSWatcher, EventEmitter); + // FIXME(joyeecheung): this method is not documented. // At the moment if filename is undefined, we // 1. Throw an Error if it's the first time .start() is called @@ -187,6 +190,13 @@ function emitCloseNT(self) { self.emit('close'); } +// Legacy alias on the C++ wrapper object. This is not public API, so we may +// want to runtime-deprecate it at some point. There's no hurry, though. +Object.defineProperty(FSEvent.prototype, 'owner', { + get() { return this[owner_symbol]; }, + set(v) { return this[owner_symbol] = v; } +}); + module.exports = { FSWatcher, StatWatcher diff --git a/lib/internal/http2/core.js b/lib/internal/http2/core.js index 950ceb45fc52aa..bef7912f272431 100644 --- a/lib/internal/http2/core.js +++ b/lib/internal/http2/core.js @@ -28,6 +28,7 @@ const { defaultTriggerAsyncIdScope, symbols: { async_id_symbol, + owner_symbol, }, } = require('internal/async_hooks'); const { internalBinding } = require('internal/bootstrap/loaders'); @@ -144,7 +145,7 @@ const kInfoHeaders = Symbol('sent-info-headers'); const kMaybeDestroy = Symbol('maybe-destroy'); const kLocalSettings = Symbol('local-settings'); const kOptions = Symbol('options'); -const kOwner = Symbol('owner'); +const kOwner = owner_symbol; const kProceed = Symbol('proceed'); const kProtocol = Symbol('protocol'); const kProxySocket = Symbol('proxy-socket'); diff --git a/lib/internal/wrap_js_stream.js b/lib/internal/wrap_js_stream.js index ec966028a024f7..e80716059ef6e7 100644 --- a/lib/internal/wrap_js_stream.js +++ b/lib/internal/wrap_js_stream.js @@ -6,16 +6,17 @@ const { Socket } = require('net'); const { JSStream } = process.binding('js_stream'); const uv = process.binding('uv'); const debug = util.debuglog('stream_wrap'); +const { owner_symbol } = require('internal/async_hooks').symbols; const { ERR_STREAM_WRAP } = require('internal/errors').codes; const kCurrentWriteRequest = Symbol('kCurrentWriteRequest'); const kCurrentShutdownRequest = Symbol('kCurrentShutdownRequest'); -function isClosing() { return this.owner.isClosing(); } -function onreadstart() { return this.owner.readStart(); } -function onreadstop() { return this.owner.readStop(); } -function onshutdown(req) { return this.owner.doShutdown(req); } -function onwrite(req, bufs) { return this.owner.doWrite(req, bufs); } +function isClosing() { return this[owner_symbol].isClosing(); } +function onreadstart() { return this[owner_symbol].readStart(); } +function onreadstop() { return this[owner_symbol].readStop(); } +function onshutdown(req) { return this[owner_symbol].doShutdown(req); } +function onwrite(req, bufs) { return this[owner_symbol].doWrite(req, bufs); } /* This class serves as a wrapper for when the C++ side of Node wants access * to a standard JS stream. For example, TLS or HTTP do not operate on network @@ -37,7 +38,7 @@ class JSStreamWrap extends Socket { this.doClose(cb); }; // Inside of the following functions, `this` refers to the handle - // and `this.owner` refers to this JSStreamWrap instance. + // and `this[owner_symbol]` refers to this JSStreamWrap instance. handle.isClosing = isClosing; handle.onreadstart = onreadstart; handle.onreadstop = onreadstop; diff --git a/lib/net.js b/lib/net.js index 4b19f6b6368641..c49dd350c36944 100644 --- a/lib/net.js +++ b/lib/net.js @@ -56,7 +56,7 @@ const { const { newAsyncId, defaultTriggerAsyncIdScope, - symbols: { async_id_symbol } + symbols: { async_id_symbol, owner_symbol } } = require('internal/async_hooks'); const { createWriteWrap, @@ -207,7 +207,7 @@ function initSocketHandle(self) { // Handle creation may be deferred to bind() or connect() time. if (self._handle) { - self._handle.owner = self; + self._handle[owner_symbol] = self; self._handle.onread = onread; self[async_id_symbol] = getNewAsyncId(self._handle); } @@ -371,7 +371,7 @@ Socket.prototype._final = function(cb) { function afterShutdown(status, handle) { - var self = handle.owner; + var self = handle[owner_symbol]; debug('afterShutdown destroyed=%j', self.destroyed, self._readableState); @@ -620,7 +620,7 @@ Socket.prototype._destroy = function(exception, cb) { // buffer, or when there's an error reading. function onread(nread, buffer) { var handle = this; - var self = handle.owner; + var self = handle[owner_symbol]; assert(handle === self._handle, 'handle != self._handle'); self._unrefTimer(); @@ -819,7 +819,7 @@ protoGetter('bytesWritten', function bytesWritten() { function afterWrite(status, handle, err) { - var self = handle.owner; + var self = handle[owner_symbol]; if (self !== process.stderr && self !== process.stdout) debug('afterWrite', status); @@ -1121,7 +1121,7 @@ Socket.prototype.unref = function() { function afterConnect(status, handle, req, readable, writable) { - var self = handle.owner; + var self = handle[owner_symbol]; // callback may come after call to destroy if (self.destroyed) { @@ -1323,7 +1323,7 @@ function setupListenHandle(address, port, addressType, backlog, fd) { this[async_id_symbol] = getNewAsyncId(this._handle); this._handle.onconnection = onconnection; - this._handle.owner = this; + this._handle[owner_symbol] = this; // Use a backlog of 512 entries. We pass 511 to the listen() call because // the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); @@ -1536,7 +1536,7 @@ Server.prototype.address = function() { function onconnection(err, clientHandle) { var handle = this; - var self = handle.owner; + var self = handle[owner_symbol]; debug('onconnection'); @@ -1667,6 +1667,14 @@ function emitCloseNT(self) { } +// Legacy alias on the C++ wrapper object. This is not public API, so we may +// want to runtime-deprecate it at some point. There's no hurry, though. +Object.defineProperty(TCP.prototype, 'owner', { + get() { return this[owner_symbol]; }, + set(v) { return this[owner_symbol] = v; } +}); + + Server.prototype.listenFD = internalUtil.deprecate(function(fd, type) { return this.listen({ fd: fd }); }, 'Server.listenFD is deprecated. Use Server.listen({fd: }) instead.', diff --git a/lib/timers.js b/lib/timers.js index 2459242a79f6cd..55b6b53c5c8fd5 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -43,7 +43,8 @@ const { // The needed emit*() functions. emitBefore, emitAfter, - emitDestroy + emitDestroy, + symbols: { owner_symbol } } = require('internal/async_hooks'); // *Must* match Environment::ImmediateInfo::Fields in src/env.h. @@ -206,8 +207,8 @@ function TimersList(msecs, unrefed) { } function processTimers(now) { - if (this.owner) - return unrefdHandle(this.owner, now); + if (this[owner_symbol]) + return unrefdHandle(this[owner_symbol], now); return listOnTimeout(this, now); } @@ -268,7 +269,7 @@ function listOnTimeout(handle, now) { // Do not close the underlying handle if its ownership has changed // (e.g it was unrefed in its callback). - if (!handle.owner) + if (!handle[owner_symbol]) handle.close(); return true; @@ -540,7 +541,7 @@ Timeout.prototype.unref = function() { } this._handle = handle || new TimerWrap(); - this._handle.owner = this; + this._handle[owner_symbol] = this; this._handle.start(delay); this._handle.unref(); } @@ -788,3 +789,10 @@ exports.clearImmediate = function clearImmediate(immediate) { immediateQueue.remove(immediate); }; + +// Legacy alias on the C++ wrapper object. This is not public API, so we may +// want to runtime-deprecate it at some point. There's no hurry, though. +Object.defineProperty(TimerWrap.prototype, 'owner', { + get() { return this[owner_symbol]; }, + set(v) { return this[owner_symbol] = v; } +}); diff --git a/src/async_wrap.cc b/src/async_wrap.cc index 46d63694dee1a2..cb2a86f2924d64 100644 --- a/src/async_wrap.cc +++ b/src/async_wrap.cc @@ -483,6 +483,10 @@ void AsyncWrap::Initialize(Local target, env->async_ids_stack_string(), env->async_hooks()->async_ids_stack().GetJSArray()).FromJust(); + target->Set(context, + FIXED_ONE_BYTE_STRING(env->isolate(), "owner_symbol"), + env->owner_symbol()).FromJust(); + Local constants = Object::New(isolate); #define SET_HOOKS_CONSTANT(name) \ FORCE_SET_TARGET_FIELD( \ diff --git a/src/env.h b/src/env.h index b1ea04b9a65bb3..c6b5a7eeafa5f5 100644 --- a/src/env.h +++ b/src/env.h @@ -111,6 +111,7 @@ struct PackageConfig { // for the sake of convenience. #define PER_ISOLATE_SYMBOL_PROPERTIES(V) \ V(handle_onclose_symbol, "handle_onclose") \ + V(owner_symbol, "owner") \ // Strings are per-isolate primitives but Environment proxies them // for the sake of convenience. Strings should be ASCII-only. @@ -233,7 +234,6 @@ struct PackageConfig { V(openssl_error_stack, "opensslErrorStack") \ V(output_string, "output") \ V(order_string, "order") \ - V(owner_string, "owner") \ V(parse_error_string, "Parse Error") \ V(password_string, "password") \ V(path_string, "path") \ diff --git a/src/tcp_wrap.cc b/src/tcp_wrap.cc index 805e566bfab08b..e817087d97110b 100644 --- a/src/tcp_wrap.cc +++ b/src/tcp_wrap.cc @@ -81,7 +81,7 @@ void TCPWrap::Initialize(Local target, // Init properties t->InstanceTemplate()->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "reading"), Boolean::New(env->isolate(), false)); - t->InstanceTemplate()->Set(env->owner_string(), Null(env->isolate())); + t->InstanceTemplate()->Set(env->owner_symbol(), Null(env->isolate())); t->InstanceTemplate()->Set(env->onread_string(), Null(env->isolate())); t->InstanceTemplate()->Set(env->onconnection_string(), Null(env->isolate())); diff --git a/src/timer_wrap.cc b/src/timer_wrap.cc index 1ba2824e4c9992..e02ea332ab0a86 100644 --- a/src/timer_wrap.cc +++ b/src/timer_wrap.cc @@ -148,7 +148,7 @@ class TimerWrap : public HandleWrap { !env->tick_info()->has_thrown() && env->can_call_into_js() && wrap->object()->Get(env->context(), - env->owner_string()).ToLocalChecked() + env->owner_symbol()).ToLocalChecked() ->IsUndefined()); }