feat(ses): module execute uses syncModuleFunctor if present
MODIFY
packages/ses/NEWS.md
packages/ses/NEWS.md
User-visible changes in SES:
+# next
+
+- Introduces the `__syncModuleFunctor__` property of static module record
+ to replace evauluation of `__syncModuleProgram__` for environments without eval.
+
# v0.18.1 (2022-12-23)
- Fixes a bug for SES initialization in a no-unsafe-eval

MODIFY
packages/ses/README.md
packages/ses/README.md
@@ -441,6 +441,11 @@ A compiled static module record has the following shape:
- `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
+ instead of the evaluation of the `__syncModuleProgram__` string. It will be
+ called with the initialization record described above. It is intended to be
+ used in environments where eval is not available. Sandboxing of the functor is
+ the responsibility of the author of the StaticModuleRecord.
- `__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
initialization. Any reexported name is assumed to possibly change.

MODIFY
packages/ses/src/module-instance.js
packages/ses/src/module-instance.js
@@ -136,6 +136,7 @@ export const makeModuleInstance = (
__liveExportMap__: liveExportMap = {},
__reexportMap__: reexportMap = {},
__needsImportMeta__: needsImportMeta = false,
+ __syncModuleFunctor__,
} = staticModuleRecord;
const compartmentFields = weakmapGet(privateFields, compartment);
@@ -444,11 +445,16 @@ export const makeModuleInstance = (
activate();
}
- let optFunctor = compartmentEvaluate(compartmentFields, functorSource, {
+ 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() {

MODIFY
packages/ses/test/test-import-gauntlet.js
packages/ses/test/test-import-gauntlet.js
@@ -366,3 +366,48 @@ test('importHook returning a RedirectStaticModuleInterface with a specified comp
await compartment.import('./main.js');
});
+
+// while this test demonstrates different behavior between module source and
+// the module's precompiled functor, that is not the intention of the
+// feature and just serves to show that the precompiled functor is used.
+test('importHook returning a ModuleInstance with a precompiled functor', async t => {
+ t.plan(2);
+
+ const makeImportHook = makeNodeImporter({
+ 'https://example.com/precompiled.js': `
+ export const a = 0;
+ export let b = 0;
+ b = 666;
+ throw new Error('this should not run');
+ `,
+ 'https://example.com/main.js': `
+ import { a, b } from './precompiled.js';
+ t.is(a, 123);
+ t.is(b, 456);
+ `,
+ });
+ const importHook = makeImportHook('https://example.com');
+
+ const compartment = new Compartment(
+ { t },
+ {},
+ {
+ resolveHook: resolveNode,
+ importHook: async moduleSpecifier => {
+ if (moduleSpecifier === './precompiled.js') {
+ const baseRecord = await importHook(moduleSpecifier);
+ return {
+ ...baseRecord,
+ __syncModuleFunctor__: ({ onceVar, liveVar }) => {
+ onceVar.a(123);
+ liveVar.b(456);
+ },
+ };
+ }
+ return importHook(moduleSpecifier);
+ },
+ },
+ );
+
+ await compartment.import('./main.js');
+});

feat(ses): module execute uses syncModuleFunctor if present