Skip to content

Instantly share code, notes, and snippets.

@jfparadis
Last active January 22, 2020 20:04
Show Gist options
  • Save jfparadis/21c16afae0716781754ac8a980db1db2 to your computer and use it in GitHub Desktop.
Save jfparadis/21c16afae0716781754ac8a980db1db2 to your computer and use it in GitHub Desktop.
SESCompartment

A Compartment object abstracts the notion of a distinct global environment, with its own global object, copy of the standard library, but shares the "intrinsics" (standard objects that are not bound to global variables, like the initial value of Object.prototype) with the current execution environment.

Compartment Constructor

class Compartment {
  constructor: (
    globals : object?,                  // extra globals added to the global object
    modules: object?,                   // module map, specifier to specifier
  ) -> object,

  // public methods
  import(specifier: string) -> promise, // same argument/return value as dynamic import

  // inherited accessor properties
  get global() -> object,               // access this compartment global object
}

Running

Place all files in a moddable examples/js/compartments/SESCompartment folder then open XSBug, cd into the directory from termial, and run mcconfig -m -d at the command-line.

Output

[object Object]mod1
g1 original value
mod1 done.
//trace(Object.keys(Compartment.map));
import SESCompartment from 'SESCompartment';
let g1Value = 'g1 original value';
// This shim only support mapping for referrer "*". Although it works, it
// makes little sense to specify anything else but bare and absolute
// specifiers with referrer "*".
const cmp = new SESCompartment({}, { '*' : { './mod1': './mod1' } });
// Populate the global before any module is loaded.
cmp.global.g1 = g1Value;
// Load the module.
cmp.import('./mod1');
{
"include": "$(MODDABLE)/examples/manifest_base.json",
"modules": {
"main": "./main",
"_mod1": "./mod1",
"SESCompartment": "./SESCompartment"
}
}
trace("mod1\n");
trace(globalThis.g1 + '\n');
globalThis.g1 = 'new value 1';
trace('mod1 done.\n');
// Resolve utility function.
// Convert "userland" specifier to XS specifier.
// This is a bare-bone function for this simple use case.
function resolve(specifier) {
return specifier.replace('./', '_');
}
export default class SESCompartment {
#instance;
constructor(globals, modules) {
// Use the native XS Compartment API as the engine.
this.#instance = new Compartment(
"SESCompartment",
globals, // No need to change globals.
this.#toXSMap(modules),
);
}
get global() {
return this.#instance.global;
}
async import(specifier) {
return this.#instance.export.SES.import(specifier);
}
// Internal API
#toXSMap(modules) {
const result = { SESCompartment: Compartment.map.SESCompartment };
const referer = '*';
const map = modules[referer];
for (const [specifier, location] of Object.entries(map)) {
result[specifier] = Compartment.map[resolve(location)];
}
trace(result);
return result;
}
}
// Make SESCompartment a global.
Object.defineProperties(globalThis, {
SESCompartment: {
value: SESCompartment,
enumerable: false,
configurable: true,
writable: true,
}
});
// Exposes import from inside the compartment used as the engine.
export const SES = {
async import(specifier) {
return import(resolve(specifier));
}
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment