primordials.js 12 KB

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