Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@masknet/compartment

Package Overview
Dependencies
Maintainers
2
Versions
24
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@masknet/compartment - npm Package Compare versions

Comparing version 0.4.2 to 0.5.0

dist/commonjs/runtime.d.ts

1

dist/index.d.ts

@@ -6,2 +6,3 @@ export type { Binding, ImportBinding, ImportAllBinding, ExportBinding, ExportAllBinding, VirtualModuleRecord, VirtualModuleRecordExecuteContext, ModuleNamespace, ImportHook, ImportMetaHook, ModuleHandler, } from './types.js';

export { makeGlobalThis } from './utils/makeGlobalThis.js';
export { type CommonJSExportReflect, type CommonJSGlobalLexicals, type CommonJSHandler, type CommonJSInitialize, CommonJSModule, type RequireHook as CommonJSRequireHook, requires as commonjsRequires, } from './commonjs/runtime.js';
//# sourceMappingURL=index.d.ts.map

@@ -6,5 +6,11 @@ import { ModuleSource } from './ModuleSource.js';

#private;
/**
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-module
*/
constructor(moduleSource: ModuleSource<T> | VirtualModuleRecord, handler: ModuleHandler);
/**
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-module.prototype.source
*/
get source(): ModuleSource | VirtualModuleRecord | null;
}
//# sourceMappingURL=Module.d.ts.map

8

package.json
{
"name": "@masknet/compartment",
"version": "0.4.2",
"version": "0.5.0",
"type": "module",

@@ -8,2 +8,6 @@ "main": "./dist/index.js",

"sideEffects": false,
"repository": {
"type": "git",
"url": "git+https://github.com/DimensionDev/aot-secure-ecmascript"
},
"exports": {

@@ -17,3 +21,3 @@ "types": "./dist/index.d.ts",

"devDependencies": {
"@swc/core": "^1.3.11"
"@swc/core": "^1.3.53"
},

@@ -20,0 +24,0 @@ "files": [

@@ -9,4 +9,3 @@ # @masknet/compartment

- [ECMA262 Normative PR: Layering: Add HostLoadImportedModule hook](https://github.com/tc39/ecma262/pull/2905/)
- [Module Block proposal](https://tc39.es/proposal-js-module-blocks/)
- [Module Expressions proposal](https://tc39.es/proposal-module-expressions/)
- [Compartment proposal: Layer 0: Module and ModuleSource constructor][layer-0]

@@ -73,3 +72,3 @@ - [Compartment proposal: Layer 1: ModuleSource reflection][layer-1]

}
const module = new Evaluators.Module(virtualModule, import.meta.url, () => null)
const module = new evaluators.Module(virtualModule, import.meta.url, () => null)
const moduleNamespace = await imports(module)

@@ -76,0 +75,0 @@ moduleNamespace.x // 42

@@ -75,3 +75,3 @@ import { Module, setParentGlobalThis, setParentImportHook, setParentImportMetaHook } from './Module.js'

}
#GetImportMeta(): ImportMeta | null {
#GetImportMeta(): object | null {
if (this.#AssignedImportMeta) return this.#AssignedImportMeta

@@ -78,0 +78,0 @@ if (this.#ParentEvaluator) return this.#ParentEvaluator.#GetImportMeta()

@@ -19,1 +19,11 @@ export type {

export { makeGlobalThis } from './utils/makeGlobalThis.js'
export {
type CommonJSExportReflect,
type CommonJSGlobalLexicals,
type CommonJSHandler,
type CommonJSInitialize,
CommonJSModule,
type RequireHook as CommonJSRequireHook,
requires as commonjsRequires,
} from './commonjs/runtime.js'

@@ -36,14 +36,15 @@ import { ModuleSource } from './ModuleSource.js'

export class Module<T extends ModuleNamespace = any> {
// The constructor is equivalent to ParseModule in SourceTextModuleRecord
// https://tc39.es/ecma262/#sec-parsemodule
/**
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-module
*/
constructor(moduleSource: ModuleSource<T> | VirtualModuleRecord, handler: ModuleHandler) {
// Note: we act as CSP enabled, therefore no ModuleSource instance can be created.
if (typeof moduleSource !== 'object') throw new TypeError('moduleSource must be an object')
// impossible to create a ModuleSource instance
if (moduleSource instanceof ModuleSource) assertFailed('ModuleSource instance cannot be created')
// At this point, the only possible & valid module source is a VirtualModuleRecord.
const module = normalizeVirtualModuleRecord(moduleSource)
if (handler === null) throw new TypeError('handler must not be null')
let importHook: ImportHook | undefined
let importMetaHook: ImportMetaHook | undefined
if (typeof handler === 'object') {
if (typeof handler === 'object' && handler) {
importHook = handler.importHook

@@ -55,13 +56,16 @@ if (typeof importHook !== 'function' && importHook !== undefined)

throw new TypeError('importMetaHook must be a function')
}
} else if (handler === undefined) {
importHook = undefined
importMetaHook = undefined
} else throw new TypeError('handler must be an object or undefined')
this.#VirtualModuleSource = moduleSource
this.#Execute = module.execute
this.#NeedsImport = module.needsImport
this.#NeedsImportMeta = module.needsImportMeta
this.#Layer2_VirtualModuleSource = moduleSource
this.#Layer2_Execute = module.execute
this.#Layer2_NeedsImport = module.needsImport
this.#Layer2_NeedsImportMeta = module.needsImportMeta
this.#HasTLA = !!module.isAsync
this.#ImportHook = importHook
this.#ImportMetaHook = importMetaHook
this.#HandlerValue = handler
this.#Layer0_ImportHook = importHook
this.#Layer0_ImportMetaHook = importMetaHook
this.#Layer0_HandlerValue = handler

@@ -76,139 +80,83 @@ const { importEntries, indirectExportEntries, localExportEntries, requestedModules, starExportEntries } =

}
/**
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-module.prototype.source
*/
get source(): ModuleSource | VirtualModuleRecord | null {
return this.#VirtualModuleSource
return this.#Layer2_VirtualModuleSource as VirtualModuleRecord
}
//#region ModuleRecord fields https://tc39.es/ecma262/#table-module-record-fields
/** first argument of execute() */
//#region ModuleRecord
/**
* The Environment Record containing the top level bindings for this module.
* This field is set when the module is linked.
* https://tc39.es/ecma262/#table-module-record-fields
*/
#Environment: object | undefined
/** result of await import(mod) */
/**
* The Module Namespace Object (28.3) if one has been created for this module.
* https://tc39.es/ecma262/#table-module-record-fields
*/
#Namespace: ModuleNamespace | undefined
//#endregion
//#region VirtualModuleRecord fields
// *this value* when calling #Execute.
#VirtualModuleSource: VirtualModuleRecord
#Execute: VirtualModuleRecord['execute']
#NeedsImportMeta: boolean | undefined
#NeedsImport: boolean | undefined
#ContextObject: VirtualModuleRecordExecuteContext | undefined
#ImportHook: ImportHook | undefined
#ImportMetaHook: ImportMetaHook | undefined
#HandlerValue: ModuleHandler
/** the global environment this module binds to */
#GlobalThis: object = globalThis
#ParentImportHook: ImportHook = defaultImportHook
#ParentImportMetaHook: ImportMetaHook | undefined
/** imported module cache */
#ImportEntries: ModuleImportEntry[]
#LocalExportEntries: ModuleExportEntry[]
#IndirectExportEntries: ModuleExportEntry[]
#StarExportEntries: ModuleExportEntry[]
/** Where local export stored */
#LocalExportedValues = new Map<string, unknown>()
/** Callback to update live exports */
#non_std_ExportCallback = new Map<string, Set<(newValue: any) => void>>()
//#endregion
//#region VirtualModuleRecord methods
#non_std_AddLiveExportCallback(name: string, callback: (newValue: any) => void) {
if (!this.#non_std_ExportCallback.has(name)) this.#non_std_ExportCallback.set(name, new Set())
this.#non_std_ExportCallback.get(name)!.add(callback)
}
//#endregion
//#region ModuleRecord methods https://tc39.es/ecma262/#table-abstract-methods-of-module-records
// https://tc39.es/ecma262/#sec-getexportednames
#GetExportedNames(exportStarSet: Module[] = []): string[] {
const module = this
if (!(module.#Status !== ModuleStatus.new)) assertFailed()
if (exportStarSet.includes(module)) return []
exportStarSet.push(module)
const exportedNames: string[] = []
for (const e of module.#LocalExportEntries) {
if (!(e.ExportName !== null)) assertFailed()
exportedNames.push(e.ExportName)
}
for (const e of module.#IndirectExportEntries) {
if (!(e.ExportName !== null)) assertFailed()
exportedNames.push(e.ExportName)
}
for (const e of module.#StarExportEntries) {
if (!(e.ModuleRequest !== null)) assertFailed()
const requestedModule = Module.#GetImportedModule(module, e.ModuleRequest)
const starNames = requestedModule.#GetExportedNames(exportStarSet)
for (const n of starNames) {
if (n === 'default') continue
if (exportedNames.includes(n)) continue
exportedNames.push(n)
}
}
return exportedNames
}
// https://tc39.es/ecma262/#sec-resolveexport
#ResolveExport(
exportName: string,
resolveSet: { module: Module; exportName: string }[] = [],
): typeof ambiguous | { module: Module; bindingName: string | typeof namespace } | null {
const module = this
if (!(module.#Status !== ModuleStatus.new)) assertFailed()
for (const r of resolveSet) {
if (module === r.module && exportName === r.exportName) {
// Assert: This is a circular import request.
return null
}
}
resolveSet.push({ module, exportName })
for (const e of module.#LocalExportEntries) {
if (exportName === e.ExportName) {
// if (!(e.LocalName !== null)) assertFailed()
// return { module, bindingName: e.LocalName }
return { module, bindingName: e.ExportName }
}
}
for (const e of module.#IndirectExportEntries) {
if (exportName === e.ExportName) {
if (!(e.ModuleRequest !== null)) assertFailed()
const importedModule = Module.#GetImportedModule(module, e.ModuleRequest)
if (e.ImportName === all) {
// Assert: module does not provide the direct binding for this export.
return { module: importedModule, bindingName: namespace }
} else {
if (!(typeof e.ImportName === 'string')) assertFailed()
return importedModule.#ResolveExport(e.ImportName, resolveSet)
}
}
}
if (exportName === 'default') {
// Assert: A default export was not explicitly provided by this module.
// Note: A default export cannot be provided by an export * from "mod" declaration.
return null
}
let starResolution: null | { module: Module; bindingName: string | typeof namespace } = null
for (const e of module.#StarExportEntries) {
if (!(e.ModuleRequest !== null)) assertFailed()
const importedModule = Module.#GetImportedModule(module, e.ModuleRequest)
let resolution = importedModule.#ResolveExport(exportName, resolveSet)
if (resolution === ambiguous) return ambiguous
if (resolution !== null) {
if (starResolution === null) starResolution = resolution
else {
// Assert: There is more than one * import that includes the requested name.
if (resolution.module !== starResolution.module) return ambiguous
if (
(resolution.bindingName === namespace && starResolution.bindingName !== namespace) ||
(resolution.bindingName !== namespace && starResolution.bindingName === namespace)
)
return ambiguous
if (
typeof resolution.bindingName === 'string' &&
typeof starResolution.bindingName === 'string' &&
resolution.bindingName !== starResolution.bindingName
) {
return ambiguous
}
}
}
}
return starResolution
}
//#region CyclicModuleRecord
/**
* Initially new. Transitions to unlinked, linking, linked, evaluating, possibly evaluating-async, evaluated (in that order) as the module progresses throughout its lifecycle. evaluating-async indicates this module is queued to execute on completion of its asynchronous dependencies or it is a module whose [[HasTLA]] field is true that has been executed and is pending top-level completion.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#Status = ModuleStatus.new
/**
* A throw completion representing the exception that occurred during evaluation. undefined if no exception occurred or if [[Status]] is not evaluated.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#EvaluationError: unknown | empty = empty
/**
* Auxiliary field used during Link and Evaluate only. If [[Status]] is linking or evaluating, this non-negative number records the point at which the module was first visited during the depth-first traversal of the dependency graph.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#DFSIndex: number | empty = empty
/**
* Auxiliary field used during Link and Evaluate only. If [[Status]] is linking or evaluating, this is either the module's own [[DFSIndex]] or that of an "earlier" module in the same strongly connected component.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#DFSAncestorIndex: number | empty = empty
/**
* A List of all the ModuleSpecifier strings used by the module represented by this record to request the importation of a module. The List is in source text occurrence order.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#RequestedModules: string[]
/**
* A map from the specifier strings used by the module represented by this record to request the importation of a module to the resolved Module Record. The list does not contain two different Records with the same [[Specifier]].
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#LoadedModules = new Map<string, Module>()
/**
* The first visited module of the cycle, the root DFS ancestor of the strongly connected component. For a module not in a cycle this would be the module itself. Once Evaluate has completed, a module's [[DFSAncestorIndex]] is equal to the [[DFSIndex]] of its [[CycleRoot]].
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#CycleRoot: Module | undefined
/**
* Whether this module is individually asynchronous (for example, if it's a Source Text Module Record containing a top-level await). Having an asynchronous dependency does not mean this field is true. This field must not change after the module is parsed.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#HasTLA: boolean
/**
* Whether this module is either itself asynchronous or has an asynchronous dependency. Note: The order in which this field is set is used to order queued executions, see 16.2.1.5.3.4.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#AsyncEvaluation = false
/**
* If this module is the [[CycleRoot]] of some cycle, and Evaluate() was called on some module in that cycle, this field contains the PromiseCapability Record for that entire evaluation. It is used to settle the Promise object that is returned from the Evaluate() abstract method. This field will be empty for any dependencies of that module, unless a top-level Evaluate() has been initiated for some of those dependencies.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#TopLevelCapability: PromiseCapability<void> | undefined
/**
* If this module or a dependency has [[HasTLA]] true, and execution is in progress, this tracks the parent importers of this module for the top-level execution job. These parent modules will not start executing before this module has successfully completed execution.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#AsyncParentModules: Module[] = []
/**
* If this module has any asynchronous dependencies, this tracks the number of asynchronous dependency modules remaining to execute for this module. A module with asynchronous dependencies will be executed when this field reaches 0 and there are no execution errors.
* https://tc39.es/ecma262/#sec-cyclic-module-records
*/
#PendingAsyncDependencies: number | empty = empty
/** https://tc39.es/ecma262/#sec-LoadRequestedModules */
#LoadRequestedModules(HostDefined: Task) {

@@ -227,2 +175,3 @@ const module = this

}
/** https://tc39.es/ecma262/#sec-InnerModuleLoading */
static #InnerModuleLoading(state: GraphLoadingState, module: Module) {

@@ -235,2 +184,4 @@ if (!state.IsLoading) assertFailed()

for (const required of module.#RequestedModules) {
// i. If module.[[LoadedModules]] contains a Record whose [[Specifier]] is required, then
// 1. Let record be that Record.
const record = module.#LoadedModules.get(required)

@@ -240,3 +191,4 @@ if (record) {

} else {
Module.#LoadImportedModule(module, required, state.HostDefined, state)
Module.#Layer0_LoadImportedModule(module, required, state.HostDefined, state)
// 2. NOTE: HostLoadImportedModule will call FinishLoadingImportedModule, which re-enters the graph loading process through ContinueModuleLoading.
}

@@ -256,2 +208,3 @@ if (!state.IsLoading) return

}
/** https://tc39.es/ecma262/#sec-ContinueModuleLoading */
static #ContinueModuleLoading(state: GraphLoadingState, moduleCompletion: Completion<Module>) {

@@ -265,181 +218,3 @@ if (!state.IsLoading) return

}
//#endregion
//#region CyclicModuleRecord fields https://tc39.es/ecma262/#sec-cyclic-module-records
#Status = ModuleStatus.new
#EvaluationError: unknown | empty = empty
#DFSIndex: number | empty = empty
#DFSAncestorIndex: number | empty = empty
#RequestedModules: string[]
#LoadedModules = new Map<string, Module>()
#LoadingModules = new Map<string, Set<GraphLoadingState | PromiseCapability<ModuleNamespace>>>()
#LoadStates = new Set<GraphLoadingState | PromiseCapability<ModuleNamespace>>()
#CycleRoot: Module | undefined
#HasTLA: boolean
#AsyncEvaluation = false
#__AsyncEvaluationPreviouslyTrue = false
#TopLevelCapability: PromiseCapability<void> | undefined
#AsyncParentModules: Module[] = []
#PendingAsyncDependencies: number | empty = empty
//#endregion
//#region CyclicModuleRecord methods https://tc39.es/ecma262/#table-cyclic-module-methods
// https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
#InitializeEnvironment() {
const module = this
for (const e of module.#IndirectExportEntries) {
if (!(e.ExportName !== null)) assertFailed()
const resolution = module.#ResolveExport(e.ExportName)
if (resolution === null || resolution === ambiguous) {
throw new SyntaxError(`Module '${e.ModuleRequest}' does not provide an export ${e.ExportName}`)
}
}
// Assert: All named exports from module are resolvable.
const env = { __proto__: null }
module.#ContextObject = createContextObject()
module.#Environment = env
const propertiesToBeDefined: PropertyDescriptorMap = {
__proto__: null!,
}
for (const i of module.#ImportEntries) {
const importedModule = Module.#GetImportedModule(module, i.ModuleRequest)
// import * as ns from '..'
if (i.ImportName === namespace) {
const namespaceObject = Module.#GetModuleNamespace(importedModule)
propertiesToBeDefined[i.LocalName] = { value: namespaceObject, enumerable: true }
} else {
const resolution = importedModule.#ResolveExport(i.ImportName)
if (resolution === null)
throw new SyntaxError(`${i.ModuleRequest} does not provide export ${i.ImportName}`)
if (resolution === ambiguous)
throw new SyntaxError(`${i.ModuleRequest} does not provide an unambiguous export ${i.ImportName}`)
// import { x } from '...' where x is a "export * as ns from '...'"
if (resolution.bindingName === namespace) {
const namespaceObject = Module.#GetModuleNamespace(resolution.module)
propertiesToBeDefined[i.LocalName] = { value: namespaceObject, enumerable: true }
} else {
resolution.module.#non_std_AddLiveExportCallback(i.ImportName, (newValue) => {
Object.defineProperty(env, i.LocalName, {
value: newValue,
configurable: true,
enumerable: true,
})
})
if (resolution.module.#LocalExportedValues.has(resolution.bindingName)) {
propertiesToBeDefined[i.LocalName] = {
configurable: true,
enumerable: true,
value: resolution.module.#LocalExportedValues.get(resolution.bindingName),
}
} else {
propertiesToBeDefined[i.LocalName] = {
get() {
throw new ReferenceError(`Cannot access '${i.LocalName}' before initialization`)
},
configurable: true,
enumerable: true,
}
}
}
}
}
for (const { ModuleRequest, ExportName, ImportName } of module.#LocalExportEntries) {
if (!(ModuleRequest === null && typeof ExportName === 'string' && ImportName === null)) assertFailed()
propertiesToBeDefined[ExportName] = {
get: () => this.#LocalExportedValues.get(ExportName),
set: (value) => {
this.#LocalExportedValues.set(ExportName, value)
this.#non_std_ExportCallback.get(ExportName)?.forEach((callback) => callback(value))
return true
},
// Note: export property should not be enumerable?
// but it will crash Chrome devtools. See: https://bugs.chromium.org/p/chromium/issues/detail?id=1358114
enumerable: true,
}
}
Object.defineProperties(env, propertiesToBeDefined)
for (const exports of module.#GetExportedNames()) {
if (module.#ResolveExport(exports) === ambiguous) {
throw new SyntaxError(`Module has multiple exports named '${exports}'`)
}
}
// TODO: https://github.com/tc39/proposal-compartments/issues/70
// prevent access to global env until [[ExecuteModule]]
Object.setPrototypeOf(env, opaqueProxy)
}
/** All call to ExecuteModule must use Task.run to keep the call stack continue */
#ExecuteModule(promise?: PromiseCapability<void>) {
const execute = this.#Execute
if (!execute) return
this.#Execute = undefined
// prepare context
this.#ContextObject!.globalThis = this.#GlobalThis as any
if (this.#NeedsImportMeta) {
const importMeta = { __proto__: null }
if (this.#ImportMetaHook) Reflect.apply(this.#ImportMetaHook, this.#HandlerValue, [importMeta])
else if (this.#ParentImportMetaHook) Reflect.apply(this.#ParentImportMetaHook, undefined, [importMeta])
this.#ContextObject!.importMeta = importMeta
}
if (this.#NeedsImport) {
this.#ContextObject!.import = async (
specifier: string | Module<ModuleNamespace>,
options?: ImportCallOptions,
) => {
const referrer = this
const promiseCapability = PromiseCapability<ModuleNamespace>()
let hasModuleInternalSlot = false
try {
;(specifier as Module).#HandlerValue
hasModuleInternalSlot = true
} catch {}
if (hasModuleInternalSlot) {
const hostDefined = createTask(`import(<module block>)`)
Module.#ContinueDynamicImport(promiseCapability, NormalCompletion(specifier as Module), hostDefined)
} else {
specifier = String(specifier)
const hostDefined = createTask(`import("${specifier}")`)
if (referrer.#LoadedModules.has(specifier)) {
Module.#ContinueDynamicImport(
promiseCapability,
NormalCompletion(referrer.#LoadedModules.get(specifier)!),
hostDefined,
)
} else {
Module.#LoadImportedModule(referrer, specifier, hostDefined, promiseCapability)
}
}
return promiseCapability.Promise as any
}
}
if (!this.#Environment) assertFailed()
const env = new Proxy(this.#Environment, moduleEnvExoticMethods)
if (!this.#HasTLA) {
if (promise) assertFailed()
const result = Reflect.apply(execute, this.#VirtualModuleSource, [env, this.#ContextObject])
if (result)
throw new TypeError(
'Due to specification limitations, in order to support Async Modules (modules that use Top Level Await or a Virtual Module that has an execute() function that returns a Promise), the Virtual Module record must be marked with `isAsync: true`. The `isAsync` property is non-standard, and it is being tracked in https://github.com/tc39/proposal-compartments/issues/84.',
)
} else {
if (!promise) assertFailed()
Promise.resolve(Reflect.apply(execute, this.#VirtualModuleSource, [env, this.#ContextObject])).then(
promise.Resolve,
promise.Reject,
)
}
}
// https://tc39.es/ecma262/#sec-moduledeclarationlinking
/** https://tc39.es/ecma262/#sec-moduledeclarationlinking */
#Link() {

@@ -471,41 +246,3 @@ const module = this

}
// https://tc39.es/ecma262/#sec-moduleevaluation
#Evaluate(HostDefined: Task) {
let module: Module = this
// TODO: Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent.
if (![ModuleStatus.linked, ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(module.#Status))
assertFailed()
if ([ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(module.#Status)) {
module = module.#CycleRoot!
if (!module) assertFailed() // TODO: https://github.com/tc39/ecma262/issues/2823
}
if (module.#TopLevelCapability) return module.#TopLevelCapability.Promise
const stack: Module[] = []
const capability = PromiseCapability<void>()
module.#TopLevelCapability = capability
try {
Module.#InnerModuleEvaluation(module, stack, 0, HostDefined)
} catch (err) {
for (const m of stack) {
if (!(m.#Status === ModuleStatus.evaluating)) assertFailed()
m.#Status = ModuleStatus.evaluated
m.#EvaluationError = err
}
if (!(module.#Status === ModuleStatus.evaluated)) assertFailed()
if (!(module.#EvaluationError === err)) assertFailed()
capability.Reject(err)
return capability.Promise
}
if (![ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(module.#Status)) assertFailed()
if (!(module.#EvaluationError === empty)) assertFailed()
if (module.#AsyncEvaluation === false) {
if (!(module.#Status === ModuleStatus.evaluated)) assertFailed()
capability.Resolve()
}
if (!(stack.length === 0)) assertFailed()
return capability.Promise
}
// https://tc39.es/ecma262/#sec-InnerModuleLinking
/** https://tc39.es/ecma262/#sec-InnerModuleLinking */
static #InnerModuleLinking(module: Module, stack: Module[], index: number) {

@@ -562,4 +299,39 @@ if (

}
// https://tc39.es/ecma262/#sec-InnerModuleEvaluation
/** https://tc39.es/ecma262/#sec-moduleevaluation */
#Evaluate(HostDefined: Task) {
let module: Module = this
// TODO: Assert: This call to Evaluate is not happening at the same time as another call to Evaluate within the surrounding agent.
if (![ModuleStatus.linked, ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(module.#Status))
assertFailed()
if ([ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(module.#Status)) {
module = module.#CycleRoot!
if (!module) assertFailed() // TODO: https://github.com/tc39/ecma262/issues/2823
}
if (module.#TopLevelCapability) return module.#TopLevelCapability.Promise
const stack: Module[] = []
const capability = PromiseCapability<void>()
module.#TopLevelCapability = capability
try {
Module.#InnerModuleEvaluation(module, stack, 0, HostDefined)
} catch (err) {
for (const m of stack) {
if (!(m.#Status === ModuleStatus.evaluating)) assertFailed()
m.#Status = ModuleStatus.evaluated
m.#EvaluationError = err
}
if (!(module.#Status === ModuleStatus.evaluated)) assertFailed()
if (!(module.#EvaluationError === err)) assertFailed()
capability.Reject(err)
return capability.Promise
}
if (![ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(module.#Status)) assertFailed()
if (!(module.#EvaluationError === empty)) assertFailed()
if (module.#AsyncEvaluation === false) {
if (!(module.#Status === ModuleStatus.evaluated)) assertFailed()
capability.Resolve()
}
if (!(stack.length === 0)) assertFailed()
return capability.Promise
}
/** https://tc39.es/ecma262/#sec-InnerModuleEvaluation */
static #InnerModuleEvaluation(module: Module, stack: Module[], index: number, HostDefined: Task) {

@@ -579,4 +351,4 @@ if ([ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(module.#Status)) {

for (const required of module.#RequestedModules) {
let requiredModule = this.#GetImportedModule(module, required)
index = this.#InnerModuleEvaluation(requiredModule, stack, index, HostDefined)
let requiredModule = Module.#GetImportedModule(module, required)
index = Module.#InnerModuleEvaluation(requiredModule, stack, index, HostDefined)
if (

@@ -611,11 +383,11 @@ ![ModuleStatus.evaluating, ModuleStatus.evaluatingAsync, ModuleStatus.evaluated].includes(

if (!(module.#AsyncEvaluation === false)) assertFailed()
if (!(module.#__AsyncEvaluationPreviouslyTrue === false)) assertFailed()
if (!(module.#NON_SPEC_AsyncEvaluationPreviouslyTrue === false)) assertFailed()
module.#AsyncEvaluation = true
module.#__AsyncEvaluationPreviouslyTrue = true
module.#NON_SPEC_AsyncEvaluationPreviouslyTrue = true
// Note: The order in which module records have their [[AsyncEvaluation]] fields transition to true is significant. (See 16.2.1.5.2.4.)
if (module.#PendingAsyncDependencies === 0) {
this.#ExecuteAsyncModule(module, HostDefined)
Module.#ExecuteAsyncModule(module, HostDefined)
}
} else {
HostDefined.run(() => module.#ExecuteModule())
HostDefined.run(() => module.#Layer2_ExecuteModule())
}

@@ -639,4 +411,3 @@ if (!(stack.filter((x) => x === module).length === 1)) assertFailed()

}
// https://tc39.es/ecma262/#sec-execute-async-module
/** https://tc39.es/ecma262/#sec-execute-async-module */
static #ExecuteAsyncModule(module: Module, HostDefined: Task) {

@@ -654,6 +425,5 @@ if (![ModuleStatus.evaluating, ModuleStatus.evaluatingAsync].includes(module.#Status)) assertFailed()

)
HostDefined.run(() => module.#ExecuteModule(capability))
HostDefined.run(() => module.#Layer2_ExecuteModule(capability))
}
// https://tc39.es/ecma262/#sec-gather-available-ancestors
/** https://tc39.es/ecma262/#sec-gather-available-ancestors */
static #GatherAvailableAncestors(module: Module, execList: Module[]) {

@@ -669,3 +439,3 @@ for (const m of module.#AsyncParentModules) {

execList.push(m)
if (!m.#HasTLA) this.#GatherAvailableAncestors(m, execList)
if (!m.#HasTLA) Module.#GatherAvailableAncestors(m, execList)
}

@@ -675,4 +445,3 @@ }

}
// https://tc39.es/ecma262/#sec-async-module-execution-fulfilled
/** https://tc39.es/ecma262/#sec-async-module-execution-fulfilled */
static #AsyncModuleExecutionFulfilled(module: Module, HostDefined: Task) {

@@ -693,3 +462,3 @@ if (module.#Status === ModuleStatus.evaluated) {

const execList: Module[] = []
this.#GatherAvailableAncestors(module, execList)
Module.#GatherAvailableAncestors(module, execList)
// TODO: Let sortedExecList be a List whose elements are the elements of execList, in the order in which they had their [[AsyncEvaluation]] fields set to true in InnerModuleEvaluation.

@@ -707,6 +476,6 @@ const sortedExecList = execList

} else if (m.#HasTLA) {
this.#ExecuteAsyncModule(m, HostDefined)
Module.#ExecuteAsyncModule(m, HostDefined)
} else {
try {
HostDefined.run(() => m.#ExecuteModule())
HostDefined.run(() => m.#Layer2_ExecuteModule())
} catch (err) {

@@ -724,4 +493,3 @@ this.#AsyncModuleExecutionRejected(m, err)

}
// https://tc39.es/ecma262/#sec-async-module-execution-rejected
/** https://tc39.es/ecma262/#sec-async-module-execution-rejected */
static #AsyncModuleExecutionRejected = (module: Module, error: unknown) => {

@@ -745,63 +513,270 @@ if (module.#Status === ModuleStatus.evaluated) {

}
static #GetModuleNamespace(module: Module): ModuleNamespace {
if (module.#Namespace) return module.#Namespace
if (!(module.#Status !== ModuleStatus.new && module.#Status !== ModuleStatus.unlinked)) assertFailed()
const exportedNames = module.#GetExportedNames()
//#endregion
//#region SourceTextModuleRecord
/**
* A List of ImportEntry records derived from the code of this module.
* https://tc39.es/ecma262/#table-additional-fields-of-source-text-module-records
*/
#ImportEntries: ModuleImportEntry[]
/**
* A List of ExportEntry records derived from the code of this module that correspond to declarations that occur within the module.
* https://tc39.es/ecma262/#table-additional-fields-of-source-text-module-records
*/
#LocalExportEntries: ModuleExportEntry[]
/**
* A List of ExportEntry records derived from the code of this module that correspond to reexported imports that occur within the module or exports from export * as namespace declarations.
* https://tc39.es/ecma262/#table-additional-fields-of-source-text-module-records
*/
#IndirectExportEntries: ModuleExportEntry[]
/**
* A List of ExportEntry records derived from the code of this module that correspond to export * declarations that occur within the module, not including export * as namespace declarations.
* https://tc39.es/ecma262/#table-additional-fields-of-source-text-module-records
*/
#StarExportEntries: ModuleExportEntry[]
/** https://tc39.es/ecma262/#sec-getexportednames */
#GetExportedNames(exportStarSet: Module[] = []): string[] {
const module = this
if (!(module.#Status !== ModuleStatus.new)) assertFailed()
if (exportStarSet.includes(module)) {
// a. Assert: We've reached the starting point of an export * circularity.
return []
}
exportStarSet.push(module)
const exportedNames: string[] = []
for (const e of module.#LocalExportEntries) {
// a. Assert: module provides the direct binding for this export.
if (!(e.ExportName !== null)) assertFailed()
exportedNames.push(e.ExportName)
}
for (const e of module.#IndirectExportEntries) {
// a. Assert: module imports a specific binding for this export.
if (!(e.ExportName !== null)) assertFailed()
exportedNames.push(e.ExportName)
}
for (const e of module.#StarExportEntries) {
// this assert does not appear in the spec.
if (!(e.ModuleRequest !== null)) assertFailed()
const requestedModule = Module.#GetImportedModule(module, e.ModuleRequest)
const starNames = requestedModule.#GetExportedNames(exportStarSet)
for (const n of starNames) {
if (n === 'default') continue
if (exportedNames.includes(n)) continue
exportedNames.push(n)
}
}
return exportedNames
}
/** https://tc39.es/ecma262/#sec-resolveexport */
#ResolveExport(
exportName: string,
resolveSet: { Module: Module; ExportName: string }[] = [],
): typeof ambiguous | ResolvedBindingRecord | null {
const module = this
if (!(module.#Status !== ModuleStatus.new)) assertFailed()
for (const r of resolveSet) {
if (module === r.Module && exportName === r.ExportName) {
// Assert: This is a circular import request.
return null
}
}
resolveSet.push({ Module: module, ExportName: exportName })
for (const e of module.#LocalExportEntries) {
if (exportName === e.ExportName) {
// i. Assert: module provides the direct binding for this export.
// if (!(e.LocalName !== null)) assertFailed()
// return { module, bindingName: e.LocalName }
const namespaceObject: ModuleNamespace = { __proto__: null }
const propertiesToBeDefined: PropertyDescriptorMap = {
__proto__: null!,
[Symbol.toStringTag]: { value: 'Module' },
// ? why we use this alternative step?
return { Module: module, BindingName: e.ExportName }
}
}
const namespaceProxy = new Proxy(namespaceObject, moduleNamespaceExoticMethods)
// set it earlier in case of circular dependency
module.#Namespace = namespaceProxy
for (const e of module.#IndirectExportEntries) {
if (exportName === e.ExportName) {
// this assert does not appear in the spec.
if (!(e.ModuleRequest !== null)) assertFailed()
const importedModule = Module.#GetImportedModule(module, e.ModuleRequest)
if (e.ImportName === all) {
// Assert: module does not provide the direct binding for this export.
return { Module: importedModule, BindingName: namespace }
} else {
// 1. Assert: module imports a specific binding for this export.
if (!(typeof e.ImportName === 'string')) assertFailed()
return importedModule.#ResolveExport(e.ImportName, resolveSet)
}
}
}
if (exportName === 'default') {
// Assert: A default export was not explicitly provided by this module.
// Note: A default export cannot be provided by an export * from "mod" declaration.
return null
}
let starResolution: null | ResolvedBindingRecord = null
for (const e of module.#StarExportEntries) {
// this assert does not appear in the spec.
if (!(e.ModuleRequest !== null)) assertFailed()
const importedModule = Module.#GetImportedModule(module, e.ModuleRequest)
let resolution = importedModule.#ResolveExport(exportName, resolveSet)
if (resolution === ambiguous) return ambiguous
if (resolution !== null) {
// i. Assert: resolution is a ResolvedBinding Record.
if (starResolution === null) starResolution = resolution
else {
// Assert: There is more than one * import that includes the requested name.
if (resolution.Module !== starResolution.Module) return ambiguous
if (
(resolution.BindingName === namespace && starResolution.BindingName !== namespace) ||
(resolution.BindingName !== namespace && starResolution.BindingName === namespace)
)
return ambiguous
if (
typeof resolution.BindingName === 'string' &&
typeof starResolution.BindingName === 'string' &&
resolution.BindingName !== starResolution.BindingName
) {
return ambiguous
}
}
}
}
return starResolution
}
/** https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment */
#InitializeEnvironment() {
const module = this
for (const e of module.#IndirectExportEntries) {
// this assert does not appear in the spec.
if (!(e.ExportName !== null)) assertFailed()
const resolution = module.#ResolveExport(e.ExportName)
if (resolution === null || resolution === ambiguous) {
throw new SyntaxError(`Module '${e.ModuleRequest}' does not provide an export ${e.ExportName}`)
}
}
for (const name of exportedNames) {
const resolution = module.#ResolveExport(name)
if (resolution === ambiguous || resolution === null) continue
// 2. Assert: All named exports from module are resolvable.
const { bindingName, module: targetModule } = resolution
if (bindingName === namespace) {
propertiesToBeDefined[name] = { enumerable: true, value: Module.#GetModuleNamespace(targetModule) }
// 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
const env = { __proto__: null }
// 6. Set module.[[Environment]] to env.
module.#Environment = env
module.#Layer2_ContextObject = createContextObject()
const envBindings: PropertyDescriptorMap = {
__proto__: null!,
}
// 7. For each ImportEntry Record in of module.[[ImportEntries]], do
for (const i of module.#ImportEntries) {
const importedModule = Module.#GetImportedModule(module, i.ModuleRequest)
// import * as ns from '..'
if (i.ImportName === namespace) {
const namespaceObject = Module.#GetModuleNamespace(importedModule)
envBindings[i.LocalName] = { value: namespaceObject, enumerable: true }
} else {
if (targetModule.#LocalExportedValues.has(bindingName)) {
propertiesToBeDefined[name] = {
enumerable: true,
// Note: this should not be configurable, but it's a trade-off for DX.
configurable: true,
value: targetModule.#LocalExportedValues.get(bindingName)!,
}
const resolution = importedModule.#ResolveExport(i.ImportName)
if (resolution === null)
throw new SyntaxError(`${i.ModuleRequest} does not provide export ${i.ImportName}`)
if (resolution === ambiguous)
throw new SyntaxError(`${i.ModuleRequest} does not provide an unambiguous export ${i.ImportName}`)
// import { x } from '...' where x is a "export * as ns from '...'"
if (resolution.BindingName === namespace) {
const namespaceObject = Module.#GetModuleNamespace(resolution.Module)
envBindings[i.LocalName] = { value: namespaceObject, enumerable: true }
} else {
propertiesToBeDefined[name] = {
get() {
throw new ReferenceError(`Cannot access '${name}' before initialization`)
},
// Note: this should not be configurable, but it's a trade-off for DX.
configurable: true,
enumerable: true,
// 1. Perform env.CreateImportBinding(in.[[LocalName]], resolution.[[Module]], resolution.[[BindingName]]).
resolution.Module.#NON_SPEC_AddLiveExportCallback(i.ImportName, (newValue) => {
Object.defineProperty(env, i.LocalName, {
value: newValue,
configurable: true,
enumerable: true,
})
})
if (resolution.Module.#NON_SPEC_LocalExportedValues.has(resolution.BindingName)) {
envBindings[i.LocalName] = {
configurable: true,
enumerable: true,
value: resolution.Module.#NON_SPEC_LocalExportedValues.get(resolution.BindingName),
}
} else {
envBindings[i.LocalName] = {
get() {
throw new ReferenceError(`Cannot access '${i.LocalName}' before initialization`)
},
configurable: true,
enumerable: true,
}
}
}
targetModule.#non_std_AddLiveExportCallback(name, (newValue) => {
Object.defineProperty(namespaceObject, name, {
enumerable: true,
writable: true,
value: newValue,
})
})
}
}
Object.defineProperties(namespaceObject, propertiesToBeDefined)
return namespaceProxy
// non-spec: set up env for exported bindings
for (const { ModuleRequest, ExportName, ImportName } of module.#LocalExportEntries) {
if (!(ModuleRequest === null && typeof ExportName === 'string' && ImportName === null)) assertFailed()
envBindings[ExportName] = {
get: () => this.#NON_SPEC_LocalExportedValues.get(ExportName),
set: (value) => {
this.#NON_SPEC_LocalExportedValues.set(ExportName, value)
this.#NON_SPEC_ExportCallback.get(ExportName)?.forEach((callback) => callback(value))
return true
},
// Note: export property should not be enumerable?
// but it will crash Chrome devtools. See: https://bugs.chromium.org/p/chromium/issues/detail?id=1358114
enumerable: true,
}
}
Object.defineProperties(env, envBindings)
// ? no spec reference?
for (const exports of module.#GetExportedNames()) {
if (module.#ResolveExport(exports) === ambiguous) {
throw new SyntaxError(`Module has multiple exports named '${exports}'`)
}
}
// TODO: https://github.com/tc39/proposal-compartments/issues/70
// prevent access to global env until [[ExecuteModule]]
Object.setPrototypeOf(env, opaqueProxy)
}
//#endregion
//#region Non-spec things (needed for this impl)
/**
* This is used as an assertion in the spec but spec does not contain it.
*/
#NON_SPEC_AsyncEvaluationPreviouslyTrue = false
/**
* A map that map the exported name to it's current value.
*/
#NON_SPEC_LocalExportedValues = new Map<string, unknown>()
/**
* A callback map that stores all listeners will be notified when the requested export name has been updated.
*/
#NON_SPEC_ExportCallback = new Map<string, Set<(newValue: any) => void>>()
#NON_SPEC_AddLiveExportCallback(name: string, callback: (newValue: any) => void) {
if (!this.#NON_SPEC_ExportCallback.has(name)) this.#NON_SPEC_ExportCallback.set(name, new Set())
this.#NON_SPEC_ExportCallback.get(name)!.add(callback)
}
//#endregion
//#region Layer 0 features
/**
* Defaults to undefined. The function can return a module instance to resolve module dependencies.
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#table-internal-slots-of-module-instances
*/
#Layer0_ImportHook: ImportHook | undefined
/**
* Defaults to undefined. The function can augment the import.meta object provided as the first argument.
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#table-internal-slots-of-module-instances
*/
#Layer0_ImportMetaHook: ImportMetaHook | undefined
/**
* This is the *this* value used for invocation of the hook functions.
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#table-internal-slots-of-module-instances
*/
#Layer0_HandlerValue: ModuleHandler
//#region Module refactor methods https://github.com/tc39/ecma262/pull/2905/
static #GetImportedModule(module: Module, spec: string) {
const record = module.#LoadedModules.get(spec)
if (!record) assertFailed()
return record
}
static #LoadImportedModule(
/**
* A map from the specifier strings imported by this module to the states of the loading processes that are waiting for the resolved module record. It is used to avoid multiple calls to the loading hook with the same (specifier, referrer) pair. The list does not contain two different Records with the same [[Specifier]].
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#table-cyclic-module-fields
*/
#Layer0_LoadingModules = new Map<string, Set<GraphLoadingState | PromiseCapability<ModuleNamespace>>>()
/** https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-LoadImportedModule */
static #Layer0_LoadImportedModule(
referrer: Module,

@@ -814,10 +789,10 @@ specifier: string,

const module = referrer.#LoadedModules.get(specifier)!
this.#FinishLoadingImportedModule(referrer, specifier, NormalCompletion(module), hostDefined)
this.#Layer0_FinishLoadingImportedModule(referrer, specifier, NormalCompletion(module), hostDefined)
return
}
if (referrer.#LoadingModules.has(specifier)) {
referrer.#LoadingModules.get(specifier)!.add(state)
if (referrer.#Layer0_LoadingModules.has(specifier)) {
referrer.#Layer0_LoadingModules.get(specifier)!.add(state)
return
}
referrer.#LoadingModules.set(specifier, new Set([state]))
referrer.#Layer0_LoadingModules.set(specifier, new Set([state]))
// Skipped spec:

@@ -829,5 +804,5 @@ // 4. If referrer is not a Source Text Module Record, referrer.[[ModuleInstance]] is undefined, or referrer.[[ModuleInstance]].[[ImportHook]] is undefined, then

try {
const importHookResult = referrer.#ImportHook
? Reflect.apply(referrer.#ImportHook, referrer.#HandlerValue, [specifier])
: Reflect.apply(referrer.#ParentImportHook, undefined, [specifier])
const importHookResult = referrer.#Layer0_ImportHook
? Reflect.apply(referrer.#Layer0_ImportHook, referrer.#Layer0_HandlerValue, [specifier])
: Reflect.apply(referrer.#Layer3_ParentImportHook, undefined, [specifier])
// unwrap importHookResult here

@@ -839,3 +814,3 @@ const importHookPromise = Promise.resolve(importHookResult)

try {
;(result as Module).#HandlerValue
;(result as Module).#Layer0_HandlerValue
completion = NormalCompletion(result)

@@ -845,14 +820,17 @@ } catch (error) {

}
this.#FinishLoadingImportedModule(referrer, specifier, completion, hostDefined)
Module.#Layer0_FinishLoadingImportedModule(referrer, specifier, completion, hostDefined)
}
const onRejected = (error: any) => {
this.#FinishLoadingImportedModule(referrer, specifier, ThrowCompletion(error), hostDefined)
this.#Layer0_FinishLoadingImportedModule(referrer, specifier, ThrowCompletion(error), hostDefined)
}
importHookPromise.then(onFulfilled, onRejected)
} catch (error) {
this.#FinishLoadingImportedModule(referrer, specifier, ThrowCompletion(error), hostDefined)
this.#Layer0_FinishLoadingImportedModule(referrer, specifier, ThrowCompletion(error), hostDefined)
}
}
static #FinishLoadingImportedModule(
/**
* https://tc39.es/ecma262/#sec-FinishLoadingImportedModule
* https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-FinishLoadingImportedModule
*/
static #Layer0_FinishLoadingImportedModule(
referrer: Module,

@@ -871,11 +849,113 @@ specifier: string,

}
const loading = referrer.#LoadingModules.get(specifier)!
const loading = referrer.#Layer0_LoadingModules.get(specifier)
if (!loading) assertFailed()
referrer.#LoadingModules.delete(specifier)
referrer.#Layer0_LoadingModules.delete(specifier)
for (const state of loading) {
if ('Visited' in state) this.#ContinueModuleLoading(state, result)
else this.#ContinueDynamicImport(state, result, hostDefined)
if ('PromiseCapability' in state) {
Module.#ContinueModuleLoading(state, result)
} else {
Module.#ContinueDynamicImport(state, result, hostDefined)
}
}
}
//#endregion
//#region Layer 2 features
#Layer2_VirtualModuleSource: unknown
#Layer2_Execute: VirtualModuleRecord['execute'] | empty = empty
#Layer2_NeedsImportMeta: boolean | undefined
#Layer2_NeedsImport: boolean | undefined
#Layer2_ContextObject: VirtualModuleRecordExecuteContext | undefined
/**
* All call to ExecuteModule must use Task.run to keep the call stack continue
* https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
* https://tc39.es/proposal-compartments/0-module-and-module-source.html
* https://github.com/tc39/proposal-compartments/blob/master/2-virtual-module-source.md
*/
#Layer2_ExecuteModule(promise?: PromiseCapability<void>) {
if (!this.#Layer2_ContextObject) assertFailed()
if (!this.#Environment) assertFailed()
// a virtual module might have no execute function available (a purely re-export module)
const execute = this.#Layer2_Execute
if (execute === empty) assertFailed()
this.#Layer2_Execute = empty
if (!execute) {
promise?.Resolve()
return
}
// prepare context
this.#Layer2_ContextObject.globalThis = this.#Layer3_GlobalThis as any
if (this.#Layer2_NeedsImportMeta) {
// https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-meta-properties-runtime-semantics-evaluation
const importMeta = { __proto__: null }
if (this.#Layer0_ImportMetaHook)
Reflect.apply(this.#Layer0_ImportMetaHook, this.#Layer0_HandlerValue, [importMeta])
else if (this.#Layer3_ParentImportMetaHook)
Reflect.apply(this.#Layer3_ParentImportMetaHook, undefined, [importMeta])
this.#Layer2_ContextObject.importMeta = importMeta
}
if (this.#Layer2_NeedsImport) {
// https://tc39.es/proposal-compartments/0-module-and-module-source.html#sec-import-calls
this.#Layer2_ContextObject.import = async (
specifier: string | Module<ModuleNamespace>,
options?: ImportCallOptions,
) => {
// 1. Let referrer be GetActiveScriptOrModule().
const referrer = this
// 5. Let promiseCapability be ! NewPromiseCapability(%Promise%).
const promiseCapability = PromiseCapability<ModuleNamespace>()
let hasModuleInternalSlot = false
try {
;(specifier as Module).#Layer0_HandlerValue
hasModuleInternalSlot = true
} catch {}
// 6. If Type(specifierOrModule) is Object that has a [[ModuleSourceInstance]] internal slot, then
if (hasModuleInternalSlot) {
const hostDefined = createTask(`import(<module block>)`)
Module.#ContinueDynamicImport(promiseCapability, NormalCompletion(specifier as Module), hostDefined)
} else {
specifier = String(specifier)
const hostDefined = createTask(`import("${specifier}")`)
Module.#Layer0_LoadImportedModule(referrer, specifier, hostDefined, promiseCapability)
}
return promiseCapability.Promise as any
}
}
const env = new Proxy(this.#Environment, moduleEnvExoticMethods)
// https://tc39.es/ecma262/#sec-source-text-module-record-execute-module
// 9. If module.[[HasTLA]] is false, then
if (!this.#HasTLA) {
// a. Assert: capability is not present.
if (promise) assertFailed()
// c. Let result be Completion(Evaluation of module.[[ECMAScriptCode]]).
// f. If result is an abrupt completion, then
// i. Return ? result.
const result = Reflect.apply(execute, this.#Layer2_VirtualModuleSource, [env, this.#Layer2_ContextObject])
if (result) {
throw new TypeError(
'Due to specification limitations, in order to support Async Modules (modules that use Top Level Await or a Virtual Module that has an execute() function that returns a Promise), the Virtual Module record must be marked with `isAsync: true`. The `isAsync` property is non-standard, and it is being tracked in https://github.com/tc39/proposal-compartments/issues/84.',
)
}
} else {
// a. Assert: capability is a PromiseCapability Record.
if (!promise) assertFailed()
// b. Perform AsyncBlockStart(capability, module.[[ECMAScriptCode]], moduleContext).
Promise.resolve()
.then(() => Reflect.apply(execute, this.#Layer2_VirtualModuleSource, [env, this.#Layer2_ContextObject]))
.then(promise.Resolve, promise.Reject)
}
}
//#endregion
//#region Layer 3 features
#Layer3_GlobalThis: object = globalThis
#Layer3_ParentImportHook: ImportHook = defaultImportHook
#Layer3_ParentImportMetaHook: ImportMetaHook | undefined
//#endregion
/** https://tc39.es/ecma262/#sec-ContinueDynamicImport */
static #ContinueDynamicImport(

@@ -910,4 +990,60 @@ promiseCapability: PromiseCapability<ModuleNamespace>,

}
//#endregion
/** @internal */
/** https://tc39.es/ecma262/#sec-GetImportedModule */
static #GetImportedModule(module: Module, spec: string) {
const record = module.#LoadedModules.get(spec)
if (!record) assertFailed()
return record
}
/** https://tc39.es/ecma262/#sec-modulenamespacecreate */
static #GetModuleNamespace(module: Module): ModuleNamespace {
if (module.#Namespace) return module.#Namespace
if (!(module.#Status !== ModuleStatus.new && module.#Status !== ModuleStatus.unlinked)) assertFailed()
const exportedNames = module.#GetExportedNames()
const namespaceObject: ModuleNamespace = { __proto__: null }
const namespaceObjectBindings: PropertyDescriptorMap = {
__proto__: null!,
[Symbol.toStringTag]: { value: 'Module' },
}
const namespaceProxy = new Proxy(namespaceObject, moduleNamespaceExoticMethods)
// set it earlier in case of circular dependency
module.#Namespace = namespaceProxy
for (const name of exportedNames) {
const resolution = module.#ResolveExport(name)
if (resolution === ambiguous || resolution === null) continue
const { BindingName, Module: targetModule } = resolution
if (BindingName === namespace) {
namespaceObjectBindings[name] = { enumerable: true, value: Module.#GetModuleNamespace(targetModule) }
} else {
if (targetModule.#NON_SPEC_LocalExportedValues.has(BindingName)) {
namespaceObjectBindings[name] = {
enumerable: true,
// Note: this should not be configurable, but it's a trade-off for DX.
configurable: true,
value: targetModule.#NON_SPEC_LocalExportedValues.get(BindingName)!,
}
} else {
namespaceObjectBindings[name] = {
get() {
throw new ReferenceError(`Cannot access '${name}' before initialization`)
},
// Note: this should not be configurable, but it's a trade-off for DX.
configurable: true,
enumerable: true,
}
}
targetModule.#NON_SPEC_AddLiveExportCallback(name, (newValue) => {
Object.defineProperty(namespaceObject, name, {
enumerable: true,
writable: true,
value: newValue,
})
})
}
}
Object.defineProperties(namespaceObject, namespaceObjectBindings)
return namespaceProxy
}
static {

@@ -921,5 +1057,5 @@ imports = async (module, options) => {

}
setParentGlobalThis = (module, global) => (module.#GlobalThis = global)
setParentImportHook = (module, hook) => (module.#ParentImportHook = hook)
setParentImportMetaHook = (module, hook) => (module.#ParentImportMetaHook = hook)
setParentGlobalThis = (module, global) => (module.#Layer3_GlobalThis = global)
setParentImportHook = (module, hook) => (module.#Layer3_ParentImportHook = hook)
setParentImportMetaHook = (module, hook) => (module.#Layer3_ParentImportMetaHook = hook)
}

@@ -996,1 +1132,6 @@ }

}
interface ResolvedBindingRecord {
Module: Module
BindingName: string | typeof namespace
}

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc