UNPKG

ses

Version:

Hardened JavaScript for Fearless Cooperation

160 lines (142 loc) 4.95 kB
/* eslint-disable no-underscore-dangle */ import { assert } from './error/assert.js'; import { makeModuleInstance, makeVirtualModuleInstance, } from './module-instance.js'; import { Map, ReferenceError, TypeError, entries, isArray, isPrimitive, mapGet, mapHas, mapSet, weakmapGet, } from './commons.js'; const { Fail, quote: q } = assert; // `link` creates `ModuleInstances` and `ModuleNamespaces` for a module and its // transitive dependencies and connects their imports and exports. // After linking, the resulting working set is ready to be executed. // The linker only concerns itself with module namespaces that are objects with // property descriptors for their exports, which the Compartment proxies with // the actual `ModuleNamespace`. export const link = ( compartmentPrivateFields, moduleAliases, compartment, moduleSpecifier, ) => { const { name: compartmentName, moduleRecords } = weakmapGet( compartmentPrivateFields, compartment, ); const moduleRecord = mapGet(moduleRecords, moduleSpecifier); if (moduleRecord === undefined) { throw ReferenceError( `Missing link to module ${q(moduleSpecifier)} from compartment ${q( compartmentName, )}`, ); } // Mutual recursion so there's no confusion about which // compartment is in context: the module record may be in another // compartment, denoted by moduleRecord.compartment. // eslint-disable-next-line no-use-before-define return instantiate(compartmentPrivateFields, moduleAliases, moduleRecord); }; function mayBePrecompiledModuleSource(moduleSource) { return typeof moduleSource.__syncModuleProgram__ === 'string'; } function validatePrecompiledModuleSource(moduleSource, moduleSpecifier) { const { __fixedExportMap__, __liveExportMap__ } = moduleSource; !isPrimitive(__fixedExportMap__) || Fail`Property '__fixedExportMap__' of a precompiled module source must be an object, got ${q( __fixedExportMap__, )}, for module ${q(moduleSpecifier)}`; !isPrimitive(__liveExportMap__) || Fail`Property '__liveExportMap__' of a precompiled module source must be an object, got ${q( __liveExportMap__, )}, for module ${q(moduleSpecifier)}`; } function mayBeVirtualModuleSource(moduleSource) { return typeof moduleSource.execute === 'function'; } function validateVirtualModuleSource(moduleSource, moduleSpecifier) { const { exports } = moduleSource; isArray(exports) || Fail`Invalid module source: 'exports' of a virtual module source must be an array, got ${q( exports, )}, for module ${q(moduleSpecifier)}`; } function validateModuleSource(moduleSource, moduleSpecifier) { !isPrimitive(moduleSource) || Fail`Invalid module source: must be of type object, got ${q( moduleSource, )}, for module ${q(moduleSpecifier)}`; const { imports, exports, reexports = [] } = moduleSource; isArray(imports) || Fail`Invalid module source: 'imports' must be an array, got ${q( imports, )}, for module ${q(moduleSpecifier)}`; isArray(exports) || Fail`Invalid module source: 'exports' must be an array, got ${q( exports, )}, for module ${q(moduleSpecifier)}`; isArray(reexports) || Fail`Invalid module source: 'reexports' must be an array if present, got ${q( reexports, )}, for module ${q(moduleSpecifier)}`; } export const instantiate = ( compartmentPrivateFields, moduleAliases, moduleRecord, ) => { const { compartment, moduleSpecifier, resolvedImports, moduleSource } = moduleRecord; const { instances } = weakmapGet(compartmentPrivateFields, compartment); // Memoize. if (mapHas(instances, moduleSpecifier)) { return mapGet(instances, moduleSpecifier); } validateModuleSource(moduleSource, moduleSpecifier); const importedInstances = new Map(); let moduleInstance; if (mayBePrecompiledModuleSource(moduleSource)) { validatePrecompiledModuleSource(moduleSource, moduleSpecifier); moduleInstance = makeModuleInstance( compartmentPrivateFields, moduleAliases, moduleRecord, importedInstances, ); } else if (mayBeVirtualModuleSource(moduleSource)) { validateVirtualModuleSource(moduleSource, moduleSpecifier); moduleInstance = makeVirtualModuleInstance( compartmentPrivateFields, moduleSource, compartment, moduleAliases, moduleSpecifier, resolvedImports, ); } else { throw TypeError(`Invalid module source, got ${q(moduleSource)}`); } // Memoize. mapSet(instances, moduleSpecifier, moduleInstance); // Link dependency modules. for (const [importSpecifier, resolvedSpecifier] of entries(resolvedImports)) { const importedInstance = link( compartmentPrivateFields, moduleAliases, compartment, resolvedSpecifier, ); mapSet(importedInstances, importSpecifier, importedInstance); } return moduleInstance; };