• 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  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    try {
186      await m.evaluate();
187    } catch (err) {
188      assert.strictEqual(m.error, err);
189      assert.strictEqual(m.status, 'errored');
190      return;
191    }
192    assert.fail('Missing expected exception');
193  })();
194}
195
196// Check for error thrown when breakOnSigint is not a boolean for evaluate()
197async function checkInvalidOptionForEvaluate() {
198  await assert.rejects(async () => {
199    const m = new SourceTextModule('export const a = 1; export let b = 2');
200    await m.evaluate({ breakOnSigint: 'a-string' });
201  }, {
202    name: 'TypeError',
203    message:
204      'The "options.breakOnSigint" property must be of type boolean. ' +
205      "Received type string ('a-string')",
206    code: 'ERR_INVALID_ARG_TYPE'
207  });
208
209  {
210    ['link', 'evaluate'].forEach(async (method) => {
211      await assert.rejects(async () => {
212        await Module.prototype[method]();
213      }, {
214        code: 'ERR_VM_MODULE_NOT_MODULE',
215        message: /Provided module is not an instance of Module/
216      });
217    });
218  }
219}
220
221function checkInvalidCachedData() {
222  [true, false, 'foo', {}, Array, function() {}].forEach((invalidArg) => {
223    const message = 'The "options.cachedData" property must be an ' +
224                    'instance of Buffer, TypedArray, or DataView.' +
225                    common.invalidArgTypeHelper(invalidArg);
226    assert.throws(
227      () => new SourceTextModule('import "foo";', { cachedData: invalidArg }),
228      {
229        code: 'ERR_INVALID_ARG_TYPE',
230        name: 'TypeError',
231        message,
232      }
233    );
234  });
235}
236
237function checkGettersErrors() {
238  const expectedError = {
239    code: 'ERR_VM_MODULE_NOT_MODULE',
240    message: /Provided module is not an instance of Module/
241  };
242  const getters = ['identifier', 'context', 'namespace', 'status', 'error'];
243  getters.forEach((getter) => {
244    assert.throws(() => {
245      // eslint-disable-next-line no-unused-expressions
246      Module.prototype[getter];
247    }, expectedError);
248    assert.throws(() => {
249      // eslint-disable-next-line no-unused-expressions
250      SourceTextModule.prototype[getter];
251    }, expectedError);
252  });
253  // `dependencySpecifiers` getter is just part of SourceTextModule
254  assert.throws(() => {
255    // eslint-disable-next-line no-unused-expressions
256    SourceTextModule.prototype.dependencySpecifiers;
257  }, expectedError);
258}
259
260const finished = common.mustCall();
261
262(async function main() {
263  await checkArgType();
264  await checkModuleState();
265  await checkLinking();
266  await checkExecution();
267  await checkInvalidOptionForEvaluate();
268  checkInvalidCachedData();
269  checkGettersErrors();
270  finished();
271})().then(common.mustCall());
272