Skip to content

Commit

Permalink
feat(ses): Compartment options migration (#2386)
Browse files Browse the repository at this point in the history
Refs: #400

## Description

To converge with XS in the patterns accepted by SES for Compartment
construction, this change introduces a `__options__` property to the
first argument of the Compartment constructor that indicates that the
constructor accepts a single options bag argument and does not receive
separate endowments and module map arguments.

### Security Considerations

None.

### Scaling Considerations

None.

### Documentation Considerations

This change includes updates for NEWS (with migration instructions and a
notice of intent to break deprecated usage patterns) and README (to
encourage recommended usage). We intend to follow up with an update to
hardenedjs.org upon release.

### Testing Considerations

All tests have been updated to use the new constructor pattern except
those already marked as legacy, which remain to exercise support for the
deprecated usage patterns.

### Compatibility Considerations

There is a remote possibility that existing code uses `__options__` as
the name of an endowment and will now be misinterpreted. We do not
imagine any guest programs are in a position to influence their host
compartment’s construction.

### Upgrade Considerations

None.
  • Loading branch information
kriskowal authored Jul 30, 2024
2 parents c7ebcc1 + 59327f0 commit b145fe1
Show file tree
Hide file tree
Showing 34 changed files with 1,573 additions and 1,803 deletions.
28 changes: 1 addition & 27 deletions packages/compartment-mapper/src/import-archive-lite.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,32 +230,6 @@ const makeArchiveImportHookMaker = (
return makeImportHook;
};

/**
* Creates a fake module namespace object that passes a brand check.
*
* @param {typeof Compartment} Compartment
* @returns {ModuleExportsNamespace}
*/
const makeFauxModuleExportsNamespace = Compartment => {
const compartment = new Compartment(
{},
{},
{
resolveHook() {
return '.';
},
async importHook() {
return {
imports: [],
execute() {},
exports: [],
};
},
},
);
return compartment.module('.');
};

// Have to give it a name to capture the external meaning of Compartment
// Otherwise @param {typeof Compartment} takes the Compartment to mean
// the const variable defined within the function.
Expand Down Expand Up @@ -377,7 +351,7 @@ export const parseArchive = async (
languageForExtension,
modules: Object.fromEntries(
Object.keys(modules || {}).map(specifier => {
return [specifier, makeFauxModuleExportsNamespace(Compartment)];
return [specifier, { namespace: {} }];
}),
),
Compartment,
Expand Down
5 changes: 3 additions & 2 deletions packages/compartment-mapper/src/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -449,13 +449,14 @@ export const link = (
scopes,
);

const compartment = new Compartment(create(null), undefined, {
const compartment = new Compartment({
name: location,
resolveHook,
importHook,
moduleMapHook,
transforms,
__shimTransforms__,
name: location,
__options__: true,
});

if (!archiveOnly) {
Expand Down
10 changes: 8 additions & 2 deletions packages/compartment-mapper/test/bundle.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@ test('bundles work', async t => {
const print = entry => {
log.push(entry);
};
const compartment = new Compartment({ print });
const compartment = new Compartment({
globals: { print },
__options__: true,
});
compartment.evaluate(bundle);
t.deepEqual(log, expectedLog);
});
Expand Down Expand Up @@ -93,7 +96,10 @@ test.failing('bundle cjs-compat', async t => {
const print = entry => {
log.push(entry);
};
const compartment = new Compartment({ print });
const compartment = new Compartment({
globals: { print },
__options__: true,
});
compartment.evaluate(bundle);
t.deepEqual(log, expectedLog);
});
Expand Down
13 changes: 8 additions & 5 deletions packages/compartment-mapper/test/main.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,14 @@ test('makeBundle / importArchive', async t => {

const archiverBundle = await makeBundle(readPowers.read, archiverLocation);
const archiverCompartment = new Compartment({
TextEncoder,
TextDecoder,
URL,
// See https://github.com/Agoric/agoric-sdk/issues/9515
assert: globalThis.assert,
globals: {
TextEncoder,
TextDecoder,
URL,
// See https://github.com/Agoric/agoric-sdk/issues/9515
assert: globalThis.assert,
},
__options__: true,
});
const evasiveArchiverBundle = archiverBundle
.replace(/(?<!\.)\bimport\b(?![:"'])/g, 'IMPORT')
Expand Down
42 changes: 36 additions & 6 deletions packages/ses/NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,43 @@ User-visible changes in SES:

# Next version

- *NOTICE*: This version introduces multiple features to converge upon a
more common standard for [Hardened JavaScript](https://hardenedjs.org).
All code should begin migrating to these usage patterns as the older
patterns are now deprecated and will not be supported in a future major
version of SES.

- To converge on a portable pattern for using `Compartment`, introduces an
`__options__` property for the first argument of the `Compartment`
constructor that must be `true` if present and indicates the object is the
options bag and not the global endowments. All code going forward should
include this flag until the next major version of SES, when we plan for it to
become vesgitial and drop support for three-argument `Compartment`
construction.

In the unlikely event that existing code names an endowment `__options__`,
that code will break and need to be adjusted to adopt this version.
Because we rate this unlikely, we have elected not to mark this with
a major version bump.

- Adds a `__noNamespaceBox__` option that aligns the behavior of the `import`
method on SES `Compartment` with the behavior of XS and the behavior we will
champion for compartment standards.
All use of `Compartment` should migrate to use this option as the standard
behavior will be enabled by default with the next major version of SES.

- Adds support for module descriptors better aligned with XS.
Compartments use module desriptors to load and link modules.
The importHook, importNowHook, and moduleMapHook all return module descriptors
(sometimes promises for module descriptors).
The modules option or argument to the Compatment constructor has module
descriptors for all its values.
- `{record, specifier, compartment}` should become `{source: record,
specifier, compartment}`.
- `{specifier, compartment}` should become `{source: specifier,
compartment}`.
- `{record: compartment.module(specifier)}` should become `{namespace:
specifier, compartment}`.

- When running transpiled code on Node, the SES error taming
gives line-numbers into the generated JavaScript, which often don't match the
Expand All @@ -25,12 +61,6 @@ User-visible changes in SES:
the stacktrace line-numbers point back into the original
source, as they do on Node without SES.

- Adds a `__noNamespaceBox__` option that aligns the behavior of the `import`
method on SES `Compartment` with the behavior of XS and the behavior we will
champion for compartment standards.
All use of `Compartment` should migrate to use this option as the standard
behavior will be enabled by default with the next major version of SES.

# v1.5.0 (2024-05-06)

- Adds `importNowHook` to the `Compartment` options. The compartment will invoke the hook whenever it encounters a missing dependency while running `compartmentInstance.importNow(specifier)`, which cannot use an asynchronous `importHook`.
Expand Down
68 changes: 47 additions & 21 deletions packages/ses/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,10 @@ function on `globalThis`.
import 'ses';

const c = new Compartment({
print: harden(console.log),
globals: {
print: harden(console.log),
},
__options__: true, // temporary migration affordance
});

c.evaluate(`
Expand Down Expand Up @@ -213,7 +216,10 @@ through:
* by assigning them to the compartment's `globalThis` after construction.

```js
const powerfulCompartment = new Compartment({ Math });
const powerfulCompartment = new Compartment({
globals: { Math },
__options__: true, // temporary migration affordance
});
powerfulCompartment.globalThis.Date = Date;
```

Expand Down Expand Up @@ -286,7 +292,7 @@ specifier for another module from a referrer module and the import specifier.
import 'ses';
import { ModuleSource } from '@endo/module-source';

const c1 = new Compartment({}, {}, {
const c1 = new Compartment({
name: "first compartment",
resolveHook: (moduleSpecifier, moduleReferrer) => {
return resolve(moduleSpecifier, moduleReferrer);
Expand All @@ -298,6 +304,7 @@ const c1 = new Compartment({}, {}, {
source: new ModuleSource(moduleText, moduleLocation);
};
},
__options__: true, // temporary migration affordance
});
```

Expand All @@ -310,15 +317,17 @@ const c1 = new Compartment({}, {}, {
A compartment can also link a module in another compartment.

```js
const c2 = new Compartment({}, {
'c1': {
source: './main.js',
compartment: c1,
},
}, {
const c2 = new Compartment({
name: "second compartment",
modules: {
'c1': {
source: './main.js',
compartment: c1,
},
},
resolveHook,
importHook,
__options__: true, // temporary migration affordance
});
```

Expand Down Expand Up @@ -406,9 +415,10 @@ const importHook = async specifier => {
throw new Error(`Cannot find module ${specifier}`);
};

const compartment = new Compartment({}, {}, {
const compartment = new Compartment({
resolveHook,
importHook,
__options__: true, // temporary migration affordance
});
```

Expand Down Expand Up @@ -443,16 +453,18 @@ const moduleMapHook = moduleSpecifier => {
}
};

const even = new Compartment({}, {}, {
const even = new Compartment({
resolveHook: nodeResolveHook,
importHook: makeImportHook('https://example.com/even'),
moduleMapHook,
__options__: true, // temporary migration affordance
});

const odd = new Compartment({}, {}, {
const odd = new Compartment({
resolveHook: nodeResolveHook,
importHook: makeImportHook('https://example.com/odd'),
moduleMapHook,
__options__: true, // temporary migration affordance
});
```

Expand All @@ -471,12 +483,13 @@ receive the same record type as from `importHook` or throw if it cannot.
import 'ses';
import { ModuleSource } from '@endo/module-source';

const compartment = new Compartment({}, {
c: {
source: new ModuleSource(''),
},
}, {
const compartment = new Compartment({
name: "first compartment",
modules: {
c: {
source: new ModuleSource(''),
},
},
resolveHook: (moduleSpecifier, moduleReferrer) => {
return resolve(moduleSpecifier, moduleReferrer);
},
Expand All @@ -495,6 +508,7 @@ const compartment = new Compartment({}, {
source: new ModuleSource(moduleText, moduleLocation),
};
},
__options__: true, // temporary migration affordance
});
//... | importHook | importNowHook
await compartment.import('a'); //| called | not called
Expand Down Expand Up @@ -595,7 +609,11 @@ not a module.

```js
const transforms = [addCodeCoverageInstrumentation];
const c = new Compartment({ console, coverage }, null, { transforms });
const c = new Compartment({
globals: { console, coverage },
transforms,
__options__: true, // temporary migration affordance
});
c.evaluate('console.log("Hello");');
```

Expand Down Expand Up @@ -633,8 +651,10 @@ instead of `transforms`.

```js
const __shimTransforms__ = [addCoverage];
const c = new Compartment({ console, coverage }, null, {
const c = new Compartment({
globals: { console, coverage },
__shimTransforms__,
__options__: true, // temporary migration affordance
});
c.evaluate('console.log("Hello");');
```
Expand Down Expand Up @@ -741,11 +761,17 @@ abilities.
lockdown();

const promise = new Promise(resolve => {
const compartmentA = new Compartment(harden({ resolve }));
const compartmentA = new Compartment({
globals: harden({ resolve }),
__options__: true, // temporary migration affordance
});
compartmentA.evaluate(programA);
});

const compartmentB = new Compartment(harden({ promise }));
const compartmentB = new Compartment({
globals: harden({ promise }),
__options__: true, // temporary migration affordance
});
compartmentB.evaluate(programB);
```

Expand Down
Loading

0 comments on commit b145fe1

Please sign in to comment.