Skip to content

Commit 7cdff9a

Browse files
ExE-Bosstargos
authored andcommitted
lib: refactor primordials.makeSafe to use more primordials
PR-URL: #36865 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
1 parent f509788 commit 7cdff9a

File tree

1 file changed

+129
-106
lines changed

1 file changed

+129
-106
lines changed

lib/internal/per_context/primordials.js

Lines changed: 129 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -10,167 +10,80 @@
1010
// benchmark all changes made in performance-sensitive areas of the codebase.
1111
// See: https://github.com/nodejs/node/pull/38248
1212

13+
const {
14+
defineProperty: ReflectDefineProperty,
15+
getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor,
16+
ownKeys: ReflectOwnKeys,
17+
} = Reflect;
18+
1319
// `uncurryThis` is equivalent to `func => Function.prototype.call.bind(func)`.
1420
// It is using `bind.bind(call)` to avoid using `Function.prototype.bind`
1521
// and `Function.prototype.call` after it may have been mutated by users.
1622
const { bind, call } = Function.prototype;
1723
const uncurryThis = bind.bind(call);
1824
primordials.uncurryThis = uncurryThis;
1925

20-
function copyProps(src, dest) {
21-
for (const key of Reflect.ownKeys(src)) {
22-
if (!Reflect.getOwnPropertyDescriptor(dest, key)) {
23-
Reflect.defineProperty(
24-
dest,
25-
key,
26-
Reflect.getOwnPropertyDescriptor(src, key));
27-
}
28-
}
29-
}
30-
3126
function getNewKey(key) {
3227
return typeof key === 'symbol' ?
3328
`Symbol${key.description[7].toUpperCase()}${key.description.slice(8)}` :
3429
`${key[0].toUpperCase()}${key.slice(1)}`;
3530
}
3631

3732
function copyAccessor(dest, prefix, key, { enumerable, get, set }) {
38-
Reflect.defineProperty(dest, `${prefix}Get${key}`, {
33+
ReflectDefineProperty(dest, `${prefix}Get${key}`, {
3934
value: uncurryThis(get),
4035
enumerable
4136
});
4237
if (set !== undefined) {
43-
Reflect.defineProperty(dest, `${prefix}Set${key}`, {
38+
ReflectDefineProperty(dest, `${prefix}Set${key}`, {
4439
value: uncurryThis(set),
4540
enumerable
4641
});
4742
}
4843
}
4944

5045
function copyPropsRenamed(src, dest, prefix) {
51-
for (const key of Reflect.ownKeys(src)) {
46+
for (const key of ReflectOwnKeys(src)) {
5247
const newKey = getNewKey(key);
53-
const desc = Reflect.getOwnPropertyDescriptor(src, key);
48+
const desc = ReflectGetOwnPropertyDescriptor(src, key);
5449
if ('get' in desc) {
5550
copyAccessor(dest, prefix, newKey, desc);
5651
} else {
57-
Reflect.defineProperty(dest, `${prefix}${newKey}`, desc);
52+
ReflectDefineProperty(dest, `${prefix}${newKey}`, desc);
5853
}
5954
}
6055
}
6156

6257
function copyPropsRenamedBound(src, dest, prefix) {
63-
for (const key of Reflect.ownKeys(src)) {
58+
for (const key of ReflectOwnKeys(src)) {
6459
const newKey = getNewKey(key);
65-
const desc = Reflect.getOwnPropertyDescriptor(src, key);
60+
const desc = ReflectGetOwnPropertyDescriptor(src, key);
6661
if ('get' in desc) {
6762
copyAccessor(dest, prefix, newKey, desc);
6863
} else {
6964
if (typeof desc.value === 'function') {
7065
desc.value = desc.value.bind(src);
7166
}
72-
Reflect.defineProperty(dest, `${prefix}${newKey}`, desc);
67+
ReflectDefineProperty(dest, `${prefix}${newKey}`, desc);
7368
}
7469
}
7570
}
7671

7772
function copyPrototype(src, dest, prefix) {
78-
for (const key of Reflect.ownKeys(src)) {
73+
for (const key of ReflectOwnKeys(src)) {
7974
const newKey = getNewKey(key);
80-
const desc = Reflect.getOwnPropertyDescriptor(src, key);
75+
const desc = ReflectGetOwnPropertyDescriptor(src, key);
8176
if ('get' in desc) {
8277
copyAccessor(dest, prefix, newKey, desc);
8378
} else {
8479
if (typeof desc.value === 'function') {
8580
desc.value = uncurryThis(desc.value);
8681
}
87-
Reflect.defineProperty(dest, `${prefix}${newKey}`, desc);
82+
ReflectDefineProperty(dest, `${prefix}${newKey}`, desc);
8883
}
8984
}
9085
}
9186

92-
const createSafeIterator = (factory, next) => {
93-
class SafeIterator {
94-
constructor(iterable) {
95-
this._iterator = factory(iterable);
96-
}
97-
next() {
98-
return next(this._iterator);
99-
}
100-
[Symbol.iterator]() {
101-
return this;
102-
}
103-
}
104-
Object.setPrototypeOf(SafeIterator.prototype, null);
105-
Object.freeze(SafeIterator.prototype);
106-
Object.freeze(SafeIterator);
107-
return SafeIterator;
108-
};
109-
110-
function makeSafe(unsafe, safe) {
111-
if (Symbol.iterator in unsafe.prototype) {
112-
const dummy = new unsafe();
113-
let next; // We can reuse the same `next` method.
114-
115-
for (const key of Reflect.ownKeys(unsafe.prototype)) {
116-
if (!Reflect.getOwnPropertyDescriptor(safe.prototype, key)) {
117-
const desc = Reflect.getOwnPropertyDescriptor(unsafe.prototype, key);
118-
if (
119-
typeof desc.value === 'function' &&
120-
desc.value.length === 0 &&
121-
Symbol.iterator in (desc.value.call(dummy) ?? {})
122-
) {
123-
const createIterator = uncurryThis(desc.value);
124-
if (next == null) next = uncurryThis(createIterator(dummy).next);
125-
const SafeIterator = createSafeIterator(createIterator, next);
126-
desc.value = function() {
127-
return new SafeIterator(this);
128-
};
129-
}
130-
Reflect.defineProperty(safe.prototype, key, desc);
131-
}
132-
}
133-
} else {
134-
copyProps(unsafe.prototype, safe.prototype);
135-
}
136-
copyProps(unsafe, safe);
137-
138-
Object.setPrototypeOf(safe.prototype, null);
139-
Object.freeze(safe.prototype);
140-
Object.freeze(safe);
141-
return safe;
142-
}
143-
primordials.makeSafe = makeSafe;
144-
145-
// Subclass the constructors because we need to use their prototype
146-
// methods later.
147-
// Defining the `constructor` is necessary here to avoid the default
148-
// constructor which uses the user-mutable `%ArrayIteratorPrototype%.next`.
149-
primordials.SafeMap = makeSafe(
150-
Map,
151-
class SafeMap extends Map {
152-
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
153-
}
154-
);
155-
primordials.SafeWeakMap = makeSafe(
156-
WeakMap,
157-
class SafeWeakMap extends WeakMap {
158-
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
159-
}
160-
);
161-
primordials.SafeSet = makeSafe(
162-
Set,
163-
class SafeSet extends Set {
164-
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
165-
}
166-
);
167-
primordials.SafeWeakSet = makeSafe(
168-
WeakSet,
169-
class SafeWeakSet extends WeakSet {
170-
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
171-
}
172-
);
173-
17487
// Create copies of configurable value properties of the global object
17588
[
17689
'Proxy',
@@ -277,6 +190,41 @@ primordials.SafeWeakSet = makeSafe(
277190
copyPrototype(original.prototype, primordials, `${name}Prototype`);
278191
});
279192

193+
/* eslint-enable node-core/prefer-primordials */
194+
195+
const {
196+
ArrayPrototypeForEach,
197+
FunctionPrototypeCall,
198+
Map,
199+
ObjectFreeze,
200+
ObjectSetPrototypeOf,
201+
Set,
202+
SymbolIterator,
203+
WeakMap,
204+
WeakSet,
205+
} = primordials;
206+
207+
// Because these functions are used by `makeSafe`, which is exposed
208+
// on the `primordials` object, it's important to use const references
209+
// to the primordials that they use:
210+
const createSafeIterator = (factory, next) => {
211+
class SafeIterator {
212+
constructor(iterable) {
213+
this._iterator = factory(iterable);
214+
}
215+
next() {
216+
return next(this._iterator);
217+
}
218+
[SymbolIterator]() {
219+
return this;
220+
}
221+
}
222+
ObjectSetPrototypeOf(SafeIterator.prototype, null);
223+
ObjectFreeze(SafeIterator.prototype);
224+
ObjectFreeze(SafeIterator);
225+
return SafeIterator;
226+
};
227+
280228
primordials.SafeArrayIterator = createSafeIterator(
281229
primordials.ArrayPrototypeSymbolIterator,
282230
primordials.ArrayIteratorPrototypeNext
@@ -286,5 +234,80 @@ primordials.SafeStringIterator = createSafeIterator(
286234
primordials.StringIteratorPrototypeNext
287235
);
288236

289-
Object.setPrototypeOf(primordials, null);
290-
Object.freeze(primordials);
237+
const copyProps = (src, dest) => {
238+
ArrayPrototypeForEach(ReflectOwnKeys(src), (key) => {
239+
if (!ReflectGetOwnPropertyDescriptor(dest, key)) {
240+
ReflectDefineProperty(
241+
dest,
242+
key,
243+
ReflectGetOwnPropertyDescriptor(src, key));
244+
}
245+
});
246+
};
247+
248+
const makeSafe = (unsafe, safe) => {
249+
if (SymbolIterator in unsafe.prototype) {
250+
const dummy = new unsafe();
251+
let next; // We can reuse the same `next` method.
252+
253+
ArrayPrototypeForEach(ReflectOwnKeys(unsafe.prototype), (key) => {
254+
if (!ReflectGetOwnPropertyDescriptor(safe.prototype, key)) {
255+
const desc = ReflectGetOwnPropertyDescriptor(unsafe.prototype, key);
256+
if (
257+
typeof desc.value === 'function' &&
258+
desc.value.length === 0 &&
259+
SymbolIterator in (FunctionPrototypeCall(desc.value, dummy) ?? {})
260+
) {
261+
const createIterator = uncurryThis(desc.value);
262+
next = next ?? uncurryThis(createIterator(dummy).next);
263+
const SafeIterator = createSafeIterator(createIterator, next);
264+
desc.value = function() {
265+
return new SafeIterator(this);
266+
};
267+
}
268+
ReflectDefineProperty(safe.prototype, key, desc);
269+
}
270+
});
271+
} else {
272+
copyProps(unsafe.prototype, safe.prototype);
273+
}
274+
copyProps(unsafe, safe);
275+
276+
ObjectSetPrototypeOf(safe.prototype, null);
277+
ObjectFreeze(safe.prototype);
278+
ObjectFreeze(safe);
279+
return safe;
280+
};
281+
primordials.makeSafe = makeSafe;
282+
283+
// Subclass the constructors because we need to use their prototype
284+
// methods later.
285+
// Defining the `constructor` is necessary here to avoid the default
286+
// constructor which uses the user-mutable `%ArrayIteratorPrototype%.next`.
287+
primordials.SafeMap = makeSafe(
288+
Map,
289+
class SafeMap extends Map {
290+
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
291+
}
292+
);
293+
primordials.SafeWeakMap = makeSafe(
294+
WeakMap,
295+
class SafeWeakMap extends WeakMap {
296+
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
297+
}
298+
);
299+
primordials.SafeSet = makeSafe(
300+
Set,
301+
class SafeSet extends Set {
302+
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
303+
}
304+
);
305+
primordials.SafeWeakSet = makeSafe(
306+
WeakSet,
307+
class SafeWeakSet extends WeakSet {
308+
constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
309+
}
310+
);
311+
312+
ObjectSetPrototypeOf(primordials, null);
313+
ObjectFreeze(primordials);

0 commit comments

Comments
 (0)