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