• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3// Flags: --experimental-vm-modules
4
5const common = require('../common');
6
7const assert = require('assert');
8
9const { SourceTextModule, createContext } = require('vm');
10
11async function createEmptyLinkedModule() {
12  const m = new SourceTextModule('');
13  await m.link(common.mustNotCall());
14  return m;
15}
16
17async function checkArgType() {
18  assert.throws(() => {
19    new SourceTextModule();
20  }, {
21    code: 'ERR_INVALID_ARG_TYPE',
22    name: 'TypeError'
23  });
24
25  for (const invalidOptions of [
26    0, 1, null, true, 'str', () => {}, { identifier: 0 }, Symbol.iterator,
27    { context: null }, { context: 'hucairz' }, { context: {} }
28  ]) {
29    assert.throws(() => {
30      new SourceTextModule('', invalidOptions);
31    }, {
32      code: 'ERR_INVALID_ARG_TYPE',
33      name: 'TypeError'
34    });
35  }
36
37  for (const invalidLinker of [
38    0, 1, undefined, null, true, 'str', {}, Symbol.iterator
39  ]) {
40    await assert.rejects(async () => {
41      const m = new SourceTextModule('');
42      await m.link(invalidLinker);
43    }, {
44      code: 'ERR_INVALID_ARG_TYPE',
45      name: 'TypeError'
46    });
47  }
48}
49
50// Check methods/properties can only be used under a specific state.
51async function checkModuleState() {
52  await assert.rejects(async () => {
53    const m = new SourceTextModule('');
54    await m.link(common.mustNotCall());
55    assert.strictEqual(m.status, 'linked');
56    await m.link(common.mustNotCall());
57  }, {
58    code: 'ERR_VM_MODULE_ALREADY_LINKED'
59  });
60
61  await assert.rejects(async () => {
62    const m = new SourceTextModule('');
63    m.link(common.mustNotCall());
64    assert.strictEqual(m.status, 'linking');
65    await m.link(common.mustNotCall());
66  }, {
67    code: 'ERR_VM_MODULE_STATUS'
68  });
69
70  await assert.rejects(async () => {
71    const m = new SourceTextModule('');
72    await m.evaluate();
73  }, {
74    code: 'ERR_VM_MODULE_STATUS',
75    message: 'Module status must be one of linked, evaluated, or errored'
76  });
77
78  await assert.rejects(async () => {
79    const m = new SourceTextModule('');
80    await m.evaluate(false);
81  }, {
82    code: 'ERR_INVALID_ARG_TYPE',
83    message: 'The "options" argument must be of type object. ' +
84             'Received type boolean (false)'
85  });
86
87  assert.throws(() => {
88    const m = new SourceTextModule('');
89    m.error;
90  }, {
91    code: 'ERR_VM_MODULE_STATUS',
92    message: 'Module status must be errored'
93  });
94
95  await assert.rejects(async () => {
96    const m = await createEmptyLinkedModule();
97    await m.evaluate();
98    m.error;
99  }, {
100    code: 'ERR_VM_MODULE_STATUS',
101    message: 'Module status must be errored'
102  });
103
104  assert.throws(() => {
105    const m = new SourceTextModule('');
106    m.namespace;
107  }, {
108    code: 'ERR_VM_MODULE_STATUS',
109    message: 'Module status must not be unlinked or linking'
110  });
111}
112
113// Check link() fails when the returned module is not valid.
114async function checkLinking() {
115  await assert.rejects(async () => {
116    const m = new SourceTextModule('import "foo";');
117    try {
118      await m.link(common.mustCall(() => ({})));
119    } catch (err) {
120      assert.strictEqual(m.status, 'errored');
121      throw err;
122    }
123  }, {
124    code: 'ERR_VM_MODULE_NOT_MODULE'
125  });
126
127  await assert.rejects(async () => {
128    const c = createContext({ a: 1 });
129    const foo = new SourceTextModule('', { context: c });
130    await foo.link(common.mustNotCall());
131    const bar = new SourceTextModule('import "foo";');
132    try {
133      await bar.link(common.mustCall(() => foo));
134    } catch (err) {
135      assert.strictEqual(bar.status, 'errored');
136      throw err;
137    }
138  }, {
139    code: 'ERR_VM_MODULE_DIFFERENT_CONTEXT'
140  });
141
142  await assert.rejects(async () => {
143    const erroredModule = new SourceTextModule('import "foo";');
144    try {
145      await erroredModule.link(common.mustCall(() => ({})));
146    } catch {
147      // ignored
148    } finally {
149      assert.strictEqual(erroredModule.status, 'errored');
150    }
151
152    const rootModule = new SourceTextModule('import "errored";');
153    await rootModule.link(common.mustCall(() => erroredModule));
154  }, {
155    code: 'ERR_VM_MODULE_LINKING_ERRORED'
156  });
157}
158
159assert.throws(() => {
160  new SourceTextModule('', {
161    importModuleDynamically: 'hucairz'
162  });
163}, {
164  code: 'ERR_INVALID_ARG_TYPE',
165  name: 'TypeError',
166  message: 'The "options.importModuleDynamically" property must be of type ' +
167    "function. Received type string ('hucairz')"
168});
169
170// Check the JavaScript engine deals with exceptions correctly
171async function checkExecution() {
172  await (async () => {
173    const m = new SourceTextModule('import { nonexistent } from "module";');
174
175    // There is no code for this exception since it is thrown by the JavaScript
176    // engine.
177    await assert.rejects(() => {
178      return m.link(common.mustCall(() => new SourceTextModule('')));
179    }, SyntaxError);
180  })();
181
182  await (async () => {
183    const m = new SourceTextModule('throw new Error();');
184    await m.link(common.mustNotCall());
185    const evaluatePromise = m.evaluate();
186    await evaluatePromise.catch(() => {});
187    assert.strictEqual(m.status, 'errored');
188    try {
189      await evaluatePromise;
190    } catch (err) {
191      assert.strictEqual(m.error, err);
192      return;
193    }
194    assert.fail('Missing expected exception');
195  })();
196}
197
198// Check for error thrown when breakOnSigint is not a boolean for evaluate()
199async function checkInvalidOptionForEvaluate() {
200  await assert.rejects(async () => {
201    const m = new SourceTextModule('export const a = 1; export let b = 2');
202    await m.evaluate({ breakOnSigint: 'a-string' });
203  }, {
204    name: 'TypeError',
205    message:
206      'The "options.breakOnSigint" property must be of type boolean. ' +
207      "Received type string ('a-string')",
208    code: 'ERR_INVALID_ARG_TYPE'
209  });
210}
211
212function checkInvalidCachedData() {
213  [true, false, 'foo', {}, Array, function() {}].forEach((invalidArg) => {
214    const message = 'The "options.cachedData" property must be an ' +
215                    'instance of Buffer, TypedArray, or DataView.' +
216                    common.invalidArgTypeHelper(invalidArg);
217    assert.throws(
218      () => new SourceTextModule('import "foo";', { cachedData: invalidArg }),
219      {
220        code: 'ERR_INVALID_ARG_TYPE',
221        name: 'TypeError',
222        message,
223      }
224    );
225  });
226}
227
228const finished = common.mustCall();
229
230(async function main() {
231  await checkArgType();
232  await checkModuleState();
233  await checkLinking();
234  await checkExecution();
235  await checkInvalidOptionForEvaluate();
236  checkInvalidCachedData();
237  finished();
238})();
239