feat(ses): Add ModuleStaticRecord
MODIFY
packages/ses-integration-test/scaffolding/rollup/rollup.config.test.js
packages/ses-integration-test/scaffolding/rollup/rollup.config.test.js
@@ -13,7 +13,12 @@ export default [
],
plugins: [
resolve({
- only: ["ses", "@agoric/make-hardener"]
+ only: [
+ "ses",
+ "@agoric/make-hardener",
+ "@agoric/transform-module",
+ "@agoric/babel-standalone"
+ ]
})
]
}

MODIFY
packages/ses/package.json
packages/ses/package.json
"demo": "http-server -o /demos"
},
"dependencies": {
- "@agoric/make-hardener": "0.0.8"
+ "@agoric/babel-standalone": "^7.9.5",
+ "@agoric/make-hardener": "0.0.8",
+ "@agoric/transform-module": "^0.3.0"
},
"devDependencies": {
"@agoric/test262-runner": "~0.1.0",

MODIFY
packages/ses/src/compartment-shim.js
packages/ses/src/compartment-shim.js
+// This module exports both Compartment and ModuleStaticRecord because they
+// communicate through the moduleAnalyses private side-table.
+/* eslint max-classes-per-file: ["error", 2] */
+
+import * as babel from '@agoric/babel-standalone';
+// We are using the above import form, and referring to its default export
+// explicitly below like babel.default, because the proper construct causes a
+// Rollup error.
+// This form:
+// import babel from '@agoric/babel-standalone';
+// And this variant:
+// import { default as babel } from '@agoric/babel-standalone';
+// Both produce:
+// Error: 'default' is not exported by .../@agoric/babel-standalone/babel.js
+import { makeModuleAnalyzer } from '@agoric/transform-module';
import { assign } from './commons.js';
import { createGlobalObject } from './global-object.js';
import { performEval } from './evaluate.js';
import { getCurrentRealmRec } from './realm-rec.js';
+const analyzeModule = makeModuleAnalyzer(babel.default);
+
+// moduleAnalyses are the private data of a ModuleStaticRecord.
+// We use moduleAnalyses in the loader/linker to look up
+// the analysis corresponding to any ModuleStaticRecord constructed by an
+// importHook.
+const moduleAnalyses = new WeakMap();
+
+/**
+ * ModuleStaticRecord captures the effort of parsing and analyzing module text
+ * so a cache of ModuleStaticRecords may be shared by multiple Compartments.
+ */
+export class ModuleStaticRecord {
+ constructor(text) {
+ const analysis = analyzeModule({ string: text });
+
+ this.imports = Object.keys(analysis.imports).sort();
+
+ Object.freeze(this);
+ Object.freeze(this.imports);
+
+ moduleAnalyses.set(this, analysis);
+ }
+
+ // eslint-disable-next-line class-methods-use-this
+ toString() {
+ return '[object ModuleStaticRecord]';
+ }
+
+ static toString() {
+ return 'function ModuleStaticRecord() { [shim code] }';
+ }
+}
+
/**
* Compartment()
* The Compartment constructor is a global. A host that wants to execute

MODIFY
packages/ses/src/intrinsics-global.js
packages/ses/src/intrinsics-global.js
@@ -94,6 +94,7 @@ const globalIntrinsicNames = [
'globalThis',
'Compartment',
+ 'ModuleStaticRecord',
'harden',
];

MODIFY
packages/ses/src/main.js
packages/ses/src/main.js
// limitations under the License.
export { lockdown } from './lockdown-shim.js';
-export { Compartment } from './compartment-shim.js';
+export { Compartment, ModuleStaticRecord } from './compartment-shim.js';

MODIFY
packages/ses/src/whitelist.js
packages/ses/src/whitelist.js
@@ -1535,5 +1535,14 @@ export default {
global: getter,
},
+ ModuleStaticRecord: {
+ '**proto**': 'FunctionPrototype',
+ prototype: 'ModuleStaticRecordPrototype',
+ },
+
+ ModuleStaticRecordPrototype: {
+ constructor: 'ModuleStaticRecord',
+ },
+
harden: fn,
};

ADD
packages/ses/test/module-static-record.test.js
+import tap from 'tap';
+import { ModuleStaticRecord } from '../src/main.js';
+
+const { test } = tap;
+
+test('module static record constructor', t => {
+ const msr = new ModuleStaticRecord(`
+ import foo from 'import-default-export-from-me.js';
+ import oof from 'import-default-export-from-me.js';
+ import * as bar from 'import-all-from-me.js';
+ import { fizz, buzz } from 'import-named-exports-from-me.js';
+ import { color as colour } from 'import-named-export-and-rename.js';
+
+ export let quuux = null;
+
+ export { qux } from 'import-and-reexport-name-from-me.js';
+ export * from 'import-and-export-all.js';
+ export default 42;
+ export const quux = 'Hello, World!';
+
+ // Late binding of an exported variable.
+ quuux = 'Hello, World!';
+ `);
+
+ t.equal(
+ '[object ModuleStaticRecord]',
+ msr.toString(),
+ 'instance string representation should be fixed',
+ );
+ t.equal(
+ 'function ModuleStaticRecord() { [shim code] }',
+ ModuleStaticRecord.toString(),
+ 'constructor string representation should be fixed',
+ );
+
+ t.deepEquals(
+ msr.imports,
+ [
+ 'import-all-from-me.js',
+ 'import-and-export-all.js',
+ 'import-and-reexport-name-from-me.js',
+ 'import-default-export-from-me.js',
+ 'import-named-export-and-rename.js',
+ 'import-named-exports-from-me.js',
+ ],
+ 'should capture sorted unique imports',
+ );
+
+ t.ok(Object.isFrozen(msr), 'ModuleStaticRecords should be frozen');
+ t.ok(
+ Object.isFrozen(msr.imports),
+ 'ModuleStaticRecord imports should be frozen',
+ );
+
+ t.end();
+});

MODIFY
yarn.lock
yarn.lock
resolved "https://registry.yarnpkg.com/@agoric/babel-parser/-/babel-parser-7.6.4.tgz#8dd5d36554cc758c29042713b5aa57dc58ee5273"
integrity sha512-3FQC3eP2hekhz1zn+2LcSL7tAG2dGgSqbmCeRfIFqhVS8bbE1hR7EvrC6jYlvqdU6yzlly43VykyRy9MHBvUAw==
+"@agoric/babel-standalone@^7.9.5":
+ version "7.9.5"
+ resolved "https://registry.yarnpkg.com/@agoric/babel-standalone/-/babel-standalone-7.9.5.tgz#1ca0c17844924199d31e49d6b67e8b2a629b8599"
+ integrity sha512-1Aa23oPuRi4kywUyZODo8zey9Gq2NpD2xUnNvgJLoT8orMQRlVOtvbG3JeHq5sjJERlF/q6csg4/P8t8/5IABA==
+
"@agoric/default-evaluate-options@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@agoric/default-evaluate-options/-/default-evaluate-options-0.3.0.tgz#c65f4342c286d8c7f912e1fc33c7b855bc615e3e"
@@ -9376,9 +9381,11 @@ serve-static@^1.12.4:
send "0.17.1"
"ses@file:packages/ses":
- version "0.7.6"
+ version "0.7.7"
dependencies:
+ "@agoric/babel-standalone" "^7.9.5"
"@agoric/make-hardener" "0.0.8"
+ "@agoric/transform-module" "^0.3.0"
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"

feat(ses): Add ModuleStaticRecord (#279)