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