Modules.cpp (111158B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 /* JavaScript modules (as in, the syntactic construct) implementation. */ 8 9 #include "vm/Modules.h" 10 11 #include "mozilla/Assertions.h" // MOZ_ASSERT 12 #include "mozilla/ScopeExit.h" 13 #include "mozilla/Utf8.h" // mozilla::Utf8Unit 14 15 #include <stdint.h> // uint32_t 16 17 #include "jstypes.h" // JS_PUBLIC_API 18 19 #include "builtin/JSON.h" // js::ParseJSONWithReviver 20 #include "builtin/ModuleObject.h" 21 #include "builtin/Promise.h" // js::CreatePromiseObjectForAsync, js::AsyncFunctionReturned 22 #include "ds/Sort.h" 23 #include "frontend/BytecodeCompiler.h" // js::frontend::CompileModule 24 #include "frontend/FrontendContext.h" // js::AutoReportFrontendContext 25 #include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin 26 #include "js/Context.h" // js::AssertHeapIsIdle 27 #include "js/ErrorReport.h" // JSErrorBase 28 #include "js/friend/StackLimits.h" // js::AutoCheckRecursionLimit 29 #include "js/RootingAPI.h" // JS::MutableHandle 30 #include "js/Value.h" // JS::Value 31 #include "vm/EnvironmentObject.h" // js::ModuleEnvironmentObject 32 #include "vm/JSAtomUtils.h" // AtomizeString 33 #include "vm/JSContext.h" // CHECK_THREAD, JSContext 34 #include "vm/JSObject.h" // JSObject 35 #include "vm/JSONParser.h" // JSONParser 36 #include "vm/List.h" // ListObject 37 #include "vm/Runtime.h" // JSRuntime 38 39 #include "builtin/HandlerFunction-inl.h" // js::ExtraValueFromHandler, js::NewHandler{,WithExtraValue}, js::TargetFromHandler 40 #include "vm/JSAtomUtils-inl.h" // AtomToId 41 #include "vm/JSContext-inl.h" // JSContext::{c,releaseC}heck 42 #include "vm/JSObject-inl.h" 43 #include "vm/NativeObject-inl.h" 44 45 using namespace js; 46 47 using mozilla::Utf8Unit; 48 49 class DynamicImportContextObject; 50 51 static bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module); 52 static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module, 53 MutableHandle<Value> rval); 54 static bool SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module, 55 MutableHandle<Value> rval); 56 static bool ContinueModuleLoading(JSContext* cx, 57 Handle<GraphLoadingStateRecordObject*> state, 58 Handle<ModuleObject*> moduleCompletion, 59 ImportPhase phase, Handle<Value> error); 60 static bool TryStartDynamicModuleImport(JSContext* cx, HandleScript script, 61 HandleValue specifierArg, 62 HandleValue optionsArg, 63 HandleObject promise); 64 static bool ContinueDynamicImport(JSContext* cx, Handle<JSScript*> referrer, 65 Handle<PromiseObject*> promiseCapability, 66 Handle<ModuleObject*> module, 67 ImportPhase phase, bool usePromise); 68 static bool LinkAndEvaluateDynamicImport(JSContext* cx, unsigned argc, 69 Value* vp); 70 static bool LinkAndEvaluateDynamicImport( 71 JSContext* cx, Handle<DynamicImportContextObject*> context); 72 static bool DynamicImportResolved(JSContext* cx, unsigned argc, Value* vp); 73 static bool DynamicImportRejected(JSContext* cx, unsigned argc, Value* vp); 74 75 //////////////////////////////////////////////////////////////////////////////// 76 // Public API 77 78 JS_PUBLIC_API JS::ModuleLoadHook JS::GetModuleLoadHook(JSRuntime* rt) { 79 AssertHeapIsIdle(); 80 81 return rt->moduleLoadHook; 82 } 83 84 JS_PUBLIC_API void JS::SetModuleLoadHook(JSRuntime* rt, ModuleLoadHook func) { 85 AssertHeapIsIdle(); 86 87 rt->moduleLoadHook = func; 88 } 89 90 JS_PUBLIC_API JS::ModuleMetadataHook JS::GetModuleMetadataHook(JSRuntime* rt) { 91 AssertHeapIsIdle(); 92 93 return rt->moduleMetadataHook; 94 } 95 96 JS_PUBLIC_API void JS::SetModuleMetadataHook(JSRuntime* rt, 97 ModuleMetadataHook func) { 98 AssertHeapIsIdle(); 99 100 rt->moduleMetadataHook = func; 101 } 102 103 // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule 104 JS_PUBLIC_API bool JS::FinishLoadingImportedModule( 105 JSContext* cx, Handle<JSScript*> referrer, Handle<JSObject*> moduleRequest, 106 Handle<Value> payload, Handle<JSObject*> result, bool usePromise) { 107 AssertHeapIsIdle(); 108 CHECK_THREAD(cx); 109 cx->check(referrer, moduleRequest, payload, result); 110 111 MOZ_ASSERT(moduleRequest->is<ModuleRequestObject>()); 112 MOZ_ASSERT(result); 113 Rooted<ModuleObject*> module(cx, &result->as<ModuleObject>()); 114 115 if (referrer && referrer->isModule()) { 116 // |loadedModules| is only required to be stored on modules. 117 118 // Step 1. If result is a normal completion, then 119 // Step 1.a. If referrer.[[LoadedModules]] contains a Record whose 120 // [[Specifier]] is specifier, then 121 LoadedModuleMap& loadedModules = referrer->module()->loadedModules(); 122 if (auto record = loadedModules.lookup(moduleRequest)) { 123 // Step 1.a.i. Assert: That Record's [[Module]] is result.[[Value]]. 124 MOZ_ASSERT(record->value() == module); 125 } else { 126 // Step 1.b. Else, append the Record { moduleRequest.[[Specifer]], 127 // [[Attributes]]: moduleRequest.[[Attributes]], 128 // [[Module]]: result.[[Value]] } to referrer.[[LoadedModules]]. 129 if (!loadedModules.putNew(moduleRequest, module)) { 130 ReportOutOfMemory(cx); 131 return FinishLoadingImportedModuleFailedWithPendingException(cx, 132 payload); 133 } 134 } 135 } 136 137 // Step 2. If payload is a GraphLoadingState Record, then 138 // Step 2.a. Perform ContinueModuleLoading(payload, result). 139 JSObject* object = &payload.toObject(); 140 if (object->is<GraphLoadingStateRecordObject>()) { 141 Rooted<GraphLoadingStateRecordObject*> state(cx); 142 state = &object->as<GraphLoadingStateRecordObject>(); 143 return ContinueModuleLoading( 144 cx, state, module, moduleRequest->as<ModuleRequestObject>().phase(), 145 UndefinedHandleValue); 146 } 147 148 // Step 3. Else, 149 // Step 3.a. Perform ContinueDynamicImport(payload, result). 150 MOZ_ASSERT(object->is<PromiseObject>()); 151 Rooted<PromiseObject*> promise(cx, &object->as<PromiseObject>()); 152 return ContinueDynamicImport(cx, referrer, promise, module, 153 moduleRequest->as<ModuleRequestObject>().phase(), 154 usePromise); 155 } 156 157 // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule 158 // Failure path where |result| is a throw completion, supplied as |error|. 159 JS_PUBLIC_API bool JS::FinishLoadingImportedModuleFailed( 160 JSContext* cx, Handle<Value> payloadArg, Handle<Value> error) { 161 AssertHeapIsIdle(); 162 CHECK_THREAD(cx); 163 cx->check(payloadArg, error); 164 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 165 166 // Step 2. If payload is a GraphLoadingState Record, then 167 // Step 2.a. Perform ContinueModuleLoading(payload, result). 168 JSObject* payload = &payloadArg.toObject(); 169 if (payload->is<GraphLoadingStateRecordObject>()) { 170 Rooted<GraphLoadingStateRecordObject*> state(cx); 171 state = &payload->as<GraphLoadingStateRecordObject>(); 172 return ContinueModuleLoading(cx, state, nullptr, ImportPhase::Evaluation, 173 error); 174 } 175 176 // Step 3. Else, 177 // Step 3.a. Perform ContinueDynamicImport(payload, result). 178 // ContinueDynamicImport: 179 // Step 1. If moduleCompletion is an abrupt completion, then 180 // Step 1. a. Perform ! Call(promiseCapability.[[Reject]], undefined, 181 // moduleCompletion.[[Value]]). 182 Rooted<PromiseObject*> promise(cx, &payload->as<PromiseObject>()); 183 return PromiseObject::reject(cx, promise, error); 184 } 185 186 // https://tc39.es/ecma262/#sec-FinishLoadingImportedModule 187 // Failure path where |result| is a throw completion, set as the pending 188 // exception. 189 JS_PUBLIC_API bool JS::FinishLoadingImportedModuleFailedWithPendingException( 190 JSContext* cx, Handle<Value> payload) { 191 AssertHeapIsIdle(); 192 CHECK_THREAD(cx); 193 cx->check(payload); 194 MOZ_ASSERT(JS_IsExceptionPending(cx)); 195 196 RootedValue error(cx); 197 if (!cx->getPendingException(&error)) { 198 MOZ_ASSERT(cx->isThrowingOutOfMemory()); 199 MOZ_ALWAYS_TRUE(cx->getPendingException(&error)); 200 } 201 cx->clearPendingException(); 202 203 return FinishLoadingImportedModuleFailed(cx, payload, error); 204 } 205 206 template <typename Unit> 207 static JSObject* CompileModuleHelper(JSContext* cx, 208 const JS::ReadOnlyCompileOptions& options, 209 JS::SourceText<Unit>& srcBuf) { 210 MOZ_ASSERT(!cx->zone()->isAtomsZone()); 211 AssertHeapIsIdle(); 212 CHECK_THREAD(cx); 213 214 JS::Rooted<JSObject*> mod(cx); 215 { 216 AutoReportFrontendContext fc(cx); 217 mod = frontend::CompileModule(cx, &fc, options, srcBuf); 218 } 219 return mod; 220 } 221 222 JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx, 223 const ReadOnlyCompileOptions& options, 224 SourceText<char16_t>& srcBuf) { 225 return CompileModuleHelper(cx, options, srcBuf); 226 } 227 228 JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx, 229 const ReadOnlyCompileOptions& options, 230 SourceText<Utf8Unit>& srcBuf) { 231 return CompileModuleHelper(cx, options, srcBuf); 232 } 233 234 JS_PUBLIC_API JSObject* JS::CompileJsonModule( 235 JSContext* cx, const ReadOnlyCompileOptions& options, 236 SourceText<mozilla::Utf8Unit>& srcBuf) { 237 size_t length = srcBuf.length(); 238 auto chars = 239 UniqueTwoByteChars(UTF8CharsToNewTwoByteCharsZ( 240 cx, JS::UTF8Chars(srcBuf.get(), srcBuf.length()), 241 &length, js::MallocArena) 242 .get()); 243 if (!chars) { 244 return nullptr; 245 } 246 247 JS::SourceText<char16_t> source; 248 if (!source.init(cx, std::move(chars), length)) { 249 return nullptr; 250 } 251 252 return CompileJsonModule(cx, options, source); 253 } 254 255 JS_PUBLIC_API JSObject* JS::CompileJsonModule( 256 JSContext* cx, const ReadOnlyCompileOptions& options, 257 SourceText<char16_t>& srcBuf) { 258 MOZ_ASSERT(!cx->zone()->isAtomsZone()); 259 AssertHeapIsIdle(); 260 CHECK_THREAD(cx); 261 262 auto charRange = 263 mozilla::Range<const char16_t>(srcBuf.get(), srcBuf.length()); 264 Rooted<JSONParser<char16_t>> parser( 265 cx, cx, charRange, JSONParser<char16_t>::ParseType::JSONParse); 266 267 parser.reportLineNumbersFromParsedData(true); 268 parser.setFilename(options.filename()); 269 270 JS::RootedValue jsonValue(cx); 271 if (!parser.parse(&jsonValue)) { 272 return nullptr; 273 } 274 275 return CreateDefaultExportSyntheticModule(cx, jsonValue); 276 } 277 278 JS_PUBLIC_API JSObject* JS::CreateDefaultExportSyntheticModule( 279 JSContext* cx, const Value& defaultExport) { 280 Rooted<ExportNameVector> exportNames(cx); 281 if (!exportNames.append(cx->names().default_)) { 282 ReportOutOfMemory(cx); 283 return nullptr; 284 } 285 286 Rooted<ModuleObject*> moduleObject( 287 cx, ModuleObject::createSynthetic(cx, &exportNames)); 288 if (!moduleObject) { 289 return nullptr; 290 } 291 292 RootedVector<Value> exportValues(cx); 293 if (!exportValues.append(defaultExport)) { 294 ReportOutOfMemory(cx); 295 return nullptr; 296 } 297 298 if (!ModuleObject::createSyntheticEnvironment(cx, moduleObject, 299 exportValues)) { 300 return nullptr; 301 } 302 303 return moduleObject; 304 } 305 306 JS_PUBLIC_API JSObject* JS::CompileWasmModule( 307 JSContext* cx, const ReadOnlyCompileOptions& options, 308 SourceText<mozilla::Utf8Unit>& srcBuf) { 309 // TODO: Compilation of wasm modules will be added in 310 // https://bugzilla.mozilla.org/show_bug.cgi?id=1997621. 311 // For now, we fail unconditionally. 312 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 313 JSMSG_WASM_COMPILE_ERROR, 314 "Compilation of wasm modules not implemented."); 315 316 return nullptr; 317 } 318 319 JS_PUBLIC_API JSObject* JS::CompileWasmModule( 320 JSContext* cx, const ReadOnlyCompileOptions& options, 321 SourceText<char16_t>& srcBuf) { 322 // TODO: Compilation of wasm modules will be added in 323 // https://bugzilla.mozilla.org/show_bug.cgi?id=1997621. 324 // For now, we fail unconditionally. 325 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 326 JSMSG_WASM_COMPILE_ERROR, 327 "Compilation of wasm modules not implemented."); 328 return nullptr; 329 } 330 331 JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module, const Value& value) { 332 JSRuntime* rt = module->zone()->runtimeFromMainThread(); 333 module->as<ModuleObject>().scriptSourceObject()->setPrivate(rt, value); 334 } 335 336 JS_PUBLIC_API void JS::ClearModulePrivate(JSObject* module) { 337 // |module| may be gray, be careful not to create edges to it. 338 JSRuntime* rt = module->zone()->runtimeFromMainThread(); 339 module->as<ModuleObject>().scriptSourceObject()->clearPrivate(rt); 340 } 341 342 JS_PUBLIC_API JS::Value JS::GetModulePrivate(JSObject* module) { 343 return module->as<ModuleObject>().scriptSourceObject()->getPrivate(); 344 } 345 346 JS_PUBLIC_API bool JS::IsCyclicModule(JSObject* module) { 347 return module->as<ModuleObject>().hasCyclicModuleFields(); 348 } 349 350 JS_PUBLIC_API bool JS::ModuleLink(JSContext* cx, Handle<JSObject*> moduleArg) { 351 AssertHeapIsIdle(); 352 CHECK_THREAD(cx); 353 cx->releaseCheck(moduleArg); 354 355 return ::ModuleLink(cx, moduleArg.as<ModuleObject>()); 356 } 357 358 JS_PUBLIC_API bool JS::LoadRequestedModules( 359 JSContext* cx, Handle<JSObject*> moduleArg, Handle<Value> hostDefined, 360 JS::LoadModuleResolvedCallback resolved, 361 JS::LoadModuleRejectedCallback rejected) { 362 AssertHeapIsIdle(); 363 CHECK_THREAD(cx); 364 cx->releaseCheck(moduleArg); 365 366 return js::LoadRequestedModules(cx, moduleArg.as<ModuleObject>(), hostDefined, 367 resolved, rejected); 368 } 369 370 JS_PUBLIC_API bool JS::LoadRequestedModules( 371 JSContext* cx, Handle<JSObject*> moduleArg, Handle<Value> hostDefined, 372 MutableHandle<JSObject*> promiseOut) { 373 AssertHeapIsIdle(); 374 CHECK_THREAD(cx); 375 cx->releaseCheck(moduleArg); 376 377 return js::LoadRequestedModules(cx, moduleArg.as<ModuleObject>(), hostDefined, 378 promiseOut); 379 } 380 381 JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx, 382 Handle<JSObject*> moduleRecord, 383 MutableHandle<JS::Value> rval) { 384 AssertHeapIsIdle(); 385 CHECK_THREAD(cx); 386 cx->releaseCheck(moduleRecord); 387 388 cx->isEvaluatingModule++; 389 auto guard = mozilla::MakeScopeExit([cx] { 390 MOZ_ASSERT(cx->isEvaluatingModule != 0); 391 cx->isEvaluatingModule--; 392 }); 393 394 if (moduleRecord.as<ModuleObject>()->hasSyntheticModuleFields()) { 395 return SyntheticModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval); 396 } 397 398 return ::ModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval); 399 } 400 401 JS_PUBLIC_API bool JS::ThrowOnModuleEvaluationFailure( 402 JSContext* cx, Handle<JSObject*> evaluationPromise, 403 ModuleErrorBehaviour errorBehaviour) { 404 AssertHeapIsIdle(); 405 CHECK_THREAD(cx); 406 cx->releaseCheck(evaluationPromise); 407 408 return OnModuleEvaluationFailure(cx, evaluationPromise, errorBehaviour); 409 } 410 411 JS_PUBLIC_API JS::ModuleType JS::GetRequestedModuleType( 412 JSContext* cx, Handle<JSObject*> moduleRecord, uint32_t index) { 413 AssertHeapIsIdle(); 414 CHECK_THREAD(cx); 415 cx->check(moduleRecord); 416 417 auto& module = moduleRecord->as<ModuleObject>(); 418 return module.requestedModules()[index].moduleRequest()->moduleType(); 419 } 420 421 JS_PUBLIC_API JSScript* JS::GetModuleScript(JS::HandleObject moduleRecord) { 422 AssertHeapIsIdle(); 423 424 auto& module = moduleRecord->as<ModuleObject>(); 425 426 // A synthetic module does not have a script associated with it. 427 if (module.hasSyntheticModuleFields()) { 428 return nullptr; 429 } 430 431 return module.script(); 432 } 433 434 JS_PUBLIC_API JSObject* JS::GetModuleObject(HandleScript moduleScript) { 435 AssertHeapIsIdle(); 436 MOZ_ASSERT(moduleScript->isModule()); 437 438 return moduleScript->module(); 439 } 440 441 JS_PUBLIC_API JSObject* JS::GetModuleNamespace(JSContext* cx, 442 HandleObject moduleRecord) { 443 AssertHeapIsIdle(); 444 CHECK_THREAD(cx); 445 cx->check(moduleRecord); 446 MOZ_ASSERT(moduleRecord->is<ModuleObject>()); 447 448 return GetOrCreateModuleNamespace(cx, moduleRecord.as<ModuleObject>()); 449 } 450 451 JS_PUBLIC_API JSObject* JS::GetModuleForNamespace( 452 JSContext* cx, HandleObject moduleNamespace) { 453 AssertHeapIsIdle(); 454 CHECK_THREAD(cx); 455 cx->check(moduleNamespace); 456 MOZ_ASSERT(moduleNamespace->is<ModuleNamespaceObject>()); 457 458 return &moduleNamespace->as<ModuleNamespaceObject>().module(); 459 } 460 461 JS_PUBLIC_API JSObject* JS::GetModuleEnvironment(JSContext* cx, 462 Handle<JSObject*> moduleObj) { 463 AssertHeapIsIdle(); 464 CHECK_THREAD(cx); 465 cx->check(moduleObj); 466 MOZ_ASSERT(moduleObj->is<ModuleObject>()); 467 468 return moduleObj->as<ModuleObject>().environment(); 469 } 470 471 JS_PUBLIC_API JSString* JS::GetModuleRequestSpecifier( 472 JSContext* cx, Handle<JSObject*> moduleRequestArg) { 473 AssertHeapIsIdle(); 474 CHECK_THREAD(cx); 475 cx->check(moduleRequestArg); 476 477 return moduleRequestArg->as<ModuleRequestObject>().specifier(); 478 } 479 480 JS_PUBLIC_API JS::ModuleType JS::GetModuleRequestType( 481 JSContext* cx, Handle<JSObject*> moduleRequestArg) { 482 AssertHeapIsIdle(); 483 CHECK_THREAD(cx); 484 cx->check(moduleRequestArg); 485 486 return moduleRequestArg->as<ModuleRequestObject>().moduleType(); 487 } 488 489 JS_PUBLIC_API void JS::ClearModuleEnvironment(JSObject* moduleObj) { 490 MOZ_ASSERT(moduleObj); 491 AssertHeapIsIdle(); 492 493 js::ModuleEnvironmentObject* env = 494 moduleObj->as<js::ModuleObject>().environment(); 495 if (!env) { 496 return; 497 } 498 499 const JSClass* clasp = env->getClass(); 500 uint32_t numReserved = JSCLASS_RESERVED_SLOTS(clasp); 501 uint32_t numSlots = env->slotSpan(); 502 for (uint32_t i = numReserved; i < numSlots; i++) { 503 env->setSlot(i, UndefinedValue()); 504 } 505 } 506 507 JS_PUBLIC_API bool JS::ModuleIsLinked(JSObject* moduleObj) { 508 AssertHeapIsIdle(); 509 return moduleObj->as<ModuleObject>().status() != ModuleStatus::New && 510 moduleObj->as<ModuleObject>().status() != ModuleStatus::Unlinked; 511 } 512 513 //////////////////////////////////////////////////////////////////////////////// 514 // Internal implementation 515 516 class ResolveSetEntry { 517 ModuleObject* module_; 518 JSAtom* exportName_; 519 520 public: 521 ResolveSetEntry(ModuleObject* module, JSAtom* exportName) 522 : module_(module), exportName_(exportName) {} 523 524 ModuleObject* module() const { return module_; } 525 JSAtom* exportName() const { return exportName_; } 526 527 void trace(JSTracer* trc) { 528 TraceRoot(trc, &module_, "ResolveSetEntry::module_"); 529 TraceRoot(trc, &exportName_, "ResolveSetEntry::exportName_"); 530 } 531 }; 532 533 using ResolveSet = GCVector<ResolveSetEntry, 0, SystemAllocPolicy>; 534 535 using ModuleSet = 536 GCHashSet<ModuleObject*, DefaultHasher<ModuleObject*>, SystemAllocPolicy>; 537 538 static bool CyclicModuleResolveExport(JSContext* cx, 539 Handle<ModuleObject*> module, 540 Handle<JSAtom*> exportName, 541 MutableHandle<ResolveSet> resolveSet, 542 MutableHandle<Value> result, 543 ModuleErrorInfo* errorInfoOut = nullptr); 544 static bool SyntheticModuleResolveExport(JSContext* cx, 545 Handle<ModuleObject*> module, 546 Handle<JSAtom*> exportName, 547 MutableHandle<Value> result, 548 ModuleErrorInfo* errorInfoOut); 549 static ModuleNamespaceObject* ModuleNamespaceCreate( 550 JSContext* cx, Handle<ModuleObject*> module, 551 MutableHandle<UniquePtr<ExportNameVector>> exports); 552 static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, 553 MutableHandle<ModuleVector> stack, size_t index, 554 size_t* indexOut); 555 static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module, 556 MutableHandle<ModuleVector> stack, 557 size_t index, size_t* indexOut); 558 static bool ExecuteAsyncModule(JSContext* cx, Handle<ModuleObject*> module); 559 static bool GatherAvailableModuleAncestors( 560 JSContext* cx, Handle<ModuleObject*> module, 561 MutableHandle<ModuleVector> execList); 562 563 static const char* ModuleStatusName(ModuleStatus status) { 564 switch (status) { 565 case ModuleStatus::New: 566 return "New"; 567 case ModuleStatus::Unlinked: 568 return "Unlinked"; 569 case ModuleStatus::Linking: 570 return "Linking"; 571 case ModuleStatus::Linked: 572 return "Linked"; 573 case ModuleStatus::Evaluating: 574 return "Evaluating"; 575 case ModuleStatus::EvaluatingAsync: 576 return "EvaluatingAsync"; 577 case ModuleStatus::Evaluated: 578 return "Evaluated"; 579 default: 580 MOZ_CRASH("Unexpected ModuleStatus"); 581 } 582 } 583 584 static bool ContainsElement(const ExportNameVector& list, JSAtom* atom) { 585 for (JSAtom* a : list) { 586 if (a == atom) { 587 return true; 588 } 589 } 590 591 return false; 592 } 593 594 static bool ContainsElement(Handle<ModuleVector> stack, ModuleObject* module) { 595 for (ModuleObject* m : stack) { 596 if (m == module) { 597 return true; 598 } 599 } 600 601 return false; 602 } 603 604 #ifdef DEBUG 605 static size_t CountElements(Handle<ModuleVector> stack, ModuleObject* module) { 606 size_t count = 0; 607 for (ModuleObject* m : stack) { 608 if (m == module) { 609 count++; 610 } 611 } 612 613 return count; 614 } 615 #endif 616 617 // https://tc39.es/proposal-json-modules/#sec-smr-getexportednames 618 static bool SyntheticModuleGetExportedNames( 619 JSContext* cx, Handle<ModuleObject*> module, 620 MutableHandle<ExportNameVector> exportedNames) { 621 MOZ_ASSERT(exportedNames.empty()); 622 623 if (!exportedNames.appendAll(module->syntheticExportNames())) { 624 ReportOutOfMemory(cx); 625 return false; 626 } 627 628 return true; 629 } 630 631 // https://tc39.es/ecma262/#sec-GetImportedModule 632 static ModuleObject* GetImportedModule( 633 JSContext* cx, Handle<ModuleObject*> referrer, 634 Handle<ModuleRequestObject*> moduleRequest) { 635 MOZ_ASSERT(referrer); 636 MOZ_ASSERT(moduleRequest); 637 638 // Step 1. Assert: Exactly one element of referrer.[[LoadedModules]] is a 639 // Record whose [[Specifier]] is specifier, since LoadRequestedModules 640 // has completed successfully on referrer prior to invoking this 641 // abstract operation. 642 // 643 // Impl note: Updated to perform lookup using ModuleRequestObject as 644 // per the Import Attributes Proposal. 645 auto record = referrer->loadedModules().lookup(moduleRequest); 646 MOZ_ASSERT(record); 647 648 // Step 2. Let record be the Record in referrer.[[LoadedModules]] whose 649 // [[Specifier]] is specifier. 650 // Step 3. Return record.[[Module]]. 651 return record->value(); 652 } 653 654 // https://tc39.es/ecma262/#sec-getexportednames 655 // ES2023 16.2.1.6.2 GetExportedNames 656 static bool ModuleGetExportedNames( 657 JSContext* cx, Handle<ModuleObject*> module, 658 MutableHandle<ModuleSet> exportStarSet, 659 MutableHandle<ExportNameVector> exportedNames) { 660 // Step 4. Let exportedNames be a new empty List. 661 MOZ_ASSERT(exportedNames.empty()); 662 663 if (module->hasSyntheticModuleFields()) { 664 return SyntheticModuleGetExportedNames(cx, module, exportedNames); 665 } 666 667 // Step 2. If exportStarSet contains module, then: 668 if (exportStarSet.has(module)) { 669 // Step 2.a. We've reached the starting point of an export * circularity. 670 // Step 2.b. Return a new empty List. 671 return true; 672 } 673 674 // Step 3. Append module to exportStarSet. 675 if (!exportStarSet.put(module)) { 676 ReportOutOfMemory(cx); 677 return false; 678 } 679 680 // Step 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do: 681 for (const ExportEntry& e : module->localExportEntries()) { 682 // Step 5.a. Assert: module provides the direct binding for this export. 683 // Step 5.b. Append e.[[ExportName]] to exportedNames. 684 if (!exportedNames.append(e.exportName())) { 685 ReportOutOfMemory(cx); 686 return false; 687 } 688 } 689 690 // Step 6. For each ExportEntry Record e of module.[[IndirectExportEntries]], 691 // do: 692 for (const ExportEntry& e : module->indirectExportEntries()) { 693 // Step 6.a. Assert: module imports a specific binding for this export. 694 // Step 6.b. Append e.[[ExportName]] to exportedNames. 695 if (!exportedNames.append(e.exportName())) { 696 ReportOutOfMemory(cx); 697 return false; 698 } 699 } 700 701 // Step 7. For each ExportEntry Record e of module.[[StarExportEntries]], do: 702 Rooted<ModuleRequestObject*> moduleRequest(cx); 703 Rooted<ModuleObject*> requestedModule(cx); 704 Rooted<JSAtom*> name(cx); 705 for (const ExportEntry& e : module->starExportEntries()) { 706 // Step 7.a. Let requestedModule be ? GetImportedModule(module, 707 // e.[[ModuleRequest]]). 708 moduleRequest = e.moduleRequest(); 709 requestedModule = GetImportedModule(cx, module, moduleRequest); 710 if (!requestedModule) { 711 return false; 712 } 713 MOZ_ASSERT(requestedModule->status() >= ModuleStatus::Unlinked); 714 715 // Step 7.b. Let starNames be ? 716 // requestedModule.GetExportedNames(exportStarSet). 717 Rooted<ExportNameVector> starNames(cx); 718 if (!ModuleGetExportedNames(cx, requestedModule, exportStarSet, 719 &starNames)) { 720 return false; 721 } 722 723 // Step 7.c. For each element n of starNames, do: 724 for (JSAtom* name : starNames) { 725 // Step 7.c.i. If SameValue(n, "default") is false, then: 726 if (name != cx->names().default_) { 727 // Step 7.c.i.1. If n is not an element of exportedNames, then: 728 if (!ContainsElement(exportedNames, name)) { 729 // Step 7.c.i.1.a. Append n to exportedNames. 730 if (!exportedNames.append(name)) { 731 ReportOutOfMemory(cx); 732 return false; 733 } 734 } 735 } 736 } 737 } 738 739 // Step 8. Return exportedNames. 740 return true; 741 } 742 743 static void ThrowUnexpectedModuleStatus(JSContext* cx, ModuleStatus status) { 744 JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, 745 JSMSG_BAD_MODULE_STATUS, ModuleStatusName(status)); 746 } 747 748 // https://tc39.es/ecma262/#sec-HostLoadImportedModule 749 // 750 // According to spec the referrer can be a module script, classic script or 751 // realm. The first two are supplied to this function by passing the script. 752 // When the referrer is a realm nullptr is passed. 753 bool js::HostLoadImportedModule(JSContext* cx, Handle<JSScript*> referrer, 754 Handle<JSObject*> moduleRequest, 755 Handle<Value> hostDefined, 756 Handle<Value> payload, uint32_t lineNumber, 757 JS::ColumnNumberOneOrigin columnNumber) { 758 MOZ_ASSERT(moduleRequest); 759 MOZ_ASSERT(!payload.isUndefined()); 760 761 JS::ModuleLoadHook moduleLoadHook = cx->runtime()->moduleLoadHook; 762 if (!moduleLoadHook) { 763 JS_ReportErrorASCII(cx, "Module load hook not set"); 764 return false; 765 } 766 767 bool ok = moduleLoadHook(cx, referrer, moduleRequest, hostDefined, payload, 768 lineNumber, columnNumber); 769 770 if (!ok) { 771 MOZ_ASSERT(JS_IsExceptionPending(cx)); 772 if (JS_IsExceptionPending(cx)) { 773 return JS::FinishLoadingImportedModuleFailedWithPendingException(cx, 774 payload); 775 } 776 777 return JS::FinishLoadingImportedModuleFailed(cx, payload, 778 UndefinedHandleValue); 779 } 780 781 return true; 782 } 783 784 static bool ModuleResolveExportImpl(JSContext* cx, Handle<ModuleObject*> module, 785 Handle<JSAtom*> exportName, 786 MutableHandle<ResolveSet> resolveSet, 787 MutableHandle<Value> result, 788 ModuleErrorInfo* errorInfoOut = nullptr) { 789 if (module->hasSyntheticModuleFields()) { 790 return SyntheticModuleResolveExport(cx, module, exportName, result, 791 errorInfoOut); 792 } 793 794 return CyclicModuleResolveExport(cx, module, exportName, resolveSet, result, 795 errorInfoOut); 796 } 797 798 // https://tc39.es/ecma262/#sec-resolveexport 799 // ES2023 16.2.1.6.3 ResolveExport 800 // 801 // Returns an value describing the location of the resolved export or indicating 802 // a failure. 803 // 804 // On success this returns a resolved binding record: { module, bindingName } 805 // 806 // There are two failure cases: 807 // 808 // - If no definition was found or the request is found to be circular, *null* 809 // is returned. 810 // 811 // - If the request is found to be ambiguous, the string `"ambiguous"` is 812 // returned. 813 // 814 static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module, 815 Handle<JSAtom*> exportName, 816 MutableHandle<Value> result, 817 ModuleErrorInfo* errorInfoOut = nullptr) { 818 // Step 1. Assert: module.[[Status]] is not new. 819 MOZ_ASSERT(module->status() != ModuleStatus::New); 820 821 // Step 2. If resolveSet is not present, set resolveSet to a new empty List. 822 Rooted<ResolveSet> resolveSet(cx); 823 return ModuleResolveExportImpl(cx, module, exportName, &resolveSet, result, 824 errorInfoOut); 825 } 826 827 static bool CreateResolvedBindingObject(JSContext* cx, 828 Handle<ModuleObject*> module, 829 Handle<JSAtom*> bindingName, 830 MutableHandle<Value> result) { 831 ResolvedBindingObject* obj = 832 ResolvedBindingObject::create(cx, module, bindingName); 833 if (!obj) { 834 return false; 835 } 836 837 result.setObject(*obj); 838 return true; 839 } 840 841 static bool CyclicModuleResolveExport(JSContext* cx, 842 Handle<ModuleObject*> module, 843 Handle<JSAtom*> exportName, 844 MutableHandle<ResolveSet> resolveSet, 845 MutableHandle<Value> result, 846 ModuleErrorInfo* errorInfoOut) { 847 // Step 2. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do: 848 for (const auto& entry : resolveSet) { 849 // Step 3.a. If module and r.[[Module]] are the same Module Record and 850 // exportName is r.[[ExportName]], then: 851 if (entry.module() == module && entry.exportName() == exportName) { 852 // Step 3.a.i. Assert: This is a circular import request. 853 // Step 3.a.ii. Return null. 854 result.setNull(); 855 if (errorInfoOut) { 856 errorInfoOut->setCircularImport(cx, module); 857 } 858 return true; 859 } 860 } 861 862 // Step 4. Append the Record { [[Module]]: module, [[ExportName]]: exportName 863 // } to resolveSet. 864 if (!resolveSet.emplaceBack(module, exportName)) { 865 ReportOutOfMemory(cx); 866 return false; 867 } 868 869 // Step 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do: 870 for (const ExportEntry& e : module->localExportEntries()) { 871 // Step 5.a. If e.[[ExportName]] is exportName, then: 872 if (exportName == e.exportName()) { 873 // Step 5.a.i. Assert: module provides the direct binding for this export. 874 // Step 5.a.ii. Return ResolvedBinding Record { [[Module]]: module, 875 // [[BindingName]]: e.[[LocalName]] }. 876 Rooted<JSAtom*> localName(cx, e.localName()); 877 return CreateResolvedBindingObject(cx, module, localName, result); 878 } 879 } 880 881 // Step 6. For each ExportEntry Record e of module.[[IndirectExportEntries]], 882 // do: 883 Rooted<ModuleRequestObject*> moduleRequest(cx); 884 Rooted<ModuleObject*> importedModule(cx); 885 Rooted<JSAtom*> name(cx); 886 for (const ExportEntry& e : module->indirectExportEntries()) { 887 // Step 6.a. If e.[[ExportName]] is exportName, then: 888 if (exportName == e.exportName()) { 889 // Step 6.a.i. Assert: e.[[ModuleRequest]] is not null. 890 MOZ_ASSERT(e.moduleRequest()); 891 892 // Step 6.a.ii. Let importedModule be ? GetImportedModule(module, 893 // e.[[ModuleRequest]]). 894 moduleRequest = e.moduleRequest(); 895 importedModule = GetImportedModule(cx, module, moduleRequest); 896 if (!importedModule) { 897 return false; 898 } 899 MOZ_ASSERT(importedModule->status() >= ModuleStatus::Unlinked); 900 901 // Step 6.a.iii. If e.[[ImportName]] is ALL, then: 902 if (!e.importName()) { 903 // Step 6.a.iii.1. Assert: module does not provide the direct binding 904 // for this export. 905 // Step 6.a.iii.2. Return ResolvedBinding Record { [[Module]]: 906 // importedModule, [[BindingName]]: NAMESPACE }. 907 name = cx->names().star_namespace_star_; 908 return CreateResolvedBindingObject(cx, importedModule, name, result); 909 } else { 910 // Step 6.a.iv.1. Assert: module imports a specific binding for this 911 // export. 912 // Step 6.a.iv.2. Return ? importedModule.ResolveExport(e.[[ImportName]] 913 // , resolveSet). 914 name = e.importName(); 915 916 return ModuleResolveExportImpl(cx, importedModule, name, resolveSet, 917 result, errorInfoOut); 918 } 919 } 920 } 921 922 // Step 7. If exportName is "default"), then: 923 if (exportName == cx->names().default_) { 924 // Step 7.a. Assert: A default export was not explicitly defined by this 925 // module. 926 // Step 7.b. Return null. 927 // Step 7.c. NOTE: A default export cannot be provided by an export * from 928 // "mod" declaration. 929 result.setNull(); 930 if (errorInfoOut) { 931 errorInfoOut->setImportedModule(cx, module); 932 } 933 return true; 934 } 935 936 // Step 8. Let starResolution be null. 937 Rooted<ResolvedBindingObject*> starResolution(cx); 938 939 // Step 9. For each ExportEntry Record e of module.[[StarExportEntries]], do: 940 Rooted<Value> resolution(cx); 941 Rooted<ResolvedBindingObject*> binding(cx); 942 for (const ExportEntry& e : module->starExportEntries()) { 943 // Step 9.a. Assert: e.[[ModuleRequest]] is not null. 944 MOZ_ASSERT(e.moduleRequest()); 945 946 // Step 9.b. Let importedModule be ? GetImportedModule(module, 947 // e.[[ModuleRequest]]). 948 moduleRequest = e.moduleRequest(); 949 importedModule = GetImportedModule(cx, module, moduleRequest); 950 if (!importedModule) { 951 return false; 952 } 953 MOZ_ASSERT(importedModule->status() >= ModuleStatus::Unlinked); 954 955 // Step 9.c. Let resolution be ? importedModule.ResolveExport(exportName, 956 // resolveSet). 957 if (!CyclicModuleResolveExport(cx, importedModule, exportName, resolveSet, 958 &resolution, errorInfoOut)) { 959 return false; 960 } 961 962 // Step 9.d. If resolution is AMBIGUOUS, return AMBIGUOUS. 963 if (resolution == StringValue(cx->names().ambiguous)) { 964 result.set(resolution); 965 return true; 966 } 967 968 // Step 9.e. If resolution is not null, then: 969 if (!resolution.isNull()) { 970 // Step 9.e.i. Assert: resolution is a ResolvedBinding Record. 971 binding = &resolution.toObject().as<ResolvedBindingObject>(); 972 973 // Step 9.e.ii. If starResolution is null, set starResolution to 974 // resolution. 975 if (!starResolution) { 976 starResolution = binding; 977 } else { 978 // Step 9.e.iii. Else: 979 // Step 9.e.iii.1. Assert: There is more than one * import that includes 980 // the requested name. 981 // Step 9.e.iii.2. If resolution.[[Module]] and 982 // starResolution.[[Module]] are not the same Module 983 // Record, return AMBIGUOUS. 984 // Step 9.e.iii.3. If resolution.[[BindingName]] is not 985 // starResolution.[[BindingName]] and either 986 // resolution.[[BindingName]] or 987 // starResolution.[[BindingName]] is namespace, return 988 // AMBIGUOUS. 989 // Step 9.e.iii.4. If resolution.[[BindingName]] is a String, 990 // starResolution.[[BindingName]] is a String, and 991 // resolution.[[BindingName]] is not 992 // starResolution.[[BindingName]]), return AMBIGUOUS. 993 if (binding->module() != starResolution->module() || 994 binding->bindingName() != starResolution->bindingName()) { 995 result.set(StringValue(cx->names().ambiguous)); 996 997 if (errorInfoOut) { 998 ModuleObject* module1 = starResolution->module(); 999 ModuleObject* module2 = binding->module(); 1000 errorInfoOut->setForAmbiguousImport(cx, module, module1, module2); 1001 } 1002 return true; 1003 } 1004 } 1005 } 1006 } 1007 1008 // Step 10. Return starResolution. 1009 result.setObjectOrNull(starResolution); 1010 if (!starResolution && errorInfoOut) { 1011 errorInfoOut->setImportedModule(cx, module); 1012 } 1013 return true; 1014 } 1015 1016 // https://tc39.es/proposal-json-modules/#sec-smr-resolveexport 1017 static bool SyntheticModuleResolveExport(JSContext* cx, 1018 Handle<ModuleObject*> module, 1019 Handle<JSAtom*> exportName, 1020 MutableHandle<Value> result, 1021 ModuleErrorInfo* errorInfoOut) { 1022 // Step 2. If module.[[ExportNames]] does not contain exportName, return null. 1023 if (!ContainsElement(module->syntheticExportNames(), exportName)) { 1024 result.setNull(); 1025 if (errorInfoOut) { 1026 errorInfoOut->setImportedModule(cx, module); 1027 } 1028 return true; 1029 } 1030 1031 // Step 3. Return ResolvedBinding Record { [[Module]]: module, 1032 // [[BindingName]]: exportName }. 1033 return CreateResolvedBindingObject(cx, module, exportName, result); 1034 } 1035 1036 // https://tc39.es/ecma262/#sec-getmodulenamespace 1037 // ES2023 16.2.1.10 GetModuleNamespace 1038 ModuleNamespaceObject* js::GetOrCreateModuleNamespace( 1039 JSContext* cx, Handle<ModuleObject*> module) { 1040 // Step 1. Assert: If module is a Cyclic Module Record, then module.[[Status]] 1041 // is not new or unlinked. 1042 MOZ_ASSERT(module->status() != ModuleStatus::New || 1043 module->status() != ModuleStatus::Unlinked); 1044 1045 // Step 2. Let namespace be module.[[Namespace]]. 1046 Rooted<ModuleNamespaceObject*> ns(cx, module->namespace_()); 1047 1048 // Step 3. If namespace is empty, then: 1049 if (!ns) { 1050 // Step 3.a. Let exportedNames be ? module.GetExportedNames(). 1051 Rooted<ModuleSet> exportStarSet(cx); 1052 Rooted<ExportNameVector> exportedNames(cx); 1053 if (!ModuleGetExportedNames(cx, module, &exportStarSet, &exportedNames)) { 1054 return nullptr; 1055 } 1056 1057 // Step 3.b. Let unambiguousNames be a new empty List. 1058 Rooted<UniquePtr<ExportNameVector>> unambiguousNames( 1059 cx, cx->make_unique<ExportNameVector>()); 1060 if (!unambiguousNames) { 1061 return nullptr; 1062 } 1063 1064 // Step 3.c. For each element name of exportedNames, do: 1065 Rooted<JSAtom*> name(cx); 1066 Rooted<Value> resolution(cx); 1067 for (JSAtom* atom : exportedNames) { 1068 name = atom; 1069 1070 // Step 3.c.i. Let resolution be ? module.ResolveExport(name). 1071 if (!ModuleResolveExport(cx, module, name, &resolution)) { 1072 return nullptr; 1073 } 1074 1075 // Step 3.c.ii. If resolution is a ResolvedBinding Record, append name to 1076 // unambiguousNames. 1077 if (resolution.isObject() && !unambiguousNames->append(name)) { 1078 ReportOutOfMemory(cx); 1079 return nullptr; 1080 } 1081 } 1082 1083 // Step 3.d. Set namespace to ModuleNamespaceCreate(module, 1084 // unambiguousNames). 1085 ns = ModuleNamespaceCreate(cx, module, &unambiguousNames); 1086 } 1087 1088 // Step 4. Return namespace. 1089 return ns; 1090 } 1091 1092 static bool IsResolvedBinding(JSContext* cx, Handle<Value> resolution) { 1093 MOZ_ASSERT(resolution.isObjectOrNull() || 1094 resolution.toString() == cx->names().ambiguous); 1095 return resolution.isObject(); 1096 } 1097 1098 static void InitNamespaceBinding(JSContext* cx, 1099 Handle<ModuleEnvironmentObject*> env, 1100 Handle<JSAtom*> name, 1101 Handle<ModuleNamespaceObject*> ns) { 1102 // The property already exists in the evironment but is not writable, so set 1103 // the slot directly. 1104 RootedId id(cx, AtomToId(name)); 1105 mozilla::Maybe<PropertyInfo> prop = env->lookup(cx, id); 1106 MOZ_ASSERT(prop.isSome()); 1107 env->setSlot(prop->slot(), ObjectValue(*ns)); 1108 } 1109 1110 struct AtomComparator { 1111 bool operator()(JSAtom* a, JSAtom* b, bool* lessOrEqualp) { 1112 int32_t result = CompareStrings(a, b); 1113 *lessOrEqualp = (result <= 0); 1114 return true; 1115 } 1116 }; 1117 1118 // https://tc39.es/ecma262/#sec-modulenamespacecreate 1119 // ES2023 10.4.6.12 ModuleNamespaceCreate 1120 static ModuleNamespaceObject* ModuleNamespaceCreate( 1121 JSContext* cx, Handle<ModuleObject*> module, 1122 MutableHandle<UniquePtr<ExportNameVector>> exports) { 1123 // Step 1. Assert: module.[[Namespace]] is empty. 1124 MOZ_ASSERT(!module->namespace_()); 1125 1126 // Step 6. Let sortedExports be a List whose elements are the elements of 1127 // exports ordered as if an Array of the same values had been sorted 1128 // using %Array.prototype.sort% using undefined as comparefn. 1129 ExportNameVector scratch; 1130 if (!scratch.resize(exports->length())) { 1131 ReportOutOfMemory(cx); 1132 return nullptr; 1133 } 1134 MOZ_ALWAYS_TRUE(MergeSort(exports->begin(), exports->length(), 1135 scratch.begin(), AtomComparator())); 1136 1137 // Steps 2 - 5. 1138 Rooted<ModuleNamespaceObject*> ns( 1139 cx, ModuleObject::createNamespace(cx, module, exports)); 1140 if (!ns) { 1141 return nullptr; 1142 } 1143 1144 // Pre-compute all binding mappings now instead of on each access. 1145 // See: 1146 // https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver 1147 // ES2023 10.4.6.8 Module Namespace Exotic Object [[Get]] 1148 Rooted<JSAtom*> name(cx); 1149 Rooted<Value> resolution(cx); 1150 Rooted<ResolvedBindingObject*> binding(cx); 1151 Rooted<ModuleObject*> importedModule(cx); 1152 Rooted<ModuleNamespaceObject*> importedNamespace(cx); 1153 Rooted<JSAtom*> bindingName(cx); 1154 for (JSAtom* atom : ns->exports()) { 1155 name = atom; 1156 1157 if (!ModuleResolveExport(cx, module, name, &resolution)) { 1158 return nullptr; 1159 } 1160 1161 MOZ_ASSERT(IsResolvedBinding(cx, resolution)); 1162 binding = &resolution.toObject().as<ResolvedBindingObject>(); 1163 importedModule = binding->module(); 1164 bindingName = binding->bindingName(); 1165 1166 if (bindingName == cx->names().star_namespace_star_) { 1167 importedNamespace = GetOrCreateModuleNamespace(cx, importedModule); 1168 if (!importedNamespace) { 1169 return nullptr; 1170 } 1171 1172 // The spec uses an immutable binding here but we have already generated 1173 // bytecode for an indirect binding. Instead, use an indirect binding to 1174 // "*namespace*" slot of the target environment. 1175 Rooted<ModuleEnvironmentObject*> env( 1176 cx, &importedModule->initialEnvironment()); 1177 InitNamespaceBinding(cx, env, bindingName, importedNamespace); 1178 } 1179 1180 if (!ns->addBinding(cx, name, importedModule, bindingName)) { 1181 return nullptr; 1182 } 1183 } 1184 1185 // Step 10. Return M. 1186 return ns; 1187 } 1188 1189 void ModuleErrorInfo::setImportedModule(JSContext* cx, 1190 ModuleObject* importedModule) { 1191 imported = importedModule->filename(); 1192 } 1193 1194 void ModuleErrorInfo::setCircularImport(JSContext* cx, 1195 ModuleObject* importedModule) { 1196 setImportedModule(cx, importedModule); 1197 isCircular = true; 1198 } 1199 1200 void ModuleErrorInfo::setForAmbiguousImport(JSContext* cx, 1201 ModuleObject* importedModule, 1202 ModuleObject* module1, 1203 ModuleObject* module2) { 1204 setImportedModule(cx, importedModule); 1205 entry1 = module1->filename(); 1206 entry2 = module2->filename(); 1207 } 1208 1209 static void CreateErrorNumberMessageUTF8(JSContext* cx, unsigned errorNumber, 1210 JSErrorReport* reportOut, ...) { 1211 va_list ap; 1212 va_start(ap, reportOut); 1213 AutoReportFrontendContext fc(cx); 1214 if (!ExpandErrorArgumentsVA(&fc, GetErrorMessage, nullptr, errorNumber, 1215 ArgumentsAreUTF8, reportOut, ap)) { 1216 ReportOutOfMemory(cx); 1217 return; 1218 } 1219 1220 va_end(ap); 1221 } 1222 1223 static void ThrowResolutionError(JSContext* cx, Handle<ModuleObject*> module, 1224 Handle<Value> resolution, Handle<JSAtom*> name, 1225 ModuleErrorInfo* errorInfo) { 1226 MOZ_ASSERT(errorInfo); 1227 auto chars = StringToNewUTF8CharsZ(cx, *name); 1228 if (!chars) { 1229 ReportOutOfMemory(cx); 1230 return; 1231 } 1232 1233 bool isAmbiguous = resolution == StringValue(cx->names().ambiguous); 1234 1235 unsigned errorNumber; 1236 if (errorInfo->isCircular) { 1237 errorNumber = JSMSG_MODULE_CIRCULAR_IMPORT; 1238 } else if (isAmbiguous) { 1239 errorNumber = JSMSG_MODULE_AMBIGUOUS; 1240 } else { 1241 errorNumber = JSMSG_MODULE_NO_EXPORT; 1242 } 1243 1244 JSErrorReport report; 1245 report.isWarning_ = false; 1246 report.errorNumber = errorNumber; 1247 1248 if (errorNumber == JSMSG_MODULE_AMBIGUOUS) { 1249 CreateErrorNumberMessageUTF8(cx, errorNumber, &report, errorInfo->imported, 1250 chars.get(), errorInfo->entry1, 1251 errorInfo->entry2); 1252 } else { 1253 CreateErrorNumberMessageUTF8(cx, errorNumber, &report, errorInfo->imported, 1254 chars.get()); 1255 } 1256 1257 Rooted<JSString*> message(cx, report.newMessageString(cx)); 1258 if (!message) { 1259 ReportOutOfMemory(cx); 1260 return; 1261 } 1262 1263 const char* file = module->filename(); 1264 RootedString filename( 1265 cx, JS_NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(file, strlen(file)))); 1266 if (!filename) { 1267 ReportOutOfMemory(cx); 1268 return; 1269 } 1270 1271 RootedValue error(cx); 1272 if (!JS::CreateError(cx, JSEXN_SYNTAXERR, nullptr, filename, 1273 errorInfo->lineNumber, errorInfo->columnNumber, nullptr, 1274 message, JS::NothingHandleValue, &error)) { 1275 ReportOutOfMemory(cx); 1276 return; 1277 } 1278 1279 cx->setPendingException(error, nullptr); 1280 } 1281 1282 // https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment 1283 // ES2023 16.2.1.6.4 InitializeEnvironment 1284 static bool ModuleInitializeEnvironment(JSContext* cx, 1285 Handle<ModuleObject*> module) { 1286 MOZ_ASSERT(module->status() == ModuleStatus::Linking); 1287 1288 // Step 1. For each ExportEntry Record e of module.[[IndirectExportEntries]], 1289 // do: 1290 Rooted<JSAtom*> exportName(cx); 1291 Rooted<Value> resolution(cx); 1292 for (const ExportEntry& e : module->indirectExportEntries()) { 1293 // Step 1.a. Assert: e.[[ExportName]] is not null. 1294 MOZ_ASSERT(e.exportName()); 1295 1296 // Step 1.b. Let resolution be ? module.ResolveExport(e.[[ExportName]]). 1297 exportName = e.exportName(); 1298 ModuleErrorInfo errorInfo{e.lineNumber(), e.columnNumber()}; 1299 if (!ModuleResolveExport(cx, module, exportName, &resolution, &errorInfo)) { 1300 return false; 1301 } 1302 1303 // Step 1.c. If resolution is either null or AMBIGUOUS, throw a SyntaxError 1304 // exception. 1305 if (!IsResolvedBinding(cx, resolution)) { 1306 ThrowResolutionError(cx, module, resolution, exportName, &errorInfo); 1307 return false; 1308 } 1309 } 1310 1311 // Step 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]). 1312 // Step 6. Set module.[[Environment]] to env. 1313 // Note that we have already created the environment by this point. 1314 Rooted<ModuleEnvironmentObject*> env(cx, &module->initialEnvironment()); 1315 1316 // Step 7. For each ImportEntry Record in of module.[[ImportEntries]], do: 1317 Rooted<ModuleRequestObject*> moduleRequest(cx); 1318 Rooted<ModuleObject*> importedModule(cx); 1319 Rooted<JSAtom*> importName(cx); 1320 Rooted<JSAtom*> localName(cx); 1321 Rooted<ModuleObject*> sourceModule(cx); 1322 Rooted<JSAtom*> bindingName(cx); 1323 for (const ImportEntry& in : module->importEntries()) { 1324 // Step 7.a. Let importedModule be ! GetImportedModule(module, 1325 // in.[[ModuleRequest]]). 1326 moduleRequest = in.moduleRequest(); 1327 importedModule = GetImportedModule(cx, module, moduleRequest); 1328 if (!importedModule) { 1329 return false; 1330 } 1331 MOZ_ASSERT(importedModule->status() >= ModuleStatus::Linking); 1332 1333 localName = in.localName(); 1334 importName = in.importName(); 1335 1336 // Step 7.c. If in.[[ImportName]] is namespace-object, then: 1337 if (!importName) { 1338 // Step 7.c.i. Let namespace be ? GetModuleNamespace(importedModule). 1339 Rooted<ModuleNamespaceObject*> ns( 1340 cx, GetOrCreateModuleNamespace(cx, importedModule)); 1341 if (!ns) { 1342 return false; 1343 } 1344 1345 // Step 7.c.ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]], 1346 // true). This happens when the environment is created. 1347 1348 // Step 7.c.iii. Perform ! env.InitializeBinding(in.[[LocalName]], 1349 // namespace). 1350 InitNamespaceBinding(cx, env, localName, ns); 1351 } else { 1352 // Step 7.d. Else: 1353 // Step 7.d.i. Let resolution be ? 1354 // importedModule.ResolveExport(in.[[ImportName]]). 1355 ModuleErrorInfo errorInfo{in.lineNumber(), in.columnNumber()}; 1356 if (!ModuleResolveExport(cx, importedModule, importName, &resolution, 1357 &errorInfo)) { 1358 return false; 1359 } 1360 1361 // Step 7.d.ii. If resolution is null or ambiguous, throw a SyntaxError 1362 // exception. 1363 if (!IsResolvedBinding(cx, resolution)) { 1364 ThrowResolutionError(cx, module, resolution, importName, &errorInfo); 1365 return false; 1366 } 1367 1368 auto* binding = &resolution.toObject().as<ResolvedBindingObject>(); 1369 sourceModule = binding->module(); 1370 bindingName = binding->bindingName(); 1371 1372 // Step 7.d.iii. If resolution.[[BindingName]] is namespace, then: 1373 if (bindingName == cx->names().star_namespace_star_) { 1374 // Step 7.d.iii.1. Let namespace be ? 1375 // GetModuleNamespace(resolution.[[Module]]). 1376 Rooted<ModuleNamespaceObject*> ns( 1377 cx, GetOrCreateModuleNamespace(cx, sourceModule)); 1378 if (!ns) { 1379 return false; 1380 } 1381 1382 // Step 7.d.iii.2. Perform ! 1383 // env.CreateImmutableBinding(in.[[LocalName]], true). 1384 // Step 7.d.iii.3. Perform ! env.InitializeBinding(in.[[LocalName]], 1385 // namespace). 1386 // 1387 // This should be InitNamespaceBinding, but we have already generated 1388 // bytecode assuming an indirect binding. Instead, ensure a special 1389 // "*namespace*"" binding exists on the target module's environment. We 1390 // then generate an indirect binding to this synthetic binding. 1391 Rooted<ModuleEnvironmentObject*> sourceEnv( 1392 cx, &sourceModule->initialEnvironment()); 1393 InitNamespaceBinding(cx, sourceEnv, bindingName, ns); 1394 if (!env->createImportBinding(cx, localName, sourceModule, 1395 bindingName)) { 1396 return false; 1397 } 1398 } else { 1399 // Step 7.d.iv. Else: 1400 // Step 7.d.iv.1. 1. Perform env.CreateImportBinding(in.[[LocalName]], 1401 // resolution.[[Module]], resolution.[[BindingName]]). 1402 if (!env->createImportBinding(cx, localName, sourceModule, 1403 bindingName)) { 1404 return false; 1405 } 1406 } 1407 } 1408 } 1409 1410 // Steps 8-26. 1411 // 1412 // Some of these do not need to happen for practical purposes. For steps 1413 // 21-23, the bindings that can be handled in a similar way to regulars 1414 // scripts are done separately. Function Declarations are special due to 1415 // hoisting and are handled within this function. See ModuleScope and 1416 // ModuleEnvironmentObject for further details. 1417 1418 // Step 24. For each element d of lexDeclarations, do: 1419 // Step 24.a. For each element dn of the BoundNames of d, do: 1420 // Step 24.a.iii. If d is a FunctionDeclaration, a GeneratorDeclaration, an 1421 // AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration, 1422 // then: 1423 // Step 24.a.iii.1 Let fo be InstantiateFunctionObject of d with arguments env 1424 // and privateEnv. 1425 // Step 24.a.iii.2. Perform ! env.InitializeBinding(dn, fo). 1426 return ModuleObject::instantiateFunctionDeclarations(cx, module); 1427 } 1428 1429 static bool FailWithUnsupportedAttributeException( 1430 JSContext* cx, Handle<GraphLoadingStateRecordObject*> state, 1431 Handle<ModuleRequestObject*> moduleRequest) { 1432 UniqueChars printableKey = AtomToPrintableString( 1433 cx, moduleRequest->getFirstUnsupportedAttributeKey()); 1434 JS_ReportErrorNumberASCII( 1435 cx, GetErrorMessage, nullptr, 1436 JSMSG_IMPORT_ATTRIBUTES_STATIC_IMPORT_UNSUPPORTED_ATTRIBUTE, 1437 printableKey ? printableKey.get() : ""); 1438 1439 JS::ExceptionStack exnStack(cx); 1440 if (!JS::StealPendingExceptionStack(cx, &exnStack)) { 1441 return false; 1442 } 1443 1444 return ContinueModuleLoading(cx, state, nullptr, ImportPhase::Evaluation, 1445 exnStack.exception()); 1446 } 1447 1448 // https://tc39.es/ecma262/#sec-InnerModuleLoading 1449 // InnerModuleLoading ( state, module ) 1450 static bool InnerModuleLoading(JSContext* cx, 1451 Handle<GraphLoadingStateRecordObject*> state, 1452 Handle<ModuleObject*> module) { 1453 MOZ_ASSERT(state); 1454 MOZ_ASSERT(module); 1455 1456 // Step 1. Assert: state.[[IsLoading]] is true. 1457 MOZ_ASSERT(state->isLoading()); 1458 1459 // Step 2. If module is a Cyclic Module Record, module.[[Status]] is new, and 1460 // state.[[Visited]] does not contain module, then 1461 if (module->hasCyclicModuleFields() && 1462 module->status() == ModuleStatus::New && !state->visited().has(module)) { 1463 // Step 2.a. Append module to state.[[Visited]]. 1464 if (!state->visited().putNew(module)) { 1465 ReportOutOfMemory(cx); 1466 return false; 1467 } 1468 1469 // Step 2.b. Let requestedModulesCount be the number of elements in 1470 // module.[[RequestedModules]]. 1471 size_t requestedModulesCount = module->requestedModules().Length(); 1472 1473 // Step 2.c. Set state.[[PendingModulesCount]] to 1474 // state.[[PendingModulesCount]] + requestedModulesCount. 1475 uint32_t count = state->pendingModulesCount() + requestedModulesCount; 1476 state->setPendingModulesCount(count); 1477 1478 // Step 2.d. For each String required of module.[[RequestedModules]], do 1479 Rooted<ModuleRequestObject*> moduleRequest(cx); 1480 Rooted<ModuleObject*> recordModule(cx); 1481 Rooted<JSAtom*> invalidKey(cx); 1482 for (const RequestedModule& request : module->requestedModules()) { 1483 moduleRequest = request.moduleRequest(); 1484 1485 // https://tc39.es/proposal-import-attributes/#sec-InnerModuleLoading 1486 if (moduleRequest->hasFirstUnsupportedAttributeKey()) { 1487 if (!FailWithUnsupportedAttributeException(cx, state, moduleRequest)) { 1488 return false; 1489 } 1490 } else if (auto record = module->loadedModules().lookup(moduleRequest)) { 1491 // Step 2.d.i. If module.[[LoadedModules]] contains a Record whose 1492 // [[Specifier]] is required, then 1493 // Step 2.d.i.1. Let record be that Record. 1494 // Step 2.d.i.2. Perform InnerModuleLoading(state, record.[[Module]]). 1495 recordModule = record->value(); 1496 if (!InnerModuleLoading(cx, state, recordModule)) { 1497 return false; 1498 } 1499 } else { 1500 // Step 2.d.ii. Else, 1501 // Step 2.d.ii.1. Perform HostLoadImportedModule(module, required, 1502 // state.[[HostDefined]], state). 1503 Rooted<JSScript*> referrer(cx, module->script()); 1504 Rooted<Value> hostDefined(cx, state->hostDefined()); 1505 Rooted<Value> payload(cx, ObjectValue(*state)); 1506 if (!HostLoadImportedModule(cx, referrer, moduleRequest, hostDefined, 1507 payload, request.lineNumber(), 1508 request.columnNumber())) { 1509 return false; 1510 } 1511 } 1512 1513 // Step 2.d.iii. If state.[[IsLoading]] is false, return unused. 1514 if (!state->isLoading()) { 1515 return true; 1516 } 1517 } 1518 } 1519 1520 // Step 3. Assert: state.[[PendingModulesCount]] ≥ 1. 1521 MOZ_ASSERT(state->pendingModulesCount() >= 1); 1522 1523 // Step 4. Set state.[[PendingModulesCount]] to 1524 // state.[[PendingModulesCount]] - 1. 1525 uint32_t count = state->pendingModulesCount() - 1; 1526 state->setPendingModulesCount(count); 1527 1528 // Step 5. If state.[[PendingModulesCount]] = 0, then 1529 if (state->pendingModulesCount() == 0) { 1530 // Step 5.a. Set state.[[IsLoading]] to false. 1531 state->setIsLoading(false); 1532 1533 // Step 5.b. For each Cyclic Module Record loaded of state.[[Visited]], do 1534 for (auto iter = state->visited().iter(); !iter.done(); iter.next()) { 1535 auto& loaded = iter.get(); 1536 // Step 5.b.i. If loaded.[[Status]] is new, set loaded.[[Status]] to 1537 // unlinked. 1538 if (loaded->status() == ModuleStatus::New) { 1539 loaded->setStatus(ModuleStatus::Unlinked); 1540 } 1541 } 1542 1543 // Step 5.c. Perform ! Call(state.[[PromiseCapability]].[[Resolve]], 1544 // undefined, « undefined »). 1545 RootedValue hostDefined(cx, state->hostDefined()); 1546 if (!state->resolved(cx, hostDefined)) { 1547 return false; 1548 } 1549 } 1550 1551 // Step 6. Return unused. 1552 return true; 1553 } 1554 1555 // https://tc39.es/ecma262/#sec-ContinueModuleLoading 1556 // ContinueModuleLoading ( state, moduleCompletion ) 1557 static bool ContinueModuleLoading(JSContext* cx, 1558 Handle<GraphLoadingStateRecordObject*> state, 1559 Handle<ModuleObject*> moduleCompletion, 1560 ImportPhase phase, Handle<Value> error) { 1561 MOZ_ASSERT_IF(moduleCompletion, error.isUndefined()); 1562 MOZ_ASSERT(phase < ImportPhase::Limit); 1563 1564 // Step 1. If state.[[IsLoading]] is false, return unused. 1565 if (!state->isLoading()) { 1566 return true; 1567 } 1568 1569 // Step 2. If moduleCompletion is a normal completion, then 1570 if (moduleCompletion) { 1571 // TODO: Bug 1943933: Implement Source Phase Imports 1572 MOZ_ASSERT(phase == ImportPhase::Evaluation); 1573 1574 // Step 2.a. Perform InnerModuleLoading(state, moduleCompletion.[[Value]]). 1575 return InnerModuleLoading(cx, state, moduleCompletion); 1576 } 1577 1578 // Step 3. Else, 1579 // Step 3.a. Set state.[[IsLoading]] to false. 1580 state->setIsLoading(false); 1581 1582 // Step 3.b. Perform ! Call(state.[[PromiseCapability]].[[Reject]], 1583 // undefined, « moduleCompletion.[[Value]] »). 1584 RootedValue hostDefined(cx, state->hostDefined()); 1585 return state->rejected(cx, hostDefined, error); 1586 } 1587 1588 // https://tc39.es/ecma262/#sec-LoadRequestedModules 1589 bool js::LoadRequestedModules(JSContext* cx, Handle<ModuleObject*> module, 1590 Handle<Value> hostDefined, 1591 JS::LoadModuleResolvedCallback resolved, 1592 JS::LoadModuleRejectedCallback rejected) { 1593 if (module->hasSyntheticModuleFields()) { 1594 // Step 1. Return ! PromiseResolve(%Promise%, undefined). 1595 return resolved(cx, hostDefined); 1596 } 1597 1598 // Step 1. If hostDefined is not present, let hostDefined be empty. 1599 // Step 2. Let pc be ! NewPromiseCapability(%Promise%). 1600 // Note: For implementation we use callbacks to notify the results. 1601 1602 // Step 3. Let state be the GraphLoadingState Record { [[IsLoading]]: true, 1603 // [[PendingModulesCount]]: 1, [[Visited]]: « », 1604 // [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }. 1605 Rooted<GraphLoadingStateRecordObject*> state( 1606 cx, GraphLoadingStateRecordObject::create(cx, true, 1, resolved, rejected, 1607 hostDefined)); 1608 if (!state) { 1609 ReportOutOfMemory(cx); 1610 return false; 1611 } 1612 1613 // Step 4. Perform InnerModuleLoading(state, module). 1614 return InnerModuleLoading(cx, state, module); 1615 } 1616 1617 bool js::LoadRequestedModules(JSContext* cx, Handle<ModuleObject*> module, 1618 Handle<Value> hostDefined, 1619 MutableHandle<JSObject*> promiseOut) { 1620 // Step 1. If hostDefined is not present, let hostDefined be empty. 1621 // Step 2. Let pc be ! NewPromiseCapability(%Promise%). 1622 Rooted<PromiseObject*> pc(cx, CreatePromiseObjectForAsync(cx)); 1623 if (!pc) { 1624 ReportOutOfMemory(cx); 1625 return false; 1626 } 1627 1628 if (module->hasSyntheticModuleFields()) { 1629 // Step 1. Return ! PromiseResolve(%Promise%, undefined). 1630 promiseOut.set(pc); 1631 return AsyncFunctionReturned(cx, pc, UndefinedHandleValue); 1632 } 1633 1634 // Step 3. Let state be the GraphLoadingState Record { [[IsLoading]]: true, 1635 // [[PendingModulesCount]]: 1, [[Visited]]: « », 1636 // [[PromiseCapability]]: pc, [[HostDefined]]: hostDefined }. 1637 Rooted<GraphLoadingStateRecordObject*> state( 1638 cx, GraphLoadingStateRecordObject::create(cx, true, 1, pc, hostDefined)); 1639 if (!state) { 1640 ReportOutOfMemory(cx); 1641 return false; 1642 } 1643 1644 // Step 4. Perform InnerModuleLoading(state, module). 1645 if (!InnerModuleLoading(cx, state, module)) { 1646 return false; 1647 } 1648 1649 // Step 5. Return pc.[[Promise]]. 1650 promiseOut.set(pc); 1651 return true; 1652 } 1653 1654 // https://tc39.es/ecma262/#sec-moduledeclarationlinking 1655 // ES2023 16.2.1.5.1 Link 1656 static bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module) { 1657 if (!module->hasCyclicModuleFields()) { 1658 return true; 1659 } 1660 1661 // Step 1. Assert: module.[[Status]] is one of unlinked, linked, 1662 // evaluating-async, or evaluated. 1663 ModuleStatus status = module->status(); 1664 if (status == ModuleStatus::New || status == ModuleStatus::Linking || 1665 status == ModuleStatus::Evaluating) { 1666 ThrowUnexpectedModuleStatus(cx, status); 1667 return false; 1668 } 1669 1670 // Step 2. Let stack be a new empty List. 1671 Rooted<ModuleVector> stack(cx); 1672 1673 // Step 3. Let result be Completion(InnerModuleLinking(module, stack, 0)). 1674 size_t ignored; 1675 bool ok = InnerModuleLinking(cx, module, &stack, 0, &ignored); 1676 1677 // Step 4. If result is an abrupt completion, then: 1678 if (!ok) { 1679 // Step 4.a. For each Cyclic Module Record m of stack, do: 1680 for (ModuleObject* m : stack) { 1681 // Step 4.a.i. Assert: m.[[Status]] is linking. 1682 MOZ_ASSERT(m->status() == ModuleStatus::Linking); 1683 // Step 4.a.ii. Set m.[[Status]] to unlinked. 1684 m->setStatus(ModuleStatus::Unlinked); 1685 m->clearDfsAncestorIndex(); 1686 } 1687 1688 // Step 4.b. Assert: module.[[Status]] is unlinked. 1689 MOZ_ASSERT(module->status() == ModuleStatus::Unlinked); 1690 1691 // Step 4.c. 1692 return false; 1693 } 1694 1695 // Step 5. Assert: module.[[Status]] is linked, evaluating-async, or 1696 // evaluated. 1697 MOZ_ASSERT(module->status() == ModuleStatus::Linked || 1698 module->status() == ModuleStatus::EvaluatingAsync || 1699 module->status() == ModuleStatus::Evaluated); 1700 1701 // Step 6. Assert: stack is empty. 1702 MOZ_ASSERT(stack.empty()); 1703 1704 // Step 7. Return unused. 1705 return true; 1706 } 1707 1708 // https://tc39.es/ecma262/#sec-InnerModuleLinking 1709 // ES2023 16.2.1.5.1.1 InnerModuleLinking 1710 static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module, 1711 MutableHandle<ModuleVector> stack, size_t index, 1712 size_t* indexOut) { 1713 // Step 1. If module is not a Cyclic Module Record, then 1714 if (!module->hasCyclicModuleFields()) { 1715 // Step 1.a. Perform ? module.Link(). (Skipped) 1716 // Step 2.b. Return index. 1717 *indexOut = index; 1718 return true; 1719 } 1720 1721 // Step 2. If module.[[Status]] is linking, linked, evaluating-async, or 1722 // evaluated, then: 1723 if (module->status() == ModuleStatus::Linking || 1724 module->status() == ModuleStatus::Linked || 1725 module->status() == ModuleStatus::EvaluatingAsync || 1726 module->status() == ModuleStatus::Evaluated) { 1727 // Step 2.a. Return index. 1728 *indexOut = index; 1729 return true; 1730 } 1731 1732 // Step 3. Assert: module.[[Status]] is unlinked. 1733 if (module->status() != ModuleStatus::Unlinked) { 1734 ThrowUnexpectedModuleStatus(cx, module->status()); 1735 return false; 1736 } 1737 1738 // Step 8. Append module to stack. 1739 // Do this before changing the status so that we can recover on failure. 1740 if (!stack.append(module)) { 1741 ReportOutOfMemory(cx); 1742 return false; 1743 } 1744 1745 // Step 4. Set module.[[Status]] to linking. 1746 module->setStatus(ModuleStatus::Linking); 1747 1748 // Step 5. Let moduleIndex be index. 1749 size_t moduleIndex = index; 1750 1751 // Step 6. Set module.[[DFSAncestorIndex]] to index. 1752 module->setDfsAncestorIndex(index); 1753 1754 // Step 7. Set index to index + 1. 1755 index++; 1756 1757 AutoCheckRecursionLimit recursion(cx); 1758 if (!recursion.check(cx)) { 1759 return false; 1760 } 1761 1762 // Step 9. For each String required that is an element of 1763 // module.[[RequestedModules]], do: 1764 Rooted<ModuleRequestObject*> required(cx); 1765 Rooted<ModuleObject*> requiredModule(cx); 1766 for (const RequestedModule& request : module->requestedModules()) { 1767 // Step 9.a. Let requiredModule be ? GetImportedModule(module, required). 1768 required = request.moduleRequest(); 1769 MOZ_ASSERT(required->phase() == ImportPhase::Evaluation); 1770 requiredModule = GetImportedModule(cx, module, required); 1771 if (!requiredModule) { 1772 return false; 1773 } 1774 MOZ_ASSERT(requiredModule->status() >= ModuleStatus::Unlinked); 1775 1776 // Step 9.b. Set index to ? InnerModuleLinking(requiredModule, stack, 1777 // index). 1778 if (!InnerModuleLinking(cx, requiredModule, stack, index, &index)) { 1779 return false; 1780 } 1781 1782 // Step 9.c. If requiredModule is a Cyclic Module Record, then: 1783 if (requiredModule->hasCyclicModuleFields()) { 1784 // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking, 1785 // linked, 1786 // evaluating-async, or evaluated. 1787 MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking || 1788 requiredModule->status() == ModuleStatus::Linked || 1789 requiredModule->status() == ModuleStatus::EvaluatingAsync || 1790 requiredModule->status() == ModuleStatus::Evaluated); 1791 1792 // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only 1793 // if 1794 // requiredModule is in stack. 1795 MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) == 1796 ContainsElement(stack, requiredModule)); 1797 1798 // Step 9.c.iii. If requiredModule.[[Status]] is linking, then: 1799 if (requiredModule->status() == ModuleStatus::Linking) { 1800 // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to 1801 // min(module.[[DFSAncestorIndex]], 1802 // requiredModule.[[DFSAncestorIndex]]). 1803 module->setDfsAncestorIndex(std::min( 1804 module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex())); 1805 } 1806 } 1807 } 1808 1809 // Step 10. Perform ? module.InitializeEnvironment(). 1810 if (!ModuleInitializeEnvironment(cx, module)) { 1811 return false; 1812 } 1813 1814 // Step 11. Assert: module occurs exactly once in stack. 1815 MOZ_ASSERT(CountElements(stack, module) == 1); 1816 1817 // Step 12. Assert: module.[[DFSAncestorIndex]] <= moduleIndex. 1818 MOZ_ASSERT(module->dfsAncestorIndex() <= moduleIndex); 1819 1820 // Step 13. If module.[[DFSAncestorIndex]] = moduleIndex, then 1821 if (module->dfsAncestorIndex() == moduleIndex) { 1822 // Step 13.a. 1823 bool done = false; 1824 1825 // Step 13.b. Repeat, while done is false: 1826 while (!done) { 1827 // Step 13.b.i. Let requiredModule be the last element in stack. 1828 // Step 13.b.ii. Remove the last element of stack. 1829 requiredModule = stack.popCopy(); 1830 1831 // Step 13.b.iv. Set requiredModule.[[Status]] to linked. 1832 requiredModule->setStatus(ModuleStatus::Linked); 1833 1834 // Step 13.b.v. If requiredModule and module are the same Module Record, 1835 // set done to true. 1836 done = requiredModule == module; 1837 } 1838 } 1839 1840 // Step 14. Return index. 1841 *indexOut = index; 1842 return true; 1843 } 1844 1845 static bool SyntheticModuleEvaluate(JSContext* cx, 1846 Handle<ModuleObject*> moduleArg, 1847 MutableHandle<Value> rval) { 1848 // Steps 1-12 happen elsewhere in the engine. 1849 1850 // Step 13. Let pc be ! NewPromiseCapability(%Promise%). 1851 Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx)); 1852 if (!resultPromise) { 1853 return false; 1854 } 1855 1856 // Since the only synthetic modules we support are JSON modules, result is 1857 // always |undefined|. 1858 1859 // Step 14. IfAbruptRejectPromise(result, pc) (Skipped) 1860 1861 // 15. Perform ! pc.[[Resolve]](result). 1862 if (!AsyncFunctionReturned(cx, resultPromise, JS::UndefinedHandleValue)) { 1863 return false; 1864 } 1865 1866 // 16. Return pc.[[Promise]]. 1867 rval.set(ObjectValue(*resultPromise)); 1868 return true; 1869 } 1870 1871 // https://tc39.es/ecma262/#sec-moduleevaluation 1872 // ES2023 16.2.1.5.2 Evaluate 1873 static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg, 1874 MutableHandle<Value> result) { 1875 Rooted<ModuleObject*> module(cx, moduleArg); 1876 1877 // Step 2. Assert: module.[[Status]] is linked, evaluating-async, or 1878 // evaluated. 1879 ModuleStatus status = module->status(); 1880 if (status != ModuleStatus::Linked && 1881 status != ModuleStatus::EvaluatingAsync && 1882 status != ModuleStatus::Evaluated) { 1883 ThrowUnexpectedModuleStatus(cx, status); 1884 return false; 1885 } 1886 1887 // Note: we return early in the error case, as the spec assumes we can get the 1888 // cycle root of |module| which may not be available. 1889 if (module->hadEvaluationError()) { 1890 Rooted<PromiseObject*> capability(cx); 1891 if (!module->hasTopLevelCapability()) { 1892 capability = ModuleObject::createTopLevelCapability(cx, module); 1893 if (!capability) { 1894 return false; 1895 } 1896 1897 Rooted<Value> error(cx, module->evaluationError()); 1898 if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) { 1899 return false; 1900 } 1901 } 1902 1903 capability = module->topLevelCapability(); 1904 MOZ_ASSERT(JS::GetPromiseState(capability) == JS::PromiseState::Rejected); 1905 MOZ_ASSERT(JS::GetPromiseResult(capability) == module->evaluationError()); 1906 result.set(ObjectValue(*capability)); 1907 return true; 1908 } 1909 1910 // Step 3. If module.[[Status]] is evaluating-async or evaluated, set module 1911 // to module.[[CycleRoot]]. 1912 if (module->status() == ModuleStatus::EvaluatingAsync || 1913 module->status() == ModuleStatus::Evaluated) { 1914 module = module->getCycleRoot(); 1915 } 1916 1917 // Step 4. If module.[[TopLevelCapability]] is not empty, then: 1918 if (module->hasTopLevelCapability()) { 1919 // Step 4.a. Return module.[[TopLevelCapability]].[[Promise]]. 1920 result.set(ObjectValue(*module->topLevelCapability())); 1921 return true; 1922 } 1923 1924 // Step 5. Let stack be a new empty List. 1925 Rooted<ModuleVector> stack(cx); 1926 1927 // Step 6. Let capability be ! NewPromiseCapability(%Promise%). 1928 // Step 7. Set module.[[TopLevelCapability]] to capability. 1929 Rooted<PromiseObject*> capability( 1930 cx, ModuleObject::createTopLevelCapability(cx, module)); 1931 if (!capability) { 1932 return false; 1933 } 1934 1935 // Step 8. Let result be Completion(InnerModuleEvaluation(module, stack, 0)). 1936 size_t ignored; 1937 bool ok = InnerModuleEvaluation(cx, module, &stack, 0, &ignored); 1938 1939 // Step 9. f result is an abrupt completion, then: 1940 if (!ok) { 1941 // Attempt to take any pending exception, but make sure we still handle 1942 // uncatchable exceptions. 1943 Rooted<Value> error(cx); 1944 if (cx->isExceptionPending()) { 1945 (void)cx->getPendingException(&error); 1946 cx->clearPendingException(); 1947 } 1948 1949 // Step 9.a. For each Cyclic Module Record m of stack, do 1950 for (ModuleObject* m : stack) { 1951 // Step 9.a.i. Assert: m.[[Status]] is evaluating. 1952 MOZ_ASSERT(m->status() == ModuleStatus::Evaluating); 1953 1954 // Step 9.a.ii. Set m.[[Status]] to evaluated. 1955 // Step 9.a.iii. Set m.[[EvaluationError]] to result. 1956 m->setEvaluationError(error); 1957 } 1958 1959 // Handle OOM when appending to the stack or over-recursion errors. 1960 if (stack.empty() && !module->hadEvaluationError()) { 1961 module->setEvaluationError(error); 1962 } 1963 1964 // Step 9.b. Assert: module.[[Status]] is evaluated. 1965 MOZ_ASSERT(module->status() == ModuleStatus::Evaluated); 1966 1967 // Step 9.c. Assert: module.[[EvaluationError]] is result. 1968 MOZ_ASSERT(module->evaluationError() == error); 1969 1970 // Step 9.d. Perform ! Call(capability.[[Reject]], undefined, 1971 // result.[[Value]]). 1972 if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) { 1973 return false; 1974 } 1975 } else { 1976 // Step 10. Else: 1977 // Step 10.a. Assert: module.[[Status]] is evaluating-async or evaluated. 1978 MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync || 1979 module->status() == ModuleStatus::Evaluated); 1980 1981 // Step 10.b. Assert: module.[[EvaluationError]] is empty. 1982 MOZ_ASSERT(!module->hadEvaluationError()); 1983 1984 // Step 10.c. If module.[[Status]] is evalated, then: 1985 if (module->status() == ModuleStatus::Evaluated) { 1986 // Step 10.c.ii. Perform ! Call(capability.[[Resolve]], undefined, 1987 // undefined). 1988 if (!ModuleObject::topLevelCapabilityResolve(cx, module)) { 1989 return false; 1990 } 1991 } 1992 1993 // Step 10.d. Assert: stack is empty. 1994 MOZ_ASSERT(stack.empty()); 1995 } 1996 1997 // Step 11. Return capability.[[Promise]]. 1998 result.set(ObjectValue(*capability)); 1999 return true; 2000 } 2001 2002 // https://tc39.es/ecma262/#sec-innermoduleevaluation 2003 // 16.2.1.5.2.1 InnerModuleEvaluation 2004 static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module, 2005 MutableHandle<ModuleVector> stack, 2006 size_t index, size_t* indexOut) { 2007 // Step 1: If module is not a Cyclic Module Record, then 2008 if (!module->hasCyclicModuleFields()) { 2009 // Step 1.a. Let promise be ! module.Evaluate(). (Skipped) 2010 // Step 1.b. Assert: promise.[[PromiseState]] is not pending. (Skipped) 2011 // Step 1.c. If promise.[[PromiseState]] is rejected, then (Skipped) 2012 // Step 1.c.i Return ThrowCompletion(promise.[[PromiseResult]]). (Skipped) 2013 // Step 1.d. Return index. 2014 *indexOut = index; 2015 return true; 2016 } 2017 2018 // Step 2. If module.[[Status]] is evaluating-async or evaluated, then: 2019 if (module->status() == ModuleStatus::EvaluatingAsync || 2020 module->status() == ModuleStatus::Evaluated) { 2021 // Step 2.a. If module.[[EvaluationError]] is empty, return index. 2022 if (!module->hadEvaluationError()) { 2023 *indexOut = index; 2024 return true; 2025 } 2026 2027 // Step 2.b. Otherwise, return ? module.[[EvaluationError]]. 2028 Rooted<Value> error(cx, module->evaluationError()); 2029 cx->setPendingException(error, ShouldCaptureStack::Maybe); 2030 return false; 2031 } 2032 2033 // Step 3. If module.[[Status]] is evaluating, return index. 2034 if (module->status() == ModuleStatus::Evaluating) { 2035 *indexOut = index; 2036 return true; 2037 } 2038 2039 // Step 4. Assert: module.[[Status]] is linked. 2040 MOZ_ASSERT(module->status() == ModuleStatus::Linked); 2041 2042 // Step 10. Append module to stack. 2043 // Do this before changing the status so that we can recover on failure. 2044 if (!stack.append(module)) { 2045 ReportOutOfMemory(cx); 2046 return false; 2047 } 2048 2049 // Step 5. Set module.[[Status]] to evaluating. 2050 module->setStatus(ModuleStatus::Evaluating); 2051 2052 // Step 6. Let moduleIndex be index. 2053 size_t moduleIndex = index; 2054 2055 // Step 7. Set module.[[DFSAncestorIndex]] to index. 2056 module->setDfsAncestorIndex(index); 2057 2058 // Step 8. Set module.[[PendingAsyncDependencies]] to 0. 2059 module->setPendingAsyncDependencies(0); 2060 2061 // Step 9. Set index to index + 1. 2062 index++; 2063 2064 // Step 11. For each String required of module.[[RequestedModules]], do: 2065 Rooted<ModuleRequestObject*> required(cx); 2066 Rooted<ModuleObject*> requiredModule(cx); 2067 for (const RequestedModule& request : module->requestedModules()) { 2068 // Step 11.a. Let requiredModule be GetImportedModule(module, 2069 // required). 2070 required = request.moduleRequest(); 2071 MOZ_ASSERT(required->phase() == ImportPhase::Evaluation); 2072 requiredModule = GetImportedModule(cx, module, required); 2073 if (!requiredModule) { 2074 return false; 2075 } 2076 MOZ_ASSERT(requiredModule->status() >= ModuleStatus::Linked); 2077 2078 // Step 11.b. Set index to ? InnerModuleEvaluation(requiredModule, stack, 2079 // index). 2080 if (!InnerModuleEvaluation(cx, requiredModule, stack, index, &index)) { 2081 return false; 2082 } 2083 2084 // Step 11.c. If requiredModule is a Cyclic Module Record, then: 2085 if (requiredModule->hasCyclicModuleFields()) { 2086 // Step 11.c.i. Assert: requiredModule.[[Status]] is either evaluating, 2087 // evaluating-async, or evaluated. 2088 MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating || 2089 requiredModule->status() == ModuleStatus::EvaluatingAsync || 2090 requiredModule->status() == ModuleStatus::Evaluated); 2091 2092 // Step 11.c.ii. Assert: requiredModule.[[Status]] is evaluating if and 2093 // only if requiredModule is in stack. 2094 MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) == 2095 ContainsElement(stack, requiredModule)); 2096 2097 // Step 11.c.iii. If requiredModule.[[Status]] is evaluating, then: 2098 if (requiredModule->status() == ModuleStatus::Evaluating) { 2099 // Step 11.c.iii.1. Set module.[[DFSAncestorIndex]] to 2100 // min(module.[[DFSAncestorIndex]], 2101 // requiredModule.[[DFSAncestorIndex]]). 2102 module->setDfsAncestorIndex(std::min( 2103 module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex())); 2104 } else { 2105 // Step 11.c.iv. Else: 2106 // Step 11.c.iv.1. Set requiredModule to requiredModule.[[CycleRoot]]. 2107 requiredModule = requiredModule->getCycleRoot(); 2108 2109 // Step 11.c.iv.2. Assert: requiredModule.[[Status]] is evaluating-async 2110 // or evaluated. 2111 MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync || 2112 requiredModule->status() == ModuleStatus::Evaluated); 2113 2114 // Step 11.c.iv.3. If requiredModule.[[EvaluationError]] is not empty, 2115 // return ? requiredModule.[[EvaluationError]]. 2116 if (requiredModule->hadEvaluationError()) { 2117 Rooted<Value> error(cx, requiredModule->evaluationError()); 2118 cx->setPendingException(error, ShouldCaptureStack::Maybe); 2119 return false; 2120 } 2121 } 2122 2123 // Step 11.c.v. If requiredModule.[[AsyncEvaluationOrder]] is an integer, 2124 // then: 2125 if (requiredModule->asyncEvaluationOrder().isInteger()) { 2126 // Step 11.c.v.2. Append module to 2127 // requiredModule.[[AsyncParentModules]]. 2128 if (!ModuleObject::appendAsyncParentModule(cx, requiredModule, 2129 module)) { 2130 return false; 2131 } 2132 2133 // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to 2134 // module.[[PendingAsyncDependencies]] + 1. 2135 module->setPendingAsyncDependencies(module->pendingAsyncDependencies() + 2136 1); 2137 } 2138 } 2139 } 2140 2141 // Step 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is 2142 // true, then: 2143 if (module->pendingAsyncDependencies() > 0 || module->hasTopLevelAwait()) { 2144 // Step 12.a. Assert: module.[[AsyncEvaluationOrder]] is unset. 2145 MOZ_ASSERT(module->asyncEvaluationOrder().isUnset()); 2146 2147 // Step 12.b. Set module.[[AsyncEvaluationOrder]] to 2148 // IncrementModuleAsyncEvaluationCount(). 2149 module->asyncEvaluationOrder().set(cx->runtime()); 2150 2151 // Step 12.d. If module.[[PendingAsyncDependencies]] is 0, perform 2152 // ExecuteAsyncModule(module). 2153 if (module->pendingAsyncDependencies() == 0) { 2154 if (!ExecuteAsyncModule(cx, module)) { 2155 return false; 2156 } 2157 } 2158 } else { 2159 // Step 13. Otherwise, perform ? module.ExecuteModule(). 2160 if (!ModuleObject::execute(cx, module)) { 2161 return false; 2162 } 2163 } 2164 2165 // Step 14. Assert: module occurs exactly once in stack. 2166 MOZ_ASSERT(CountElements(stack, module) == 1); 2167 2168 // Step 15. Assert: module.[[DFSAncestorIndex]] <= moduleIndex. 2169 MOZ_ASSERT(module->dfsAncestorIndex() <= moduleIndex); 2170 2171 // Step 16. If module.[[DFSAncestorIndex]] = momoduleIndex, then: 2172 if (module->dfsAncestorIndex() == moduleIndex) { 2173 // Step 16.a. Let done be false. 2174 bool done = false; 2175 2176 // Step 16.b. Repeat, while done is false: 2177 while (!done) { 2178 // Step 16.b.i. Let requiredModule be the last element in stack. 2179 // Step 16.b.ii. Remove the last element of stack. 2180 requiredModule = stack.popCopy(); 2181 2182 // Step 16.b.iii. Assert: requiredModule.[[AsyncEvaluationOrder]] is 2183 // either an integer or unset 2184 MOZ_ASSERT(requiredModule->asyncEvaluationOrder().isInteger() || 2185 requiredModule->asyncEvaluationOrder().isUnset()); 2186 2187 // Step 16.b.v. If requiredModule.[[AsyncEvaluationOrder]] is unset, set 2188 // requiredModule.[[Status]] to evaluated. 2189 if (requiredModule->asyncEvaluationOrder().isUnset()) { 2190 requiredModule->setStatus(ModuleStatus::Evaluated); 2191 } else { 2192 // Step 16.b.vi. Otherwise, set requiredModule.[[Status]] to 2193 // evaluating-async. 2194 requiredModule->setStatus(ModuleStatus::EvaluatingAsync); 2195 } 2196 2197 // Step 16.b.vii. If requiredModule and module are the same Module Record, 2198 // set done to true. 2199 done = requiredModule == module; 2200 2201 // Step 16.b.viii. Set requiredModule.[[CycleRoot]] to module. 2202 requiredModule->setCycleRoot(module); 2203 } 2204 } 2205 2206 // Step 17. Return index. 2207 *indexOut = index; 2208 return true; 2209 } 2210 2211 // https://tc39.es/ecma262/#sec-execute-async-module 2212 // ES2023 16.2.1.5.2.2 ExecuteAsyncModule 2213 static bool ExecuteAsyncModule(JSContext* cx, Handle<ModuleObject*> module) { 2214 // Step 1. Assert: module.[[Status]] is evaluating or evaluating-async. 2215 MOZ_ASSERT(module->status() == ModuleStatus::Evaluating || 2216 module->status() == ModuleStatus::EvaluatingAsync); 2217 2218 // Step 2. Assert: module.[[HasTLA]] is true. 2219 MOZ_ASSERT(module->hasTopLevelAwait()); 2220 2221 // Steps 3 - 8 are performed by the AsyncAwait opcode. 2222 2223 // Step 9. Perform ! module.ExecuteModule(capability). 2224 // Step 10. Return unused. 2225 return ModuleObject::execute(cx, module); 2226 } 2227 2228 // https://tc39.es/ecma262/#sec-gather-available-ancestors 2229 // ES2023 16.2.1.5.2.3 GatherAvailableAncestors 2230 static bool GatherAvailableModuleAncestors( 2231 JSContext* cx, Handle<ModuleObject*> module, 2232 MutableHandle<ModuleVector> execList) { 2233 MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync); 2234 2235 // Step 1. For each Cyclic Module Record m of module.[[AsyncParentModules]], 2236 // do: 2237 Rooted<ListObject*> asyncParentModules(cx, module->asyncParentModules()); 2238 Rooted<ModuleObject*> m(cx); 2239 for (uint32_t i = 0; i != asyncParentModules->length(); i++) { 2240 m = &asyncParentModules->getDenseElement(i).toObject().as<ModuleObject>(); 2241 2242 // Step 1.a. If execList does not contain m and 2243 // m.[[CycleRoot]].[[EvaluationError]] is empty, then: 2244 // 2245 // Note: we also check whether m.[[EvaluationError]] is empty since an error 2246 // in synchronous execution can prevent the CycleRoot field from being set. 2247 if (!m->hadEvaluationError() && !m->getCycleRoot()->hadEvaluationError() && 2248 !ContainsElement(execList, m)) { 2249 // Step 1.a.i. Assert: m.[[Status]] is evaluating-async. 2250 MOZ_ASSERT(m->status() == ModuleStatus::EvaluatingAsync); 2251 2252 // Step 1.a.ii. Assert: m.[[EvaluationError]] is empty. 2253 MOZ_ASSERT(!m->hadEvaluationError()); 2254 2255 // Step 1.a.iii. Assert: m.[[AsyncEvaluationOrder]] is an integer. 2256 MOZ_ASSERT(m->asyncEvaluationOrder().isInteger()); 2257 2258 // Step 1.a.iv. Assert: m.[[PendingAsyncDependencies]] > 0. 2259 MOZ_ASSERT(m->pendingAsyncDependencies() > 0); 2260 2261 // Step 1.a.v. Set m.[[PendingAsyncDependencies]] to 2262 // m.[[PendingAsyncDependencies]] - 1. 2263 m->setPendingAsyncDependencies(m->pendingAsyncDependencies() - 1); 2264 2265 // Step 1.a.vi. If m.[[PendingAsyncDependencies]] = 0, then: 2266 if (m->pendingAsyncDependencies() == 0) { 2267 // Step 1.a.vi.1. Append m to execList. 2268 if (!execList.append(m)) { 2269 return false; 2270 } 2271 2272 // Step 1.a.vi.2. If m.[[HasTLA]] is false, perform 2273 // GatherAvailableAncestors(m, execList). 2274 if (!m->hasTopLevelAwait() && 2275 !GatherAvailableModuleAncestors(cx, m, execList)) { 2276 return false; 2277 } 2278 } 2279 } 2280 } 2281 2282 // Step 2. Return unused. 2283 return true; 2284 } 2285 2286 struct EvalOrderComparator { 2287 bool operator()(ModuleObject* a, ModuleObject* b, bool* lessOrEqualp) { 2288 int32_t result = int32_t(a->asyncEvaluationOrder().get()) - 2289 int32_t(b->asyncEvaluationOrder().get()); 2290 *lessOrEqualp = (result <= 0); 2291 return true; 2292 } 2293 }; 2294 2295 static void RejectExecutionWithPendingException(JSContext* cx, 2296 Handle<ModuleObject*> module) { 2297 // If there is no exception pending then we have been interrupted or have 2298 // OOM'd and all bets are off. We reject the execution by throwing 2299 // undefined. Not much more we can do. 2300 RootedValue exception(cx); 2301 if (cx->isExceptionPending()) { 2302 (void)cx->getPendingException(&exception); 2303 } 2304 cx->clearPendingException(); 2305 AsyncModuleExecutionRejected(cx, module, exception); 2306 } 2307 2308 // https://tc39.es/ecma262/#sec-async-module-execution-fulfilled 2309 // ES2023 16.2.1.5.2.4 AsyncModuleExecutionFulfilled 2310 void js::AsyncModuleExecutionFulfilled(JSContext* cx, 2311 Handle<ModuleObject*> module) { 2312 // Step 1. If module.[[Status]] is evaluated, then: 2313 if (module->status() == ModuleStatus::Evaluated) { 2314 // Step 1.a. Assert: module.[[EvaluationError]] is not empty. 2315 MOZ_ASSERT(module->hadEvaluationError()); 2316 2317 // Step 1.b. Return unused. 2318 return; 2319 } 2320 2321 // Step 2. Assert: module.[[Status]] is evaluating-async. 2322 MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync); 2323 2324 // Step 3. Assert: module.[[AsyncEvaluationOrder]] is an integer. 2325 MOZ_ASSERT(module->asyncEvaluationOrder().isInteger()); 2326 2327 // Step 4. Assert: module.[[EvaluationError]] is empty. 2328 MOZ_ASSERT(!module->hadEvaluationError()); 2329 2330 // The following steps are performed in a different order from the 2331 // spec. Gather available module ancestors before mutating the module object 2332 // as this can fail in our implementation. 2333 2334 // Step 8. Let execList be a new empty List. 2335 Rooted<ModuleVector> execList(cx); 2336 2337 // Step 9. Perform GatherAvailableAncestors(module, execList). 2338 if (!GatherAvailableModuleAncestors(cx, module, &execList)) { 2339 RejectExecutionWithPendingException(cx, module); 2340 return; 2341 } 2342 2343 // Step 10. Let sortedExecList be a List whose elements are the elements of 2344 // execList, in the order in which they had their [[AsyncEvaluation]] 2345 // fields set to true in InnerModuleEvaluation. 2346 2347 Rooted<ModuleVector> scratch(cx); 2348 if (!scratch.resize(execList.length())) { 2349 ReportOutOfMemory(cx); 2350 RejectExecutionWithPendingException(cx, module); 2351 return; 2352 } 2353 2354 MOZ_ALWAYS_TRUE(MergeSort(execList.begin(), execList.length(), 2355 scratch.begin(), EvalOrderComparator())); 2356 2357 // Step 11. Assert: All elements of sortedExecList have their 2358 // [[AsyncEvaluationOrder]] field set to an integer, 2359 // [[PendingAsyncDependencies]] field set to 0, and 2360 // [[EvaluationError]] field set to empty. 2361 #ifdef DEBUG 2362 for (ModuleObject* m : execList) { 2363 MOZ_ASSERT(m->asyncEvaluationOrder().isInteger()); 2364 MOZ_ASSERT(m->pendingAsyncDependencies() == 0); 2365 MOZ_ASSERT(!m->hadEvaluationError()); 2366 } 2367 #endif 2368 2369 // Return to original order of steps. 2370 2371 ModuleObject::onTopLevelEvaluationFinished(module); 2372 2373 // Step 5. Set module.[[AsyncEvaluationOrder]] to done. 2374 module->asyncEvaluationOrder().setDone(cx->runtime()); 2375 2376 // Step 6. Set module.[[Status]] to evaluated. 2377 module->setStatus(ModuleStatus::Evaluated); 2378 2379 // Step 7. If module.[[TopLevelCapability]] is not empty, then: 2380 if (module->hasTopLevelCapability()) { 2381 // Step 7.a. Assert: module.[[CycleRoot]] is module. 2382 MOZ_ASSERT(module->getCycleRoot() == module); 2383 2384 // Step 7.b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]], 2385 // undefined, undefined). 2386 if (!ModuleObject::topLevelCapabilityResolve(cx, module)) { 2387 // If Resolve fails, there's nothing more we can do here. 2388 cx->clearPendingException(); 2389 } 2390 } 2391 2392 // Step 12. For each Cyclic Module Record m of sortedExecList, do: 2393 Rooted<ModuleObject*> m(cx); 2394 for (ModuleObject* obj : execList) { 2395 m = obj; 2396 2397 // Step 12.a. If m.[[Status]] is evaluated, then: 2398 if (m->status() == ModuleStatus::Evaluated) { 2399 // Step 12.a.i. Assert: m.[[EvaluationError]] is not empty. 2400 MOZ_ASSERT(m->hadEvaluationError()); 2401 } else if (m->hasTopLevelAwait()) { 2402 // Step 12.b. Else if m.[[HasTLA]] is true, then: 2403 // Step 12.b.i. Perform ExecuteAsyncModule(m). 2404 if (!ExecuteAsyncModule(cx, m)) { 2405 MOZ_ASSERT(cx->isThrowingOutOfMemory() || cx->isThrowingOverRecursed()); 2406 cx->clearPendingException(); 2407 } 2408 } else { 2409 // Step 12.c. Else: 2410 // Step 12.c.i. Let result be m.ExecuteModule(). 2411 bool ok = ModuleObject::execute(cx, m); 2412 2413 // Step 12.c.ii. If result is an abrupt completion, then: 2414 if (!ok) { 2415 // Step 12.c.ii.1. Perform AsyncModuleExecutionRejected(m, 2416 // result.[[Value]]). 2417 RejectExecutionWithPendingException(cx, m); 2418 } else { 2419 // Step 12.c.iii. Else: 2420 // Step 12.c.iii.1. Set m.[[AsyncEvaluationOrder]] to done. 2421 m->asyncEvaluationOrder().setDone(m->zone()->runtimeFromMainThread()); 2422 // Step 12.c.iii.2. Set m.[[Status]] to evaluated. 2423 m->setStatus(ModuleStatus::Evaluated); 2424 2425 // Step 12.c.iii.2. If m.[[TopLevelCapability]] is not empty, then: 2426 if (m->hasTopLevelCapability()) { 2427 // Step 12.c.iii.2.a. Assert: m.[[CycleRoot]] is m. 2428 MOZ_ASSERT(m->getCycleRoot() == m); 2429 2430 // Step 12.c.iii.2.b. Perform ! 2431 // Call(m.[[TopLevelCapability]].[[Resolve]], 2432 // undefined, undefined). 2433 if (!ModuleObject::topLevelCapabilityResolve(cx, m)) { 2434 // If Resolve fails, there's nothing more we can do here. 2435 cx->clearPendingException(); 2436 } 2437 } 2438 } 2439 } 2440 } 2441 2442 // Step 13. Return unused. 2443 } 2444 2445 // https://tc39.es/ecma262/#sec-async-module-execution-rejected 2446 // ES2023 16.2.1.5.2.5 AsyncModuleExecutionRejected 2447 void js::AsyncModuleExecutionRejected(JSContext* cx, 2448 Handle<ModuleObject*> module, 2449 HandleValue error) { 2450 // Step 1. If module.[[Status]] is evaluated, then: 2451 if (module->status() == ModuleStatus::Evaluated) { 2452 // Step 1.a. Assert: module.[[EvaluationError]] is not empty 2453 MOZ_ASSERT(module->hadEvaluationError()); 2454 2455 // Step 1.b. Return unused. 2456 return; 2457 } 2458 2459 // Step 2. Assert: module.[[Status]] is evaluating-async. 2460 MOZ_ASSERT(module->status() == ModuleStatus::EvaluatingAsync); 2461 2462 // Step 3. Assert: module.[[AsyncEvaluationOrder]] is an integer. 2463 MOZ_ASSERT(module->asyncEvaluationOrder().isInteger()); 2464 2465 // Step 4. Assert: module.[[EvaluationError]] is empty. 2466 MOZ_ASSERT(!module->hadEvaluationError()); 2467 2468 ModuleObject::onTopLevelEvaluationFinished(module); 2469 2470 // Step 5. Set module.[[EvaluationError]] to ThrowCompletion(error). 2471 module->setEvaluationError(error); 2472 2473 // Step 6. Set module.[[Status]] to evaluated. 2474 MOZ_ASSERT(module->status() == ModuleStatus::Evaluated); 2475 2476 // Step 7. Set module.[[AsyncEvaluationOrder]] to done. 2477 module->asyncEvaluationOrder().setDone(cx->runtime()); 2478 2479 // Step 9. If module.[[TopLevelCapability]] is not empty, then: 2480 if (module->hasTopLevelCapability()) { 2481 // Step 9.a. Assert: module.[[CycleRoot]] is module. 2482 MOZ_ASSERT(module->getCycleRoot() == module); 2483 2484 // Step 9.b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]], 2485 // undefined, error). 2486 if (!ModuleObject::topLevelCapabilityReject(cx, module, error)) { 2487 // If Reject fails, there's nothing more we can do here. 2488 cx->clearPendingException(); 2489 } 2490 } 2491 2492 // Step 10. For each Cyclic Module Record m of module.[[AsyncParentModules]], 2493 // do: 2494 Rooted<ListObject*> parents(cx, module->asyncParentModules()); 2495 Rooted<ModuleObject*> parent(cx); 2496 for (uint32_t i = 0; i < parents->length(); i++) { 2497 parent = &parents->get(i).toObject().as<ModuleObject>(); 2498 2499 // Step 10.a. Perform AsyncModuleExecutionRejected(m, error). 2500 AsyncModuleExecutionRejected(cx, parent, error); 2501 } 2502 2503 // Step 11. Return unused. 2504 } 2505 2506 // https://tc39.es/proposal-import-attributes/#sec-evaluate-import-call 2507 // NOTE: The caller needs to handle the promise. 2508 static bool EvaluateDynamicImportOptions( 2509 JSContext* cx, HandleValue optionsArg, 2510 MutableHandle<ImportAttributeVector> attributesArrayArg) { 2511 // Step 11. If options is not undefined, then 2512 if (optionsArg.isUndefined()) { 2513 return true; 2514 } 2515 2516 // Step 11.a. If options is not an Object, then 2517 if (!optionsArg.isObject()) { 2518 JS_ReportErrorNumberASCII( 2519 cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "import", 2520 "object or undefined", InformalValueTypeName(optionsArg)); 2521 return false; 2522 } 2523 2524 RootedObject attributesWrapperObject(cx, &optionsArg.toObject()); 2525 RootedValue attributesValue(cx); 2526 2527 // Step 11.b. Let attributesObj be Completion(Get(options, "with")). 2528 RootedId withId(cx, NameToId(cx->names().with)); 2529 if (!GetProperty(cx, attributesWrapperObject, attributesWrapperObject, withId, 2530 &attributesValue)) { 2531 return false; 2532 } 2533 2534 // Step 11.e. If attributesObj is not undefined, then 2535 if (attributesValue.isUndefined()) { 2536 return true; 2537 } 2538 2539 // Step 11.e.i. If attributesObj is not an Object, then 2540 if (!attributesValue.isObject()) { 2541 JS_ReportErrorNumberASCII( 2542 cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE, "import", 2543 "object or undefined", InformalValueTypeName(attributesValue)); 2544 return false; 2545 } 2546 2547 // Step 11.e.ii. Let entries be 2548 // Completion(EnumerableOwnProperties(attributesObj, key+value)). 2549 RootedObject attributesObject(cx, &attributesValue.toObject()); 2550 RootedIdVector attributes(cx); 2551 if (!GetPropertyKeys(cx, attributesObject, JSITER_OWNONLY, &attributes)) { 2552 return false; 2553 } 2554 2555 uint32_t numberOfAttributes = attributes.length(); 2556 if (numberOfAttributes == 0) { 2557 return true; 2558 } 2559 2560 // Step 10 (reordered). Let attributes be a new empty List. 2561 if (!attributesArrayArg.reserve(numberOfAttributes)) { 2562 ReportOutOfMemory(cx); 2563 return false; 2564 } 2565 2566 size_t numberOfValidAttributes = 0; 2567 2568 // Step 11.e.iv. For each element entry of entries, do 2569 RootedId key(cx); 2570 RootedValue value(cx); 2571 Rooted<JSAtom*> keyAtom(cx); 2572 Rooted<JSString*> valueString(cx); 2573 for (size_t i = 0; i < numberOfAttributes; i++) { 2574 // Step 11.e.ii.iv.1. Let key be ! Get(entry, "0"). 2575 key = attributes[i]; 2576 2577 // Step 11.e.ii.iv.2. Let value be ! Get(entry, "1"). 2578 if (!GetProperty(cx, attributesObject, attributesObject, key, &value)) { 2579 return false; 2580 } 2581 2582 // Step 11.e.ii.iv.3. If key is a String, then 2583 if (key.isString()) { 2584 // Step 11.f (reordered). If AllImportAttributesSupported(attributes) is 2585 // false, then 2586 // 2587 // Note: This should be driven by a host hook 2588 // (HostGetSupportedImportAttributes), however the infrastructure of said 2589 // host hook is deeply unclear, and so right now embedders will not have 2590 // the ability to alter or extend the set of supported attributes. 2591 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1840723. 2592 bool supported = key.isAtom(cx->names().type); 2593 if (!supported) { 2594 UniqueChars printableKey = AtomToPrintableString(cx, key.toAtom()); 2595 if (!printableKey) { 2596 return false; 2597 } 2598 JS_ReportErrorNumberASCII( 2599 cx, GetErrorMessage, nullptr, 2600 JSMSG_IMPORT_ATTRIBUTES_DYNAMIC_IMPORT_UNSUPPORTED_ATTRIBUTE, 2601 printableKey.get()); 2602 return false; 2603 } 2604 2605 // Step 10.d.v.3.a. If value is not a String, then 2606 if (!value.isString()) { 2607 JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, 2608 JSMSG_NOT_EXPECTED_TYPE, "import", "string", 2609 InformalValueTypeName(value)); 2610 return false; 2611 } 2612 2613 // Step 10.d.v.3.b. Append the ImportAttribute Record { [[Key]]: key, 2614 // [[Value]]: value } to attributes. 2615 keyAtom = key.toAtom(); 2616 valueString = value.toString(); 2617 attributesArrayArg.infallibleEmplaceBack(keyAtom, valueString); 2618 ++numberOfValidAttributes; 2619 } 2620 } 2621 2622 if (numberOfValidAttributes == 0) { 2623 return true; 2624 } 2625 2626 // Step 10.g (skipped). Sort attributes according to the lexicographic order 2627 // of their [[Key]] fields, treating the value of each such field as a 2628 // sequence of UTF-16 code unit values. 2629 // 2630 // We only support "type", so we can ignore this. 2631 2632 return true; 2633 } 2634 2635 // https://tc39.es/ecma262/#sec-evaluate-import-call 2636 // 2637 // ShadowRealmImportValue duplicates some of this, so be sure to keep these in 2638 // sync. 2639 JSObject* js::StartDynamicModuleImport(JSContext* cx, HandleScript script, 2640 HandleValue specifierArg, 2641 HandleValue optionsArg) { 2642 // Step 7. Let promiseCapability be ! NewPromiseCapability(%Promise%). 2643 RootedObject promise(cx, JS::NewPromiseObject(cx, nullptr)); 2644 if (!promise) { 2645 return nullptr; 2646 } 2647 2648 if (!TryStartDynamicModuleImport(cx, script, specifierArg, optionsArg, 2649 promise)) { 2650 if (!RejectPromiseWithPendingError(cx, promise.as<PromiseObject>())) { 2651 return nullptr; 2652 } 2653 } 2654 2655 return promise; 2656 } 2657 2658 // https://tc39.es/ecma262/#sec-evaluate-import-call continued. 2659 static bool TryStartDynamicModuleImport(JSContext* cx, HandleScript script, 2660 HandleValue specifierArg, 2661 HandleValue optionsArg, 2662 HandleObject promise) { 2663 RootedString specifier(cx, ToString(cx, specifierArg)); 2664 if (!specifier) { 2665 return false; 2666 } 2667 2668 Rooted<JSAtom*> specifierAtom(cx, AtomizeString(cx, specifier)); 2669 if (!specifierAtom) { 2670 return false; 2671 } 2672 2673 Rooted<ImportAttributeVector> attributes(cx); 2674 if (!EvaluateDynamicImportOptions(cx, optionsArg, &attributes)) { 2675 return false; 2676 } 2677 2678 // Step 12. Let moduleRequest be a new ModuleRequest Record { [[Specifier]]: 2679 // specifierString, [[Attributes]]: attributes }. 2680 RootedObject moduleRequest( 2681 cx, ModuleRequestObject::create(cx, specifierAtom, attributes, 2682 ImportPhase::Evaluation)); 2683 if (!moduleRequest) { 2684 return false; 2685 } 2686 2687 // Step 13. Perform HostLoadImportedModule(referrer, moduleRequest, empty, 2688 // promiseCapability). 2689 RootedValue payload(cx, ObjectValue(*promise)); 2690 (void)HostLoadImportedModule(cx, script, moduleRequest, 2691 JS::UndefinedHandleValue, payload); 2692 2693 return true; 2694 } 2695 2696 static bool OnRootModuleRejected(JSContext* cx, unsigned argc, Value* vp) { 2697 CallArgs args = CallArgsFromVp(argc, vp); 2698 HandleValue error = args.get(0); 2699 2700 ReportExceptionClosure reportExn(error); 2701 PrepareScriptEnvironmentAndInvoke(cx, cx->global(), reportExn); 2702 2703 args.rval().setUndefined(); 2704 return true; 2705 }; 2706 2707 bool js::OnModuleEvaluationFailure(JSContext* cx, 2708 HandleObject evaluationPromise, 2709 JS::ModuleErrorBehaviour errorBehaviour) { 2710 if (evaluationPromise == nullptr) { 2711 return false; 2712 } 2713 2714 // To allow module evaluation to happen synchronously throw the error 2715 // immediately. This assumes that any error will already have caused the 2716 // promise to be rejected, and doesn't support top-level await. 2717 if (errorBehaviour == JS::ThrowModuleErrorsSync) { 2718 JS::PromiseState state = JS::GetPromiseState(evaluationPromise); 2719 MOZ_DIAGNOSTIC_ASSERT(state == JS::PromiseState::Rejected || 2720 state == JS::PromiseState::Fulfilled); 2721 2722 JS::SetSettledPromiseIsHandled(cx, evaluationPromise); 2723 if (state == JS::PromiseState::Fulfilled) { 2724 return true; 2725 } 2726 2727 RootedValue error(cx, JS::GetPromiseResult(evaluationPromise)); 2728 JS_SetPendingException(cx, error); 2729 return false; 2730 } 2731 2732 RootedFunction onRejected( 2733 cx, NewHandler(cx, OnRootModuleRejected, evaluationPromise)); 2734 if (!onRejected) { 2735 return false; 2736 } 2737 2738 return JS::AddPromiseReactions(cx, evaluationPromise, nullptr, onRejected); 2739 } 2740 2741 // This is used for |fulfilledClosure| and |rejectedClosure| in 2742 // https://tc39.es/ecma262/#sec-ContinueDynamicImport 2743 // 2744 // It is used to marshal some arguments and pass them through to the promise 2745 // resolve and reject callbacks. It holds a reference to the referencing private 2746 // to keep it alive until it is needed. 2747 // 2748 // TODO: The |referrer| field is used to keep the importing script alive while 2749 // the import operation is happening. It is possible that this is no longer 2750 // required. 2751 class DynamicImportContextObject : public NativeObject { 2752 public: 2753 enum { ReferrerSlot = 0, PromiseSlot, ModuleSlot, PhaseSlot, SlotCount }; 2754 2755 static const JSClass class_; 2756 2757 [[nodiscard]] static DynamicImportContextObject* create( 2758 JSContext* cx, Handle<JSScript*> referrer, Handle<PromiseObject*> promise, 2759 Handle<ModuleObject*> module, ImportPhase phase); 2760 2761 JSScript* referrer() const; 2762 PromiseObject* promise() const; 2763 ModuleObject* module() const; 2764 ImportPhase phase() const; 2765 2766 static void finalize(JS::GCContext* gcx, JSObject* obj); 2767 }; 2768 2769 /* static */ 2770 const JSClass DynamicImportContextObject::class_ = { 2771 "DynamicImportContextObject", 2772 JSCLASS_HAS_RESERVED_SLOTS(DynamicImportContextObject::SlotCount)}; 2773 2774 /* static */ 2775 DynamicImportContextObject* DynamicImportContextObject::create( 2776 JSContext* cx, Handle<JSScript*> referrer, Handle<PromiseObject*> promise, 2777 Handle<ModuleObject*> module, ImportPhase phase) { 2778 Rooted<DynamicImportContextObject*> self( 2779 cx, NewObjectWithGivenProto<DynamicImportContextObject>(cx, nullptr)); 2780 if (!self) { 2781 return nullptr; 2782 } 2783 2784 if (referrer) { 2785 self->initReservedSlot(ReferrerSlot, PrivateGCThingValue(referrer)); 2786 } 2787 self->initReservedSlot(PromiseSlot, ObjectValue(*promise)); 2788 self->initReservedSlot(ModuleSlot, ObjectValue(*module)); 2789 self->initReservedSlot(PhaseSlot, Int32Value(int32_t(phase))); 2790 return self; 2791 } 2792 2793 JSScript* DynamicImportContextObject::referrer() const { 2794 Value value = getReservedSlot(ReferrerSlot); 2795 if (value.isUndefined()) { 2796 return nullptr; 2797 } 2798 2799 return static_cast<JSScript*>(value.toGCThing()); 2800 } 2801 2802 PromiseObject* DynamicImportContextObject::promise() const { 2803 Value value = getReservedSlot(PromiseSlot); 2804 if (value.isUndefined()) { 2805 return nullptr; 2806 } 2807 2808 return &value.toObject().as<PromiseObject>(); 2809 } 2810 2811 ModuleObject* DynamicImportContextObject::module() const { 2812 Value value = getReservedSlot(ModuleSlot); 2813 if (value.isUndefined()) { 2814 return nullptr; 2815 } 2816 2817 return &value.toObject().as<ModuleObject>(); 2818 } 2819 2820 ImportPhase DynamicImportContextObject::phase() const { 2821 Value value = getReservedSlot(PhaseSlot); 2822 if (value.isUndefined()) { 2823 return ImportPhase::Limit; 2824 } 2825 2826 return static_cast<ImportPhase>(value.toInt32()); 2827 } 2828 2829 // https://tc39.es/ecma262/#sec-ContinueDynamicImport 2830 /* static */ 2831 bool ContinueDynamicImport(JSContext* cx, Handle<JSScript*> referrer, 2832 Handle<PromiseObject*> promiseCapability, 2833 Handle<ModuleObject*> module, ImportPhase phase, 2834 bool usePromise) { 2835 MOZ_ASSERT(module); 2836 2837 // Step 1, 2: Already handled in FinishLoadingImportedModuleFailed functions. 2838 2839 // Step 6. Let linkAndEvaluateClosure be a new Abstract Closure with no 2840 // parameters that captures module, promiseCapability, and onRejected... 2841 Rooted<DynamicImportContextObject*> context( 2842 cx, DynamicImportContextObject::create(cx, referrer, promiseCapability, 2843 module, phase)); 2844 if (!context) { 2845 return RejectPromiseWithPendingError(cx, promiseCapability); 2846 } 2847 2848 // Our implementation provides an option for synchronous completion for 2849 // environments where we can't use promises. 2850 if (!usePromise) { 2851 return LinkAndEvaluateDynamicImport(cx, context); 2852 } 2853 2854 // Step 3: The module dependencies has been loaded in the host layer, so we 2855 // only need to do _linkAndEvaluate_ part defined in the spec. Create a 2856 // promise that we'll resolve immediately. 2857 JS::Rooted<PromiseObject*> loadPromise(cx, CreatePromiseObjectForAsync(cx)); 2858 if (!loadPromise) { 2859 return RejectPromiseWithPendingError(cx, promiseCapability); 2860 } 2861 2862 // Step 7. Let linkAndEvaluate be 2863 // CreateBuiltinFunction(linkAndEvaluateClosure, 0, "", []). 2864 Rooted<JSFunction*> linkAndEvaluate(cx); 2865 linkAndEvaluate = js::NewFunctionWithReserved( 2866 cx, LinkAndEvaluateDynamicImport, 0, 0, "resolved"); 2867 if (!linkAndEvaluate) { 2868 return RejectPromiseWithPendingError(cx, promiseCapability); 2869 } 2870 2871 // Step 8. Perform PerformPromiseThen(loadPromise, linkAndEvaluate, 2872 // onRejected). 2873 js::SetFunctionNativeReserved(linkAndEvaluate, 0, ObjectValue(*context)); 2874 JS::AddPromiseReactions(cx, loadPromise, linkAndEvaluate, nullptr); 2875 return AsyncFunctionReturned(cx, loadPromise, UndefinedHandleValue); 2876 } 2877 2878 // static 2879 bool LinkAndEvaluateDynamicImport(JSContext* cx, unsigned argc, Value* vp) { 2880 CallArgs args = CallArgsFromVp(argc, vp); 2881 Value value = js::GetFunctionNativeReserved(&args.callee(), 0); 2882 Rooted<DynamicImportContextObject*> context(cx); 2883 context = &value.toObject().as<DynamicImportContextObject>(); 2884 return LinkAndEvaluateDynamicImport(cx, context); 2885 } 2886 2887 // https://tc39.es/ecma262/#sec-ContinueDynamicImport 2888 static bool LinkAndEvaluateDynamicImport( 2889 JSContext* cx, Handle<DynamicImportContextObject*> context) { 2890 MOZ_ASSERT(context); 2891 Rooted<ModuleObject*> module(cx, context->module()); 2892 Rooted<PromiseObject*> promise(cx, context->promise()); 2893 2894 // Step 6.a. Let link be Completion(module.Link()). 2895 if (!JS::ModuleLink(cx, module)) { 2896 // b. If link is an abrupt completion, then 2897 // i. Perform ! Call(promiseCapability.[[Reject]], undefined, [ 2898 // link.[[Value]] ]). 2899 // ii. Return unused. 2900 return RejectPromiseWithPendingError(cx, promise); 2901 } 2902 MOZ_ASSERT(!JS_IsExceptionPending(cx)); 2903 2904 // TODO: Bug 1952263: Implement Defer Imports Evaluation. 2905 MOZ_ASSERT(context->phase() == ImportPhase::Evaluation); 2906 2907 // Step 6.c. Let evaluatePromise be module.Evaluate(). 2908 JS::Rooted<JS::Value> rval(cx); 2909 mozilla::DebugOnly<bool> ok = JS::ModuleEvaluate(cx, module, &rval); 2910 MOZ_ASSERT_IF(ok, !JS_IsExceptionPending(cx)); 2911 if (!rval.isObject()) { 2912 // If we do not have an evaluation promise or a module request for the 2913 // module, we can assume that evaluation has failed or been interrupted and 2914 // can reject the dynamic module. 2915 return RejectPromiseWithPendingError(cx, promise); 2916 } 2917 2918 JS::Rooted<JSObject*> evaluatePromise(cx, &rval.toObject()); 2919 MOZ_ASSERT(evaluatePromise->is<PromiseObject>()); 2920 2921 // Step 6.e. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 0, "", 2922 // []). 2923 RootedValue contextValue(cx, ObjectValue(*context)); 2924 RootedFunction onFulfilled(cx); 2925 onFulfilled = NewHandlerWithExtraValue(cx, DynamicImportResolved, promise, 2926 contextValue); 2927 if (!onFulfilled) { 2928 return false; 2929 } 2930 2931 // Step 5. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", 2932 // []). 2933 RootedFunction onRejected(cx); 2934 onRejected = NewHandlerWithExtraValue(cx, DynamicImportRejected, promise, 2935 contextValue); 2936 if (!onRejected) { 2937 return false; 2938 } 2939 2940 // Step 6.f. Perform PerformPromiseThen(evaluatePromise, onFulfilled, 2941 // onRejected). 2942 // Step 6.g. Return unused. 2943 return JS::AddPromiseReactionsIgnoringUnhandledRejection( 2944 cx, evaluatePromise, onFulfilled, onRejected); 2945 } 2946 2947 // This performs the steps for |fulfilledClosure| from 2948 // https://tc39.es/ecma262/#sec-ContinueDynamicImport step 6.d. 2949 // 2950 // With adjustment for Top-level await: 2951 // https://GitHub.com/tc39/proposal-dynamic-import/pull/71/files 2952 static bool DynamicImportResolved(JSContext* cx, unsigned argc, Value* vp) { 2953 CallArgs args = CallArgsFromVp(argc, vp); 2954 MOZ_ASSERT(args.get(0).isUndefined()); 2955 2956 Rooted<DynamicImportContextObject*> context( 2957 cx, ExtraFromHandler<DynamicImportContextObject>(args)); 2958 2959 Rooted<PromiseObject*> promise(cx, TargetFromHandler<PromiseObject>(args)); 2960 2961 Rooted<ModuleObject*> module(cx, context->module()); 2962 if (module->status() != ModuleStatus::EvaluatingAsync && 2963 module->status() != ModuleStatus::Evaluated) { 2964 JS_ReportErrorASCII( 2965 cx, "Unevaluated or errored module returned by module resolve hook"); 2966 return RejectPromiseWithPendingError(cx, promise); 2967 } 2968 2969 // This is called when |evaluationPromise| is resolved, step 6.f. 2970 MOZ_ASSERT_IF(module->hasCyclicModuleFields(), 2971 module->getCycleRoot() 2972 ->topLevelCapability() 2973 ->as<PromiseObject>() 2974 .state() == JS::PromiseState::Fulfilled); 2975 2976 // Step 6.d.i. Let namespace be GetModuleNamespace(module). 2977 RootedObject ns(cx, GetOrCreateModuleNamespace(cx, module)); 2978 if (!ns) { 2979 return RejectPromiseWithPendingError(cx, promise); 2980 } 2981 2982 // Step 6.d.ii. Perform ! Call(promiseCapability.[[Resolve]], undefined, [ 2983 // namespace ]). 2984 RootedValue value(cx, ObjectValue(*ns)); 2985 if (!PromiseObject::resolve(cx, promise, value)) { 2986 return false; 2987 } 2988 2989 // Step 6.d.iii. Return NormalCompletion(undefined). 2990 args.rval().setUndefined(); 2991 return true; 2992 }; 2993 2994 // This performs the steps for |rejectedClosure| from 2995 // https://tc39.es/ecma262/#sec-ContinueDynamicImport step 4. 2996 static bool DynamicImportRejected(JSContext* cx, unsigned argc, Value* vp) { 2997 CallArgs args = CallArgsFromVp(argc, vp); 2998 HandleValue error = args.get(0); 2999 3000 Rooted<DynamicImportContextObject*> context( 3001 cx, ExtraFromHandler<DynamicImportContextObject>(args)); 3002 3003 Rooted<PromiseObject*> promise(cx, TargetFromHandler<PromiseObject>(args)); 3004 3005 // Step 4.a. Perform ! Call(promiseCapability.[[Reject]], undefined, [ reason 3006 // ]). 3007 if (!PromiseObject::reject(cx, promise, error)) { 3008 return false; 3009 } 3010 3011 // Step 4.b. Return NormalCompletion(undefined). 3012 args.rval().setUndefined(); 3013 return true; 3014 }