summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorpunteek <prateek1@ucla.edu>2018-03-06 19:46:39 -0800
committerGus Caplan <me@gus.host>2018-03-31 19:55:50 -0500
commit07ba9141e475ec63f6ef56b67ec5f98077cd3446 (patch)
tree99f36abde8cd6fd8b4a074fdfe8b1d8fe7fa6388
parent28b622cb08602d77512fa3d659451cd317dfcc41 (diff)
downloadandroid-node-v8-07ba9141e475ec63f6ef56b67ec5f98077cd3446.tar.gz
android-node-v8-07ba9141e475ec63f6ef56b67ec5f98077cd3446.tar.bz2
android-node-v8-07ba9141e475ec63f6ef56b67ec5f98077cd3446.zip
vm: add support for import.meta to Module
Fixes: https://github.com/nodejs/node/issues/18570 PR-URL: https://github.com/nodejs/node/pull/19277 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
-rw-r--r--doc/api/vm.md40
-rw-r--r--lib/internal/bootstrap/node.js11
-rw-r--r--lib/internal/process/esm_loader.js17
-rw-r--r--lib/internal/vm/module.js19
-rw-r--r--test/parallel/test-vm-module-import-meta.js45
5 files changed, 126 insertions, 6 deletions
diff --git a/doc/api/vm.md b/doc/api/vm.md
index c729ef5999..ed7be81776 100644
--- a/doc/api/vm.md
+++ b/doc/api/vm.md
@@ -167,9 +167,49 @@ const contextifiedSandbox = vm.createContext({ secret: 42 });
in stack traces produced by this Module.
* `columnOffset` {integer} Spcifies the column number offset that is displayed
in stack traces produced by this Module.
+ * `initalizeImportMeta` {Function} Called during evaluation of this Module to
+ initialize the `import.meta`. This function has the signature `(meta,
+ module)`, where `meta` is the `import.meta` object in the Module, and
+ `module` is this `vm.Module` object.
Creates a new ES `Module` object.
+*Note*: Properties assigned to the `import.meta` object that are objects may
+allow the Module to access information outside the specified `context`, if the
+object is created in the top level context. Use `vm.runInContext()` to create
+objects in a specific context.
+
+```js
+const vm = require('vm');
+
+const contextifiedSandbox = vm.createContext({ secret: 42 });
+
+(async () => {
+ const module = new vm.Module(
+ 'Object.getPrototypeOf(import.meta.prop).secret = secret;',
+ {
+ initializeImportMeta(meta) {
+ // Note: this object is created in the top context. As such,
+ // Object.getPrototypeOf(import.meta.prop) points to the
+ // Object.prototype in the top context rather than that in
+ // the sandbox.
+ meta.prop = {};
+ }
+ });
+ // Since module has no dependencies, the linker function will never be called.
+ await module.link(() => {});
+ module.initialize();
+ await module.evaluate();
+
+ // Now, Object.prototype.secret will be equal to 42.
+ //
+ // To fix this problem, replace
+ // meta.prop = {};
+ // above with
+ // meta.prop = vm.runInContext('{}', contextifiedSandbox);
+})();
+```
+
### module.dependencySpecifiers
* {string[]}
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index 01efe6f329..99bf00a6d6 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -108,10 +108,13 @@
'DeprecationWarning', 'DEP0062', startup, true);
}
- if (process.binding('config').experimentalModules) {
- process.emitWarning(
- 'The ESM module loader is experimental.',
- 'ExperimentalWarning', undefined);
+ if (process.binding('config').experimentalModules ||
+ process.binding('config').experimentalVMModules) {
+ if (process.binding('config').experimentalModules) {
+ process.emitWarning(
+ 'The ESM module loader is experimental.',
+ 'ExperimentalWarning', undefined);
+ }
NativeModule.require('internal/process/esm_loader').setup();
}
diff --git a/lib/internal/process/esm_loader.js b/lib/internal/process/esm_loader.js
index bcb6501af6..db28ca04b1 100644
--- a/lib/internal/process/esm_loader.js
+++ b/lib/internal/process/esm_loader.js
@@ -10,6 +10,10 @@ const { getURLFromFilePath } = require('internal/url');
const Loader = require('internal/modules/esm/loader');
const path = require('path');
const { URL } = require('url');
+const {
+ initImportMetaMap,
+ wrapToModuleMap
+} = require('internal/vm/module');
function normalizeReferrerURL(referrer) {
if (typeof referrer === 'string' && path.isAbsolute(referrer)) {
@@ -19,7 +23,18 @@ function normalizeReferrerURL(referrer) {
}
function initializeImportMetaObject(wrap, meta) {
- meta.url = wrap.url;
+ const vmModule = wrapToModuleMap.get(wrap);
+ if (vmModule === undefined) {
+ // This ModuleWrap belongs to the Loader.
+ meta.url = wrap.url;
+ } else {
+ const initializeImportMeta = initImportMetaMap.get(vmModule);
+ if (initializeImportMeta !== undefined) {
+ // This ModuleWrap belongs to vm.Module, initializer callback was
+ // provided.
+ initializeImportMeta(meta, vmModule);
+ }
+ }
}
let loaderResolve;
diff --git a/lib/internal/vm/module.js b/lib/internal/vm/module.js
index 48d591f3bf..9af071ce28 100644
--- a/lib/internal/vm/module.js
+++ b/lib/internal/vm/module.js
@@ -43,6 +43,10 @@ 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();
class Module {
constructor(src, options = {}) {
@@ -80,6 +84,16 @@ class Module {
perContextModuleId.set(context, 1);
}
+ if (options.initializeImportMeta !== undefined) {
+ if (typeof options.initializeImportMeta === 'function') {
+ initImportMetaMap.set(this, options.initializeImportMeta);
+ } else {
+ throw new ERR_INVALID_ARG_TYPE(
+ 'options.initializeImportMeta', 'function',
+ options.initializeImportMeta);
+ }
+ }
+
const wrap = new ModuleWrap(src, url, {
[kParsingContext]: context,
lineOffset: options.lineOffset,
@@ -88,6 +102,7 @@ class Module {
wrapMap.set(this, wrap);
linkingStatusMap.set(this, 'unlinked');
+ wrapToModuleMap.set(wrap, this);
Object.defineProperties(this, {
url: { value: url, enumerable: true },
@@ -206,5 +221,7 @@ class Module {
}
module.exports = {
- Module
+ Module,
+ initImportMetaMap,
+ wrapToModuleMap
};
diff --git a/test/parallel/test-vm-module-import-meta.js b/test/parallel/test-vm-module-import-meta.js
new file mode 100644
index 0000000000..835ef5b6eb
--- /dev/null
+++ b/test/parallel/test-vm-module-import-meta.js
@@ -0,0 +1,45 @@
+'use strict';
+
+// Flags: --experimental-vm-modules --harmony-import-meta
+
+const common = require('../common');
+const assert = require('assert');
+const { Module } = require('vm');
+
+common.crashOnUnhandledRejection();
+
+async function testBasic() {
+ const m = new Module('import.meta;', {
+ initializeImportMeta: common.mustCall((meta, module) => {
+ assert.strictEqual(module, m);
+ meta.prop = 42;
+ })
+ });
+ await m.link(common.mustNotCall());
+ m.instantiate();
+ const { result } = await m.evaluate();
+ assert.strictEqual(typeof result, 'object');
+ assert.strictEqual(Object.getPrototypeOf(result), null);
+ assert.strictEqual(result.prop, 42);
+ assert.deepStrictEqual(Reflect.ownKeys(result), ['prop']);
+}
+
+async function testInvalid() {
+ for (const invalidValue of [
+ null, {}, 0, Symbol.iterator, [], 'string', false
+ ]) {
+ common.expectsError(() => {
+ new Module('', {
+ initializeImportMeta: invalidValue
+ });
+ }, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ type: TypeError
+ });
+ }
+}
+
+(async () => {
+ await testBasic();
+ await testInvalid();
+})();