Skip to content

Commit

Permalink
feat(compartment-mapper): Collect unused module descriptors (#2614)
Browse files Browse the repository at this point in the history
Closes: #2313

## Description

This change adds instrumentation to the compartment mapper that allows
it to omit module descriptors from archive compartment maps if they’re
not needed to link the archived application.

### Security Considerations

None.

### Scaling Considerations

Should reduce bundle sizes.

### Documentation Considerations

Does not merit attention and one less surprise for bundle size
investigators. Noted in NEWS.

### Testing Considerations

This change augments the existing test for compartment retention to
cover module descriptor collection.

### Compatibility Considerations

The change does not add the "retained" property to archived
`compartment-map.json`, so new bundles should remain compatible with
older versions of `checkBundle`. However, I relaxed the schema validator
to get an unrelated test for `captureCompartmentMap` passing, so future
deployments of `checkBundle` will be more lenient.

### Upgrade Considerations

Should not affect upgrades.
  • Loading branch information
kriskowal authored Oct 24, 2024
2 parents d37a434 + e3b310d commit 4ca4028
Show file tree
Hide file tree
Showing 8 changed files with 45 additions and 13 deletions.
5 changes: 5 additions & 0 deletions packages/compartment-mapper/NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
User-visible changes to `@endo/compartment-mapper`:

# Next version

- Omits unused module descriptors from `compartment-map.json` in archived
applications, potentially reducing file sizes.

# v1.3.0 (2024-10-10)

- Adds support for dynamic requires in CommonJS modules. This requires specific
Expand Down
23 changes: 13 additions & 10 deletions packages/compartment-mapper/src/archive-lite.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,22 +148,25 @@ const translateCompartmentMap = (compartments, sources, compartmentRenames) => {
const result = create(null);
for (const compartmentName of keys(compartmentRenames)) {
const compartment = compartments[compartmentName];
const { name, label, retained, policy } = compartment;
if (retained) {
const { name, label, retained: compartmentRetained, policy } = compartment;
if (compartmentRetained) {
// rename module compartments
/** @type {Record<string, ModuleDescriptor>} */
const modules = create(null);
const compartmentModules = compartment.modules;
if (compartment.modules) {
for (const name of keys(compartmentModules).sort()) {
const module = compartmentModules[name];
if (module.compartment !== undefined) {
modules[name] = {
...module,
compartment: compartmentRenames[module.compartment],
};
} else {
modules[name] = module;
const { retained: moduleRetained, ...retainedModule } =
compartmentModules[name];
if (moduleRetained) {
if (retainedModule.compartment !== undefined) {
modules[name] = {
...retainedModule,
compartment: compartmentRenames[retainedModule.compartment],
};
} else {
modules[name] = retainedModule;
}
}
}
}
Expand Down
9 changes: 8 additions & 1 deletion packages/compartment-mapper/src/compartment-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const assertConditions = (conditions, url) => {
* @param {string} url
*/
const assertCompartmentModule = (allegedModule, path, url) => {
const { compartment, module, ...extra } = allegedModule;
const { compartment, module, retained, ...extra } = allegedModule;
assertEmptyObject(
extra,
`${path} must not have extra properties, got ${q({
Expand All @@ -125,6 +125,13 @@ const assertCompartmentModule = (allegedModule, path, url) => {
'string',
`${path}.module must be a string, got ${q(module)} in ${q(url)}`,
);
if (retained !== undefined) {
assert.typeof(
retained,
'boolean',
`${path}.retained must be a boolean, got ${q(retained)} in ${q(url)}`,
);
}
};

/**
Expand Down
2 changes: 2 additions & 0 deletions packages/compartment-mapper/src/import-hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ function* chooseModuleDescriptor(
for (const candidateSpecifier of candidates) {
const candidateModuleDescriptor = moduleDescriptors[candidateSpecifier];
if (candidateModuleDescriptor !== undefined) {
candidateModuleDescriptor.retained = true;
const { compartment: candidateCompartmentName = packageLocation } =
candidateModuleDescriptor;
const candidateCompartment = compartments[candidateCompartmentName];
Expand Down Expand Up @@ -339,6 +340,7 @@ function* chooseModuleDescriptor(
// module specifier than the requested one.
if (candidateSpecifier !== moduleSpecifier) {
moduleDescriptors[moduleSpecifier] = {
retained: true,
module: candidateSpecifier,
compartment: packageLocation,
};
Expand Down
3 changes: 3 additions & 0 deletions packages/compartment-mapper/src/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ const makeModuleMapHook = (

const moduleDescriptor = moduleDescriptors[moduleSpecifier];
if (moduleDescriptor !== undefined) {
moduleDescriptor.retained = true;

// "foreignCompartmentName" refers to the compartment which
// may differ from the current compartment
const {
Expand Down Expand Up @@ -193,6 +195,7 @@ const makeModuleMapHook = (
// a moduleMapHook when we assemble compartments from the resulting
// archive.
moduleDescriptors[moduleSpecifier] = {
retained: true,
compartment: foreignCompartmentName,
module: foreignModuleSpecifier,
};
Expand Down
3 changes: 2 additions & 1 deletion packages/compartment-mapper/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,14 @@ export {};
* `package.json`, there is a corresponding module descriptor.
*
* @typedef {object} ModuleDescriptor
* @property {string=} [compartment]
* @property {string} [compartment]
* @property {string} [module]
* @property {string} [location]
* @property {Language} [parser]
* @property {string} [sha512] in base 16, hex
* @property {string} [exit]
* @property {string} [deferredError]
* @property {boolean} [retained]
*/

/**
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions packages/compartment-mapper/test/retained.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,14 @@ test('archives only contain compartments retained by modules', async t => {
// Notably absent:
// 'sweep-v1.0.0',
]);
t.deepEqual(
Object.keys(compartmentMap.compartments['app-v1.0.0'].modules).sort(),
[
'./app.js',
// Notably absent: 'app',
'mk1',
// Notably absent: 'mk1/bogus.js',
// Notably absent: 'sweep',
],
);
});

0 comments on commit 4ca4028

Please sign in to comment.