From 1a68100df8fffe4bea169614b29048df9c379e03 Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Wed, 8 Jun 2022 18:00:51 -0700 Subject: [PATCH] Replace Loader with Global --- README.md | 244 ++++-------------------------------------------------- 1 file changed, 17 insertions(+), 227 deletions(-) diff --git a/README.md b/README.md index f8622a7..25b638d 100644 --- a/README.md +++ b/README.md @@ -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, - - // 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 - - // 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 - - // 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; - - // 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 @@ -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, + importMeta?: Object, + global?: Global, + } ); // Links the module instance with another module instance @@ -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; get namespace(): ModuleExportsNamespace;