summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorGus Caplan <me@gus.host>2018-01-13 23:35:51 -0800
committerTimothy Gu <timothygu99@gmail.com>2018-01-30 17:00:57 -0800
commit0993fbe5b213d0fe746c3162bcda85f6c66bb552 (patch)
treef0a1ffef2ab24f1da963a23a43f3dd58eed5bba0 /test
parent2033a9f4362ee46b0fbddf9caf9cf6238391561e (diff)
downloadandroid-node-v8-0993fbe5b213d0fe746c3162bcda85f6c66bb552.tar.gz
android-node-v8-0993fbe5b213d0fe746c3162bcda85f6c66bb552.tar.bz2
android-node-v8-0993fbe5b213d0fe746c3162bcda85f6c66bb552.zip
vm: add modules
Adds vm.Module, which wraps around ModuleWrap to provide an interface for developers to work with modules in a more reflective manner. Co-authored-by: Timothy Gu <timothygu99@gmail.com> PR-URL: https://github.com/nodejs/node/pull/17560 Reviewed-By: Michaƫl Zasso <targos@protonmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Diffstat (limited to 'test')
-rw-r--r--test/parallel/test-vm-module-basic.js54
-rw-r--r--test/parallel/test-vm-module-dynamic-import.js27
-rw-r--r--test/parallel/test-vm-module-errors.js264
-rw-r--r--test/parallel/test-vm-module-link.js135
-rw-r--r--test/parallel/test-vm-module-reevaluate.js49
5 files changed, 529 insertions, 0 deletions
diff --git a/test/parallel/test-vm-module-basic.js b/test/parallel/test-vm-module-basic.js
new file mode 100644
index 0000000000..4bbe0a95ee
--- /dev/null
+++ b/test/parallel/test-vm-module-basic.js
@@ -0,0 +1,54 @@
+'use strict';
+
+// Flags: --experimental-vm-modules
+
+const common = require('../common');
+const assert = require('assert');
+const { Module, createContext } = require('vm');
+
+common.crashOnUnhandledRejection();
+
+(async function test1() {
+ const context = createContext({
+ foo: 'bar',
+ baz: undefined,
+ typeofProcess: undefined,
+ });
+ const m = new Module(
+ 'baz = foo; typeofProcess = typeof process; typeof Object;',
+ { context }
+ );
+ assert.strictEqual(m.status, 'uninstantiated');
+ await m.link(common.mustNotCall());
+ m.instantiate();
+ assert.strictEqual(m.status, 'instantiated');
+ const result = await m.evaluate();
+ assert.strictEqual(m.status, 'evaluated');
+ assert.strictEqual(Object.getPrototypeOf(result), null);
+ assert.deepStrictEqual(context, {
+ foo: 'bar',
+ baz: 'bar',
+ typeofProcess: 'undefined'
+ });
+ assert.strictEqual(result.result, 'function');
+}());
+
+(async () => {
+ const m = new Module(
+ 'global.vmResult = "foo"; Object.prototype.toString.call(process);'
+ );
+ await m.link(common.mustNotCall());
+ m.instantiate();
+ const { result } = await m.evaluate();
+ assert.strictEqual(global.vmResult, 'foo');
+ assert.strictEqual(result, '[object process]');
+ delete global.vmResult;
+})();
+
+(async () => {
+ const m = new Module('while (true) {}');
+ await m.link(common.mustNotCall());
+ m.instantiate();
+ await m.evaluate({ timeout: 500 })
+ .then(() => assert(false), () => {});
+})();
diff --git a/test/parallel/test-vm-module-dynamic-import.js b/test/parallel/test-vm-module-dynamic-import.js
new file mode 100644
index 0000000000..ca4dceb5de
--- /dev/null
+++ b/test/parallel/test-vm-module-dynamic-import.js
@@ -0,0 +1,27 @@
+'use strict';
+
+// Flags: --experimental-vm-modules --experimental-modules --harmony-dynamic-import
+
+const common = require('../common');
+common.crashOnUnhandledRejection();
+
+const assert = require('assert');
+const { Module, createContext } = require('vm');
+
+const finished = common.mustCall();
+
+(async function() {
+ const m = new Module('import("foo")', { context: createContext() });
+ await m.link(common.mustNotCall());
+ m.instantiate();
+ const { result } = await m.evaluate();
+ let threw = false;
+ try {
+ await result;
+ } catch (err) {
+ threw = true;
+ assert.strictEqual(err.message, 'import() called outside of main context');
+ }
+ assert(threw);
+ finished();
+}());
diff --git a/test/parallel/test-vm-module-errors.js b/test/parallel/test-vm-module-errors.js
new file mode 100644
index 0000000000..8bcb101ccc
--- /dev/null
+++ b/test/parallel/test-vm-module-errors.js
@@ -0,0 +1,264 @@
+'use strict';
+
+// Flags: --experimental-vm-modules
+
+const common = require('../common');
+common.crashOnUnhandledRejection();
+
+const assert = require('assert');
+
+const { Module, createContext } = require('vm');
+
+async function expectsRejection(fn, settings) {
+ const validateError = common.expectsError(settings);
+ // Retain async context.
+ const storedError = new Error('Thrown from:');
+ try {
+ await fn();
+ } catch (err) {
+ try {
+ validateError(err);
+ } catch (validationError) {
+ console.error(validationError);
+ console.error('Original error:');
+ console.error(err);
+ throw storedError;
+ }
+ return;
+ }
+ assert.fail('Missing expected exception');
+}
+
+async function createEmptyLinkedModule() {
+ const m = new Module('');
+ await m.link(common.mustNotCall());
+ return m;
+}
+
+async function checkArgType() {
+ common.expectsError(() => {
+ new Module();
+ }, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ type: TypeError
+ });
+
+ for (const invalidOptions of [
+ 0, 1, null, true, 'str', () => {}, Symbol.iterator
+ ]) {
+ common.expectsError(() => {
+ new Module('', invalidOptions);
+ }, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ type: TypeError
+ });
+ }
+
+ for (const invalidLinker of [
+ 0, 1, undefined, null, true, 'str', {}, Symbol.iterator
+ ]) {
+ await expectsRejection(async () => {
+ const m = new Module('');
+ await m.link(invalidLinker);
+ }, {
+ code: 'ERR_INVALID_ARG_TYPE',
+ type: TypeError
+ });
+ }
+}
+
+// Check methods/properties can only be used under a specific state.
+async function checkModuleState() {
+ await expectsRejection(async () => {
+ const m = new Module('');
+ await m.link(common.mustNotCall());
+ assert.strictEqual(m.linkingStatus, 'linked');
+ await m.link(common.mustNotCall());
+ }, {
+ code: 'ERR_VM_MODULE_ALREADY_LINKED'
+ });
+
+ await expectsRejection(async () => {
+ const m = new Module('');
+ m.link(common.mustNotCall());
+ assert.strictEqual(m.linkingStatus, 'linking');
+ await m.link(common.mustNotCall());
+ }, {
+ code: 'ERR_VM_MODULE_ALREADY_LINKED'
+ });
+
+ common.expectsError(() => {
+ const m = new Module('');
+ m.instantiate();
+ }, {
+ code: 'ERR_VM_MODULE_NOT_LINKED'
+ });
+
+ await expectsRejection(async () => {
+ const m = new Module('import "foo";');
+ try {
+ await m.link(common.mustCall(() => ({})));
+ } catch (err) {
+ assert.strictEqual(m.linkingStatus, 'errored');
+ m.instantiate();
+ }
+ assert.fail('Unreachable');
+ }, {
+ code: 'ERR_VM_MODULE_NOT_LINKED'
+ });
+
+ {
+ const m = new Module('import "foo";');
+ await m.link(common.mustCall(async (module, specifier) => {
+ assert.strictEqual(module, m);
+ assert.strictEqual(specifier, 'foo');
+ assert.strictEqual(m.linkingStatus, 'linking');
+ common.expectsError(() => {
+ m.instantiate();
+ }, {
+ code: 'ERR_VM_MODULE_NOT_LINKED'
+ });
+ return new Module('');
+ }));
+ m.instantiate();
+ await m.evaluate();
+ }
+
+ await expectsRejection(async () => {
+ const m = new Module('');
+ await m.evaluate();
+ }, {
+ code: 'ERR_VM_MODULE_STATUS',
+ message: 'Module status must be one of instantiated, evaluated, and errored'
+ });
+
+ await expectsRejection(async () => {
+ const m = await createEmptyLinkedModule();
+ await m.evaluate();
+ }, {
+ code: 'ERR_VM_MODULE_STATUS',
+ message: 'Module status must be one of instantiated, evaluated, and errored'
+ });
+
+ common.expectsError(() => {
+ const m = new Module('');
+ m.error;
+ }, {
+ code: 'ERR_VM_MODULE_STATUS',
+ message: 'Module status must be errored'
+ });
+
+ await expectsRejection(async () => {
+ const m = await createEmptyLinkedModule();
+ m.instantiate();
+ await m.evaluate();
+ m.error;
+ }, {
+ code: 'ERR_VM_MODULE_STATUS',
+ message: 'Module status must be errored'
+ });
+
+ common.expectsError(() => {
+ const m = new Module('');
+ m.namespace;
+ }, {
+ code: 'ERR_VM_MODULE_STATUS',
+ message: 'Module status must not be uninstantiated or instantiating'
+ });
+
+ await expectsRejection(async () => {
+ const m = await createEmptyLinkedModule();
+ m.namespace;
+ }, {
+ code: 'ERR_VM_MODULE_STATUS',
+ message: 'Module status must not be uninstantiated or instantiating'
+ });
+}
+
+// Check link() fails when the returned module is not valid.
+async function checkLinking() {
+ await expectsRejection(async () => {
+ const m = new Module('import "foo";');
+ try {
+ await m.link(common.mustCall(() => ({})));
+ } catch (err) {
+ assert.strictEqual(m.linkingStatus, 'errored');
+ throw err;
+ }
+ assert.fail('Unreachable');
+ }, {
+ code: 'ERR_VM_MODULE_NOT_MODULE'
+ });
+
+ await expectsRejection(async () => {
+ const c = createContext({ a: 1 });
+ const foo = new Module('', { context: c });
+ await foo.link(common.mustNotCall());
+ const bar = new Module('import "foo";');
+ try {
+ await bar.link(common.mustCall(() => foo));
+ } catch (err) {
+ assert.strictEqual(bar.linkingStatus, 'errored');
+ throw err;
+ }
+ assert.fail('Unreachable');
+ }, {
+ code: 'ERR_VM_MODULE_DIFFERENT_CONTEXT'
+ });
+
+ await expectsRejection(async () => {
+ const erroredModule = new Module('import "foo";');
+ try {
+ await erroredModule.link(common.mustCall(() => ({})));
+ } catch (err) {
+ // ignored
+ } finally {
+ assert.strictEqual(erroredModule.linkingStatus, 'errored');
+ }
+
+ const rootModule = new Module('import "errored";');
+ await rootModule.link(common.mustCall(() => erroredModule));
+ }, {
+ code: 'ERR_VM_MODULE_LINKING_ERRORED'
+ });
+}
+
+// Check the JavaScript engine deals with exceptions correctly
+async function checkExecution() {
+ await (async () => {
+ const m = new Module('import { nonexistent } from "module";');
+ await m.link(common.mustCall(() => new Module('')));
+
+ // There is no code for this exception since it is thrown by the JavaScript
+ // engine.
+ assert.throws(() => {
+ m.instantiate();
+ }, SyntaxError);
+ })();
+
+ await (async () => {
+ const m = new Module('throw new Error();');
+ await m.link(common.mustNotCall());
+ m.instantiate();
+ const evaluatePromise = m.evaluate();
+ await evaluatePromise.catch(() => {});
+ assert.strictEqual(m.status, 'errored');
+ try {
+ await evaluatePromise;
+ } catch (err) {
+ assert.strictEqual(m.error, err);
+ return;
+ }
+ assert.fail('Missing expected exception');
+ })();
+}
+
+const finished = common.mustCall();
+
+(async function main() {
+ await checkArgType();
+ await checkModuleState();
+ await checkLinking();
+ await checkExecution();
+ finished();
+})();
diff --git a/test/parallel/test-vm-module-link.js b/test/parallel/test-vm-module-link.js
new file mode 100644
index 0000000000..870427e91b
--- /dev/null
+++ b/test/parallel/test-vm-module-link.js
@@ -0,0 +1,135 @@
+'use strict';
+
+// Flags: --experimental-vm-modules
+
+const common = require('../common');
+common.crashOnUnhandledRejection();
+
+const assert = require('assert');
+const { URL } = require('url');
+
+const { Module } = require('vm');
+
+async function simple() {
+ const foo = new Module('export default 5;');
+ await foo.link(common.mustNotCall());
+
+ const bar = new Module('import five from "foo"; five');
+
+ assert.deepStrictEqual(bar.dependencySpecifiers, ['foo']);
+
+ await bar.link(common.mustCall((module, specifier) => {
+ assert.strictEqual(module, bar);
+ assert.strictEqual(specifier, 'foo');
+ return foo;
+ }));
+
+ bar.instantiate();
+
+ assert.strictEqual((await bar.evaluate()).result, 5);
+}
+
+async function depth() {
+ const foo = new Module('export default 5');
+ await foo.link(common.mustNotCall());
+
+ async function getProxy(parentName, parentModule) {
+ const mod = new Module(`
+ import ${parentName} from '${parentName}';
+ export default ${parentName};
+ `);
+ await mod.link(common.mustCall((module, specifier) => {
+ assert.strictEqual(module, mod);
+ assert.strictEqual(specifier, parentName);
+ return parentModule;
+ }));
+ return mod;
+ }
+
+ const bar = await getProxy('foo', foo);
+ const baz = await getProxy('bar', bar);
+ const barz = await getProxy('baz', baz);
+
+ barz.instantiate();
+ await barz.evaluate();
+
+ assert.strictEqual(barz.namespace.default, 5);
+}
+
+async function circular() {
+ const foo = new Module(`
+ import getFoo from 'bar';
+ export let foo = 42;
+ export default getFoo();
+ `);
+ const bar = new Module(`
+ import { foo } from 'foo';
+ export default function getFoo() {
+ return foo;
+ }
+ `);
+ await foo.link(common.mustCall(async (fooModule, fooSpecifier) => {
+ assert.strictEqual(fooModule, foo);
+ assert.strictEqual(fooSpecifier, 'bar');
+ await bar.link(common.mustCall((barModule, barSpecifier) => {
+ assert.strictEqual(barModule, bar);
+ assert.strictEqual(barSpecifier, 'foo');
+ assert.strictEqual(foo.linkingStatus, 'linking');
+ return foo;
+ }));
+ assert.strictEqual(bar.linkingStatus, 'linked');
+ return bar;
+ }));
+
+ foo.instantiate();
+ await foo.evaluate();
+ assert.strictEqual(foo.namespace.default, 42);
+}
+
+async function circular2() {
+ const sourceMap = {
+ root: `
+ import * as a from './a.mjs';
+ import * as b from './b.mjs';
+ if (!('fromA' in a))
+ throw new Error();
+ if (!('fromB' in a))
+ throw new Error();
+ if (!('fromA' in b))
+ throw new Error();
+ if (!('fromB' in b))
+ throw new Error();
+ `,
+ './a.mjs': `
+ export * from './b.mjs';
+ export var fromA;
+ `,
+ './b.mjs': `
+ export * from './a.mjs';
+ export var fromB;
+ `
+ };
+ const moduleMap = new Map();
+ const rootModule = new Module(sourceMap.root, { url: 'vm:root' });
+ async function link(referencingModule, specifier) {
+ if (moduleMap.has(specifier)) {
+ return moduleMap.get(specifier);
+ }
+ const mod = new Module(sourceMap[specifier], { url: new URL(specifier, 'file:///').href });
+ moduleMap.set(specifier, mod);
+ return mod;
+ }
+ await rootModule.link(link);
+ rootModule.instantiate();
+ await rootModule.evaluate();
+}
+
+const finished = common.mustCall();
+
+(async function main() {
+ await simple();
+ await depth();
+ await circular();
+ await circular2();
+ finished();
+})();
diff --git a/test/parallel/test-vm-module-reevaluate.js b/test/parallel/test-vm-module-reevaluate.js
new file mode 100644
index 0000000000..e4f5858800
--- /dev/null
+++ b/test/parallel/test-vm-module-reevaluate.js
@@ -0,0 +1,49 @@
+'use strict';
+
+// Flags: --experimental-vm-modules
+
+const common = require('../common');
+common.crashOnUnhandledRejection();
+
+const assert = require('assert');
+
+const { Module } = require('vm');
+
+const finished = common.mustCall();
+
+(async function main() {
+ {
+ const m = new Module('1');
+ await m.link(common.mustNotCall());
+ m.instantiate();
+ assert.strictEqual((await m.evaluate()).result, 1);
+ assert.strictEqual((await m.evaluate()).result, undefined);
+ assert.strictEqual((await m.evaluate()).result, undefined);
+ }
+
+ {
+ const m = new Module('throw new Error()');
+ await m.link(common.mustNotCall());
+ m.instantiate();
+
+ let threw = false;
+ try {
+ await m.evaluate();
+ } catch (err) {
+ assert(err instanceof Error);
+ threw = true;
+ }
+ assert(threw);
+
+ threw = false;
+ try {
+ await m.evaluate();
+ } catch (err) {
+ assert(err instanceof Error);
+ threw = true;
+ }
+ assert(threw);
+ }
+
+ finished();
+})();