diff --git a/app/store/index.ts b/app/store/index.ts index 6f818dc1929..d89bd9d59fe 100644 --- a/app/store/index.ts +++ b/app/store/index.ts @@ -43,6 +43,21 @@ const createStoreAndPersistor = async () => { * Initialize services after persist is completed */ const onPersistComplete = () => { + /** + * EngineService.initalizeEngine(store) with SES/lockdown: + * Requires ethjs nested patches (lib->src) + * - ethjs/ethjs-query + * - ethjs/ethjs-contract + * Otherwise causing the following errors: + * - TypeError: Cannot assign to read only property 'constructor' of object '[object Object]' + * - Error: Requiring module "node_modules/ethjs/node_modules/ethjs-query/lib/index.js", which threw an exception: TypeError: + * - V8: Cannot assign to read only property 'constructor' of object '[object Object]' + * - JSC: Attempted to assign to readonly property + * - node_modules/babel-runtime/node_modules/regenerator-runtime/runtime.js + * - V8: TypeError: _$$_REQUIRE(...) is not a constructor + * - TypeError: undefined is not an object (evaluating 'TokenListController.tokenList') + * - V8: SES_UNHANDLED_REJECTION + */ EngineService.initalizeEngine(store); Authentication.init(store); LockManagerService.init(store); diff --git a/babel.config.js b/babel.config.js index d6091806d60..1712d8b9cb3 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,5 +1,6 @@ // eslint-disable-next-line import/no-commonjs module.exports = { + ignore: [/ses\.cjs/], presets: ['module:metro-react-native-babel-preset'], plugins: [ 'transform-inline-environment-variables', diff --git a/index.js b/index.js index ed3ea77cf77..e69508d3732 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,14 @@ +// Importing SES (0.18.7+) here then calling lockdown causes: +// https://github.com/LavaMoat/docs/issues/24 + +// Importing the SES (0.18.7) lockdown shim here then calling lockdown causes: +// https://github.com/LavaMoat/docs/issues/27 + +import './shim.js'; + // Needed to polyfill random number generation. import 'react-native-get-random-values'; import '@walletconnect/react-native-compat'; -import './shim.js'; import 'react-native-gesture-handler'; import 'react-native-url-polyfill/auto'; diff --git a/patches/ethjs++ethjs-contract+0.2.2.patch b/patches/ethjs++ethjs-contract+0.2.2.patch new file mode 100644 index 00000000000..2dba8b31c38 --- /dev/null +++ b/patches/ethjs++ethjs-contract+0.2.2.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/ethjs/node_modules/ethjs-contract/package.json b/node_modules/ethjs/node_modules/ethjs-contract/package.json +index 8e58ff5..470d00b 100644 +--- a/node_modules/ethjs/node_modules/ethjs-contract/package.json ++++ b/node_modules/ethjs/node_modules/ethjs-contract/package.json +@@ -197,7 +197,7 @@ + "lint-staged": { + "lint:eslint": "*.js" + }, +- "main": "lib/index.js", ++ "main": "src/index.js", + "name": "ethjs-contract", + "pre-commit": "build", + "repository": { diff --git a/patches/ethjs++ethjs-query+0.3.7.patch b/patches/ethjs++ethjs-query+0.3.7.patch new file mode 100644 index 00000000000..ab7492e02af --- /dev/null +++ b/patches/ethjs++ethjs-query+0.3.7.patch @@ -0,0 +1,13 @@ +diff --git a/node_modules/ethjs/node_modules/ethjs-query/package.json b/node_modules/ethjs/node_modules/ethjs-query/package.json +index fb82d51..503159c 100644 +--- a/node_modules/ethjs/node_modules/ethjs-query/package.json ++++ b/node_modules/ethjs/node_modules/ethjs-query/package.json +@@ -2,7 +2,7 @@ + "name": "ethjs-query", + "version": "0.3.7", + "description": "A simple query layer for the Ethereum RPC.", +- "main": "lib/index.js", ++ "main": "src/index.js", + "files": [ + "dist", + "internals", diff --git a/patches/react-native+0.71.14.patch b/patches/react-native+0.71.14.patch index 40c761e4b03..2fda90f1af9 100644 --- a/patches/react-native+0.71.14.patch +++ b/patches/react-native+0.71.14.patch @@ -1,3 +1,61 @@ +diff --git a/node_modules/react-native/Libraries/Core/InitializeCore.js b/node_modules/react-native/Libraries/Core/InitializeCore.js +index 1379ffd..340f48d 100644 +--- a/node_modules/react-native/Libraries/Core/InitializeCore.js ++++ b/node_modules/react-native/Libraries/Core/InitializeCore.js +@@ -24,26 +24,51 @@ + + 'use strict'; + ++const Platform = require('../Utilities/Platform'); ++ ++if (Platform.OS === 'ios' && !global?.HermesInternal) { ++ require('../../../../ses.cjs'); // ses@0.18.8 ++ /** ++ * Without consoleTaming: 'unsafe' causes: ++ * - Attempting to define property on object that is not extensible. ++ * Without errorTrapping 'none' causes: ++ * - TypeError: undefined is not a function (near '...globalThis.process.on...') ++ * Without unhandledRejectionTrapping 'none' causes: ++ * - TypeError: globalThis.process.on is not a function. (In 'globalThis.process.on('unhandledRejection', h.unhandledRejectionHandler)', 'globalThis.process.on' is undefined) ++ * overrideTaming 'severe' is ideal (default override?) ++ * Nb: global.process is only partially shimmed, which confuses SES ++ * Nb: All are Unhandled JS Exceptions, since we call lockdown before setUpErrorHandling ++ */ ++ repairIntrinsics({ consoleTaming: 'unsafe', errorTrapping: 'none', unhandledRejectionTrapping: 'none', overrideTaming: 'severe' }); ++ require('reflect-metadata'); // Vetted shim required to fix: https://github.com/LavaMoat/docs/issues/26 ++ hardenIntrinsics(); ++} ++ + const start = Date.now(); + + require('./setUpGlobals'); ++// require('./setUpDOM'); Introduced in RN v0.72, ensure included when upgrading patch + require('./setUpPerformance'); + require('./setUpErrorHandling'); ++ + require('./polyfillPromise'); ++ + require('./setUpRegeneratorRuntime'); ++ + require('./setUpTimers'); + require('./setUpXHR'); ++ + require('./setUpAlert'); + require('./setUpNavigator'); + require('./setUpBatchedBridge'); + require('./setUpSegmentFetcher'); + if (__DEV__) { + require('./checkNativeVersion'); +- require('./setUpDeveloperTools'); ++ require('./setUpDeveloperTools'); // console.log calls visible in Metro from here + require('../LogBox/LogBox').install(); + } + +-require('../ReactNative/AppRegistry'); ++require('../ReactNative/AppRegistry'); // reflect-metadata imported after here causes: https://github.com/LavaMoat/docs/issues/26 + + const GlobalPerformanceLogger = require('../Utilities/GlobalPerformanceLogger'); + // We could just call GlobalPerformanceLogger.markPoint at the top of the file, diff --git a/node_modules/react-native/ReactAndroid/build.gradle b/node_modules/react-native/ReactAndroid/build.gradle index 155cb59..053550c 100644 --- a/node_modules/react-native/ReactAndroid/build.gradle diff --git a/ses.cjs b/ses.cjs new file mode 100644 index 00000000000..8d4a18782a7 --- /dev/null +++ b/ses.cjs @@ -0,0 +1,11183 @@ +'use strict'; +(() => { + const functors = [ +// === functors[0] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([]); /* global globalThis */ +/* eslint-disable no-restricted-globals */ + +/** + * commons.js + * Declare shorthand functions. Sharing these declarations across modules + * improves on consistency and minification. Unused declarations are + * dropped by the tree shaking process. + * + * We capture these, not just for brevity, but for security. If any code + * modifies Object to change what 'assign' points to, the Compartment shim + * would be corrupted. + */ + +// We cannot use globalThis as the local name since it would capture the +// lexical name. +const universalThis= globalThis;$h‍_once.universalThis(universalThis); + + +const { + Array, + Date, + FinalizationRegistry, + Float32Array, + JSON, + Map, + Math, + Number, + Object, + Promise, + Proxy, + Reflect, + RegExp: FERAL_REG_EXP, + Set, + String, + Symbol, + WeakMap, + WeakSet}= + globalThis;$h‍_once.Array(Array);$h‍_once.Date(Date);$h‍_once.FinalizationRegistry(FinalizationRegistry);$h‍_once.Float32Array(Float32Array);$h‍_once.JSON(JSON);$h‍_once.Map(Map);$h‍_once.Math(Math);$h‍_once.Number(Number);$h‍_once.Object(Object);$h‍_once.Promise(Promise);$h‍_once.Proxy(Proxy);$h‍_once.Reflect(Reflect);$h‍_once.FERAL_REG_EXP(FERAL_REG_EXP);$h‍_once.Set(Set);$h‍_once.String(String);$h‍_once.Symbol(Symbol);$h‍_once.WeakMap(WeakMap);$h‍_once.WeakSet(WeakSet); + +const { + // The feral Error constructor is safe for internal use, but must not be + // revealed to post-lockdown code in any compartment including the start + // compartment since in V8 at least it bears stack inspection capabilities. + Error: FERAL_ERROR, + RangeError, + ReferenceError, + SyntaxError, + TypeError}= + globalThis;$h‍_once.FERAL_ERROR(FERAL_ERROR);$h‍_once.RangeError(RangeError);$h‍_once.ReferenceError(ReferenceError);$h‍_once.SyntaxError(SyntaxError);$h‍_once.TypeError(TypeError); + +const { + assign, + create, + defineProperties, + entries, + freeze, + getOwnPropertyDescriptor, + getOwnPropertyDescriptors, + getOwnPropertyNames, + getPrototypeOf, + is, + isFrozen, + isSealed, + isExtensible, + keys, + prototype: objectPrototype, + seal, + preventExtensions, + setPrototypeOf, + values, + fromEntries}= + Object;$h‍_once.assign(assign);$h‍_once.create(create);$h‍_once.defineProperties(defineProperties);$h‍_once.entries(entries);$h‍_once.freeze(freeze);$h‍_once.getOwnPropertyDescriptor(getOwnPropertyDescriptor);$h‍_once.getOwnPropertyDescriptors(getOwnPropertyDescriptors);$h‍_once.getOwnPropertyNames(getOwnPropertyNames);$h‍_once.getPrototypeOf(getPrototypeOf);$h‍_once.is(is);$h‍_once.isFrozen(isFrozen);$h‍_once.isSealed(isSealed);$h‍_once.isExtensible(isExtensible);$h‍_once.keys(keys);$h‍_once.objectPrototype(objectPrototype);$h‍_once.seal(seal);$h‍_once.preventExtensions(preventExtensions);$h‍_once.setPrototypeOf(setPrototypeOf);$h‍_once.values(values);$h‍_once.fromEntries(fromEntries); + +const { + species: speciesSymbol, + toStringTag: toStringTagSymbol, + iterator: iteratorSymbol, + matchAll: matchAllSymbol, + unscopables: unscopablesSymbol, + keyFor: symbolKeyFor, + for: symbolFor}= + Symbol;$h‍_once.speciesSymbol(speciesSymbol);$h‍_once.toStringTagSymbol(toStringTagSymbol);$h‍_once.iteratorSymbol(iteratorSymbol);$h‍_once.matchAllSymbol(matchAllSymbol);$h‍_once.unscopablesSymbol(unscopablesSymbol);$h‍_once.symbolKeyFor(symbolKeyFor);$h‍_once.symbolFor(symbolFor); + +const { isInteger}= Number;$h‍_once.isInteger(isInteger); + +const { stringify: stringifyJson}= JSON; + +// Needed only for the Safari bug workaround below +$h‍_once.stringifyJson(stringifyJson);const{defineProperty:originalDefineProperty}=Object; + +const defineProperty= (object, prop, descriptor)=> { + // We used to do the following, until we had to reopen Safari bug + // https://bugs.webkit.org/show_bug.cgi?id=222538#c17 + // Once this is fixed, we may restore it. + // // Object.defineProperty is allowed to fail silently so we use + // // Object.defineProperties instead. + // return defineProperties(object, { [prop]: descriptor }); + + // Instead, to workaround the Safari bug + const result= originalDefineProperty(object, prop, descriptor); + if( result!== object) { + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_DEFINE_PROPERTY_FAILED_SILENTLY.md + throw TypeError( + `Please report that the original defineProperty silently failed to set ${stringifyJson( + String(prop)) + }. (SES_DEFINE_PROPERTY_FAILED_SILENTLY)`); + + } + return result; + };$h‍_once.defineProperty(defineProperty); + +const { + apply, + construct, + get: reflectGet, + getOwnPropertyDescriptor: reflectGetOwnPropertyDescriptor, + has: reflectHas, + isExtensible: reflectIsExtensible, + ownKeys, + preventExtensions: reflectPreventExtensions, + set: reflectSet}= + Reflect;$h‍_once.apply(apply);$h‍_once.construct(construct);$h‍_once.reflectGet(reflectGet);$h‍_once.reflectGetOwnPropertyDescriptor(reflectGetOwnPropertyDescriptor);$h‍_once.reflectHas(reflectHas);$h‍_once.reflectIsExtensible(reflectIsExtensible);$h‍_once.ownKeys(ownKeys);$h‍_once.reflectPreventExtensions(reflectPreventExtensions);$h‍_once.reflectSet(reflectSet); + +const { isArray, prototype: arrayPrototype}= Array;$h‍_once.isArray(isArray);$h‍_once.arrayPrototype(arrayPrototype); +const { prototype: mapPrototype}= Map;$h‍_once.mapPrototype(mapPrototype); +const { revocable: proxyRevocable}= Proxy;$h‍_once.proxyRevocable(proxyRevocable); +const { prototype: regexpPrototype}= RegExp;$h‍_once.regexpPrototype(regexpPrototype); +const { prototype: setPrototype}= Set;$h‍_once.setPrototype(setPrototype); +const { prototype: stringPrototype}= String;$h‍_once.stringPrototype(stringPrototype); +const { prototype: weakmapPrototype}= WeakMap;$h‍_once.weakmapPrototype(weakmapPrototype); +const { prototype: weaksetPrototype}= WeakSet;$h‍_once.weaksetPrototype(weaksetPrototype); +const { prototype: functionPrototype}= Function;$h‍_once.functionPrototype(functionPrototype); +const { prototype: promisePrototype}= Promise;$h‍_once.promisePrototype(promisePrototype); + +const typedArrayPrototype= getPrototypeOf(Uint8Array.prototype);$h‍_once.typedArrayPrototype(typedArrayPrototype); + +const { bind}= functionPrototype; + +/** + * uncurryThis() + * Equivalent of: fn => (thisArg, ...args) => apply(fn, thisArg, args) + * + * See those reference for a complete explanation: + * http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming + * which only lives at + * http://web.archive.org/web/20160805225710/http://wiki.ecmascript.org/doku.php?id=conventions:safe_meta_programming + * + * @type { any>(fn: F) => ((thisArg: ThisParameterType, ...args: Parameters) => ReturnType)} + */ +const uncurryThis= bind.bind(bind.call); // eslint-disable-line @endo/no-polymorphic-call +$h‍_once.uncurryThis(uncurryThis); +const objectHasOwnProperty= uncurryThis(objectPrototype.hasOwnProperty); +// +$h‍_once.objectHasOwnProperty(objectHasOwnProperty);const arrayFilter=uncurryThis(arrayPrototype.filter);$h‍_once.arrayFilter(arrayFilter); +const arrayForEach= uncurryThis(arrayPrototype.forEach);$h‍_once.arrayForEach(arrayForEach); +const arrayIncludes= uncurryThis(arrayPrototype.includes);$h‍_once.arrayIncludes(arrayIncludes); +const arrayJoin= uncurryThis(arrayPrototype.join); +/** @type {(thisArg: readonly T[], callbackfn: (value: T, index: number, array: T[]) => U, cbThisArg?: any) => U[]} */$h‍_once.arrayJoin(arrayJoin); +const arrayMap= /** @type {any} */ uncurryThis(arrayPrototype.map);$h‍_once.arrayMap(arrayMap); +const arrayPop= uncurryThis(arrayPrototype.pop); +/** @type {(thisArg: T[], ...items: T[]) => number} */$h‍_once.arrayPop(arrayPop); +const arrayPush= uncurryThis(arrayPrototype.push);$h‍_once.arrayPush(arrayPush); +const arraySlice= uncurryThis(arrayPrototype.slice);$h‍_once.arraySlice(arraySlice); +const arraySome= uncurryThis(arrayPrototype.some);$h‍_once.arraySome(arraySome); +const arraySort= uncurryThis(arrayPrototype.sort);$h‍_once.arraySort(arraySort); +const iterateArray= uncurryThis(arrayPrototype[iteratorSymbol]); +// +$h‍_once.iterateArray(iterateArray);const mapSet=uncurryThis(mapPrototype.set);$h‍_once.mapSet(mapSet); +const mapGet= uncurryThis(mapPrototype.get);$h‍_once.mapGet(mapGet); +const mapHas= uncurryThis(mapPrototype.has);$h‍_once.mapHas(mapHas); +const mapDelete= uncurryThis(mapPrototype.delete);$h‍_once.mapDelete(mapDelete); +const mapEntries= uncurryThis(mapPrototype.entries);$h‍_once.mapEntries(mapEntries); +const iterateMap= uncurryThis(mapPrototype[iteratorSymbol]); +// +$h‍_once.iterateMap(iterateMap);const setAdd=uncurryThis(setPrototype.add);$h‍_once.setAdd(setAdd); +const setDelete= uncurryThis(setPrototype.delete);$h‍_once.setDelete(setDelete); +const setForEach= uncurryThis(setPrototype.forEach);$h‍_once.setForEach(setForEach); +const setHas= uncurryThis(setPrototype.has);$h‍_once.setHas(setHas); +const iterateSet= uncurryThis(setPrototype[iteratorSymbol]); +// +$h‍_once.iterateSet(iterateSet);const regexpTest=uncurryThis(regexpPrototype.test);$h‍_once.regexpTest(regexpTest); +const regexpExec= uncurryThis(regexpPrototype.exec);$h‍_once.regexpExec(regexpExec); +const matchAllRegExp= uncurryThis(regexpPrototype[matchAllSymbol]); +// +$h‍_once.matchAllRegExp(matchAllRegExp);const stringEndsWith=uncurryThis(stringPrototype.endsWith);$h‍_once.stringEndsWith(stringEndsWith); +const stringIncludes= uncurryThis(stringPrototype.includes);$h‍_once.stringIncludes(stringIncludes); +const stringIndexOf= uncurryThis(stringPrototype.indexOf);$h‍_once.stringIndexOf(stringIndexOf); +const stringMatch= uncurryThis(stringPrototype.match); +/** + * @type { & + * ((thisArg: string, searchValue: { [Symbol.replace](string: string, replaceValue: string): string; }, replaceValue: string) => string) & + * ((thisArg: string, searchValue: { [Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string; }, replacer: (substring: string, ...args: any[]) => string) => string) + * } + */$h‍_once.stringMatch(stringMatch); +const stringReplace= /** @type {any} */ + uncurryThis(stringPrototype.replace);$h‍_once.stringReplace(stringReplace); + +const stringSearch= uncurryThis(stringPrototype.search);$h‍_once.stringSearch(stringSearch); +const stringSlice= uncurryThis(stringPrototype.slice); +/** @type {(thisArg: string, splitter: string | RegExp | { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number) => string[]} */$h‍_once.stringSlice(stringSlice); +const stringSplit= uncurryThis(stringPrototype.split);$h‍_once.stringSplit(stringSplit); +const stringStartsWith= uncurryThis(stringPrototype.startsWith);$h‍_once.stringStartsWith(stringStartsWith); +const iterateString= uncurryThis(stringPrototype[iteratorSymbol]); +// +$h‍_once.iterateString(iterateString);const weakmapDelete=uncurryThis(weakmapPrototype.delete); +/** @type {(thisArg: WeakMap, ...args: Parameters['get']>) => ReturnType['get']>} */$h‍_once.weakmapDelete(weakmapDelete); +const weakmapGet= uncurryThis(weakmapPrototype.get);$h‍_once.weakmapGet(weakmapGet); +const weakmapHas= uncurryThis(weakmapPrototype.has);$h‍_once.weakmapHas(weakmapHas); +const weakmapSet= uncurryThis(weakmapPrototype.set); +// +$h‍_once.weakmapSet(weakmapSet);const weaksetAdd=uncurryThis(weaksetPrototype.add);$h‍_once.weaksetAdd(weaksetAdd); +const weaksetHas= uncurryThis(weaksetPrototype.has); +// +$h‍_once.weaksetHas(weaksetHas);const functionToString=uncurryThis(functionPrototype.toString); +// +$h‍_once.functionToString(functionToString);const{all}=Promise; +const promiseAll= (promises)=>apply(all, Promise, [promises]);$h‍_once.promiseAll(promiseAll); +const promiseCatch= uncurryThis(promisePrototype.catch); +/** @type {(thisArg: T, onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null) => Promise} */$h‍_once.promiseCatch(promiseCatch); +const promiseThen= /** @type {any} */ + uncurryThis(promisePrototype.then); + +// +$h‍_once.promiseThen(promiseThen);const finalizationRegistryRegister= + FinalizationRegistry&& uncurryThis(FinalizationRegistry.prototype.register);$h‍_once.finalizationRegistryRegister(finalizationRegistryRegister); +const finalizationRegistryUnregister= + FinalizationRegistry&& + uncurryThis(FinalizationRegistry.prototype.unregister); + +/** + * getConstructorOf() + * Return the constructor from an instance. + * + * @param {Function} fn + */$h‍_once.finalizationRegistryUnregister(finalizationRegistryUnregister); +const getConstructorOf= (fn)=> + reflectGet(getPrototypeOf(fn), 'constructor'); + +/** + * immutableObject + * An immutable (frozen) empty object that is safe to share. + */$h‍_once.getConstructorOf(getConstructorOf); +const immutableObject= freeze(create(null)); + +/** + * isObject tests whether a value is an object. + * Today, this is equivalent to: + * + * const isObject = value => { + * if (value === null) return false; + * const type = typeof value; + * return type === 'object' || type === 'function'; + * }; + * + * But this is not safe in the face of possible evolution of the language, for + * example new types or semantics of records and tuples. + * We use this implementation despite the unnecessary allocation implied by + * attempting to box a primitive. + * + * @param {any} value + */$h‍_once.immutableObject(immutableObject); +const isObject= (value)=>Object(value)=== value; + +/** + * isError tests whether an object inherits from the intrinsic + * `Error.prototype`. + * We capture the original error constructor as FERAL_ERROR to provide a clear + * signal for reviewers that we are handling an object with excess authority, + * like stack trace inspection, that we are carefully hiding from client code. + * Checking instanceof happens to be safe, but to avoid uttering FERAL_ERROR + * for such a trivial case outside commons.js, we provide a utility function. + * + * @param {any} value + */$h‍_once.isObject(isObject); +const isError= (value)=>value instanceof FERAL_ERROR; + +// The original unsafe untamed eval function, which must not escape. +// Sample at module initialization time, which is before lockdown can +// repair it. Use it only to build powerless abstractions. +// eslint-disable-next-line no-eval +$h‍_once.isError(isError);const FERAL_EVAL=eval; + +// The original unsafe untamed Function constructor, which must not escape. +// Sample at module initialization time, which is before lockdown can +// repair it. Use it only to build powerless abstractions. +$h‍_once.FERAL_EVAL(FERAL_EVAL);const FERAL_FUNCTION=Function;$h‍_once.FERAL_FUNCTION(FERAL_FUNCTION); + +const noEvalEvaluate= ()=> { + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_EVAL.md + throw TypeError('Cannot eval with evalTaming set to "noEval" (SES_NO_EVAL)'); + };$h‍_once.noEvalEvaluate(noEvalEvaluate); +}) +, +// === functors[1] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let TypeError;$h‍_imports([["./commons.js", [["TypeError", [$h‍_a => (TypeError = $h‍_a)]]]]]); + +/** getThis returns globalThis in sloppy mode or undefined in strict mode. */ +function getThis() { + return this; + } + +if( getThis()) { + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_SLOPPY.md + throw TypeError( `SES failed to initialize, sloppy mode (SES_NO_SLOPPY)`); + } +}) +, +// === functors[2] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([]); // @ts-check + +// `@endo/env-options` needs to be imported quite early, and so should +// avoid importing from ses or anything that depends on ses. + +// ///////////////////////////////////////////////////////////////////////////// +// Prelude of cheap good - enough imitations of things we'd use or +// do differently if we could depend on ses + +const { freeze}= Object; +const { apply}= Reflect; + +// Should be equivalent to the one in ses' commons.js even though it +// uses the other technique. +const uncurryThis= + (fn)=> + (receiver, ...args)=> + apply(fn, receiver, args); +const arrayPush= uncurryThis(Array.prototype.push); + +const q= JSON.stringify; + +const Fail= (literals, ...args)=> { + let msg= literals[0]; + for( let i= 0; i< args.length; i+= 1) { + msg= `${msg}${args[i]}${literals[i+ 1] }`; + } + throw Error(msg); + }; + +// end prelude +// ///////////////////////////////////////////////////////////////////////////// + +/** + * `makeEnvironmentCaptor` provides a mechanism for getting environment + * variables, if they are needed, and a way to catalog the names of all + * the environment variables that were captured. + * + * @param {object} aGlobal + */ +const makeEnvironmentCaptor= (aGlobal)=>{ + const capturedEnvironmentOptionNames= []; + + /** + * Gets an environment option by name and returns the option value or the + * given default. + * + * @param {string} optionName + * @param {string} defaultSetting + * @returns {string} + */ + const getEnvironmentOption= (optionName, defaultSetting)=> { + // eslint-disable-next-line @endo/no-polymorphic-call + typeof optionName=== 'string'|| + Fail `Environment option name ${q(optionName)} must be a string.`; + // eslint-disable-next-line @endo/no-polymorphic-call + typeof defaultSetting=== 'string'|| + Fail `Environment option default setting ${q( + defaultSetting) + } must be a string.`; + + /** @type {string} */ + let setting= defaultSetting; + const globalProcess= aGlobal.process; + if( globalProcess&& typeof globalProcess=== 'object') { + const globalEnv= globalProcess.env; + if( globalEnv&& typeof globalEnv=== 'object') { + if( optionName in globalEnv) { + arrayPush(capturedEnvironmentOptionNames, optionName); + const optionValue= globalEnv[optionName]; + // eslint-disable-next-line @endo/no-polymorphic-call + typeof optionValue=== 'string'|| + Fail `Environment option named ${q( + optionName) + }, if present, must have a corresponding string value, got ${q( + optionValue) + }`; + setting= optionValue; + } + } + } + return setting; + }; + freeze(getEnvironmentOption); + + const getCapturedEnvironmentOptionNames= ()=> { + return freeze([...capturedEnvironmentOptionNames]); + }; + freeze(getCapturedEnvironmentOptionNames); + + return freeze({ getEnvironmentOption, getCapturedEnvironmentOptionNames}); + };$h‍_once.makeEnvironmentCaptor(makeEnvironmentCaptor); +freeze(makeEnvironmentCaptor); +}) +, +// === functors[3] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([["./src/env-options.js", []]]); +}) +, +// === functors[4] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Set,String,isArray,arrayJoin,arraySlice,arraySort,arrayMap,keys,fromEntries,freeze,is,isError,setAdd,setHas,stringIncludes,stringStartsWith,stringifyJson,toStringTagSymbol;$h‍_imports([["../commons.js", [["Set", [$h‍_a => (Set = $h‍_a)]],["String", [$h‍_a => (String = $h‍_a)]],["isArray", [$h‍_a => (isArray = $h‍_a)]],["arrayJoin", [$h‍_a => (arrayJoin = $h‍_a)]],["arraySlice", [$h‍_a => (arraySlice = $h‍_a)]],["arraySort", [$h‍_a => (arraySort = $h‍_a)]],["arrayMap", [$h‍_a => (arrayMap = $h‍_a)]],["keys", [$h‍_a => (keys = $h‍_a)]],["fromEntries", [$h‍_a => (fromEntries = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["is", [$h‍_a => (is = $h‍_a)]],["isError", [$h‍_a => (isError = $h‍_a)]],["setAdd", [$h‍_a => (setAdd = $h‍_a)]],["setHas", [$h‍_a => (setHas = $h‍_a)]],["stringIncludes", [$h‍_a => (stringIncludes = $h‍_a)]],["stringStartsWith", [$h‍_a => (stringStartsWith = $h‍_a)]],["stringifyJson", [$h‍_a => (stringifyJson = $h‍_a)]],["toStringTagSymbol", [$h‍_a => (toStringTagSymbol = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + +/** + * Joins English terms with commas and an optional conjunction. + * + * @param {(string | StringablePayload)[]} terms + * @param {"and" | "or"} conjunction + */ +const enJoin= (terms, conjunction)=> { + if( terms.length=== 0) { + return '(none)'; + }else if( terms.length=== 1) { + return terms[0]; + }else if( terms.length=== 2) { + const [first, second]= terms; + return `${first} ${conjunction} ${second}`; + }else { + return `${arrayJoin(arraySlice(terms,0, -1), ', ') }, ${conjunction} ${ + terms[terms.length- 1] + }`; + } + }; + +/** + * Prepend the correct indefinite article onto a noun, typically a typeof + * result, e.g., "an object" vs. "a number" + * + * @param {string} str The noun to prepend + * @returns {string} The noun prepended with a/an + */$h‍_once.enJoin(enJoin); +const an= (str)=>{ + str= `${str}`; + if( str.length>= 1&& stringIncludes('aeiouAEIOU', str[0])) { + return `an ${str}`; + } + return `a ${str}`; + };$h‍_once.an(an); +freeze(an); + + +/** + * Like `JSON.stringify` but does not blow up if given a cycle or a bigint. + * This is not + * intended to be a serialization to support any useful unserialization, + * or any programmatic use of the resulting string. The string is intended + * *only* for showing a human under benign conditions, in order to be + * informative enough for some + * logging purposes. As such, this `bestEffortStringify` has an + * imprecise specification and may change over time. + * + * The current `bestEffortStringify` possibly emits too many "seen" + * markings: Not only for cycles, but also for repeated subtrees by + * object identity. + * + * As a best effort only for diagnostic interpretation by humans, + * `bestEffortStringify` also turns various cases that normal + * `JSON.stringify` skips or errors on, like `undefined` or bigints, + * into strings that convey their meaning. To distinguish this from + * strings in the input, these synthesized strings always begin and + * end with square brackets. To distinguish those strings from an + * input string with square brackets, and input string that starts + * with an open square bracket `[` is itself placed in square brackets. + * + * @param {any} payload + * @param {(string|number)=} spaces + * @returns {string} + */ +const bestEffortStringify= (payload, spaces= undefined)=> { + const seenSet= new Set(); + const replacer= (_, val)=> { + switch( typeof val){ + case 'object': { + if( val=== null) { + return null; + } + if( setHas(seenSet, val)) { + return '[Seen]'; + } + setAdd(seenSet, val); + if( isError(val)) { + return `[${val.name}: ${val.message}]`; + } + if( toStringTagSymbol in val) { + // For the built-ins that have or inherit a `Symbol.toStringTag`-named + // property, most of them inherit the default `toString` method, + // which will print in a similar manner: `"[object Foo]"` vs + // `"[Foo]"`. The exceptions are + // * `Symbol.prototype`, `BigInt.prototype`, `String.prototype` + // which don't matter to us since we handle primitives + // separately and we don't care about primitive wrapper objects. + // * TODO + // `Date.prototype`, `TypedArray.prototype`. + // Hmmm, we probably should make special cases for these. We're + // not using these yet, so it's not urgent. But others will run + // into these. + // + // Once #2018 is closed, the only objects in our code that have or + // inherit a `Symbol.toStringTag`-named property are remotables + // or their remote presences. + // This printing will do a good job for these without + // violating abstraction layering. This behavior makes sense + // purely in terms of JavaScript concepts. That's some of the + // motivation for choosing that representation of remotables + // and their remote presences in the first place. + return `[${val[toStringTagSymbol]}]`; + } + if( isArray(val)) { + return val; + } + const names= keys(val); + if( names.length< 2) { + return val; + } + let sorted= true; + for( let i= 1; i< names.length; i+= 1) { + if( names[i- 1]>= names[i]) { + sorted= false; + break; + } + } + if( sorted) { + return val; + } + arraySort(names); + const entries= arrayMap(names, (name)=>[name, val[name]]); + return fromEntries(entries); + } + case 'function': { + return `[Function ${val.name|| '' }]`; + } + case 'string': { + if( stringStartsWith(val, '[')) { + return `[${val}]`; + } + return val; + } + case 'undefined': + case 'symbol': { + return `[${String(val)}]`; + } + case 'bigint': { + return `[${val}n]`; + } + case 'number': { + if( is(val, NaN)) { + return '[NaN]'; + }else if( val=== Infinity) { + return '[Infinity]'; + }else if( val=== -Infinity) { + return '[-Infinity]'; + } + return val; + } + default: { + return val; + }} + + }; + try { + return stringifyJson(payload, replacer, spaces); + }catch( _err) { + // Don't do anything more fancy here if there is any + // chance that might throw, unless you surround that + // with another try-catch-recovery. For example, + // the caught thing might be a proxy or other exotic + // object rather than an error. The proxy might throw + // whenever it is possible for it to. + return '[Something that failed to stringify]'; + } + };$h‍_once.bestEffortStringify(bestEffortStringify); +freeze(bestEffortStringify); +}) +, +// === functors[5] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([]); // @ts-check + +/** + * @callback BaseAssert + * The `assert` function itself. + * + * @param {*} flag The truthy/falsy value + * @param {Details=} optDetails The details to throw + * @param {ErrorConstructor=} ErrorConstructor An optional alternate error + * constructor to use. + * @returns {asserts flag} + */ + +/** + * @typedef {object} AssertMakeErrorOptions + * @property {string=} errorName + */ + +/** + * @callback AssertMakeError + * + * The `assert.error` method, recording details for the console. + * + * The optional `optDetails` can be a string. + * @param {Details=} optDetails The details of what was asserted + * @param {ErrorConstructor=} ErrorConstructor An optional alternate error + * constructor to use. + * @param {AssertMakeErrorOptions=} options + * @returns {Error} + */ + +/** + * @callback AssertFail + * + * The `assert.fail` method. + * + * Fail an assertion, recording full details to the console and + * raising an exception with a message in which `details` substitution values + * have been redacted. + * + * The optional `optDetails` can be a string for backwards compatibility + * with the nodejs assertion library. + * @param {Details=} optDetails The details of what was asserted + * @param {ErrorConstructor=} ErrorConstructor An optional alternate error + * constructor to use. + * @returns {never} + */ + +/** + * @callback AssertEqual + * The `assert.equal` method + * + * Assert that two values must be `Object.is`. + * @param {*} actual The value we received + * @param {*} expected What we wanted + * @param {Details=} optDetails The details to throw + * @param {ErrorConstructor=} ErrorConstructor An optional alternate error + * constructor to use. + * @returns {void} + */ + +// Type all the overloads of the assertTypeof function. +// There may eventually be a better way to do this, but +// thems the breaks with Typescript 4.0. +/** + * @callback AssertTypeofBigint + * @param {any} specimen + * @param {'bigint'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is bigint} + */ + +/** + * @callback AssertTypeofBoolean + * @param {any} specimen + * @param {'boolean'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is boolean} + */ + +/** + * @callback AssertTypeofFunction + * @param {any} specimen + * @param {'function'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is Function} + */ + +/** + * @callback AssertTypeofNumber + * @param {any} specimen + * @param {'number'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is number} + */ + +/** + * @callback AssertTypeofObject + * @param {any} specimen + * @param {'object'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is Record | null} + */ + +/** + * @callback AssertTypeofString + * @param {any} specimen + * @param {'string'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is string} + */ + +/** + * @callback AssertTypeofSymbol + * @param {any} specimen + * @param {'symbol'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is symbol} + */ + +/** + * @callback AssertTypeofUndefined + * @param {any} specimen + * @param {'undefined'} typename + * @param {Details=} optDetails + * @returns {asserts specimen is undefined} + */ + +/** + * The `assert.typeof` method + * + * @typedef {AssertTypeofBigint & AssertTypeofBoolean & AssertTypeofFunction & AssertTypeofNumber & AssertTypeofObject & AssertTypeofString & AssertTypeofSymbol & AssertTypeofUndefined} AssertTypeof + */ + +/** + * @callback AssertString + * The `assert.string` method. + * + * `assert.string(v)` is equivalent to `assert.typeof(v, 'string')`. We + * special case this one because it is the most frequently used. + * + * Assert an expected typeof result. + * @param {any} specimen The value to get the typeof + * @param {Details=} optDetails The details to throw + * @returns {asserts specimen is string} + */ + +/** + * @callback AssertNote + * The `assert.note` method. + * + * Annotate an error with details, potentially to be used by an + * augmented console such as the causal console of `console.js`, to + * provide extra information associated with logged errors. + * + * @param {Error} error + * @param {Details} detailsNote + * @returns {void} + */ + +// ///////////////////////////////////////////////////////////////////////////// + +/** + * @typedef {{}} DetailsToken + * A call to the `details` template literal makes and returns a fresh details + * token, which is a frozen empty object associated with the arguments of that + * `details` template literal expression. + */ + +/** + * @typedef {string | DetailsToken} Details + * Either a plain string, or made by the `details` template literal tag. + */ + +/** + * @typedef {object} StringablePayload + * Holds the payload passed to quote so that its printed form is visible. + * @property {() => string} toString How to print the payload + */ + +/** + * To "declassify" and quote a substitution value used in a + * ``` details`...` ``` template literal, enclose that substitution expression + * in a call to `quote`. This makes the value appear quoted + * (as if with `JSON.stringify`) in the message of the thrown error. The + * payload itself is still passed unquoted to the console as it would be + * without `quote`. + * + * For example, the following will reveal the expected sky color, but not the + * actual incorrect sky color, in the thrown error's message: + * ```js + * sky.color === expectedColor || Fail`${sky.color} should be ${quote(expectedColor)}`; + * ``` + * + * // TODO Update SES-shim to new convention, where `details` is + * // renamed to `X` rather than `d`. + * The normal convention is to locally rename `details` to `d` and `quote` to `q` + * like `const { details: d, quote: q } = assert;`, so the above example would then be + * ```js + * sky.color === expectedColor || Fail`${sky.color} should be ${q(expectedColor)}`; + * ``` + * + * @callback AssertQuote + * @param {*} payload What to declassify + * @param {(string|number)=} spaces + * @returns {StringablePayload} The declassified payload + */ + +/** + * @callback Raise + * + * To make an `assert` which terminates some larger unit of computation + * like a transaction, vat, or process, call `makeAssert` with a `Raise` + * callback, where that callback actually performs that larger termination. + * If possible, the callback should also report its `reason` parameter as + * the alleged reason for the termination. + * + * @param {Error} reason + */ + +/** + * @callback MakeAssert + * + * Makes and returns an `assert` function object that shares the bookkeeping + * state defined by this module with other `assert` function objects made by + * `makeAssert`. This state is per-module-instance and is exposed by the + * `loggedErrorHandler` above. We refer to `assert` as a "function object" + * because it can be called directly as a function, but also has methods that + * can be called. + * + * If `optRaise` is provided, the returned `assert` function object will call + * `optRaise(reason)` before throwing the error. This enables `optRaise` to + * engage in even more violent termination behavior, like terminating the vat, + * that prevents execution from reaching the following throw. However, if + * `optRaise` returns normally, which would be unusual, the throw following + * `optRaise(reason)` would still happen. + * + * @param {Raise=} optRaise + * @param {boolean=} unredacted + * @returns {Assert} + */ + +/** + * @typedef {(template: TemplateStringsArray | string[], ...args: any) => DetailsToken} DetailsTag + * + * Use the `details` function as a template literal tag to create + * informative error messages. The assertion functions take such messages + * as optional arguments: + * ```js + * assert(sky.isBlue(), details`${sky.color} should be "blue"`); + * ``` + * // TODO Update SES-shim to new convention, where `details` is + * // renamed to `X` rather than `d`. + * or following the normal convention to locally rename `details` to `d` + * and `quote` to `q` like `const { details: d, quote: q } = assert;`: + * ```js + * assert(sky.isBlue(), d`${sky.color} should be "blue"`); + * ``` + * However, note that in most cases it is preferable to instead use the `Fail` + * template literal tag (which has the same input signature as `details` + * but automatically creates and throws an error): + * ```js + * sky.isBlue() || Fail`${sky.color} should be "blue"`; + * ``` + * + * The details template tag returns a `DetailsToken` object that can print + * itself with the formatted message in two ways. + * It will report full details to the console, but + * mask embedded substitution values with their typeof information in the thrown error + * to prevent revealing secrets up the exceptional path. In the example + * above, the thrown error may reveal only that `sky.color` is a string, + * whereas the same diagnostic printed to the console reveals that the + * sky was green. This masking can be disabled for an individual substitution value + * using `quote`. + * + * The `raw` property of an input template array is ignored, so a simple + * array of strings may be provided directly. + */ + +/** + * @typedef {(template: TemplateStringsArray | string[], ...args: any) => never} FailTag + * + * Use the `Fail` function as a template literal tag to efficiently + * create and throw a `details`-style error only when a condition is not satisfied. + * ```js + * condition || Fail`...complaint...`; + * ``` + * This avoids the overhead of creating usually-unnecessary errors like + * ```js + * assert(condition, details`...complaint...`); + * ``` + * while improving readability over alternatives like + * ```js + * condition || assert.fail(details`...complaint...`); + * ``` + * + * However, due to current weakness in TypeScript, static reasoning + * is less powerful with the `||` patterns than with an `assert` call. + * Until/unless https://github.com/microsoft/TypeScript/issues/51426 is fixed, + * for `||`-style assertions where this loss of static reasoning is a problem, + * instead express the assertion as + * ```js + * if (!condition) { + * Fail`...complaint...`; + * } + * ``` + * or, if needed, + * ```js + * if (!condition) { + * // `throw` is noop since `Fail` throws, but it improves static analysis + * throw Fail`...complaint...`; + * } + * ``` + */ + +/** + * assert that expr is truthy, with an optional details to describe + * the assertion. It is a tagged template literal like + * ```js + * assert(expr, details`....`);` + * ``` + * + * The literal portions of the template are assumed non-sensitive, as + * are the `typeof` types of the substitution values. These are + * assembled into the thrown error message. The actual contents of the + * substitution values are assumed sensitive, to be revealed to + * the console only. We assume only the virtual platform's owner can read + * what is written to the console, where the owner is in a privileged + * position over computation running on that platform. + * + * The optional `optDetails` can be a string for backwards compatibility + * with the nodejs assertion library. + * + * @typedef { BaseAssert & { + * typeof: AssertTypeof, + * error: AssertMakeError, + * fail: AssertFail, + * equal: AssertEqual, + * string: AssertString, + * note: AssertNote, + * details: DetailsTag, + * Fail: FailTag, + * quote: AssertQuote, + * bare: AssertQuote, + * makeAssert: MakeAssert, + * } } Assert + */ + +// ///////////////////////////////////////////////////////////////////////////// + +/** + * @typedef {object} VirtualConsole + * @property {Console['debug']} debug + * @property {Console['log']} log + * @property {Console['info']} info + * @property {Console['warn']} warn + * @property {Console['error']} error + * + * @property {Console['trace']} trace + * @property {Console['dirxml']} dirxml + * @property {Console['group']} group + * @property {Console['groupCollapsed']} groupCollapsed + * + * @property {Console['assert']} assert + * @property {Console['timeLog']} timeLog + * + * @property {Console['clear']} clear + * @property {Console['count']} count + * @property {Console['countReset']} countReset + * @property {Console['dir']} dir + * @property {Console['groupEnd']} groupEnd + * + * @property {Console['table']} table + * @property {Console['time']} time + * @property {Console['timeEnd']} timeEnd + * @property {Console['timeStamp']} timeStamp + */ + +/* This is deliberately *not* JSDoc, it is a regular comment. + * + * TODO: We'd like to add the following properties to the above + * VirtualConsole, but they currently cause conflicts where + * some Typescript implementations don't have these properties + * on the Console type. + * + * @property {Console['profile']} profile + * @property {Console['profileEnd']} profileEnd + */ + +/** + * @typedef {'debug' | 'log' | 'info' | 'warn' | 'error'} LogSeverity + */ + +/** + * @typedef ConsoleFilter + * @property {(severity: LogSeverity) => boolean} canLog + */ + +/** + * @callback FilterConsole + * @param {VirtualConsole} baseConsole + * @param {ConsoleFilter} filter + * @param {string=} topic + * @returns {VirtualConsole} + */ +}) +, +// === functors[6] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([]); // @ts-check + +/** + * @typedef {readonly any[]} LogArgs + * + * This is an array suitable to be used as arguments of a console + * level message *after* the format string argument. It is the result of + * a `details` template string and consists of alternating literal strings + * and substitution values, starting with a literal string. At least that + * first literal string is always present. + */ + +/** + * @callback NoteCallback + * + * @param {Error} error + * @param {LogArgs} noteLogArgs + * @returns {void} + */ + +/** + * @callback GetStackString + * @param {Error} error + * @returns {string=} + */ + +/** + * @typedef {object} LoggedErrorHandler + * + * Used to parameterize `makeCausalConsole` to give it access to potentially + * hidden information to augment the logging of errors. + * + * @property {GetStackString} getStackString + * @property {(error: Error) => string} tagError + * @property {() => void} resetErrorTagNum for debugging purposes only + * @property {(error: Error) => (LogArgs | undefined)} getMessageLogArgs + * @property {(error: Error) => (LogArgs | undefined)} takeMessageLogArgs + * @property {(error: Error, callback?: NoteCallback) => LogArgs[] } takeNoteLogArgsArray + */ + +// ///////////////////////////////////////////////////////////////////////////// + +/** + * @typedef {readonly [string, ...any[]]} LogRecord + */ + +/** + * @typedef {object} LoggingConsoleKit + * @property {VirtualConsole} loggingConsole + * @property {() => readonly LogRecord[]} takeLog + */ + +/** + * @typedef {object} MakeLoggingConsoleKitOptions + * @property {boolean=} shouldResetForDebugging + */ + +/** + * @callback MakeLoggingConsoleKit + * + * A logging console just accumulates the contents of all whitelisted calls, + * making them available to callers of `takeLog()`. Calling `takeLog()` + * consumes these, so later calls to `takeLog()` will only provide a log of + * calls that have happened since then. + * + * @param {LoggedErrorHandler} loggedErrorHandler + * @param {MakeLoggingConsoleKitOptions=} options + * @returns {LoggingConsoleKit} + */ + +/** + * @typedef {{ NOTE: 'ERROR_NOTE:', MESSAGE: 'ERROR_MESSAGE:' }} ErrorInfo + */ + +/** + * @typedef {ErrorInfo[keyof ErrorInfo]} ErrorInfoKind + */ + +/** + * @callback MakeCausalConsole + * + * Makes a causal console wrapper of a `baseConsole`, where the causal console + * calls methods of the `loggedErrorHandler` to customize how it handles logged + * errors. + * + * @param {VirtualConsole} baseConsole + * @param {LoggedErrorHandler} loggedErrorHandler + * @returns {VirtualConsole} + */ +}) +, +// === functors[7] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([["./internal-types.js", []]]); + + + + + +const { freeze}= Object; +const { isSafeInteger}= Number; + +/** + * @template Data + * @typedef {object} DoublyLinkedCell + * A cell of a doubly-linked ring, i.e., a doubly-linked circular list. + * DoublyLinkedCells are not frozen, and so should be closely encapsulated by + * any abstraction that uses them. + * @property {DoublyLinkedCell} next + * @property {DoublyLinkedCell} prev + * @property {Data} data + */ + +/** + * Makes a new self-linked cell. There are two reasons to do so: + * * To make the head sigil of a new initially-empty doubly-linked ring. + * * To make a non-sigil cell to be `spliceAfter`ed. + * + * @template Data + * @param {Data} data + * @returns {DoublyLinkedCell} + */ +const makeSelfCell= (data)=>{ + /** @type {Partial>} */ + const incompleteCell= { + next: undefined, + prev: undefined, + data}; + + const selfCell= /** @type {DoublyLinkedCell} */ incompleteCell; + selfCell.next= selfCell; + selfCell.prev= selfCell; + // Not frozen! + return selfCell; + }; + +/** + * Splices a self-linked non-sigil cell into a ring after `prev`. + * `prev` could be the head sigil, or it could be some other non-sigil + * cell within a ring. + * + * @template Data + * @param {DoublyLinkedCell} prev + * @param {DoublyLinkedCell} selfCell + */ +const spliceAfter= (prev, selfCell)=> { + if( prev=== selfCell) { + throw TypeError('Cannot splice a cell into itself'); + } + if( selfCell.next!== selfCell|| selfCell.prev!== selfCell) { + throw TypeError('Expected self-linked cell'); + } + const cell= selfCell; + // rename variable cause it isn't self-linked after this point. + + const next= prev.next; + cell.prev= prev; + cell.next= next; + prev.next= cell; + next.prev= cell; + // Not frozen! + return cell; + }; + +/** + * @template Data + * @param {DoublyLinkedCell} cell + * No-op if the cell is self-linked. + */ +const spliceOut= (cell)=>{ + const { prev, next}= cell; + prev.next= next; + next.prev= prev; + cell.prev= cell; + cell.next= cell; + }; + +/** + * The LRUCacheMap is used within the implementation of `assert` and so + * at a layer below SES or harden. Thus, we give it a `WeakMap`-like interface + * rather than a `WeakMapStore`-like interface. To work before `lockdown`, + * the implementation must use `freeze` manually, but still exhaustively. + * + * It implements the WeakMap interface, and holds its keys weakly. Cached + * values are only held while the key is held by the user and the key/value + * bookkeeping cell has not been pushed off the end of the cache by `budget` + * number of more recently referenced cells. If the key is dropped by the user, + * the value will no longer be held by the cache, but the bookkeeping cell + * itself will stay in memory. + * + * @template {{}} K + * @template {unknown} V + * @param {number} keysBudget + * @returns {WeakMap} + */ +const makeLRUCacheMap= (keysBudget)=>{ + if( !isSafeInteger(keysBudget)|| keysBudget< 0) { + throw TypeError('keysBudget must be a safe non-negative integer number'); + } + /** @typedef {DoublyLinkedCell | undefined>} LRUCacheCell */ + /** @type {WeakMap} */ + const keyToCell= new WeakMap(); + let size= 0; // `size` must remain <= `keysBudget` + // As a sigil, `head` uniquely is not in the `keyToCell` map. + /** @type {LRUCacheCell} */ + const head= makeSelfCell(undefined); + + const touchCell= (key)=>{ + const cell= keyToCell.get(key); + if( cell=== undefined|| cell.data=== undefined) { + // Either the key was GCed, or the cell was condemned. + return undefined; + } + // Becomes most recently used + spliceOut(cell); + spliceAfter(head, cell); + return cell; + }; + + /** + * @param {K} key + */ + const has= (key)=>touchCell(key)!== undefined; + freeze(has); + + /** + * @param {K} key + */ + // UNTIL https://github.com/endojs/endo/issues/1514 + // Prefer: const get = key => touchCell(key)?.data?.get(key); + const get= (key)=>{ + const cell= touchCell(key); + return cell&& cell.data&& cell.data.get(key); + }; + freeze(get); + + /** + * @param {K} key + * @param {V} value + */ + const set= (key, value)=> { + if( keysBudget< 1) { + // eslint-disable-next-line no-use-before-define + return lruCacheMap; // Implements WeakMap.set + } + + let cell= touchCell(key); + if( cell=== undefined) { + cell= makeSelfCell(undefined); + spliceAfter(head, cell); // start most recently used + } + if( !cell.data) { + // Either a fresh cell or a reused condemned cell. + size+= 1; + // Add its data. + cell.data= new WeakMap(); + // Advertise the cell for this key. + keyToCell.set(key, cell); + while( size> keysBudget) { + const condemned= head.prev; + spliceOut(condemned); // Drop least recently used + condemned.data= undefined; + size-= 1; + } + } + + // Update the data. + cell.data.set(key, value); + + // eslint-disable-next-line no-use-before-define + return lruCacheMap; // Implements WeakMap.set + }; + freeze(set); + + // "delete" is a keyword. + /** + * @param {K} key + */ + const deleteIt= (key)=>{ + const cell= keyToCell.get(key); + if( cell=== undefined) { + return false; + } + spliceOut(cell); + keyToCell.delete(key); + if( cell.data=== undefined) { + // Already condemned. + return false; + } + + cell.data= undefined; + size-= 1; + return true; + }; + freeze(deleteIt); + + const lruCacheMap= freeze({ + has, + get, + set, + delete: deleteIt, + [Symbol.toStringTag]: 'LRUCacheMap'}); + + return lruCacheMap; + };$h‍_once.makeLRUCacheMap(makeLRUCacheMap); +freeze(makeLRUCacheMap); + +const defaultLoggedErrorsBudget= 1000; +const defaultArgsPerErrorBudget= 100; + +/** + * @param {number} [errorsBudget] + * @param {number} [argsPerErrorBudget] + */ +const makeNoteLogArgsArrayKit= ( + errorsBudget= defaultLoggedErrorsBudget, + argsPerErrorBudget= defaultArgsPerErrorBudget)=> + { + if( !isSafeInteger(argsPerErrorBudget)|| argsPerErrorBudget< 1) { + throw TypeError( + 'argsPerErrorBudget must be a safe positive integer number'); + + } + + /** + * @type {WeakMap} + * + * Maps from an error to an array of log args, where each log args is + * remembered as an annotation on that error. This can be used, for example, + * to keep track of additional causes of the error. The elements of any + * log args may include errors which are associated with further annotations. + * An augmented console, like the causal console of `console.js`, could + * then retrieve the graph of such annotations. + */ + const noteLogArgsArrayMap= makeLRUCacheMap(errorsBudget); + + /** + * @param {Error} error + * @param {LogArgs} logArgs + */ + const addLogArgs= (error, logArgs)=> { + const logArgsArray= noteLogArgsArrayMap.get(error); + if( logArgsArray!== undefined) { + if( logArgsArray.length>= argsPerErrorBudget) { + logArgsArray.shift(); + } + logArgsArray.push(logArgs); + }else { + noteLogArgsArrayMap.set(error, [logArgs]); + } + }; + freeze(addLogArgs); + + /** + * @param {Error} error + * @returns {LogArgs[] | undefined} + */ + const takeLogArgsArray= (error)=>{ + const result= noteLogArgsArrayMap.get(error); + noteLogArgsArrayMap.delete(error); + return result; + }; + freeze(takeLogArgsArray); + + return freeze({ + addLogArgs, + takeLogArgsArray}); + + };$h‍_once.makeNoteLogArgsArrayKit(makeNoteLogArgsArrayKit); +freeze(makeNoteLogArgsArrayKit); +}) +, +// === functors[8] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let RangeError,TypeError,WeakMap,arrayJoin,arrayMap,arrayPop,arrayPush,assign,freeze,globalThis,is,isError,regexpTest,stringIndexOf,stringReplace,stringSlice,stringStartsWith,weakmapDelete,weakmapGet,weakmapHas,weakmapSet,an,bestEffortStringify,makeNoteLogArgsArrayKit;$h‍_imports([["../commons.js", [["RangeError", [$h‍_a => (RangeError = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["WeakMap", [$h‍_a => (WeakMap = $h‍_a)]],["arrayJoin", [$h‍_a => (arrayJoin = $h‍_a)]],["arrayMap", [$h‍_a => (arrayMap = $h‍_a)]],["arrayPop", [$h‍_a => (arrayPop = $h‍_a)]],["arrayPush", [$h‍_a => (arrayPush = $h‍_a)]],["assign", [$h‍_a => (assign = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]],["is", [$h‍_a => (is = $h‍_a)]],["isError", [$h‍_a => (isError = $h‍_a)]],["regexpTest", [$h‍_a => (regexpTest = $h‍_a)]],["stringIndexOf", [$h‍_a => (stringIndexOf = $h‍_a)]],["stringReplace", [$h‍_a => (stringReplace = $h‍_a)]],["stringSlice", [$h‍_a => (stringSlice = $h‍_a)]],["stringStartsWith", [$h‍_a => (stringStartsWith = $h‍_a)]],["weakmapDelete", [$h‍_a => (weakmapDelete = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]],["weakmapHas", [$h‍_a => (weakmapHas = $h‍_a)]],["weakmapSet", [$h‍_a => (weakmapSet = $h‍_a)]]]],["./stringify-utils.js", [["an", [$h‍_a => (an = $h‍_a)]],["bestEffortStringify", [$h‍_a => (bestEffortStringify = $h‍_a)]]]],["./types.js", []],["./internal-types.js", []],["./note-log-args.js", [["makeNoteLogArgsArrayKit", [$h‍_a => (makeNoteLogArgsArrayKit = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +// For our internal debugging purposes, uncomment +// const internalDebugConsole = console; + +// ///////////////////////////////////////////////////////////////////////////// + +/** @type {WeakMap} */ +const declassifiers= new WeakMap(); + +/** @type {AssertQuote} */ +const quote= (payload, spaces= undefined)=> { + const result= freeze({ + toString: freeze(()=> bestEffortStringify(payload, spaces))}); + + weakmapSet(declassifiers, result, payload); + return result; + }; +freeze(quote); + +const canBeBare= freeze(/^[\w:-]( ?[\w:-])*$/); + +/** + * Embed a string directly into error details without wrapping punctuation. + * To avoid injection attacks that exploit quoting confusion, this must NEVER + * be used with data that is possibly attacker-controlled. + * As a further safeguard, we fall back to quoting any input that is not a + * string of sufficiently word-like parts separated by isolated spaces (rather + * than throwing an exception, which could hide the original problem for which + * explanatory details are being constructed---i.e., ``` assert.details`...` ``` + * should never be the source of a new exception, nor should an attempt to + * render its output, although we _could_ instead decide to handle the latter + * by inline replacement similar to that of `bestEffortStringify` for producing + * rendered messages like `(an object) was tagged "[Unsafe bare string]"`). + * + * @type {AssertQuote} + */ +const bare= (payload, spaces= undefined)=> { + if( typeof payload!== 'string'|| !regexpTest(canBeBare, payload)) { + return quote(payload, spaces); + } + const result= freeze({ + toString: freeze(()=> payload)}); + + weakmapSet(declassifiers, result, payload); + return result; + }; +freeze(bare); + +// ///////////////////////////////////////////////////////////////////////////// + +/** + * @typedef {object} HiddenDetails + * + * Captures the arguments passed to the `details` template string tag. + * + * @property {TemplateStringsArray | string[]} template + * @property {any[]} args + */ + +/** + * @type {WeakMap} + * + * Maps from a details token which a `details` template literal returned + * to a record of the contents of that template literal expression. + */ +const hiddenDetailsMap= new WeakMap(); + +/** + * @param {HiddenDetails} hiddenDetails + * @returns {string} + */ +const getMessageString= ({ template, args})=> { + const parts= [template[0]]; + for( let i= 0; i< args.length; i+= 1) { + const arg= args[i]; + let argStr; + if( weakmapHas(declassifiers, arg)) { + argStr= `${arg}`; + }else if( isError(arg)) { + argStr= `(${an(arg.name)})`; + }else { + argStr= `(${an(typeof arg)})`; + } + arrayPush(parts, argStr, template[i+ 1]); + } + return arrayJoin(parts, ''); + }; + +/** + * Give detailsTokens a toString behavior. To minimize the overhead of + * creating new detailsTokens, we do this with an + * inherited `this` sensitive `toString` method, even though we normally + * avoid `this` sensitivity. To protect the method from inappropriate + * `this` application, it does something interesting only for objects + * registered in `redactedDetails`, which should be exactly the detailsTokens. + * + * The printing behavior must not reveal anything redacted, so we just use + * the same `getMessageString` we use to construct the redacted message + * string for a thrown assertion error. + */ +const DetailsTokenProto= freeze({ + toString() { + const hiddenDetails= weakmapGet(hiddenDetailsMap, this); + if( hiddenDetails=== undefined) { + return '[Not a DetailsToken]'; + } + return getMessageString(hiddenDetails); + }}); + +freeze(DetailsTokenProto.toString); + +/** + * Normally this is the function exported as `assert.details` and often + * spelled `d`. However, if the `{errorTaming: 'unsafe'}` option is given to + * `lockdown`, then `unredactedDetails` is used instead. + * + * There are some unconditional uses of `redactedDetails` in this module. All + * of them should be uses where the template literal has no redacted + * substitution values. In those cases, the two are equivalent. + * + * @type {DetailsTag} + */ +const redactedDetails= (template, ...args)=> { + // Keep in mind that the vast majority of calls to `details` creates + // a details token that is never used, so this path must remain as fast as + // possible. Hence we store what we've got with little processing, postponing + // all the work to happen only if needed, for example, if an assertion fails. + const detailsToken= freeze({ __proto__: DetailsTokenProto}); + weakmapSet(hiddenDetailsMap, detailsToken, { template, args}); + return detailsToken; + }; +freeze(redactedDetails); + +/** + * `unredactedDetails` is like `details` except that it does not redact + * anything. It acts like `details` would act if all substitution values + * were wrapped with the `quote` function above (the function normally + * spelled `q`). If the `{errorTaming: 'unsafe'}` option is given to + * `lockdown`, then the lockdown-shim arranges for the global `assert` to be + * one whose `details` property is `unredactedDetails`. + * This setting optimizes the debugging and testing experience at the price + * of safety. `unredactedDetails` also sacrifices the speed of `details`, + * which is usually fine in debugging and testing. + * + * @type {DetailsTag} + */ +const unredactedDetails= (template, ...args)=> { + args= arrayMap(args, (arg)=> + weakmapHas(declassifiers, arg)? arg: quote(arg)); + + return redactedDetails(template, ...args); + };$h‍_once.unredactedDetails(unredactedDetails); +freeze(unredactedDetails); + + +/** + * @param {HiddenDetails} hiddenDetails + * @returns {LogArgs} + */ +const getLogArgs= ({ template, args})=> { + const logArgs= [template[0]]; + for( let i= 0; i< args.length; i+= 1) { + let arg= args[i]; + if( weakmapHas(declassifiers, arg)) { + arg= weakmapGet(declassifiers, arg); + } + // Remove the extra spaces (since console.error puts them + // between each cause). + const priorWithoutSpace= stringReplace(arrayPop(logArgs)|| '', / $/, ''); + if( priorWithoutSpace!== '') { + arrayPush(logArgs, priorWithoutSpace); + } + const nextWithoutSpace= stringReplace(template[i+ 1], /^ /, ''); + arrayPush(logArgs, arg, nextWithoutSpace); + } + if( logArgs[logArgs.length- 1]=== '') { + arrayPop(logArgs); + } + return logArgs; + }; + +/** + * @type {WeakMap} + * + * Maps from an error object to the log args that are a more informative + * alternative message for that error. When logging the error, these + * log args should be preferred to `error.message`. + */ +const hiddenMessageLogArgs= new WeakMap(); + +// So each error tag will be unique. +let errorTagNum= 0; + +/** + * @type {WeakMap} + */ +const errorTags= new WeakMap(); + +/** + * @param {Error} err + * @param {string=} optErrorName + * @returns {string} + */ +const tagError= (err, optErrorName= err.name)=> { + let errorTag= weakmapGet(errorTags, err); + if( errorTag!== undefined) { + return errorTag; + } + errorTagNum+= 1; + errorTag= `${optErrorName}#${errorTagNum}`; + weakmapSet(errorTags, err, errorTag); + return errorTag; + }; + +/** + * @type {AssertMakeError} + */ +const makeError= ( + optDetails= redactedDetails `Assert failed`, + ErrorConstructor= globalThis.Error, + { errorName= undefined}= {})=> + { + if( typeof optDetails=== 'string') { + // If it is a string, use it as the literal part of the template so + // it doesn't get quoted. + optDetails= redactedDetails([optDetails]); + } + const hiddenDetails= weakmapGet(hiddenDetailsMap, optDetails); + if( hiddenDetails=== undefined) { + throw TypeError( `unrecognized details ${quote(optDetails)}`); + } + const messageString= getMessageString(hiddenDetails); + const error= new ErrorConstructor(messageString); + weakmapSet(hiddenMessageLogArgs, error, getLogArgs(hiddenDetails)); + if( errorName!== undefined) { + tagError(error, errorName); + } + // The next line is a particularly fruitful place to put a breakpoint. + return error; + }; +freeze(makeError); + +// ///////////////////////////////////////////////////////////////////////////// + +const { addLogArgs, takeLogArgsArray}= makeNoteLogArgsArrayKit(); + +/** + * @type {WeakMap} + * + * An augmented console will normally only take the hidden noteArgs array once, + * when it logs the error being annotated. Once that happens, further + * annotations of that error should go to the console immediately. We arrange + * that by accepting a note-callback function from the console as an optional + * part of that taking operation. Normally there will only be at most one + * callback per error, but that depends on console behavior which we should not + * assume. We make this an array of callbacks so multiple registrations + * are independent. + */ +const hiddenNoteCallbackArrays= new WeakMap(); + +/** @type {AssertNote} */ +const note= (error, detailsNote)=> { + if( typeof detailsNote=== 'string') { + // If it is a string, use it as the literal part of the template so + // it doesn't get quoted. + detailsNote= redactedDetails([detailsNote]); + } + const hiddenDetails= weakmapGet(hiddenDetailsMap, detailsNote); + if( hiddenDetails=== undefined) { + throw TypeError( `unrecognized details ${quote(detailsNote)}`); + } + const logArgs= getLogArgs(hiddenDetails); + const callbacks= weakmapGet(hiddenNoteCallbackArrays, error); + if( callbacks!== undefined) { + for( const callback of callbacks) { + callback(error, logArgs); + } + }else { + addLogArgs(error, logArgs); + } + }; +freeze(note); + +/** + * The unprivileged form that just uses the de facto `error.stack` property. + * The start compartment normally has a privileged `globalThis.getStackString` + * which should be preferred if present. + * + * @param {Error} error + * @returns {string} + */ +const defaultGetStackString= (error)=>{ + if( !('stack'in error)) { + return ''; + } + const stackString= `${error.stack}`; + const pos= stringIndexOf(stackString, '\n'); + if( stringStartsWith(stackString, ' ')|| pos=== -1) { + return stackString; + } + return stringSlice(stackString, pos+ 1); // exclude the initial newline + }; + +/** @type {LoggedErrorHandler} */ +const loggedErrorHandler= { + getStackString: globalThis.getStackString|| defaultGetStackString, + tagError: (error)=>tagError(error), + resetErrorTagNum: ()=> { + errorTagNum= 0; + }, + getMessageLogArgs: (error)=>weakmapGet(hiddenMessageLogArgs, error), + takeMessageLogArgs: (error)=>{ + const result= weakmapGet(hiddenMessageLogArgs, error); + weakmapDelete(hiddenMessageLogArgs, error); + return result; + }, + takeNoteLogArgsArray: (error, callback)=> { + const result= takeLogArgsArray(error); + if( callback!== undefined) { + const callbacks= weakmapGet(hiddenNoteCallbackArrays, error); + if( callbacks) { + arrayPush(callbacks, callback); + }else { + weakmapSet(hiddenNoteCallbackArrays, error, [callback]); + } + } + return result|| []; + }};$h‍_once.loggedErrorHandler(loggedErrorHandler); + +freeze(loggedErrorHandler); + + +// ///////////////////////////////////////////////////////////////////////////// + +/** + * @type {MakeAssert} + */ +const makeAssert= (optRaise= undefined, unredacted= false)=> { + const details= unredacted? unredactedDetails: redactedDetails; + const assertFailedDetails= details `Check failed`; + + /** @type {AssertFail} */ + const fail= ( + optDetails= assertFailedDetails, + ErrorConstructor= globalThis.Error)=> + { + const reason= makeError(optDetails, ErrorConstructor); + if( optRaise!== undefined) { + optRaise(reason); + } + throw reason; + }; + freeze(fail); + + /** @type {FailTag} */ + const Fail= (template, ...args)=> fail(details(template, ...args)); + + // Don't freeze or export `baseAssert` until we add methods. + // TODO If I change this from a `function` function to an arrow + // function, I seem to get type errors from TypeScript. Why? + /** @type {BaseAssert} */ + function baseAssert( + flag, + optDetails= undefined, + ErrorConstructor= undefined) + { + flag|| fail(optDetails, ErrorConstructor); + } + + /** @type {AssertEqual} */ + const equal= ( + actual, + expected, + optDetails= undefined, + ErrorConstructor= undefined)=> + { + is(actual, expected)|| + fail( + optDetails|| details `Expected ${actual} is same as ${expected}`, + ErrorConstructor|| RangeError); + + }; + freeze(equal); + + /** @type {AssertTypeof} */ + const assertTypeof= (specimen, typename, optDetails)=> { + // This will safely fall through if typename is not a string, + // which is what we want. + // eslint-disable-next-line valid-typeof + if( typeof specimen=== typename) { + return; + } + typeof typename=== 'string'|| Fail `${quote(typename)} must be a string`; + + if( optDetails=== undefined) { + // Embed the type phrase without quotes. + const typeWithDeterminer= an(typename); + optDetails= details `${specimen} must be ${bare(typeWithDeterminer)}`; + } + fail(optDetails, TypeError); + }; + freeze(assertTypeof); + + /** @type {AssertString} */ + const assertString= (specimen, optDetails= undefined)=> + assertTypeof(specimen, 'string', optDetails); + + // Note that "assert === baseAssert" + /** @type {Assert} */ + const assert= assign(baseAssert, { + error: makeError, + fail, + equal, + typeof: assertTypeof, + string: assertString, + note, + details, + Fail, + quote, + bare, + makeAssert}); + + return freeze(assert); + };$h‍_once.makeAssert(makeAssert); +freeze(makeAssert); + + +/** @type {Assert} */ +const assert= makeAssert();$h‍_once.assert(assert); +}) +, +// === functors[9] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Set,String,TypeError,WeakMap,WeakSet,globalThis,apply,arrayForEach,defineProperty,freeze,getOwnPropertyDescriptor,getOwnPropertyDescriptors,getPrototypeOf,isInteger,isObject,objectHasOwnProperty,ownKeys,preventExtensions,setAdd,setForEach,setHas,toStringTagSymbol,typedArrayPrototype,weakmapGet,weakmapSet,weaksetAdd,weaksetHas,assert;$h‍_imports([["./commons.js", [["Set", [$h‍_a => (Set = $h‍_a)]],["String", [$h‍_a => (String = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["WeakMap", [$h‍_a => (WeakMap = $h‍_a)]],["WeakSet", [$h‍_a => (WeakSet = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]],["apply", [$h‍_a => (apply = $h‍_a)]],["arrayForEach", [$h‍_a => (arrayForEach = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]],["getPrototypeOf", [$h‍_a => (getPrototypeOf = $h‍_a)]],["isInteger", [$h‍_a => (isInteger = $h‍_a)]],["isObject", [$h‍_a => (isObject = $h‍_a)]],["objectHasOwnProperty", [$h‍_a => (objectHasOwnProperty = $h‍_a)]],["ownKeys", [$h‍_a => (ownKeys = $h‍_a)]],["preventExtensions", [$h‍_a => (preventExtensions = $h‍_a)]],["setAdd", [$h‍_a => (setAdd = $h‍_a)]],["setForEach", [$h‍_a => (setForEach = $h‍_a)]],["setHas", [$h‍_a => (setHas = $h‍_a)]],["toStringTagSymbol", [$h‍_a => (toStringTagSymbol = $h‍_a)]],["typedArrayPrototype", [$h‍_a => (typedArrayPrototype = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]],["weakmapSet", [$h‍_a => (weakmapSet = $h‍_a)]],["weaksetAdd", [$h‍_a => (weaksetAdd = $h‍_a)]],["weaksetHas", [$h‍_a => (weaksetHas = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * @typedef {import('../types.js').Harden} Harden + */ + +// Obtain the string tag accessor of of TypedArray so we can indirectly use the +// TypedArray brand check it employs. +const typedArrayToStringTag= getOwnPropertyDescriptor( + typedArrayPrototype, + toStringTagSymbol); + +assert(typedArrayToStringTag); +const getTypedArrayToStringTag= typedArrayToStringTag.get; +assert(getTypedArrayToStringTag); + +// Exported for tests. +/** + * Duplicates packages/marshal/src/helpers/passStyle-helpers.js to avoid a dependency. + * + * @param {unknown} object + */ +const isTypedArray= (object)=>{ + // The object must pass a brand check or toStringTag will return undefined. + const tag= apply(getTypedArrayToStringTag, object, []); + return tag!== undefined; + }; + +/** + * Tests if a property key is an integer-valued canonical numeric index. + * https://tc39.es/ecma262/#sec-canonicalnumericindexstring + * + * @param {string | symbol} propertyKey + */$h‍_once.isTypedArray(isTypedArray); +const isCanonicalIntegerIndexString= (propertyKey)=>{ + const n= +String(propertyKey); + return isInteger(n)&& String(n)=== propertyKey; + }; + +/** + * @template T + * @param {ArrayLike} array + */ +const freezeTypedArray= (array)=>{ + preventExtensions(array); + + // Downgrade writable expandos to readonly, even if non-configurable. + // We get each descriptor individually rather than using + // getOwnPropertyDescriptors in order to fail safe when encountering + // an obscure GraalJS issue where getOwnPropertyDescriptor returns + // undefined for a property that does exist. + arrayForEach(ownKeys(array), (/** @type {string | symbol} */ name)=> { + const desc= getOwnPropertyDescriptor(array, name); + assert(desc); + // TypedArrays are integer-indexed exotic objects, which define special + // treatment for property names in canonical numeric form: + // integers in range are permanently writable and non-configurable. + // https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects + // + // This is analogous to the data of a hardened Map or Set, + // so we carve out this exceptional behavior but make all other + // properties non-configurable. + if( !isCanonicalIntegerIndexString(name)) { + defineProperty(array, name, { + ...desc, + writable: false, + configurable: false}); + + } + }); + }; + +/** + * Create a `harden` function. + * + * @returns {Harden} + */ +const makeHardener= ()=> { + // Use a native hardener if possible. + if( typeof globalThis.harden=== 'function') { + const safeHarden= globalThis.harden; + return safeHarden; + } + + const hardened= new WeakSet(); + + const { harden}= { + /** + * @template T + * @param {T} root + * @returns {T} + */ + harden(root) { + const toFreeze= new Set(); + const paths= new WeakMap(); + + // If val is something we should be freezing but aren't yet, + // add it to toFreeze. + /** + * @param {any} val + * @param {string} [path] + */ + function enqueue(val, path= undefined) { + if( !isObject(val)) { + // ignore primitives + return; + } + const type= typeof val; + if( type!== 'object'&& type!== 'function') { + // future proof: break until someone figures out what it should do + throw TypeError( `Unexpected typeof: ${type}`); + } + if( weaksetHas(hardened, val)|| setHas(toFreeze, val)) { + // Ignore if this is an exit, or we've already visited it + return; + } + // console.warn(`adding ${val} to toFreeze`, val); + setAdd(toFreeze, val); + weakmapSet(paths, val, path); + } + + /** + * @param {any} obj + */ + function freezeAndTraverse(obj) { + // Now 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). + // Also throws if the object is an ArrayBuffer or any TypedArray. + if( isTypedArray(obj)) { + freezeTypedArray(obj); + }else { + 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 path= weakmapGet(paths, obj)|| 'unknown'; + const descs= getOwnPropertyDescriptors(obj); + const proto= getPrototypeOf(obj); + enqueue(proto, `${path}.__proto__`); + + arrayForEach(ownKeys(descs), (/** @type {string | symbol} */ name)=> { + const pathname= `${path}.${String(name)}`; + // The 'name' may be a symbol, and TypeScript doesn't like us to + // index arbitrary symbols on objects, so we pretend they're just + // strings. + const desc= descs[/** @type {string} */ name]; + // getOwnPropertyDescriptors is guaranteed to return well-formed + // descriptors, but they still inherit from Object.prototype. If + // someone has poisoned Object.prototype to add 'value' or 'get' + // properties, then a simple 'if ("value" in desc)' or 'desc.value' + // test could be confused. We use hasOwnProperty to be sure about + // whether 'value' is present or not, which tells us for sure that + // this is a data property. + if( objectHasOwnProperty(desc, 'value')) { + enqueue(desc.value, `${pathname}`); + }else { + enqueue(desc.get, `${pathname}(get)`); + enqueue(desc.set, `${pathname}(set)`); + } + }); + } + + function dequeue() { + // New values added before forEach() has finished will be visited. + setForEach(toFreeze, freezeAndTraverse); + } + + /** @param {any} value */ + function markHardened(value) { + weaksetAdd(hardened, value); + } + + function commit() { + setForEach(toFreeze, markHardened); + } + + enqueue(root); + dequeue(); + // console.warn("toFreeze set:", toFreeze); + commit(); + + return root; + }}; + + + return harden; + };$h‍_once.makeHardener(makeHardener); +}) +, +// === functors[10] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([]); /* eslint-disable no-restricted-globals */ +/** + * @file Exports {@code whitelist}, a recursively defined + * JSON record enumerating all intrinsics and their properties + * according to ECMA specs. + * + * @author JF Paradis + * @author Mark S. Miller + */ + +/* eslint max-lines: 0 */ + +/** + * constantProperties + * non-configurable, non-writable data properties of all global objects. + * Must be powerless. + * Maps from property name to the actual value + */ +const constantProperties= { + // *** Value Properties of the Global Object + + Infinity, + NaN, + undefined}; + + +/** + * universalPropertyNames + * Properties of all global objects. + * Must be powerless. + * Maps from property name to the intrinsic name in the whitelist. + */$h‍_once.constantProperties(constantProperties); +const universalPropertyNames= { + // *** Function Properties of the Global Object + + isFinite: 'isFinite', + isNaN: 'isNaN', + parseFloat: 'parseFloat', + parseInt: 'parseInt', + + decodeURI: 'decodeURI', + decodeURIComponent: 'decodeURIComponent', + encodeURI: 'encodeURI', + encodeURIComponent: 'encodeURIComponent', + + // *** Constructor Properties of the Global Object + + Array: 'Array', + ArrayBuffer: 'ArrayBuffer', + BigInt: 'BigInt', + BigInt64Array: 'BigInt64Array', + BigUint64Array: 'BigUint64Array', + Boolean: 'Boolean', + DataView: 'DataView', + EvalError: 'EvalError', + Float32Array: 'Float32Array', + Float64Array: 'Float64Array', + Int8Array: 'Int8Array', + Int16Array: 'Int16Array', + Int32Array: 'Int32Array', + Map: 'Map', + Number: 'Number', + Object: 'Object', + Promise: 'Promise', + Proxy: 'Proxy', + RangeError: 'RangeError', + ReferenceError: 'ReferenceError', + Set: 'Set', + String: 'String', + SyntaxError: 'SyntaxError', + TypeError: 'TypeError', + Uint8Array: 'Uint8Array', + Uint8ClampedArray: 'Uint8ClampedArray', + Uint16Array: 'Uint16Array', + Uint32Array: 'Uint32Array', + URIError: 'URIError', + WeakMap: 'WeakMap', + WeakSet: 'WeakSet', + // https://github.com/tc39/proposal-iterator-helpers + Iterator: 'Iterator', + // https://github.com/tc39/proposal-async-iterator-helpers + AsyncIterator: 'AsyncIterator', + + // *** Other Properties of the Global Object + + JSON: 'JSON', + Reflect: 'Reflect', + + // *** Annex B + + escape: 'escape', + unescape: 'unescape', + + // ESNext + + lockdown: 'lockdown', + harden: 'harden', + HandledPromise: 'HandledPromise' // TODO: Until Promise.delegate (see below). +}; + +/** + * initialGlobalPropertyNames + * Those found only on the initial global, i.e., the global of the + * start compartment, as well as any compartments created before lockdown. + * These may provide much of the power provided by the original. + * Maps from property name to the intrinsic name in the whitelist. + */$h‍_once.universalPropertyNames(universalPropertyNames); +const initialGlobalPropertyNames= { + // *** Constructor Properties of the Global Object + + Date: '%InitialDate%', + Error: '%InitialError%', + RegExp: '%InitialRegExp%', + + // Omit `Symbol`, because we want the original to appear on the + // start compartment without passing through the whitelist mechanism, since + // we want to preserve all its properties, even if we never heard of them. + // Symbol: '%InitialSymbol%', + + // *** Other Properties of the Global Object + + Math: '%InitialMath%', + + // ESNext + + // From Error-stack proposal + // Only on initial global. No corresponding + // powerless form for other globals. + getStackString: '%InitialGetStackString%' + + // TODO https://github.com/Agoric/SES-shim/issues/551 + // Need initial WeakRef and FinalizationGroup in + // start compartment only. +}; + +/** + * sharedGlobalPropertyNames + * Those found only on the globals of new compartments created after lockdown, + * which must therefore be powerless. + * Maps from property name to the intrinsic name in the whitelist. + */$h‍_once.initialGlobalPropertyNames(initialGlobalPropertyNames); +const sharedGlobalPropertyNames= { + // *** Constructor Properties of the Global Object + + Date: '%SharedDate%', + Error: '%SharedError%', + RegExp: '%SharedRegExp%', + Symbol: '%SharedSymbol%', + + // *** Other Properties of the Global Object + + Math: '%SharedMath%'}; + + +/** + * uniqueGlobalPropertyNames + * Those made separately for each global, including the initial global + * of the start compartment. + * Maps from property name to the intrinsic name in the whitelist + * (which is currently always the same). + */$h‍_once.sharedGlobalPropertyNames(sharedGlobalPropertyNames); +const uniqueGlobalPropertyNames= { + // *** Value Properties of the Global Object + + globalThis: '%UniqueGlobalThis%', + + // *** Function Properties of the Global Object + + eval: '%UniqueEval%', + + // *** Constructor Properties of the Global Object + + Function: '%UniqueFunction%', + + // *** Other Properties of the Global Object + + // ESNext + + Compartment: '%UniqueCompartment%' + // According to current agreements, eventually the Realm constructor too. + // 'Realm', +}; + +// All the "subclasses" of Error. These are collectively represented in the +// ECMAScript spec by the meta variable NativeError. +// TODO Add AggregateError https://github.com/Agoric/SES-shim/issues/550 +$h‍_once.uniqueGlobalPropertyNames(uniqueGlobalPropertyNames);const NativeErrors=[ + EvalError, + RangeError, + ReferenceError, + SyntaxError, + TypeError, + URIError]; + + +/** + *

Each JSON record enumerates the disposition of the properties on + * some corresponding intrinsic object. + * + *

All records are made of key-value pairs where the key + * is the property to process, and the value is the associated + * dispositions a.k.a. the "permit". Those permits can be: + *

    + *
  • The boolean value "false", in which case this property is + * blacklisted and simply removed. Properties not mentioned + * are also considered blacklisted and are removed. + *
  • A string value equal to a primitive ("number", "string", etc), + * in which case the property is whitelisted if its value property + * is typeof the given type. For example, {@code "Infinity"} leads to + * "number" and property values that fail {@code typeof "number"}. + * are removed. + *
  • A string value equal to an intinsic name ("ObjectPrototype", + * "Array", etc), in which case the property whitelisted if its + * value property is equal to the value of the corresponfing + * intrinsics. For example, {@code Map.prototype} leads to + * "MapPrototype" and the property is removed if its value is + * not equal to %MapPrototype% + *
  • Another record, in which case this property is simply + * whitelisted and that next record represents the disposition of + * the object which is its value. For example, {@code "Object"} + * leads to another record explaining what properties {@code + * "Object"} may have and how each such property should be treated. + * + *

    Notes: + *

  • "[[Proto]]" is used to refer to the "[[Prototype]]" internal + * slot, which says which object this object inherits from. + *
  • "--proto--" is used to refer to the "__proto__" property name, + * which is the name of an accessor property on Object.prototype. + * In practice, it is used to access the [[Proto]] internal slot, + * but is distinct from the internal slot itself. We use + * "--proto--" rather than "__proto__" below because "__proto__" + * in an object literal is special syntax rather than a normal + * property definition. + *
  • "ObjectPrototype" is the default "[[Proto]]" (when not specified). + *
  • Constants "fn" and "getter" are used to keep the structure DRY. + *
  • Symbol properties are listed as follow: + *
  • Well-known symbols use the "@@name" form. + *
  • Registered symbols use the "RegisteredSymbol(key)" form. + *
  • Unique symbols use the "UniqueSymbol(description)" form. + */ + +// Function Instances +$h‍_once.NativeErrors(NativeErrors);const FunctionInstance={ + '[[Proto]]': '%FunctionPrototype%', + length: 'number', + name: 'string' + // Do not specify "prototype" here, since only Function instances that can + // be used as a constructor have a prototype property. For constructors, + // since prototype properties are instance-specific, we define it there. +}; + +// AsyncFunction Instances +$h‍_once.FunctionInstance(FunctionInstance);const AsyncFunctionInstance={ + // This property is not mentioned in ECMA 262, but is present in V8 and + // necessary for lockdown to succeed. + '[[Proto]]': '%AsyncFunctionPrototype%'}; + + +// Aliases +$h‍_once.AsyncFunctionInstance(AsyncFunctionInstance);const fn=FunctionInstance; +const asyncFn= AsyncFunctionInstance; + +const getter= { + get: fn, + set: 'undefined'}; + + +// Possible but not encountered in the specs +// export const setter = { +// get: 'undefined', +// set: fn, +// }; + +const accessor= { + get: fn, + set: fn}; + + +const isAccessorPermit= (permit)=>{ + return permit=== getter|| permit=== accessor; + }; + +// NativeError Object Structure +$h‍_once.isAccessorPermit(isAccessorPermit);function NativeError(prototype){ + return { + // Properties of the NativeError Constructors + '[[Proto]]': '%SharedError%', + + // NativeError.prototype + prototype}; + + } + +function NativeErrorPrototype(constructor) { + return { + // Properties of the NativeError Prototype Objects + '[[Proto]]': '%ErrorPrototype%', + constructor, + message: 'string', + name: 'string', + // Redundantly present only on v8. Safe to remove. + toString: false, + // Superfluously present in some versions of V8. + // https://github.com/tc39/notes/blob/master/meetings/2021-10/oct-26.md#:~:text=However%2C%20Chrome%2093,and%20node%2016.11. + cause: false}; + + } + +// The TypedArray Constructors +function TypedArray(prototype) { + return { + // Properties of the TypedArray Constructors + '[[Proto]]': '%TypedArray%', + BYTES_PER_ELEMENT: 'number', + prototype}; + + } + +function TypedArrayPrototype(constructor) { + return { + // Properties of the TypedArray Prototype Objects + '[[Proto]]': '%TypedArrayPrototype%', + BYTES_PER_ELEMENT: 'number', + constructor}; + + } + +// Without Math.random +const CommonMath= { + E: 'number', + LN10: 'number', + LN2: 'number', + LOG10E: 'number', + LOG2E: 'number', + PI: 'number', + SQRT1_2: 'number', + SQRT2: 'number', + '@@toStringTag': 'string', + abs: fn, + acos: fn, + acosh: fn, + asin: fn, + asinh: fn, + atan: fn, + atanh: fn, + atan2: fn, + cbrt: fn, + ceil: fn, + clz32: fn, + cos: fn, + cosh: fn, + exp: fn, + expm1: fn, + floor: fn, + fround: fn, + hypot: fn, + imul: fn, + log: fn, + log1p: fn, + log10: fn, + log2: fn, + max: fn, + min: fn, + pow: fn, + round: fn, + sign: fn, + sin: fn, + sinh: fn, + sqrt: fn, + tan: fn, + tanh: fn, + trunc: fn, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + idiv: false, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + idivmod: false, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + imod: false, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + imuldiv: false, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + irem: false, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + mod: false}; + + +const permitted= { + // ECMA https://tc39.es/ecma262 + + // The intrinsics object has no prototype to avoid conflicts. + '[[Proto]]': null, + + // %ThrowTypeError% + '%ThrowTypeError%': fn, + + // *** The Global Object + + // *** Value Properties of the Global Object + Infinity: 'number', + NaN: 'number', + undefined: 'undefined', + + // *** Function Properties of the Global Object + + // eval + '%UniqueEval%': fn, + isFinite: fn, + isNaN: fn, + parseFloat: fn, + parseInt: fn, + decodeURI: fn, + decodeURIComponent: fn, + encodeURI: fn, + encodeURIComponent: fn, + + // *** Fundamental Objects + + Object: { + // Properties of the Object Constructor + '[[Proto]]': '%FunctionPrototype%', + assign: fn, + create: fn, + defineProperties: fn, + defineProperty: fn, + entries: fn, + freeze: fn, + fromEntries: fn, + getOwnPropertyDescriptor: fn, + getOwnPropertyDescriptors: fn, + getOwnPropertyNames: fn, + getOwnPropertySymbols: fn, + getPrototypeOf: fn, + hasOwn: fn, + is: fn, + isExtensible: fn, + isFrozen: fn, + isSealed: fn, + keys: fn, + preventExtensions: fn, + prototype: '%ObjectPrototype%', + seal: fn, + setPrototypeOf: fn, + values: fn, + // https://github.com/tc39/proposal-array-grouping + groupBy: fn}, + + + '%ObjectPrototype%': { + // Properties of the Object Prototype Object + '[[Proto]]': null, + constructor: 'Object', + hasOwnProperty: fn, + isPrototypeOf: fn, + propertyIsEnumerable: fn, + toLocaleString: fn, + toString: fn, + valueOf: fn, + + // Annex B: Additional Properties of the Object.prototype Object + + // See note in header about the difference between [[Proto]] and --proto-- + // special notations. + '--proto--': accessor, + __defineGetter__: fn, + __defineSetter__: fn, + __lookupGetter__: fn, + __lookupSetter__: fn}, + + + '%UniqueFunction%': { + // Properties of the Function Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%FunctionPrototype%'}, + + + '%InertFunction%': { + '[[Proto]]': '%FunctionPrototype%', + prototype: '%FunctionPrototype%'}, + + + '%FunctionPrototype%': { + apply: fn, + bind: fn, + call: fn, + constructor: '%InertFunction%', + toString: fn, + '@@hasInstance': fn, + // proposed but not yet std. To be removed if there + caller: false, + // proposed but not yet std. To be removed if there + arguments: false}, + + + Boolean: { + // Properties of the Boolean Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%BooleanPrototype%'}, + + + '%BooleanPrototype%': { + constructor: 'Boolean', + toString: fn, + valueOf: fn}, + + + '%SharedSymbol%': { + // Properties of the Symbol Constructor + '[[Proto]]': '%FunctionPrototype%', + asyncDispose: 'symbol', + asyncIterator: 'symbol', + dispose: 'symbol', + for: fn, + hasInstance: 'symbol', + isConcatSpreadable: 'symbol', + iterator: 'symbol', + keyFor: fn, + match: 'symbol', + matchAll: 'symbol', + prototype: '%SymbolPrototype%', + replace: 'symbol', + search: 'symbol', + species: 'symbol', + split: 'symbol', + toPrimitive: 'symbol', + toStringTag: 'symbol', + unscopables: 'symbol', + // Seen at core-js https://github.com/zloirock/core-js#ecmascript-symbol + useSimple: false, + // Seen at core-js https://github.com/zloirock/core-js#ecmascript-symbol + useSetter: false}, + + + '%SymbolPrototype%': { + // Properties of the Symbol Prototype Object + constructor: '%SharedSymbol%', + description: getter, + toString: fn, + valueOf: fn, + '@@toPrimitive': fn, + '@@toStringTag': 'string'}, + + + '%InitialError%': { + // Properties of the Error Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%ErrorPrototype%', + // Non standard, v8 only, used by tap + captureStackTrace: fn, + // Non standard, v8 only, used by tap, tamed to accessor + stackTraceLimit: accessor, + // Non standard, v8 only, used by several, tamed to accessor + prepareStackTrace: accessor}, + + + '%SharedError%': { + // Properties of the Error Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%ErrorPrototype%', + // Non standard, v8 only, used by tap + captureStackTrace: fn, + // Non standard, v8 only, used by tap, tamed to accessor + stackTraceLimit: accessor, + // Non standard, v8 only, used by several, tamed to accessor + prepareStackTrace: accessor}, + + + '%ErrorPrototype%': { + constructor: '%SharedError%', + message: 'string', + name: 'string', + toString: fn, + // proposed de-facto, assumed TODO + // Seen on FF Nightly 88.0a1 + at: false, + // Seen on FF and XS + stack: accessor, + // Superfluously present in some versions of V8. + // https://github.com/tc39/notes/blob/master/meetings/2021-10/oct-26.md#:~:text=However%2C%20Chrome%2093,and%20node%2016.11. + cause: false}, + + + // NativeError + + EvalError: NativeError('%EvalErrorPrototype%'), + RangeError: NativeError('%RangeErrorPrototype%'), + ReferenceError: NativeError('%ReferenceErrorPrototype%'), + SyntaxError: NativeError('%SyntaxErrorPrototype%'), + TypeError: NativeError('%TypeErrorPrototype%'), + URIError: NativeError('%URIErrorPrototype%'), + + '%EvalErrorPrototype%': NativeErrorPrototype('EvalError'), + '%RangeErrorPrototype%': NativeErrorPrototype('RangeError'), + '%ReferenceErrorPrototype%': NativeErrorPrototype('ReferenceError'), + '%SyntaxErrorPrototype%': NativeErrorPrototype('SyntaxError'), + '%TypeErrorPrototype%': NativeErrorPrototype('TypeError'), + '%URIErrorPrototype%': NativeErrorPrototype('URIError'), + + // *** Numbers and Dates + + Number: { + // Properties of the Number Constructor + '[[Proto]]': '%FunctionPrototype%', + EPSILON: 'number', + isFinite: fn, + isInteger: fn, + isNaN: fn, + isSafeInteger: fn, + MAX_SAFE_INTEGER: 'number', + MAX_VALUE: 'number', + MIN_SAFE_INTEGER: 'number', + MIN_VALUE: 'number', + NaN: 'number', + NEGATIVE_INFINITY: 'number', + parseFloat: fn, + parseInt: fn, + POSITIVE_INFINITY: 'number', + prototype: '%NumberPrototype%'}, + + + '%NumberPrototype%': { + // Properties of the Number Prototype Object + constructor: 'Number', + toExponential: fn, + toFixed: fn, + toLocaleString: fn, + toPrecision: fn, + toString: fn, + valueOf: fn}, + + + BigInt: { + // Properties of the BigInt Constructor + '[[Proto]]': '%FunctionPrototype%', + asIntN: fn, + asUintN: fn, + prototype: '%BigIntPrototype%', + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + bitLength: false, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + fromArrayBuffer: false}, + + + '%BigIntPrototype%': { + constructor: 'BigInt', + toLocaleString: fn, + toString: fn, + valueOf: fn, + '@@toStringTag': 'string'}, + + + '%InitialMath%': { + ...CommonMath, + // `%InitialMath%.random()` has the standard unsafe behavior + random: fn}, + + + '%SharedMath%': { + ...CommonMath, + // `%SharedMath%.random()` is tamed to always throw + random: fn}, + + + '%InitialDate%': { + // Properties of the Date Constructor + '[[Proto]]': '%FunctionPrototype%', + now: fn, + parse: fn, + prototype: '%DatePrototype%', + UTC: fn}, + + + '%SharedDate%': { + // Properties of the Date Constructor + '[[Proto]]': '%FunctionPrototype%', + // `%SharedDate%.now()` is tamed to always throw + now: fn, + parse: fn, + prototype: '%DatePrototype%', + UTC: fn}, + + + '%DatePrototype%': { + constructor: '%SharedDate%', + getDate: fn, + getDay: fn, + getFullYear: fn, + getHours: fn, + getMilliseconds: fn, + getMinutes: fn, + getMonth: fn, + getSeconds: fn, + getTime: fn, + getTimezoneOffset: fn, + getUTCDate: fn, + getUTCDay: fn, + getUTCFullYear: fn, + getUTCHours: fn, + getUTCMilliseconds: fn, + getUTCMinutes: fn, + getUTCMonth: fn, + getUTCSeconds: fn, + setDate: fn, + setFullYear: fn, + setHours: fn, + setMilliseconds: fn, + setMinutes: fn, + setMonth: fn, + setSeconds: fn, + setTime: fn, + setUTCDate: fn, + setUTCFullYear: fn, + setUTCHours: fn, + setUTCMilliseconds: fn, + setUTCMinutes: fn, + setUTCMonth: fn, + setUTCSeconds: fn, + toDateString: fn, + toISOString: fn, + toJSON: fn, + toLocaleDateString: fn, + toLocaleString: fn, + toLocaleTimeString: fn, + toString: fn, + toTimeString: fn, + toUTCString: fn, + valueOf: fn, + '@@toPrimitive': fn, + + // Annex B: Additional Properties of the Date.prototype Object + getYear: fn, + setYear: fn, + toGMTString: fn}, + + + // Text Processing + + String: { + // Properties of the String Constructor + '[[Proto]]': '%FunctionPrototype%', + fromCharCode: fn, + fromCodePoint: fn, + prototype: '%StringPrototype%', + raw: fn, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + fromArrayBuffer: false}, + + + '%StringPrototype%': { + // Properties of the String Prototype Object + length: 'number', + at: fn, + charAt: fn, + charCodeAt: fn, + codePointAt: fn, + concat: fn, + constructor: 'String', + endsWith: fn, + includes: fn, + indexOf: fn, + lastIndexOf: fn, + localeCompare: fn, + match: fn, + matchAll: fn, + normalize: fn, + padEnd: fn, + padStart: fn, + repeat: fn, + replace: fn, + replaceAll: fn, // ES2021 + search: fn, + slice: fn, + split: fn, + startsWith: fn, + substring: fn, + toLocaleLowerCase: fn, + toLocaleUpperCase: fn, + toLowerCase: fn, + toString: fn, + toUpperCase: fn, + trim: fn, + trimEnd: fn, + trimStart: fn, + valueOf: fn, + '@@iterator': fn, + + // Annex B: Additional Properties of the String.prototype Object + substr: fn, + anchor: fn, + big: fn, + blink: fn, + bold: fn, + fixed: fn, + fontcolor: fn, + fontsize: fn, + italics: fn, + link: fn, + small: fn, + strike: fn, + sub: fn, + sup: fn, + trimLeft: fn, + trimRight: fn, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + compare: false, + // https://github.com/tc39/proposal-is-usv-string + isWellFormed: fn, + toWellFormed: fn, + unicodeSets: fn}, + + + '%StringIteratorPrototype%': { + '[[Proto]]': '%IteratorPrototype%', + next: fn, + '@@toStringTag': 'string'}, + + + '%InitialRegExp%': { + // Properties of the RegExp Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%RegExpPrototype%', + '@@species': getter, + + // The https://github.com/tc39/proposal-regexp-legacy-features + // are all optional, unsafe, and omitted + input: false, + $_: false, + lastMatch: false, + '$&': false, + lastParen: false, + '$+': false, + leftContext: false, + '$`': false, + rightContext: false, + "$'": false, + $1: false, + $2: false, + $3: false, + $4: false, + $5: false, + $6: false, + $7: false, + $8: false, + $9: false}, + + + '%SharedRegExp%': { + // Properties of the RegExp Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%RegExpPrototype%', + '@@species': getter}, + + + '%RegExpPrototype%': { + // Properties of the RegExp Prototype Object + constructor: '%SharedRegExp%', + exec: fn, + dotAll: getter, + flags: getter, + global: getter, + hasIndices: getter, + ignoreCase: getter, + '@@match': fn, + '@@matchAll': fn, + multiline: getter, + '@@replace': fn, + '@@search': fn, + source: getter, + '@@split': fn, + sticky: getter, + test: fn, + toString: fn, + unicode: getter, + unicodeSets: getter, + + // Annex B: Additional Properties of the RegExp.prototype Object + compile: false // UNSAFE and suppressed. +}, + + '%RegExpStringIteratorPrototype%': { + // The %RegExpStringIteratorPrototype% Object + '[[Proto]]': '%IteratorPrototype%', + next: fn, + '@@toStringTag': 'string'}, + + + // Indexed Collections + + Array: { + // Properties of the Array Constructor + '[[Proto]]': '%FunctionPrototype%', + from: fn, + isArray: fn, + of: fn, + prototype: '%ArrayPrototype%', + '@@species': getter, + + // Stage 3: + // https://tc39.es/proposal-relative-indexing-method/ + at: fn, + // https://tc39.es/proposal-array-from-async/ + fromAsync: fn}, + + + '%ArrayPrototype%': { + // Properties of the Array Prototype Object + at: fn, + length: 'number', + concat: fn, + constructor: 'Array', + copyWithin: fn, + entries: fn, + every: fn, + fill: fn, + filter: fn, + find: fn, + findIndex: fn, + flat: fn, + flatMap: fn, + forEach: fn, + includes: fn, + indexOf: fn, + join: fn, + keys: fn, + lastIndexOf: fn, + map: fn, + pop: fn, + push: fn, + reduce: fn, + reduceRight: fn, + reverse: fn, + shift: fn, + slice: fn, + some: fn, + sort: fn, + splice: fn, + toLocaleString: fn, + toString: fn, + unshift: fn, + values: fn, + '@@iterator': fn, + '@@unscopables': { + '[[Proto]]': null, + copyWithin: 'boolean', + entries: 'boolean', + fill: 'boolean', + find: 'boolean', + findIndex: 'boolean', + flat: 'boolean', + flatMap: 'boolean', + includes: 'boolean', + keys: 'boolean', + values: 'boolean', + // Failed tc39 proposal + // Seen on FF Nightly 88.0a1 + at: 'boolean', + // See https://github.com/tc39/proposal-array-find-from-last + findLast: 'boolean', + findLastIndex: 'boolean', + // https://github.com/tc39/proposal-change-array-by-copy + toReversed: 'boolean', + toSorted: 'boolean', + toSpliced: 'boolean', + with: 'boolean', + // https://github.com/tc39/proposal-array-grouping + group: 'boolean', + groupToMap: 'boolean', + groupBy: 'boolean'}, + + // See https://github.com/tc39/proposal-array-find-from-last + findLast: fn, + findLastIndex: fn, + // https://github.com/tc39/proposal-change-array-by-copy + toReversed: fn, + toSorted: fn, + toSpliced: fn, + with: fn, + // https://github.com/tc39/proposal-array-grouping + group: fn, // Not in proposal? Where? + groupToMap: fn, // Not in proposal? Where? + groupBy: fn}, + + + '%ArrayIteratorPrototype%': { + // The %ArrayIteratorPrototype% Object + '[[Proto]]': '%IteratorPrototype%', + next: fn, + '@@toStringTag': 'string'}, + + + // *** TypedArray Objects + + '%TypedArray%': { + // Properties of the %TypedArray% Intrinsic Object + '[[Proto]]': '%FunctionPrototype%', + from: fn, + of: fn, + prototype: '%TypedArrayPrototype%', + '@@species': getter}, + + + '%TypedArrayPrototype%': { + at: fn, + buffer: getter, + byteLength: getter, + byteOffset: getter, + constructor: '%TypedArray%', + copyWithin: fn, + entries: fn, + every: fn, + fill: fn, + filter: fn, + find: fn, + findIndex: fn, + forEach: fn, + includes: fn, + indexOf: fn, + join: fn, + keys: fn, + lastIndexOf: fn, + length: getter, + map: fn, + reduce: fn, + reduceRight: fn, + reverse: fn, + set: fn, + slice: fn, + some: fn, + sort: fn, + subarray: fn, + toLocaleString: fn, + toString: fn, + values: fn, + '@@iterator': fn, + '@@toStringTag': getter, + // See https://github.com/tc39/proposal-array-find-from-last + findLast: fn, + findLastIndex: fn, + // https://github.com/tc39/proposal-change-array-by-copy + toReversed: fn, + toSorted: fn, + with: fn}, + + + // The TypedArray Constructors + + BigInt64Array: TypedArray('%BigInt64ArrayPrototype%'), + BigUint64Array: TypedArray('%BigUint64ArrayPrototype%'), + Float32Array: TypedArray('%Float32ArrayPrototype%'), + Float64Array: TypedArray('%Float64ArrayPrototype%'), + Int16Array: TypedArray('%Int16ArrayPrototype%'), + Int32Array: TypedArray('%Int32ArrayPrototype%'), + Int8Array: TypedArray('%Int8ArrayPrototype%'), + Uint16Array: TypedArray('%Uint16ArrayPrototype%'), + Uint32Array: TypedArray('%Uint32ArrayPrototype%'), + Uint8Array: TypedArray('%Uint8ArrayPrototype%'), + Uint8ClampedArray: TypedArray('%Uint8ClampedArrayPrototype%'), + + '%BigInt64ArrayPrototype%': TypedArrayPrototype('BigInt64Array'), + '%BigUint64ArrayPrototype%': TypedArrayPrototype('BigUint64Array'), + '%Float32ArrayPrototype%': TypedArrayPrototype('Float32Array'), + '%Float64ArrayPrototype%': TypedArrayPrototype('Float64Array'), + '%Int16ArrayPrototype%': TypedArrayPrototype('Int16Array'), + '%Int32ArrayPrototype%': TypedArrayPrototype('Int32Array'), + '%Int8ArrayPrototype%': TypedArrayPrototype('Int8Array'), + '%Uint16ArrayPrototype%': TypedArrayPrototype('Uint16Array'), + '%Uint32ArrayPrototype%': TypedArrayPrototype('Uint32Array'), + '%Uint8ArrayPrototype%': TypedArrayPrototype('Uint8Array'), + '%Uint8ClampedArrayPrototype%': TypedArrayPrototype('Uint8ClampedArray'), + + // *** Keyed Collections + + Map: { + // Properties of the Map Constructor + '[[Proto]]': '%FunctionPrototype%', + '@@species': getter, + prototype: '%MapPrototype%', + // https://github.com/tc39/proposal-array-grouping + groupBy: fn}, + + + '%MapPrototype%': { + clear: fn, + constructor: 'Map', + delete: fn, + entries: fn, + forEach: fn, + get: fn, + has: fn, + keys: fn, + set: fn, + size: getter, + values: fn, + '@@iterator': fn, + '@@toStringTag': 'string'}, + + + '%MapIteratorPrototype%': { + // The %MapIteratorPrototype% Object + '[[Proto]]': '%IteratorPrototype%', + next: fn, + '@@toStringTag': 'string'}, + + + Set: { + // Properties of the Set Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%SetPrototype%', + '@@species': getter}, + + + '%SetPrototype%': { + add: fn, + clear: fn, + constructor: 'Set', + delete: fn, + entries: fn, + forEach: fn, + has: fn, + keys: fn, + size: getter, + values: fn, + '@@iterator': fn, + '@@toStringTag': 'string'}, + + + '%SetIteratorPrototype%': { + // The %SetIteratorPrototype% Object + '[[Proto]]': '%IteratorPrototype%', + next: fn, + '@@toStringTag': 'string'}, + + + WeakMap: { + // Properties of the WeakMap Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%WeakMapPrototype%'}, + + + '%WeakMapPrototype%': { + constructor: 'WeakMap', + delete: fn, + get: fn, + has: fn, + set: fn, + '@@toStringTag': 'string'}, + + + WeakSet: { + // Properties of the WeakSet Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%WeakSetPrototype%'}, + + + '%WeakSetPrototype%': { + add: fn, + constructor: 'WeakSet', + delete: fn, + has: fn, + '@@toStringTag': 'string'}, + + + // *** Structured Data + + ArrayBuffer: { + // Properties of the ArrayBuffer Constructor + '[[Proto]]': '%FunctionPrototype%', + isView: fn, + prototype: '%ArrayBufferPrototype%', + '@@species': getter, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + fromString: false, + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + fromBigInt: false}, + + + '%ArrayBufferPrototype%': { + byteLength: getter, + constructor: 'ArrayBuffer', + slice: fn, + '@@toStringTag': 'string', + // See https://github.com/Moddable-OpenSource/moddable/issues/523 + concat: false, + // See https://github.com/tc39/proposal-resizablearraybuffer + transfer: fn, + resize: fn, + resizable: getter, + maxByteLength: getter, + // https://github.com/tc39/proposal-arraybuffer-transfer + transferToFixedLength: fn, + detached: getter}, + + + // SharedArrayBuffer Objects + SharedArrayBuffer: false, // UNSAFE and purposely suppressed. + '%SharedArrayBufferPrototype%': false, // UNSAFE and purposely suppressed. + + DataView: { + // Properties of the DataView Constructor + '[[Proto]]': '%FunctionPrototype%', + BYTES_PER_ELEMENT: 'number', // Non std but undeletable on Safari. + prototype: '%DataViewPrototype%'}, + + + '%DataViewPrototype%': { + buffer: getter, + byteLength: getter, + byteOffset: getter, + constructor: 'DataView', + getBigInt64: fn, + getBigUint64: fn, + getFloat32: fn, + getFloat64: fn, + getInt8: fn, + getInt16: fn, + getInt32: fn, + getUint8: fn, + getUint16: fn, + getUint32: fn, + setBigInt64: fn, + setBigUint64: fn, + setFloat32: fn, + setFloat64: fn, + setInt8: fn, + setInt16: fn, + setInt32: fn, + setUint8: fn, + setUint16: fn, + setUint32: fn, + '@@toStringTag': 'string'}, + + + // Atomics + Atomics: false, // UNSAFE and suppressed. + + JSON: { + parse: fn, + stringify: fn, + '@@toStringTag': 'string', + // https://github.com/tc39/proposal-json-parse-with-source/ + rawJSON: fn, + isRawJSON: fn}, + + + // *** Control Abstraction Objects + + // https://github.com/tc39/proposal-iterator-helpers + Iterator: { + // Properties of the Iterator Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%IteratorPrototype%', + from: fn}, + + + '%IteratorPrototype%': { + // The %IteratorPrototype% Object + '@@iterator': fn, + // https://github.com/tc39/proposal-iterator-helpers + constructor: 'Iterator', + map: fn, + filter: fn, + take: fn, + drop: fn, + flatMap: fn, + reduce: fn, + toArray: fn, + forEach: fn, + some: fn, + every: fn, + find: fn, + '@@toStringTag': 'string', + // https://github.com/tc39/proposal-async-iterator-helpers + toAsync: fn}, + + + // https://github.com/tc39/proposal-iterator-helpers + '%WrapForValidIteratorPrototype%': { + '[[Proto]]': '%IteratorPrototype%', + next: fn, + return: fn}, + + + // https://github.com/tc39/proposal-iterator-helpers + '%IteratorHelperPrototype%': { + '[[Proto]]': '%IteratorPrototype%', + next: fn, + return: fn, + '@@toStringTag': 'string'}, + + + // https://github.com/tc39/proposal-async-iterator-helpers + AsyncIterator: { + // Properties of the Iterator Constructor + '[[Proto]]': '%FunctionPrototype%', + prototype: '%AsyncIteratorPrototype%', + from: fn}, + + + '%AsyncIteratorPrototype%': { + // The %AsyncIteratorPrototype% Object + '@@asyncIterator': fn, + // https://github.com/tc39/proposal-async-iterator-helpers + constructor: 'AsyncIterator', + map: fn, + filter: fn, + take: fn, + drop: fn, + flatMap: fn, + reduce: fn, + toArray: fn, + forEach: fn, + some: fn, + every: fn, + find: fn, + '@@toStringTag': 'string'}, + + + // https://github.com/tc39/proposal-async-iterator-helpers + '%WrapForValidAsyncIteratorPrototype%': { + '[[Proto]]': '%AsyncIteratorPrototype%', + next: fn, + return: fn}, + + + // https://github.com/tc39/proposal-async-iterator-helpers + '%AsyncIteratorHelperPrototype%': { + '[[Proto]]': '%AsyncIteratorPrototype%', + next: fn, + return: fn, + '@@toStringTag': 'string'}, + + + '%InertGeneratorFunction%': { + // Properties of the GeneratorFunction Constructor + '[[Proto]]': '%InertFunction%', + prototype: '%Generator%'}, + + + '%Generator%': { + // Properties of the GeneratorFunction Prototype Object + '[[Proto]]': '%FunctionPrototype%', + constructor: '%InertGeneratorFunction%', + prototype: '%GeneratorPrototype%', + '@@toStringTag': 'string'}, + + + '%InertAsyncGeneratorFunction%': { + // Properties of the AsyncGeneratorFunction Constructor + '[[Proto]]': '%InertFunction%', + prototype: '%AsyncGenerator%'}, + + + '%AsyncGenerator%': { + // Properties of the AsyncGeneratorFunction Prototype Object + '[[Proto]]': '%FunctionPrototype%', + constructor: '%InertAsyncGeneratorFunction%', + prototype: '%AsyncGeneratorPrototype%', + // length prop added here for React Native jsc-android + // https://github.com/endojs/endo/issues/660 + // https://github.com/react-native-community/jsc-android-buildscripts/issues/181 + length: 'number', + '@@toStringTag': 'string'}, + + + '%GeneratorPrototype%': { + // Properties of the Generator Prototype Object + '[[Proto]]': '%IteratorPrototype%', + constructor: '%Generator%', + next: fn, + return: fn, + throw: fn, + '@@toStringTag': 'string'}, + + + '%AsyncGeneratorPrototype%': { + // Properties of the AsyncGenerator Prototype Object + '[[Proto]]': '%AsyncIteratorPrototype%', + constructor: '%AsyncGenerator%', + next: fn, + return: fn, + throw: fn, + '@@toStringTag': 'string'}, + + + // TODO: To be replaced with Promise.delegate + // + // The HandledPromise global variable shimmed by `@agoric/eventual-send/shim` + // implements an initial version of the eventual send specification at: + // https://github.com/tc39/proposal-eventual-send + // + // We will likely change this to add a property to Promise called + // Promise.delegate and put static methods on it, which will necessitate + // another whitelist change to update to the current proposed standard. + HandledPromise: { + '[[Proto]]': 'Promise', + applyFunction: fn, + applyFunctionSendOnly: fn, + applyMethod: fn, + applyMethodSendOnly: fn, + get: fn, + getSendOnly: fn, + prototype: '%PromisePrototype%', + resolve: fn}, + + + Promise: { + // Properties of the Promise Constructor + '[[Proto]]': '%FunctionPrototype%', + all: fn, + allSettled: fn, + // To transition from `false` to `fn` once we also have `AggregateError` + // TODO https://github.com/Agoric/SES-shim/issues/550 + any: false, // ES2021 + prototype: '%PromisePrototype%', + race: fn, + reject: fn, + resolve: fn, + '@@species': getter}, + + + '%PromisePrototype%': { + // Properties of the Promise Prototype Object + catch: fn, + constructor: 'Promise', + finally: fn, + then: fn, + '@@toStringTag': 'string', + // Non-standard, used in node to prevent async_hooks from breaking + 'UniqueSymbol(async_id_symbol)': accessor, + 'UniqueSymbol(trigger_async_id_symbol)': accessor, + 'UniqueSymbol(destroyed)': accessor}, + + + '%InertAsyncFunction%': { + // Properties of the AsyncFunction Constructor + '[[Proto]]': '%InertFunction%', + prototype: '%AsyncFunctionPrototype%'}, + + + '%AsyncFunctionPrototype%': { + // Properties of the AsyncFunction Prototype Object + '[[Proto]]': '%FunctionPrototype%', + constructor: '%InertAsyncFunction%', + // length prop added here for React Native jsc-android + // https://github.com/endojs/endo/issues/660 + // https://github.com/react-native-community/jsc-android-buildscripts/issues/181 + length: 'number', + '@@toStringTag': 'string'}, + + + // Reflection + + Reflect: { + // The Reflect Object + // Not a function object. + apply: fn, + construct: fn, + defineProperty: fn, + deleteProperty: fn, + get: fn, + getOwnPropertyDescriptor: fn, + getPrototypeOf: fn, + has: fn, + isExtensible: fn, + ownKeys: fn, + preventExtensions: fn, + set: fn, + setPrototypeOf: fn, + '@@toStringTag': 'string'}, + + + Proxy: { + // Properties of the Proxy Constructor + '[[Proto]]': '%FunctionPrototype%', + revocable: fn}, + + + // Appendix B + + // Annex B: Additional Properties of the Global Object + + escape: fn, + unescape: fn, + + // Proposed + + '%UniqueCompartment%': { + '[[Proto]]': '%FunctionPrototype%', + prototype: '%CompartmentPrototype%', + toString: fn}, + + + '%InertCompartment%': { + '[[Proto]]': '%FunctionPrototype%', + prototype: '%CompartmentPrototype%', + toString: fn}, + + + '%CompartmentPrototype%': { + constructor: '%InertCompartment%', + evaluate: fn, + globalThis: getter, + name: getter, + // Should this be proposed? + toString: fn, + import: asyncFn, + load: asyncFn, + importNow: fn, + module: fn}, + + + lockdown: fn, + harden: { ...fn, isFake: 'boolean'}, + + '%InitialGetStackString%': fn};$h‍_once.permitted(permitted); +}) +, +// === functors[11] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let TypeError,WeakSet,arrayFilter,create,defineProperty,entries,freeze,getOwnPropertyDescriptor,getOwnPropertyDescriptors,globalThis,is,isObject,objectHasOwnProperty,values,weaksetHas,constantProperties,sharedGlobalPropertyNames,universalPropertyNames,permitted;$h‍_imports([["./commons.js", [["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["WeakSet", [$h‍_a => (WeakSet = $h‍_a)]],["arrayFilter", [$h‍_a => (arrayFilter = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["entries", [$h‍_a => (entries = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]],["is", [$h‍_a => (is = $h‍_a)]],["isObject", [$h‍_a => (isObject = $h‍_a)]],["objectHasOwnProperty", [$h‍_a => (objectHasOwnProperty = $h‍_a)]],["values", [$h‍_a => (values = $h‍_a)]],["weaksetHas", [$h‍_a => (weaksetHas = $h‍_a)]]]],["./permits.js", [["constantProperties", [$h‍_a => (constantProperties = $h‍_a)]],["sharedGlobalPropertyNames", [$h‍_a => (sharedGlobalPropertyNames = $h‍_a)]],["universalPropertyNames", [$h‍_a => (universalPropertyNames = $h‍_a)]],["permitted", [$h‍_a => (permitted = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + +const isFunction= (obj)=>typeof obj=== 'function'; + +// Like defineProperty, but throws if it would modify an existing property. +// We use this to ensure that two conflicting attempts to define the same +// property throws, causing SES initialization to fail. Otherwise, a +// conflict between, for example, two of SES's internal whitelists might +// get masked as one overwrites the other. Accordingly, the thrown error +// complains of a "Conflicting definition". +function initProperty(obj, name, desc) { + if( objectHasOwnProperty(obj, name)) { + const preDesc= getOwnPropertyDescriptor(obj, name); + if( + !preDesc|| + !is(preDesc.value, desc.value)|| + preDesc.get!== desc.get|| + preDesc.set!== desc.set|| + preDesc.writable!== desc.writable|| + preDesc.enumerable!== desc.enumerable|| + preDesc.configurable!== desc.configurable) + { + throw TypeError( `Conflicting definitions of ${name}`); + } + } + defineProperty(obj, name, desc); + } + +// Like defineProperties, but throws if it would modify an existing property. +// This ensures that the intrinsics added to the intrinsics collector object +// graph do not overlap. +function initProperties(obj, descs) { + for( const [name, desc]of entries(descs)) { + initProperty(obj, name, desc); + } + } + +// sampleGlobals creates an intrinsics object, suitable for +// interinsicsCollector.addIntrinsics, from the named properties of a global +// object. +function sampleGlobals(globalObject, newPropertyNames) { + const newIntrinsics= { __proto__: null}; + for( const [globalName, intrinsicName]of entries(newPropertyNames)) { + if( objectHasOwnProperty(globalObject, globalName)) { + newIntrinsics[intrinsicName]= globalObject[globalName]; + } + } + return newIntrinsics; + } + +const makeIntrinsicsCollector= ()=> { + /** @type {Record} */ + const intrinsics= create(null); + let pseudoNatives; + + const addIntrinsics= (newIntrinsics)=>{ + initProperties(intrinsics, getOwnPropertyDescriptors(newIntrinsics)); + }; + freeze(addIntrinsics); + + // For each intrinsic, if it has a `.prototype` property, use the + // whitelist to find out the intrinsic name for that prototype and add it + // to the intrinsics. + const completePrototypes= ()=> { + for( const [name, intrinsic]of entries(intrinsics)) { + if( !isObject(intrinsic)) { + // eslint-disable-next-line no-continue + continue; + } + if( !objectHasOwnProperty(intrinsic, 'prototype')) { + // eslint-disable-next-line no-continue + continue; + } + const permit= permitted[name]; + if( typeof permit!== 'object') { + throw TypeError( `Expected permit object at whitelist.${name}`); + } + const namePrototype= permit.prototype; + if( !namePrototype) { + throw TypeError( `${name}.prototype property not whitelisted`); + } + if( + typeof namePrototype!== 'string'|| + !objectHasOwnProperty(permitted, namePrototype)) + { + throw TypeError( `Unrecognized ${name}.prototype whitelist entry`); + } + const intrinsicPrototype= intrinsic.prototype; + if( objectHasOwnProperty(intrinsics, namePrototype)) { + if( intrinsics[namePrototype]!== intrinsicPrototype) { + throw TypeError( `Conflicting bindings of ${namePrototype}`); + } + // eslint-disable-next-line no-continue + continue; + } + intrinsics[namePrototype]= intrinsicPrototype; + } + }; + freeze(completePrototypes); + + const finalIntrinsics= ()=> { + freeze(intrinsics); + pseudoNatives= new WeakSet(arrayFilter(values(intrinsics), isFunction)); + return intrinsics; + }; + freeze(finalIntrinsics); + + const isPseudoNative= (obj)=>{ + if( !pseudoNatives) { + throw TypeError( + 'isPseudoNative can only be called after finalIntrinsics'); + + } + return weaksetHas(pseudoNatives, obj); + }; + freeze(isPseudoNative); + + const intrinsicsCollector= { + addIntrinsics, + completePrototypes, + finalIntrinsics, + isPseudoNative}; + + freeze(intrinsicsCollector); + + addIntrinsics(constantProperties); + addIntrinsics(sampleGlobals(globalThis, universalPropertyNames)); + + return intrinsicsCollector; + }; + +/** + * getGlobalIntrinsics() + * Doesn't tame, delete, or modify anything. Samples globalObject to create an + * intrinsics record containing only the whitelisted global variables, listed + * by the intrinsic names appropriate for new globals, i.e., the globals of + * newly constructed compartments. + * + * WARNING: + * If run before lockdown, the returned intrinsics record will carry the + * *original* unsafe (feral, untamed) bindings of these global variables. + * + * @param {object} globalObject + */$h‍_once.makeIntrinsicsCollector(makeIntrinsicsCollector); +const getGlobalIntrinsics= (globalObject)=>{ + const { addIntrinsics, finalIntrinsics}= makeIntrinsicsCollector(); + + addIntrinsics(sampleGlobals(globalObject, sharedGlobalPropertyNames)); + + return finalIntrinsics(); + };$h‍_once.getGlobalIntrinsics(getGlobalIntrinsics); +}) +, +// === functors[12] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let permitted,FunctionInstance,isAccessorPermit,Map,String,Symbol,TypeError,arrayFilter,arrayIncludes,arrayMap,entries,getOwnPropertyDescriptor,getPrototypeOf,isObject,mapGet,objectHasOwnProperty,ownKeys,symbolKeyFor;$h‍_imports([["./permits.js", [["permitted", [$h‍_a => (permitted = $h‍_a)]],["FunctionInstance", [$h‍_a => (FunctionInstance = $h‍_a)]],["isAccessorPermit", [$h‍_a => (isAccessorPermit = $h‍_a)]]]],["./commons.js", [["Map", [$h‍_a => (Map = $h‍_a)]],["String", [$h‍_a => (String = $h‍_a)]],["Symbol", [$h‍_a => (Symbol = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["arrayFilter", [$h‍_a => (arrayFilter = $h‍_a)]],["arrayIncludes", [$h‍_a => (arrayIncludes = $h‍_a)]],["arrayMap", [$h‍_a => (arrayMap = $h‍_a)]],["entries", [$h‍_a => (entries = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["getPrototypeOf", [$h‍_a => (getPrototypeOf = $h‍_a)]],["isObject", [$h‍_a => (isObject = $h‍_a)]],["mapGet", [$h‍_a => (mapGet = $h‍_a)]],["objectHasOwnProperty", [$h‍_a => (objectHasOwnProperty = $h‍_a)]],["ownKeys", [$h‍_a => (ownKeys = $h‍_a)]],["symbolKeyFor", [$h‍_a => (symbolKeyFor = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * whitelistIntrinsics() + * Removes all non-allowed properties found by recursively and + * reflectively walking own property chains. + * + * @param {object} intrinsics + * @param {(object) => void} markVirtualizedNativeFunction + */ +function whitelistIntrinsics( + intrinsics, + markVirtualizedNativeFunction) + { + // These primitives are allowed allowed for permits. + const primitives= ['undefined', 'boolean', 'number', 'string', 'symbol']; + + // These symbols are allowed as well-known symbols + const wellKnownSymbolNames= new Map( + Symbol? + arrayMap( + arrayFilter( + entries(permitted['%SharedSymbol%']), + ([name, permit])=> + permit=== 'symbol'&& typeof Symbol[name]=== 'symbol'), + + ([name])=> [Symbol[name], `@@${name}`]): + + []); + + + /** + * asStringPropertyName() + * + * @param {string} path + * @param {string | symbol} prop + */ + function asStringPropertyName(path, prop) { + if( typeof prop=== 'string') { + return prop; + } + + const wellKnownSymbol= mapGet(wellKnownSymbolNames, prop); + + if( typeof prop=== 'symbol') { + if( wellKnownSymbol) { + return wellKnownSymbol; + }else { + const registeredKey= symbolKeyFor(prop); + if( registeredKey!== undefined) { + return `RegisteredSymbol(${registeredKey})`; + }else { + return `Unique${String(prop)}`; + } + } + } + + throw TypeError( `Unexpected property name type ${path} ${prop}`); + } + + /* + * visitPrototype() + * Validate the object's [[prototype]] against a permit. + */ + function visitPrototype(path, obj, protoName) { + if( !isObject(obj)) { + throw TypeError( `Object expected: ${path}, ${obj}, ${protoName}`); + } + const proto= getPrototypeOf(obj); + + // Null prototype. + if( proto=== null&& protoName=== null) { + return; + } + + // Assert: protoName, if provided, is a string. + if( protoName!== undefined&& typeof protoName!== 'string') { + throw TypeError( `Malformed whitelist permit ${path}.__proto__`); + } + + // If permit not specified, default to Object.prototype. + if( proto=== intrinsics[protoName|| '%ObjectPrototype%']) { + return; + } + + // We can't clean [[prototype]], therefore abort. + throw TypeError( `Unexpected intrinsic ${path}.__proto__ at ${protoName}`); + } + + /* + * isAllowedPropertyValue() + * Whitelist a single property value against a permit. + */ + function isAllowedPropertyValue(path, value, prop, permit) { + if( typeof permit=== 'object') { + // eslint-disable-next-line no-use-before-define + visitProperties(path, value, permit); + // The property is allowed. + return true; + } + + if( permit=== false) { + // A boolan 'false' permit specifies the removal of a property. + // We require a more specific permit instead of allowing 'true'. + return false; + } + + if( typeof permit=== 'string') { + // A string permit can have one of two meanings: + + if( prop=== 'prototype'|| prop=== 'constructor') { + // For prototype and constructor value properties, the permit + // is the name of an intrinsic. + // Assumption: prototype and constructor cannot be primitives. + // Assert: the permit is the name of an intrinsic. + // Assert: the property value is equal to that intrinsic. + + if( objectHasOwnProperty(intrinsics, permit)) { + if( value!== intrinsics[permit]) { + throw TypeError( `Does not match whitelist ${path}`); + } + return true; + } + }else { + // For all other properties, the permit is the name of a primitive. + // Assert: the permit is the name of a primitive. + // Assert: the property value type is equal to that primitive. + + // eslint-disable-next-line no-lonely-if + if( arrayIncludes(primitives, permit)) { + // eslint-disable-next-line valid-typeof + if( typeof value!== permit) { + throw TypeError( + `At ${path} expected ${permit} not ${typeof value}`); + + } + return true; + } + } + } + + throw TypeError( `Unexpected whitelist permit ${permit} at ${path}`); + } + + /* + * isAllowedProperty() + * Check whether a single property is allowed. + */ + function isAllowedProperty(path, obj, prop, permit) { + const desc= getOwnPropertyDescriptor(obj, prop); + if( !desc) { + throw TypeError( `Property ${prop} not found at ${path}`); + } + + // Is this a value property? + if( objectHasOwnProperty(desc, 'value')) { + if( isAccessorPermit(permit)) { + throw TypeError( `Accessor expected at ${path}`); + } + return isAllowedPropertyValue(path, desc.value, prop, permit); + } + if( !isAccessorPermit(permit)) { + throw TypeError( `Accessor not expected at ${path}`); + } + return( + isAllowedPropertyValue( `${path}`,desc.get, prop, permit.get)&& + isAllowedPropertyValue( `${path}`,desc.set, prop, permit.set)); + + } + + /* + * getSubPermit() + */ + function getSubPermit(obj, permit, prop) { + const permitProp= prop=== '__proto__'? '--proto--': prop; + if( objectHasOwnProperty(permit, permitProp)) { + return permit[permitProp]; + } + + if( typeof obj=== 'function') { + if( objectHasOwnProperty(FunctionInstance, permitProp)) { + return FunctionInstance[permitProp]; + } + } + + return undefined; + } + + /* + * visitProperties() + * Visit all properties for a permit. + */ + function visitProperties(path, obj, permit) { + if( obj=== undefined|| obj=== null) { + return; + } + + const protoName= permit['[[Proto]]']; + visitPrototype(path, obj, protoName); + + if( typeof obj=== 'function') { + markVirtualizedNativeFunction(obj); + } + + for( const prop of ownKeys(obj)) { + const propString= asStringPropertyName(path, prop); + const subPath= `${path}.${propString}`; + const subPermit= getSubPermit(obj, permit, propString); + + if( !subPermit|| !isAllowedProperty(subPath, obj, prop, subPermit)) { + // Either the object lacks a permit or the object doesn't match the + // permit. + // If the permit is specifically false, not merely undefined, + // this is a property we expect to see because we know it exists in + // some environments and we have expressly decided to exclude it. + // Any other disallowed property is one we have not audited and we log + // that we are removing it so we know to look into it, as happens when + // the language evolves new features to existing intrinsics. + if( subPermit!== false) { + // This call to `console.warn` is intentional. It is not a vestige of + // a debugging attempt. See the comment at top of file for an + // explanation. + // eslint-disable-next-line @endo/no-polymorphic-call + console.warn( `Removing ${subPath}`); + } + try { + delete obj[prop]; + }catch( err) { + if( prop in obj) { + if( typeof obj=== 'function'&& prop=== 'prototype') { + obj.prototype= undefined; + if( obj.prototype=== undefined) { + // eslint-disable-next-line @endo/no-polymorphic-call + console.warn( `Tolerating undeletable ${subPath} === undefined`); + // eslint-disable-next-line no-continue + continue; + } + } + // eslint-disable-next-line @endo/no-polymorphic-call + console.error( `failed to delete ${subPath}`,err); + }else { + // eslint-disable-next-line @endo/no-polymorphic-call + console.error( `deleting ${subPath} threw`,err); + } + throw err; + } + } + } + } + + // Start path with 'intrinsics' to clarify that properties are not + // removed from the global object by the whitelisting operation. + visitProperties('intrinsics', intrinsics, permitted); + }$h‍_once.default( whitelistIntrinsics); +}) +, +// === functors[13] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_FUNCTION,SyntaxError,TypeError,defineProperties,getPrototypeOf,setPrototypeOf,freeze;$h‍_imports([["./commons.js", [["FERAL_FUNCTION", [$h‍_a => (FERAL_FUNCTION = $h‍_a)]],["SyntaxError", [$h‍_a => (SyntaxError = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["getPrototypeOf", [$h‍_a => (getPrototypeOf = $h‍_a)]],["setPrototypeOf", [$h‍_a => (setPrototypeOf = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]]]]]); + + + + + + + + + +// This module replaces the original `Function` constructor, and the original +// `%GeneratorFunction%`, `%AsyncFunction%` and `%AsyncGeneratorFunction%`, +// with safe replacements that throw if invoked. +// +// These are all reachable via syntax, so it isn't sufficient to just +// replace global properties with safe versions. Our main goal is to prevent +// access to the `Function` constructor through these starting points. +// +// After modules block is done, the originals must no longer be reachable, +// unless a copy has been made, and functions can only be created by syntax +// (using eval) or by invoking a previously saved reference to the originals. +// +// Typically, this module will not be used directly, but via the +// [lockdown - shim] which handles all necessary repairs and taming in SES. +// +// Relation to ECMA specifications +// +// The taming of constructors really wants to be part of the standard, because +// new constructors may be added in the future, reachable from syntax, and this +// list must be updated to match. +// +// In addition, the standard needs to define four new intrinsics for the safe +// replacement functions. See [./permits-intrinsics.js]. +// +// Adapted from SES/Caja +// Copyright (C) 2011 Google Inc. +// https://github.com/google/caja/blob/master/src/com/google/caja/ses/startSES.js +// https://github.com/google/caja/blob/master/src/com/google/caja/ses/repairES5.js + +/** + * tameFunctionConstructors() + * This block replaces the original Function constructor, and the original + * %GeneratorFunction% %AsyncFunction% and %AsyncGeneratorFunction%, with + * safe replacements that throw if invoked. + */ +function tameFunctionConstructors() { + try { + // Verify that the method is not callable. + // eslint-disable-next-line @endo/no-polymorphic-call + FERAL_FUNCTION.prototype.constructor('return 1'); + }catch( ignore) { + // Throws, no need to patch. + return freeze({}); + } + + const newIntrinsics= {}; + + /* + * The process to repair constructors: + * 1. Create an instance of the function by evaluating syntax + * 2. Obtain the prototype from the instance + * 3. Create a substitute tamed constructor + * 4. Replace the original constructor with the tamed constructor + * 5. Replace tamed constructor prototype property with the original one + * 6. Replace its [[Prototype]] slot with the tamed constructor of Function + */ + function repairFunction(name, intrinsicName, declaration) { + let FunctionInstance; + try { + // eslint-disable-next-line no-eval, no-restricted-globals + FunctionInstance= (0, eval)(declaration); + }catch( e) { + if( e instanceof SyntaxError) { + // Prevent failure on platforms where async and/or generators + // are not supported. + return; + } + // Re-throw + throw e; + } + const FunctionPrototype= getPrototypeOf(FunctionInstance); + + // Prevents the evaluation of source when calling constructor on the + // prototype of functions. + // eslint-disable-next-line func-names + const InertConstructor= function() { + throw TypeError( + 'Function.prototype.constructor is not a valid constructor.'); + + }; + defineProperties(InertConstructor, { + prototype: { value: FunctionPrototype}, + name: { + value: name, + writable: false, + enumerable: false, + configurable: true}}); + + + + defineProperties(FunctionPrototype, { + constructor: { value: InertConstructor}}); + + + // Reconstructs the inheritance among the new tamed constructors + // to mirror the original specified in normal JS. + if( InertConstructor!== FERAL_FUNCTION.prototype.constructor) { + setPrototypeOf(InertConstructor, FERAL_FUNCTION.prototype.constructor); + } + + newIntrinsics[intrinsicName]= InertConstructor; + } + + // Here, the order of operation is important: Function needs to be repaired + // first since the other repaired constructors need to inherit from the + // tamed Function function constructor. + + repairFunction('Function', '%InertFunction%', '(function(){})'); + repairFunction( + 'GeneratorFunction', + '%InertGeneratorFunction%', + '(function*(){})'); + + repairFunction( + 'AsyncFunction', + '%InertAsyncFunction%', + '(async function(){})'); + + repairFunction( + 'AsyncGeneratorFunction', + '%InertAsyncGeneratorFunction%', + '(async function*(){})'); + + + return newIntrinsics; + }$h‍_once.default( tameFunctionConstructors); +}) +, +// === functors[14] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Date,TypeError,apply,construct,defineProperties;$h‍_imports([["./commons.js", [["Date", [$h‍_a => (Date = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["apply", [$h‍_a => (apply = $h‍_a)]],["construct", [$h‍_a => (construct = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]]]]]); + + + + + + + + + +function tameDateConstructor(dateTaming= 'safe') { + if( dateTaming!== 'safe'&& dateTaming!== 'unsafe') { + throw TypeError( `unrecognized dateTaming ${dateTaming}`); + } + const OriginalDate= Date; + const DatePrototype= OriginalDate.prototype; + + // Use concise methods to obtain named functions without constructors. + const tamedMethods= { + /** + * `%SharedDate%.now()` throw a `TypeError` starting with "secure mode". + * See https://github.com/endojs/endo/issues/910#issuecomment-1581855420 + */ + now() { + throw TypeError('secure mode Calling %SharedDate%.now() throws'); + }}; + + + /** + * Tame the Date constructor. + * See https://github.com/endojs/endo/issues/910#issuecomment-1581855420 + * + * Common behavior + * * `new Date(x)` coerces x into a number and then returns a Date + * for that number of millis since the epoch + * * `new Date(NaN)` returns a Date object which stringifies to + * 'Invalid Date' + * * `new Date(undefined)` returns a Date object which stringifies to + * 'Invalid Date' + * + * OriginalDate (normal standard) behavior preserved by + * `%InitialDate%`. + * * `Date(anything)` gives a string with the current time + * * `new Date()` returns the current time, as a Date object + * + * `%SharedDate%` behavior + * * `Date(anything)` throws a TypeError starting with "secure mode" + * * `new Date()` throws a TypeError starting with "secure mode" + * + * @param {{powers?: string}} [opts] + */ + const makeDateConstructor= ({ powers= 'none'}= {})=> { + let ResultDate; + if( powers=== 'original') { + // eslint-disable-next-line no-shadow + ResultDate= function Date(...rest) { + if( new.target=== undefined) { + return apply(OriginalDate, undefined, rest); + } + return construct(OriginalDate, rest, new.target); + }; + }else { + // eslint-disable-next-line no-shadow + ResultDate= function Date(...rest) { + if( new.target=== undefined) { + throw TypeError( + 'secure mode Calling %SharedDate% constructor as a function throws'); + + } + if( rest.length=== 0) { + throw TypeError( + 'secure mode Calling new %SharedDate%() with no arguments throws'); + + } + return construct(OriginalDate, rest, new.target); + }; + } + + defineProperties(ResultDate, { + length: { value: 7}, + prototype: { + value: DatePrototype, + writable: false, + enumerable: false, + configurable: false}, + + parse: { + value: OriginalDate.parse, + writable: true, + enumerable: false, + configurable: true}, + + UTC: { + value: OriginalDate.UTC, + writable: true, + enumerable: false, + configurable: true}}); + + + return ResultDate; + }; + const InitialDate= makeDateConstructor({ powers: 'original'}); + const SharedDate= makeDateConstructor({ powers: 'none'}); + + defineProperties(InitialDate, { + now: { + value: OriginalDate.now, + writable: true, + enumerable: false, + configurable: true}}); + + + defineProperties(SharedDate, { + now: { + value: tamedMethods.now, + writable: true, + enumerable: false, + configurable: true}}); + + + + defineProperties(DatePrototype, { + constructor: { value: SharedDate}}); + + + return { + '%InitialDate%': InitialDate, + '%SharedDate%': SharedDate}; + + }$h‍_once.default( tameDateConstructor); +}) +, +// === functors[15] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Math,TypeError,create,getOwnPropertyDescriptors,objectPrototype;$h‍_imports([["./commons.js", [["Math", [$h‍_a => (Math = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]],["objectPrototype", [$h‍_a => (objectPrototype = $h‍_a)]]]]]); + + + + + + + +function tameMathObject(mathTaming= 'safe') { + if( mathTaming!== 'safe'&& mathTaming!== 'unsafe') { + throw TypeError( `unrecognized mathTaming ${mathTaming}`); + } + const originalMath= Math; + const initialMath= originalMath; // to follow the naming pattern + + const { random: _, ...otherDescriptors}= + getOwnPropertyDescriptors(originalMath); + + // Use concise methods to obtain named functions without constructors. + const tamedMethods= { + /** + * `%SharedMath%.random()` throws a TypeError starting with "secure mode". + * See https://github.com/endojs/endo/issues/910#issuecomment-1581855420 + */ + random() { + throw TypeError('secure mode %SharedMath%.random() throws'); + }}; + + + const sharedMath= create(objectPrototype, { + ...otherDescriptors, + random: { + value: tamedMethods.random, + writable: true, + enumerable: false, + configurable: true}}); + + + + return { + '%InitialMath%': initialMath, + '%SharedMath%': sharedMath}; + + }$h‍_once.default( tameMathObject); +}) +, +// === functors[16] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_REG_EXP,TypeError,construct,defineProperties,getOwnPropertyDescriptor,speciesSymbol;$h‍_imports([["./commons.js", [["FERAL_REG_EXP", [$h‍_a => (FERAL_REG_EXP = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["construct", [$h‍_a => (construct = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["speciesSymbol", [$h‍_a => (speciesSymbol = $h‍_a)]]]]]); + + + + + + + + +function tameRegExpConstructor(regExpTaming= 'safe') { + if( regExpTaming!== 'safe'&& regExpTaming!== 'unsafe') { + throw TypeError( `unrecognized regExpTaming ${regExpTaming}`); + } + const RegExpPrototype= FERAL_REG_EXP.prototype; + + const makeRegExpConstructor= (_= {})=> { + // RegExp has non-writable static properties we need to omit. + /** + * @param {Parameters} rest + */ + const ResultRegExp= function RegExp(...rest) { + if( new.target=== undefined) { + return FERAL_REG_EXP(...rest); + } + return construct(FERAL_REG_EXP, rest, new.target); + }; + + const speciesDesc= getOwnPropertyDescriptor(FERAL_REG_EXP, speciesSymbol); + if( !speciesDesc) { + throw TypeError('no RegExp[Symbol.species] descriptor'); + } + + defineProperties(ResultRegExp, { + length: { value: 2}, + prototype: { + value: RegExpPrototype, + writable: false, + enumerable: false, + configurable: false}, + + [speciesSymbol]: speciesDesc}); + + return ResultRegExp; + }; + + const InitialRegExp= makeRegExpConstructor(); + const SharedRegExp= makeRegExpConstructor(); + + if( regExpTaming!== 'unsafe') { + // @ts-expect-error Deleted properties must be optional + delete RegExpPrototype.compile; + } + defineProperties(RegExpPrototype, { + constructor: { value: SharedRegExp}}); + + + return { + '%InitialRegExp%': InitialRegExp, + '%SharedRegExp%': SharedRegExp}; + + }$h‍_once.default( tameRegExpConstructor); +}) +, +// === functors[17] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([]); /** + * @file Exports {@code enablements}, a recursively defined + * JSON record defining the optimum set of intrinsics properties + * that need to be "repaired" before hardening is applied on + * enviromments subject to the override mistake. + * + * @author JF Paradis + * @author Mark S. Miller + */ + +/** + *

    Because "repairing" replaces data properties with accessors, every + * time a repaired property is accessed, the associated getter is invoked, + * which degrades the runtime performance of all code executing in the + * repaired enviromment, compared to the non-repaired case. In order + * to maintain performance, we only repair the properties of objects + * for which hardening causes a breakage of their normal intended usage. + * + * There are three unwanted cases: + *

      + *
    • Overriding properties on objects typically used as records, + * namely {@code "Object"} and {@code "Array"}. In the case of arrays, + * the situation is unintentional, a given program might not be aware + * that non-numerical properties are stored on the underlying object + * instance, not on the array. When an object is typically used as a + * map, we repair all of its prototype properties. + *
    • Overriding properties on objects that provide defaults on their + * prototype and that programs typically set using an assignment, such as + * {@code "Error.prototype.message"} and {@code "Function.prototype.name"} + * (both default to ""). + *
    • Setting-up a prototype chain, where a constructor is set to extend + * another one. This is typically set by assignment, for example + * {@code "Child.prototype.constructor = Child"}, instead of invoking + * Object.defineProperty(); + * + *

      Each JSON record enumerates the disposition of the properties on + * some corresponding intrinsic object. + * + *

      For each such record, the values associated with its property + * names can be: + *

        + *
      • true, in which case this property is simply repaired. The + * value associated with that property is not traversed. For + * example, {@code "Function.prototype.name"} leads to true, + * meaning that the {@code "name"} property of {@code + * "Function.prototype"} should be repaired (which is needed + * when inheriting from @code{Function} and setting the subclass's + * {@code "prototype.name"} property). If the property is + * already an accessor property, it is not repaired (because + * accessors are not subject to the override mistake). + *
      • "*", in which case this property is not repaired but the + * value associated with that property are traversed and repaired. + *
      • Another record, in which case this property is not repaired + * and that next record represents the disposition of the object + * which is its value. For example,{@code "FunctionPrototype"} + * leads to another record explaining which properties {@code + * Function.prototype} need to be repaired. + */ + +/** + * Minimal enablements when all the code is modern and known not to + * step into the override mistake, except for the following pervasive + * cases. + */ +const minEnablements= { + '%ObjectPrototype%': { + toString: true}, + + + '%FunctionPrototype%': { + toString: true // set by "rollup" +}, + + '%ErrorPrototype%': { + name: true // set by "precond", "ava", "node-fetch" +}}; + + +/** + * Moderate enablements are usually good enough for legacy compat. + */$h‍_once.minEnablements(minEnablements); +const moderateEnablements= { + '%ObjectPrototype%': { + toString: true, + valueOf: true}, + + + '%ArrayPrototype%': { + toString: true, + push: true // set by "Google Analytics" +}, + + // Function.prototype has no 'prototype' property to enable. + // Function instances have their own 'name' and 'length' properties + // which are configurable and non-writable. Thus, they are already + // non-assignable anyway. + '%FunctionPrototype%': { + constructor: true, // set by "regenerator-runtime" + bind: true, // set by "underscore", "express" + toString: true // set by "rollup" +}, + + '%ErrorPrototype%': { + constructor: true, // set by "fast-json-patch", "node-fetch" + message: true, + name: true, // set by "precond", "ava", "node-fetch", "node 14" + toString: true // set by "bluebird" +}, + + '%TypeErrorPrototype%': { + constructor: true, // set by "readable-stream" + message: true, // set by "tape" + name: true // set by "readable-stream", "node 14" +}, + + '%SyntaxErrorPrototype%': { + message: true, // to match TypeErrorPrototype.message + name: true // set by "node 14" +}, + + '%RangeErrorPrototype%': { + message: true, // to match TypeErrorPrototype.message + name: true // set by "node 14" +}, + + '%URIErrorPrototype%': { + message: true, // to match TypeErrorPrototype.message + name: true // set by "node 14" +}, + + '%EvalErrorPrototype%': { + message: true, // to match TypeErrorPrototype.message + name: true // set by "node 14" +}, + + '%ReferenceErrorPrototype%': { + message: true, // to match TypeErrorPrototype.message + name: true // set by "node 14" +}, + + '%PromisePrototype%': { + constructor: true // set by "core-js" +}, + + '%TypedArrayPrototype%': '*', // set by https://github.com/feross/buffer + + '%Generator%': { + constructor: true, + name: true, + toString: true}, + + + '%IteratorPrototype%': { + toString: true}}; + + + +/** + * The 'severe' enablement are needed because of issues tracked at + * https://github.com/endojs/endo/issues/576 + * + * They are like the `moderate` enablements except for the entries below. + */$h‍_once.moderateEnablements(moderateEnablements); +const severeEnablements= { + ...moderateEnablements, + + /** + * Rollup (as used at least by vega) and webpack + * (as used at least by regenerator) both turn exports into assignments + * to a big `exports` object that inherits directly from + * `Object.prototype`. Some of the exported names we've seen include + * `hasOwnProperty`, `constructor`, and `toString`. But the strategy used + * by rollup and webpack potentionally turns any exported name + * into an assignment rejected by the override mistake. That's why + * the `severe` enablements takes the extreme step of enabling + * everything on `Object.prototype`. + * + * In addition, code doing inheritance manually will often override + * the `constructor` property on the new prototype by assignment. We've + * seen this several times. + * + * The cost of enabling all these is that they create a miserable debugging + * experience specifically on Node. + * https://github.com/Agoric/agoric-sdk/issues/2324 + * explains how it confused the Node console. + * + * (TODO Reexamine the vscode situation. I think it may have improved + * since the following paragraph was written.) + * + * The vscode debugger's object inspector shows the own data properties of + * an object, which is typically what you want, but also shows both getter + * and setter for every accessor property whether inherited or own. + * With the `'*'` setting here, all the properties inherited from + * `Object.prototype` are accessors, creating an unusable display as seen + * at As explained at + * https://github.com/endojs/endo/blob/master/packages/ses/lockdown-options.md#overridetaming-options + * Open the triangles at the bottom of that section. + */ + '%ObjectPrototype%': '*', + + /** + * The widely used Buffer defined at https://github.com/feross/buffer + * on initialization, manually creates the equivalent of a subclass of + * `TypedArray`, which it then initializes by assignment. These assignments + * include enough of the `TypeArray` methods that here, the `severe` + * enablements just enable them all. + */ + '%TypedArrayPrototype%': '*', + + /** + * Needed to work with Immer before https://github.com/immerjs/immer/pull/914 + * is accepted. + */ + '%MapPrototype%': '*', + + /** + * Needed to work with Immer before https://github.com/immerjs/immer/pull/914 + * is accepted. + */ + '%SetPrototype%': '*'};$h‍_once.severeEnablements(severeEnablements); +}) +, +// === functors[18] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Set,String,TypeError,arrayForEach,defineProperty,getOwnPropertyDescriptor,getOwnPropertyDescriptors,getOwnPropertyNames,isObject,objectHasOwnProperty,ownKeys,setHas,minEnablements,moderateEnablements,severeEnablements;$h‍_imports([["./commons.js", [["Set", [$h‍_a => (Set = $h‍_a)]],["String", [$h‍_a => (String = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["arrayForEach", [$h‍_a => (arrayForEach = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]],["getOwnPropertyNames", [$h‍_a => (getOwnPropertyNames = $h‍_a)]],["isObject", [$h‍_a => (isObject = $h‍_a)]],["objectHasOwnProperty", [$h‍_a => (objectHasOwnProperty = $h‍_a)]],["ownKeys", [$h‍_a => (ownKeys = $h‍_a)]],["setHas", [$h‍_a => (setHas = $h‍_a)]]]],["./enablements.js", [["minEnablements", [$h‍_a => (minEnablements = $h‍_a)]],["moderateEnablements", [$h‍_a => (moderateEnablements = $h‍_a)]],["severeEnablements", [$h‍_a => (severeEnablements = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + +/** + * For a special set of properties defined in the `enablement` whitelist, + * `enablePropertyOverrides` 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 an non-writable data property of the same name in the + * shadow of the prototype chain. In retrospect, this was a mistake, the + * so-called "override 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. + * + * For the enabled properties, `enablePropertyOverrides` effectively shims what + * the assignment behavior would have been in the absence of the override + * mistake. However, the shim produces an imperfect emulation. It shims the + * behavior by turning these data properties into accessor properties, where + * the accessor's getter and setter provide the desired behavior. For + * non-reflective operations, the illusion is perfect. However, reflective + * operations like `getOwnPropertyDescriptor` see the descriptor of an accessor + * property rather than the descriptor of a data property. At the time of this + * writing, this is the best we know how to do. + * + * To the getter of the accessor we add a property named + * `'originalValue'` whose value is, as it says, the value that the + * data property had before being converted to an accessor property. We add + * this extra property to the getter for two reason: + * + * The harden algorithm walks the own properties reflectively, i.e., with + * `getOwnPropertyDescriptor` semantics, rather than `[[Get]]` semantics. When + * it sees an accessor property, it does not invoke the getter. Rather, it + * proceeds to walk both the getter and setter as part of its transitive + * traversal. Without this extra property, `enablePropertyOverrides` would have + * hidden the original data property value from `harden`, which would be bad. + * Instead, by exposing that value in an own data property on the getter, + * `harden` finds and walks it anyway. + * + * We enable a form of cooperative emulation, giving reflective code an + * opportunity to cooperate in upholding the illusion. When such cooperative + * reflective code sees an accessor property, where the accessor's getter + * has an `originalValue` property, it knows that the getter is + * alleging that it is the result of the `enablePropertyOverrides` conversion + * pattern, so it can decide to cooperatively "pretend" that it sees a data + * property with that value. + * + * @param {Record} intrinsics + * @param {'min' | 'moderate' | 'severe'} overrideTaming + * @param {Iterable} [overrideDebug] + */ +function enablePropertyOverrides( + intrinsics, + overrideTaming, + overrideDebug= []) + { + const debugProperties= new Set(overrideDebug); + function enable(path, obj, prop, desc) { + if( 'value'in desc&& desc.configurable) { + const { value}= desc; + + function getter() { + return value; + } + defineProperty(getter, 'originalValue', { + value, + writable: false, + enumerable: false, + configurable: false}); + + + const isDebug= setHas(debugProperties, prop); + + function setter(newValue) { + if( obj=== this) { + throw TypeError( + `Cannot assign to read only property '${String( + prop) + }' of '${path}'`); + + } + if( objectHasOwnProperty(this, prop)) { + this[prop]= newValue; + }else { + if( isDebug) { + // eslint-disable-next-line @endo/no-polymorphic-call + console.error(TypeError( `Override property ${prop}`)); + } + defineProperty(this, prop, { + value: newValue, + writable: true, + enumerable: true, + configurable: true}); + + } + } + + defineProperty(obj, prop, { + get: getter, + set: setter, + enumerable: desc.enumerable, + configurable: desc.configurable}); + + } + } + + function enableProperty(path, obj, prop) { + const desc= getOwnPropertyDescriptor(obj, prop); + if( !desc) { + return; + } + enable(path, obj, prop, desc); + } + + function enableAllProperties(path, obj) { + const descs= getOwnPropertyDescriptors(obj); + if( !descs) { + return; + } + // TypeScript does not allow symbols to be used as indexes because it + // cannot recokon types of symbolized properties. + // @ts-ignore + arrayForEach(ownKeys(descs), (prop)=>enable(path, obj, prop, descs[prop])); + } + + function enableProperties(path, obj, plan) { + for( const prop of getOwnPropertyNames(plan)) { + const desc= getOwnPropertyDescriptor(obj, prop); + if( !desc|| desc.get|| desc.set) { + // No not a value property, nothing to do. + // eslint-disable-next-line no-continue + continue; + } + + // Plan has no symbol keys and we use getOwnPropertyNames() + // so `prop` cannot only be a string, not a symbol. We coerce it in place + // with `String(..)` anyway just as good hygiene, since these paths are just + // for diagnostic purposes. + const subPath= `${path}.${String(prop)}`; + const subPlan= plan[prop]; + + if( subPlan=== true) { + enableProperty(subPath, obj, prop); + }else if( subPlan=== '*') { + enableAllProperties(subPath, desc.value); + }else if( isObject(subPlan)) { + enableProperties(subPath, desc.value, subPlan); + }else { + throw TypeError( `Unexpected override enablement plan ${subPath}`); + } + } + } + + let plan; + switch( overrideTaming){ + case 'min': { + plan= minEnablements; + break; + } + case 'moderate': { + plan= moderateEnablements; + break; + } + case 'severe': { + plan= severeEnablements; + break; + } + default: { + throw TypeError( `unrecognized overrideTaming ${overrideTaming}`); + }} + + + // Do the repair. + enableProperties('root', intrinsics, plan); + }$h‍_once.default( enablePropertyOverrides); +}) +, +// === functors[19] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Number,String,TypeError,defineProperty,getOwnPropertyNames,isObject,regexpExec,assert;$h‍_imports([["./commons.js", [["Number", [$h‍_a => (Number = $h‍_a)]],["String", [$h‍_a => (String = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["getOwnPropertyNames", [$h‍_a => (getOwnPropertyNames = $h‍_a)]],["isObject", [$h‍_a => (isObject = $h‍_a)]],["regexpExec", [$h‍_a => (regexpExec = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + + + + + + + + +const { Fail, quote: q}= assert; + +const localePattern= /^(\w*[a-z])Locale([A-Z]\w*)$/; + +// Use concise methods to obtain named functions without constructor +// behavior or `.prototype` property. +const tamedMethods= { + // See https://tc39.es/ecma262/#sec-string.prototype.localecompare + localeCompare(arg) { + if( this=== null|| this=== undefined) { + throw TypeError( + 'Cannot localeCompare with null or undefined "this" value'); + + } + const s= `${this}`; + const that= `${arg}`; + if( s< that) { + return -1; + } + if( s> that) { + return 1; + } + s=== that|| Fail `expected ${q(s)} and ${q(that)} to compare`; + return 0; + }, + + toString() { + return `${this}`; + }}; + + +const nonLocaleCompare= tamedMethods.localeCompare; +const numberToString= tamedMethods.toString; + +function tameLocaleMethods(intrinsics, localeTaming= 'safe') { + if( localeTaming!== 'safe'&& localeTaming!== 'unsafe') { + throw TypeError( `unrecognized localeTaming ${localeTaming}`); + } + if( localeTaming=== 'unsafe') { + return; + } + + defineProperty(String.prototype, 'localeCompare', { + value: nonLocaleCompare}); + + + for( const intrinsicName of getOwnPropertyNames(intrinsics)) { + const intrinsic= intrinsics[intrinsicName]; + if( isObject(intrinsic)) { + for( const methodName of getOwnPropertyNames(intrinsic)) { + const match= regexpExec(localePattern, methodName); + if( match) { + typeof intrinsic[methodName]=== 'function'|| + Fail `expected ${q(methodName)} to be a function`; + const nonLocaleMethodName= `${match[1]}${match[2]}`; + const method= intrinsic[nonLocaleMethodName]; + typeof method=== 'function'|| + Fail `function ${q(nonLocaleMethodName)} not found`; + defineProperty(intrinsic, methodName, { value: method}); + } + } + } + } + + // Numbers are special because toString accepts a radix instead of ignoring + // all of the arguments that we would otherwise forward. + defineProperty(Number.prototype, 'toLocaleString', { + value: numberToString}); + + }$h‍_once.default( tameLocaleMethods); +}) +, +// === functors[20] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([]); /** + * makeEvalFunction() + * A safe version of the native eval function which relies on + * the safety of safeEvaluate for confinement. + * + * @param {Function} safeEvaluate + */ +const makeEvalFunction= (safeEvaluate)=>{ + // We use the the concise method syntax to create an eval without a + // [[Construct]] behavior (such that the invocation "new eval()" throws + // TypeError: eval is not a constructor"), but which still accepts a + // 'this' binding. + const newEval= { + eval(source) { + if( typeof source!== 'string') { + // As per the runtime semantic of PerformEval [ECMAScript 18.2.1.1]: + // If Type(source) is not String, return source. + // TODO Recent proposals from Mike Samuel may change this non-string + // rule. Track. + return source; + } + return safeEvaluate(source); + }}. + eval; + + return newEval; + };$h‍_once.makeEvalFunction(makeEvalFunction); +}) +, +// === functors[21] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_FUNCTION,arrayJoin,arrayPop,defineProperties,getPrototypeOf,assert;$h‍_imports([["./commons.js", [["FERAL_FUNCTION", [$h‍_a => (FERAL_FUNCTION = $h‍_a)]],["arrayJoin", [$h‍_a => (arrayJoin = $h‍_a)]],["arrayPop", [$h‍_a => (arrayPop = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["getPrototypeOf", [$h‍_a => (getPrototypeOf = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + + + + + + +const { Fail}= assert; + +/* + * makeFunctionConstructor() + * A safe version of the native Function which relies on + * the safety of safeEvaluate for confinement. + */ +const makeFunctionConstructor= (safeEvaluate)=>{ + // Define an unused parameter to ensure Function.length === 1 + const newFunction= function Function(_body) { + // Sanitize all parameters at the entry point. + // eslint-disable-next-line prefer-rest-params + const bodyText= `${arrayPop(arguments)|| '' }`; + // eslint-disable-next-line prefer-rest-params + const parameters= `${arrayJoin(arguments,',') }`; + + // Are parameters and bodyText valid code, or is someone + // attempting an injection attack? This will throw a SyntaxError if: + // - parameters doesn't parse as parameters + // - bodyText doesn't parse as a function body + // - either contain a call to super() or references a super property. + // + // It seems that XS may still be vulnerable to the attack explained at + // https://github.com/tc39/ecma262/pull/2374#issuecomment-813769710 + // where `new Function('/*', '*/ ) {')` would incorrectly validate. + // Before we worried about this, we check the parameters and bodyText + // together in one call + // ```js + // new FERAL_FUNCTION(parameters, bodyTest); + // ``` + // However, this check is vulnerable to that bug. Aside from that case, + // all engines do seem to validate the parameters, taken by themselves, + // correctly. And all engines do seem to validate the bodyText, taken + // by itself correctly. So with the following two checks, SES builds a + // correct safe `Function` constructor by composing two calls to an + // original unsafe `Function` constructor that may suffer from this bug + // but is otherwise correctly validating. + // + // eslint-disable-next-line no-new + new FERAL_FUNCTION(parameters, ''); + // eslint-disable-next-line no-new + new FERAL_FUNCTION(bodyText); + + // Safe to be combined. Defeat potential trailing comments. + // TODO: since we create an anonymous function, the 'this' value + // isn't bound to the global object as per specs, but set as undefined. + const src= `(function anonymous(${parameters}\n) {\n${bodyText}\n})`; + return safeEvaluate(src); + }; + + defineProperties(newFunction, { + // Ensure that any function created in any evaluator in a realm is an + // instance of Function in any evaluator of the same realm. + prototype: { + value: FERAL_FUNCTION.prototype, + writable: false, + enumerable: false, + configurable: false}}); + + + + // Assert identity of Function.__proto__ accross all compartments + getPrototypeOf(FERAL_FUNCTION)=== FERAL_FUNCTION.prototype|| + Fail `Function prototype is the same accross compartments`; + getPrototypeOf(newFunction)=== FERAL_FUNCTION.prototype|| + Fail `Function constructor prototype is the same accross compartments`; + + return newFunction; + };$h‍_once.makeFunctionConstructor(makeFunctionConstructor); +}) +, +// === functors[22] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let TypeError,assign,create,defineProperty,entries,freeze,objectHasOwnProperty,unscopablesSymbol,makeEvalFunction,makeFunctionConstructor,constantProperties,universalPropertyNames;$h‍_imports([["./commons.js", [["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["assign", [$h‍_a => (assign = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["entries", [$h‍_a => (entries = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["objectHasOwnProperty", [$h‍_a => (objectHasOwnProperty = $h‍_a)]],["unscopablesSymbol", [$h‍_a => (unscopablesSymbol = $h‍_a)]]]],["./make-eval-function.js", [["makeEvalFunction", [$h‍_a => (makeEvalFunction = $h‍_a)]]]],["./make-function-constructor.js", [["makeFunctionConstructor", [$h‍_a => (makeFunctionConstructor = $h‍_a)]]]],["./permits.js", [["constantProperties", [$h‍_a => (constantProperties = $h‍_a)]],["universalPropertyNames", [$h‍_a => (universalPropertyNames = $h‍_a)]]]]]); + + + + + + + + + + + + + +/** + * The host's ordinary global object is not provided by a `with` block, so + * assigning to Symbol.unscopables has no effect. + * Since this shim uses `with` blocks to create a confined lexical scope for + * guest programs, we cannot emulate the proper behavior. + * With this shim, assigning Symbol.unscopables causes the given lexical + * names to fall through to the terminal scope proxy. + * But, we can install this setter to prevent a program from proceding on + * this false assumption. + * + * @param {object} globalObject + */ +const setGlobalObjectSymbolUnscopables= (globalObject)=>{ + defineProperty( + globalObject, + unscopablesSymbol, + freeze( + assign(create(null), { + set: freeze(()=> { + throw TypeError( + `Cannot set Symbol.unscopables of a Compartment's globalThis`); + + }), + enumerable: false, + configurable: false}))); + + + + }; + +/** + * setGlobalObjectConstantProperties() + * Initializes a new global object using a process similar to ECMA specifications + * (SetDefaultGlobalBindings). This process is split between this function and + * `setGlobalObjectMutableProperties`. + * + * @param {object} globalObject + */$h‍_once.setGlobalObjectSymbolUnscopables(setGlobalObjectSymbolUnscopables); +const setGlobalObjectConstantProperties= (globalObject)=>{ + for( const [name, constant]of entries(constantProperties)) { + defineProperty(globalObject, name, { + value: constant, + writable: false, + enumerable: false, + configurable: false}); + + } + }; + +/** + * setGlobalObjectMutableProperties() + * Create new global object using a process similar to ECMA specifications + * (portions of SetRealmGlobalObject and SetDefaultGlobalBindings). + * `newGlobalPropertyNames` should be either `initialGlobalPropertyNames` or + * `sharedGlobalPropertyNames`. + * + * @param {object} globalObject + * @param {object} param1 + * @param {object} param1.intrinsics + * @param {object} param1.newGlobalPropertyNames + * @param {Function} param1.makeCompartmentConstructor + * @param {(object) => void} param1.markVirtualizedNativeFunction + */$h‍_once.setGlobalObjectConstantProperties(setGlobalObjectConstantProperties); +const setGlobalObjectMutableProperties= ( + globalObject, + { + intrinsics, + newGlobalPropertyNames, + makeCompartmentConstructor, + markVirtualizedNativeFunction})=> + + { + for( const [name, intrinsicName]of entries(universalPropertyNames)) { + if( objectHasOwnProperty(intrinsics, intrinsicName)) { + defineProperty(globalObject, name, { + value: intrinsics[intrinsicName], + writable: true, + enumerable: false, + configurable: true}); + + } + } + + for( const [name, intrinsicName]of entries(newGlobalPropertyNames)) { + if( objectHasOwnProperty(intrinsics, intrinsicName)) { + defineProperty(globalObject, name, { + value: intrinsics[intrinsicName], + writable: true, + enumerable: false, + configurable: true}); + + } + } + + const perCompartmentGlobals= { + globalThis: globalObject}; + + + perCompartmentGlobals.Compartment= makeCompartmentConstructor( + makeCompartmentConstructor, + intrinsics, + markVirtualizedNativeFunction); + + + // TODO These should still be tamed according to the whitelist before + // being made available. + for( const [name, value]of entries(perCompartmentGlobals)) { + defineProperty(globalObject, name, { + value, + writable: true, + enumerable: false, + configurable: true}); + + if( typeof value=== 'function') { + markVirtualizedNativeFunction(value); + } + } + }; + +/** + * setGlobalObjectEvaluators() + * Set the eval and the Function evaluator on the global object with given evalTaming policy. + * + * @param {object} globalObject + * @param {Function} evaluator + * @param {(object) => void} markVirtualizedNativeFunction + */$h‍_once.setGlobalObjectMutableProperties(setGlobalObjectMutableProperties); +const setGlobalObjectEvaluators= ( + globalObject, + evaluator, + markVirtualizedNativeFunction)=> + { + { + const f= makeEvalFunction(evaluator); + markVirtualizedNativeFunction(f); + defineProperty(globalObject, 'eval', { + value: f, + writable: true, + enumerable: false, + configurable: true}); + + } + { + const f= makeFunctionConstructor(evaluator); + markVirtualizedNativeFunction(f); + defineProperty(globalObject, 'Function', { + value: f, + writable: true, + enumerable: false, + configurable: true}); + + } + };$h‍_once.setGlobalObjectEvaluators(setGlobalObjectEvaluators); +}) +, +// === functors[23] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Proxy,String,TypeError,ReferenceError,create,freeze,getOwnPropertyDescriptors,globalThis,immutableObject,assert;$h‍_imports([["./commons.js", [["Proxy", [$h‍_a => (Proxy = $h‍_a)]],["String", [$h‍_a => (String = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["ReferenceError", [$h‍_a => (ReferenceError = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]],["immutableObject", [$h‍_a => (immutableObject = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + + + + + + + + + + +const { Fail, quote: q}= assert; + +/** + * alwaysThrowHandler + * This is an object that throws if any property is called. It's used as + * a proxy handler which throws on any trap called. + * It's made from a proxy with a get trap that throws. It's safe to + * create one and share it between all Proxy handlers. + */ +const alwaysThrowHandler= new Proxy( + immutableObject, + freeze({ + get(_shadow, prop) { + Fail `Please report unexpected scope handler trap: ${q(String(prop))}`; + }})); + + + +/* + * scopeProxyHandlerProperties + * scopeTerminatorHandler manages a strictScopeTerminator Proxy which serves as + * the final scope boundary that will always return "undefined" in order + * to prevent access to "start compartment globals". + */$h‍_once.alwaysThrowHandler(alwaysThrowHandler); +const scopeProxyHandlerProperties= { + get(_shadow, _prop) { + return undefined; + }, + + set(_shadow, prop, _value) { + // We should only hit this if the has() hook returned true matches the v8 + // ReferenceError message "Uncaught ReferenceError: xyz is not defined" + throw ReferenceError( `${String(prop)} is not defined`); + }, + + has(_shadow, prop) { + // we must at least return true for all properties on the realm globalThis + return prop in globalThis; + }, + + // note: this is likely a bug of safari + // https://bugs.webkit.org/show_bug.cgi?id=195534 + getPrototypeOf(_shadow) { + return null; + }, + + // See https://github.com/endojs/endo/issues/1510 + // TODO: report as bug to v8 or Chrome, and record issue link here. + getOwnPropertyDescriptor(_shadow, prop) { + // Coerce with `String` in case prop is a symbol. + const quotedProp= q(String(prop)); + // eslint-disable-next-line @endo/no-polymorphic-call + console.warn( + `getOwnPropertyDescriptor trap on scopeTerminatorHandler for ${quotedProp}`, + TypeError().stack); + + return undefined; + }, + + // See https://github.com/endojs/endo/issues/1490 + // TODO Report bug to JSC or Safari + ownKeys(_shadow) { + return []; + }}; + + +// The scope handler's prototype is a proxy that throws if any trap other +// than get/set/has are run (like getOwnPropertyDescriptors, apply, +// getPrototypeOf). +const strictScopeTerminatorHandler= freeze( + create( + alwaysThrowHandler, + getOwnPropertyDescriptors(scopeProxyHandlerProperties)));$h‍_once.strictScopeTerminatorHandler(strictScopeTerminatorHandler); + + + +const strictScopeTerminator= new Proxy( + immutableObject, + strictScopeTerminatorHandler);$h‍_once.strictScopeTerminator(strictScopeTerminator); +}) +, +// === functors[24] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Proxy,create,freeze,getOwnPropertyDescriptors,immutableObject,reflectSet,strictScopeTerminatorHandler,alwaysThrowHandler;$h‍_imports([["./commons.js", [["Proxy", [$h‍_a => (Proxy = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]],["immutableObject", [$h‍_a => (immutableObject = $h‍_a)]],["reflectSet", [$h‍_a => (reflectSet = $h‍_a)]]]],["./strict-scope-terminator.js", [["strictScopeTerminatorHandler", [$h‍_a => (strictScopeTerminatorHandler = $h‍_a)]],["alwaysThrowHandler", [$h‍_a => (alwaysThrowHandler = $h‍_a)]]]]]); + + + + + + + + + + + + +/* + * createSloppyGlobalsScopeTerminator() + * strictScopeTerminatorHandler manages a scopeTerminator Proxy which serves as + * the final scope boundary that will always return "undefined" in order + * to prevent access to "start compartment globals". When "sloppyGlobalsMode" + * is true, the Proxy will perform sets on the "globalObject". + */ +const createSloppyGlobalsScopeTerminator= (globalObject)=>{ + const scopeProxyHandlerProperties= { + // inherit scopeTerminator behavior + ...strictScopeTerminatorHandler, + + // Redirect set properties to the globalObject. + set(_shadow, prop, value) { + return reflectSet(globalObject, prop, value); + }, + + // Always claim to have a potential property in order to be the recipient of a set + has(_shadow, _prop) { + return true; + }}; + + + // The scope handler's prototype is a proxy that throws if any trap other + // than get/set/has are run (like getOwnPropertyDescriptors, apply, + // getPrototypeOf). + const sloppyGlobalsScopeTerminatorHandler= freeze( + create( + alwaysThrowHandler, + getOwnPropertyDescriptors(scopeProxyHandlerProperties))); + + + + const sloppyGlobalsScopeTerminator= new Proxy( + immutableObject, + sloppyGlobalsScopeTerminatorHandler); + + + return sloppyGlobalsScopeTerminator; + };$h‍_once.createSloppyGlobalsScopeTerminator(createSloppyGlobalsScopeTerminator); +freeze(createSloppyGlobalsScopeTerminator); +}) +, +// === functors[25] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_EVAL,create,defineProperties,freeze,assert;$h‍_imports([["./commons.js", [["FERAL_EVAL", [$h‍_a => (FERAL_EVAL = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + +const { Fail}= assert; + +// We attempt to frustrate stack bumping attacks on the safe evaluator +// (`make-safe-evaluator.js`). +// A stack bumping attack forces an API call to throw a stack overflow +// `RangeError` at an inopportune time. +// The attacker arranges for the stack to be sufficiently deep that the API +// consumes exactly enough stack frames to throw an exception. +// +// For the safe evaluator, an exception thrown between adding and then deleting +// `eval` on `evalScope` could leak the real `eval` to an attacker's lexical +// scope. +// This would be sufficiently disastrous that we guard against it twice. +// First, we delete `eval` from `evalScope` immediately before rendering it to +// the guest program's lexical scope. +// +// If the attacker manages to arrange for `eval` to throw an exception after we +// call `allowNextEvalToBeUnsafe` but before the guest program accesses `eval`, +// it would be able to access `eval` once more in its own code. +// Although they could do no harm with a direct `eval`, they would be able to +// escape to the true global scope with an indirect `eval`. +// +// prepareStack(depth, () => { +// (eval)(''); +// }); +// const unsafeEval = (eval); +// const safeEval = (eval); +// const realGlobal = unsafeEval('globalThis'); +// +// To protect against that case, we also delete `eval` from the `evalScope` in +// a `finally` block surrounding the call to the safe evaluator. +// The only way to reach this case is if `eval` remains on `evalScope` due to +// an attack, so we assume that attack would have have invalided our isolation +// and revoke all future access to the evaluator. +// +// To defeat a stack bumping attack, we must use fewer stack frames to recover +// in that `finally` block than we used in the `try` block. +// We have no reliable guarantees about how many stack frames a block of +// JavaScript will consume. +// Function inlining, tail-call optimization, variations in the size of a stack +// frame, and block scopes may affect the depth of the stack. +// The only number of acceptable stack frames to use in the finally block is +// zero. +// We only use property assignment and deletion in the safe evaluator's +// `finally` block. +// We use `delete evalScope.eval` to withhold the evaluator. +// We assign an envelope object over `evalScopeKit.revoked` to revoke the +// evaluator. +// +// This is why we supply a meaningfully named function for +// `allowNextEvalToBeUnsafe` but do not provide a corresponding +// `revokeAccessToUnsafeEval` or even simply `revoke`. +// These recovery routines are expressed inline in the safe evaluator. + +const makeEvalScopeKit= ()=> { + const evalScope= create(null); + const oneTimeEvalProperties= freeze({ + eval: { + get() { + delete evalScope.eval; + return FERAL_EVAL; + }, + enumerable: false, + configurable: true}}); + + + + const evalScopeKit= { + evalScope, + allowNextEvalToBeUnsafe() { + const { revoked}= evalScopeKit; + if( revoked!== null) { + Fail `a handler did not reset allowNextEvalToBeUnsafe ${revoked.err}`; + } + // Allow next reference to eval produce the unsafe FERAL_EVAL. + // We avoid defineProperty because it consumes an extra stack frame taming + // its return value. + defineProperties(evalScope, oneTimeEvalProperties); + }, + /** @type {null | { err: any }} */ + revoked: null}; + + + return evalScopeKit; + };$h‍_once.makeEvalScopeKit(makeEvalScopeKit); +}) +, +// === functors[26] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_REG_EXP,regexpExec,stringSlice;$h‍_imports([["./commons.js", [["FERAL_REG_EXP", [$h‍_a => (FERAL_REG_EXP = $h‍_a)]],["regexpExec", [$h‍_a => (regexpExec = $h‍_a)]],["stringSlice", [$h‍_a => (stringSlice = $h‍_a)]]]]]); + +// Captures a key and value of the form #key=value or @key=value +const sourceMetaEntryRegExp= + '\\s*[@#]\\s*([a-zA-Z][a-zA-Z0-9]*)\\s*=\\s*([^\\s\\*]*)'; +// Captures either a one-line or multi-line comment containing +// one #key=value or @key=value. +// Produces two pairs of capture groups, but the initial two may be undefined. +// On account of the mechanics of regular expressions, scanning from the end +// does not allow us to capture every pair, so getSourceURL must capture and +// trim until there are no matching comments. +const sourceMetaEntriesRegExp= new FERAL_REG_EXP( + `(?:\\s*//${sourceMetaEntryRegExp}|/\\*${sourceMetaEntryRegExp}\\s*\\*/)\\s*$`); + + +/** + * @param {string} src + */ +const getSourceURL= (src)=>{ + let sourceURL= ''; + + // Our regular expression matches the last one or two comments with key value + // pairs at the end of the source, avoiding a scan over the entire length of + // the string, but at the expense of being able to capture all the (key, + // value) pair meta comments at the end of the source, which may include + // sourceMapURL in addition to sourceURL. + // So, we sublimate the comments out of the source until no source or no + // comments remain. + while( src.length> 0) { + const match= regexpExec(sourceMetaEntriesRegExp, src); + if( match=== null) { + break; + } + src= stringSlice(src, 0, src.length- match[0].length); + + // We skip $0 since it contains the entire match. + // The match contains four capture groups, + // two (key, value) pairs, the first of which + // may be undefined. + // On the off-chance someone put two sourceURL comments in their code with + // different commenting conventions, the latter has precedence. + if( match[3]=== 'sourceURL') { + sourceURL= match[4]; + }else if( match[1]=== 'sourceURL') { + sourceURL= match[2]; + } + } + + return sourceURL; + };$h‍_once.getSourceURL(getSourceURL); +}) +, +// === functors[27] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_REG_EXP,SyntaxError,stringReplace,stringSearch,stringSlice,stringSplit,freeze,getSourceURL;$h‍_imports([["./commons.js", [["FERAL_REG_EXP", [$h‍_a => (FERAL_REG_EXP = $h‍_a)]],["SyntaxError", [$h‍_a => (SyntaxError = $h‍_a)]],["stringReplace", [$h‍_a => (stringReplace = $h‍_a)]],["stringSearch", [$h‍_a => (stringSearch = $h‍_a)]],["stringSlice", [$h‍_a => (stringSlice = $h‍_a)]],["stringSplit", [$h‍_a => (stringSplit = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]]]],["./get-source-url.js", [["getSourceURL", [$h‍_a => (getSourceURL = $h‍_a)]]]]]); + + + + + + + + + + + + +/** + * Find the first occurence of the given pattern and return + * the location as the approximate line number. + * + * @param {string} src + * @param {RegExp} pattern + * @returns {number} + */ +function getLineNumber(src, pattern) { + const index= stringSearch(src, pattern); + if( index< 0) { + return -1; + } + + // The importPattern incidentally captures an initial \n in + // an attempt to reject a . prefix, so we need to offset + // the line number in that case. + const adjustment= src[index]=== '\n'? 1: 0; + + return stringSplit(stringSlice(src, 0, index), '\n').length+ adjustment; + } + +// ///////////////////////////////////////////////////////////////////////////// + +const htmlCommentPattern= new FERAL_REG_EXP( `(?:${'<'}!--|--${'>'})`,'g'); + +/** + * Conservatively reject the source text if it may contain text that some + * JavaScript parsers may treat as an html-like comment. To reject without + * parsing, `rejectHtmlComments` will also reject some other text as well. + * + * https://www.ecma-international.org/ecma-262/9.0/index.html#sec-html-like-comments + * explains that JavaScript parsers may or may not recognize html + * comment tokens "<" immediately followed by "!--" and "--" + * immediately followed by ">" in non-module source text, and treat + * them as a kind of line comment. Since otherwise both of these can + * appear in normal JavaScript source code as a sequence of operators, + * we have the terrifying possibility of the same source code parsing + * one way on one correct JavaScript implementation, and another way + * on another. + * + * This shim takes the conservative strategy of just rejecting source + * text that contains these strings anywhere. Note that this very + * source file is written strangely to avoid mentioning these + * character strings explicitly. + * + * We do not write the regexp in a straightforward way, so that an + * apparennt html comment does not appear in this file. Thus, we avoid + * rejection by the overly eager rejectDangerousSources. + * + * @param {string} src + * @returns {string} + */ +const rejectHtmlComments= (src)=>{ + const lineNumber= getLineNumber(src, htmlCommentPattern); + if( lineNumber< 0) { + return src; + } + const name= getSourceURL(src); + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_HTML_COMMENT_REJECTED.md + throw SyntaxError( + `Possible HTML comment rejected at ${name}:${lineNumber}. (SES_HTML_COMMENT_REJECTED)`); + + }; + +/** + * An optional transform to place ahead of `rejectHtmlComments` to evade *that* + * rejection. However, it may change the meaning of the program. + * + * This evasion replaces each alleged html comment with the space-separated + * JavaScript operator sequence that it may mean, assuming that it appears + * outside of a comment or literal string, in source code where the JS + * parser makes no special case for html comments (like module source code). + * In that case, this evasion preserves the meaning of the program, though it + * does change the souce column numbers on each effected line. + * + * If the html comment appeared in a literal (a string literal, regexp literal, + * or a template literal), then this evasion will change the meaning of the + * program by changing the text of that literal. + * + * If the html comment appeared in a JavaScript comment, then this evasion does + * not change the meaning of the program because it only changes the contents of + * those comments. + * + * @param {string} src + * @returns {string} + */$h‍_once.rejectHtmlComments(rejectHtmlComments); +const evadeHtmlCommentTest= (src)=>{ + const replaceFn= (match)=> match[0]=== '<'? '< ! --': '-- >'; + return stringReplace(src, htmlCommentPattern, replaceFn); + }; + +// ///////////////////////////////////////////////////////////////////////////// +$h‍_once.evadeHtmlCommentTest(evadeHtmlCommentTest); +const importPattern= new FERAL_REG_EXP( + '(^|[^.]|\\.\\.\\.)\\bimport(\\s*(?:\\(|/[/*]))', + 'g'); + + +/** + * Conservatively reject the source text if it may contain a dynamic + * import expression. To reject without parsing, `rejectImportExpressions` will + * also reject some other text as well. + * + * The proposed dynamic import expression is the only syntax currently + * proposed, that can appear in non-module JavaScript code, that + * enables direct access to the outside world that cannot be + * suppressed or intercepted without parsing and rewriting. Instead, + * this shim conservatively rejects any source text that seems to + * contain such an expression. To do this safely without parsing, we + * must also reject some valid programs, i.e., those containing + * apparent import expressions in literal strings or comments. + * + * The current conservative rule looks for the identifier "import" + * followed by either an open paren or something that looks like the + * beginning of a comment. We assume that we do not need to worry + * about html comment syntax because that was already rejected by + * rejectHtmlComments. + * + * this \s *must* match all kinds of syntax-defined whitespace. If e.g. + * U+2028 (LINE SEPARATOR) or U+2029 (PARAGRAPH SEPARATOR) is treated as + * whitespace by the parser, but not matched by /\s/, then this would admit + * an attack like: import\u2028('power.js') . We're trying to distinguish + * something like that from something like importnotreally('power.js') which + * is perfectly safe. + * + * @param {string} src + * @returns {string} + */ +const rejectImportExpressions= (src)=>{ + const lineNumber= getLineNumber(src, importPattern); + if( lineNumber< 0) { + return src; + } + const name= getSourceURL(src); + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_IMPORT_REJECTED.md + throw SyntaxError( + `Possible import expression rejected at ${name}:${lineNumber}. (SES_IMPORT_REJECTED)`); + + }; + +/** + * An optional transform to place ahead of `rejectImportExpressions` to evade + * *that* rejection. However, it may change the meaning of the program. + * + * This evasion replaces each suspicious `import` identifier with `__import__`. + * If the alleged import expression appears in a JavaScript comment, this + * evasion will not change the meaning of the program. If it appears in a + * literal (string literal, regexp literal, or a template literal), then this + * evasion will change the contents of that literal. If it appears as code + * where it would be parsed as an expression, then it might or might not change + * the meaning of the program, depending on the binding, if any, of the lexical + * variable `__import__`. + * + * @param {string} src + * @returns {string} + */$h‍_once.rejectImportExpressions(rejectImportExpressions); +const evadeImportExpressionTest= (src)=>{ + const replaceFn= (_, p1, p2)=> `${p1}__import__${p2}`; + return stringReplace(src, importPattern, replaceFn); + }; + +// ///////////////////////////////////////////////////////////////////////////// +$h‍_once.evadeImportExpressionTest(evadeImportExpressionTest); +const someDirectEvalPattern= new FERAL_REG_EXP( + '(^|[^.])\\beval(\\s*\\()', + 'g'); + + +/** + * Heuristically reject some text that seems to contain a direct eval + * expression, with both false positives and false negavives. To reject without + * parsing, `rejectSomeDirectEvalExpressions` may will also reject some other + * text as well. It may also accept source text that contains a direct eval + * written oddly, such as `(eval)(src)`. This false negative is not a security + * vulnerability. Rather it is a compat hazard because it will execute as + * an indirect eval under the SES-shim but as a direct eval on platforms that + * support SES directly (like XS). + * + * The shim cannot correctly emulate a direct eval as explained at + * https://github.com/Agoric/realms-shim/issues/12 + * If we did not reject direct eval syntax, we would + * accidentally evaluate these with an emulation of indirect eval. To + * prevent future compatibility problems, in shifting from use of the + * shim to genuine platform support for the proposal, we should + * instead statically reject code that seems to contain a direct eval + * expression. + * + * As with the dynamic import expression, to avoid a full parse, we do + * this approximately with a regexp, that will also reject strings + * that appear safely in comments or strings. Unlike dynamic import, + * if we miss some, this only creates future compat problems, not + * security problems. Thus, we are only trying to catch innocent + * occurrences, not malicious one. In particular, `(eval)(...)` is + * direct eval syntax that would not be caught by the following regexp. + * + * Exported for unit tests. + * + * @param {string} src + * @returns {string} + */ +const rejectSomeDirectEvalExpressions= (src)=>{ + const lineNumber= getLineNumber(src, someDirectEvalPattern); + if( lineNumber< 0) { + return src; + } + const name= getSourceURL(src); + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_EVAL_REJECTED.md + throw SyntaxError( + `Possible direct eval expression rejected at ${name}:${lineNumber}. (SES_EVAL_REJECTED)`); + + }; + +// ///////////////////////////////////////////////////////////////////////////// + +/** + * A transform that bundles together the transforms that must unconditionally + * happen last in order to ensure safe evaluation without parsing. + * + * @param {string} source + * @returns {string} + */$h‍_once.rejectSomeDirectEvalExpressions(rejectSomeDirectEvalExpressions); +const mandatoryTransforms= (source)=>{ + source= rejectHtmlComments(source); + source= rejectImportExpressions(source); + return source; + }; + +/** + * Starting with `source`, apply each transform to the result of the + * previous one, returning the result of the last transformation. + * + * @param {string} source + * @param {((str: string) => string)[]} transforms + * @returns {string} + */$h‍_once.mandatoryTransforms(mandatoryTransforms); +const applyTransforms= (source, transforms)=> { + for( const transform of transforms) { + source= transform(source); + } + return source; + }; + +// export all as a frozen object +$h‍_once.applyTransforms(applyTransforms);const transforms=freeze({ + rejectHtmlComments: freeze(rejectHtmlComments), + evadeHtmlCommentTest: freeze(evadeHtmlCommentTest), + rejectImportExpressions: freeze(rejectImportExpressions), + evadeImportExpressionTest: freeze(evadeImportExpressionTest), + rejectSomeDirectEvalExpressions: freeze(rejectSomeDirectEvalExpressions), + mandatoryTransforms: freeze(mandatoryTransforms), + applyTransforms: freeze(applyTransforms)});$h‍_once.transforms(transforms); +}) +, +// === functors[28] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let arrayFilter,arrayIncludes,getOwnPropertyDescriptor,getOwnPropertyNames,objectHasOwnProperty,regexpTest;$h‍_imports([["./commons.js", [["arrayFilter", [$h‍_a => (arrayFilter = $h‍_a)]],["arrayIncludes", [$h‍_a => (arrayIncludes = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["getOwnPropertyNames", [$h‍_a => (getOwnPropertyNames = $h‍_a)]],["objectHasOwnProperty", [$h‍_a => (objectHasOwnProperty = $h‍_a)]],["regexpTest", [$h‍_a => (regexpTest = $h‍_a)]]]]]); + + + + + + + + +/** + * keywords + * In JavaScript you cannot use these reserved words as variables. + * See 11.6.1 Identifier Names + */ +const keywords= [ + // 11.6.2.1 Keywords + 'await', + 'break', + 'case', + 'catch', + 'class', + 'const', + 'continue', + 'debugger', + 'default', + 'delete', + 'do', + 'else', + 'export', + 'extends', + 'finally', + 'for', + 'function', + 'if', + 'import', + 'in', + 'instanceof', + 'new', + 'return', + 'super', + 'switch', + 'this', + 'throw', + 'try', + 'typeof', + 'var', + 'void', + 'while', + 'with', + 'yield', + + // Also reserved when parsing strict mode code + 'let', + 'static', + + // 11.6.2.2 Future Reserved Words + 'enum', + + // Also reserved when parsing strict mode code + 'implements', + 'package', + 'protected', + 'interface', + 'private', + 'public', + + // Reserved but not mentioned in specs + 'await', + + 'null', + 'true', + 'false', + + 'this', + 'arguments']; + + +/** + * identifierPattern + * Simplified validation of identifier names: may only contain alphanumeric + * characters (or "$" or "_"), and may not start with a digit. This is safe + * and does not reduces the compatibility of the shim. The motivation for + * this limitation was to decrease the complexity of the implementation, + * and to maintain a resonable level of performance. + * Note: \w is equivalent [a-zA-Z_0-9] + * See 11.6.1 Identifier Names + */ +const identifierPattern= /^[a-zA-Z_$][\w$]*$/; + +/** + * isValidIdentifierName() + * What variable names might it bring into scope? These include all + * property names which can be variable names, including the names + * of inherited properties. It excludes symbols and names which are + * keywords. We drop symbols safely. Currently, this shim refuses + * service if any of the names are keywords or keyword-like. This is + * safe and only prevent performance optimization. + * + * @param {string} name + */ +const isValidIdentifierName= (name)=>{ + // Ensure we have a valid identifier. We use regexpTest rather than + // /../.test() to guard against the case where RegExp has been poisoned. + return( + name!== 'eval'&& + !arrayIncludes(keywords, name)&& + regexpTest(identifierPattern, name)); + + }; + +/* + * isImmutableDataProperty + */$h‍_once.isValidIdentifierName(isValidIdentifierName); + +function isImmutableDataProperty(obj, name) { + const desc= getOwnPropertyDescriptor(obj, name); + return( + desc&& + // + // The getters will not have .writable, don't let the falsyness of + // 'undefined' trick us: test with === false, not ! . However descriptors + // inherit from the (potentially poisoned) global object, so we might see + // extra properties which weren't really there. Accessor properties have + // 'get/set/enumerable/configurable', while data properties have + // 'value/writable/enumerable/configurable'. + desc.configurable=== false&& + desc.writable=== false&& + // + // Checks for data properties because they're the only ones we can + // optimize (accessors are most likely non-constant). Descriptors can't + // can't have accessors and value properties at the same time, therefore + // this check is sufficient. Using explicit own property deal with the + // case where Object.prototype has been poisoned. + objectHasOwnProperty(desc, 'value')); + + } + +/** + * getScopeConstants() + * What variable names might it bring into scope? These include all + * property names which can be variable names, including the names + * of inherited properties. It excludes symbols and names which are + * keywords. We drop symbols safely. Currently, this shim refuses + * service if any of the names are keywords or keyword-like. This is + * safe and only prevent performance optimization. + * + * @param {object} globalObject + * @param {object} moduleLexicals + */ +const getScopeConstants= (globalObject, moduleLexicals= {})=> { + // getOwnPropertyNames() does ignore Symbols so we don't need to + // filter them out. + const globalObjectNames= getOwnPropertyNames(globalObject); + const moduleLexicalNames= getOwnPropertyNames(moduleLexicals); + + // Collect all valid & immutable identifiers from the endowments. + const moduleLexicalConstants= arrayFilter( + moduleLexicalNames, + (name)=> + isValidIdentifierName(name)&& + isImmutableDataProperty(moduleLexicals, name)); + + + // Collect all valid & immutable identifiers from the global that + // are also not present in the endowments (immutable or not). + const globalObjectConstants= arrayFilter( + globalObjectNames, + (name)=> + // Can't define a constant: it would prevent a + // lookup on the endowments. + !arrayIncludes(moduleLexicalNames, name)&& + isValidIdentifierName(name)&& + isImmutableDataProperty(globalObject, name)); + + + return { + globalObjectConstants, + moduleLexicalConstants}; + + };$h‍_once.getScopeConstants(getScopeConstants); +}) +, +// === functors[29] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_FUNCTION,arrayJoin,apply,getScopeConstants;$h‍_imports([["./commons.js", [["FERAL_FUNCTION", [$h‍_a => (FERAL_FUNCTION = $h‍_a)]],["arrayJoin", [$h‍_a => (arrayJoin = $h‍_a)]],["apply", [$h‍_a => (apply = $h‍_a)]]]],["./scope-constants.js", [["getScopeConstants", [$h‍_a => (getScopeConstants = $h‍_a)]]]]]); + + + + +/** + * buildOptimizer() + * Given an array of identifiers, the optimizer returns a `const` declaration + * destructuring `this.${name}`. + * + * @param {Array} constants + * @param {string} name + */ +function buildOptimizer(constants, name) { + // No need to build an optimizer when there are no constants. + if( constants.length=== 0) return ''; + // Use 'this' to avoid going through the scope proxy, which is unnecessary + // since the optimizer only needs references to the safe global. + // Destructure the constants from the target scope object. + return `const {${arrayJoin(constants,',') }} = this.${name};`; + } + +/** + * makeEvaluate() + * Create an 'evaluate' function with the correct optimizer inserted. + * + * @param {object} context + * @param {object} context.evalScope + * @param {object} context.moduleLexicals + * @param {object} context.globalObject + * @param {object} context.scopeTerminator + */ +const makeEvaluate= (context)=>{ + const { globalObjectConstants, moduleLexicalConstants}= getScopeConstants( + context.globalObject, + context.moduleLexicals); + + const globalObjectOptimizer= buildOptimizer( + globalObjectConstants, + 'globalObject'); + + const moduleLexicalOptimizer= buildOptimizer( + moduleLexicalConstants, + 'moduleLexicals'); + + + // Create a function in sloppy mode, so that we can use 'with'. It returns + // a function in strict mode that evaluates the provided code using direct + // eval, and thus in strict mode in the same scope. We must be very careful + // to not create new names in this scope + + // 1: we use multiple nested 'with' to catch all free variable names. The + // `this` value of the outer sloppy function holds the different scope + // layers, from inner to outer: + // a) `evalScope` which must release the `FERAL_EVAL` as 'eval' once for + // every invocation of the inner `evaluate` function in order to + // trigger direct eval. The direct eval semantics is what allows the + // evaluated code to lookup free variable names on the other scope + // objects and not in global scope. + // b) `moduleLexicals` which provide a way to introduce free variables + // that are not available on the globalObject. + // c) `globalObject` is the global scope object of the evaluator, aka the + // Compartment's `globalThis`. + // d) `scopeTerminator` is a proxy object which prevents free variable + // lookups to escape to the start compartment's global object. + // 2: `optimizer`s catch constant variable names for speed. + // 3: The inner strict `evaluate` function should be passed two parameters: + // a) its arguments[0] is the source to be directly evaluated. + // b) its 'this' is the this binding seen by the code being + // directly evaluated (the globalObject). + + // Notes: + // - The `optimizer` strings only lookup values on the `globalObject` and + // `moduleLexicals` objects by construct. Keywords like 'function' are + // reserved and cannot be used as a variable, so they are excluded from the + // optimizer. Furthermore to prevent shadowing 'eval', while a valid + // identifier, that name is also explicitly excluded. + // - when 'eval' is looked up in the `evalScope`, the powerful unsafe eval + // intrinsic is returned after automatically removing it from the + // `evalScope`. Any further reference to 'eval' in the evaluate string will + // get the tamed evaluator from the `globalObject`, if any. + + // TODO https://github.com/endojs/endo/issues/816 + // The optimizer currently runs under sloppy mode, and although we doubt that + // there is any vulnerability introduced just by running the optimizer + // sloppy, we are much more confident in the semantics of strict mode. + // The `evaluate` function can be and is reused across multiple evaluations. + // Since the optimizer should not be re-evaluated every time, it cannot be + // inside the `evaluate` closure. While we could potentially nest an + // intermediate layer of `() => {'use strict'; ${optimizers}; ...`, it + // doesn't seem worth the overhead and complexity. + const evaluateFactory= FERAL_FUNCTION( ` + with (this.scopeTerminator) { + with (this.globalObject) { + with (this.moduleLexicals) { + with (this.evalScope) { + ${globalObjectOptimizer } + ${moduleLexicalOptimizer } + return function() { + 'use strict'; + return eval(arguments[0]); + }; + } + } + } + } + `); + + return apply(evaluateFactory, context, []); + };$h‍_once.makeEvaluate(makeEvaluate); +}) +, +// === functors[30] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let apply,freeze,strictScopeTerminator,createSloppyGlobalsScopeTerminator,makeEvalScopeKit,applyTransforms,mandatoryTransforms,makeEvaluate,assert;$h‍_imports([["./commons.js", [["apply", [$h‍_a => (apply = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]]]],["./strict-scope-terminator.js", [["strictScopeTerminator", [$h‍_a => (strictScopeTerminator = $h‍_a)]]]],["./sloppy-globals-scope-terminator.js", [["createSloppyGlobalsScopeTerminator", [$h‍_a => (createSloppyGlobalsScopeTerminator = $h‍_a)]]]],["./eval-scope.js", [["makeEvalScopeKit", [$h‍_a => (makeEvalScopeKit = $h‍_a)]]]],["./transforms.js", [["applyTransforms", [$h‍_a => (applyTransforms = $h‍_a)]],["mandatoryTransforms", [$h‍_a => (mandatoryTransforms = $h‍_a)]]]],["./make-evaluate.js", [["makeEvaluate", [$h‍_a => (makeEvaluate = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + + + + + + + + +const { Fail}= assert; + +/** + * makeSafeEvaluator() + * Build the low-level operation used by all evaluators: + * eval(), Function(), Compartment.prototype.evaluate(). + * + * @param {object} options + * @param {object} options.globalObject + * @param {object} [options.moduleLexicals] + * @param {Array} [options.globalTransforms] + * @param {boolean} [options.sloppyGlobalsMode] + */ +const makeSafeEvaluator= ({ + globalObject, + moduleLexicals= {}, + globalTransforms= [], + sloppyGlobalsMode= false})=> + { + const scopeTerminator= sloppyGlobalsMode? + createSloppyGlobalsScopeTerminator(globalObject): + strictScopeTerminator; + const evalScopeKit= makeEvalScopeKit(); + const { evalScope}= evalScopeKit; + + const evaluateContext= freeze({ + evalScope, + moduleLexicals, + globalObject, + scopeTerminator}); + + + // Defer creating the actual evaluator to first use. + // Creating a compartment should be possible in no-eval environments + // It also allows more global constants to be captured by the optimizer + let evaluate; + const provideEvaluate= ()=> { + if( !evaluate) { + evaluate= makeEvaluate(evaluateContext); + } + }; + + /** + * @param {string} source + * @param {object} [options] + * @param {Array} [options.localTransforms] + */ + const safeEvaluate= (source, options)=> { + const { localTransforms= []}= options|| {}; + provideEvaluate(); + + // Execute the mandatory transforms last to ensure that any rewritten code + // meets those mandatory requirements. + source= applyTransforms(source, [ + ...localTransforms, + ...globalTransforms, + mandatoryTransforms]); + + + let err; + try { + // Allow next reference to eval produce the unsafe FERAL_EVAL. + // eslint-disable-next-line @endo/no-polymorphic-call + evalScopeKit.allowNextEvalToBeUnsafe(); + + // Ensure that "this" resolves to the safe global. + return apply(evaluate, globalObject, [source]); + }catch( e) { + // stash the child-code error in hopes of debugging the internal failure + err= e; + throw e; + }finally { + const unsafeEvalWasStillExposed=( 'eval'in evalScope); + delete evalScope.eval; + if( unsafeEvalWasStillExposed) { + // Barring a defect in the SES shim, the evalScope should allow the + // powerful, unsafe `eval` to be used by `evaluate` exactly once, as the + // very first name that it attempts to access from the lexical scope. + // A defect in the SES shim could throw an exception after we set + // `evalScope.eval` and before `evaluate` calls `eval` internally. + // If we get here, SES is very broken. + // This condition is one where this vat is now hopelessly confused, and + // the vat as a whole should be aborted. + // No further code should run. + // All immediately reachable state should be abandoned. + // However, that is not yet possible, so we at least prevent further + // variable resolution via the scopeHandler, and throw an error with + // diagnostic info including the thrown error if any from evaluating the + // source code. + evalScopeKit.revoked= { err}; + // TODO A GOOD PLACE TO PANIC(), i.e., kill the vat incarnation. + // See https://github.com/Agoric/SES-shim/issues/490 + Fail `handler did not reset allowNextEvalToBeUnsafe ${err}`; + } + } + }; + + return { safeEvaluate}; + };$h‍_once.makeSafeEvaluator(makeSafeEvaluator); +}) +, +// === functors[31] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let WeakSet,defineProperty,freeze,functionPrototype,functionToString,stringEndsWith,weaksetAdd,weaksetHas;$h‍_imports([["./commons.js", [["WeakSet", [$h‍_a => (WeakSet = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["functionPrototype", [$h‍_a => (functionPrototype = $h‍_a)]],["functionToString", [$h‍_a => (functionToString = $h‍_a)]],["stringEndsWith", [$h‍_a => (stringEndsWith = $h‍_a)]],["weaksetAdd", [$h‍_a => (weaksetAdd = $h‍_a)]],["weaksetHas", [$h‍_a => (weaksetHas = $h‍_a)]]]]]); + + + + + + + + + + +const nativeSuffix= ') { [native code] }'; + +// Note: Top level mutable state. Does not make anything worse, since the +// patching of `Function.prototype.toString` is also globally stateful. We +// use this top level state so that multiple calls to `tameFunctionToString` are +// idempotent, rather than creating redundant indirections. +let markVirtualizedNativeFunction; + +/** + * Replace `Function.prototype.toString` with one that recognizes + * shimmed functions as honorary native functions. + */ +const tameFunctionToString= ()=> { + if( markVirtualizedNativeFunction=== undefined) { + const virtualizedNativeFunctions= new WeakSet(); + + const tamingMethods= { + toString() { + const str= functionToString(this); + if( + stringEndsWith(str, nativeSuffix)|| + !weaksetHas(virtualizedNativeFunctions, this)) + { + return str; + } + return `function ${this.name}() { [native code] }`; + }}; + + + defineProperty(functionPrototype, 'toString', { + value: tamingMethods.toString}); + + + markVirtualizedNativeFunction= freeze((func)=> + weaksetAdd(virtualizedNativeFunctions, func)); + + } + return markVirtualizedNativeFunction; + };$h‍_once.tameFunctionToString(tameFunctionToString); +}) +, +// === functors[32] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let TypeError,globalThis,getOwnPropertyDescriptor,defineProperty;$h‍_imports([["./commons.js", [["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]]]]]);Object.defineProperty(tameDomains, 'name', {value: "tameDomains"});$h‍_once.tameDomains(tameDomains); + + + + + + + + +function tameDomains(domainTaming= 'safe') { + if( domainTaming!== 'safe'&& domainTaming!== 'unsafe') { + throw TypeError( `unrecognized domainTaming ${domainTaming}`); + } + + if( domainTaming=== 'unsafe') { + return; + } + + // Protect against the hazard presented by Node.js domains. + if( typeof globalThis.process=== 'object'&& globalThis.process!== null) { + // Check whether domains were initialized. + const domainDescriptor= getOwnPropertyDescriptor( + globalThis.process, + 'domain'); + + if( domainDescriptor!== undefined&& domainDescriptor.get!== undefined) { + // The domain descriptor on Node.js initially has value: null, which + // becomes a get, set pair after domains initialize. + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_NO_DOMAINS.md + throw TypeError( + `SES failed to lockdown, Node.js domains have been initialized (SES_NO_DOMAINS)`); + + } + // Prevent domains from initializing. + // This is clunky because the exception thrown from the domains package does + // not direct the user's gaze toward a knowledge base about the problem. + // The domain module merely throws an exception when it attempts to define + // the domain property of the process global during its initialization. + // We have no better recourse because Node.js uses defineProperty too. + defineProperty(globalThis.process, 'domain', { + value: null, + configurable: false, + writable: false, + enumerable: false}); + + } + } +}) +, +// === functors[33] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let WeakSet,arrayFilter,arrayMap,arrayPush,defineProperty,freeze,fromEntries,isError,stringEndsWith,weaksetAdd,weaksetHas;$h‍_imports([["../commons.js", [["WeakSet", [$h‍_a => (WeakSet = $h‍_a)]],["arrayFilter", [$h‍_a => (arrayFilter = $h‍_a)]],["arrayMap", [$h‍_a => (arrayMap = $h‍_a)]],["arrayPush", [$h‍_a => (arrayPush = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["fromEntries", [$h‍_a => (fromEntries = $h‍_a)]],["isError", [$h‍_a => (isError = $h‍_a)]],["stringEndsWith", [$h‍_a => (stringEndsWith = $h‍_a)]],["weaksetAdd", [$h‍_a => (weaksetAdd = $h‍_a)]],["weaksetHas", [$h‍_a => (weaksetHas = $h‍_a)]]]],["./types.js", []],["./internal-types.js", []]]); + + + + + + + + + + + + + + + + + + + + + + +// For our internal debugging purposes, uncomment +// const internalDebugConsole = console; + +// The whitelists of console methods, from: +// Whatwg "living standard" https://console.spec.whatwg.org/ +// Node https://nodejs.org/dist/latest-v14.x/docs/api/console.html +// MDN https://developer.mozilla.org/en-US/docs/Web/API/Console_API +// TypeScript https://openstapps.gitlab.io/projectmanagement/interfaces/_node_modules__types_node_globals_d_.console.html +// Chrome https://developers.google.com/web/tools/chrome-devtools/console/api + +// All console level methods have parameters (fmt?, ...args) +// where the argument sequence `fmt?, ...args` formats args according to +// fmt if fmt is a format string. Otherwise, it just renders them all as values +// separated by spaces. +// https://console.spec.whatwg.org/#formatter +// https://nodejs.org/docs/latest/api/util.html#util_util_format_format_args + +// For the causal console, all occurrences of `fmt, ...args` or `...args` by +// itself must check for the presence of an error to ask the +// `loggedErrorHandler` to handle. +// In theory we should do a deep inspection to detect for example an array +// containing an error. We currently do not detect these and may never. + +/** @typedef {keyof VirtualConsole | 'profile' | 'profileEnd'} ConsoleProps */ + +/** @type {readonly [ConsoleProps, LogSeverity | undefined][]} */ +const consoleLevelMethods= freeze([ + ['debug', 'debug'], // (fmt?, ...args) verbose level on Chrome + ['log', 'log'], // (fmt?, ...args) info level on Chrome + ['info', 'info'], // (fmt?, ...args) + ['warn', 'warn'], // (fmt?, ...args) + ['error', 'error'], // (fmt?, ...args) + + ['trace', 'log'], // (fmt?, ...args) + ['dirxml', 'log'], // (fmt?, ...args) + ['group', 'log'], // (fmt?, ...args) + ['groupCollapsed', 'log'] // (fmt?, ...args) +]); + +/** @type {readonly [ConsoleProps, LogSeverity | undefined][]} */ +const consoleOtherMethods= freeze([ + ['assert', 'error'], // (value, fmt?, ...args) + ['timeLog', 'log'], // (label?, ...args) no fmt string + + // Insensitive to whether any argument is an error. All arguments can pass + // thru to baseConsole as is. + ['clear', undefined], // () + ['count', 'info'], // (label?) + ['countReset', undefined], // (label?) + ['dir', 'log'], // (item, options?) + ['groupEnd', 'log'], // () + // In theory tabular data may be or contain an error. However, we currently + // do not detect these and may never. + ['table', 'log'], // (tabularData, properties?) + ['time', 'info'], // (label?) + ['timeEnd', 'info'], // (label?) + + // Node Inspector only, MDN, and TypeScript, but not whatwg + ['profile', undefined], // (label?) + ['profileEnd', undefined], // (label?) + ['timeStamp', undefined] // (label?) +]); + +/** @type {readonly [ConsoleProps, LogSeverity | undefined][]} */ +const consoleWhitelist= freeze([ + ...consoleLevelMethods, + ...consoleOtherMethods]); + + +/** + * consoleOmittedProperties is currently unused. I record and maintain it here + * with the intention that it be treated like the `false` entries in the main + * SES whitelist: that seeing these on the original console is expected, but + * seeing anything else that's outside the whitelist is surprising and should + * provide a diagnostic. + * + * const consoleOmittedProperties = freeze([ + * 'memory', // Chrome + * 'exception', // FF, MDN + * '_ignoreErrors', // Node + * '_stderr', // Node + * '_stderrErrorHandler', // Node + * '_stdout', // Node + * '_stdoutErrorHandler', // Node + * '_times', // Node + * 'context', // Chrome, Node + * 'record', // Safari + * 'recordEnd', // Safari + * + * 'screenshot', // Safari + * // Symbols + * '@@toStringTag', // Chrome: "Object", Safari: "Console" + * // A variety of other symbols also seen on Node + * ]); + */ + +// ///////////////////////////////////////////////////////////////////////////// + +/** @type {MakeLoggingConsoleKit} */$h‍_once.consoleWhitelist(consoleWhitelist); +const makeLoggingConsoleKit= ( + loggedErrorHandler, + { shouldResetForDebugging= false}= {})=> + { + if( shouldResetForDebugging) { + // eslint-disable-next-line @endo/no-polymorphic-call + loggedErrorHandler.resetErrorTagNum(); + } + + // Not frozen! + let logArray= []; + + const loggingConsole= fromEntries( + arrayMap(consoleWhitelist, ([name, _])=> { + // Use an arrow function so that it doesn't come with its own name in + // its printed form. Instead, we're hoping that tooling uses only + // the `.name` property set below. + /** + * @param {...any} args + */ + const method= (...args)=> { + arrayPush(logArray, [name, ...args]); + }; + defineProperty(method, 'name', { value: name}); + return [name, freeze(method)]; + })); + + freeze(loggingConsole); + + const takeLog= ()=> { + const result= freeze(logArray); + logArray= []; + return result; + }; + freeze(takeLog); + + const typedLoggingConsole= /** @type {VirtualConsole} */ loggingConsole; + + return freeze({ loggingConsole: typedLoggingConsole, takeLog}); + };$h‍_once.makeLoggingConsoleKit(makeLoggingConsoleKit); +freeze(makeLoggingConsoleKit); + + +// ///////////////////////////////////////////////////////////////////////////// + +/** @type {ErrorInfo} */ +const ErrorInfo= { + NOTE: 'ERROR_NOTE:', + MESSAGE: 'ERROR_MESSAGE:'}; + +freeze(ErrorInfo); + +/** @type {MakeCausalConsole} */ +const makeCausalConsole= (baseConsole, loggedErrorHandler)=> { + const { getStackString, tagError, takeMessageLogArgs, takeNoteLogArgsArray}= + loggedErrorHandler; + + /** + * @param {ReadonlyArray} logArgs + * @param {Array} subErrorsSink + * @returns {any} + */ + const extractErrorArgs= (logArgs, subErrorsSink)=> { + const argTags= arrayMap(logArgs, (arg)=>{ + if( isError(arg)) { + arrayPush(subErrorsSink, arg); + return `(${tagError(arg)})`; + } + return arg; + }); + return argTags; + }; + + /** + * @param {LogSeverity} severity + * @param {Error} error + * @param {ErrorInfoKind} kind + * @param {readonly any[]} logArgs + * @param {Array} subErrorsSink + */ + const logErrorInfo= (severity, error, kind, logArgs, subErrorsSink)=> { + const errorTag= tagError(error); + const errorName= + kind=== ErrorInfo.MESSAGE? `${errorTag}:`: `${errorTag} ${kind}`; + const argTags= extractErrorArgs(logArgs, subErrorsSink); + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole[severity](errorName, ...argTags); + }; + + /** + * Logs the `subErrors` within a group name mentioning `optTag`. + * + * @param {LogSeverity} severity + * @param {Error[]} subErrors + * @param {string | undefined} optTag + * @returns {void} + */ + const logSubErrors= (severity, subErrors, optTag= undefined)=> { + if( subErrors.length=== 0) { + return; + } + if( subErrors.length=== 1&& optTag=== undefined) { + // eslint-disable-next-line no-use-before-define + logError(severity, subErrors[0]); + return; + } + let label; + if( subErrors.length=== 1) { + label= `Nested error`; + }else { + label= `Nested ${subErrors.length} errors`; + } + if( optTag!== undefined) { + label= `${label} under ${optTag}`; + } + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole.group(label); + try { + for( const subError of subErrors) { + // eslint-disable-next-line no-use-before-define + logError(severity, subError); + } + }finally { + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole.groupEnd(); + } + }; + + const errorsLogged= new WeakSet(); + + /** @type {(severity: LogSeverity) => NoteCallback} */ + const makeNoteCallback= (severity)=>(error, noteLogArgs)=> { + const subErrors= []; + // Annotation arrived after the error has already been logged, + // so just log the annotation immediately, rather than remembering it. + logErrorInfo(severity, error, ErrorInfo.NOTE, noteLogArgs, subErrors); + logSubErrors(severity, subErrors, tagError(error)); + }; + + /** + * @param {LogSeverity} severity + * @param {Error} error + */ + const logError= (severity, error)=> { + if( weaksetHas(errorsLogged, error)) { + return; + } + const errorTag= tagError(error); + weaksetAdd(errorsLogged, error); + const subErrors= []; + const messageLogArgs= takeMessageLogArgs(error); + const noteLogArgsArray= takeNoteLogArgsArray( + error, + makeNoteCallback(severity)); + + // Show the error's most informative error message + if( messageLogArgs=== undefined) { + // If there is no message log args, then just show the message that + // the error itself carries. + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole[severity]( `${errorTag}:`,error.message); + }else { + // If there is one, we take it to be strictly more informative than the + // message string carried by the error, so show it *instead*. + logErrorInfo( + severity, + error, + ErrorInfo.MESSAGE, + messageLogArgs, + subErrors); + + } + // After the message but before any other annotations, show the stack. + let stackString= getStackString(error); + if( + typeof stackString=== 'string'&& + stackString.length>= 1&& + !stringEndsWith(stackString, '\n')) + { + stackString+= '\n'; + } + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole[severity](stackString); + // Show the other annotations on error + for( const noteLogArgs of noteLogArgsArray) { + logErrorInfo(severity, error, ErrorInfo.NOTE, noteLogArgs, subErrors); + } + // explain all the errors seen in the messages already emitted. + logSubErrors(severity, subErrors, errorTag); + }; + + const levelMethods= arrayMap(consoleLevelMethods, ([level, _])=> { + /** + * @param {...any} logArgs + */ + const levelMethod= (...logArgs)=> { + const subErrors= []; + const argTags= extractErrorArgs(logArgs, subErrors); + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole[level](...argTags); + // @ts-expect-error ConsoleProp vs LogSeverity mismatch + logSubErrors(level, subErrors); + }; + defineProperty(levelMethod, 'name', { value: level}); + return [level, freeze(levelMethod)]; + }); + const otherMethodNames= arrayFilter( + consoleOtherMethods, + ([name, _])=> name in baseConsole); + + const otherMethods= arrayMap(otherMethodNames, ([name, _])=> { + /** + * @param {...any} args + */ + const otherMethod= (...args)=> { + // @ts-ignore + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole[name](...args); + return undefined; + }; + defineProperty(otherMethod, 'name', { value: name}); + return [name, freeze(otherMethod)]; + }); + + const causalConsole= fromEntries([...levelMethods, ...otherMethods]); + return (/** @type {VirtualConsole} */ freeze(causalConsole)); + };$h‍_once.makeCausalConsole(makeCausalConsole); +freeze(makeCausalConsole); + + +// ///////////////////////////////////////////////////////////////////////////// + +/** @type {FilterConsole} */ +const filterConsole= (baseConsole, filter, _topic= undefined)=> { + // TODO do something with optional topic string + const whitelist= arrayFilter( + consoleWhitelist, + ([name, _])=> name in baseConsole); + + const methods= arrayMap(whitelist, ([name, severity])=> { + /** + * @param {...any} args + */ + const method= (...args)=> { + // eslint-disable-next-line @endo/no-polymorphic-call + if( severity=== undefined|| filter.canLog(severity)) { + // @ts-ignore + // eslint-disable-next-line @endo/no-polymorphic-call + baseConsole[name](...args); + } + }; + return [name, freeze(method)]; + }); + const filteringConsole= fromEntries(methods); + return (/** @type {VirtualConsole} */ freeze(filteringConsole)); + };$h‍_once.filterConsole(filterConsole); +freeze(filterConsole); +}) +, +// === functors[34] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FinalizationRegistry,Map,mapGet,mapDelete,WeakMap,mapSet,finalizationRegistryRegister,weakmapSet,weakmapGet,mapEntries,mapHas;$h‍_imports([["../commons.js", [["FinalizationRegistry", [$h‍_a => (FinalizationRegistry = $h‍_a)]],["Map", [$h‍_a => (Map = $h‍_a)]],["mapGet", [$h‍_a => (mapGet = $h‍_a)]],["mapDelete", [$h‍_a => (mapDelete = $h‍_a)]],["WeakMap", [$h‍_a => (WeakMap = $h‍_a)]],["mapSet", [$h‍_a => (mapSet = $h‍_a)]],["finalizationRegistryRegister", [$h‍_a => (finalizationRegistryRegister = $h‍_a)]],["weakmapSet", [$h‍_a => (weakmapSet = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]],["mapEntries", [$h‍_a => (mapEntries = $h‍_a)]],["mapHas", [$h‍_a => (mapHas = $h‍_a)]]]]]); + + + + + + + + + + + + + + +/** + * Create rejection-tracking machinery compatible with Node.js and browsers. + * + * Note that modern browsers *prevent* access to the 'unhandledrejection' and + * 'rejectionhandled' events needed: + * - in cross-origin mode, like when served from file:// + * - in the browser console (interactively typed-in code) + * - in the debugger + * + * Then, they just look like: `Uncaught (in promise) Error: ...` and don't + * implement the machinery. + * + * The solution is to serve your web page from an http:// or https:// web server + * and execute actual code. + * + * @param {(reason: unknown) => void} reportReason report the reason for an + * unhandled rejection. + */ +const makeRejectionHandlers= (reportReason)=>{ + if( FinalizationRegistry=== undefined) { + return undefined; + } + + /** @typedef {number} ReasonId */ + let lastReasonId= 0; + + /** @type {Map} */ + const idToReason= new Map(); + + /** @type {(() => void) | undefined} */ + let cancelChecking; + + const removeReasonId= (reasonId)=>{ + mapDelete(idToReason, reasonId); + if( cancelChecking&& idToReason.size=== 0) { + // No more unhandled rejections to check, just cancel the check. + cancelChecking(); + cancelChecking= undefined; + } + }; + + /** @type {WeakMap} */ + const promiseToReasonId= new WeakMap(); + + /** + * Clean up and report the reason for a GCed unhandled rejection. + * + * @param {ReasonId} heldReasonId + */ + const finalizeDroppedPromise= (heldReasonId)=>{ + if( mapHas(idToReason, heldReasonId)) { + const reason= mapGet(idToReason, heldReasonId); + removeReasonId(heldReasonId); + reportReason(reason); + } + }; + + /** @type {FinalizationRegistry} */ + const promiseToReason= new FinalizationRegistry(finalizeDroppedPromise); + + /** + * Track a rejected promise and its corresponding reason if there is no + * rejection handler synchronously attached. + * + * @param {unknown} reason + * @param {Promise} pr + */ + const unhandledRejectionHandler= (reason, pr)=> { + lastReasonId+= 1; + const reasonId= lastReasonId; + + // Update bookkeeping. + mapSet(idToReason, reasonId, reason); + weakmapSet(promiseToReasonId, pr, reasonId); + finalizationRegistryRegister(promiseToReason, pr, reasonId, pr); + }; + + /** + * Deal with the addition of a handler to a previously rejected promise. + * + * Just remove it from our list. Let the FinalizationRegistry or + * processTermination report any GCed unhandled rejected promises. + * + * @param {Promise} pr + */ + const rejectionHandledHandler= (pr)=>{ + const reasonId= weakmapGet(promiseToReasonId, pr); + removeReasonId(reasonId); + }; + + /** + * Report all the unhandled rejections, now that we are abruptly terminating + * the agent cluster. + */ + const processTerminationHandler= ()=> { + for( const [reasonId, reason]of mapEntries(idToReason)) { + removeReasonId(reasonId); + reportReason(reason); + } + }; + + return { + rejectionHandledHandler, + unhandledRejectionHandler, + processTerminationHandler}; + + };$h‍_once.makeRejectionHandlers(makeRejectionHandlers); +}) +, +// === functors[35] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let TypeError,globalThis,defaultHandler,makeCausalConsole,makeRejectionHandlers;$h‍_imports([["../commons.js", [["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]]]],["./assert.js", [["loggedErrorHandler", [$h‍_a => (defaultHandler = $h‍_a)]]]],["./console.js", [["makeCausalConsole", [$h‍_a => (makeCausalConsole = $h‍_a)]]]],["./unhandled-rejection.js", [["makeRejectionHandlers", [$h‍_a => (makeRejectionHandlers = $h‍_a)]]]],["./types.js", []],["./internal-types.js", []]]); + + + + + + + + +// eslint-disable-next-line no-restricted-globals +const originalConsole= console; + +/** + * Wrap console unless suppressed. + * At the moment, the console is considered a host power in the start + * compartment, and not a primordial. Hence it is absent from the whilelist + * and bypasses the intrinsicsCollector. + * + * @param {"safe" | "unsafe"} consoleTaming + * @param {"platform" | "exit" | "abort" | "report" | "none"} [errorTrapping] + * @param {"report" | "none"} [unhandledRejectionTrapping] + * @param {GetStackString=} optGetStackString + */ +const tameConsole= ( + consoleTaming= 'safe', + errorTrapping= 'platform', + unhandledRejectionTrapping= 'report', + optGetStackString= undefined)=> + { + if( consoleTaming!== 'safe'&& consoleTaming!== 'unsafe') { + throw TypeError( `unrecognized consoleTaming ${consoleTaming}`); + } + + let loggedErrorHandler; + if( optGetStackString=== undefined) { + loggedErrorHandler= defaultHandler; + }else { + loggedErrorHandler= { + ...defaultHandler, + getStackString: optGetStackString}; + + } + const ourConsole= + consoleTaming=== 'unsafe'? + originalConsole: + makeCausalConsole(originalConsole, loggedErrorHandler); + + // Attach platform-specific error traps such that any error that gets thrown + // at top-of-turn (the bottom of stack) will get logged by our causal + // console, revealing the diagnostic information associated with the error, + // including the stack from when the error was created. + + // In the following Node.js and web browser cases, `process` and `window` are + // spelled as `globalThis` properties to avoid the overweaning gaze of + // Parcel, which dutifully installs an unnecessary `process` shim if we ever + // utter that. That unnecessary shim forces the whole bundle into sloppy mode, + // which in turn breaks SES's strict mode invariant. + + // Disable the polymorphic check for the rest of this file. It's too noisy + // when dealing with platform APIs. + /* eslint-disable @endo/no-polymorphic-call */ + + // Node.js + if( errorTrapping!== 'none'&& globalThis.process!== undefined) { + globalThis.process.on('uncaughtException', (error)=>{ + // causalConsole is born frozen so not vulnerable to method tampering. + ourConsole.error(error); + if( errorTrapping=== 'platform'|| errorTrapping=== 'exit') { + globalThis.process.exit(globalThis.process.exitCode|| -1); + }else if( errorTrapping=== 'abort') { + globalThis.process.abort(); + } + }); + } + + if( + unhandledRejectionTrapping!== 'none'&& + globalThis.process!== undefined) + { + const handleRejection= (reason)=>{ + // 'platform' and 'report' just log the reason. + ourConsole.error('SES_UNHANDLED_REJECTION:', reason); + }; + // Maybe track unhandled promise rejections. + const h= makeRejectionHandlers(handleRejection); + if( h) { + // Rejection handlers are supported. + globalThis.process.on('unhandledRejection', h.unhandledRejectionHandler); + globalThis.process.on('rejectionHandled', h.rejectionHandledHandler); + globalThis.process.on('exit', h.processTerminationHandler); + } + } + + // Browser + if( + errorTrapping!== 'none'&& + globalThis.window!== undefined&& + globalThis.window.addEventListener!== undefined) + { + globalThis.window.addEventListener('error', (event)=>{ + event.preventDefault(); + // 'platform' and 'report' just log the reason. + ourConsole.error(event.error); + if( errorTrapping=== 'exit'|| errorTrapping=== 'abort') { + globalThis.window.location.href= `about:blank`; + } + }); + } + + if( + unhandledRejectionTrapping!== 'none'&& + globalThis.window!== undefined&& + globalThis.window.addEventListener!== undefined) + { + const handleRejection= (reason)=>{ + ourConsole.error('SES_UNHANDLED_REJECTION:', reason); + }; + + const h= makeRejectionHandlers(handleRejection); + if( h) { + // Rejection handlers are supported. + globalThis.window.addEventListener('unhandledrejection', (event)=>{ + event.preventDefault(); + h.unhandledRejectionHandler(event.reason, event.promise); + }); + + globalThis.window.addEventListener('rejectionhandled', (event)=>{ + event.preventDefault(); + h.rejectionHandledHandler(event.promise); + }); + + globalThis.window.addEventListener('beforeunload', (_event)=>{ + h.processTerminationHandler(); + }); + } + } + /* eslint-enable @endo/no-polymorphic-call */ + + return { console: ourConsole}; + };$h‍_once.tameConsole(tameConsole); +}) +, +// === functors[36] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let WeakMap,WeakSet,apply,arrayFilter,arrayJoin,arrayMap,arraySlice,create,defineProperties,fromEntries,reflectSet,regexpExec,regexpTest,weakmapGet,weakmapSet,weaksetAdd,weaksetHas;$h‍_imports([["../commons.js", [["WeakMap", [$h‍_a => (WeakMap = $h‍_a)]],["WeakSet", [$h‍_a => (WeakSet = $h‍_a)]],["apply", [$h‍_a => (apply = $h‍_a)]],["arrayFilter", [$h‍_a => (arrayFilter = $h‍_a)]],["arrayJoin", [$h‍_a => (arrayJoin = $h‍_a)]],["arrayMap", [$h‍_a => (arrayMap = $h‍_a)]],["arraySlice", [$h‍_a => (arraySlice = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["fromEntries", [$h‍_a => (fromEntries = $h‍_a)]],["reflectSet", [$h‍_a => (reflectSet = $h‍_a)]],["regexpExec", [$h‍_a => (regexpExec = $h‍_a)]],["regexpTest", [$h‍_a => (regexpTest = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]],["weakmapSet", [$h‍_a => (weakmapSet = $h‍_a)]],["weaksetAdd", [$h‍_a => (weaksetAdd = $h‍_a)]],["weaksetHas", [$h‍_a => (weaksetHas = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + +// Whitelist names from https://v8.dev/docs/stack-trace-api +// Whitelisting only the names used by error-stack-shim/src/v8StackFrames +// callSiteToFrame to shim the error stack proposal. +const safeV8CallSiteMethodNames= [ + // suppress 'getThis' definitely + 'getTypeName', + // suppress 'getFunction' definitely + 'getFunctionName', + 'getMethodName', + 'getFileName', + 'getLineNumber', + 'getColumnNumber', + 'getEvalOrigin', + 'isToplevel', + 'isEval', + 'isNative', + 'isConstructor', + 'isAsync', + // suppress 'isPromiseAll' for now + // suppress 'getPromiseIndex' for now + + // Additional names found by experiment, absent from + // https://v8.dev/docs/stack-trace-api + 'getPosition', + 'getScriptNameOrSourceURL', + + 'toString' // TODO replace to use only whitelisted info +]; + +// TODO this is a ridiculously expensive way to attenuate callsites. +// Before that matters, we should switch to a reasonable representation. +const safeV8CallSiteFacet= (callSite)=>{ + const methodEntry= (name)=>{ + const method= callSite[name]; + return [name, ()=> apply(method, callSite, [])]; + }; + const o= fromEntries(arrayMap(safeV8CallSiteMethodNames, methodEntry)); + return create(o, {}); + }; + +const safeV8SST= (sst)=>arrayMap(sst, safeV8CallSiteFacet); + +// If it has `/node_modules/` anywhere in it, on Node it is likely +// to be a dependent package of the current package, and so to +// be an infrastructure frame to be dropped from concise stack traces. +const FILENAME_NODE_DEPENDENTS_CENSOR= /\/node_modules\//; + +// If it begins with `internal/` or `node:internal` then it is likely +// part of the node infrustructre itself, to be dropped from concise +// stack traces. +const FILENAME_NODE_INTERNALS_CENSOR= /^(?:node:)?internal\//; + +// Frames within the `assert.js` package should be dropped from +// concise stack traces, as these are just steps towards creating the +// error object in question. +const FILENAME_ASSERT_CENSOR= /\/packages\/ses\/src\/error\/assert.js$/; + +// Frames within the `eventual-send` shim should be dropped so that concise +// deep stacks omit the internals of the eventual-sending mechanism causing +// asynchronous messages to be sent. +// Note that the eventual-send package will move from agoric-sdk to +// Endo, so this rule will be of general interest. +const FILENAME_EVENTUAL_SEND_CENSOR= /\/packages\/eventual-send\/src\//; + +// Any stack frame whose `fileName` matches any of these censor patterns +// will be omitted from concise stacks. +// TODO Enable users to configure FILENAME_CENSORS via `lockdown` options. +const FILENAME_CENSORS= [ + FILENAME_NODE_DEPENDENTS_CENSOR, + FILENAME_NODE_INTERNALS_CENSOR, + FILENAME_ASSERT_CENSOR, + FILENAME_EVENTUAL_SEND_CENSOR]; + + +// Should a stack frame with this as its fileName be included in a concise +// stack trace? +// Exported only so it can be unit tested. +// TODO Move so that it applies not just to v8. +const filterFileName= (fileName)=>{ + if( !fileName) { + // Stack frames with no fileName should appear in concise stack traces. + return true; + } + for( const filter of FILENAME_CENSORS) { + if( regexpTest(filter, fileName)) { + return false; + } + } + return true; + }; + +// The ad-hoc rule of the current pattern is that any likely-file-path or +// likely url-path prefix, ending in a `/.../` should get dropped. +// Anything to the left of the likely path text is kept. +// Everything to the right of `/.../` is kept. Thus +// `'Object.bar (/vat-v1/.../eventual-send/test/test-deep-send.js:13:21)'` +// simplifies to +// `'Object.bar (eventual-send/test/test-deep-send.js:13:21)'`. +// +// See thread starting at +// https://github.com/Agoric/agoric-sdk/issues/2326#issuecomment-773020389 +$h‍_once.filterFileName(filterFileName);const CALLSITE_ELLIPSES_PATTERN=/^((?:.*[( ])?)[:/\w_-]*\/\.\.\.\/(.+)$/; + +// The ad-hoc rule of the current pattern is that any likely-file-path or +// likely url-path prefix, ending in a `/` and prior to `package/` should get +// dropped. +// Anything to the left of the likely path prefix text is kept. `package/` and +// everything to its right is kept. Thus +// `'Object.bar (/Users/markmiller/src/ongithub/agoric/agoric-sdk/packages/eventual-send/test/test-deep-send.js:13:21)'` +// simplifies to +// `'Object.bar (packages/eventual-send/test/test-deep-send.js:13:21)'`. +// Note that `/packages/` is a convention for monorepos encouraged by +// lerna. +const CALLSITE_PACKAGES_PATTERN= /^((?:.*[( ])?)[:/\w_-]*\/(packages\/.+)$/; + +// The use of these callSite patterns below assumes that any match will bind +// capture groups containing the parts of the original string we want +// to keep. The parts outside those capture groups will be dropped from concise +// stacks. +// TODO Enable users to configure CALLSITE_PATTERNS via `lockdown` options. +const CALLSITE_PATTERNS= [ + CALLSITE_ELLIPSES_PATTERN, + CALLSITE_PACKAGES_PATTERN]; + + +// For a stack frame that should be included in a concise stack trace, if +// `callSiteString` is the original stringified stack frame, return the +// possibly-shorter stringified stack frame that should be shown instead. +// Exported only so it can be unit tested. +// TODO Move so that it applies not just to v8. +const shortenCallSiteString= (callSiteString)=>{ + for( const filter of CALLSITE_PATTERNS) { + const match= regexpExec(filter, callSiteString); + if( match) { + return arrayJoin(arraySlice(match, 1), ''); + } + } + return callSiteString; + };$h‍_once.shortenCallSiteString(shortenCallSiteString); + +const tameV8ErrorConstructor= ( + OriginalError, + InitialError, + errorTaming, + stackFiltering)=> + { + // TODO: Proper CallSite types + /** @typedef {{}} CallSite */ + + const originalCaptureStackTrace= OriginalError.captureStackTrace; + + // const callSiteFilter = _callSite => true; + const callSiteFilter= (callSite)=>{ + if( stackFiltering=== 'verbose') { + return true; + } + // eslint-disable-next-line @endo/no-polymorphic-call + return filterFileName(callSite.getFileName()); + }; + + const callSiteStringifier= (callSite)=>{ + let callSiteString= `${callSite}`; + if( stackFiltering=== 'concise') { + callSiteString= shortenCallSiteString(callSiteString); + } + return `\n at ${callSiteString}`; + }; + + const stackStringFromSST= (_error, sst)=> + arrayJoin( + arrayMap(arrayFilter(sst, callSiteFilter), callSiteStringifier), + ''); + + + /** + * @typedef {object} StructuredStackInfo + * @property {CallSite[]} callSites + * @property {undefined} [stackString] + */ + + /** + * @typedef {object} ParsedStackInfo + * @property {undefined} [callSites] + * @property {string} stackString + */ + + // Mapping from error instance to the stack for that instance. + // The stack info is either the structured stack trace + // or the generated tamed stack string + /** @type {WeakMap} */ + const stackInfos= new WeakMap(); + + // Use concise methods to obtain named functions without constructors. + const tamedMethods= { + // The optional `optFn` argument is for cutting off the bottom of + // the stack --- for capturing the stack only above the topmost + // call to that function. Since this isn't the "real" captureStackTrace + // but instead calls the real one, if no other cutoff is provided, + // we cut this one off. + captureStackTrace(error, optFn= tamedMethods.captureStackTrace) { + if( typeof originalCaptureStackTrace=== 'function') { + // OriginalError.captureStackTrace is only on v8 + apply(originalCaptureStackTrace, OriginalError, [error, optFn]); + return; + } + reflectSet(error, 'stack', ''); + }, + // Shim of proposed special power, to reside by default only + // in the start compartment, for getting the stack traceback + // string associated with an error. + // See https://tc39.es/proposal-error-stacks/ + getStackString(error) { + let stackInfo= weakmapGet(stackInfos, error); + + if( stackInfo=== undefined) { + // The following will call `prepareStackTrace()` synchronously + // which will populate stackInfos + // eslint-disable-next-line no-void + void error.stack; + stackInfo= weakmapGet(stackInfos, error); + if( !stackInfo) { + stackInfo= { stackString: ''}; + weakmapSet(stackInfos, error, stackInfo); + } + } + + // prepareStackTrace() may generate the stackString + // if errorTaming === 'unsafe' + + if( stackInfo.stackString!== undefined) { + return stackInfo.stackString; + } + + const stackString= stackStringFromSST(error, stackInfo.callSites); + weakmapSet(stackInfos, error, { stackString}); + + return stackString; + }, + prepareStackTrace(error, sst) { + if( errorTaming=== 'unsafe') { + const stackString= stackStringFromSST(error, sst); + weakmapSet(stackInfos, error, { stackString}); + return `${error}${stackString}`; + }else { + weakmapSet(stackInfos, error, { callSites: sst}); + return ''; + } + }}; + + + // A prepareFn is a prepareStackTrace function. + // An sst is a `structuredStackTrace`, which is an array of + // callsites. + // A user prepareFn is a prepareFn defined by a client of this API, + // and provided by assigning to `Error.prepareStackTrace`. + // A user prepareFn should only receive an attenuated sst, which + // is an array of attenuated callsites. + // A system prepareFn is the prepareFn created by this module to + // be installed on the real `Error` constructor, to receive + // an original sst, i.e., an array of unattenuated callsites. + // An input prepareFn is a function the user assigns to + // `Error.prepareStackTrace`, which might be a user prepareFn or + // a system prepareFn previously obtained by reading + // `Error.prepareStackTrace`. + + const defaultPrepareFn= tamedMethods.prepareStackTrace; + + OriginalError.prepareStackTrace= defaultPrepareFn; + + // A weakset branding some functions as system prepareFns, all of which + // must be defined by this module, since they can receive an + // unattenuated sst. + const systemPrepareFnSet= new WeakSet([defaultPrepareFn]); + + const systemPrepareFnFor= (inputPrepareFn)=>{ + if( weaksetHas(systemPrepareFnSet, inputPrepareFn)) { + return inputPrepareFn; + } + // Use concise methods to obtain named functions without constructors. + const systemMethods= { + prepareStackTrace(error, sst) { + weakmapSet(stackInfos, error, { callSites: sst}); + return inputPrepareFn(error, safeV8SST(sst)); + }}; + + weaksetAdd(systemPrepareFnSet, systemMethods.prepareStackTrace); + return systemMethods.prepareStackTrace; + }; + + // Note `stackTraceLimit` accessor already defined by + // tame-error-constructor.js + defineProperties(InitialError, { + captureStackTrace: { + value: tamedMethods.captureStackTrace, + writable: true, + enumerable: false, + configurable: true}, + + prepareStackTrace: { + get() { + return OriginalError.prepareStackTrace; + }, + set(inputPrepareStackTraceFn) { + if( typeof inputPrepareStackTraceFn=== 'function') { + const systemPrepareFn= systemPrepareFnFor(inputPrepareStackTraceFn); + OriginalError.prepareStackTrace= systemPrepareFn; + }else { + OriginalError.prepareStackTrace= defaultPrepareFn; + } + }, + enumerable: false, + configurable: true}}); + + + + return tamedMethods.getStackString; + };$h‍_once.tameV8ErrorConstructor(tameV8ErrorConstructor); +}) +, +// === functors[37] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_ERROR,TypeError,apply,construct,defineProperties,setPrototypeOf,getOwnPropertyDescriptor,defineProperty,NativeErrors,tameV8ErrorConstructor;$h‍_imports([["../commons.js", [["FERAL_ERROR", [$h‍_a => (FERAL_ERROR = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["apply", [$h‍_a => (apply = $h‍_a)]],["construct", [$h‍_a => (construct = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["setPrototypeOf", [$h‍_a => (setPrototypeOf = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]]]],["../permits.js", [["NativeErrors", [$h‍_a => (NativeErrors = $h‍_a)]]]],["./tame-v8-error-constructor.js", [["tameV8ErrorConstructor", [$h‍_a => (tameV8ErrorConstructor = $h‍_a)]]]]]); + + + + + + + + + + + + +// Present on at least FF and XS. Proposed by Error-proposal. The original +// is dangerous, so tameErrorConstructor replaces it with a safe one. +// We grab the original here before it gets replaced. +const stackDesc= getOwnPropertyDescriptor(FERAL_ERROR.prototype, 'stack'); +const stackGetter= stackDesc&& stackDesc.get; + +// Use concise methods to obtain named functions without constructors. +const tamedMethods= { + getStackString(error) { + if( typeof stackGetter=== 'function') { + return apply(stackGetter, error, []); + }else if( 'stack'in error) { + // The fallback is to just use the de facto `error.stack` if present + return `${error.stack}`; + } + return ''; + }}; + + +function tameErrorConstructor( + errorTaming= 'safe', + stackFiltering= 'concise') + { + if( errorTaming!== 'safe'&& errorTaming!== 'unsafe') { + throw TypeError( `unrecognized errorTaming ${errorTaming}`); + } + if( stackFiltering!== 'concise'&& stackFiltering!== 'verbose') { + throw TypeError( `unrecognized stackFiltering ${stackFiltering}`); + } + const ErrorPrototype= FERAL_ERROR.prototype; + + const platform= + typeof FERAL_ERROR.captureStackTrace=== 'function'? 'v8': 'unknown'; + const { captureStackTrace: originalCaptureStackTrace}= FERAL_ERROR; + + const makeErrorConstructor= (_= {})=> { + // eslint-disable-next-line no-shadow + const ResultError= function Error(...rest) { + let error; + if( new.target=== undefined) { + error= apply(FERAL_ERROR, this, rest); + }else { + error= construct(FERAL_ERROR, rest, new.target); + } + if( platform=== 'v8') { + // TODO Likely expensive! + apply(originalCaptureStackTrace, FERAL_ERROR, [error, ResultError]); + } + return error; + }; + defineProperties(ResultError, { + length: { value: 1}, + prototype: { + value: ErrorPrototype, + writable: false, + enumerable: false, + configurable: false}}); + + + return ResultError; + }; + const InitialError= makeErrorConstructor({ powers: 'original'}); + const SharedError= makeErrorConstructor({ powers: 'none'}); + defineProperties(ErrorPrototype, { + constructor: { value: SharedError}}); + + + for( const NativeError of NativeErrors) { + setPrototypeOf(NativeError, SharedError); + } + + // https://v8.dev/docs/stack-trace-api#compatibility advises that + // programmers can "always" set `Error.stackTraceLimit` + // even on non-v8 platforms. On non-v8 + // it will have no effect, but this advice only makes sense + // if the assignment itself does not fail, which it would + // if `Error` were naively frozen. Hence, we add setters that + // accept but ignore the assignment on non-v8 platforms. + defineProperties(InitialError, { + stackTraceLimit: { + get() { + if( typeof FERAL_ERROR.stackTraceLimit=== 'number') { + // FERAL_ERROR.stackTraceLimit is only on v8 + return FERAL_ERROR.stackTraceLimit; + } + return undefined; + }, + set(newLimit) { + if( typeof newLimit!== 'number') { + // silently do nothing. This behavior doesn't precisely + // emulate v8 edge-case behavior. But given the purpose + // of this emulation, having edge cases err towards + // harmless seems the safer option. + return; + } + if( typeof FERAL_ERROR.stackTraceLimit=== 'number') { + // FERAL_ERROR.stackTraceLimit is only on v8 + FERAL_ERROR.stackTraceLimit= newLimit; + // We place the useless return on the next line to ensure + // that anything we place after the if in the future only + // happens if the then-case does not. + // eslint-disable-next-line no-useless-return + return; + } + }, + // WTF on v8 stackTraceLimit is enumerable + enumerable: false, + configurable: true}}); + + + + // The default SharedError much be completely powerless even on v8, + // so the lenient `stackTraceLimit` accessor does nothing on all + // platforms. + defineProperties(SharedError, { + stackTraceLimit: { + get() { + return undefined; + }, + set(_newLimit) { + // do nothing + }, + enumerable: false, + configurable: true}}); + + + + if( platform=== 'v8') { + // `SharedError.prepareStackTrace`, if it exists, must also be + // powerless. However, from what we've heard, depd expects to be able to + // assign to it without the assignment throwing. It is normally a function + // that returns a stack string to be magically added to error objects. + // However, as long as we're adding a lenient standin, we may as well + // accommodate any who expect to get a function they can call and get + // a string back. This prepareStackTrace is a do-nothing function that + // always returns the empty string. + defineProperties(SharedError, { + prepareStackTrace: { + get() { + return ()=> ''; + }, + set(_prepareFn) { + // do nothing + }, + enumerable: false, + configurable: true}, + + captureStackTrace: { + value: (errorish, _constructorOpt)=> { + defineProperty(errorish, 'stack', { + value: ''}); + + }, + writable: false, + enumerable: false, + configurable: true}}); + + + } + + let initialGetStackString= tamedMethods.getStackString; + if( platform=== 'v8') { + initialGetStackString= tameV8ErrorConstructor( + FERAL_ERROR, + InitialError, + errorTaming, + stackFiltering); + + }else if( errorTaming=== 'unsafe') { + // v8 has too much magic around their 'stack' own property for it to + // coexist cleanly with this accessor. So only install it on non-v8 + + // Error.prototype.stack property as proposed at + // https://tc39.es/proposal-error-stacks/ + // with the fix proposed at + // https://github.com/tc39/proposal-error-stacks/issues/46 + // On others, this still protects from the override mistake, + // essentially like enable-property-overrides.js would + // once this accessor property itself is frozen, as will happen + // later during lockdown. + // + // However, there is here a change from the intent in the current + // state of the proposal. If experience tells us whether this change + // is a good idea, we should modify the proposal accordingly. There is + // much code in the world that assumes `error.stack` is a string. So + // where the proposal accommodates secure operation by making the + // property optional, we instead accommodate secure operation by + // having the secure form always return only the stable part, the + // stringified error instance, and omitting all the frame information + // rather than omitting the property. + defineProperties(ErrorPrototype, { + stack: { + get() { + return initialGetStackString(this); + }, + set(newValue) { + defineProperties(this, { + stack: { + value: newValue, + writable: true, + enumerable: true, + configurable: true}}); + + + }}}); + + + }else { + // v8 has too much magic around their 'stack' own property for it to + // coexist cleanly with this accessor. So only install it on non-v8 + defineProperties(ErrorPrototype, { + stack: { + get() { + // https://github.com/tc39/proposal-error-stacks/issues/46 + // allows this to not add an unpleasant newline. Otherwise + // we should fix this. + return `${this}`; + }, + set(newValue) { + defineProperties(this, { + stack: { + value: newValue, + writable: true, + enumerable: true, + configurable: true}}); + + + }}}); + + + } + + return { + '%InitialGetStackString%': initialGetStackString, + '%InitialError%': InitialError, + '%SharedError%': SharedError}; + + }$h‍_once.default( tameErrorConstructor); +}) +, +// === functors[38] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let ReferenceError,TypeError,Map,Set,arrayJoin,arrayMap,arrayPush,create,freeze,mapGet,mapHas,mapSet,setAdd,promiseCatch,promiseThen,values,weakmapGet,assert;$h‍_imports([["./commons.js", [["ReferenceError", [$h‍_a => (ReferenceError = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["Map", [$h‍_a => (Map = $h‍_a)]],["Set", [$h‍_a => (Set = $h‍_a)]],["arrayJoin", [$h‍_a => (arrayJoin = $h‍_a)]],["arrayMap", [$h‍_a => (arrayMap = $h‍_a)]],["arrayPush", [$h‍_a => (arrayPush = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["mapGet", [$h‍_a => (mapGet = $h‍_a)]],["mapHas", [$h‍_a => (mapHas = $h‍_a)]],["mapSet", [$h‍_a => (mapSet = $h‍_a)]],["setAdd", [$h‍_a => (setAdd = $h‍_a)]],["promiseCatch", [$h‍_a => (promiseCatch = $h‍_a)]],["promiseThen", [$h‍_a => (promiseThen = $h‍_a)]],["values", [$h‍_a => (values = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + +const { Fail, details: d, quote: q}= assert; + +const noop= ()=> { }; + +// `makeAlias` constructs compartment specifier tuples for the `aliases` +// private field of compartments. +// These aliases allow a compartment to alias an internal module specifier to a +// module specifier in an external compartment, and also to create internal +// aliases. +// Both are facilitated by the moduleMap Compartment constructor option. +const makeAlias= (compartment, specifier)=> + freeze({ + compartment, + specifier}); + + +// `resolveAll` pre-computes resolutions of all imports within the compartment +// in which a module was loaded. +$h‍_once.makeAlias(makeAlias);const resolveAll=(imports,resolveHook,fullReferrerSpecifier)=>{ + const resolvedImports= create(null); + for( const importSpecifier of imports) { + const fullSpecifier= resolveHook(importSpecifier, fullReferrerSpecifier); + resolvedImports[importSpecifier]= fullSpecifier; + } + return freeze(resolvedImports); + }; + +const loadRecord= ( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier, + staticModuleRecord, + pendingJobs, + moduleLoads, + errors, + importMeta)=> + { + const { resolveHook, moduleRecords}= weakmapGet( + compartmentPrivateFields, + compartment); + + + // resolve all imports relative to this referrer module. + const resolvedImports= resolveAll( + staticModuleRecord.imports, + resolveHook, + moduleSpecifier); + + const moduleRecord= freeze({ + compartment, + staticModuleRecord, + moduleSpecifier, + resolvedImports, + importMeta}); + + + // Enqueue jobs to load this module's shallow dependencies. + for( const fullSpecifier of values(resolvedImports)) { + // Behold: recursion. + // eslint-disable-next-line no-use-before-define + const dependencyLoaded= memoizedLoadWithErrorAnnotation( + compartmentPrivateFields, + moduleAliases, + compartment, + fullSpecifier, + pendingJobs, + moduleLoads, + errors); + + setAdd( + pendingJobs, + promiseThen(dependencyLoaded, noop, (error)=>{ + arrayPush(errors, error); + })); + + } + + // Memoize. + mapSet(moduleRecords, moduleSpecifier, moduleRecord); + return moduleRecord; + }; + +const loadWithoutErrorAnnotation= async( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier, + pendingJobs, + moduleLoads, + errors)=> + { + const { importHook, moduleMap, moduleMapHook, moduleRecords}= weakmapGet( + compartmentPrivateFields, + compartment); + + + // Follow moduleMap, or moduleMapHook if present. + let aliasNamespace= moduleMap[moduleSpecifier]; + if( aliasNamespace=== undefined&& moduleMapHook!== undefined) { + aliasNamespace= moduleMapHook(moduleSpecifier); + } + if( typeof aliasNamespace=== 'string') { + // eslint-disable-next-line @endo/no-polymorphic-call + assert.fail( + d `Cannot map module ${q(moduleSpecifier)} to ${q( + aliasNamespace) + } in parent compartment, not yet implemented`, + TypeError); + + }else if( aliasNamespace!== undefined) { + const alias= weakmapGet(moduleAliases, aliasNamespace); + if( alias=== undefined) { + // eslint-disable-next-line @endo/no-polymorphic-call + assert.fail( + d `Cannot map module ${q( + moduleSpecifier) + } because the value is not a module exports namespace, or is from another realm`, + ReferenceError); + + } + // Behold: recursion. + // eslint-disable-next-line no-use-before-define + const aliasRecord= await memoizedLoadWithErrorAnnotation( + compartmentPrivateFields, + moduleAliases, + alias.compartment, + alias.specifier, + pendingJobs, + moduleLoads, + errors); + + mapSet(moduleRecords, moduleSpecifier, aliasRecord); + return aliasRecord; + } + + if( mapHas(moduleRecords, moduleSpecifier)) { + return mapGet(moduleRecords, moduleSpecifier); + } + + const staticModuleRecord= await importHook(moduleSpecifier); + + if( staticModuleRecord=== null|| typeof staticModuleRecord!== 'object') { + Fail `importHook must return a promise for an object, for module ${q( + moduleSpecifier) + } in compartment ${q(compartment.name)}`; + } + + // check if record is a RedirectStaticModuleInterface + if( staticModuleRecord.specifier!== undefined) { + // check if this redirect with an explicit record + if( staticModuleRecord.record!== undefined) { + // ensure expected record shape + if( staticModuleRecord.compartment!== undefined) { + throw TypeError( + 'Cannot redirect to an explicit record with a specified compartment'); + + } + const { + compartment: aliasCompartment= compartment, + specifier: aliasSpecifier= moduleSpecifier, + record: aliasModuleRecord, + importMeta}= + staticModuleRecord; + + const aliasRecord= loadRecord( + compartmentPrivateFields, + moduleAliases, + aliasCompartment, + aliasSpecifier, + aliasModuleRecord, + pendingJobs, + moduleLoads, + errors, + importMeta); + + mapSet(moduleRecords, moduleSpecifier, aliasRecord); + return aliasRecord; + } + + // check if this redirect with an explicit compartment + if( staticModuleRecord.compartment!== undefined) { + // ensure expected record shape + if( staticModuleRecord.importMeta!== undefined) { + throw TypeError( + 'Cannot redirect to an implicit record with a specified importMeta'); + + } + // Behold: recursion. + // eslint-disable-next-line no-use-before-define + const aliasRecord= await memoizedLoadWithErrorAnnotation( + compartmentPrivateFields, + moduleAliases, + staticModuleRecord.compartment, + staticModuleRecord.specifier, + pendingJobs, + moduleLoads, + errors); + + mapSet(moduleRecords, moduleSpecifier, aliasRecord); + return aliasRecord; + } + + throw TypeError('Unnexpected RedirectStaticModuleInterface record shape'); + } + + return loadRecord( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier, + staticModuleRecord, + pendingJobs, + moduleLoads, + errors); + + }; + +const memoizedLoadWithErrorAnnotation= async( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier, + pendingJobs, + moduleLoads, + errors)=> + { + const { name: compartmentName}= weakmapGet( + compartmentPrivateFields, + compartment); + + + // Prevent data-lock from recursion into branches visited in dependent loads. + let compartmentLoading= mapGet(moduleLoads, compartment); + if( compartmentLoading=== undefined) { + compartmentLoading= new Map(); + mapSet(moduleLoads, compartment, compartmentLoading); + } + let moduleLoading= mapGet(compartmentLoading, moduleSpecifier); + if( moduleLoading!== undefined) { + return moduleLoading; + } + + moduleLoading= promiseCatch( + loadWithoutErrorAnnotation( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier, + pendingJobs, + moduleLoads, + errors), + + (error)=>{ + // eslint-disable-next-line @endo/no-polymorphic-call + assert.note( + error, + d `${error.message}, loading ${q(moduleSpecifier)} in compartment ${q( + compartmentName) + }`); + + throw error; + }); + + + mapSet(compartmentLoading, moduleSpecifier, moduleLoading); + + return moduleLoading; + }; + +/* + * `load` asynchronously gathers the `StaticModuleRecord`s for a module and its + * transitive dependencies. + * The module records refer to each other by a reference to the dependency's + * compartment and the specifier of the module within its own compartment. + * This graph is then ready to be synchronously linked and executed. + */ +const load= async( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier)=> + { + const { name: compartmentName}= weakmapGet( + compartmentPrivateFields, + compartment); + + + /** @type {Set>} */ + const pendingJobs= new Set(); + /** @type {Map>>>} */ + const moduleLoads= new Map(); + /** @type {Array} */ + const errors= []; + + const dependencyLoaded= memoizedLoadWithErrorAnnotation( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier, + pendingJobs, + moduleLoads, + errors); + + setAdd( + pendingJobs, + promiseThen(dependencyLoaded, noop, (error)=>{ + arrayPush(errors, error); + })); + + + // Drain pending jobs queue. + // Each job is a promise for undefined, regardless of success or failure. + // Before we add a job to the queue, we catch any error and push it into the + // `errors` accumulator. + for( const job of pendingJobs) { + // eslint-disable-next-line no-await-in-loop + await job; + } + + // Throw an aggregate error if there were any errors. + if( errors.length> 0) { + throw TypeError( + `Failed to load module ${q(moduleSpecifier)} in package ${q( + compartmentName) + } (${errors.length} underlying failures: ${arrayJoin( + arrayMap(errors, (error)=>error.message), + ', ') + }`); + + } + };$h‍_once.load(load); +}) +, +// === functors[39] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let makeAlias,Proxy,TypeError,create,freeze,mapGet,mapHas,mapSet,ownKeys,reflectGet,reflectGetOwnPropertyDescriptor,reflectHas,reflectIsExtensible,reflectPreventExtensions,weakmapSet,assert;$h‍_imports([["./module-load.js", [["makeAlias", [$h‍_a => (makeAlias = $h‍_a)]]]],["./commons.js", [["Proxy", [$h‍_a => (Proxy = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["mapGet", [$h‍_a => (mapGet = $h‍_a)]],["mapHas", [$h‍_a => (mapHas = $h‍_a)]],["mapSet", [$h‍_a => (mapSet = $h‍_a)]],["ownKeys", [$h‍_a => (ownKeys = $h‍_a)]],["reflectGet", [$h‍_a => (reflectGet = $h‍_a)]],["reflectGetOwnPropertyDescriptor", [$h‍_a => (reflectGetOwnPropertyDescriptor = $h‍_a)]],["reflectHas", [$h‍_a => (reflectHas = $h‍_a)]],["reflectIsExtensible", [$h‍_a => (reflectIsExtensible = $h‍_a)]],["reflectPreventExtensions", [$h‍_a => (reflectPreventExtensions = $h‍_a)]],["weakmapSet", [$h‍_a => (weakmapSet = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +const { quote: q}= assert; + +// `deferExports` creates a module's exports proxy, proxied exports, and +// activator. +// A `Compartment` can create a module for any module specifier, regardless of +// whether it is loadable or executable, and use that object as a token that +// can be fed into another compartment's module map. +// Only after the specified module has been analyzed is it possible for the +// module namespace proxy to behave properly, so it throws exceptions until +// after the compartment has begun executing the module. +// The module instance must freeze the proxied exports and activate the exports +// proxy before executing the module. +// +// The module exports proxy's behavior differs from the ECMAScript 262 +// specification for "module namespace exotic objects" only in that according +// to the specification value property descriptors have a non-writable "value" +// and this implementation models all properties with accessors. +// +// https://tc39.es/ecma262/#sec-module-namespace-exotic-objects +// +const deferExports= ()=> { + let active= false; + const proxiedExports= create(null); + return freeze({ + activate() { + active= true; + }, + proxiedExports, + exportsProxy: new Proxy(proxiedExports, { + get(_target, name, receiver) { + if( !active) { + throw TypeError( + `Cannot get property ${q( + name) + } of module exports namespace, the module has not yet begun to execute`); + + } + return reflectGet(proxiedExports, name, receiver); + }, + set(_target, name, _value) { + throw TypeError( + `Cannot set property ${q(name)} of module exports namespace`); + + }, + has(_target, name) { + if( !active) { + throw TypeError( + `Cannot check property ${q( + name) + }, the module has not yet begun to execute`); + + } + return reflectHas(proxiedExports, name); + }, + deleteProperty(_target, name) { + throw TypeError( + `Cannot delete property ${q(name)}s of module exports namespace`); + + }, + ownKeys(_target) { + if( !active) { + throw TypeError( + 'Cannot enumerate keys, the module has not yet begun to execute'); + + } + return ownKeys(proxiedExports); + }, + getOwnPropertyDescriptor(_target, name) { + if( !active) { + throw TypeError( + `Cannot get own property descriptor ${q( + name) + }, the module has not yet begun to execute`); + + } + return reflectGetOwnPropertyDescriptor(proxiedExports, name); + }, + preventExtensions(_target) { + if( !active) { + throw TypeError( + 'Cannot prevent extensions of module exports namespace, the module has not yet begun to execute'); + + } + return reflectPreventExtensions(proxiedExports); + }, + isExtensible() { + if( !active) { + throw TypeError( + 'Cannot check extensibility of module exports namespace, the module has not yet begun to execute'); + + } + return reflectIsExtensible(proxiedExports); + }, + getPrototypeOf(_target) { + return null; + }, + setPrototypeOf(_target, _proto) { + throw TypeError('Cannot set prototype of module exports namespace'); + }, + defineProperty(_target, name, _descriptor) { + throw TypeError( + `Cannot define property ${q(name)} of module exports namespace`); + + }, + apply(_target, _thisArg, _args) { + throw TypeError( + 'Cannot call module exports namespace, it is not a function'); + + }, + construct(_target, _args) { + throw TypeError( + 'Cannot construct module exports namespace, it is not a constructor'); + + }})}); + + + }; + +// `getDeferredExports` memoizes the creation of a deferred module exports +// namespace proxy for any abritrary full specifier in a compartment. +// It also records the compartment and specifier affiliated with that module +// exports namespace proxy so it can be used as an alias into another +// compartment when threaded through a compartment's `moduleMap` argument. +$h‍_once.deferExports(deferExports);const getDeferredExports=( + compartment, + compartmentPrivateFields, + moduleAliases, + specifier)=> + { + const { deferredExports}= compartmentPrivateFields; + if( !mapHas(deferredExports, specifier)) { + const deferred= deferExports(); + weakmapSet( + moduleAliases, + deferred.exportsProxy, + makeAlias(compartment, specifier)); + + mapSet(deferredExports, specifier, deferred); + } + return mapGet(deferredExports, specifier); + };$h‍_once.getDeferredExports(getDeferredExports); +}) +, +// === functors[40] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let TypeError,arrayPush,create,getOwnPropertyDescriptors,evadeHtmlCommentTest,evadeImportExpressionTest,rejectSomeDirectEvalExpressions,makeSafeEvaluator;$h‍_imports([["./commons.js", [["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["arrayPush", [$h‍_a => (arrayPush = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]]]],["./transforms.js", [["evadeHtmlCommentTest", [$h‍_a => (evadeHtmlCommentTest = $h‍_a)]],["evadeImportExpressionTest", [$h‍_a => (evadeImportExpressionTest = $h‍_a)]],["rejectSomeDirectEvalExpressions", [$h‍_a => (rejectSomeDirectEvalExpressions = $h‍_a)]]]],["./make-safe-evaluator.js", [["makeSafeEvaluator", [$h‍_a => (makeSafeEvaluator = $h‍_a)]]]]]); + + + + + + + + + + + + + +const provideCompartmentEvaluator= (compartmentFields, options)=> { + const { sloppyGlobalsMode= false, __moduleShimLexicals__= undefined}= + options; + + let safeEvaluate; + + if( __moduleShimLexicals__=== undefined&& !sloppyGlobalsMode) { + ({ safeEvaluate}= compartmentFields); + }else { + // The scope proxy or global lexicals are different from the + // shared evaluator so we need to build a new one + + let { globalTransforms}= compartmentFields; + const { globalObject}= compartmentFields; + + let moduleLexicals; + if( __moduleShimLexicals__!== undefined) { + // When using `evaluate` for ESM modules, as should only occur from the + // module-shim's module-instance.js, we do not reveal the SES-shim's + // module-to-program translation, as this is not standardizable behavior. + // However, the `localTransforms` will come from the `__shimTransforms__` + // Compartment option in this case, which is a non-standardizable escape + // hatch so programs designed specifically for the SES-shim + // implementation may opt-in to use the same transforms for `evaluate` + // and `import`, at the expense of being tightly coupled to SES-shim. + globalTransforms= undefined; + + moduleLexicals= create( + null, + getOwnPropertyDescriptors(__moduleShimLexicals__)); + + } + + ({ safeEvaluate}= makeSafeEvaluator({ + globalObject, + moduleLexicals, + globalTransforms, + sloppyGlobalsMode})); + + } + + return { safeEvaluate}; + };$h‍_once.provideCompartmentEvaluator(provideCompartmentEvaluator); + +const compartmentEvaluate= (compartmentFields, source, options)=> { + // Perform this check first to avoid unnecessary sanitizing. + // TODO Maybe relax string check and coerce instead: + // https://github.com/tc39/proposal-dynamic-code-brand-checks + if( typeof source!== 'string') { + throw TypeError('first argument of evaluate() must be a string'); + } + + // Extract options, and shallow-clone transforms. + const { + transforms= [], + __evadeHtmlCommentTest__= false, + __evadeImportExpressionTest__= false, + __rejectSomeDirectEvalExpressions__= true // Note default on +}= options; + const localTransforms= [...transforms]; + if( __evadeHtmlCommentTest__=== true) { + arrayPush(localTransforms, evadeHtmlCommentTest); + } + if( __evadeImportExpressionTest__=== true) { + arrayPush(localTransforms, evadeImportExpressionTest); + } + if( __rejectSomeDirectEvalExpressions__=== true) { + arrayPush(localTransforms, rejectSomeDirectEvalExpressions); + } + + const { safeEvaluate}= provideCompartmentEvaluator( + compartmentFields, + options); + + + return safeEvaluate(source, { + localTransforms}); + + };$h‍_once.compartmentEvaluate(compartmentEvaluate); +}) +, +// === functors[41] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let assert,getDeferredExports,ReferenceError,SyntaxError,TypeError,arrayForEach,arrayIncludes,arrayPush,arraySome,arraySort,create,defineProperty,entries,freeze,isArray,keys,mapGet,weakmapGet,reflectHas,assign,compartmentEvaluate;$h‍_imports([["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]],["./module-proxy.js", [["getDeferredExports", [$h‍_a => (getDeferredExports = $h‍_a)]]]],["./commons.js", [["ReferenceError", [$h‍_a => (ReferenceError = $h‍_a)]],["SyntaxError", [$h‍_a => (SyntaxError = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["arrayForEach", [$h‍_a => (arrayForEach = $h‍_a)]],["arrayIncludes", [$h‍_a => (arrayIncludes = $h‍_a)]],["arrayPush", [$h‍_a => (arrayPush = $h‍_a)]],["arraySome", [$h‍_a => (arraySome = $h‍_a)]],["arraySort", [$h‍_a => (arraySort = $h‍_a)]],["create", [$h‍_a => (create = $h‍_a)]],["defineProperty", [$h‍_a => (defineProperty = $h‍_a)]],["entries", [$h‍_a => (entries = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]],["isArray", [$h‍_a => (isArray = $h‍_a)]],["keys", [$h‍_a => (keys = $h‍_a)]],["mapGet", [$h‍_a => (mapGet = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]],["reflectHas", [$h‍_a => (reflectHas = $h‍_a)]],["assign", [$h‍_a => (assign = $h‍_a)]]]],["./compartment-evaluate.js", [["compartmentEvaluate", [$h‍_a => (compartmentEvaluate = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + +const { quote: q}= assert; + +const makeThirdPartyModuleInstance= ( + compartmentPrivateFields, + staticModuleRecord, + compartment, + moduleAliases, + moduleSpecifier, + resolvedImports)=> + { + const { exportsProxy, proxiedExports, activate}= getDeferredExports( + compartment, + weakmapGet(compartmentPrivateFields, compartment), + moduleAliases, + moduleSpecifier); + + + const notifiers= create(null); + + if( staticModuleRecord.exports) { + if( + !isArray(staticModuleRecord.exports)|| + arraySome(staticModuleRecord.exports, (name)=>typeof name!== 'string')) + { + throw TypeError( + `SES third-party static module record "exports" property must be an array of strings for module ${moduleSpecifier}`); + + } + arrayForEach(staticModuleRecord.exports, (name)=>{ + let value= proxiedExports[name]; + const updaters= []; + + const get= ()=> value; + + const set= (newValue)=>{ + value= newValue; + for( const updater of updaters) { + updater(newValue); + } + }; + + defineProperty(proxiedExports, name, { + get, + set, + enumerable: true, + configurable: false}); + + + notifiers[name]= (update)=>{ + arrayPush(updaters, update); + update(value); + }; + }); + // This is enough to support import * from cjs - the '*' field doesn't need to be in exports nor proxiedExports because import will only ever access it via notifiers + notifiers['*']= (update)=>{ + update(proxiedExports); + }; + } + + const localState= { + activated: false}; + + return freeze({ + notifiers, + exportsProxy, + execute() { + if( reflectHas(localState, 'errorFromExecute')) { + throw localState.errorFromExecute; + } + if( !localState.activated) { + activate(); + localState.activated= true; + try { + // eslint-disable-next-line @endo/no-polymorphic-call + staticModuleRecord.execute( + proxiedExports, + compartment, + resolvedImports); + + }catch( err) { + localState.errorFromExecute= err; + throw err; + } + } + }}); + + }; + +// `makeModuleInstance` takes a module's compartment record, the live import +// namespace, and a global object; and produces a module instance. +// The module instance carries the proxied module exports namespace (the +// "exports"), notifiers to update the module's internal import namespace, and +// an idempotent execute function. +// The module exports namespace is a proxy to the proxied exports namespace +// that the execution of the module instance populates. +$h‍_once.makeThirdPartyModuleInstance(makeThirdPartyModuleInstance);const makeModuleInstance=( + privateFields, + moduleAliases, + moduleRecord, + importedInstances)=> + { + const { + compartment, + moduleSpecifier, + staticModuleRecord, + importMeta: moduleRecordMeta}= + moduleRecord; + const { + reexports: exportAlls= [], + __syncModuleProgram__: functorSource, + __fixedExportMap__: fixedExportMap= {}, + __liveExportMap__: liveExportMap= {}, + __reexportMap__: reexportMap= {}, + __needsImportMeta__: needsImportMeta= false, + __syncModuleFunctor__}= + staticModuleRecord; + + const compartmentFields= weakmapGet(privateFields, compartment); + + const { __shimTransforms__, importMetaHook}= compartmentFields; + + const { exportsProxy, proxiedExports, activate}= getDeferredExports( + compartment, + compartmentFields, + moduleAliases, + moduleSpecifier); + + + // {_exportName_: getter} module exports namespace + // object (eventually proxied). + const exportsProps= create(null); + + // {_localName_: accessor} proxy traps for moduleLexicals and live bindings. + // The moduleLexicals object is frozen and the corresponding properties of + // moduleLexicals must be immutable, so we copy the descriptors. + const moduleLexicals= create(null); + + // {_localName_: init(initValue) -> initValue} used by the + // rewritten code to initialize exported fixed bindings. + const onceVar= create(null); + + // {_localName_: update(newValue)} used by the rewritten code to + // both initialize and update live bindings. + const liveVar= create(null); + + const importMeta= create(null); + if( moduleRecordMeta) { + assign(importMeta, moduleRecordMeta); + } + if( needsImportMeta&& importMetaHook) { + importMetaHook(moduleSpecifier, importMeta); + } + + // {_localName_: [{get, set, notify}]} used to merge all the export updaters. + const localGetNotify= create(null); + + // {[importName: string]: notify(update(newValue))} Used by code that imports + // one of this module's exports, so that their update function will + // be notified when this binding is initialized or updated. + const notifiers= create(null); + + arrayForEach(entries(fixedExportMap), ([fixedExportName, [localName]])=> { + let fixedGetNotify= localGetNotify[localName]; + if( !fixedGetNotify) { + // fixed binding state + let value; + let tdz= true; + /** @type {null | Array<(value: any) => void>} */ + let optUpdaters= []; + + // tdz sensitive getter + const get= ()=> { + if( tdz) { + throw ReferenceError( `binding ${q(localName)} not yet initialized`); + } + return value; + }; + + // leave tdz once + const init= freeze((initValue)=>{ + // init with initValue of a declared const binding, and return + // it. + if( !tdz) { + throw TypeError( + `Internal: binding ${q(localName)} already initialized`); + + } + value= initValue; + const updaters= optUpdaters; + optUpdaters= null; + tdz= false; + for( const updater of updaters|| []) { + updater(initValue); + } + return initValue; + }); + + // If still tdz, register update for notification later. + // Otherwise, update now. + const notify= (updater)=>{ + if( updater=== init) { + // Prevent recursion. + return; + } + if( tdz) { + arrayPush(optUpdaters|| [], updater); + }else { + updater(value); + } + }; + + // Need these for additional exports of the local variable. + fixedGetNotify= { + get, + notify}; + + localGetNotify[localName]= fixedGetNotify; + onceVar[localName]= init; + } + + exportsProps[fixedExportName]= { + get: fixedGetNotify.get, + set: undefined, + enumerable: true, + configurable: false}; + + + notifiers[fixedExportName]= fixedGetNotify.notify; + }); + + arrayForEach( + entries(liveExportMap), + ([liveExportName, [localName, setProxyTrap]])=> { + let liveGetNotify= localGetNotify[localName]; + if( !liveGetNotify) { + // live binding state + let value; + let tdz= true; + const updaters= []; + + // tdz sensitive getter + const get= ()=> { + if( tdz) { + throw ReferenceError( + `binding ${q(liveExportName)} not yet initialized`); + + } + return value; + }; + + // This must be usable locally for the translation of initializing + // a declared local live binding variable. + // + // For reexported variable, this is also an update function to + // register for notification with the downstream import, which we + // must assume to be live. Thus, it can be called independent of + // tdz but always leaves tdz. Such reexporting creates a tree of + // bindings. This lets the tree be hooked up even if the imported + // module instance isn't initialized yet, as may happen in cycles. + const update= freeze((newValue)=>{ + value= newValue; + tdz= false; + for( const updater of updaters) { + updater(newValue); + } + }); + + // tdz sensitive setter + const set= (newValue)=>{ + if( tdz) { + throw ReferenceError( `binding ${q(localName)} not yet initialized`); + } + value= newValue; + for( const updater of updaters) { + updater(newValue); + } + }; + + // Always register the updater function. + // If not in tdz, also update now. + const notify= (updater)=>{ + if( updater=== update) { + // Prevent recursion. + return; + } + arrayPush(updaters, updater); + if( !tdz) { + updater(value); + } + }; + + liveGetNotify= { + get, + notify}; + + + localGetNotify[localName]= liveGetNotify; + if( setProxyTrap) { + defineProperty(moduleLexicals, localName, { + get, + set, + enumerable: true, + configurable: false}); + + } + liveVar[localName]= update; + } + + exportsProps[liveExportName]= { + get: liveGetNotify.get, + set: undefined, + enumerable: true, + configurable: false}; + + + notifiers[liveExportName]= liveGetNotify.notify; + }); + + + const notifyStar= (update)=>{ + update(proxiedExports); + }; + notifiers['*']= notifyStar; + + // Per the calling convention for the moduleFunctor generated from + // an ESM, the `imports` function gets called once up front + // to populate or arrange the population of imports and reexports. + // The generated code produces an `updateRecord`: the means for + // the linker to update the imports and exports of the module. + // The updateRecord must conform to moduleAnalysis.imports + // updateRecord = Map + // importUpdaters = Map + function imports(updateRecord) { + // By the time imports is called, the importedInstances should already be + // initialized with module instances that satisfy + // imports. + // importedInstances = Map[_specifier_, { notifiers, module, execute }] + // notifiers = { [importName: string]: notify(update(newValue))} + + // export * cannot export default. + const candidateAll= create(null); + candidateAll.default= false; + for( const [specifier, importUpdaters]of updateRecord) { + const instance= mapGet(importedInstances, specifier); + // The module instance object is an internal literal, does not bind this, + // and never revealed outside the SES shim. + // There are two instantiation sites for instances and they are both in + // this module. + // eslint-disable-next-line @endo/no-polymorphic-call + instance.execute(); // bottom up cycle tolerant + const { notifiers: importNotifiers}= instance; + for( const [importName, updaters]of importUpdaters) { + const importNotify= importNotifiers[importName]; + if( !importNotify) { + throw SyntaxError( + `The requested module '${specifier}' does not provide an export named '${importName}'`); + + } + for( const updater of updaters) { + importNotify(updater); + } + } + if( arrayIncludes(exportAlls, specifier)) { + // Make all these imports candidates. + // Note names don't change in reexporting all + for( const [importAndExportName, importNotify]of entries( + importNotifiers)) + { + if( candidateAll[importAndExportName]=== undefined) { + candidateAll[importAndExportName]= importNotify; + }else { + // Already a candidate: remove ambiguity. + candidateAll[importAndExportName]= false; + } + } + } + if( reexportMap[specifier]) { + // Make named reexports candidates too. + for( const [localName, exportedName]of reexportMap[specifier]) { + candidateAll[exportedName]= importNotifiers[localName]; + } + } + } + + for( const [exportName, notify]of entries(candidateAll)) { + if( !notifiers[exportName]&& notify!== false) { + notifiers[exportName]= notify; + + // exported live binding state + let value; + const update= (newValue)=> value= newValue; + notify(update); + exportsProps[exportName]= { + get() { + return value; + }, + set: undefined, + enumerable: true, + configurable: false}; + + } + } + + // Sort the module exports namespace as per spec. + // The module exports namespace will be wrapped in a module namespace + // exports proxy which will serve as a "module exports namespace exotic + // object". + // Sorting properties is not generally reliable because some properties may + // be symbols, and symbols do not have an inherent relative order, but + // since all properties of the exports namespace must be keyed by a string + // and the string must correspond to a valid identifier, sorting these + // properties works for this specific case. + arrayForEach(arraySort(keys(exportsProps)), (k)=> + defineProperty(proxiedExports, k, exportsProps[k])); + + + freeze(proxiedExports); + activate(); + } + + let optFunctor; + if( __syncModuleFunctor__!== undefined) { + optFunctor= __syncModuleFunctor__; + }else { + optFunctor= compartmentEvaluate(compartmentFields, functorSource, { + globalObject: compartment.globalThis, + transforms: __shimTransforms__, + __moduleShimLexicals__: moduleLexicals}); + + } + let didThrow= false; + let thrownError; + function execute() { + if( optFunctor) { + // uninitialized + const functor= optFunctor; + optFunctor= null; + // initializing - call with `this` of `undefined`. + try { + functor( + freeze({ + imports: freeze(imports), + onceVar: freeze(onceVar), + liveVar: freeze(liveVar), + importMeta})); + + + }catch( e) { + didThrow= true; + thrownError= e; + } + // initialized + } + if( didThrow) { + throw thrownError; + } + } + + return freeze({ + notifiers, + exportsProxy, + execute}); + + };$h‍_once.makeModuleInstance(makeModuleInstance); +}) +, +// === functors[42] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let assert,makeModuleInstance,makeThirdPartyModuleInstance,Map,ReferenceError,TypeError,entries,isArray,isObject,mapGet,mapHas,mapSet,weakmapGet;$h‍_imports([["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]],["./module-instance.js", [["makeModuleInstance", [$h‍_a => (makeModuleInstance = $h‍_a)]],["makeThirdPartyModuleInstance", [$h‍_a => (makeThirdPartyModuleInstance = $h‍_a)]]]],["./commons.js", [["Map", [$h‍_a => (Map = $h‍_a)]],["ReferenceError", [$h‍_a => (ReferenceError = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["entries", [$h‍_a => (entries = $h‍_a)]],["isArray", [$h‍_a => (isArray = $h‍_a)]],["isObject", [$h‍_a => (isObject = $h‍_a)]],["mapGet", [$h‍_a => (mapGet = $h‍_a)]],["mapHas", [$h‍_a => (mapHas = $h‍_a)]],["mapSet", [$h‍_a => (mapSet = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + +const { Fail, quote: q}= assert; + +// `link` creates `ModuleInstances` and `ModuleNamespaces` for a module and its +// transitive dependencies and connects their imports and exports. +// After linking, the resulting working set is ready to be executed. +// The linker only concerns itself with module namespaces that are objects with +// property descriptors for their exports, which the Compartment proxies with +// the actual `ModuleNamespace`. +const link= ( + compartmentPrivateFields, + moduleAliases, + compartment, + moduleSpecifier)=> + { + const { name: compartmentName, moduleRecords}= weakmapGet( + compartmentPrivateFields, + compartment); + + + const moduleRecord= mapGet(moduleRecords, moduleSpecifier); + if( moduleRecord=== undefined) { + throw ReferenceError( + `Missing link to module ${q(moduleSpecifier)} from compartment ${q( + compartmentName) + }`); + + } + + // Mutual recursion so there's no confusion about which + // compartment is in context: the module record may be in another + // compartment, denoted by moduleRecord.compartment. + // eslint-disable-next-line no-use-before-define + return instantiate(compartmentPrivateFields, moduleAliases, moduleRecord); + };$h‍_once.link(link); + +function isPrecompiled(staticModuleRecord) { + return typeof staticModuleRecord.__syncModuleProgram__=== 'string'; + } + +function validatePrecompiledStaticModuleRecord( + staticModuleRecord, + moduleSpecifier) + { + const { __fixedExportMap__, __liveExportMap__}= staticModuleRecord; + isObject(__fixedExportMap__)|| + Fail `Property '__fixedExportMap__' of a precompiled module record must be an object, got ${q( + __fixedExportMap__) + }, for module ${q(moduleSpecifier)}`; + isObject(__liveExportMap__)|| + Fail `Property '__liveExportMap__' of a precompiled module record must be an object, got ${q( + __liveExportMap__) + }, for module ${q(moduleSpecifier)}`; + } + +function isThirdParty(staticModuleRecord) { + return typeof staticModuleRecord.execute=== 'function'; + } + +function validateThirdPartyStaticModuleRecord( + staticModuleRecord, + moduleSpecifier) + { + const { exports}= staticModuleRecord; + isArray(exports)|| + Fail `Property 'exports' of a third-party static module record must be an array, got ${q( + exports) + }, for module ${q(moduleSpecifier)}`; + } + +function validateStaticModuleRecord(staticModuleRecord, moduleSpecifier) { + isObject(staticModuleRecord)|| + Fail `Static module records must be of type object, got ${q( + staticModuleRecord) + }, for module ${q(moduleSpecifier)}`; + const { imports, exports, reexports= []}= staticModuleRecord; + isArray(imports)|| + Fail `Property 'imports' of a static module record must be an array, got ${q( + imports) + }, for module ${q(moduleSpecifier)}`; + isArray(exports)|| + Fail `Property 'exports' of a precompiled module record must be an array, got ${q( + exports) + }, for module ${q(moduleSpecifier)}`; + isArray(reexports)|| + Fail `Property 'reexports' of a precompiled module record must be an array if present, got ${q( + reexports) + }, for module ${q(moduleSpecifier)}`; + } + +const instantiate= ( + compartmentPrivateFields, + moduleAliases, + moduleRecord)=> + { + const { compartment, moduleSpecifier, resolvedImports, staticModuleRecord}= + moduleRecord; + const { instances}= weakmapGet(compartmentPrivateFields, compartment); + + // Memoize. + if( mapHas(instances, moduleSpecifier)) { + return mapGet(instances, moduleSpecifier); + } + + validateStaticModuleRecord(staticModuleRecord, moduleSpecifier); + + const importedInstances= new Map(); + let moduleInstance; + if( isPrecompiled(staticModuleRecord)) { + validatePrecompiledStaticModuleRecord(staticModuleRecord, moduleSpecifier); + moduleInstance= makeModuleInstance( + compartmentPrivateFields, + moduleAliases, + moduleRecord, + importedInstances); + + }else if( isThirdParty(staticModuleRecord)) { + validateThirdPartyStaticModuleRecord(staticModuleRecord, moduleSpecifier); + moduleInstance= makeThirdPartyModuleInstance( + compartmentPrivateFields, + staticModuleRecord, + compartment, + moduleAliases, + moduleSpecifier, + resolvedImports); + + }else { + throw TypeError( + `importHook must return a static module record, got ${q( + staticModuleRecord) + }`); + + } + + // Memoize. + mapSet(instances, moduleSpecifier, moduleInstance); + + // Link dependency modules. + for( const [importSpecifier, resolvedSpecifier]of entries(resolvedImports)) { + const importedInstance= link( + compartmentPrivateFields, + moduleAliases, + compartment, + resolvedSpecifier); + + mapSet(importedInstances, importSpecifier, importedInstance); + } + + return moduleInstance; + };$h‍_once.instantiate(instantiate); +}) +, +// === functors[43] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Map,ReferenceError,TypeError,WeakMap,assign,defineProperties,entries,promiseThen,weakmapGet,weakmapSet,setGlobalObjectSymbolUnscopables,setGlobalObjectConstantProperties,setGlobalObjectMutableProperties,setGlobalObjectEvaluators,sharedGlobalPropertyNames,load,link,getDeferredExports,assert,compartmentEvaluate,makeSafeEvaluator;$h‍_imports([["./commons.js", [["Map", [$h‍_a => (Map = $h‍_a)]],["ReferenceError", [$h‍_a => (ReferenceError = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["WeakMap", [$h‍_a => (WeakMap = $h‍_a)]],["assign", [$h‍_a => (assign = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["entries", [$h‍_a => (entries = $h‍_a)]],["promiseThen", [$h‍_a => (promiseThen = $h‍_a)]],["weakmapGet", [$h‍_a => (weakmapGet = $h‍_a)]],["weakmapSet", [$h‍_a => (weakmapSet = $h‍_a)]]]],["./global-object.js", [["setGlobalObjectSymbolUnscopables", [$h‍_a => (setGlobalObjectSymbolUnscopables = $h‍_a)]],["setGlobalObjectConstantProperties", [$h‍_a => (setGlobalObjectConstantProperties = $h‍_a)]],["setGlobalObjectMutableProperties", [$h‍_a => (setGlobalObjectMutableProperties = $h‍_a)]],["setGlobalObjectEvaluators", [$h‍_a => (setGlobalObjectEvaluators = $h‍_a)]]]],["./permits.js", [["sharedGlobalPropertyNames", [$h‍_a => (sharedGlobalPropertyNames = $h‍_a)]]]],["./module-load.js", [["load", [$h‍_a => (load = $h‍_a)]]]],["./module-link.js", [["link", [$h‍_a => (link = $h‍_a)]]]],["./module-proxy.js", [["getDeferredExports", [$h‍_a => (getDeferredExports = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]],["./compartment-evaluate.js", [["compartmentEvaluate", [$h‍_a => (compartmentEvaluate = $h‍_a)]]]],["./make-safe-evaluator.js", [["makeSafeEvaluator", [$h‍_a => (makeSafeEvaluator = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +const { quote: q}= assert; + +// moduleAliases associates every public module exports namespace with its +// corresponding compartment and specifier so they can be used to link modules +// across compartments. +// The mechanism to thread an alias is to use the compartment.module function +// to obtain the exports namespace of a foreign module and pass it into another +// compartment's moduleMap constructor option. +const moduleAliases= new WeakMap(); + +// privateFields captures the private state for each compartment. +const privateFields= new WeakMap(); + +// Compartments do not need an importHook or resolveHook to be useful +// as a vessel for evaluating programs. +// However, any method that operates the module system will throw an exception +// if these hooks are not available. +const assertModuleHooks= (compartment)=>{ + const { importHook, resolveHook}= weakmapGet(privateFields, compartment); + if( typeof importHook!== 'function'|| typeof resolveHook!== 'function') { + throw TypeError( + 'Compartment must be constructed with an importHook and a resolveHook for it to be able to load modules'); + + } + }; + +const InertCompartment= function Compartment( + _endowments= {}, + _modules= {}, + _options= {}) + { + throw TypeError( + 'Compartment.prototype.constructor is not a valid constructor.'); + + }; + +/** + * @param {Compartment} compartment + * @param {string} specifier + */$h‍_once.InertCompartment(InertCompartment); +const compartmentImportNow= (compartment, specifier)=> { + const { execute, exportsProxy}= link( + privateFields, + moduleAliases, + compartment, + specifier); + + execute(); + return exportsProxy; + }; + +const CompartmentPrototype= { + constructor: InertCompartment, + + get globalThis() { + return weakmapGet(privateFields, this).globalObject; + }, + + get name() { + return weakmapGet(privateFields, this).name; + }, + + /** + * @param {string} source is a JavaScript program grammar construction. + * @param {object} [options] + * @param {Array} [options.transforms] + * @param {boolean} [options.sloppyGlobalsMode] + * @param {object} [options.__moduleShimLexicals__] + * @param {boolean} [options.__evadeHtmlCommentTest__] + * @param {boolean} [options.__evadeImportExpressionTest__] + * @param {boolean} [options.__rejectSomeDirectEvalExpressions__] + */ + evaluate(source, options= {}) { + const compartmentFields= weakmapGet(privateFields, this); + return compartmentEvaluate(compartmentFields, source, options); + }, + + toString() { + return '[object Compartment]'; + }, + + module(specifier) { + if( typeof specifier!== 'string') { + throw TypeError('first argument of module() must be a string'); + } + + assertModuleHooks(this); + + const { exportsProxy}= getDeferredExports( + this, + weakmapGet(privateFields, this), + moduleAliases, + specifier); + + + return exportsProxy; + }, + + async import(specifier){ + if( typeof specifier!== 'string') { + throw TypeError('first argument of import() must be a string'); + } + + assertModuleHooks(this); + + return promiseThen( + load(privateFields, moduleAliases, this, specifier), + ()=> { + // The namespace box is a contentious design and likely to be a breaking + // change in an appropriately numbered future version. + const namespace= compartmentImportNow( + /** @type {Compartment} */ this, + specifier); + + return { namespace}; + }); + + }, + + async load(specifier){ + if( typeof specifier!== 'string') { + throw TypeError('first argument of load() must be a string'); + } + + assertModuleHooks(this); + + return load(privateFields, moduleAliases, this, specifier); + }, + + importNow(specifier) { + if( typeof specifier!== 'string') { + throw TypeError('first argument of importNow() must be a string'); + } + + assertModuleHooks(this); + + return compartmentImportNow(/** @type {Compartment} */ this, specifier); + }};$h‍_once.CompartmentPrototype(CompartmentPrototype); + + +defineProperties(InertCompartment, { + prototype: { value: CompartmentPrototype}}); + + +/** + * @callback MakeCompartmentConstructor + * @param {MakeCompartmentConstructor} targetMakeCompartmentConstructor + * @param {Record} intrinsics + * @param {(object: object) => void} markVirtualizedNativeFunction + * @returns {Compartment['constructor']} + */ + +/** @type {MakeCompartmentConstructor} */ +const makeCompartmentConstructor= ( + targetMakeCompartmentConstructor, + intrinsics, + markVirtualizedNativeFunction)=> + { + function Compartment(endowments= {}, moduleMap= {}, options= {}) { + if( new.target=== undefined) { + throw TypeError( + "Class constructor Compartment cannot be invoked without 'new'"); + + } + + // Extract options, and shallow-clone transforms. + const { + name= '', + transforms= [], + __shimTransforms__= [], + resolveHook, + importHook, + moduleMapHook, + importMetaHook}= + options; + const globalTransforms= [...transforms, ...__shimTransforms__]; + + // Map + const moduleRecords= new Map(); + // Map + const instances= new Map(); + // Map + const deferredExports= new Map(); + + // Validate given moduleMap. + // The module map gets translated on-demand in module-load.js and the + // moduleMap can be invalid in ways that cannot be detected in the + // constructor, but these checks allow us to throw early for a better + // developer experience. + for( const [specifier, aliasNamespace]of entries(moduleMap|| {})) { + if( typeof aliasNamespace=== 'string') { + // TODO implement parent module record retrieval. + throw TypeError( + `Cannot map module ${q(specifier)} to ${q( + aliasNamespace) + } in parent compartment`); + + }else if( weakmapGet(moduleAliases, aliasNamespace)=== undefined) { + // TODO create and link a synthetic module instance from the given + // namespace object. + throw ReferenceError( + `Cannot map module ${q( + specifier) + } because it has no known compartment in this realm`); + + } + } + + const globalObject= {}; + + setGlobalObjectSymbolUnscopables(globalObject); + + // We must initialize all constant properties first because + // `makeSafeEvaluator` may use them to create optimized bindings + // in the evaluator. + // TODO: consider merging into a single initialization if internal + // evaluator is no longer eagerly created + setGlobalObjectConstantProperties(globalObject); + + const { safeEvaluate}= makeSafeEvaluator({ + globalObject, + globalTransforms, + sloppyGlobalsMode: false}); + + + setGlobalObjectMutableProperties(globalObject, { + intrinsics, + newGlobalPropertyNames: sharedGlobalPropertyNames, + makeCompartmentConstructor: targetMakeCompartmentConstructor, + markVirtualizedNativeFunction}); + + + // TODO: maybe add evalTaming to the Compartment constructor 3rd options? + setGlobalObjectEvaluators( + globalObject, + safeEvaluate, + markVirtualizedNativeFunction); + + + assign(globalObject, endowments); + + weakmapSet(privateFields, this, { + name: `${name}`, + globalTransforms, + globalObject, + safeEvaluate, + resolveHook, + importHook, + moduleMap, + moduleMapHook, + importMetaHook, + moduleRecords, + __shimTransforms__, + deferredExports, + instances}); + + } + + Compartment.prototype= CompartmentPrototype; + + return Compartment; + };$h‍_once.makeCompartmentConstructor(makeCompartmentConstructor); +}) +, +// === functors[44] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let FERAL_FUNCTION,Float32Array,Map,Set,String,getOwnPropertyDescriptor,getPrototypeOf,iterateArray,iterateMap,iterateSet,iterateString,matchAllRegExp,matchAllSymbol,regexpPrototype,globalThis,InertCompartment;$h‍_imports([["./commons.js", [["FERAL_FUNCTION", [$h‍_a => (FERAL_FUNCTION = $h‍_a)]],["Float32Array", [$h‍_a => (Float32Array = $h‍_a)]],["Map", [$h‍_a => (Map = $h‍_a)]],["Set", [$h‍_a => (Set = $h‍_a)]],["String", [$h‍_a => (String = $h‍_a)]],["getOwnPropertyDescriptor", [$h‍_a => (getOwnPropertyDescriptor = $h‍_a)]],["getPrototypeOf", [$h‍_a => (getPrototypeOf = $h‍_a)]],["iterateArray", [$h‍_a => (iterateArray = $h‍_a)]],["iterateMap", [$h‍_a => (iterateMap = $h‍_a)]],["iterateSet", [$h‍_a => (iterateSet = $h‍_a)]],["iterateString", [$h‍_a => (iterateString = $h‍_a)]],["matchAllRegExp", [$h‍_a => (matchAllRegExp = $h‍_a)]],["matchAllSymbol", [$h‍_a => (matchAllSymbol = $h‍_a)]],["regexpPrototype", [$h‍_a => (regexpPrototype = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]]]],["./compartment.js", [["InertCompartment", [$h‍_a => (InertCompartment = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + +/** + * Object.getConstructorOf() + * Helper function to improve readability, similar to Object.getPrototypeOf(). + * + * @param {object} obj + */ +function getConstructorOf(obj) { + return getPrototypeOf(obj).constructor; + } + +// getAnonymousIntrinsics uses a utility function to construct an arguments +// object, since it cannot have one of its own and also be a const export. +function makeArguments() { + // eslint-disable-next-line prefer-rest-params + return arguments; + } + +/** + * getAnonymousIntrinsics() + * Get the intrinsics not otherwise reachable by named own property + * traversal from the global object. + * + * @returns {object} + */ +const getAnonymousIntrinsics= ()=> { + const InertFunction= FERAL_FUNCTION.prototype.constructor; + + // 9.2.4.1 %ThrowTypeError% + + const argsCalleeDesc= getOwnPropertyDescriptor(makeArguments(), 'callee'); + const ThrowTypeError= argsCalleeDesc&& argsCalleeDesc.get; + + // 21.1.5.2 The %StringIteratorPrototype% Object + + // eslint-disable-next-line no-new-wrappers + const StringIteratorObject= iterateString(new String()); + const StringIteratorPrototype= getPrototypeOf(StringIteratorObject); + + // 21.2.7.1 The %RegExpStringIteratorPrototype% Object + const RegExpStringIterator= + regexpPrototype[matchAllSymbol]&& matchAllRegExp(/./); + const RegExpStringIteratorPrototype= + RegExpStringIterator&& getPrototypeOf(RegExpStringIterator); + + // 22.1.5.2 The %ArrayIteratorPrototype% Object + + // eslint-disable-next-line no-array-constructor + const ArrayIteratorObject= iterateArray([]); + const ArrayIteratorPrototype= getPrototypeOf(ArrayIteratorObject); + + // 22.2.1 The %TypedArray% Intrinsic Object + + const TypedArray= getPrototypeOf(Float32Array); + + // 23.1.5.2 The %MapIteratorPrototype% Object + + const MapIteratorObject= iterateMap(new Map()); + const MapIteratorPrototype= getPrototypeOf(MapIteratorObject); + + // 23.2.5.2 The %SetIteratorPrototype% Object + + const SetIteratorObject= iterateSet(new Set()); + const SetIteratorPrototype= getPrototypeOf(SetIteratorObject); + + // 25.1.2 The %IteratorPrototype% Object + + const IteratorPrototype= getPrototypeOf(ArrayIteratorPrototype); + + // 25.2.1 The GeneratorFunction Constructor + + // eslint-disable-next-line no-empty-function + function* GeneratorFunctionInstance() { } + const GeneratorFunction= getConstructorOf(GeneratorFunctionInstance); + + // 25.2.3 Properties of the GeneratorFunction Prototype Object + + const Generator= GeneratorFunction.prototype; + + // 25.3.1 The AsyncGeneratorFunction Constructor + + // eslint-disable-next-line no-empty-function + async function* AsyncGeneratorFunctionInstance() { } + const AsyncGeneratorFunction= getConstructorOf( + AsyncGeneratorFunctionInstance); + + + // 25.3.2.2 AsyncGeneratorFunction.prototype + const AsyncGenerator= AsyncGeneratorFunction.prototype; + // 25.5.1 Properties of the AsyncGenerator Prototype Object + const AsyncGeneratorPrototype= AsyncGenerator.prototype; + const AsyncIteratorPrototype= getPrototypeOf(AsyncGeneratorPrototype); + + // 25.7.1 The AsyncFunction Constructor + + // eslint-disable-next-line no-empty-function + async function AsyncFunctionInstance() { } + const AsyncFunction= getConstructorOf(AsyncFunctionInstance); + + const intrinsics= { + '%InertFunction%': InertFunction, + '%ArrayIteratorPrototype%': ArrayIteratorPrototype, + '%InertAsyncFunction%': AsyncFunction, + '%AsyncGenerator%': AsyncGenerator, + '%InertAsyncGeneratorFunction%': AsyncGeneratorFunction, + '%AsyncGeneratorPrototype%': AsyncGeneratorPrototype, + '%AsyncIteratorPrototype%': AsyncIteratorPrototype, + '%Generator%': Generator, + '%InertGeneratorFunction%': GeneratorFunction, + '%IteratorPrototype%': IteratorPrototype, + '%MapIteratorPrototype%': MapIteratorPrototype, + '%RegExpStringIteratorPrototype%': RegExpStringIteratorPrototype, + '%SetIteratorPrototype%': SetIteratorPrototype, + '%StringIteratorPrototype%': StringIteratorPrototype, + '%ThrowTypeError%': ThrowTypeError, + '%TypedArray%': TypedArray, + '%InertCompartment%': InertCompartment}; + + + if( globalThis.Iterator) { + intrinsics['%IteratorHelperPrototype%']= getPrototypeOf( + // eslint-disable-next-line @endo/no-polymorphic-call + globalThis.Iterator.from([]).take(0)); + + intrinsics['%WrapForValidIteratorPrototype%']= getPrototypeOf( + // eslint-disable-next-line @endo/no-polymorphic-call + globalThis.Iterator.from({ next() { }})); + + } + + if( globalThis.AsyncIterator) { + intrinsics['%AsyncIteratorHelperPrototype%']= getPrototypeOf( + // eslint-disable-next-line @endo/no-polymorphic-call + globalThis.AsyncIterator.from([]).take(0)); + + intrinsics['%WrapForValidAsyncIteratorPrototype%']= getPrototypeOf( + // eslint-disable-next-line @endo/no-polymorphic-call + globalThis.AsyncIterator.from({ next() { }})); + + } + + return intrinsics; + };$h‍_once.getAnonymousIntrinsics(getAnonymousIntrinsics); +}) +, +// === functors[45] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let TypeError,freeze;$h‍_imports([["./commons.js", [["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["freeze", [$h‍_a => (freeze = $h‍_a)]]]]]); + + +const tameHarden= (safeHarden, hardenTaming)=> { + if( hardenTaming!== 'safe'&& hardenTaming!== 'unsafe') { + throw TypeError( `unrecognized fakeHardenOption ${hardenTaming}`); + } + + if( hardenTaming=== 'safe') { + return safeHarden; + } + + // In on the joke + Object.isExtensible= ()=> false; + Object.isFrozen= ()=> true; + Object.isSealed= ()=> true; + Reflect.isExtensible= ()=> false; + + if( safeHarden.isFake) { + // The "safe" hardener is already a fake hardener. + // Just use it. + return safeHarden; + } + + const fakeHarden= (arg)=>arg; + fakeHarden.isFake= true; + return freeze(fakeHarden); + };$h‍_once.tameHarden(tameHarden); +freeze(tameHarden); +}) +, +// === functors[46] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let Symbol,entries,fromEntries,getOwnPropertyDescriptors,defineProperties,arrayMap;$h‍_imports([["./commons.js", [["Symbol", [$h‍_a => (Symbol = $h‍_a)]],["entries", [$h‍_a => (entries = $h‍_a)]],["fromEntries", [$h‍_a => (fromEntries = $h‍_a)]],["getOwnPropertyDescriptors", [$h‍_a => (getOwnPropertyDescriptors = $h‍_a)]],["defineProperties", [$h‍_a => (defineProperties = $h‍_a)]],["arrayMap", [$h‍_a => (arrayMap = $h‍_a)]]]]]); + + + + + + + + +/** + * This taming provides a tamed alternative to the original `Symbol` constructor + * that starts off identical, except that all its properties are "temporarily" + * configurable. The original `Symbol` constructor remains unmodified on + * the start compartment's global. The tamed alternative is used as the shared + * `Symbol` constructor on constructed compartments. + * + * Starting these properties as configurable assumes two succeeding phases of + * processing: A whitelisting phase, that + * removes all properties not on the whitelist (which requires them to be + * configurable) and a global hardening step that freezes all primordials, + * returning these properties to their expected non-configurable status. + * + * The ses shim is constructed to eventually enable vetted shims to run between + * repair and global hardening. However, such vetted shims would normally + * run in the start compartment, which continues to use the original unmodified + * `Symbol`, so they should not normally be affected by the temporary + * configurability of these properties. + * + * Note that the spec refers to the global `Symbol` function as the + * ["Symbol Constructor"](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-symbol-constructor) + * even though it has a call behavior (can be called as a function) and does not + * not have a construct behavior (cannot be called with `new`). Accordingly, + * to tame it, we must replace it with a function without a construct + * behavior. + */ +const tameSymbolConstructor= ()=> { + const OriginalSymbol= Symbol; + const SymbolPrototype= OriginalSymbol.prototype; + + const SharedSymbol= { + Symbol(description) { + return OriginalSymbol(description); + }}. + Symbol; + + defineProperties(SymbolPrototype, { + constructor: { + value: SharedSymbol + // leave other `constructor` attributes as is +}}); + + + const originalDescsEntries= entries( + getOwnPropertyDescriptors(OriginalSymbol)); + + const descs= fromEntries( + arrayMap(originalDescsEntries, ([name, desc])=> [ + name, + { ...desc, configurable: true}])); + + + defineProperties(SharedSymbol, descs); + + return { '%SharedSymbol%': SharedSymbol}; + };$h‍_once.tameSymbolConstructor(tameSymbolConstructor); +}) +, +// === functors[47] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let makeEnvironmentCaptor,FERAL_FUNCTION,FERAL_EVAL,TypeError,arrayFilter,globalThis,is,ownKeys,stringSplit,noEvalEvaluate,makeHardener,makeIntrinsicsCollector,whitelistIntrinsics,tameFunctionConstructors,tameDateConstructor,tameMathObject,tameRegExpConstructor,enablePropertyOverrides,tameLocaleMethods,setGlobalObjectConstantProperties,setGlobalObjectMutableProperties,setGlobalObjectEvaluators,makeSafeEvaluator,initialGlobalPropertyNames,tameFunctionToString,tameDomains,tameConsole,tameErrorConstructor,assert,makeAssert,getAnonymousIntrinsics,makeCompartmentConstructor,tameHarden,tameSymbolConstructor;$h‍_imports([["@endo/env-options", [["makeEnvironmentCaptor", [$h‍_a => (makeEnvironmentCaptor = $h‍_a)]]]],["./commons.js", [["FERAL_FUNCTION", [$h‍_a => (FERAL_FUNCTION = $h‍_a)]],["FERAL_EVAL", [$h‍_a => (FERAL_EVAL = $h‍_a)]],["TypeError", [$h‍_a => (TypeError = $h‍_a)]],["arrayFilter", [$h‍_a => (arrayFilter = $h‍_a)]],["globalThis", [$h‍_a => (globalThis = $h‍_a)]],["is", [$h‍_a => (is = $h‍_a)]],["ownKeys", [$h‍_a => (ownKeys = $h‍_a)]],["stringSplit", [$h‍_a => (stringSplit = $h‍_a)]],["noEvalEvaluate", [$h‍_a => (noEvalEvaluate = $h‍_a)]]]],["./make-hardener.js", [["makeHardener", [$h‍_a => (makeHardener = $h‍_a)]]]],["./intrinsics.js", [["makeIntrinsicsCollector", [$h‍_a => (makeIntrinsicsCollector = $h‍_a)]]]],["./permits-intrinsics.js", [["default", [$h‍_a => (whitelistIntrinsics = $h‍_a)]]]],["./tame-function-constructors.js", [["default", [$h‍_a => (tameFunctionConstructors = $h‍_a)]]]],["./tame-date-constructor.js", [["default", [$h‍_a => (tameDateConstructor = $h‍_a)]]]],["./tame-math-object.js", [["default", [$h‍_a => (tameMathObject = $h‍_a)]]]],["./tame-regexp-constructor.js", [["default", [$h‍_a => (tameRegExpConstructor = $h‍_a)]]]],["./enable-property-overrides.js", [["default", [$h‍_a => (enablePropertyOverrides = $h‍_a)]]]],["./tame-locale-methods.js", [["default", [$h‍_a => (tameLocaleMethods = $h‍_a)]]]],["./global-object.js", [["setGlobalObjectConstantProperties", [$h‍_a => (setGlobalObjectConstantProperties = $h‍_a)]],["setGlobalObjectMutableProperties", [$h‍_a => (setGlobalObjectMutableProperties = $h‍_a)]],["setGlobalObjectEvaluators", [$h‍_a => (setGlobalObjectEvaluators = $h‍_a)]]]],["./make-safe-evaluator.js", [["makeSafeEvaluator", [$h‍_a => (makeSafeEvaluator = $h‍_a)]]]],["./permits.js", [["initialGlobalPropertyNames", [$h‍_a => (initialGlobalPropertyNames = $h‍_a)]]]],["./tame-function-tostring.js", [["tameFunctionToString", [$h‍_a => (tameFunctionToString = $h‍_a)]]]],["./tame-domains.js", [["tameDomains", [$h‍_a => (tameDomains = $h‍_a)]]]],["./error/tame-console.js", [["tameConsole", [$h‍_a => (tameConsole = $h‍_a)]]]],["./error/tame-error-constructor.js", [["default", [$h‍_a => (tameErrorConstructor = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]],["makeAssert", [$h‍_a => (makeAssert = $h‍_a)]]]],["./get-anonymous-intrinsics.js", [["getAnonymousIntrinsics", [$h‍_a => (getAnonymousIntrinsics = $h‍_a)]]]],["./compartment.js", [["makeCompartmentConstructor", [$h‍_a => (makeCompartmentConstructor = $h‍_a)]]]],["./tame-harden.js", [["tameHarden", [$h‍_a => (tameHarden = $h‍_a)]]]],["./tame-symbol-constructor.js", [["tameSymbolConstructor", [$h‍_a => (tameSymbolConstructor = $h‍_a)]]]]]); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +/** @typedef {import('../types.js').LockdownOptions} LockdownOptions */ + +const { Fail, details: d, quote: q}= assert; + +/** @type {Error=} */ +let priorRepairIntrinsics; + +/** @type {Error=} */ +let priorHardenIntrinsics; + +// Build a harden() with an empty fringe. +// Gate it on lockdown. +/** + * @template T + * @param {T} ref + * @returns {T} + */ +const safeHarden= makeHardener(); + +/** + * @callback Transform + * @param {string} source + * @returns {string} + */ + +/** + * @callback CompartmentConstructor + * @param {object} endowments + * @param {object} moduleMap + * @param {object} [options] + * @param {Array} [options.transforms] + * @param {Array} [options.__shimTransforms__] + */ + +// TODO https://github.com/endojs/endo/issues/814 +// Lockdown currently allows multiple calls provided that the specified options +// of every call agree. With experience, we have observed that lockdown should +// only ever need to be called once and that simplifying lockdown will improve +// the quality of audits. + +const assertDirectEvalAvailable= ()=> { + let allowed= false; + try { + allowed= FERAL_FUNCTION( + 'eval', + 'SES_changed', + `\ + eval("SES_changed = true"); + return SES_changed; + `)( + FERAL_EVAL, false); + // If we get here and SES_changed stayed false, that means the eval was sloppy + // and indirect, which generally creates a new global. + // We are going to throw an exception for failing to initialize SES, but + // good neighbors clean up. + if( !allowed) { + delete globalThis.SES_changed; + } + }catch( _error) { + // We reach here if eval is outright forbidden by a Content Security Policy. + // We allow this for SES usage that delegates the responsibility to isolate + // guest code to production code generation. + allowed= true; + } + if( !allowed) { + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_DIRECT_EVAL.md + throw TypeError( + `SES cannot initialize unless 'eval' is the original intrinsic 'eval', suitable for direct-eval (dynamically scoped eval) (SES_DIRECT_EVAL)`); + + } + }; + +/** + * @param {LockdownOptions} [options] + */ +const repairIntrinsics= (options= {})=> { + // First time, absent options default to 'safe'. + // Subsequent times, absent options default to first options. + // Thus, all present options must agree with first options. + // Reconstructing `option` here also ensures that it is a well + // behaved record, with only own data properties. + // + // The `overrideTaming` is not a safety issue. Rather it is a tradeoff + // between code compatibility, which is better with the `'moderate'` + // setting, and tool compatibility, which is better with the `'min'` + // setting. See + // https://github.com/Agoric/SES-shim/blob/master/packages/ses/README.md#enabling-override-by-assignment) + // for an explanation of when to use which. + // + // The `stackFiltering` is not a safety issue. Rather it is a tradeoff + // between relevance and completeness of the stack frames shown on the + // console. Setting`stackFiltering` to `'verbose'` applies no filters, providing + // the raw stack frames that can be quite versbose. Setting + // `stackFrameFiltering` to`'concise'` limits the display to the stack frame + // information most likely to be relevant, eliminating distracting frames + // such as those from the infrastructure. However, the bug you're trying to + // track down might be in the infrastrure, in which case the `'verbose'` setting + // is useful. See + // [`stackFiltering` options](https://github.com/Agoric/SES-shim/blob/master/packages/ses/lockdown-options.md#stackfiltering-options) + // for an explanation. + + const { getEnvironmentOption: getenv}= makeEnvironmentCaptor(globalThis); + + const { + errorTaming= getenv('LOCKDOWN_ERROR_TAMING', 'safe'), + errorTrapping= getenv('LOCKDOWN_ERROR_TRAPPING', 'platform'), + unhandledRejectionTrapping= getenv( + 'LOCKDOWN_UNHANDLED_REJECTION_TRAPPING', + 'report'), + + regExpTaming= getenv('LOCKDOWN_REGEXP_TAMING', 'safe'), + localeTaming= getenv('LOCKDOWN_LOCALE_TAMING', 'safe'), + consoleTaming= getenv('LOCKDOWN_CONSOLE_TAMING', 'safe'), + overrideTaming= getenv('LOCKDOWN_OVERRIDE_TAMING', 'moderate'), + stackFiltering= getenv('LOCKDOWN_STACK_FILTERING', 'concise'), + domainTaming= getenv('LOCKDOWN_DOMAIN_TAMING', 'safe'), + evalTaming= getenv('LOCKDOWN_EVAL_TAMING', 'safeEval'), + overrideDebug= arrayFilter( + stringSplit(getenv('LOCKDOWN_OVERRIDE_DEBUG', ''), ','), + /** @param {string} debugName */ + (debugName)=>debugName!== ''), + + __hardenTaming__= getenv('LOCKDOWN_HARDEN_TAMING', 'safe'), + dateTaming= 'safe', // deprecated + mathTaming= 'safe', // deprecated + ...extraOptions}= + options; + + evalTaming=== 'unsafeEval'|| + evalTaming=== 'safeEval'|| + evalTaming=== 'noEval'|| + Fail `lockdown(): non supported option evalTaming: ${q(evalTaming)}`; + + // Assert that only supported options were passed. + // Use Reflect.ownKeys to reject symbol-named properties as well. + const extraOptionsNames= ownKeys(extraOptions); + extraOptionsNames.length=== 0|| + Fail `lockdown(): non supported option ${q(extraOptionsNames)}`; + + priorRepairIntrinsics=== undefined|| + // eslint-disable-next-line @endo/no-polymorphic-call + assert.fail( + d `Already locked down at ${priorRepairIntrinsics} (SES_ALREADY_LOCKED_DOWN)`, + TypeError); + + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_ALREADY_LOCKED_DOWN.md + priorRepairIntrinsics= TypeError('Prior lockdown (SES_ALREADY_LOCKED_DOWN)'); + // Tease V8 to generate the stack string and release the closures the stack + // trace retained: + priorRepairIntrinsics.stack; + + assertDirectEvalAvailable(); + + /** + * Because of packagers and bundlers, etc, multiple invocations of lockdown + * might happen in separate instantiations of the source of this module. + * In that case, each one sees its own `firstOptions` variable, so the test + * above will not detect that lockdown has already happened. We + * unreliably test some telltale signs that lockdown has run, to avoid + * trying to lock down a locked down environment. Although the test is + * unreliable, this is consistent with the SES threat model. SES provides + * security only if it runs first in a given realm, or if everything that + * runs before it is SES-aware and cooperative. Neither SES nor anything + * can protect itself from corrupting code that runs first. For these + * purposes, code that turns a realm into something that passes these + * tests without actually locking down counts as corrupting code. + * + * The specifics of what this tests for may change over time, but it + * should be consistent with any setting of the lockdown options. + */ + const seemsToBeLockedDown= ()=> { + return( + globalThis.Function.prototype.constructor!== globalThis.Function&& + // @ts-ignore harden is absent on globalThis type def. + typeof globalThis.harden=== 'function'&& + // @ts-ignore lockdown is absent on globalThis type def. + typeof globalThis.lockdown=== 'function'&& + globalThis.Date.prototype.constructor!== globalThis.Date&& + typeof globalThis.Date.now=== 'function'&& + // @ts-ignore does not recognize that Date constructor is a special + // Function. + // eslint-disable-next-line @endo/no-polymorphic-call + is(globalThis.Date.prototype.constructor.now(), NaN)); + + }; + + if( seemsToBeLockedDown()) { + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_MULTIPLE_INSTANCES.md + throw TypeError( + `Already locked down but not by this SES instance (SES_MULTIPLE_INSTANCES)`); + + } + + /** + * 1. TAME powers & gather intrinsics first. + */ + + tameDomains(domainTaming); + + // Replace Function.prototype.toString with one that recognizes + // shimmed functions as honorary native functions. + const markVirtualizedNativeFunction= tameFunctionToString(); + + const { addIntrinsics, completePrototypes, finalIntrinsics}= + makeIntrinsicsCollector(); + + const tamedHarden= tameHarden(safeHarden, __hardenTaming__); + addIntrinsics({ harden: tamedHarden}); + + addIntrinsics(tameFunctionConstructors()); + + addIntrinsics(tameDateConstructor(dateTaming)); + addIntrinsics(tameErrorConstructor(errorTaming, stackFiltering)); + addIntrinsics(tameMathObject(mathTaming)); + addIntrinsics(tameRegExpConstructor(regExpTaming)); + addIntrinsics(tameSymbolConstructor()); + + addIntrinsics(getAnonymousIntrinsics()); + + completePrototypes(); + + const intrinsics= finalIntrinsics(); + + /** + * Wrap console unless suppressed. + * At the moment, the console is considered a host power in the start + * compartment, and not a primordial. Hence it is absent from the whilelist + * and bypasses the intrinsicsCollector. + * + * @type {((error: any) => string | undefined) | undefined} + */ + let optGetStackString; + if( errorTaming!== 'unsafe') { + optGetStackString= intrinsics['%InitialGetStackString%']; + } + const consoleRecord= tameConsole( + consoleTaming, + errorTrapping, + unhandledRejectionTrapping, + optGetStackString); + + globalThis.console= /** @type {Console} */ consoleRecord.console; + + // @ts-ignore assert is absent on globalThis type def. + if( errorTaming=== 'unsafe'&& globalThis.assert=== assert) { + // If errorTaming is 'unsafe' we replace the global assert with + // one whose `details` template literal tag does not redact + // unmarked substitution values. IOW, it blabs information that + // was supposed to be secret from callers, as an aid to debugging + // at a further cost in safety. + // @ts-ignore assert is absent on globalThis type def. + globalThis.assert= makeAssert(undefined, true); + } + + // Replace *Locale* methods with their non-locale equivalents + tameLocaleMethods(intrinsics, localeTaming); + + /** + * 2. WHITELIST to standardize the environment. + */ + + // Remove non-standard properties. + // All remaining function encountered during whitelisting are + // branded as honorary native functions. + whitelistIntrinsics(intrinsics, markVirtualizedNativeFunction); + + // Initialize the powerful initial global, i.e., the global of the + // start compartment, from the intrinsics. + + setGlobalObjectConstantProperties(globalThis); + + setGlobalObjectMutableProperties(globalThis, { + intrinsics, + newGlobalPropertyNames: initialGlobalPropertyNames, + makeCompartmentConstructor, + markVirtualizedNativeFunction}); + + + if( evalTaming=== 'noEval') { + setGlobalObjectEvaluators( + globalThis, + noEvalEvaluate, + markVirtualizedNativeFunction); + + }else if( evalTaming=== 'safeEval') { + const { safeEvaluate}= makeSafeEvaluator({ globalObject: globalThis}); + setGlobalObjectEvaluators( + globalThis, + safeEvaluate, + markVirtualizedNativeFunction); + + }else if( evalTaming=== 'unsafeEval') { + // Leave eval function and Function constructor of the initial compartment in-tact. + // Other compartments will not have access to these evaluators unless a guest program + // escapes containment. + } + + /** + * 3. HARDEN to share the intrinsics. + * + * We define hardenIntrinsics here so that options are in scope, but return + * it to the caller because we intend to eventually allow vetted shims to run + * between repairs and the hardening of intrinsics and so we can benchmark + * repair separately from hardening. + */ + + const hardenIntrinsics= ()=> { + priorHardenIntrinsics=== undefined|| + // eslint-disable-next-line @endo/no-polymorphic-call + assert.fail( + d `Already locked down at ${priorHardenIntrinsics} (SES_ALREADY_LOCKED_DOWN)`, + TypeError); + + // See https://github.com/endojs/endo/blob/master/packages/ses/error-codes/SES_ALREADY_LOCKED_DOWN.md + priorHardenIntrinsics= TypeError( + 'Prior lockdown (SES_ALREADY_LOCKED_DOWN)'); + + // Tease V8 to generate the stack string and release the closures the stack + // trace retained: + priorHardenIntrinsics.stack; + + // Circumvent the override mistake. + // TODO consider moving this to the end of the repair phase, and + // therefore before vetted shims rather than afterwards. It is not + // clear yet which is better. + // @ts-ignore enablePropertyOverrides does its own input validation + enablePropertyOverrides(intrinsics, overrideTaming, overrideDebug); + + // Finally register and optionally freeze all the intrinsics. This + // must be the operation that modifies the intrinsics. + tamedHarden(intrinsics); + + return tamedHarden; + }; + + return hardenIntrinsics; + };$h‍_once.repairIntrinsics(repairIntrinsics); +}) +, +// === functors[48] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let globalThis,repairIntrinsics;$h‍_imports([["./assert-sloppy-mode.js", []],["./commons.js", [["globalThis", [$h‍_a => (globalThis = $h‍_a)]]]],["./lockdown.js", [["repairIntrinsics", [$h‍_a => (repairIntrinsics = $h‍_a)]]]]]); + + + + + + + + +/** + * @param {import('./lockdown.js').LockdownOptions} options + */ +globalThis.lockdown= (options)=>{ + const hardenIntrinsics= repairIntrinsics(options); + globalThis.harden= hardenIntrinsics(); + }; + +/** + * @param {import('./lockdown.js').LockdownOptions} options + */ +globalThis.repairIntrinsics= (options)=>{ + const hardenIntrinsics= repairIntrinsics(options); + // Reveal hardenIntrinsics after repairs. + globalThis.hardenIntrinsics= ()=> { + // Reveal harden after hardenIntrinsics. + // Harden is dangerous before hardenIntrinsics because hardening just + // about anything will inadvertently render intrinsics irreparable. + // Also, for modules that must work both before or after lockdown (code + // that is portable between JS and SES), the existence of harden in global + // scope signals whether such code should attempt to use harden in the + // defense of its own API. + // @ts-ignore harden not yet recognized on globalThis. + globalThis.harden= hardenIntrinsics(); + }; + }; +}) +, +// === functors[49] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let globalThis,makeCompartmentConstructor,tameFunctionToString,getGlobalIntrinsics;$h‍_imports([["./commons.js", [["globalThis", [$h‍_a => (globalThis = $h‍_a)]]]],["./compartment.js", [["makeCompartmentConstructor", [$h‍_a => (makeCompartmentConstructor = $h‍_a)]]]],["./tame-function-tostring.js", [["tameFunctionToString", [$h‍_a => (tameFunctionToString = $h‍_a)]]]],["./intrinsics.js", [["getGlobalIntrinsics", [$h‍_a => (getGlobalIntrinsics = $h‍_a)]]]]]); + + + + + + +const markVirtualizedNativeFunction= tameFunctionToString(); + +// @ts-ignore Compartment is definitely on globalThis. +globalThis.Compartment= makeCompartmentConstructor( + makeCompartmentConstructor, + getGlobalIntrinsics(globalThis), + markVirtualizedNativeFunction); +}) +, +// === functors[50] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { let globalThis,assert;$h‍_imports([["./commons.js", [["globalThis", [$h‍_a => (globalThis = $h‍_a)]]]],["./error/assert.js", [["assert", [$h‍_a => (assert = $h‍_a)]]]]]); + + +globalThis.assert= assert; +}) +, +// === functors[51] === +(({ imports: $h‍_imports, liveVar: $h‍_live, onceVar: $h‍_once, importMeta: $h‍____meta, }) => { $h‍_imports([["./src/lockdown-shim.js", []],["./src/compartment-shim.js", []],["./src/assert-shim.js", []]]); +}) +, +]; // functors end + + const cell = (name, value = undefined) => { + const observers = []; + return Object.freeze({ + get: Object.freeze(() => { + return value; + }), + set: Object.freeze((newValue) => { + value = newValue; + for (const observe of observers) { + observe(value); + } + }), + observe: Object.freeze((observe) => { + observers.push(observe); + observe(value); + }), + enumerable: true, + }); + }; + + const cells = [ + { + globalThis: cell("globalThis"), + Array: cell("Array"), + Date: cell("Date"), + FinalizationRegistry: cell("FinalizationRegistry"), + Float32Array: cell("Float32Array"), + JSON: cell("JSON"), + Map: cell("Map"), + Math: cell("Math"), + Number: cell("Number"), + Object: cell("Object"), + Promise: cell("Promise"), + Proxy: cell("Proxy"), + Reflect: cell("Reflect"), + FERAL_REG_EXP: cell("FERAL_REG_EXP"), + Set: cell("Set"), + String: cell("String"), + Symbol: cell("Symbol"), + WeakMap: cell("WeakMap"), + WeakSet: cell("WeakSet"), + FERAL_ERROR: cell("FERAL_ERROR"), + RangeError: cell("RangeError"), + ReferenceError: cell("ReferenceError"), + SyntaxError: cell("SyntaxError"), + TypeError: cell("TypeError"), + assign: cell("assign"), + create: cell("create"), + defineProperties: cell("defineProperties"), + entries: cell("entries"), + freeze: cell("freeze"), + getOwnPropertyDescriptor: cell("getOwnPropertyDescriptor"), + getOwnPropertyDescriptors: cell("getOwnPropertyDescriptors"), + getOwnPropertyNames: cell("getOwnPropertyNames"), + getPrototypeOf: cell("getPrototypeOf"), + is: cell("is"), + isFrozen: cell("isFrozen"), + isSealed: cell("isSealed"), + isExtensible: cell("isExtensible"), + keys: cell("keys"), + objectPrototype: cell("objectPrototype"), + seal: cell("seal"), + preventExtensions: cell("preventExtensions"), + setPrototypeOf: cell("setPrototypeOf"), + values: cell("values"), + fromEntries: cell("fromEntries"), + speciesSymbol: cell("speciesSymbol"), + toStringTagSymbol: cell("toStringTagSymbol"), + iteratorSymbol: cell("iteratorSymbol"), + matchAllSymbol: cell("matchAllSymbol"), + unscopablesSymbol: cell("unscopablesSymbol"), + symbolKeyFor: cell("symbolKeyFor"), + symbolFor: cell("symbolFor"), + isInteger: cell("isInteger"), + stringifyJson: cell("stringifyJson"), + defineProperty: cell("defineProperty"), + apply: cell("apply"), + construct: cell("construct"), + reflectGet: cell("reflectGet"), + reflectGetOwnPropertyDescriptor: cell("reflectGetOwnPropertyDescriptor"), + reflectHas: cell("reflectHas"), + reflectIsExtensible: cell("reflectIsExtensible"), + ownKeys: cell("ownKeys"), + reflectPreventExtensions: cell("reflectPreventExtensions"), + reflectSet: cell("reflectSet"), + isArray: cell("isArray"), + arrayPrototype: cell("arrayPrototype"), + mapPrototype: cell("mapPrototype"), + proxyRevocable: cell("proxyRevocable"), + regexpPrototype: cell("regexpPrototype"), + setPrototype: cell("setPrototype"), + stringPrototype: cell("stringPrototype"), + weakmapPrototype: cell("weakmapPrototype"), + weaksetPrototype: cell("weaksetPrototype"), + functionPrototype: cell("functionPrototype"), + promisePrototype: cell("promisePrototype"), + typedArrayPrototype: cell("typedArrayPrototype"), + uncurryThis: cell("uncurryThis"), + objectHasOwnProperty: cell("objectHasOwnProperty"), + arrayFilter: cell("arrayFilter"), + arrayForEach: cell("arrayForEach"), + arrayIncludes: cell("arrayIncludes"), + arrayJoin: cell("arrayJoin"), + arrayMap: cell("arrayMap"), + arrayPop: cell("arrayPop"), + arrayPush: cell("arrayPush"), + arraySlice: cell("arraySlice"), + arraySome: cell("arraySome"), + arraySort: cell("arraySort"), + iterateArray: cell("iterateArray"), + mapSet: cell("mapSet"), + mapGet: cell("mapGet"), + mapHas: cell("mapHas"), + mapDelete: cell("mapDelete"), + mapEntries: cell("mapEntries"), + iterateMap: cell("iterateMap"), + setAdd: cell("setAdd"), + setDelete: cell("setDelete"), + setForEach: cell("setForEach"), + setHas: cell("setHas"), + iterateSet: cell("iterateSet"), + regexpTest: cell("regexpTest"), + regexpExec: cell("regexpExec"), + matchAllRegExp: cell("matchAllRegExp"), + stringEndsWith: cell("stringEndsWith"), + stringIncludes: cell("stringIncludes"), + stringIndexOf: cell("stringIndexOf"), + stringMatch: cell("stringMatch"), + stringReplace: cell("stringReplace"), + stringSearch: cell("stringSearch"), + stringSlice: cell("stringSlice"), + stringSplit: cell("stringSplit"), + stringStartsWith: cell("stringStartsWith"), + iterateString: cell("iterateString"), + weakmapDelete: cell("weakmapDelete"), + weakmapGet: cell("weakmapGet"), + weakmapHas: cell("weakmapHas"), + weakmapSet: cell("weakmapSet"), + weaksetAdd: cell("weaksetAdd"), + weaksetHas: cell("weaksetHas"), + functionToString: cell("functionToString"), + promiseAll: cell("promiseAll"), + promiseCatch: cell("promiseCatch"), + promiseThen: cell("promiseThen"), + finalizationRegistryRegister: cell("finalizationRegistryRegister"), + finalizationRegistryUnregister: cell("finalizationRegistryUnregister"), + getConstructorOf: cell("getConstructorOf"), + immutableObject: cell("immutableObject"), + isObject: cell("isObject"), + isError: cell("isError"), + FERAL_EVAL: cell("FERAL_EVAL"), + FERAL_FUNCTION: cell("FERAL_FUNCTION"), + noEvalEvaluate: cell("noEvalEvaluate"), + }, + { + }, + { + makeEnvironmentCaptor: cell("makeEnvironmentCaptor"), + }, + { + }, + { + an: cell("an"), + bestEffortStringify: cell("bestEffortStringify"), + enJoin: cell("enJoin"), + }, + { + }, + { + }, + { + makeLRUCacheMap: cell("makeLRUCacheMap"), + makeNoteLogArgsArrayKit: cell("makeNoteLogArgsArrayKit"), + }, + { + unredactedDetails: cell("unredactedDetails"), + loggedErrorHandler: cell("loggedErrorHandler"), + makeAssert: cell("makeAssert"), + assert: cell("assert"), + }, + { + isTypedArray: cell("isTypedArray"), + makeHardener: cell("makeHardener"), + }, + { + constantProperties: cell("constantProperties"), + universalPropertyNames: cell("universalPropertyNames"), + initialGlobalPropertyNames: cell("initialGlobalPropertyNames"), + sharedGlobalPropertyNames: cell("sharedGlobalPropertyNames"), + uniqueGlobalPropertyNames: cell("uniqueGlobalPropertyNames"), + NativeErrors: cell("NativeErrors"), + FunctionInstance: cell("FunctionInstance"), + AsyncFunctionInstance: cell("AsyncFunctionInstance"), + isAccessorPermit: cell("isAccessorPermit"), + permitted: cell("permitted"), + }, + { + makeIntrinsicsCollector: cell("makeIntrinsicsCollector"), + getGlobalIntrinsics: cell("getGlobalIntrinsics"), + }, + { + default: cell("default"), + }, + { + default: cell("default"), + }, + { + default: cell("default"), + }, + { + default: cell("default"), + }, + { + default: cell("default"), + }, + { + minEnablements: cell("minEnablements"), + moderateEnablements: cell("moderateEnablements"), + severeEnablements: cell("severeEnablements"), + }, + { + default: cell("default"), + }, + { + default: cell("default"), + }, + { + makeEvalFunction: cell("makeEvalFunction"), + }, + { + makeFunctionConstructor: cell("makeFunctionConstructor"), + }, + { + setGlobalObjectSymbolUnscopables: cell("setGlobalObjectSymbolUnscopables"), + setGlobalObjectConstantProperties: cell("setGlobalObjectConstantProperties"), + setGlobalObjectMutableProperties: cell("setGlobalObjectMutableProperties"), + setGlobalObjectEvaluators: cell("setGlobalObjectEvaluators"), + }, + { + alwaysThrowHandler: cell("alwaysThrowHandler"), + strictScopeTerminatorHandler: cell("strictScopeTerminatorHandler"), + strictScopeTerminator: cell("strictScopeTerminator"), + }, + { + createSloppyGlobalsScopeTerminator: cell("createSloppyGlobalsScopeTerminator"), + }, + { + makeEvalScopeKit: cell("makeEvalScopeKit"), + }, + { + getSourceURL: cell("getSourceURL"), + }, + { + rejectHtmlComments: cell("rejectHtmlComments"), + evadeHtmlCommentTest: cell("evadeHtmlCommentTest"), + rejectImportExpressions: cell("rejectImportExpressions"), + evadeImportExpressionTest: cell("evadeImportExpressionTest"), + rejectSomeDirectEvalExpressions: cell("rejectSomeDirectEvalExpressions"), + mandatoryTransforms: cell("mandatoryTransforms"), + applyTransforms: cell("applyTransforms"), + transforms: cell("transforms"), + }, + { + isValidIdentifierName: cell("isValidIdentifierName"), + getScopeConstants: cell("getScopeConstants"), + }, + { + makeEvaluate: cell("makeEvaluate"), + }, + { + makeSafeEvaluator: cell("makeSafeEvaluator"), + }, + { + tameFunctionToString: cell("tameFunctionToString"), + }, + { + tameDomains: cell("tameDomains"), + }, + { + makeLoggingConsoleKit: cell("makeLoggingConsoleKit"), + makeCausalConsole: cell("makeCausalConsole"), + filterConsole: cell("filterConsole"), + consoleWhitelist: cell("consoleWhitelist"), + }, + { + makeRejectionHandlers: cell("makeRejectionHandlers"), + }, + { + tameConsole: cell("tameConsole"), + }, + { + filterFileName: cell("filterFileName"), + shortenCallSiteString: cell("shortenCallSiteString"), + tameV8ErrorConstructor: cell("tameV8ErrorConstructor"), + }, + { + default: cell("default"), + }, + { + makeAlias: cell("makeAlias"), + load: cell("load"), + }, + { + deferExports: cell("deferExports"), + getDeferredExports: cell("getDeferredExports"), + }, + { + provideCompartmentEvaluator: cell("provideCompartmentEvaluator"), + compartmentEvaluate: cell("compartmentEvaluate"), + }, + { + makeThirdPartyModuleInstance: cell("makeThirdPartyModuleInstance"), + makeModuleInstance: cell("makeModuleInstance"), + }, + { + link: cell("link"), + instantiate: cell("instantiate"), + }, + { + InertCompartment: cell("InertCompartment"), + CompartmentPrototype: cell("CompartmentPrototype"), + makeCompartmentConstructor: cell("makeCompartmentConstructor"), + }, + { + getAnonymousIntrinsics: cell("getAnonymousIntrinsics"), + }, + { + tameHarden: cell("tameHarden"), + }, + { + tameSymbolConstructor: cell("tameSymbolConstructor"), + }, + { + repairIntrinsics: cell("repairIntrinsics"), + }, + { + }, + { + }, + { + }, + { + }, + ]; + + Object.defineProperties(cells[3], Object.getOwnPropertyDescriptors(cells[2])); + + const namespaces = cells.map(cells => Object.freeze(Object.create(null, cells))); + + for (let index = 0; index < namespaces.length; index += 1) { + cells[index]['*'] = cell('*', namespaces[index]); + } + +function observeImports(map, importName, importIndex) { + for (const [name, observers] of map.get(importName)) { + const cell = cells[importIndex][name]; + if (cell === undefined) { + throw new ReferenceError(`Cannot import name ${name}`); + } + for (const observer of observers) { + cell.observe(observer); + } + } +} + + + functors[0]({ + imports(entries) { + const map = new Map(entries); + }, + liveVar: { + }, + onceVar: { + universalThis: cells[0].globalThis.set, + Array: cells[0].Array.set, + Date: cells[0].Date.set, + FinalizationRegistry: cells[0].FinalizationRegistry.set, + Float32Array: cells[0].Float32Array.set, + JSON: cells[0].JSON.set, + Map: cells[0].Map.set, + Math: cells[0].Math.set, + Number: cells[0].Number.set, + Object: cells[0].Object.set, + Promise: cells[0].Promise.set, + Proxy: cells[0].Proxy.set, + Reflect: cells[0].Reflect.set, + FERAL_REG_EXP: cells[0].FERAL_REG_EXP.set, + Set: cells[0].Set.set, + String: cells[0].String.set, + Symbol: cells[0].Symbol.set, + WeakMap: cells[0].WeakMap.set, + WeakSet: cells[0].WeakSet.set, + FERAL_ERROR: cells[0].FERAL_ERROR.set, + RangeError: cells[0].RangeError.set, + ReferenceError: cells[0].ReferenceError.set, + SyntaxError: cells[0].SyntaxError.set, + TypeError: cells[0].TypeError.set, + assign: cells[0].assign.set, + create: cells[0].create.set, + defineProperties: cells[0].defineProperties.set, + entries: cells[0].entries.set, + freeze: cells[0].freeze.set, + getOwnPropertyDescriptor: cells[0].getOwnPropertyDescriptor.set, + getOwnPropertyDescriptors: cells[0].getOwnPropertyDescriptors.set, + getOwnPropertyNames: cells[0].getOwnPropertyNames.set, + getPrototypeOf: cells[0].getPrototypeOf.set, + is: cells[0].is.set, + isFrozen: cells[0].isFrozen.set, + isSealed: cells[0].isSealed.set, + isExtensible: cells[0].isExtensible.set, + keys: cells[0].keys.set, + objectPrototype: cells[0].objectPrototype.set, + seal: cells[0].seal.set, + preventExtensions: cells[0].preventExtensions.set, + setPrototypeOf: cells[0].setPrototypeOf.set, + values: cells[0].values.set, + fromEntries: cells[0].fromEntries.set, + speciesSymbol: cells[0].speciesSymbol.set, + toStringTagSymbol: cells[0].toStringTagSymbol.set, + iteratorSymbol: cells[0].iteratorSymbol.set, + matchAllSymbol: cells[0].matchAllSymbol.set, + unscopablesSymbol: cells[0].unscopablesSymbol.set, + symbolKeyFor: cells[0].symbolKeyFor.set, + symbolFor: cells[0].symbolFor.set, + isInteger: cells[0].isInteger.set, + stringifyJson: cells[0].stringifyJson.set, + defineProperty: cells[0].defineProperty.set, + apply: cells[0].apply.set, + construct: cells[0].construct.set, + reflectGet: cells[0].reflectGet.set, + reflectGetOwnPropertyDescriptor: cells[0].reflectGetOwnPropertyDescriptor.set, + reflectHas: cells[0].reflectHas.set, + reflectIsExtensible: cells[0].reflectIsExtensible.set, + ownKeys: cells[0].ownKeys.set, + reflectPreventExtensions: cells[0].reflectPreventExtensions.set, + reflectSet: cells[0].reflectSet.set, + isArray: cells[0].isArray.set, + arrayPrototype: cells[0].arrayPrototype.set, + mapPrototype: cells[0].mapPrototype.set, + proxyRevocable: cells[0].proxyRevocable.set, + regexpPrototype: cells[0].regexpPrototype.set, + setPrototype: cells[0].setPrototype.set, + stringPrototype: cells[0].stringPrototype.set, + weakmapPrototype: cells[0].weakmapPrototype.set, + weaksetPrototype: cells[0].weaksetPrototype.set, + functionPrototype: cells[0].functionPrototype.set, + promisePrototype: cells[0].promisePrototype.set, + typedArrayPrototype: cells[0].typedArrayPrototype.set, + uncurryThis: cells[0].uncurryThis.set, + objectHasOwnProperty: cells[0].objectHasOwnProperty.set, + arrayFilter: cells[0].arrayFilter.set, + arrayForEach: cells[0].arrayForEach.set, + arrayIncludes: cells[0].arrayIncludes.set, + arrayJoin: cells[0].arrayJoin.set, + arrayMap: cells[0].arrayMap.set, + arrayPop: cells[0].arrayPop.set, + arrayPush: cells[0].arrayPush.set, + arraySlice: cells[0].arraySlice.set, + arraySome: cells[0].arraySome.set, + arraySort: cells[0].arraySort.set, + iterateArray: cells[0].iterateArray.set, + mapSet: cells[0].mapSet.set, + mapGet: cells[0].mapGet.set, + mapHas: cells[0].mapHas.set, + mapDelete: cells[0].mapDelete.set, + mapEntries: cells[0].mapEntries.set, + iterateMap: cells[0].iterateMap.set, + setAdd: cells[0].setAdd.set, + setDelete: cells[0].setDelete.set, + setForEach: cells[0].setForEach.set, + setHas: cells[0].setHas.set, + iterateSet: cells[0].iterateSet.set, + regexpTest: cells[0].regexpTest.set, + regexpExec: cells[0].regexpExec.set, + matchAllRegExp: cells[0].matchAllRegExp.set, + stringEndsWith: cells[0].stringEndsWith.set, + stringIncludes: cells[0].stringIncludes.set, + stringIndexOf: cells[0].stringIndexOf.set, + stringMatch: cells[0].stringMatch.set, + stringReplace: cells[0].stringReplace.set, + stringSearch: cells[0].stringSearch.set, + stringSlice: cells[0].stringSlice.set, + stringSplit: cells[0].stringSplit.set, + stringStartsWith: cells[0].stringStartsWith.set, + iterateString: cells[0].iterateString.set, + weakmapDelete: cells[0].weakmapDelete.set, + weakmapGet: cells[0].weakmapGet.set, + weakmapHas: cells[0].weakmapHas.set, + weakmapSet: cells[0].weakmapSet.set, + weaksetAdd: cells[0].weaksetAdd.set, + weaksetHas: cells[0].weaksetHas.set, + functionToString: cells[0].functionToString.set, + promiseAll: cells[0].promiseAll.set, + promiseCatch: cells[0].promiseCatch.set, + promiseThen: cells[0].promiseThen.set, + finalizationRegistryRegister: cells[0].finalizationRegistryRegister.set, + finalizationRegistryUnregister: cells[0].finalizationRegistryUnregister.set, + getConstructorOf: cells[0].getConstructorOf.set, + immutableObject: cells[0].immutableObject.set, + isObject: cells[0].isObject.set, + isError: cells[0].isError.set, + FERAL_EVAL: cells[0].FERAL_EVAL.set, + FERAL_FUNCTION: cells[0].FERAL_FUNCTION.set, + noEvalEvaluate: cells[0].noEvalEvaluate.set, + }, + importMeta: {}, + }); + functors[1]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + functors[2]({ + imports(entries) { + const map = new Map(entries); + }, + liveVar: { + }, + onceVar: { + makeEnvironmentCaptor: cells[2].makeEnvironmentCaptor.set, + }, + importMeta: {}, + }); + functors[3]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./src/env-options.js", 2); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + functors[4]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "../commons.js", 0); + }, + liveVar: { + }, + onceVar: { + an: cells[4].an.set, + bestEffortStringify: cells[4].bestEffortStringify.set, + enJoin: cells[4].enJoin.set, + }, + importMeta: {}, + }); + functors[5]({ + imports(entries) { + const map = new Map(entries); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + functors[6]({ + imports(entries) { + const map = new Map(entries); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + functors[7]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./internal-types.js", 6); + }, + liveVar: { + }, + onceVar: { + makeLRUCacheMap: cells[7].makeLRUCacheMap.set, + makeNoteLogArgsArrayKit: cells[7].makeNoteLogArgsArrayKit.set, + }, + importMeta: {}, + }); + functors[8]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "../commons.js", 0); + observeImports(map, "./stringify-utils.js", 4); + observeImports(map, "./types.js", 5); + observeImports(map, "./internal-types.js", 6); + observeImports(map, "./note-log-args.js", 7); + }, + liveVar: { + }, + onceVar: { + unredactedDetails: cells[8].unredactedDetails.set, + loggedErrorHandler: cells[8].loggedErrorHandler.set, + makeAssert: cells[8].makeAssert.set, + assert: cells[8].assert.set, + }, + importMeta: {}, + }); + functors[9]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + isTypedArray: cells[9].isTypedArray.set, + makeHardener: cells[9].makeHardener.set, + }, + importMeta: {}, + }); + functors[10]({ + imports(entries) { + const map = new Map(entries); + }, + liveVar: { + }, + onceVar: { + constantProperties: cells[10].constantProperties.set, + universalPropertyNames: cells[10].universalPropertyNames.set, + initialGlobalPropertyNames: cells[10].initialGlobalPropertyNames.set, + sharedGlobalPropertyNames: cells[10].sharedGlobalPropertyNames.set, + uniqueGlobalPropertyNames: cells[10].uniqueGlobalPropertyNames.set, + NativeErrors: cells[10].NativeErrors.set, + FunctionInstance: cells[10].FunctionInstance.set, + AsyncFunctionInstance: cells[10].AsyncFunctionInstance.set, + isAccessorPermit: cells[10].isAccessorPermit.set, + permitted: cells[10].permitted.set, + }, + importMeta: {}, + }); + functors[11]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./permits.js", 10); + }, + liveVar: { + }, + onceVar: { + makeIntrinsicsCollector: cells[11].makeIntrinsicsCollector.set, + getGlobalIntrinsics: cells[11].getGlobalIntrinsics.set, + }, + importMeta: {}, + }); + functors[12]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./permits.js", 10); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + default: cells[12].default.set, + }, + importMeta: {}, + }); + functors[13]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + default: cells[13].default.set, + }, + importMeta: {}, + }); + functors[14]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + default: cells[14].default.set, + }, + importMeta: {}, + }); + functors[15]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + default: cells[15].default.set, + }, + importMeta: {}, + }); + functors[16]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + default: cells[16].default.set, + }, + importMeta: {}, + }); + functors[17]({ + imports(entries) { + const map = new Map(entries); + }, + liveVar: { + }, + onceVar: { + minEnablements: cells[17].minEnablements.set, + moderateEnablements: cells[17].moderateEnablements.set, + severeEnablements: cells[17].severeEnablements.set, + }, + importMeta: {}, + }); + functors[18]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./enablements.js", 17); + }, + liveVar: { + }, + onceVar: { + default: cells[18].default.set, + }, + importMeta: {}, + }); + functors[19]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + default: cells[19].default.set, + }, + importMeta: {}, + }); + functors[20]({ + imports(entries) { + const map = new Map(entries); + }, + liveVar: { + }, + onceVar: { + makeEvalFunction: cells[20].makeEvalFunction.set, + }, + importMeta: {}, + }); + functors[21]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + makeFunctionConstructor: cells[21].makeFunctionConstructor.set, + }, + importMeta: {}, + }); + functors[22]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./make-eval-function.js", 20); + observeImports(map, "./make-function-constructor.js", 21); + observeImports(map, "./permits.js", 10); + }, + liveVar: { + }, + onceVar: { + setGlobalObjectSymbolUnscopables: cells[22].setGlobalObjectSymbolUnscopables.set, + setGlobalObjectConstantProperties: cells[22].setGlobalObjectConstantProperties.set, + setGlobalObjectMutableProperties: cells[22].setGlobalObjectMutableProperties.set, + setGlobalObjectEvaluators: cells[22].setGlobalObjectEvaluators.set, + }, + importMeta: {}, + }); + functors[23]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + alwaysThrowHandler: cells[23].alwaysThrowHandler.set, + strictScopeTerminatorHandler: cells[23].strictScopeTerminatorHandler.set, + strictScopeTerminator: cells[23].strictScopeTerminator.set, + }, + importMeta: {}, + }); + functors[24]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./strict-scope-terminator.js", 23); + }, + liveVar: { + }, + onceVar: { + createSloppyGlobalsScopeTerminator: cells[24].createSloppyGlobalsScopeTerminator.set, + }, + importMeta: {}, + }); + functors[25]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + makeEvalScopeKit: cells[25].makeEvalScopeKit.set, + }, + importMeta: {}, + }); + functors[26]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + getSourceURL: cells[26].getSourceURL.set, + }, + importMeta: {}, + }); + functors[27]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./get-source-url.js", 26); + }, + liveVar: { + }, + onceVar: { + rejectHtmlComments: cells[27].rejectHtmlComments.set, + evadeHtmlCommentTest: cells[27].evadeHtmlCommentTest.set, + rejectImportExpressions: cells[27].rejectImportExpressions.set, + evadeImportExpressionTest: cells[27].evadeImportExpressionTest.set, + rejectSomeDirectEvalExpressions: cells[27].rejectSomeDirectEvalExpressions.set, + mandatoryTransforms: cells[27].mandatoryTransforms.set, + applyTransforms: cells[27].applyTransforms.set, + transforms: cells[27].transforms.set, + }, + importMeta: {}, + }); + functors[28]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + isValidIdentifierName: cells[28].isValidIdentifierName.set, + getScopeConstants: cells[28].getScopeConstants.set, + }, + importMeta: {}, + }); + functors[29]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./scope-constants.js", 28); + }, + liveVar: { + }, + onceVar: { + makeEvaluate: cells[29].makeEvaluate.set, + }, + importMeta: {}, + }); + functors[30]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./strict-scope-terminator.js", 23); + observeImports(map, "./sloppy-globals-scope-terminator.js", 24); + observeImports(map, "./eval-scope.js", 25); + observeImports(map, "./transforms.js", 27); + observeImports(map, "./make-evaluate.js", 29); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + makeSafeEvaluator: cells[30].makeSafeEvaluator.set, + }, + importMeta: {}, + }); + functors[31]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + tameFunctionToString: cells[31].tameFunctionToString.set, + }, + importMeta: {}, + }); + functors[32]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + tameDomains: cells[32].tameDomains.set, + }, + importMeta: {}, + }); + functors[33]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "../commons.js", 0); + observeImports(map, "./types.js", 5); + observeImports(map, "./internal-types.js", 6); + }, + liveVar: { + }, + onceVar: { + makeLoggingConsoleKit: cells[33].makeLoggingConsoleKit.set, + makeCausalConsole: cells[33].makeCausalConsole.set, + filterConsole: cells[33].filterConsole.set, + consoleWhitelist: cells[33].consoleWhitelist.set, + }, + importMeta: {}, + }); + functors[34]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "../commons.js", 0); + }, + liveVar: { + }, + onceVar: { + makeRejectionHandlers: cells[34].makeRejectionHandlers.set, + }, + importMeta: {}, + }); + functors[35]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "../commons.js", 0); + observeImports(map, "./assert.js", 8); + observeImports(map, "./console.js", 33); + observeImports(map, "./unhandled-rejection.js", 34); + observeImports(map, "./types.js", 5); + observeImports(map, "./internal-types.js", 6); + }, + liveVar: { + }, + onceVar: { + tameConsole: cells[35].tameConsole.set, + }, + importMeta: {}, + }); + functors[36]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "../commons.js", 0); + }, + liveVar: { + }, + onceVar: { + filterFileName: cells[36].filterFileName.set, + shortenCallSiteString: cells[36].shortenCallSiteString.set, + tameV8ErrorConstructor: cells[36].tameV8ErrorConstructor.set, + }, + importMeta: {}, + }); + functors[37]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "../commons.js", 0); + observeImports(map, "../permits.js", 10); + observeImports(map, "./tame-v8-error-constructor.js", 36); + }, + liveVar: { + }, + onceVar: { + default: cells[37].default.set, + }, + importMeta: {}, + }); + functors[38]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + makeAlias: cells[38].makeAlias.set, + load: cells[38].load.set, + }, + importMeta: {}, + }); + functors[39]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./module-load.js", 38); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + deferExports: cells[39].deferExports.set, + getDeferredExports: cells[39].getDeferredExports.set, + }, + importMeta: {}, + }); + functors[40]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./transforms.js", 27); + observeImports(map, "./make-safe-evaluator.js", 30); + }, + liveVar: { + }, + onceVar: { + provideCompartmentEvaluator: cells[40].provideCompartmentEvaluator.set, + compartmentEvaluate: cells[40].compartmentEvaluate.set, + }, + importMeta: {}, + }); + functors[41]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./error/assert.js", 8); + observeImports(map, "./module-proxy.js", 39); + observeImports(map, "./commons.js", 0); + observeImports(map, "./compartment-evaluate.js", 40); + }, + liveVar: { + }, + onceVar: { + makeThirdPartyModuleInstance: cells[41].makeThirdPartyModuleInstance.set, + makeModuleInstance: cells[41].makeModuleInstance.set, + }, + importMeta: {}, + }); + functors[42]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./error/assert.js", 8); + observeImports(map, "./module-instance.js", 41); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + link: cells[42].link.set, + instantiate: cells[42].instantiate.set, + }, + importMeta: {}, + }); + functors[43]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./global-object.js", 22); + observeImports(map, "./permits.js", 10); + observeImports(map, "./module-load.js", 38); + observeImports(map, "./module-link.js", 42); + observeImports(map, "./module-proxy.js", 39); + observeImports(map, "./error/assert.js", 8); + observeImports(map, "./compartment-evaluate.js", 40); + observeImports(map, "./make-safe-evaluator.js", 30); + }, + liveVar: { + }, + onceVar: { + InertCompartment: cells[43].InertCompartment.set, + CompartmentPrototype: cells[43].CompartmentPrototype.set, + makeCompartmentConstructor: cells[43].makeCompartmentConstructor.set, + }, + importMeta: {}, + }); + functors[44]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./compartment.js", 43); + }, + liveVar: { + }, + onceVar: { + getAnonymousIntrinsics: cells[44].getAnonymousIntrinsics.set, + }, + importMeta: {}, + }); + functors[45]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + tameHarden: cells[45].tameHarden.set, + }, + importMeta: {}, + }); + functors[46]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + }, + liveVar: { + }, + onceVar: { + tameSymbolConstructor: cells[46].tameSymbolConstructor.set, + }, + importMeta: {}, + }); + functors[47]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "@endo/env-options", 3); + observeImports(map, "./commons.js", 0); + observeImports(map, "./make-hardener.js", 9); + observeImports(map, "./intrinsics.js", 11); + observeImports(map, "./permits-intrinsics.js", 12); + observeImports(map, "./tame-function-constructors.js", 13); + observeImports(map, "./tame-date-constructor.js", 14); + observeImports(map, "./tame-math-object.js", 15); + observeImports(map, "./tame-regexp-constructor.js", 16); + observeImports(map, "./enable-property-overrides.js", 18); + observeImports(map, "./tame-locale-methods.js", 19); + observeImports(map, "./global-object.js", 22); + observeImports(map, "./make-safe-evaluator.js", 30); + observeImports(map, "./permits.js", 10); + observeImports(map, "./tame-function-tostring.js", 31); + observeImports(map, "./tame-domains.js", 32); + observeImports(map, "./error/tame-console.js", 35); + observeImports(map, "./error/tame-error-constructor.js", 37); + observeImports(map, "./error/assert.js", 8); + observeImports(map, "./get-anonymous-intrinsics.js", 44); + observeImports(map, "./compartment.js", 43); + observeImports(map, "./tame-harden.js", 45); + observeImports(map, "./tame-symbol-constructor.js", 46); + }, + liveVar: { + }, + onceVar: { + repairIntrinsics: cells[47].repairIntrinsics.set, + }, + importMeta: {}, + }); + functors[48]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./assert-sloppy-mode.js", 1); + observeImports(map, "./commons.js", 0); + observeImports(map, "./lockdown.js", 47); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + functors[49]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./compartment.js", 43); + observeImports(map, "./tame-function-tostring.js", 31); + observeImports(map, "./intrinsics.js", 11); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + functors[50]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./commons.js", 0); + observeImports(map, "./error/assert.js", 8); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + functors[51]({ + imports(entries) { + const map = new Map(entries); + observeImports(map, "./src/lockdown-shim.js", 48); + observeImports(map, "./src/compartment-shim.js", 49); + observeImports(map, "./src/assert-shim.js", 50); + }, + liveVar: { + }, + onceVar: { + }, + importMeta: {}, + }); + + return cells[cells.length - 1]['*'].get(); +})();