Skip to content

Commit c34db7a

Browse files
addaleaxtargos
authored andcommitted
worker: reduce MessagePort prototype to documented API
`MessagePort` is special because it has to be a C++ API that is exposed to userland. Therefore, there is a number of internal methods on its native prototype; this commit reduces this set of methods to only what is documented in the API. PR-URL: #23037 Reviewed-By: Gus Caplan <[email protected]> Reviewed-By: Michaël Zasso <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 2e30a68 commit c34db7a

File tree

4 files changed

+41
-24
lines changed

4 files changed

+41
-24
lines changed

lib/internal/worker.js

+31-18
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ const {
1414

1515
const { internalBinding } = require('internal/bootstrap/loaders');
1616
const { MessagePort, MessageChannel } = internalBinding('messaging');
17-
const { handle_onclose } = internalBinding('symbols');
17+
const {
18+
handle_onclose: handleOnCloseSymbol,
19+
oninit: onInitSymbol
20+
} = internalBinding('symbols');
1821
const { clearAsyncIdStack } = require('internal/async_hooks');
1922
const { serializeError, deserializeError } = require('internal/error-serdes');
2023
const { pathToFileURL } = require('url');
2124

22-
util.inherits(MessagePort, EventEmitter);
23-
2425
const {
2526
Worker: WorkerImpl,
2627
getEnvMessagePort,
27-
threadId,
28-
oninit: oninit_symbol
28+
threadId
2929
} = internalBinding('worker');
3030

3131
const isMainThread = threadId === 0;
@@ -58,6 +58,23 @@ const messageTypes = {
5858
LOAD_SCRIPT: 'loadScript'
5959
};
6060

61+
// We have to mess with the MessagePort prototype a bit, so that a) we can make
62+
// it inherit from EventEmitter, even though it is a C++ class, and b) we do
63+
// not provide methods that are not present in the Browser and not documented
64+
// on our side (e.g. hasRef).
65+
// Save a copy of the original set of methods as a shallow clone.
66+
const MessagePortPrototype = Object.create(
67+
Object.getPrototypeOf(MessagePort.prototype),
68+
Object.getOwnPropertyDescriptors(MessagePort.prototype));
69+
// Set up the new inheritance chain.
70+
Object.setPrototypeOf(MessagePort, EventEmitter);
71+
Object.setPrototypeOf(MessagePort.prototype, EventEmitter.prototype);
72+
// Finally, purge methods we don't want to be public.
73+
delete MessagePort.prototype.stop;
74+
delete MessagePort.prototype.drain;
75+
delete MessagePort.prototype.hasRef;
76+
delete MessagePort.prototype.getAsyncId;
77+
6178
// A communication channel consisting of a handle (that wraps around an
6279
// uv_async_t) which can receive information from other threads and emits
6380
// .onmessage events, and a function used for sending data to a MessagePort
@@ -81,10 +98,10 @@ Object.defineProperty(MessagePort.prototype, 'onmessage', {
8198
this[kOnMessageListener] = value;
8299
if (typeof value === 'function') {
83100
this.ref();
84-
this.start();
101+
MessagePortPrototype.start.call(this);
85102
} else {
86103
this.unref();
87-
this.stop();
104+
MessagePortPrototype.stop.call(this);
88105
}
89106
}
90107
});
@@ -94,7 +111,7 @@ function oninit() {
94111
setupPortReferencing(this, this, 'message');
95112
}
96113

97-
Object.defineProperty(MessagePort.prototype, oninit_symbol, {
114+
Object.defineProperty(MessagePort.prototype, onInitSymbol, {
98115
enumerable: true,
99116
writable: false,
100117
value: oninit
@@ -111,22 +128,18 @@ function onclose() {
111128
this.emit('close');
112129
}
113130

114-
Object.defineProperty(MessagePort.prototype, handle_onclose, {
131+
Object.defineProperty(MessagePort.prototype, handleOnCloseSymbol, {
115132
enumerable: false,
116133
writable: false,
117134
value: onclose
118135
});
119136

120-
const originalClose = MessagePort.prototype.close;
121137
MessagePort.prototype.close = function(cb) {
122138
if (typeof cb === 'function')
123139
this.once('close', cb);
124-
originalClose.call(this);
140+
MessagePortPrototype.close.call(this);
125141
};
126142

127-
const drainMessagePort = MessagePort.prototype.drain;
128-
delete MessagePort.prototype.drain;
129-
130143
Object.defineProperty(MessagePort.prototype, util.inspect.custom, {
131144
enumerable: false,
132145
writable: false,
@@ -135,7 +148,7 @@ Object.defineProperty(MessagePort.prototype, util.inspect.custom, {
135148
try {
136149
// This may throw when `this` does not refer to a native object,
137150
// e.g. when accessing the prototype directly.
138-
ref = this.hasRef();
151+
ref = MessagePortPrototype.hasRef.call(this);
139152
} catch { return this; }
140153
return Object.assign(Object.create(MessagePort.prototype),
141154
ref === undefined ? {
@@ -157,12 +170,12 @@ function setupPortReferencing(port, eventEmitter, eventName) {
157170
eventEmitter.on('newListener', (name) => {
158171
if (name === eventName && eventEmitter.listenerCount(eventName) === 0) {
159172
port.ref();
160-
port.start();
173+
MessagePortPrototype.start.call(port);
161174
}
162175
});
163176
eventEmitter.on('removeListener', (name) => {
164177
if (name === eventName && eventEmitter.listenerCount(eventName) === 0) {
165-
port.stop();
178+
MessagePortPrototype.stop.call(port);
166179
port.unref();
167180
}
168181
});
@@ -304,7 +317,7 @@ class Worker extends EventEmitter {
304317

305318
[kOnExit](code) {
306319
debug(`[${threadId}] hears end event for Worker ${this.threadId}`);
307-
drainMessagePort.call(this[kPublicPort]);
320+
MessagePortPrototype.drain.call(this[kPublicPort]);
308321
this[kDispose]();
309322
this.emit('exit', code);
310323
this.removeAllListeners();

src/node_worker.cc

-4
Original file line numberDiff line numberDiff line change
@@ -501,10 +501,6 @@ void InitWorker(Local<Object> target,
501501
thread_id_string,
502502
Number::New(env->isolate(),
503503
static_cast<double>(env->thread_id()))).FromJust();
504-
Local<String> oninit_string = FIXED_ONE_BYTE_STRING(env->isolate(), "oninit");
505-
target->Set(env->context(),
506-
oninit_string,
507-
env->oninit_symbol()).FromJust();
508504
}
509505

510506
} // anonymous namespace

test/parallel/test-heapdump-worker.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ validateSnapshotNodes('Worker', [
1919
validateSnapshotNodes('MessagePort', [
2020
{
2121
children: [
22-
{ name: 'MessagePortData' },
23-
{ name: 'MessagePort' }
22+
{ name: 'MessagePortData' }
2423
]
2524
}
2625
], { loose: true });

test/parallel/test-worker-message-port.js

+9
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,12 @@ const { MessageChannel, MessagePort } = require('worker_threads');
6969
});
7070
});
7171
}
72+
73+
{
74+
assert.deepStrictEqual(
75+
Object.getOwnPropertyNames(MessagePort.prototype).sort(),
76+
[
77+
'close', 'constructor', 'onmessage', 'postMessage', 'ref', 'start',
78+
'unref'
79+
]);
80+
}

0 commit comments

Comments
 (0)