Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v8 Synthetic Modules Implementation #29846

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 15 additions & 15 deletions lib/internal/bootstrap/loaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@ function NativeModule(id) {
this.filename = `${id}.js`;
this.id = id;
this.exports = {};
this.reflect = undefined;
this.esmFacade = undefined;
this.module = undefined;
this.exportKeys = undefined;
this.loaded = false;
this.loading = false;
Expand Down Expand Up @@ -240,16 +239,18 @@ NativeModule.prototype.getURL = function() {
};

NativeModule.prototype.getESMFacade = function() {
if (this.esmFacade) return this.esmFacade;
const createDynamicModule = nativeModuleRequire(
'internal/modules/esm/create_dynamic_module');
if (this.module) return this.module;
const { ModuleWrap } = internalBinding('module_wrap');
const url = this.getURL();
return this.esmFacade = createDynamicModule(
[], [...this.exportKeys, 'default'], url, (reflect) => {
this.reflect = reflect;
this.syncExports();
reflect.exports.default.set(this.exports);
});
const nativeModule = this;
this.module = new ModuleWrap(function() {
nativeModule.syncExports();
this.setExport('default', nativeModule.exports);
}, [...this.exportKeys, 'default'], url);
// Ensure immediate sync execution to capture exports now
this.module.instantiate();
this.module.evaluate(-1, false);
return this.module;
};

// Provide named exports for all builtin libraries so that the libraries
Expand All @@ -258,13 +259,12 @@ NativeModule.prototype.getESMFacade = function() {
// called so that APMs and other behavior are supported.
NativeModule.prototype.syncExports = function() {
const names = this.exportKeys;
if (this.reflect) {
if (this.module) {
for (let i = 0; i < names.length; i++) {
const exportName = names[i];
if (exportName === 'default') continue;
this.reflect.exports[exportName].set(
getOwn(this.exports, exportName, this.exports)
);
this.module.setExport(exportName,
getOwn(this.exports, exportName, this.exports));
}
}
};
Expand Down
28 changes: 11 additions & 17 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,7 @@ const experimentalExports = getOptionValue('--experimental-exports');

module.exports = { wrapSafe, Module };

let asyncESM;
let ModuleJob;
let createDynamicModule;
let asyncESM, ModuleJob, ModuleWrap, kInstantiated;

const {
CHAR_FORWARD_SLASH,
Expand Down Expand Up @@ -819,21 +817,18 @@ Module.prototype.load = function(filename) {
const module = ESMLoader.moduleMap.get(url);
// Create module entry at load time to snapshot exports correctly
const exports = this.exports;
if (module !== undefined) { // Called from cjs translator
if (module.reflect) {
module.reflect.onReady((reflect) => {
reflect.exports.default.set(exports);
});
}
// Called from cjs translator
if (module !== undefined && module.module !== undefined) {
if (module.module.getStatus() >= kInstantiated)
module.module.setExport('default', exports);
} else { // preemptively cache
ESMLoader.moduleMap.set(
url,
new ModuleJob(ESMLoader, url, async () => {
return createDynamicModule(
[], ['default'], url, (reflect) => {
reflect.exports.default.set(exports);
});
})
new ModuleJob(ESMLoader, url, () =>
new ModuleWrap(function() {
this.setExport('default', exports);
}, ['default'], url)
)
);
}
}
Expand Down Expand Up @@ -1150,6 +1145,5 @@ Module.Module = Module;
if (experimentalModules) {
asyncESM = require('internal/process/esm_loader');
ModuleJob = require('internal/modules/esm/module_job');
createDynamicModule = require(
'internal/modules/esm/create_dynamic_module');
({ ModuleWrap, kInstantiated } = internalBinding('module_wrap'));
}
9 changes: 2 additions & 7 deletions lib/internal/modules/esm/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -117,12 +117,7 @@ class Loader {
source,
url = pathToFileURL(`${process.cwd()}/[eval${++this.evalIndex}]`).href
) {
const evalInstance = async (url) => {
return {
module: new ModuleWrap(source, url),
reflect: undefined
};
};
const evalInstance = (url) => new ModuleWrap(source, url);
const job = new ModuleJob(this, url, evalInstance, false);
this.moduleMap.set(url, job);
const { module, result } = await job.run();
Expand Down Expand Up @@ -165,7 +160,7 @@ class Loader {
return createDynamicModule([], exports, url, (reflect) => {
debug(`Loading dynamic ${url}`);
execute(reflect.exports);
});
}).module;
};
} else {
if (!translators.has(format))
Expand Down
6 changes: 2 additions & 4 deletions lib/internal/modules/esm/module_job.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,17 @@ class ModuleJob {
// onto `this` by `link()` below once it has been resolved.
this.modulePromise = moduleProvider.call(loader, url, isMain);
this.module = undefined;
this.reflect = undefined;

// Wait for the ModuleWrap instance being linked with all dependencies.
const link = async () => {
({ module: this.module,
reflect: this.reflect } = await this.modulePromise);
this.module = await this.modulePromise;
assert(this.module instanceof ModuleWrap);

const dependencyJobs = [];
const promises = this.module.link(async (specifier) => {
const jobPromise = this.loader.getModuleJob(specifier, url);
dependencyJobs.push(jobPromise);
return (await (await jobPromise).modulePromise).module;
return (await jobPromise).modulePromise;
});

if (promises !== undefined)
Expand Down
42 changes: 20 additions & 22 deletions lib/internal/modules/esm/translators.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const {
const readFileAsync = promisify(fs.readFile);
const JsonParse = JSON.parse;
const { maybeCacheSourceMap } = require('internal/source_map/source_map_cache');
const moduleWrap = internalBinding('module_wrap');
const { ModuleWrap } = moduleWrap;

const debug = debuglog('esm');

Expand Down Expand Up @@ -77,22 +79,18 @@ translators.set('module', async function moduleStrategy(url) {
const source = `${await getSource(url)}`;
maybeCacheSourceMap(url, source);
debug(`Translating StandardModule ${url}`);
const { ModuleWrap, callbackMap } = internalBinding('module_wrap');
const module = new ModuleWrap(source, url);
callbackMap.set(module, {
moduleWrap.callbackMap.set(module, {
initializeImportMeta,
importModuleDynamically,
});
return {
module,
reflect: undefined,
};
return module;
});

// Strategy for loading a node-style CommonJS module
const isWindows = process.platform === 'win32';
const winSepRegEx = /\//g;
translators.set('commonjs', async function commonjsStrategy(url, isMain) {
translators.set('commonjs', function commonjsStrategy(url, isMain) {
debug(`Translating CJSModule ${url}`);
const pathname = internalURLModule.fileURLToPath(new URL(url));
const cached = this.cjsCache.get(url);
Expand All @@ -105,17 +103,17 @@ translators.set('commonjs', async function commonjsStrategy(url, isMain) {
];
if (module && module.loaded) {
const exports = module.exports;
return createDynamicModule([], ['default'], url, (reflect) => {
reflect.exports.default.set(exports);
});
return new ModuleWrap(function() {
this.setExport('default', exports);
}, ['default'], url);
}
return createDynamicModule([], ['default'], url, () => {
return new ModuleWrap(function() {
debug(`Loading CJSModule ${url}`);
// We don't care about the return val of _load here because Module#load
// will handle it for us by checking the loader registry and filling the
// exports like above
CJSModule._load(pathname, undefined, isMain);
});
}, ['default'], url);
});

// Strategy for loading a node builtin CommonJS module that isn't
Expand Down Expand Up @@ -145,9 +143,9 @@ translators.set('json', async function jsonStrategy(url) {
module = CJSModule._cache[modulePath];
if (module && module.loaded) {
const exports = module.exports;
return createDynamicModule([], ['default'], url, (reflect) => {
reflect.exports.default.set(exports);
});
return new ModuleWrap(function() {
this.setExport('default', exports);
}, ['default'], url);
}
}
const content = `${await getSource(url)}`;
Expand All @@ -158,9 +156,9 @@ translators.set('json', async function jsonStrategy(url) {
module = CJSModule._cache[modulePath];
if (module && module.loaded) {
const exports = module.exports;
return createDynamicModule(['default'], url, (reflect) => {
reflect.exports.default.set(exports);
});
return new ModuleWrap(function() {
this.setExport('default', exports);
}, ['default'], url);
}
}
try {
Expand All @@ -180,10 +178,10 @@ translators.set('json', async function jsonStrategy(url) {
if (pathname) {
CJSModule._cache[modulePath] = module;
}
return createDynamicModule([], ['default'], url, (reflect) => {
return new ModuleWrap(function() {
debug(`Parsing JSONModule ${url}`);
reflect.exports.default.set(module.exports);
});
this.setExport('default', module.exports);
}, ['default'], url);
});

// Strategy for loading a wasm module
Expand All @@ -206,5 +204,5 @@ translators.set('wasm', async function(url) {
const { exports } = new WebAssembly.Instance(compiled, reflect.imports);
for (const expt of Object.keys(exports))
reflect.exports[expt].set(exports[expt]);
});
}).module;
});