Skip to content

Commit

Permalink
Replace Loader with Global
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Jun 9, 2022
1 parent 75bac2d commit 1a68100
Showing 1 changed file with 17 additions and 227 deletions.
244 changes: 17 additions & 227 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,221 +225,12 @@ interface StaticModuleRecord {
needsImportMeta: boolean;
}

// A ModuleDescriptor captures a static module record and per-loader metadata.
type ModuleDescriptor =

// Describes a module by referring to a *reusable* record of the compiled
// source.
// The static module record captures a static analysis
// of the `import` and `export` bindings and whether the source ever utters
// the keywords `import` or `import.meta`.
// The loader will then asynchronously _load_ the shallow dependencies
// of the module and memoize the promise for the
// result of loading the module and its transitive dependencies.
// If the loader _imports_ the module, it will generate and memoize
// a module instance and initialize the module.
// To initialize the module, the loader will construct an `import.meta` object.
// If the source utters `import.meta`,
// * The loader will construct an `importMeta` object with a null prototype.
// * If the module descriptor has an `importMeta` property, the loader
// will copy the own properties of the descriptor's `importMeta` over
// the loader's `importMeta' using `[[Set]]`.
// * If the loader has an `importMetaHook`, it will call
// `importMetaHook(fullSpecifier, importMeta)`.
// The loader will then begin initializing the module.
// The loader memoizes a promise for the module exports namespace
// that will be fulfilled at a time already defined in 262 for dynamic import.
| {
record: StaticModuleRecord,

// Properties to copy to the `import.meta` of the resulting module instance,
// if the source mentions `import.meta`.
importMeta?: Object,
}

// Describes a module by a *reusable* third-party static module record.
// When the loader _loads_ the module, it will use the bindings array
// of the third-party static module record to discover shallow dependencies
// in lieu of compiling the source, and otherwise behaves identically
// to { source } descriptors.
// When the loader _imports_ the module, it will construct a
// [[ModuleEnvironmentRecord]] and [[ModuleExportsNamespace]] based
// entirely on the bindings array of the third-party static module record.
// If the third-party static-module-record has a true `needsImport`, the
// loader will construct an `import` that resolves
// the given import specifier relative to the module instance's full specifier
// and returns a promise for the module exports namespace of the
// imported module.
// If the third-party static-module-record has a true `needsImportMeta`
// property, the loader will construct an `importMeta` by the
// same process as any { source } module.
// `importMeta` will otherwise be `undefined`.
// The loader will then initialize the module by calling the
// `initialize` function of the third-party static module record,
// giving it the module environment record and an options bag containing
// either `import` or `importMeta` if needed.
// The loader memoizes a promise for the module exports namespace
// that is fulfilled as already specified in 262 for dynamic import,
// where the behavior if the initialize function is async is analogous to
// top-level-await for a module compiled from source.
| {
record: SyntheticStaticModuleRecord,

// Properties to copy to the `import.meta` of the resulting module instance,
// if the `record` has a `true` `needsImportMeta` property.
importMeta?: Object,
}

// Use a static module record from the loader in which this loader
// was constructed.
// If the loader _loads_ the corresponding module, the static
// module record will be synchronously available only if it is already
// in the parent loader's synchronous memo.
// Otherwise, loading induces the parent loader to load the module,
// but does not induce the parent loader to load the module's transitive
// dependencies.
// This loader then resolves the shallow dependencies according
// to its own resolveHook and loads the consequent transitive dependencies.
// If the loader _imports_ the module, the behavior is equivalent
// to loading for the retrieved static module record or third-party static
// module record.
| {
record: string,

// Properties to copy to the `import.meta` of the resulting module instance.
importMeta?: Object,
}

// FOR SHARED MODULE INSTANCES:

// To create an alias to an existing module instance in this loader:
| {
// A full specifier for another module in this loader.
instance: string,
}

// To create an alias to an existing module instance given the full specifier
// of the module in a different loader:
| {
instance: string,

// A loader instance.
// We do not preclude the possibility that loader is the same loader,
// as that is possible to achieve in a `loadHook`.
loader: Loader,
}

// To create an alias to an existing module instance given a module exports
// namespace object that can pass a brand check:
| {
namespace: ModuleExportsNamespace,
}

// If the given namespace does not pass a ModuleExportsNamespace brandcheck,
// the loader will not support live bindings to the properties of that
// object, but will instead produce an emulation of a module exports namespace,
// using a frozen snapshot of the own properties of the given object.
// The module exports namespace for this module does not reflect any future
// changes to the shape of the given object.
| {
// Any object that does not pass a module exports namespace brand check:
namespace: ^ModuleExportsNamespace,
};

type LoaderConstructorOptions = {
// The loader uses the resolveHook to synchronously elevate
// an import specifier (as it appears in the source of a StaticModuleRecord
// or bindings array of a third-party StaticModuleRecord), to
// the corresponding full specifier, given the full specifier of the
// referring (containing) module.
// The full specifier is the memo key for the module.
// TODO This proposal does not yet account for import assertions,
// which evidence shows are actually import type specifiers as they were
// originally designed and may need to also participate in the memo key.
resolveHook: (importSpecifier: string, referrerSpecifier: string) => string,

// The loader's load and import methods may need to load or initialize
// additional modules.
// If the loader does not have a module on hand,
// it will first consult the `modules` object for a descriptor of the needed module.
modules?: Record<string, ModuleDescriptor>,

// If loading or importing a module misses the loader's memo and the
// `modules` table, the loader calls the asynchronous `loadHook`.
// Note: This name differs from the implementation of SES shim and a
// prior revision of this proposal, where it is currently called `importHook`.
loadHook?: (fullSpecifier: string) => Promise<ModuleDescriptor?>

// A ModuleDescriptor can have an `importMeta` property.
// The loader assigns these properties over the true `import.meta`
// object for the module instance.
// That object in turn has a null prototype.
// However some properties of `import.meta` are too expensive for a host
// to instantiate for every module, like Node.js's `import.meta.resolve`,
// which can be avoided for modules that never utter `import.meta`
// in their sources.
// This hook gets called for any module that utters `import.meta`,
// so some work can be deferred until just before the loader
// initializes the module.
importMetaHook?: (fullSpecifier: string, importMeta: Object) => void,

// Causes the guest loader to create its own globalThis instead of sharing
// its host's.
// The constructor copies the own properties over its own globalThis by
// assignment.
globals: Object,
};

interface Loader {
// Note: This single-argument form differs from earlier proposal versions,
// implementations of SES shim, and Moddable's XS, which accept three arguments,
// including a final options bag.
constructor(options?: LoaderConstructorOptions): Loader;

// Accessor for this loader's globals.
globalThis: Object,

// Evaluates a program program using this loader's associated global
// environment record.
// A subsequent proposal might add options to either the loader
// constructor or the evaluate method to persist the global contour
// between calls to evaluate, making loaders a suitable
// tool for a REPL, such that a const or let binding from one evaluation
// persists to the next.
// Subsequent proposals might afford other modes, like sloppy globals mode.
// TODO is sloppyGlobalsMode only sensible in the context of the shim?
evaluate(source: string): any;

// load causes a loader to load module descriptors for the
// transitive dependencies of a specified module into its
// memo, but does not initialize any modules.
// The load function is useful for tools like bundlers and importmap
// generators that load a module graph in an emulated host environment
// but cannot and should not emulate evaluation.
async load(fullSpecifier: string): Promise<void>

// import induces the asynchronous load phase and then initializes
// the given module and any of its transitive dependencies that
// have not already begun initialization.
async import(fullSpecifier: string): Promise<ModuleExportsNamespace>;

// TC53: some embedded systems hosts exclude promises and so require these
// synchronous variants of import and load.
// On hosts where these functions cannot succeed synchronously,
// they must throw an error without the side effect of initializing any
// additional modules.
// For modules that have a top-level await, these would return
// after the module first awaits but not after any subsequent promise queue
// job.

// If a host supports both importNow and import, they must share
// a memo of full specifiers to promises for the module exports namespace.
// importNow and loadNow followed by import or load must not accidentally
// reinitialize the underlying module or produce different namespaces.

importNow(fullSpecifier: string): ModuleExportsNamespace;

loadNow(fullSpecifier: string): void;
// Constructs a global environment with its own globalThis containing
// unique bindings for `eval` and `Function`.
interface Global {
constructor();
get globalThis: Object;
evaluate(string): unknown;
}

// ModuleInstance reifies an entangled pair of module environment record
Expand All @@ -450,16 +241,18 @@ interface ModuleInstance {
// Creates a module instance for either a static module record
// or a synthetic static module record.
// Creates a module environment record that defers to the
// global environment record of the designated loader.
// If the static module record needs a dynamic import behavior,
// creates a function that resolves import specifiers relative
// to its own referrer specifier and delegates to the
// attached loader.
// designated global environment record or that associated
// with the ModuleInstance constructor.
// The module instance will delegate to the given dynamic import
// and will use the given global for all eval and Function constructor
// behaviors.
constructor(
specifier: string,
record: StaticModuleRecord | SyntheticStaticModuleRecord,
loader: Loader,
importMeta?: Object,
{
import?: (importSpecifier: string) => Promise<ModuleExportsNamespace>,
importMeta?: Object,
global?: Global,
}
);

// Links the module instance with another module instance
Expand All @@ -472,10 +265,7 @@ interface ModuleInstance {
// The module instances for the given module environment record
// and their transitive module instance dependencies must be fully linked.
// Defers to the initialize function of a synthetic static module record,
// reifying its own module environment record.
// TODO must all instances be bound in the same global environment record?
// Returns a promise if the static module record uses top-level-await
// or if the synthatic static module record initializer returns a promise.
// reifying and providing its own module environment record.
initialize(): void | Promise<void>;

get namespace(): ModuleExportsNamespace;
Expand Down

0 comments on commit 1a68100

Please sign in to comment.