+5
-0
@@ -175,2 +175,7 @@ /** | ||
| __native__?: boolean; | ||
| /** | ||
| * If `true`, the first error encountered during module loading will be thrown immediately | ||
| */ | ||
| noAggregateLoadErrors?: boolean; | ||
| } | ||
@@ -177,0 +182,0 @@ |
+9
-85
| { | ||
| "name": "ses", | ||
| "version": "1.13.1", | ||
| "version": "1.14.0", | ||
| "description": "Hardened JavaScript for Fearless Cooperation", | ||
@@ -97,4 +97,5 @@ "keywords": [ | ||
| "dependencies": { | ||
| "@endo/env-options": "^1.1.10", | ||
| "@endo/immutable-arraybuffer": "^1.1.1" | ||
| "@endo/cache-map": "^1.1.0", | ||
| "@endo/env-options": "^1.1.11", | ||
| "@endo/immutable-arraybuffer": "^1.1.2" | ||
| }, | ||
@@ -106,5 +107,5 @@ "devDependencies": { | ||
| "@babel/types": "~7.26.0", | ||
| "@endo/compartment-mapper": "^1.6.2", | ||
| "@endo/module-source": "^1.3.2", | ||
| "@endo/test262-runner": "^0.1.47", | ||
| "@endo/compartment-mapper": "^1.6.3", | ||
| "@endo/module-source": "^1.3.3", | ||
| "@endo/test262-runner": "^0.1.48", | ||
| "@types/babel__traverse": "^7.20.5", | ||
@@ -142,80 +143,3 @@ "ava": "^6.1.3", | ||
| "extends": [ | ||
| "plugin:@endo/internal" | ||
| ], | ||
| "rules": { | ||
| "no-restricted-globals": [ | ||
| "error", | ||
| "AggregateError", | ||
| "Array", | ||
| "ArrayBuffer", | ||
| "Atomics", | ||
| "BigInt", | ||
| "BigInt64Array", | ||
| "BigUint64Array", | ||
| "Boolean", | ||
| "Compartment", | ||
| "DataView", | ||
| "Date", | ||
| "Error", | ||
| "EvalError", | ||
| "Float32Array", | ||
| "Float64Array", | ||
| "Function", | ||
| "HandledPromise", | ||
| "Int16Array", | ||
| "Int32Array", | ||
| "Int8Array", | ||
| "JSON", | ||
| "Map", | ||
| "Math", | ||
| "Number", | ||
| "Object", | ||
| "Promise", | ||
| "Proxy", | ||
| "RangeError", | ||
| "ReferenceError", | ||
| "Reflect", | ||
| "RegExp", | ||
| "Set", | ||
| "SharedArrayBuffer", | ||
| "String", | ||
| "Symbol", | ||
| "SyntaxError", | ||
| "TypeError", | ||
| "URIError", | ||
| "Uint16Array", | ||
| "Uint32Array", | ||
| "Uint8Array", | ||
| "Uint8ClampedArray", | ||
| "WeakMap", | ||
| "WeakSet", | ||
| "assert", | ||
| "decodeURI", | ||
| "decodeURIComponent", | ||
| "encodeURI", | ||
| "encodeURIComponent", | ||
| "escape", | ||
| "eval", | ||
| "globalThis", | ||
| "isFinite", | ||
| "isNaN", | ||
| "lockdown", | ||
| "parseFloat", | ||
| "parseInt", | ||
| "unescape" | ||
| ], | ||
| "@endo/no-polymorphic-call": "error" | ||
| }, | ||
| "overrides": [ | ||
| { | ||
| "files": [ | ||
| "test/**/*.js", | ||
| "demos/**/*.js", | ||
| "scripts/**/*.js" | ||
| ], | ||
| "rules": { | ||
| "no-restricted-globals": "off", | ||
| "@endo/no-polymorphic-call": "off" | ||
| } | ||
| } | ||
| "plugin:@endo/ses" | ||
| ] | ||
@@ -232,3 +156,3 @@ }, | ||
| }, | ||
| "gitHead": "03b92fc383da5d8bb4ea993b90149a0db5799d0b" | ||
| "gitHead": "9815aea9541f241389d2135c6097a7442bdffa17" | ||
| } |
+68
-50
@@ -35,8 +35,8 @@ # SES | ||
| See https://github.com/Agoric/Jessie to see how SES fits into the various | ||
| See <https://github.com/Agoric/Jessie> to see how SES fits into the various | ||
| flavors of confined JavaScript execution. And visit | ||
| https://ses-demo.agoric.app/demos/ for a demo. | ||
| <https://ses-demo.agoric.app/demos/> for a demo. | ||
| SES starts where the Caja project left off | ||
| https://github.com/google/caja/wiki/SES, and goes on to introduce compartments | ||
| <https://github.com/google/caja/wiki/SES>, and goes on to introduce compartments | ||
| and modernize the permitted JavaScript features. | ||
@@ -49,7 +49,7 @@ | ||
| _Hardened JavaScript_, Kris Kowal: | ||
| *Hardened JavaScript*, Kris Kowal: | ||
| [](https://www.youtube.com/watch?v=RZ7bBIU8DRc) | ||
| _Don't add Security, Remove Insecurity_, Mark Miller: | ||
| *Don't add Security, Remove Insecurity*, Mark Miller: | ||
@@ -157,3 +157,2 @@ [](https://www.youtube.com/watch?v=u-XETUbxNUU) | ||
| ### Compartment | ||
@@ -284,6 +283,6 @@ | ||
| - the `modules` map provided to the `Compartment` constructor, | ||
| - returned by a `moduleMapHook(specifier)` passed as an option to the | ||
| * the `modules` map provided to the `Compartment` constructor, | ||
| * returned by a `moduleMapHook(specifier)` passed as an option to the | ||
| `Compartment` constructor. | ||
| - returned by either the `importHook(specifier)` or `importNowHook(specifier)` | ||
| * returned by either the `importHook(specifier)` or `importNowHook(specifier)` | ||
| option passed to the `Compartment` constructor. Calling | ||
@@ -348,9 +347,9 @@ `compartment.import(specifier)` falls through to the `importHook` which may | ||
| - If fhe value of the `source` property is a string, the parent compartment | ||
| * If fhe value of the `source` property is a string, the parent compartment | ||
| loads the module but the compartment itself initializes the module. | ||
| - Otherwise, if the value of the `source` property is the module source, the | ||
| * Otherwise, if the value of the `source` property is the module source, the | ||
| module is initialized from the module source. | ||
| - Otherwise, the value of the `source` property must be an object. The module | ||
| * Otherwise, the value of the `source` property must be an object. The module | ||
| is loaded and initialized from the object according to the [virtual module | ||
@@ -371,16 +370,16 @@ source](#VirtualModuleSource) pattern. | ||
| - If fhe value of the `namespace` property is a string, the descriptor shares a | ||
| * If fhe value of the `namespace` property is a string, the descriptor shares a | ||
| module to be loaded and initialized by the compartment referred by the | ||
| `compartment` property. | ||
| - If the `compartment` property is present, its value must be a | ||
| * If the `compartment` property is present, its value must be a | ||
| compartment. | ||
| - If absent, the `compartment` property defaults to the compartment being | ||
| * If absent, the `compartment` property defaults to the compartment being | ||
| constructed in the `modules` option, or being hooked in the `loadHook` | ||
| and `loadNowHook` options. | ||
| - Otherwise, if the value of the `namespace` property is a module namepace, the | ||
| * Otherwise, if the value of the `namespace` property is a module namepace, the | ||
| descriptor shares a module that is already available. | ||
| - Otherwise, the value of `namespace` property must be an object. The module is | ||
| * Otherwise, the value of `namespace` property must be an object. The module is | ||
| loaded and initialized from the object according to the [virtual module | ||
@@ -399,7 +398,7 @@ namespace](#VirtualModuleNamespace) pattern. | ||
| - `source` must be a module source, either a virtual module source | ||
| * `source` must be a module source, either a virtual module source | ||
| or a compiled module source. | ||
| - `compartment` is optional, to be specified if the alias transits to a | ||
| * `compartment` is optional, to be specified if the alias transits to a | ||
| the specified different compartment, and | ||
| - `specifier` is the full module specifier of the module in its compartment. | ||
| * `specifier` is the full module specifier of the module in its compartment. | ||
| This defaults to the request specifier, which is only useful if the | ||
@@ -566,15 +565,15 @@ compartment is different. | ||
| - `imports` is a record that maps partial module specifiers to a list of | ||
| * `imports` is a record that maps partial module specifiers to a list of | ||
| names imported from the corresponding module. | ||
| - `exports` is an array of all the names that the module will export. | ||
| - `reexports` is an array of partial module specifier for which this | ||
| * `exports` is an array of all the names that the module will export. | ||
| * `reexports` is an array of partial module specifier for which this | ||
| module exports all imported names. | ||
| This field is optional. | ||
| - `__syncModuleProgram__` is a string that evaluates to a function that accepts | ||
| * `__syncModuleProgram__` is a string that evaluates to a function that accepts | ||
| an initialization record and initializes the module. | ||
| This property distinguishes this type of module record. | ||
| The name implies a future record type that supports top-level await. | ||
| - An initialization record has the properties `imports`, `liveVar`, `importMeta` and | ||
| * An initialization record has the properties `imports`, `liveVar`, `importMeta` and | ||
| `onceVar`. | ||
| - `imports` is a function that accepts a map from partial import | ||
| * `imports` is a function that accepts a map from partial import | ||
| module specifiers to maps from names that the corresponding module | ||
@@ -584,12 +583,12 @@ exports to notifier functions. | ||
| to receive updates for the value exported by the other module. | ||
| - `importMeta` is a null-prototype object with keys transferred from `importMeta` | ||
| * `importMeta` is a null-prototype object with keys transferred from `importMeta` | ||
| property in the envelope returned by importHook and/or mutated by | ||
| calling `importMetaHook(moduleSpecifier, importMeta)` | ||
| - `liveVar` is a record that maps names exported by this module | ||
| * `liveVar` is a record that maps names exported by this module | ||
| to a function that may be called to initialize or update | ||
| the corresponding value in another module. | ||
| - `onceVar` is a record that maps constants exported by this | ||
| * `onceVar` is a record that maps constants exported by this | ||
| module to a function that may be called to initialize the | ||
| corresponding value in another module. | ||
| - `__syncModuleFunctor__` is an optional function that if present is used | ||
| * `__syncModuleFunctor__` is an optional function that if present is used | ||
| instead of the evaluation of the `__syncModuleProgram__` string. It will be | ||
@@ -599,3 +598,3 @@ called with the initialization record described above. It is intended to be | ||
| the responsibility of the author of the ModuleSource. | ||
| - `__liveExportsMap__` is a record that maps import names or names in the lexical | ||
| * `__liveExportsMap__` is a record that maps import names or names in the lexical | ||
| scope of the module to export names, for variables that may change after | ||
@@ -607,3 +606,3 @@ initialization. Any reexported name is assumed to possibly change. | ||
| name should throw a `ReferenceError`. | ||
| - `__fixedExportsMap__` is a record that maps import names to export names | ||
| * `__fixedExportsMap__` is a record that maps import names to export names | ||
| for constants exported by this module. | ||
@@ -687,2 +686,20 @@ The fixed exports map is an aesthetic subtype of the live exports map, | ||
| ### Controlling Module-Loading Errors | ||
| The `Compartment` constructor now accepts a `boolean` option, `noAggregateLoadErrors`, to control how module-loading errors are reported. | ||
| By default, its value is `false`, which causes all relevant errors to be collected and rejected or thrown in a single exception from `compartment.import()` or `compartment.importNow()`, respectively. | ||
| If set to `true`, this will cause the *first* module-loading error encountered to be thrown (or rejected) immediately; no further module-loading will be attempted, and no further errors will be collected. | ||
| This is mostly useful for supporting optional dependencies in CommonJS modules, for example: | ||
| ```js | ||
| try { | ||
| require('something-optional') | ||
| } catch (err) { | ||
| // continue | ||
| } | ||
| ``` | ||
| ## Security claims and caveats | ||
@@ -711,16 +728,16 @@ | ||
| - will initially only have access to one mutable object, the compartment's | ||
| * will initially only have access to one mutable object, the compartment's | ||
| `globalThis`, | ||
| - specifically cannot modify any shared primordial objects, which are part of | ||
| * specifically cannot modify any shared primordial objects, which are part of | ||
| the default execution environment, | ||
| - cannot initially perform any I/O (except I/O necessarily performed by the | ||
| * cannot initially perform any I/O (except I/O necessarily performed by the | ||
| trusted compute base like paging virtual memory), | ||
| - and specifically cannot measure the passage of time at any resolution. | ||
| * and specifically cannot measure the passage of time at any resolution. | ||
| However, such a program can: | ||
| - execute for an indefinite amount of time, | ||
| - allocate arbitrary amounts of memory, | ||
| - detect the platform endianness, | ||
| - in some JavaScript engines, observe the contents of the stack. | ||
| * execute for an indefinite amount of time, | ||
| * allocate arbitrary amounts of memory, | ||
| * detect the platform endianness, | ||
| * in some JavaScript engines, observe the contents of the stack. | ||
| This may include sensitive information about the layout of files on the host | ||
@@ -745,6 +762,6 @@ disk. | ||
| - initially share *any* mutable objects. | ||
| - be able to observe the relative passage of time of the other program, | ||
| * initially share *any* mutable objects. | ||
| * be able to observe the relative passage of time of the other program, | ||
| as they would had they been given a reference to a working `Date.now()`. | ||
| - be able to communicate, as they would if they had shared access to mutable | ||
| * be able to communicate, as they would if they had shared access to mutable | ||
| state like an unfrozen object, a hardened collection like a `Map`, or even | ||
@@ -843,7 +860,7 @@ `Math.random()`. | ||
| - the host hardware, | ||
| - the host operating system, | ||
| - any intermediate virtual operating systems or hypervisors, | ||
| - the process memory manager, | ||
| - an implementation of JavaScript conforming to ECMAScript 262 as of | ||
| * the host hardware, | ||
| * the host operating system, | ||
| * any intermediate virtual operating systems or hypervisors, | ||
| * the process memory manager, | ||
| * an implementation of JavaScript conforming to ECMAScript 262 as of | ||
| 2021, providing no unspecified embedding host behavior like the introduction of syntax | ||
@@ -854,4 +871,4 @@ that when evaluated reveals a mutable object. | ||
| `domain` module. | ||
| - Also, any attached debugger, and | ||
| - any JavaScript that has executed in the same realm before the host program calls | ||
| * Also, any attached debugger, and | ||
| * any JavaScript that has executed in the same realm before the host program calls | ||
| `lockdown`, including JavaScript that executes after `ses` initializes. | ||
@@ -913,2 +930,3 @@ | ||
| replacing `<lhs>.<propertyKey> = <rhs>;` or `<lhs>[<propertyKey>] = <rhs>;` with | ||
| ```js | ||
@@ -915,0 +933,0 @@ Object.defineProperties(<lhs>, { |
@@ -1,2 +0,2 @@ | ||
| import { objectHasOwnProperty } from './commons.js'; | ||
| import { hasOwn } from './commons.js'; | ||
@@ -55,3 +55,3 @@ /** | ||
| } catch (err) { | ||
| if (objectHasOwnProperty(obj, prop)) { | ||
| if (hasOwn(obj, prop)) { | ||
| if (typeof obj === 'function' && prop === 'prototype') { | ||
@@ -58,0 +58,0 @@ obj.prototype = undefined; |
+17
-16
@@ -80,2 +80,3 @@ /** | ||
| fromEntries, | ||
| hasOwn, | ||
| } = Object; | ||
@@ -170,3 +171,6 @@ | ||
| export const objectHasOwnProperty = uncurryThis(objectPrototype.hasOwnProperty); | ||
| /** | ||
| * @deprecated Use `hasOwn` instead | ||
| */ | ||
| export const objectHasOwnProperty = hasOwn; | ||
| // | ||
@@ -277,19 +281,16 @@ export const arrayFilter = uncurryThis(arrayPrototype.filter); | ||
| /** | ||
| * isObject tests whether a value is an object. | ||
| * Today, this is equivalent to: | ||
| * TODO Consolidate with `isPrimitive` that's currently in `@endo/pass-style`. | ||
| * Layering constraints make this tricky, which is why we haven't yet figured | ||
| * out how to do this. | ||
| * | ||
| * 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 | ||
| * @type {(val: unknown) => val is (undefined | ||
| * | null | ||
| * | boolean | ||
| * | number | ||
| * | bigint | ||
| * | string | ||
| * | symbol)} | ||
| */ | ||
| export const isObject = value => Object(value) === value; | ||
| export const isPrimitive = val => | ||
| !val || (typeof val !== 'object' && typeof val !== 'function'); | ||
@@ -296,0 +297,0 @@ /** |
+25
-8
@@ -107,2 +107,3 @@ /** | ||
| * @property {(fullSpecifier: string) => Promise<ModuleExportsNamespace>} compartmentImport | ||
| * @property {boolean} [noAggregateLoadErrors] | ||
| */ | ||
@@ -177,5 +178,4 @@ | ||
| async import(specifier) { | ||
| const { noNamespaceBox } = /** @type {CompartmentFields} */ ( | ||
| weakmapGet(privateFields, this) | ||
| ); | ||
| const { noNamespaceBox, noAggregateLoadErrors } = | ||
| /** @type {CompartmentFields} */ (weakmapGet(privateFields, this)); | ||
@@ -187,3 +187,5 @@ if (typeof specifier !== 'string') { | ||
| return promiseThen( | ||
| load(privateFields, moduleAliases, this, specifier), | ||
| load(privateFields, moduleAliases, this, specifier, { | ||
| noAggregateErrors: noAggregateLoadErrors, | ||
| }), | ||
| () => { | ||
@@ -211,3 +213,9 @@ // The namespace box is a contentious design and likely to be a breaking | ||
| return load(privateFields, moduleAliases, this, specifier); | ||
| const { noAggregateLoadErrors } = /** @type {CompartmentFields} */ ( | ||
| weakmapGet(privateFields, this) | ||
| ); | ||
| return load(privateFields, moduleAliases, this, specifier, { | ||
| noAggregateErrors: noAggregateLoadErrors, | ||
| }); | ||
| }, | ||
@@ -219,5 +227,10 @@ | ||
| } | ||
| const { noAggregateLoadErrors } = /** @type {CompartmentFields} */ ( | ||
| weakmapGet(privateFields, this) | ||
| ); | ||
| loadNow(privateFields, moduleAliases, this, specifier); | ||
| return compartmentImportNow(this, specifier); | ||
| loadNow(privateFields, moduleAliases, this, specifier, { | ||
| noAggregateErrors: noAggregateLoadErrors, | ||
| }); | ||
| return compartmentImportNow(/** @type {Compartment} */ (this), specifier); | ||
| }, | ||
@@ -353,2 +366,3 @@ }; | ||
| __noNamespaceBox__: noNamespaceBox = false, | ||
| noAggregateLoadErrors = false, | ||
| } = compartmentOptions(...args); | ||
@@ -431,3 +445,5 @@ const globalTransforms = arrayFlatMap( | ||
| } | ||
| await load(privateFields, moduleAliases, compartment, fullSpecifier); | ||
| await load(privateFields, moduleAliases, compartment, fullSpecifier, { | ||
| noAggregateErrors: noAggregateLoadErrors, | ||
| }); | ||
| const { execute, exportsProxy } = link( | ||
@@ -461,2 +477,3 @@ privateFields, | ||
| compartmentImport, | ||
| noAggregateLoadErrors, | ||
| }); | ||
@@ -463,0 +480,0 @@ } |
@@ -14,4 +14,4 @@ // Adapted from SES/Caja | ||
| getOwnPropertyDescriptors, | ||
| isObject, | ||
| objectHasOwnProperty, | ||
| isPrimitive, | ||
| hasOwn, | ||
| ownKeys, | ||
@@ -113,3 +113,3 @@ setHas, | ||
| } | ||
| if (objectHasOwnProperty(this, prop)) { | ||
| if (hasOwn(this, prop)) { | ||
| this[prop] = newValue; | ||
@@ -184,3 +184,3 @@ } else { | ||
| enableAllProperties(subPath, desc.value); | ||
| } else if (isObject(subPlan)) { | ||
| } else if (!isPrimitive(subPlan)) { | ||
| enableProperties(subPath, desc.value, subPlan); | ||
@@ -187,0 +187,0 @@ } else { |
@@ -42,3 +42,3 @@ // Copyright (C) 2019 Agoric, under Apache License 2.0 | ||
| objectPrototype, | ||
| objectHasOwnProperty, | ||
| hasOwn, | ||
| } from '../commons.js'; | ||
@@ -311,3 +311,3 @@ import { an, bestEffortStringify } from './stringify-utils.js'; | ||
| const desc = descs[name]; | ||
| if (desc && objectHasOwnProperty(desc, 'get')) { | ||
| if (desc && hasOwn(desc, 'get')) { | ||
| defineProperty(error, name, { | ||
@@ -314,0 +314,0 @@ value: error[name], // invoke the getter to convert to data property |
@@ -5,5 +5,6 @@ // @ts-check | ||
| import { makeLRUCacheMap } from '../make-lru-cachemap.js'; | ||
| import { makeCacheMapKit } from '@endo/cache-map'; | ||
| /** | ||
| * @import {CacheMapKit} from '@endo/cache-map'; | ||
| * @import {LogArgs} from './internal-types.js'; | ||
@@ -33,4 +34,2 @@ */ | ||
| /** | ||
| * @type {WeakMap<Error, LogArgs[]>} | ||
| * | ||
| * Maps from an error to an array of log args, where each log args is | ||
@@ -42,4 +41,6 @@ * remembered as an annotation on that error. This can be used, for example, | ||
| * then retrieve the graph of such annotations. | ||
| * | ||
| * @type {CacheMapKit<WeakMapConstructor, Error, LogArgs[]>} | ||
| */ | ||
| const noteLogArgsArrayMap = makeLRUCacheMap(errorsBudget); | ||
| const { cache: noteLogArgsArrayMap } = makeCacheMapKit(errorsBudget); | ||
@@ -46,0 +47,0 @@ /** |
@@ -8,3 +8,3 @@ import { | ||
| freeze, | ||
| objectHasOwnProperty, | ||
| hasOwn, | ||
| unscopablesSymbol, | ||
@@ -91,3 +91,3 @@ } from './commons.js'; | ||
| for (const [name, intrinsicName] of entries(universalPropertyNames)) { | ||
| if (objectHasOwnProperty(intrinsics, intrinsicName)) { | ||
| if (hasOwn(intrinsics, intrinsicName)) { | ||
| defineProperty(globalObject, name, { | ||
@@ -103,3 +103,3 @@ value: intrinsics[intrinsicName], | ||
| for (const [name, intrinsicName] of entries(newGlobalPropertyNames)) { | ||
| if (objectHasOwnProperty(intrinsics, intrinsicName)) { | ||
| if (hasOwn(intrinsics, intrinsicName)) { | ||
| defineProperty(globalObject, name, { | ||
@@ -106,0 +106,0 @@ value: intrinsics[intrinsicName], |
@@ -14,4 +14,4 @@ import { cauterizeProperty } from './cauterize-property.js'; | ||
| is, | ||
| isObject, | ||
| objectHasOwnProperty, | ||
| isPrimitive, | ||
| hasOwn, | ||
| values, | ||
@@ -41,3 +41,3 @@ weaksetHas, | ||
| function initProperty(obj, name, desc) { | ||
| if (objectHasOwnProperty(obj, name)) { | ||
| if (hasOwn(obj, name)) { | ||
| const preDesc = getOwnPropertyDescriptor(obj, name); | ||
@@ -74,3 +74,3 @@ if ( | ||
| for (const [globalName, intrinsicName] of entries(newPropertyNames)) { | ||
| if (objectHasOwnProperty(globalObject, globalName)) { | ||
| if (hasOwn(globalObject, globalName)) { | ||
| newIntrinsics[intrinsicName] = globalObject[globalName]; | ||
@@ -100,7 +100,7 @@ } | ||
| for (const [name, intrinsic] of entries(intrinsics)) { | ||
| if (!isObject(intrinsic)) { | ||
| if (isPrimitive(intrinsic)) { | ||
| // eslint-disable-next-line no-continue | ||
| continue; | ||
| } | ||
| if (!objectHasOwnProperty(intrinsic, 'prototype')) { | ||
| if (!hasOwn(intrinsic, 'prototype')) { | ||
| // eslint-disable-next-line no-continue | ||
@@ -127,3 +127,3 @@ continue; | ||
| typeof namePrototype !== 'string' || | ||
| !objectHasOwnProperty(permitted, namePrototype) | ||
| !hasOwn(permitted, namePrototype) | ||
| ) { | ||
@@ -133,3 +133,3 @@ throw TypeError(`Unrecognized ${name}.prototype permits entry`); | ||
| const intrinsicPrototype = intrinsic.prototype; | ||
| if (objectHasOwnProperty(intrinsics, namePrototype)) { | ||
| if (hasOwn(intrinsics, namePrototype)) { | ||
| if (intrinsics[namePrototype] !== intrinsicPrototype) { | ||
@@ -136,0 +136,0 @@ throw TypeError(`Conflicting bindings of ${namePrototype}`); |
@@ -38,4 +38,4 @@ // Adapted from SES/Caja - Copyright (C) 2011 Google Inc. | ||
| isInteger, | ||
| isObject, | ||
| objectHasOwnProperty, | ||
| isPrimitive, | ||
| hasOwn, | ||
| ownKeys, | ||
@@ -155,3 +155,3 @@ preventExtensions, | ||
| function enqueue(val) { | ||
| if (!isObject(val)) { | ||
| if (isPrimitive(val)) { | ||
| // ignore primitives | ||
@@ -211,3 +211,3 @@ return; | ||
| // this is a data property. | ||
| if (objectHasOwnProperty(desc, 'value')) { | ||
| if (hasOwn(desc, 'value')) { | ||
| enqueue(desc.value); | ||
@@ -214,0 +214,0 @@ } else { |
@@ -14,3 +14,3 @@ /* eslint-disable no-underscore-dangle */ | ||
| isArray, | ||
| isObject, | ||
| isPrimitive, | ||
| mapGet, | ||
@@ -63,7 +63,7 @@ mapHas, | ||
| const { __fixedExportMap__, __liveExportMap__ } = moduleSource; | ||
| isObject(__fixedExportMap__) || | ||
| !isPrimitive(__fixedExportMap__) || | ||
| Fail`Property '__fixedExportMap__' of a precompiled module source must be an object, got ${q( | ||
| __fixedExportMap__, | ||
| )}, for module ${q(moduleSpecifier)}`; | ||
| isObject(__liveExportMap__) || | ||
| !isPrimitive(__liveExportMap__) || | ||
| Fail`Property '__liveExportMap__' of a precompiled module source must be an object, got ${q( | ||
@@ -87,3 +87,3 @@ __liveExportMap__, | ||
| function validateModuleSource(moduleSource, moduleSpecifier) { | ||
| isObject(moduleSource) || | ||
| !isPrimitive(moduleSource) || | ||
| Fail`Invalid module source: must be of type object, got ${q( | ||
@@ -90,0 +90,0 @@ moduleSource, |
+48
-13
@@ -16,3 +16,3 @@ import { getEnvironmentOption as getenv } from '@endo/env-options'; | ||
| isArray, | ||
| isObject, | ||
| isPrimitive, | ||
| mapGet, | ||
@@ -190,3 +190,3 @@ mapHas, | ||
| ); | ||
| } else if (isObject(moduleDescriptor)) { | ||
| } else if (!isPrimitive(moduleDescriptor)) { | ||
| // In this shim (and not in XS, and not in the standard we imagine), we | ||
@@ -214,3 +214,3 @@ // allow a module namespace object to stand in for a module descriptor that | ||
| if ( | ||
| !isObject(aliasCompartment) || | ||
| isPrimitive(aliasCompartment) || | ||
| !weakmapHas(compartmentPrivateFields, aliasCompartment) | ||
@@ -239,3 +239,3 @@ ) { | ||
| // promoted into a module namespace with a virtual module source. | ||
| if (isObject(moduleDescriptor.namespace)) { | ||
| if (!isPrimitive(moduleDescriptor.namespace)) { | ||
| const { namespace } = moduleDescriptor; | ||
@@ -413,3 +413,3 @@ // Brand-check SES shim module exports namespaces: | ||
| if ( | ||
| !isObject(moduleDescriptor.compartment) || | ||
| isPrimitive(moduleDescriptor.compartment) || | ||
| !weakmapHas(compartmentPrivateFields, moduleDescriptor.compartment) || | ||
@@ -515,3 +515,8 @@ typeof moduleDescriptor.specifier !== 'string' | ||
| const asyncJobQueue = (errors = []) => { | ||
| /** | ||
| * If `aggregateErrors` is `false`, the `errors` property of the fulfilled object | ||
| * will always be empty. | ||
| * @param {{errors?: Error[], noAggregateErrors?: boolean}} [options] | ||
| */ | ||
| const asyncJobQueue = ({ errors = [], noAggregateErrors = false } = {}) => { | ||
| /** @type {Set<Promise<undefined>>} */ | ||
@@ -532,3 +537,7 @@ const pendingJobs = new Set(); | ||
| promiseThen(func(...args), noop, error => { | ||
| arrayPush(errors, error); | ||
| if (noAggregateErrors) { | ||
| throw error; | ||
| } else { | ||
| arrayPush(errors, error); | ||
| } | ||
| }), | ||
@@ -550,3 +559,8 @@ ); | ||
| const syncJobQueue = (errors = []) => { | ||
| /** | ||
| * If `aggregateErrors` is `false`, the `errors` property of the returned object | ||
| * will always be empty. | ||
| * @param {{errors?: Error[], noAggregateErrors?: boolean}} [options] | ||
| */ | ||
| const syncJobQueue = ({ errors = [], noAggregateErrors = false } = {}) => { | ||
| let current = []; | ||
@@ -575,3 +589,7 @@ let next = []; | ||
| } catch (error) { | ||
| arrayPush(errors, error); | ||
| if (noAggregateErrors) { | ||
| throw error; | ||
| } else { | ||
| arrayPush(errors, error); | ||
| } | ||
| } | ||
@@ -609,3 +627,3 @@ } | ||
| /* | ||
| /** | ||
| * `load` asynchronously gathers the module records for a module and its | ||
@@ -616,2 +634,7 @@ * transitive dependencies. | ||
| * This graph is then ready to be synchronously linked and executed. | ||
| * @param {WeakMap<Compartment, any>} compartmentPrivateFields | ||
| * @param {WeakMap<object, object>} moduleAliases | ||
| * @param {Compartment} compartment | ||
| * @param {string} moduleSpecifier - The module specifier to load. | ||
| * @param {{ noAggregateErrors?: boolean}} options | ||
| */ | ||
@@ -623,2 +646,3 @@ export const load = async ( | ||
| moduleSpecifier, | ||
| { noAggregateErrors = false } = {}, | ||
| ) => { | ||
@@ -633,3 +657,5 @@ const { name: compartmentName } = weakmapGet( | ||
| const { enqueueJob, drainQueue, errors } = asyncJobQueue(); | ||
| const { enqueueJob, drainQueue, errors } = asyncJobQueue({ | ||
| noAggregateErrors, | ||
| }); | ||
@@ -656,3 +682,3 @@ enqueueJob(memoizedLoadWithErrorAnnotation, [ | ||
| /* | ||
| /** | ||
| * `loadNow` synchronously gathers the module records for a specified module | ||
@@ -663,3 +689,9 @@ * and its transitive dependencies. | ||
| * This graph is then ready to be synchronously linked and executed. | ||
| * @param {WeakMap<Compartment, any>} compartmentPrivateFields | ||
| * @param {WeakMap<object, object>} moduleAliases | ||
| * @param {Compartment} compartment | ||
| * @param {string} moduleSpecifier - The module specifier to load. | ||
| * @param {{ noAggregateErrors?: boolean}} options | ||
| */ | ||
| export const loadNow = ( | ||
@@ -670,2 +702,3 @@ compartmentPrivateFields, | ||
| moduleSpecifier, | ||
| { noAggregateErrors = false } = {}, | ||
| ) => { | ||
@@ -680,3 +713,5 @@ const { name: compartmentName } = weakmapGet( | ||
| const { enqueueJob, drainQueue, errors } = syncJobQueue(); | ||
| const { enqueueJob, drainQueue, errors } = syncJobQueue({ | ||
| noAggregateErrors, | ||
| }); | ||
@@ -683,0 +718,0 @@ enqueueJob(memoizedLoadWithErrorAnnotation, [ |
@@ -58,5 +58,5 @@ // Copyright (C) 2011 Google Inc. | ||
| getPrototypeOf, | ||
| isObject, | ||
| isPrimitive, | ||
| mapGet, | ||
| objectHasOwnProperty, | ||
| hasOwn, | ||
| ownKeys, | ||
@@ -135,4 +135,4 @@ symbolKeyFor, | ||
| function visitPrototype(path, obj, protoName) { | ||
| if (!isObject(obj)) { | ||
| throw TypeError(`Object expected: ${path}, ${obj}, ${protoName}`); | ||
| if (isPrimitive(obj)) { | ||
| throw TypeError(`Object expected: ${path}, ${String(obj)}, ${protoName}`); | ||
| } | ||
@@ -190,3 +190,3 @@ const proto = getPrototypeOf(obj); | ||
| if (objectHasOwnProperty(intrinsics, permit)) { | ||
| if (hasOwn(intrinsics, permit)) { | ||
| if (value !== intrinsics[permit]) { | ||
@@ -231,3 +231,3 @@ throw TypeError(`Does not match permit for ${path}`); | ||
| // Is this a value property? | ||
| if (objectHasOwnProperty(desc, 'value')) { | ||
| if (hasOwn(desc, 'value')) { | ||
| if (isAccessorPermit(permit)) { | ||
@@ -252,3 +252,3 @@ throw TypeError(`Accessor expected at ${path}`); | ||
| const permitProp = prop === '__proto__' ? '--proto--' : prop; | ||
| if (objectHasOwnProperty(permit, permitProp)) { | ||
| if (hasOwn(permit, permitProp)) { | ||
| return permit[permitProp]; | ||
@@ -258,3 +258,3 @@ } | ||
| if (typeof obj === 'function') { | ||
| if (objectHasOwnProperty(FunctionInstance, permitProp)) { | ||
| if (hasOwn(FunctionInstance, permitProp)) { | ||
| return FunctionInstance[permitProp]; | ||
@@ -261,0 +261,0 @@ } |
@@ -6,3 +6,3 @@ import { | ||
| getOwnPropertyNames, | ||
| objectHasOwnProperty, | ||
| hasOwn, | ||
| regexpTest, | ||
@@ -135,3 +135,3 @@ } from './commons.js'; | ||
| // case where Object.prototype has been poisoned. | ||
| objectHasOwnProperty(desc, 'value') | ||
| hasOwn(desc, 'value') | ||
| ); | ||
@@ -138,0 +138,0 @@ } |
@@ -7,3 +7,3 @@ import { | ||
| getOwnPropertyNames, | ||
| isObject, | ||
| isPrimitive, | ||
| regexpExec, | ||
@@ -58,3 +58,3 @@ } from './commons.js'; | ||
| const intrinsic = intrinsics[intrinsicName]; | ||
| if (isObject(intrinsic)) { | ||
| if (!isPrimitive(intrinsic)) { | ||
| for (const methodName of getOwnPropertyNames(intrinsic)) { | ||
@@ -61,0 +61,0 @@ const match = regexpExec(localePattern, methodName); |
@@ -5,3 +5,3 @@ import { | ||
| iteratorSymbol, | ||
| objectHasOwnProperty, | ||
| hasOwn, | ||
| } from './commons.js'; | ||
@@ -19,3 +19,3 @@ | ||
| if (this === iteratorPrototype) return; | ||
| if (objectHasOwnProperty(this, iteratorSymbol)) { | ||
| if (hasOwn(this, iteratorSymbol)) { | ||
| this[iteratorSymbol] = value; | ||
@@ -22,0 +22,0 @@ } |
+5
-0
@@ -175,2 +175,7 @@ /** | ||
| __native__?: boolean; | ||
| /** | ||
| * If `true`, the first error encountered during module loading will be thrown immediately | ||
| */ | ||
| noAggregateLoadErrors?: boolean; | ||
| } | ||
@@ -177,0 +182,0 @@ |
| // @ts-check | ||
| /* eslint-disable @endo/no-polymorphic-call */ | ||
| // eslint-disable-next-line no-restricted-globals | ||
| const { isSafeInteger } = Number; | ||
| // eslint-disable-next-line no-restricted-globals | ||
| const { freeze } = Object; | ||
| // eslint-disable-next-line no-restricted-globals | ||
| const { toStringTag: toStringTagSymbol } = Symbol; | ||
| /** | ||
| * @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<Data>} next | ||
| * @property {DoublyLinkedCell<Data>} 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<Data>} | ||
| */ | ||
| const makeSelfCell = data => { | ||
| /** @type {Partial<DoublyLinkedCell<Data>>} */ | ||
| const incompleteCell = { | ||
| next: undefined, | ||
| prev: undefined, | ||
| data, | ||
| }; | ||
| const selfCell = /** @type {DoublyLinkedCell<Data>} */ (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<Data>} prev | ||
| * @param {DoublyLinkedCell<Data>} selfCell | ||
| */ | ||
| const spliceAfter = (prev, selfCell) => { | ||
| if (prev === selfCell) { | ||
| // eslint-disable-next-line no-restricted-globals | ||
| throw TypeError('Cannot splice a cell into itself'); | ||
| } | ||
| if (selfCell.next !== selfCell || selfCell.prev !== selfCell) { | ||
| // eslint-disable-next-line no-restricted-globals | ||
| 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<Data>} 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<K,V>} | ||
| */ | ||
| export const makeLRUCacheMap = keysBudget => { | ||
| if (!isSafeInteger(keysBudget) || keysBudget < 0) { | ||
| // eslint-disable-next-line no-restricted-globals | ||
| throw TypeError('keysBudget must be a safe non-negative integer number'); | ||
| } | ||
| /** @typedef {DoublyLinkedCell<WeakMap<K, V> | undefined>} LRUCacheCell */ | ||
| /** @type {WeakMap<K, LRUCacheCell>} */ | ||
| // eslint-disable-next-line no-restricted-globals | ||
| 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. | ||
| // eslint-disable-next-line no-restricted-globals | ||
| 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, | ||
| // eslint-disable-next-line jsdoc/check-types | ||
| [/** @type {typeof Symbol.toStringTag} */ (toStringTagSymbol)]: | ||
| 'LRUCacheMap', | ||
| }); | ||
| return lruCacheMap; | ||
| }; | ||
| freeze(makeLRUCacheMap); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
4275205
1.36%96831
1.7%965
1.9%3
50%85
-1.16%+ Added
+ Added
Updated