• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2This file is copied from https://github.com/nodejs/node/blob/v14.19.3/lib/internal/per_context/primordials.js
3under the following license:
4
5Copyright Node.js contributors. All rights reserved.
6
7Permission is hereby granted, free of charge, to any person obtaining a copy
8of this software and associated documentation files (the "Software"), to
9deal in the Software without restriction, including without limitation the
10rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11sell copies of the Software, and to permit persons to whom the Software is
12furnished to do so, subject to the following conditions:
13
14The above copyright notice and this permission notice shall be included in
15all copies or substantial portions of the Software.
16
17THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
23IN THE SOFTWARE.
24*/
25
26'use strict';
27
28/* eslint-disable node-core/prefer-primordials */
29
30// This file subclasses and stores the JS builtins that come from the VM
31// so that Node.js's builtin modules do not need to later look these up from
32// the global proxy, which can be mutated by users.
33
34// Use of primordials have sometimes a dramatic impact on performance, please
35// benchmark all changes made in performance-sensitive areas of the codebase.
36// See: https://github.com/nodejs/node/pull/38248
37
38const primordials = {};
39
40const {
41  defineProperty: ReflectDefineProperty,
42  getOwnPropertyDescriptor: ReflectGetOwnPropertyDescriptor,
43  ownKeys: ReflectOwnKeys,
44} = Reflect;
45
46// `uncurryThis` is equivalent to `func => Function.prototype.call.bind(func)`.
47// It is using `bind.bind(call)` to avoid using `Function.prototype.bind`
48// and `Function.prototype.call` after it may have been mutated by users.
49const { apply, bind, call } = Function.prototype;
50const uncurryThis = bind.bind(call);
51primordials.uncurryThis = uncurryThis;
52
53// `applyBind` is equivalent to `func => Function.prototype.apply.bind(func)`.
54// It is using `bind.bind(apply)` to avoid using `Function.prototype.bind`
55// and `Function.prototype.apply` after it may have been mutated by users.
56const applyBind = bind.bind(apply);
57primordials.applyBind = applyBind;
58
59// Methods that accept a variable number of arguments, and thus it's useful to
60// also create `${prefix}${key}Apply`, which uses `Function.prototype.apply`,
61// instead of `Function.prototype.call`, and thus doesn't require iterator
62// destructuring.
63const varargsMethods = [
64  // 'ArrayPrototypeConcat' is omitted, because it performs the spread
65  // on its own for arrays and array-likes with a truthy
66  // @@isConcatSpreadable symbol property.
67  'ArrayOf',
68  'ArrayPrototypePush',
69  'ArrayPrototypeUnshift',
70  // 'FunctionPrototypeCall' is omitted, since there's 'ReflectApply'
71  // and 'FunctionPrototypeApply'.
72  'MathHypot',
73  'MathMax',
74  'MathMin',
75  'StringPrototypeConcat',
76  'TypedArrayOf',
77];
78
79function getNewKey(key) {
80  return typeof key === 'symbol' ?
81    `Symbol${key.description[7].toUpperCase()}${key.description.slice(8)}` :
82    `${key[0].toUpperCase()}${key.slice(1)}`;
83}
84
85function copyAccessor(dest, prefix, key, { enumerable, get, set }) {
86  ReflectDefineProperty(dest, `${prefix}Get${key}`, {
87    value: uncurryThis(get),
88    enumerable
89  });
90  if (set !== undefined) {
91    ReflectDefineProperty(dest, `${prefix}Set${key}`, {
92      value: uncurryThis(set),
93      enumerable
94    });
95  }
96}
97
98function copyPropsRenamed(src, dest, prefix) {
99  for (const key of ReflectOwnKeys(src)) {
100    const newKey = getNewKey(key);
101    const desc = ReflectGetOwnPropertyDescriptor(src, key);
102    if ('get' in desc) {
103      copyAccessor(dest, prefix, newKey, desc);
104    } else {
105      const name = `${prefix}${newKey}`;
106      ReflectDefineProperty(dest, name, desc);
107      if (varargsMethods.includes(name)) {
108        ReflectDefineProperty(dest, `${name}Apply`, {
109          // `src` is bound as the `this` so that the static `this` points
110          // to the object it was defined on,
111          // e.g.: `ArrayOfApply` gets a `this` of `Array`:
112          value: applyBind(desc.value, src),
113        });
114      }
115    }
116  }
117}
118
119function copyPropsRenamedBound(src, dest, prefix) {
120  for (const key of ReflectOwnKeys(src)) {
121    const newKey = getNewKey(key);
122    const desc = ReflectGetOwnPropertyDescriptor(src, key);
123    if ('get' in desc) {
124      copyAccessor(dest, prefix, newKey, desc);
125    } else {
126      const { value } = desc;
127      if (typeof value === 'function') {
128        desc.value = value.bind(src);
129      }
130
131      const name = `${prefix}${newKey}`;
132      ReflectDefineProperty(dest, name, desc);
133      if (varargsMethods.includes(name)) {
134        ReflectDefineProperty(dest, `${name}Apply`, {
135          value: applyBind(value, src),
136        });
137      }
138    }
139  }
140}
141
142function copyPrototype(src, dest, prefix) {
143  for (const key of ReflectOwnKeys(src)) {
144    const newKey = getNewKey(key);
145    const desc = ReflectGetOwnPropertyDescriptor(src, key);
146    if ('get' in desc) {
147      copyAccessor(dest, prefix, newKey, desc);
148    } else {
149      const { value } = desc;
150      if (typeof value === 'function') {
151        desc.value = uncurryThis(value);
152      }
153
154      const name = `${prefix}${newKey}`;
155      ReflectDefineProperty(dest, name, desc);
156      if (varargsMethods.includes(name)) {
157        ReflectDefineProperty(dest, `${name}Apply`, {
158          value: applyBind(value),
159        });
160      }
161    }
162  }
163}
164
165// Create copies of configurable value properties of the global object
166[
167  'Proxy',
168  'globalThis',
169].forEach((name) => {
170  // eslint-disable-next-line no-restricted-globals
171  primordials[name] = globalThis[name];
172});
173
174// Create copies of URI handling functions
175[
176  decodeURI,
177  decodeURIComponent,
178  encodeURI,
179  encodeURIComponent,
180].forEach((fn) => {
181  primordials[fn.name] = fn;
182});
183
184// Create copies of the namespace objects
185[
186  'JSON',
187  'Math',
188  'Proxy',
189  'Reflect',
190].forEach((name) => {
191  // eslint-disable-next-line no-restricted-globals
192  copyPropsRenamed(global[name], primordials, name);
193});
194
195// Create copies of intrinsic objects
196[
197  'Array',
198  'ArrayBuffer',
199  'BigInt',
200  'BigInt64Array',
201  'BigUint64Array',
202  'Boolean',
203  'DataView',
204  'Date',
205  'Error',
206  'EvalError',
207  'Float32Array',
208  'Float64Array',
209  'Function',
210  'Int16Array',
211  'Int32Array',
212  'Int8Array',
213  'Map',
214  'Number',
215  'Object',
216  'RangeError',
217  'ReferenceError',
218  'RegExp',
219  'Set',
220  'String',
221  'Symbol',
222  'SyntaxError',
223  'TypeError',
224  'URIError',
225  'Uint16Array',
226  'Uint32Array',
227  'Uint8Array',
228  'Uint8ClampedArray',
229  'WeakMap',
230  'WeakSet',
231].forEach((name) => {
232  // eslint-disable-next-line no-restricted-globals
233  const original = global[name];
234  primordials[name] = original;
235  copyPropsRenamed(original, primordials, name);
236  copyPrototype(original.prototype, primordials, `${name}Prototype`);
237});
238
239// Create copies of intrinsic objects that require a valid `this` to call
240// static methods.
241// Refs: https://www.ecma-international.org/ecma-262/#sec-promise.all
242[
243  'Promise',
244].forEach((name) => {
245  // eslint-disable-next-line no-restricted-globals
246  const original = global[name];
247  primordials[name] = original;
248  copyPropsRenamedBound(original, primordials, name);
249  copyPrototype(original.prototype, primordials, `${name}Prototype`);
250});
251
252// Create copies of abstract intrinsic objects that are not directly exposed
253// on the global object.
254// Refs: https://tc39.es/ecma262/#sec-%typedarray%-intrinsic-object
255[
256  { name: 'TypedArray', original: Reflect.getPrototypeOf(Uint8Array) },
257  { name: 'ArrayIterator', original: {
258    prototype: Reflect.getPrototypeOf(Array.prototype[Symbol.iterator]()),
259  } },
260  { name: 'StringIterator', original: {
261    prototype: Reflect.getPrototypeOf(String.prototype[Symbol.iterator]()),
262  } },
263].forEach(({ name, original }) => {
264  primordials[name] = original;
265  // The static %TypedArray% methods require a valid `this`, but can't be bound,
266  // as they need a subclass constructor as the receiver:
267  copyPrototype(original, primordials, name);
268  copyPrototype(original.prototype, primordials, `${name}Prototype`);
269});
270
271/* eslint-enable node-core/prefer-primordials */
272
273const {
274  ArrayPrototypeForEach,
275  FunctionPrototypeCall,
276  Map,
277  ObjectFreeze,
278  ObjectSetPrototypeOf,
279  Set,
280  SymbolIterator,
281  WeakMap,
282  WeakSet,
283} = primordials;
284
285// Because these functions are used by `makeSafe`, which is exposed
286// on the `primordials` object, it's important to use const references
287// to the primordials that they use:
288const createSafeIterator = (factory, next) => {
289  class SafeIterator {
290    constructor(iterable) {
291      this._iterator = factory(iterable);
292    }
293    next() {
294      return next(this._iterator);
295    }
296    [SymbolIterator]() {
297      return this;
298    }
299  }
300  ObjectSetPrototypeOf(SafeIterator.prototype, null);
301  ObjectFreeze(SafeIterator.prototype);
302  ObjectFreeze(SafeIterator);
303  return SafeIterator;
304};
305
306primordials.SafeArrayIterator = createSafeIterator(
307  primordials.ArrayPrototypeSymbolIterator,
308  primordials.ArrayIteratorPrototypeNext
309);
310primordials.SafeStringIterator = createSafeIterator(
311  primordials.StringPrototypeSymbolIterator,
312  primordials.StringIteratorPrototypeNext
313);
314
315const copyProps = (src, dest) => {
316  ArrayPrototypeForEach(ReflectOwnKeys(src), (key) => {
317    if (!ReflectGetOwnPropertyDescriptor(dest, key)) {
318      ReflectDefineProperty(
319        dest,
320        key,
321        ReflectGetOwnPropertyDescriptor(src, key));
322    }
323  });
324};
325
326const makeSafe = (unsafe, safe) => {
327  if (SymbolIterator in unsafe.prototype) {
328    const dummy = new unsafe();
329    let next; // We can reuse the same `next` method.
330
331    ArrayPrototypeForEach(ReflectOwnKeys(unsafe.prototype), (key) => {
332      if (!ReflectGetOwnPropertyDescriptor(safe.prototype, key)) {
333        const desc = ReflectGetOwnPropertyDescriptor(unsafe.prototype, key);
334        if (
335          typeof desc.value === 'function' &&
336          desc.value.length === 0 &&
337          SymbolIterator in (FunctionPrototypeCall(desc.value, dummy) ?? {})
338        ) {
339          const createIterator = uncurryThis(desc.value);
340          next = next ?? uncurryThis(createIterator(dummy).next);
341          const SafeIterator = createSafeIterator(createIterator, next);
342          desc.value = function() {
343            return new SafeIterator(this);
344          };
345        }
346        ReflectDefineProperty(safe.prototype, key, desc);
347      }
348    });
349  } else {
350    copyProps(unsafe.prototype, safe.prototype);
351  }
352  copyProps(unsafe, safe);
353
354  ObjectSetPrototypeOf(safe.prototype, null);
355  ObjectFreeze(safe.prototype);
356  ObjectFreeze(safe);
357  return safe;
358};
359primordials.makeSafe = makeSafe;
360
361// Subclass the constructors because we need to use their prototype
362// methods later.
363// Defining the `constructor` is necessary here to avoid the default
364// constructor which uses the user-mutable `%ArrayIteratorPrototype%.next`.
365primordials.SafeMap = makeSafe(
366  Map,
367  class SafeMap extends Map {
368    constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
369  }
370);
371primordials.SafeWeakMap = makeSafe(
372  WeakMap,
373  class SafeWeakMap extends WeakMap {
374    constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
375  }
376);
377primordials.SafeSet = makeSafe(
378  Set,
379  class SafeSet extends Set {
380    constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
381  }
382);
383primordials.SafeWeakSet = makeSafe(
384  WeakSet,
385  class SafeWeakSet extends WeakSet {
386    constructor(i) { super(i); } // eslint-disable-line no-useless-constructor
387  }
388);
389
390ObjectSetPrototypeOf(primordials, null);
391ObjectFreeze(primordials);
392
393module.exports = primordials;
394