-
Notifications
You must be signed in to change notification settings - Fork 29.7k
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
vm: add dynamic import callback and streamline initialize import.meta callback to match it #19717
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,13 +12,14 @@ const { | |
ERR_VM_MODULE_LINKING_ERRORED, | ||
ERR_VM_MODULE_NOT_LINKED, | ||
ERR_VM_MODULE_NOT_MODULE, | ||
ERR_VM_MODULE_STATUS | ||
ERR_VM_MODULE_STATUS, | ||
} = require('internal/errors').codes; | ||
const { | ||
getConstructorOf, | ||
customInspectSymbol, | ||
} = require('internal/util'); | ||
const { SafePromise } = require('internal/safe_globals'); | ||
const { isModuleNamespaceObject } = require('util').types; | ||
|
||
const { | ||
ModuleWrap, | ||
|
@@ -44,10 +45,7 @@ const perContextModuleId = new WeakMap(); | |
const wrapMap = new WeakMap(); | ||
const dependencyCacheMap = new WeakMap(); | ||
const linkingStatusMap = new WeakMap(); | ||
// vm.Module -> function | ||
const initImportMetaMap = new WeakMap(); | ||
// ModuleWrap -> vm.Module | ||
const wrapToModuleMap = new WeakMap(); | ||
const linkerFnMap = new WeakMap(); | ||
|
||
class Module { | ||
constructor(src, options = {}) { | ||
|
@@ -62,7 +60,6 @@ class Module { | |
context, | ||
lineOffset = 0, | ||
columnOffset = 0, | ||
initializeImportMeta | ||
} = options; | ||
|
||
if (context !== undefined) { | ||
|
@@ -95,19 +92,34 @@ class Module { | |
validateInteger(lineOffset, 'options.lineOffset'); | ||
validateInteger(columnOffset, 'options.columnOffset'); | ||
|
||
let { initializeImportMeta } = options; | ||
if (initializeImportMeta !== undefined) { | ||
if (typeof initializeImportMeta === 'function') { | ||
initImportMetaMap.set(this, initializeImportMeta); | ||
const fn = initializeImportMeta; | ||
initializeImportMeta = (meta) => fn(meta, this); | ||
} else { | ||
throw new ERR_INVALID_ARG_TYPE( | ||
'options.initializeImportMeta', 'function', initializeImportMeta); | ||
} | ||
} | ||
|
||
const wrap = new ModuleWrap(src, url, context, lineOffset, columnOffset); | ||
|
||
const { | ||
initializeImportMetaMap, | ||
importModuleDynamicallyMap, | ||
} = require('internal/process/esm_loader'); | ||
|
||
if (initializeImportMeta) | ||
initializeImportMetaMap.set(wrap, initializeImportMeta); | ||
|
||
importModuleDynamicallyMap.set(wrap, async (specifier) => { | ||
const linker = linkerFnMap.get(this); | ||
return callLinkerForNamespace(linker, specifier, this); | ||
}); | ||
|
||
wrapMap.set(this, wrap); | ||
linkingStatusMap.set(this, 'unlinked'); | ||
wrapToModuleMap.set(wrap, this); | ||
|
||
Object.defineProperties(this, { | ||
url: { value: url, enumerable: true }, | ||
|
@@ -160,20 +172,9 @@ class Module { | |
throw new ERR_VM_MODULE_STATUS('must be uninstantiated'); | ||
|
||
linkingStatusMap.set(this, 'linking'); | ||
linkerFnMap.set(this, linker); | ||
|
||
const promises = wrap.link(async (specifier) => { | ||
const m = await linker(specifier, this); | ||
if (!m || !wrapMap.has(m)) | ||
throw new ERR_VM_MODULE_NOT_MODULE(); | ||
if (m.context !== this.context) | ||
throw new ERR_VM_MODULE_DIFFERENT_CONTEXT(); | ||
const childLinkingStatus = linkingStatusMap.get(m); | ||
if (childLinkingStatus === 'errored') | ||
throw new ERR_VM_MODULE_LINKING_ERRORED(); | ||
if (childLinkingStatus === 'unlinked') | ||
await m.link(linker); | ||
return wrapMap.get(m); | ||
}); | ||
const promises = wrap.link((s) => callLinkerForModuleWrap(linker, s, this)); | ||
|
||
try { | ||
if (promises !== undefined) | ||
|
@@ -252,8 +253,43 @@ function validateInteger(prop, propName) { | |
} | ||
} | ||
|
||
async function getWrapFromModule(m, scriptOrModule, linker) { | ||
if (!m || !wrapMap.has(m)) | ||
throw new ERR_VM_MODULE_NOT_MODULE(); | ||
|
||
if (scriptOrModule instanceof Module && | ||
(m.context !== scriptOrModule.context)) | ||
throw new ERR_VM_MODULE_DIFFERENT_CONTEXT(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we not need the context check in the script case as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. good point. I'm not quite sure how/if it's possible to get the context it's currently running the code in though. ideas are appreciated |
||
|
||
const childLinkingStatus = linkingStatusMap.get(m); | ||
|
||
if (childLinkingStatus === 'errored') | ||
throw new ERR_VM_MODULE_LINKING_ERRORED(); | ||
if (childLinkingStatus === 'unlinked') | ||
await m.link(linker); | ||
|
||
return wrapMap.get(m); | ||
} | ||
|
||
async function callLinkerForModuleWrap(linker, specifier, scriptOrModule) { | ||
const m = await linker(specifier, scriptOrModule); | ||
return getWrapFromModule(m, scriptOrModule, linker); | ||
} | ||
|
||
async function callLinkerForNamespace(linker, specifier, scriptOrModule) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if returning a vm.Module instance that has not been linked (and that has dependencies)? Can we have a test for this error case? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is already tested by |
||
const m = await linker(specifier, scriptOrModule); | ||
if (isModuleNamespaceObject(m)) | ||
return m; | ||
const wrap = await getWrapFromModule(m, scriptOrModule, linker); | ||
const status = wrap.getStatus(); | ||
if (status < kInstantiated) | ||
wrap.instantiate(); | ||
if (status < kEvaluated) | ||
await wrap.evaluate(-1, false); | ||
return wrap.namespace(); | ||
} | ||
|
||
module.exports = { | ||
Module, | ||
initImportMetaMap, | ||
wrapToModuleMap | ||
callLinkerForNamespace, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -49,6 +49,7 @@ const { | |
} = require('internal/modules/cjs/helpers'); | ||
const internalUtil = require('internal/util'); | ||
const { isTypedArray } = require('internal/util/types'); | ||
const { getURLFromFilePath } = require('internal/url'); | ||
const util = require('util'); | ||
const utilBinding = process.binding('util'); | ||
const { inherits } = util; | ||
|
@@ -256,7 +257,17 @@ function REPLServer(prompt, | |
} | ||
script = vm.createScript(code, { | ||
filename: file, | ||
displayErrors: true | ||
displayErrors: true, | ||
async resolveDynamicImport(specifier, scriptOrModule) { | ||
const loader = await require('internal/process/esm_loader') | ||
.loaderPromise; | ||
|
||
const filename = scriptOrModule.filename; | ||
const referrer = scriptOrModule.url || | ||
getURLFromFilePath(filename === 'repl' ? | ||
path.join(process.cwd(), filename) : filename).href; | ||
return loader.import(specifier, referrer); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How come this can work without any There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it doesn't work, it's wrong and needs a test :D |
||
}, | ||
}); | ||
} catch (e) { | ||
debug('parse error %j', code, e); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍