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