• 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, Module } = 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; // eslint-disable-line no-unused-expressions
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; // eslint-disable-line no-unused-expressions
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; // eslint-disable-line no-unused-expressions
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  const error = new Error();
143  await assert.rejects(async () => {
144    globalThis.error = error;
145    const erroredModule = new SourceTextModule('throw error;');
146    await erroredModule.link(common.mustNotCall());
147    try {
148      await erroredModule.evaluate();
149    } catch {
150      // ignored
151    }
152    delete globalThis.error;
153
154    assert.strictEqual(erroredModule.status, 'errored');
155
156    const rootModule = new SourceTextModule('import "errored";');
157    await rootModule.link(common.mustCall(() => erroredModule));
158  }, {
159    code: 'ERR_VM_MODULE_LINK_FAILURE',
160    cause: error,
161  });
162}
163
164assert.throws(() => {
165  new SourceTextModule('', {
166    importModuleDynamically: 'hucairz'
167  });
168}, {
169  code: 'ERR_INVALID_ARG_TYPE',
170  name: 'TypeError',
171  message: 'The "options.importModuleDynamically" property must be of type ' +
172    "function. Received type string ('hucairz')"
173});
174
175// Check the JavaScript engine deals with exceptions correctly
176async function checkExecution() {
177  await (async () => {
178    const m = new SourceTextModule('import { nonexistent } from "module";');
179
180    // There is no code for this exception since it is thrown by the JavaScript
181    // engine.
182    await assert.rejects(() => {
183      return m.link(common.mustCall(() => new SourceTextModule('')));
184    }, SyntaxError);
185  })();
186
187  await (async () => {
188    const m = new SourceTextModule('throw new Error();');
189    await m.link(common.mustNotCall());
190    try {
191      await m.evaluate();
192    } catch (err) {
193      assert.strictEqual(m.error, err);
194      assert.strictEqual(m.status, 'errored');
195      return;
196    }
197    assert.fail('Missing expected exception');
198  })();
199}
200
201// Check for error thrown when breakOnSigint is not a boolean for evaluate()
202async function checkInvalidOptionForEvaluate() {
203  await assert.rejects(async () => {
204    const m = new SourceTextModule('export const a = 1; export let b = 2');
205    await m.evaluate({ breakOnSigint: 'a-string' });
206  }, {
207    name: 'TypeError',
208    message:
209      'The "options.breakOnSigint" property must be of type boolean. ' +
210      "Received type string ('a-string')",
211    code: 'ERR_INVALID_ARG_TYPE'
212  });
213
214  {
215    ['link', 'evaluate'].forEach(async (method) => {
216      await assert.rejects(async () => {
217        await Module.prototype[method]();
218      }, {
219        code: 'ERR_VM_MODULE_NOT_MODULE',
220        message: /Provided module is not an instance of Module/
221      });
222    });
223  }
224}
225
226function checkInvalidCachedData() {
227  [true, false, 'foo', {}, Array, function() {}].forEach((invalidArg) => {
228    const message = 'The "options.cachedData" property must be an ' +
229                    'instance of Buffer, TypedArray, or DataView.' +
230                    common.invalidArgTypeHelper(invalidArg);
231    assert.throws(
232      () => new SourceTextModule('import "foo";', { cachedData: invalidArg }),
233      {
234        code: 'ERR_INVALID_ARG_TYPE',
235        name: 'TypeError',
236        message,
237      }
238    );
239  });
240}
241
242function checkGettersErrors() {
243  const expectedError = {
244    code: 'ERR_VM_MODULE_NOT_MODULE',
245    message: /Provided module is not an instance of Module/
246  };
247  const getters = ['identifier', 'context', 'namespace', 'status', 'error'];
248  getters.forEach((getter) => {
249    assert.throws(() => {
250      // eslint-disable-next-line no-unused-expressions
251      Module.prototype[getter];
252    }, expectedError);
253    assert.throws(() => {
254      // eslint-disable-next-line no-unused-expressions
255      SourceTextModule.prototype[getter];
256    }, expectedError);
257  });
258  // `dependencySpecifiers` getter is just part of SourceTextModule
259  assert.throws(() => {
260    // eslint-disable-next-line no-unused-expressions
261    SourceTextModule.prototype.dependencySpecifiers;
262  }, expectedError);
263}
264
265const finished = common.mustCall();
266
267(async function main() {
268  await checkArgType();
269  await checkModuleState();
270  await checkLinking();
271  await checkExecution();
272  await checkInvalidOptionForEvaluate();
273  checkInvalidCachedData();
274  checkGettersErrors();
275  finished();
276})().then(common.mustCall());
277