1'use strict'; 2 3const assert = require('internal/assert'); 4const { 5 ArrayIsArray, 6 ObjectCreate, 7 ObjectDefineProperty, 8 SafePromise, 9 Symbol, 10 WeakMap, 11} = primordials; 12 13const { isContext } = internalBinding('contextify'); 14const { 15 isModuleNamespaceObject, 16 isArrayBufferView, 17} = require('internal/util/types'); 18const { 19 getConstructorOf, 20 customInspectSymbol, 21 emitExperimentalWarning, 22} = require('internal/util'); 23const { 24 ERR_INVALID_ARG_TYPE, 25 ERR_INVALID_ARG_VALUE, 26 ERR_VM_MODULE_ALREADY_LINKED, 27 ERR_VM_MODULE_DIFFERENT_CONTEXT, 28 ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA, 29 ERR_VM_MODULE_LINKING_ERRORED, 30 ERR_VM_MODULE_NOT_MODULE, 31 ERR_VM_MODULE_STATUS, 32} = require('internal/errors').codes; 33const { 34 validateInt32, 35 validateUint32, 36 validateString, 37} = require('internal/validators'); 38 39const binding = internalBinding('module_wrap'); 40const { 41 ModuleWrap, 42 kUninstantiated, 43 kInstantiating, 44 kInstantiated, 45 kEvaluating, 46 kEvaluated, 47 kErrored, 48} = binding; 49 50const STATUS_MAP = { 51 [kUninstantiated]: 'unlinked', 52 [kInstantiating]: 'linking', 53 [kInstantiated]: 'linked', 54 [kEvaluating]: 'evaluating', 55 [kEvaluated]: 'evaluated', 56 [kErrored]: 'errored', 57}; 58 59let globalModuleId = 0; 60const defaultModuleName = 'vm:module'; 61const wrapToModuleMap = new WeakMap(); 62 63const kWrap = Symbol('kWrap'); 64const kContext = Symbol('kContext'); 65const kPerContextModuleId = Symbol('kPerContextModuleId'); 66const kLink = Symbol('kLink'); 67 68class Module { 69 constructor(options) { 70 emitExperimentalWarning('VM Modules'); 71 72 if (new.target === Module) { 73 // eslint-disable-next-line no-restricted-syntax 74 throw new TypeError('Module is not a constructor'); 75 } 76 77 const { 78 context, 79 sourceText, 80 syntheticExportNames, 81 syntheticEvaluationSteps, 82 } = options; 83 84 if (context !== undefined) { 85 if (typeof context !== 'object' || context === null) { 86 throw new ERR_INVALID_ARG_TYPE('options.context', 'Object', context); 87 } 88 if (!isContext(context)) { 89 throw new ERR_INVALID_ARG_TYPE('options.context', 'vm.Context', 90 context); 91 } 92 } 93 94 let { identifier } = options; 95 if (identifier !== undefined) { 96 validateString(identifier, 'options.identifier'); 97 } else if (context === undefined) { 98 identifier = `${defaultModuleName}(${globalModuleId++})`; 99 } else if (context[kPerContextModuleId] !== undefined) { 100 const curId = context[kPerContextModuleId]; 101 identifier = `${defaultModuleName}(${curId})`; 102 context[kPerContextModuleId] += 1; 103 } else { 104 identifier = `${defaultModuleName}(0)`; 105 ObjectDefineProperty(context, kPerContextModuleId, { 106 value: 1, 107 writable: true, 108 enumerable: false, 109 configurable: true, 110 }); 111 } 112 113 if (sourceText !== undefined) { 114 this[kWrap] = new ModuleWrap(identifier, context, sourceText, 115 options.lineOffset, options.columnOffset, 116 options.cachedData); 117 118 binding.callbackMap.set(this[kWrap], { 119 initializeImportMeta: options.initializeImportMeta, 120 importModuleDynamically: options.importModuleDynamically ? 121 importModuleDynamicallyWrap(options.importModuleDynamically) : 122 undefined, 123 }); 124 } else { 125 assert(syntheticEvaluationSteps); 126 this[kWrap] = new ModuleWrap(identifier, context, 127 syntheticExportNames, 128 syntheticEvaluationSteps); 129 } 130 131 wrapToModuleMap.set(this[kWrap], this); 132 133 this[kContext] = context; 134 } 135 136 get identifier() { 137 if (this[kWrap] === undefined) { 138 throw new ERR_VM_MODULE_NOT_MODULE(); 139 } 140 return this[kWrap].url; 141 } 142 143 get context() { 144 if (this[kWrap] === undefined) { 145 throw new ERR_VM_MODULE_NOT_MODULE(); 146 } 147 return this[kContext]; 148 } 149 150 get namespace() { 151 if (this[kWrap] === undefined) { 152 throw new ERR_VM_MODULE_NOT_MODULE(); 153 } 154 if (this[kWrap].getStatus() < kInstantiated) { 155 throw new ERR_VM_MODULE_STATUS('must not be unlinked or linking'); 156 } 157 return this[kWrap].getNamespace(); 158 } 159 160 get status() { 161 if (this[kWrap] === undefined) { 162 throw new ERR_VM_MODULE_NOT_MODULE(); 163 } 164 return STATUS_MAP[this[kWrap].getStatus()]; 165 } 166 167 get error() { 168 if (this[kWrap] === undefined) { 169 throw new ERR_VM_MODULE_NOT_MODULE(); 170 } 171 if (this[kWrap].getStatus() !== kErrored) { 172 throw new ERR_VM_MODULE_STATUS('must be errored'); 173 } 174 return this[kWrap].getError(); 175 } 176 177 async link(linker) { 178 if (this[kWrap] === undefined) { 179 throw new ERR_VM_MODULE_NOT_MODULE(); 180 } 181 if (typeof linker !== 'function') { 182 throw new ERR_INVALID_ARG_TYPE('linker', 'function', linker); 183 } 184 if (this.status === 'linked') { 185 throw new ERR_VM_MODULE_ALREADY_LINKED(); 186 } 187 if (this.status !== 'unlinked') { 188 throw new ERR_VM_MODULE_STATUS('must be unlinked'); 189 } 190 await this[kLink](linker); 191 this[kWrap].instantiate(); 192 } 193 194 async evaluate(options = {}) { 195 if (this[kWrap] === undefined) { 196 throw new ERR_VM_MODULE_NOT_MODULE(); 197 } 198 199 if (typeof options !== 'object' || options === null) { 200 throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); 201 } 202 203 let timeout = options.timeout; 204 if (timeout === undefined) { 205 timeout = -1; 206 } else { 207 validateUint32(timeout, 'options.timeout', true); 208 } 209 const { breakOnSigint = false } = options; 210 if (typeof breakOnSigint !== 'boolean') { 211 throw new ERR_INVALID_ARG_TYPE('options.breakOnSigint', 'boolean', 212 breakOnSigint); 213 } 214 const status = this[kWrap].getStatus(); 215 if (status !== kInstantiated && 216 status !== kEvaluated && 217 status !== kErrored) { 218 throw new ERR_VM_MODULE_STATUS( 219 'must be one of linked, evaluated, or errored' 220 ); 221 } 222 const result = this[kWrap].evaluate(timeout, breakOnSigint); 223 return { __proto__: null, result }; 224 } 225 226 [customInspectSymbol](depth, options) { 227 let ctor = getConstructorOf(this); 228 ctor = ctor === null ? Module : ctor; 229 230 if (typeof depth === 'number' && depth < 0) 231 return options.stylize(`[${ctor.name}]`, 'special'); 232 233 const o = ObjectCreate({ constructor: ctor }); 234 o.status = this.status; 235 o.identifier = this.identifier; 236 o.context = this.context; 237 return require('internal/util/inspect').inspect(o, options); 238 } 239} 240 241const kDependencySpecifiers = Symbol('kDependencySpecifiers'); 242const kNoError = Symbol('kNoError'); 243 244class SourceTextModule extends Module { 245 #error = kNoError; 246 #statusOverride; 247 248 constructor(sourceText, options = {}) { 249 validateString(sourceText, 'sourceText'); 250 251 if (typeof options !== 'object' || options === null) { 252 throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); 253 } 254 255 const { 256 lineOffset = 0, 257 columnOffset = 0, 258 initializeImportMeta, 259 importModuleDynamically, 260 context, 261 identifier, 262 cachedData, 263 } = options; 264 265 validateInt32(lineOffset, 'options.lineOffset'); 266 validateInt32(columnOffset, 'options.columnOffset'); 267 268 if (initializeImportMeta !== undefined && 269 typeof initializeImportMeta !== 'function') { 270 throw new ERR_INVALID_ARG_TYPE( 271 'options.initializeImportMeta', 'function', initializeImportMeta); 272 } 273 274 if (importModuleDynamically !== undefined && 275 typeof importModuleDynamically !== 'function') { 276 throw new ERR_INVALID_ARG_TYPE( 277 'options.importModuleDynamically', 'function', 278 importModuleDynamically); 279 } 280 281 if (cachedData !== undefined && !isArrayBufferView(cachedData)) { 282 throw new ERR_INVALID_ARG_TYPE( 283 'options.cachedData', 284 ['Buffer', 'TypedArray', 'DataView'], 285 cachedData 286 ); 287 } 288 289 super({ 290 sourceText, 291 context, 292 identifier, 293 lineOffset, 294 columnOffset, 295 cachedData, 296 initializeImportMeta, 297 importModuleDynamically, 298 }); 299 300 this[kLink] = async (linker) => { 301 this.#statusOverride = 'linking'; 302 303 const promises = this[kWrap].link(async (identifier) => { 304 const module = await linker(identifier, this); 305 if (module[kWrap] === undefined) { 306 throw new ERR_VM_MODULE_NOT_MODULE(); 307 } 308 if (module.context !== this.context) { 309 throw new ERR_VM_MODULE_DIFFERENT_CONTEXT(); 310 } 311 if (module.status === 'errored') { 312 throw new ERR_VM_MODULE_LINKING_ERRORED(); 313 } 314 if (module.status === 'unlinked') { 315 await module[kLink](linker); 316 } 317 return module[kWrap]; 318 }); 319 320 try { 321 if (promises !== undefined) { 322 await SafePromise.all(promises); 323 } 324 } catch (e) { 325 this.#error = e; 326 throw e; 327 } finally { 328 this.#statusOverride = undefined; 329 } 330 }; 331 332 this[kDependencySpecifiers] = undefined; 333 } 334 335 get dependencySpecifiers() { 336 if (this[kWrap] === undefined) { 337 throw new ERR_VM_MODULE_NOT_MODULE(); 338 } 339 if (this[kDependencySpecifiers] === undefined) { 340 this[kDependencySpecifiers] = this[kWrap].getStaticDependencySpecifiers(); 341 } 342 return this[kDependencySpecifiers]; 343 } 344 345 get status() { 346 if (this[kWrap] === undefined) { 347 throw new ERR_VM_MODULE_NOT_MODULE(); 348 } 349 if (this.#error !== kNoError) { 350 return 'errored'; 351 } 352 if (this.#statusOverride) { 353 return this.#statusOverride; 354 } 355 return super.status; 356 } 357 358 get error() { 359 if (this[kWrap] === undefined) { 360 throw new ERR_VM_MODULE_NOT_MODULE(); 361 } 362 if (this.#error !== kNoError) { 363 return this.#error; 364 } 365 return super.error; 366 } 367 368 createCachedData() { 369 const { status } = this; 370 if (status === 'evaluating' || 371 status === 'evaluated' || 372 status === 'errored') { 373 throw new ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA(); 374 } 375 return this[kWrap].createCachedData(); 376 } 377} 378 379class SyntheticModule extends Module { 380 constructor(exportNames, evaluateCallback, options = {}) { 381 if (!ArrayIsArray(exportNames) || 382 exportNames.some((e) => typeof e !== 'string')) { 383 throw new ERR_INVALID_ARG_TYPE('exportNames', 384 'Array of unique strings', 385 exportNames); 386 } else { 387 exportNames.forEach((name, i) => { 388 if (exportNames.indexOf(name, i + 1) !== -1) { 389 throw new ERR_INVALID_ARG_VALUE(`exportNames.${name}`, 390 name, 391 'is duplicated'); 392 } 393 }); 394 } 395 if (typeof evaluateCallback !== 'function') { 396 throw new ERR_INVALID_ARG_TYPE('evaluateCallback', 'function', 397 evaluateCallback); 398 } 399 400 if (typeof options !== 'object' || options === null) { 401 throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); 402 } 403 404 const { context, identifier } = options; 405 406 super({ 407 syntheticExportNames: exportNames, 408 syntheticEvaluationSteps: evaluateCallback, 409 context, 410 identifier, 411 }); 412 413 this[kLink] = () => this[kWrap].link(() => { 414 assert.fail('link callback should not be called'); 415 }); 416 } 417 418 setExport(name, value) { 419 if (this[kWrap] === undefined) { 420 throw new ERR_VM_MODULE_NOT_MODULE(); 421 } 422 validateString(name, 'name'); 423 if (this[kWrap].getStatus() < kInstantiated) { 424 throw new ERR_VM_MODULE_STATUS('must be linked'); 425 } 426 this[kWrap].setExport(name, value); 427 } 428} 429 430function importModuleDynamicallyWrap(importModuleDynamically) { 431 const importModuleDynamicallyWrapper = async (...args) => { 432 const m = await importModuleDynamically(...args); 433 if (isModuleNamespaceObject(m)) { 434 return m; 435 } 436 if (!m || m[kWrap] === undefined) { 437 throw new ERR_VM_MODULE_NOT_MODULE(); 438 } 439 if (m.status === 'errored') { 440 throw m.error; 441 } 442 return m.namespace; 443 }; 444 return importModuleDynamicallyWrapper; 445} 446 447module.exports = { 448 Module, 449 SourceTextModule, 450 SyntheticModule, 451 importModuleDynamicallyWrap, 452 getModuleFromWrap: (wrap) => wrapToModuleMap.get(wrap), 453}; 454