Skip to content

Commit

Permalink
feat(ses): Module descriptor parity with XS
Browse files Browse the repository at this point in the history
  • Loading branch information
kriskowal committed Jul 11, 2024
1 parent 29a1fa2 commit 830b440
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 18 deletions.
4 changes: 4 additions & 0 deletions packages/ses/src/compartment.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ defineProperties(InertCompartment, {
* @param {MakeCompartmentConstructor} targetMakeCompartmentConstructor
* @param {Record<string, any>} intrinsics
* @param {(object: object) => void} markVirtualizedNativeFunction
* @param {Compartment} [parentCompartment]
* @returns {Compartment['constructor']}
*/

Expand All @@ -169,6 +170,7 @@ export const makeCompartmentConstructor = (
targetMakeCompartmentConstructor,
intrinsics,
markVirtualizedNativeFunction,
parentCompartment = undefined,
) => {
function Compartment(endowments = {}, moduleMap = {}, options = {}) {
if (new.target === undefined) {
Expand Down Expand Up @@ -218,6 +220,7 @@ export const makeCompartmentConstructor = (
intrinsics,
newGlobalPropertyNames: sharedGlobalPropertyNames,
makeCompartmentConstructor: targetMakeCompartmentConstructor,
parentCompartment: this,
markVirtualizedNativeFunction,
});

Expand Down Expand Up @@ -245,6 +248,7 @@ export const makeCompartmentConstructor = (
__shimTransforms__,
deferredExports,
instances,
parentCompartment,
});
}

Expand Down
13 changes: 8 additions & 5 deletions packages/ses/src/global-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -82,6 +83,7 @@ export const setGlobalObjectMutableProperties = (
newGlobalPropertyNames,
makeCompartmentConstructor,
markVirtualizedNativeFunction,
parentCompartment,
},
) => {
for (const [name, intrinsicName] of entries(universalPropertyNames)) {
Expand Down Expand Up @@ -115,6 +117,7 @@ export const setGlobalObjectMutableProperties = (
makeCompartmentConstructor,
intrinsics,
markVirtualizedNativeFunction,
parentCompartment,
),
);

Expand Down
95 changes: 82 additions & 13 deletions packages/ses/src/module-load.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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(
Expand Down Expand Up @@ -122,8 +116,6 @@ const loadModuleSource = (
]);
}

// Memoize.
mapSet(moduleRecords, moduleSpecifier, moduleRecord);
return moduleRecord;
};

Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand All @@ -211,6 +276,7 @@ function* loadWithoutErrorAnnotation(
importMeta,
);
mapSet(moduleRecords, moduleSpecifier, aliasRecord);
mapSet(moduleRecords, aliasSpecifier, aliasRecord);
return aliasRecord;
}

Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand Down

0 comments on commit 830b440

Please sign in to comment.