diff --git a/packages/ses/src/compartment.js b/packages/ses/src/compartment.js index 97113f1382..8728a0b11f 100644 --- a/packages/ses/src/compartment.js +++ b/packages/ses/src/compartment.js @@ -161,6 +161,7 @@ defineProperties(InertCompartment, { * @param {MakeCompartmentConstructor} targetMakeCompartmentConstructor * @param {Record} intrinsics * @param {(object: object) => void} markVirtualizedNativeFunction + * @param {Compartment} [parentCompartment] * @returns {Compartment['constructor']} */ @@ -169,6 +170,7 @@ export const makeCompartmentConstructor = ( targetMakeCompartmentConstructor, intrinsics, markVirtualizedNativeFunction, + parentCompartment = undefined, ) => { function Compartment(endowments = {}, moduleMap = {}, options = {}) { if (new.target === undefined) { @@ -218,6 +220,7 @@ export const makeCompartmentConstructor = ( intrinsics, newGlobalPropertyNames: sharedGlobalPropertyNames, makeCompartmentConstructor: targetMakeCompartmentConstructor, + parentCompartment: this, markVirtualizedNativeFunction, }); @@ -245,6 +248,7 @@ export const makeCompartmentConstructor = ( __shimTransforms__, deferredExports, instances, + parentCompartment, }); } diff --git a/packages/ses/src/global-object.js b/packages/ses/src/global-object.js index 8107fac25b..468d3296c5 100644 --- a/packages/ses/src/global-object.js +++ b/packages/ses/src/global-object.js @@ -69,11 +69,12 @@ export const setGlobalObjectConstantProperties = globalObject => { * `sharedGlobalPropertyNames`. * * @param {object} globalObject - * @param {object} param1 - * @param {object} param1.intrinsics - * @param {object} param1.newGlobalPropertyNames - * @param {Function} param1.makeCompartmentConstructor - * @param {(object) => void} param1.markVirtualizedNativeFunction + * @param {object} args + * @param {object} args.intrinsics + * @param {object} args.newGlobalPropertyNames + * @param {Function} args.makeCompartmentConstructor + * @param {(object) => void} args.markVirtualizedNativeFunction + * @param {Compartment} [args.parentCompartment] */ export const setGlobalObjectMutableProperties = ( globalObject, @@ -82,6 +83,7 @@ export const setGlobalObjectMutableProperties = ( newGlobalPropertyNames, makeCompartmentConstructor, markVirtualizedNativeFunction, + parentCompartment, }, ) => { for (const [name, intrinsicName] of entries(universalPropertyNames)) { @@ -115,6 +117,7 @@ export const setGlobalObjectMutableProperties = ( makeCompartmentConstructor, intrinsics, markVirtualizedNativeFunction, + parentCompartment, ), ); diff --git a/packages/ses/src/module-load.js b/packages/ses/src/module-load.js index 04ce0d8c0c..8f730ce1ab 100644 --- a/packages/ses/src/module-load.js +++ b/packages/ses/src/module-load.js @@ -61,10 +61,7 @@ const syncTrampoline = (generatorFunc, args) => { // aliases. // Both are facilitated by the moduleMap Compartment constructor option. export const makeAlias = (compartment, specifier) => - freeze({ - compartment, - specifier, - }); + freeze({ compartment, specifier }); // `resolveAll` pre-computes resolutions of all imports within the compartment // in which a module was loaded. @@ -88,10 +85,7 @@ const loadModuleSource = ( moduleLoads, importMeta, ) => { - const { resolveHook, moduleRecords } = weakmapGet( - compartmentPrivateFields, - compartment, - ); + const { resolveHook } = weakmapGet(compartmentPrivateFields, compartment); // resolve all imports relative to this referrer module. const resolvedImports = resolveAll( @@ -122,8 +116,6 @@ const loadModuleSource = ( ]); } - // Memoize. - mapSet(moduleRecords, moduleSpecifier, moduleRecord); return moduleRecord; }; @@ -136,8 +128,14 @@ function* loadWithoutErrorAnnotation( selectImplementation, moduleLoads, ) { - const { importHook, importNowHook, moduleMap, moduleMapHook, moduleRecords } = - weakmapGet(compartmentPrivateFields, compartment); + const { + importHook, + importNowHook, + moduleMap, + moduleMapHook, + moduleRecords, + parentCompartment, + } = weakmapGet(compartmentPrivateFields, compartment); if (mapHas(moduleRecords, moduleSpecifier)) { return mapGet(moduleRecords, moduleSpecifier); @@ -185,6 +183,73 @@ function* loadWithoutErrorAnnotation( moduleDescriptor = aliasDescriptor; } + if (moduleDescriptor.source !== undefined) { + if (typeof moduleDescriptor.source === 'string') { + // { source: string, importMeta?, specifier?: string } + Fail``; // TODO + } + + // { source: ModuleSource, importMeta?, specifier?: string } + // { source: VirtualModuleSource, importMeta?, specifier?: string } + const { + source: moduleSource, + specifier: aliasSpecifier = moduleSpecifier, + importMeta, + } = moduleDescriptor; + + const aliasRecord = loadModuleSource( + compartmentPrivateFields, + moduleAliases, + compartment, + aliasSpecifier, + moduleSource, + enqueueJob, + selectImplementation, + moduleLoads, + importMeta, + ); + mapSet(moduleRecords, moduleSpecifier, aliasRecord); + return aliasRecord; + } + + if (moduleDescriptor.namespace !== undefined) { + // { namespace: string, compartment?: Compartment } + if (typeof moduleDescriptor.namespace === 'string') { + const { + compartment: aliasCompartment = parentCompartment, + namespace: aliasSpecifier, + } = moduleDescriptor; + if ( + !isObject(aliasCompartment) || + !weakmapHas(compartmentPrivateFields, aliasCompartment) + ) { + Fail`Invalid compartment in module descriptor for specifier ${q(moduleSpecifier)} in compartment ${q(compartment.name)}`; + } + // Behold: recursion. + // eslint-disable-next-line no-use-before-define + const aliasRecord = yield memoizedLoadWithErrorAnnotation( + compartmentPrivateFields, + moduleAliases, + aliasCompartment, + aliasSpecifier, + enqueueJob, + selectImplementation, + moduleLoads, + ); + mapSet(moduleRecords, moduleSpecifier, aliasRecord); + return aliasRecord; + } + if (isObject(moduleDescriptor.namespace)) { + // { namespace: ModuleNamespace, compartment?: Compartment } + // { namespace: Object, compartment?: Compartment } + } + Fail`Invalid compartment in module descriptor for specifier ${q(moduleSpecifier)} in compartment ${q(compartment.name)}`; + } + + if (moduleDescriptor.archive !== undefined) { + // { archive: Archive, path: string } Not implemented by SES + } + // A (legacy) module descriptor for when we find the module source (record) // but at a different specifier than requested. // Providing this {specifier, record} descriptor serves as an ergonomic @@ -211,6 +276,7 @@ function* loadWithoutErrorAnnotation( importMeta, ); mapSet(moduleRecords, moduleSpecifier, aliasRecord); + mapSet(moduleRecords, aliasSpecifier, aliasRecord); return aliasRecord; } @@ -245,7 +311,7 @@ function* loadWithoutErrorAnnotation( // A (legacy) behavior: If we do not recognize the module descriptor as a // module descriptor, we assume that it is a module source (record): const moduleSource = moduleDescriptor; - return loadModuleSource( + const moduleRecord = loadModuleSource( compartmentPrivateFields, moduleAliases, compartment, @@ -255,6 +321,9 @@ function* loadWithoutErrorAnnotation( selectImplementation, moduleLoads, ); + // Memoize. + mapSet(moduleRecords, moduleSpecifier, moduleRecord); + return moduleRecord; } else { Fail`module descriptor must be a string or object for specifier ${q( moduleSpecifier,