Skip to content

Commit

Permalink
module: use v8 synthetic modules
Browse files Browse the repository at this point in the history
PR-URL: #29846
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Minwoo Jung <minwoo@nodesource.com>
  • Loading branch information
guybedford committed Oct 6, 2019
1 parent 0521a98 commit ffd22e8
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 96 deletions.
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;
});

0 comments on commit ffd22e8

Please sign in to comment.