UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

448 lines (396 loc) 13.6 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/internal/freeze_intrinsics.js import * as __hoisted_internal_console_constructor__ from "nstdlib/lib/internal/console/constructor"; import * as __hoisted_internal_console_global__ from "nstdlib/lib/internal/console/global"; import * as __hoisted_timers__ from "nstdlib/lib/timers"; // Adapted from SES/Caja - Copyright (C) 2011 Google Inc. // Copyright (C) 2018 Agoric // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // SPDX-License-Identifier: Apache-2.0 // Based upon: // https://github.com/google/caja/blob/HEAD/src/com/google/caja/ses/startSES.js // https://github.com/google/caja/blob/HEAD/src/com/google/caja/ses/repairES5.js // https://github.com/tc39/proposal-ses/blob/e5271cc42a257a05dcae2fd94713ed2f46c08620/shim/src/freeze.js const { Intl, SharedArrayBuffer, WebAssembly } = globalThis; export default function () { const { Console } = __hoisted_internal_console_constructor__; const console = __hoisted_internal_console_global__; const { clearImmediate, clearInterval, clearTimeout, setImmediate, setInterval, setTimeout, } = __hoisted_timers__; const intrinsicPrototypes = [ // 20 Fundamental Objects ObjectPrototype, // 20.1 Function.prototype, // 20.2 BooleanPrototype, // 20.3 SymbolPrototype, // 20.4 ErrorPrototype, // 20.5 AggregateErrorPrototype, EvalErrorPrototype, RangeErrorPrototype, ReferenceErrorPrototype, SyntaxErrorPrototype, TypeErrorPrototype, URIErrorPrototype, // 21 Numbers and Dates NumberPrototype, // 21.1 BigIntPrototype, // 21.2 DatePrototype, // 21.4 // 22 Text Processing StringPrototype, // 22.1 StringIteratorPrototype, // 22.1.5 RegExpPrototype, // 22.2 // 22.2.7 RegExpStringIteratorPrototype Object.getPrototypeOf(/e/[Symbol.matchAll]()), // 23 Indexed Collections ArrayPrototype, // 23.1 ArrayIteratorPrototype, // 23.1.5 TypedArrayPrototype, // 23.2 Int8ArrayPrototype, Uint8Array.prototype.call, Uint8ClampedArrayPrototype, Int16ArrayPrototype, Uint16ArrayPrototype, Int32ArrayPrototype, Uint32ArrayPrototype, Float32ArrayPrototype, Float64ArrayPrototype, BigInt64ArrayPrototype, BigUint64ArrayPrototype, // 24 Keyed Collections MapPrototype, // 24.1 // 24.1.5 MapIteratorPrototype Object.getPrototypeOf(new Map()[Symbol.iterator]()), SetPrototype, // 24.2 // 24.2.5 SetIteratorPrototype Object.getPrototypeOf(new Set()[Symbol.iterator]()), WeakMapPrototype, // 24.3 WeakSetPrototype, // 24.4 // 25 Structured Data ArrayBufferPrototype, // 25.1 DataViewPrototype, // 25.3 // 26 Managing Memory WeakRefPrototype, // 26.1 FinalizationRegistryPrototype, // 26.2 // 27 Control Abstraction Objects // 27.1 Iteration IteratorPrototype, // 27.1.2 IteratorPrototype // 27.1.3 AsyncIteratorPrototype Object.getPrototypeOf( Object.getPrototypeOf(Object.getPrototypeOf((async function* () {})())), ), PromisePrototype, // 27.2 // Other APIs / Web Compatibility Console.prototype, ]; const intrinsics = [ // 10.2.4.1 ThrowTypeError Object.getOwnPropertyDescriptor(Function.prototype, "caller").get, // 19 The Global Object // 19.2 Function Properties of the Global Object // eslint-disable-next-line node-core/prefer-primordials eval, // eslint-disable-next-line node-core/prefer-primordials isFinite, // eslint-disable-next-line node-core/prefer-primordials isNaN, // eslint-disable-next-line node-core/prefer-primordials parseFloat, // eslint-disable-next-line node-core/prefer-primordials parseInt, // 19.2.6 URI Handling Functions decodeURI, decodeURIComponent, encodeURI, encodeURIComponent, // 20 Fundamental Objects Object, // 20.1 Function, // 20.2 Boolean, // 20.3 Symbol, // 20.4 Error, // 20.5 AggregateError, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError, // 21 Numbers and Dates Number, // 21.1 BigInt, // 21.2 // eslint-disable-next-line node-core/prefer-primordials Math, // 21.3 Date, // 21.4 // 22 Text Processing String, // 22.1 StringIteratorPrototype, // 22.1.5 RegExp, // 22.2 // 22.2.7 RegExpStringIteratorPrototype Object.getPrototypeOf(/e/[Symbol.matchAll]()), // 23 Indexed Collections Array, // 23.1 ArrayIteratorPrototype, // 23.1.5 // 23.2 TypedArray TypedArray, Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array, BigInt64Array, BigUint64Array, // 24 Keyed Collections Map, // 24.1 // 24.1.5 MapIteratorPrototype Object.getPrototypeOf(new Map()[Symbol.iterator]()), Set, // 24.2 // 24.2.5 SetIteratorPrototype Object.getPrototypeOf(new Set()[Symbol.iterator]()), WeakMap, // 24.3 WeakSet, // 24.4 // 25 Structured Data ArrayBuffer, // 25.1 DataView, // 25.3 Atomics, // 25.4 // eslint-disable-next-line node-core/prefer-primordials JSON, // 25.5 // 26 Managing Memory WeakRef, // 26.1 FinalizationRegistry, // 26.2 // 27 Control Abstraction Objects // 27.1 Iteration Object.getPrototypeOf(ArrayIteratorPrototype), // 27.1.2 IteratorPrototype // 27.1.3 AsyncIteratorPrototype Object.getPrototypeOf( Object.getPrototypeOf(Object.getPrototypeOf((async function* () {})())), ), Promise, // 27.2 // 27.3 GeneratorFunction Object.getPrototypeOf(function* () {}), // 27.4 AsyncGeneratorFunction Object.getPrototypeOf(async function* () {}), // 27.7 AsyncFunction Object.getPrototypeOf(async function () {}), // 28 Reflection // eslint-disable-next-line node-core/prefer-primordials Reflect, // 28.1 Proxy, // 28.2 // B.2.1 escape, unescape, // Other APIs / Web Compatibility clearImmediate, clearInterval, clearTimeout, setImmediate, setInterval, setTimeout, console, ]; if (typeof SharedArrayBuffer !== "undefined") { // 25.2 Array.prototype.push.call(intrinsicPrototypes, SharedArrayBuffer.prototype); Array.prototype.push.call(intrinsics, SharedArrayBuffer); } if (typeof WebAssembly !== "undefined") { Array.prototype.push.call( intrinsicPrototypes, WebAssembly.Module.prototype, WebAssembly.Instance.prototype, WebAssembly.Table.prototype, WebAssembly.Memory.prototype, WebAssembly.CompileError.prototype, WebAssembly.LinkError.prototype, WebAssembly.RuntimeError.prototype, ); Array.prototype.push.call(intrinsics, WebAssembly); } if (typeof Intl !== "undefined") { Array.prototype.push.call( intrinsicPrototypes, Intl.Collator.prototype, Intl.DateTimeFormat.prototype, Intl.ListFormat.prototype, Intl.NumberFormat.prototype, Intl.PluralRules.prototype, Intl.RelativeTimeFormat.prototype, ); Array.prototype.push.call(intrinsics, Intl); } Array.prototype.forEach.call(intrinsicPrototypes, enableDerivedOverrides); const frozenSet = new WeakSet(); Array.prototype.forEach.call(intrinsics, deepFreeze); // 19.1 Value Properties of the Global Object Object.defineProperty(globalThis, "globalThis", { __proto__: null, configurable: false, writable: false, value: globalThis, }); // Objects that are deeply frozen. function deepFreeze(root) { /** * "innerDeepFreeze()" acts like "Object.freeze()", except that: * * To deepFreeze an object is to freeze it and all objects transitively * reachable from it via transitive reflective property and prototype * traversal. */ function innerDeepFreeze(node) { // Objects that we have frozen in this round. const freezingSet = new Set(); // If val is something we should be freezing but aren't yet, // add it to freezingSet. function enqueue(val) { if (Object(val) !== val) { // ignore primitives return; } const type = typeof val; if (type !== "object" && type !== "function") { // NB: handle for any new cases in future } if (frozenSet.has(val) || freezingSet.has(val)) { // TODO: Use uncurried form // Ignore if already frozen or freezing return; } freezingSet.add(val); // TODO: Use uncurried form } function doFreeze(obj) { // Immediately freeze the object to ensure reactive // objects such as proxies won't add properties // during traversal, before they get frozen. // Object are verified before being enqueued, // therefore this is a valid candidate. // Throws if this fails (strict mode). Object.freeze(obj); // We rely upon certain commitments of Object.freeze and proxies here // Get stable/immutable outbound links before a Proxy has a chance to do // something sneaky. const proto = Object.getPrototypeOf(obj); const descs = Object.getOwnPropertyDescriptors(obj); enqueue(proto); Array.prototype.forEach.call(Reflect.ownKeys(descs), (name) => { const desc = descs[name]; if (Object.prototype.hasOwnProperty.call(desc, "value")) { // todo uncurried form enqueue(desc.value); } else { enqueue(desc.get); enqueue(desc.set); } }); } function dequeue() { // New values added before forEach() has finished will be visited. freezingSet.forEach(doFreeze); // TODO: Curried forEach } function commit() { // TODO: Curried forEach // We capture the real WeakSet.prototype.add above, in case someone // changes it. The two-argument form of forEach passes the second // argument as the 'this' binding, so we add to the correct set. freezingSet.forEach(frozenSet.add, frozenSet); } enqueue(node); dequeue(); commit(); } innerDeepFreeze(root); return root; } /** * For a special set of properties (defined below), it ensures that the * effect of freezing does not suppress the ability to override these * properties on derived objects by simple assignment. * * Because of lack of sufficient foresight at the time, ES5 unfortunately * specified that a simple assignment to a non-existent property must fail if * it would override a non-writable data property of the same name. (In * retrospect, this was a mistake, but it is now too late and we must live * with the consequences.) As a result, simply freezing an object to make it * tamper proof has the unfortunate side effect of breaking previously correct * code that is considered to have followed JS best practices, if this * previous code used assignment to override. * * To work around this mistake, deepFreeze(), prior to freezing, replaces * selected configurable own data properties with accessor properties which * simulate what we should have specified -- that assignments to derived * objects succeed if otherwise possible. */ function enableDerivedOverride(obj, prop, desc) { if ( Object.prototype.hasOwnProperty.call(desc, "value") && desc.configurable ) { const value = desc.value; function getter() { return value; } // Re-attach the data property on the object so // it can be found by the deep-freeze traversal process. getter.value = value; function setter(newValue) { if (obj === this) { // eslint-disable-next-line no-restricted-syntax throw new TypeError( `Cannot assign to read only property '${prop}' of object '${obj}'`, ); } if (Object.prototype.hasOwnProperty.call(this, prop)) { this[prop] = newValue; } else { Object.defineProperty(this, prop, { __proto__: null, value: newValue, writable: true, enumerable: true, configurable: true, }); } } Object.defineProperty(obj, prop, { __proto__: null, get: getter, set: setter, enumerable: desc.enumerable, configurable: desc.configurable, }); } } function enableDerivedOverrides(obj) { if (!obj) { return; } const descs = Object.getOwnPropertyDescriptors(obj); if (!descs) { return; } Array.prototype.forEach.call(Object.getOwnPropertyNames(obj), (prop) => { return enableDerivedOverride(obj, prop, descs[prop]); }); Array.prototype.forEach.call(Object.getOwnPropertySymbols(obj), (prop) => { return enableDerivedOverride(obj, prop, descs[prop]); }); } }