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