Skip to content

Commit ca73f52

Browse files
committed
src,lib: make JSTransferables based on private symbols
Serializes and transfers platform objects implemented as a JS class based on private symbols instead of V8 object internal slots. This avoids the need to alter the prototype chains and mixins to make the JS class to be transferable.
1 parent 0abd5e7 commit ca73f52

23 files changed

+282
-171
lines changed

lib/internal/abort_controller.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,9 @@ const {
6262
} = internalBinding('symbols');
6363

6464
let _MessageChannel;
65-
let makeTransferable;
65+
let markTransferMode;
6666

67-
// Loading the MessageChannel and makeTransferable have to be done lazily
67+
// Loading the MessageChannel and markTransferable have to be done lazily
6868
// because otherwise we'll end up with a require cycle that ends up with
6969
// an incomplete initialization of abort_controller.
7070

@@ -73,10 +73,10 @@ function lazyMessageChannel() {
7373
return new _MessageChannel();
7474
}
7575

76-
function lazyMakeTransferable(obj) {
77-
makeTransferable ??=
78-
require('internal/worker/js_transferable').makeTransferable;
79-
return makeTransferable(obj);
76+
function lazyMarkTransferMode(obj, cloneable, transferable) {
77+
markTransferMode ??=
78+
require('internal/worker/js_transferable').markTransferMode;
79+
return markTransferMode(obj, cloneable, transferable);
8080
}
8181

8282
const clearTimeoutRegistry = new SafeFinalizationRegistry(clearTimeout);
@@ -301,7 +301,9 @@ function createAbortSignal(init = kEmptyObject) {
301301
ObjectSetPrototypeOf(signal, AbortSignal.prototype);
302302
signal[kAborted] = aborted;
303303
signal[kReason] = reason;
304-
return transferable ? lazyMakeTransferable(signal) : signal;
304+
if (transferable)
305+
lazyMarkTransferMode(signal, false, true);
306+
return signal;
305307
}
306308

307309
function abortSignal(signal, reason) {
@@ -353,7 +355,8 @@ class AbortController {
353355
function transferableAbortSignal(signal) {
354356
if (signal?.[kAborted] === undefined)
355357
throw new ERR_INVALID_ARG_TYPE('signal', 'AbortSignal', signal);
356-
return lazyMakeTransferable(signal);
358+
lazyMarkTransferMode(signal, false, true);
359+
return signal;
357360
}
358361

359362
/**

lib/internal/blob.js

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const {
3232
const { URL } = require('internal/url');
3333

3434
const {
35-
makeTransferable,
35+
markTransferMode,
3636
kClone,
3737
kDeserialize,
3838
} = require('internal/worker/js_transferable');
@@ -136,6 +136,8 @@ class Blob {
136136
* @constructs {Blob}
137137
*/
138138
constructor(sources = [], options) {
139+
markTransferMode(this, true, false);
140+
139141
if (sources === null ||
140142
typeof sources[SymbolIterator] !== 'function' ||
141143
typeof sources === 'string') {
@@ -167,9 +169,6 @@ class Blob {
167169
type = `${type}`;
168170
this[kType] = RegExpPrototypeExec(disallowedTypeCharacters, type) !== null ?
169171
'' : StringPrototypeToLowerCase(type);
170-
171-
// eslint-disable-next-line no-constructor-return
172-
return makeTransferable(this);
173172
}
174173

175174
[kInspect](depth, options) {
@@ -374,16 +373,19 @@ class Blob {
374373
}
375374

376375
function ClonedBlob() {
377-
return makeTransferable(ReflectConstruct(function() {}, [], Blob));
376+
return ReflectConstruct(function() {
377+
markTransferMode(this, true, false);
378+
}, [], Blob);
378379
}
379380
ClonedBlob.prototype[kDeserialize] = () => {};
380381

381382
function createBlob(handle, length, type = '') {
382-
return makeTransferable(ReflectConstruct(function() {
383+
return ReflectConstruct(function() {
384+
markTransferMode(this, true, false);
383385
this[kHandle] = handle;
384386
this[kType] = type;
385387
this[kLength] = length;
386-
}, [], Blob));
388+
}, [], Blob);
387389
}
388390

389391
ObjectDefineProperty(Blob.prototype, SymbolToStringTag, {

lib/internal/blocklist.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const {
2020
} = require('internal/socketaddress');
2121

2222
const {
23-
JSTransferable,
23+
markTransferMode,
2424
kClone,
2525
kDeserialize,
2626
} = require('internal/worker/js_transferable');
@@ -36,9 +36,9 @@ const {
3636

3737
const { validateInt32, validateString } = require('internal/validators');
3838

39-
class BlockList extends JSTransferable {
39+
class BlockList {
4040
constructor() {
41-
super();
41+
markTransferMode(this, true, false);
4242
this[kHandle] = new BlockListHandle();
4343
this[kHandle][owner_symbol] = this;
4444
}
@@ -148,9 +148,9 @@ class BlockList extends JSTransferable {
148148
}
149149
}
150150

151-
class InternalBlockList extends JSTransferable {
151+
class InternalBlockList {
152152
constructor(handle) {
153-
super();
153+
markTransferMode(this, true, false);
154154
this[kHandle] = handle;
155155
if (handle !== undefined)
156156
handle[owner_symbol] = this;

lib/internal/crypto/keys.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const {
5757
} = require('internal/util/types');
5858

5959
const {
60-
makeTransferable,
60+
markTransferMode,
6161
kClone,
6262
kDeserialize,
6363
} = require('internal/worker/js_transferable');
@@ -706,24 +706,22 @@ ObjectDefineProperties(CryptoKey.prototype, {
706706
// All internal code must use new InternalCryptoKey to create
707707
// CryptoKey instances. The CryptoKey class is exposed to end
708708
// user code but is not permitted to be constructed directly.
709-
// Using makeTransferable also allows the CryptoKey to be
709+
// Using markTransferMode also allows the CryptoKey to be
710710
// cloned to Workers.
711711
class InternalCryptoKey {
712712
constructor(
713713
keyObject,
714714
algorithm,
715715
keyUsages,
716716
extractable) {
717+
markTransferMode(this, true, false);
717718
// Using symbol properties here currently instead of private
718719
// properties because (for now) the performance penalty of
719720
// private fields is still too high.
720721
this[kKeyObject] = keyObject;
721722
this[kAlgorithm] = algorithm;
722723
this[kExtractable] = extractable;
723724
this[kKeyUsages] = keyUsages;
724-
725-
// eslint-disable-next-line no-constructor-return
726-
return makeTransferable(this);
727725
}
728726

729727
[kClone]() {

lib/internal/crypto/x509.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const {
4848
} = require('internal/errors');
4949

5050
const {
51-
JSTransferable,
51+
markTransferMode,
5252
kClone,
5353
kDeserialize,
5454
} = require('internal/worker/js_transferable');
@@ -94,16 +94,16 @@ function getFlags(options = kEmptyObject) {
9494
return flags;
9595
}
9696

97-
class InternalX509Certificate extends JSTransferable {
97+
class InternalX509Certificate {
9898
[kInternalState] = new SafeMap();
9999

100100
constructor(handle) {
101-
super();
101+
markTransferMode(this, true, false);
102102
this[kHandle] = handle;
103103
}
104104
}
105105

106-
class X509Certificate extends JSTransferable {
106+
class X509Certificate {
107107
[kInternalState] = new SafeMap();
108108

109109
constructor(buffer) {
@@ -115,7 +115,7 @@ class X509Certificate extends JSTransferable {
115115
['string', 'Buffer', 'TypedArray', 'DataView'],
116116
buffer);
117117
}
118-
super();
118+
markTransferMode(this, true, false);
119119
this[kHandle] = parseX509(buffer);
120120
}
121121

lib/internal/event_target.js

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@ const {
1010
ObjectDefineProperties,
1111
ObjectDefineProperty,
1212
ObjectGetOwnPropertyDescriptor,
13-
ObjectGetOwnPropertyDescriptors,
14-
ObjectSetPrototypeOf,
15-
ObjectValues,
1613
ReflectApply,
17-
SafeArrayIterator,
1814
SafeFinalizationRegistry,
1915
SafeMap,
2016
SafeWeakMap,
@@ -1097,30 +1093,9 @@ function defineEventHandler(emitter, name, event = name) {
10971093
});
10981094
}
10991095

1100-
const EventEmitterMixin = (Superclass) => {
1101-
class MixedEventEmitter extends Superclass {
1102-
constructor(...args) {
1103-
args = new SafeArrayIterator(args);
1104-
super(...args);
1105-
FunctionPrototypeCall(EventEmitter, this);
1106-
}
1107-
}
1108-
const protoProps = ObjectGetOwnPropertyDescriptors(EventEmitter.prototype);
1109-
delete protoProps.constructor;
1110-
const propertiesValues = ObjectValues(protoProps);
1111-
for (let i = 0; i < propertiesValues.length; i++) {
1112-
// We want to use null-prototype objects to not rely on globally mutable
1113-
// %Object.prototype%.
1114-
ObjectSetPrototypeOf(propertiesValues[i], null);
1115-
}
1116-
ObjectDefineProperties(MixedEventEmitter.prototype, protoProps);
1117-
return MixedEventEmitter;
1118-
};
1119-
11201096
module.exports = {
11211097
Event,
11221098
CustomEvent,
1123-
EventEmitterMixin,
11241099
EventTarget,
11251100
NodeEventTarget,
11261101
defineEventHandler,

lib/internal/fs/promises.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const {
8989
lazyDOMException,
9090
promisify,
9191
} = require('internal/util');
92-
const { EventEmitterMixin } = require('internal/event_target');
92+
const EventEmitter = require('events');
9393
const { StringDecoder } = require('string_decoder');
9494
const { kFSWatchStart, watch } = require('internal/fs/watchers');
9595
const nonNativeWatcher = require('internal/fs/recursive_watch');
@@ -109,7 +109,7 @@ const kLocked = Symbol('kLocked');
109109
const { kUsePromises } = binding;
110110
const { Interface } = require('internal/readline/interface');
111111
const {
112-
JSTransferable, kDeserialize, kTransfer, kTransferList,
112+
kDeserialize, kTransfer, kTransferList, markTransferMode,
113113
} = require('internal/worker/js_transferable');
114114

115115
const getDirectoryEntriesPromise = promisify(getDirents);
@@ -129,12 +129,13 @@ function lazyFsStreams() {
129129
return fsStreams ??= require('internal/fs/streams');
130130
}
131131

132-
class FileHandle extends EventEmitterMixin(JSTransferable) {
132+
class FileHandle extends EventEmitter {
133133
/**
134134
* @param {InternalFSBinding.FileHandle | undefined} filehandle
135135
*/
136136
constructor(filehandle) {
137137
super();
138+
markTransferMode(this, false, true);
138139
this[kHandle] = filehandle;
139140
this[kFd] = filehandle ? filehandle.fd : -1;
140141

lib/internal/histogram.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ const kRecordable = Symbol('kRecordable');
4545
const {
4646
kClone,
4747
kDeserialize,
48-
makeTransferable,
48+
markTransferMode,
4949
} = require('internal/worker/js_transferable');
5050

5151
function isHistogram(object) {
@@ -319,21 +319,23 @@ class RecordableHistogram extends Histogram {
319319
}
320320

321321
function internalHistogram(handle) {
322-
return makeTransferable(ReflectConstruct(
322+
return ReflectConstruct(
323323
function() {
324+
markTransferMode(this, true, false);
324325
this[kHandle] = handle;
325326
this[kMap] = new SafeMap();
326-
}, [], Histogram));
327+
}, [], Histogram);
327328
}
328329
internalHistogram.prototype[kDeserialize] = () => {};
329330

330331
function internalRecordableHistogram(handle) {
331-
return makeTransferable(ReflectConstruct(
332+
return ReflectConstruct(
332333
function() {
334+
markTransferMode(this, true, false);
333335
this[kHandle] = handle;
334336
this[kMap] = new SafeMap();
335337
this[kRecordable] = true;
336-
}, [], RecordableHistogram));
338+
}, [], RecordableHistogram);
337339
}
338340
internalRecordableHistogram.prototype[kDeserialize] = () => {};
339341

lib/internal/perf/event_loop_delay.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const {
3232
} = require('internal/util');
3333

3434
const {
35-
makeTransferable,
35+
markTransferMode,
3636
} = require('internal/worker/js_transferable');
3737

3838
const kEnabled = Symbol('kEnabled');
@@ -79,12 +79,13 @@ function monitorEventLoopDelay(options = kEmptyObject) {
7979
const { resolution = 10 } = options;
8080
validateInteger(resolution, 'options.resolution', 1);
8181

82-
return makeTransferable(ReflectConstruct(
82+
return ReflectConstruct(
8383
function() {
84+
markTransferMode(this, true, false);
8485
this[kEnabled] = false;
8586
this[kHandle] = createELDHistogram(resolution);
8687
this[kMap] = new SafeMap();
87-
}, [], ELDHistogram));
88+
}, [], ELDHistogram);
8889
}
8990

9091
module.exports = monitorEventLoopDelay;

lib/internal/socketaddress.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,22 @@ const {
3232
const { inspect } = require('internal/util/inspect');
3333

3434
const {
35-
JSTransferable,
35+
markTransferMode,
3636
kClone,
3737
kDeserialize,
3838
} = require('internal/worker/js_transferable');
3939

4040
const kHandle = Symbol('kHandle');
4141
const kDetail = Symbol('kDetail');
4242

43-
class SocketAddress extends JSTransferable {
43+
class SocketAddress {
4444
static isSocketAddress(value) {
4545
return value?.[kHandle] !== undefined;
4646
}
4747

4848
constructor(options = kEmptyObject) {
49-
super();
49+
markTransferMode(this, true, false);
50+
5051
validateObject(options, 'options');
5152
let { family = 'ipv4' } = options;
5253
const {
@@ -139,9 +140,10 @@ class SocketAddress extends JSTransferable {
139140
}
140141
}
141142

142-
class InternalSocketAddress extends JSTransferable {
143+
class InternalSocketAddress {
143144
constructor(handle) {
144-
super();
145+
markTransferMode(this, true, false);
146+
145147
this[kHandle] = handle;
146148
this[kDetail] = this[kHandle]?.detail({
147149
address: undefined,

0 commit comments

Comments
 (0)