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/. */
7 /* JavaScript modules (as in, the syntactic construct) implementation. */
9 #include "vm/Modules.h"
11 #include "mozilla/Assertions.h" // MOZ_ASSERT
12 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
14 #include <stdint.h> // uint32_t
16 #include "jstypes.h" // JS_PUBLIC_API
18 #include "builtin/ModuleObject.h" // js::FinishDynamicModuleImport, js::{,Requested}ModuleObject
20 #include "frontend/BytecodeCompiler.h" // js::frontend::CompileModule
21 #include "frontend/FrontendContext.h" // js::AutoReportFrontendContext
22 #include "js/ColumnNumber.h" // JS::ColumnNumberZeroOrigin, JS::ColumnNumberOneOrigin
23 #include "js/Context.h" // js::AssertHeapIsIdle
24 #include "js/ErrorReport.h" // JSErrorBase
25 #include "js/RootingAPI.h" // JS::MutableHandle
26 #include "js/Value.h" // JS::Value
27 #include "vm/EnvironmentObject.h" // js::ModuleEnvironmentObject
28 #include "vm/JSAtomUtils.h" // AtomizeString
29 #include "vm/JSContext.h" // CHECK_THREAD, JSContext
30 #include "vm/JSObject.h" // JSObject
31 #include "vm/List.h" // ListObject
32 #include "vm/Runtime.h" // JSRuntime
34 #include "vm/JSAtomUtils-inl.h" // AtomToId
35 #include "vm/JSContext-inl.h" // JSContext::{c,releaseC}heck
39 using mozilla::Utf8Unit
;
41 ////////////////////////////////////////////////////////////////////////////////
44 JS_PUBLIC_API
void JS::SetSupportedImportAssertions(
45 JSRuntime
* rt
, const ImportAssertionVector
& assertions
) {
47 MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt
));
48 MOZ_ASSERT(rt
->supportedImportAssertions
.ref().empty());
50 AutoEnterOOMUnsafeRegion oomUnsafe
;
51 if (!rt
->supportedImportAssertions
.ref().appendAll(assertions
)) {
52 oomUnsafe
.crash("SetSupportedImportAssertions");
56 JS_PUBLIC_API
JS::ModuleResolveHook
JS::GetModuleResolveHook(JSRuntime
* rt
) {
59 return rt
->moduleResolveHook
;
62 JS_PUBLIC_API
void JS::SetModuleResolveHook(JSRuntime
* rt
,
63 ModuleResolveHook func
) {
66 rt
->moduleResolveHook
= func
;
69 JS_PUBLIC_API
JS::ModuleMetadataHook
JS::GetModuleMetadataHook(JSRuntime
* rt
) {
72 return rt
->moduleMetadataHook
;
75 JS_PUBLIC_API
void JS::SetModuleMetadataHook(JSRuntime
* rt
,
76 ModuleMetadataHook func
) {
79 rt
->moduleMetadataHook
= func
;
82 JS_PUBLIC_API
JS::ModuleDynamicImportHook
JS::GetModuleDynamicImportHook(
86 return rt
->moduleDynamicImportHook
;
89 JS_PUBLIC_API
void JS::SetModuleDynamicImportHook(
90 JSRuntime
* rt
, ModuleDynamicImportHook func
) {
93 rt
->moduleDynamicImportHook
= func
;
96 JS_PUBLIC_API
bool JS::FinishDynamicModuleImport(
97 JSContext
* cx
, Handle
<JSObject
*> evaluationPromise
,
98 Handle
<Value
> referencingPrivate
, Handle
<JSObject
*> moduleRequest
,
99 Handle
<JSObject
*> promise
) {
102 cx
->check(referencingPrivate
, promise
);
104 return js::FinishDynamicModuleImport(
105 cx
, evaluationPromise
, referencingPrivate
, moduleRequest
, promise
);
108 template <typename Unit
>
109 static JSObject
* CompileModuleHelper(JSContext
* cx
,
110 const JS::ReadOnlyCompileOptions
& options
,
111 JS::SourceText
<Unit
>& srcBuf
) {
112 MOZ_ASSERT(!cx
->zone()->isAtomsZone());
116 JS::Rooted
<JSObject
*> mod(cx
);
118 AutoReportFrontendContext
fc(cx
);
119 mod
= frontend::CompileModule(cx
, &fc
, options
, srcBuf
);
124 JS_PUBLIC_API JSObject
* JS::CompileModule(JSContext
* cx
,
125 const ReadOnlyCompileOptions
& options
,
126 SourceText
<char16_t
>& srcBuf
) {
127 return CompileModuleHelper(cx
, options
, srcBuf
);
130 JS_PUBLIC_API JSObject
* JS::CompileModule(JSContext
* cx
,
131 const ReadOnlyCompileOptions
& options
,
132 SourceText
<Utf8Unit
>& srcBuf
) {
133 return CompileModuleHelper(cx
, options
, srcBuf
);
136 JS_PUBLIC_API
void JS::SetModulePrivate(JSObject
* module
, const Value
& value
) {
137 JSRuntime
* rt
= module
->zone()->runtimeFromMainThread();
138 module
->as
<ModuleObject
>().scriptSourceObject()->setPrivate(rt
, value
);
141 JS_PUBLIC_API
void JS::ClearModulePrivate(JSObject
* module
) {
142 // |module| may be gray, be careful not to create edges to it.
143 JSRuntime
* rt
= module
->zone()->runtimeFromMainThread();
144 module
->as
<ModuleObject
>().scriptSourceObject()->clearPrivate(rt
);
147 JS_PUBLIC_API
JS::Value
JS::GetModulePrivate(JSObject
* module
) {
148 return module
->as
<ModuleObject
>().scriptSourceObject()->getPrivate();
151 JS_PUBLIC_API
bool JS::ModuleLink(JSContext
* cx
, Handle
<JSObject
*> moduleArg
) {
154 cx
->releaseCheck(moduleArg
);
156 return js::ModuleLink(cx
, moduleArg
.as
<ModuleObject
>());
159 JS_PUBLIC_API
bool JS::ModuleEvaluate(JSContext
* cx
,
160 Handle
<JSObject
*> moduleRecord
,
161 MutableHandle
<JS::Value
> rval
) {
164 cx
->releaseCheck(moduleRecord
);
166 return js::ModuleEvaluate(cx
, moduleRecord
.as
<ModuleObject
>(), rval
);
169 JS_PUBLIC_API
bool JS::ThrowOnModuleEvaluationFailure(
170 JSContext
* cx
, Handle
<JSObject
*> evaluationPromise
,
171 ModuleErrorBehaviour errorBehaviour
) {
174 cx
->releaseCheck(evaluationPromise
);
176 return OnModuleEvaluationFailure(cx
, evaluationPromise
, errorBehaviour
);
179 JS_PUBLIC_API
uint32_t
180 JS::GetRequestedModulesCount(JSContext
* cx
, Handle
<JSObject
*> moduleRecord
) {
183 cx
->check(moduleRecord
);
185 return moduleRecord
->as
<ModuleObject
>().requestedModules().Length();
188 JS_PUBLIC_API JSString
* JS::GetRequestedModuleSpecifier(
189 JSContext
* cx
, Handle
<JSObject
*> moduleRecord
, uint32_t index
) {
192 cx
->check(moduleRecord
);
194 auto& module
= moduleRecord
->as
<ModuleObject
>();
195 return module
.requestedModules()[index
].moduleRequest()->specifier();
198 JS_PUBLIC_API
void JS::GetRequestedModuleSourcePos(
199 JSContext
* cx
, Handle
<JSObject
*> moduleRecord
, uint32_t index
,
200 uint32_t* lineNumber
, JS::ColumnNumberZeroOrigin
* columnNumber
) {
203 cx
->check(moduleRecord
);
204 MOZ_ASSERT(lineNumber
);
205 MOZ_ASSERT(columnNumber
);
207 auto& module
= moduleRecord
->as
<ModuleObject
>();
208 *lineNumber
= module
.requestedModules()[index
].lineNumber();
209 *columnNumber
= module
.requestedModules()[index
].columnNumber();
212 JS_PUBLIC_API JSScript
* JS::GetModuleScript(JS::HandleObject moduleRecord
) {
215 return moduleRecord
->as
<ModuleObject
>().script();
218 JS_PUBLIC_API JSObject
* JS::GetModuleObject(HandleScript moduleScript
) {
220 MOZ_ASSERT(moduleScript
->isModule());
222 return moduleScript
->module();
225 JS_PUBLIC_API JSObject
* JS::GetModuleNamespace(JSContext
* cx
,
226 HandleObject moduleRecord
) {
229 cx
->check(moduleRecord
);
230 MOZ_ASSERT(moduleRecord
->is
<ModuleObject
>());
232 return GetOrCreateModuleNamespace(cx
, moduleRecord
.as
<ModuleObject
>());
235 JS_PUBLIC_API JSObject
* JS::GetModuleForNamespace(
236 JSContext
* cx
, HandleObject moduleNamespace
) {
239 cx
->check(moduleNamespace
);
240 MOZ_ASSERT(moduleNamespace
->is
<ModuleNamespaceObject
>());
242 return &moduleNamespace
->as
<ModuleNamespaceObject
>().module();
245 JS_PUBLIC_API JSObject
* JS::GetModuleEnvironment(JSContext
* cx
,
246 Handle
<JSObject
*> moduleObj
) {
249 cx
->check(moduleObj
);
250 MOZ_ASSERT(moduleObj
->is
<ModuleObject
>());
252 return moduleObj
->as
<ModuleObject
>().environment();
255 JS_PUBLIC_API JSObject
* JS::CreateModuleRequest(
256 JSContext
* cx
, Handle
<JSString
*> specifierArg
) {
260 Rooted
<JSAtom
*> specifierAtom(cx
, AtomizeString(cx
, specifierArg
));
261 if (!specifierAtom
) {
265 return ModuleRequestObject::create(cx
, specifierAtom
, nullptr);
268 JS_PUBLIC_API JSString
* JS::GetModuleRequestSpecifier(
269 JSContext
* cx
, Handle
<JSObject
*> moduleRequestArg
) {
272 cx
->check(moduleRequestArg
);
274 return moduleRequestArg
->as
<ModuleRequestObject
>().specifier();
277 JS_PUBLIC_API
void JS::ClearModuleEnvironment(JSObject
* moduleObj
) {
278 MOZ_ASSERT(moduleObj
);
281 js::ModuleEnvironmentObject
* env
=
282 moduleObj
->as
<js::ModuleObject
>().environment();
287 const JSClass
* clasp
= env
->getClass();
288 uint32_t numReserved
= JSCLASS_RESERVED_SLOTS(clasp
);
289 uint32_t numSlots
= env
->slotSpan();
290 for (uint32_t i
= numReserved
; i
< numSlots
; i
++) {
291 env
->setSlot(i
, UndefinedValue());
295 JS_PUBLIC_API
void JS::AssertModuleUnlinked(JSObject
* moduleObj
) {
296 MOZ_ASSERT(moduleObj
);
299 MOZ_DIAGNOSTIC_ASSERT(moduleObj
->as
<ModuleObject
>().status() ==
300 ModuleStatus::Unlinked
);
303 ////////////////////////////////////////////////////////////////////////////////
304 // Internal implementation
306 class ResolveSetEntry
{
307 ModuleObject
* module_
;
311 ResolveSetEntry(ModuleObject
* module
, JSAtom
* exportName
)
312 : module_(module
), exportName_(exportName
) {}
314 ModuleObject
* module() const { return module_
; }
315 JSAtom
* exportName() const { return exportName_
; }
317 void trace(JSTracer
* trc
) {
318 TraceRoot(trc
, &module_
, "ResolveSetEntry::module_");
319 TraceRoot(trc
, &exportName_
, "ResolveSetEntry::exportName_");
323 using ResolveSet
= GCVector
<ResolveSetEntry
, 0, SystemAllocPolicy
>;
326 GCHashSet
<ModuleObject
*, DefaultHasher
<ModuleObject
*>, SystemAllocPolicy
>;
328 static ModuleObject
* HostResolveImportedModule(
329 JSContext
* cx
, Handle
<ModuleObject
*> module
,
330 Handle
<ModuleRequestObject
*> moduleRequest
,
331 ModuleStatus expectedMinimumStatus
);
332 static bool ModuleResolveExport(JSContext
* cx
, Handle
<ModuleObject
*> module
,
333 Handle
<JSAtom
*> exportName
,
334 MutableHandle
<ResolveSet
> resolveSet
,
335 MutableHandle
<Value
> result
);
336 static ModuleNamespaceObject
* ModuleNamespaceCreate(
337 JSContext
* cx
, Handle
<ModuleObject
*> module
,
338 MutableHandle
<UniquePtr
<ExportNameVector
>> exports
);
339 static bool InnerModuleLinking(JSContext
* cx
, Handle
<ModuleObject
*> module
,
340 MutableHandle
<ModuleVector
> stack
, size_t index
,
342 static bool InnerModuleEvaluation(JSContext
* cx
, Handle
<ModuleObject
*> module
,
343 MutableHandle
<ModuleVector
> stack
,
344 size_t index
, size_t* indexOut
);
345 static bool ExecuteAsyncModule(JSContext
* cx
, Handle
<ModuleObject
*> module
);
346 static bool GatherAvailableModuleAncestors(
347 JSContext
* cx
, Handle
<ModuleObject
*> module
,
348 MutableHandle
<ModuleVector
> execList
);
350 static const char* ModuleStatusName(ModuleStatus status
) {
352 case ModuleStatus::Unlinked
:
354 case ModuleStatus::Linking
:
356 case ModuleStatus::Linked
:
358 case ModuleStatus::Evaluating
:
360 case ModuleStatus::EvaluatingAsync
:
361 return "EvaluatingAsync";
362 case ModuleStatus::Evaluated
:
365 MOZ_CRASH("Unexpected ModuleStatus");
369 static bool ContainsElement(Handle
<ExportNameVector
> list
, JSAtom
* atom
) {
370 for (JSAtom
* a
: list
) {
379 static bool ContainsElement(Handle
<ModuleVector
> stack
, ModuleObject
* module
) {
380 for (ModuleObject
* m
: stack
) {
390 static size_t CountElements(Handle
<ModuleVector
> stack
, ModuleObject
* module
) {
392 for (ModuleObject
* m
: stack
) {
402 // https://tc39.es/ecma262/#sec-getexportednames
403 // ES2023 16.2.1.6.2 GetExportedNames
404 static bool ModuleGetExportedNames(
405 JSContext
* cx
, Handle
<ModuleObject
*> module
,
406 MutableHandle
<ModuleSet
> exportStarSet
,
407 MutableHandle
<ExportNameVector
> exportedNames
) {
408 // Step 4. Let exportedNames be a new empty List.
409 MOZ_ASSERT(exportedNames
.empty());
411 // Step 2. If exportStarSet contains module, then:
412 if (exportStarSet
.has(module
)) {
413 // Step 2.a. We've reached the starting point of an export * circularity.
414 // Step 2.b. Return a new empty List.
418 // Step 3. Append module to exportStarSet.
419 if (!exportStarSet
.put(module
)) {
420 ReportOutOfMemory(cx
);
424 // Step 5. For each ExportEntry Record e of module.[[LocalExportEntries]], do:
425 for (const ExportEntry
& e
: module
->localExportEntries()) {
426 // Step 5.a. Assert: module provides the direct binding for this export.
427 // Step 5.b. Append e.[[ExportName]] to exportedNames.
428 if (!exportedNames
.append(e
.exportName())) {
429 ReportOutOfMemory(cx
);
434 // Step 6. For each ExportEntry Record e of module.[[IndirectExportEntries]],
436 for (const ExportEntry
& e
: module
->indirectExportEntries()) {
437 // Step 6.a. Assert: module imports a specific binding for this export.
438 // Step 6.b. Append e.[[ExportName]] to exportedNames.
439 if (!exportedNames
.append(e
.exportName())) {
440 ReportOutOfMemory(cx
);
445 // Step 7. For each ExportEntry Record e of module.[[StarExportEntries]], do:
446 Rooted
<ModuleRequestObject
*> moduleRequest(cx
);
447 Rooted
<ModuleObject
*> requestedModule(cx
);
448 Rooted
<JSAtom
*> name(cx
);
449 for (const ExportEntry
& e
: module
->starExportEntries()) {
450 // Step 7.a. Let requestedModule be ? HostResolveImportedModule(module,
451 // e.[[ModuleRequest]]).
452 moduleRequest
= e
.moduleRequest();
453 requestedModule
= HostResolveImportedModule(cx
, module
, moduleRequest
,
454 ModuleStatus::Unlinked
);
455 if (!requestedModule
) {
459 // Step 7.b. Let starNames be ?
460 // requestedModule.GetExportedNames(exportStarSet).
461 Rooted
<ExportNameVector
> starNames(cx
);
462 if (!ModuleGetExportedNames(cx
, requestedModule
, exportStarSet
,
467 // Step 7.c. For each element n of starNames, do:
468 for (JSAtom
* name
: starNames
) {
469 // Step 7.c.i. If SameValue(n, "default") is false, then:
470 if (name
!= cx
->names().default_
) {
471 // Step 7.c.i.1. If n is not an element of exportedNames, then:
472 if (!ContainsElement(exportedNames
, name
)) {
473 // Step 7.c.i.1.a. Append n to exportedNames.
474 if (!exportedNames
.append(name
)) {
475 ReportOutOfMemory(cx
);
483 // Step 8. Return exportedNames.
487 static void ThrowUnexpectedModuleStatus(JSContext
* cx
, ModuleStatus status
) {
488 JS_ReportErrorNumberUTF8(cx
, GetErrorMessage
, nullptr,
489 JSMSG_BAD_MODULE_STATUS
, ModuleStatusName(status
));
492 static ModuleObject
* HostResolveImportedModule(
493 JSContext
* cx
, Handle
<ModuleObject
*> module
,
494 Handle
<ModuleRequestObject
*> moduleRequest
,
495 ModuleStatus expectedMinimumStatus
) {
497 MOZ_ASSERT(moduleRequest
);
499 Rooted
<Value
> referencingPrivate(cx
, JS::GetModulePrivate(module
));
500 Rooted
<ModuleObject
*> requestedModule(cx
);
502 CallModuleResolveHook(cx
, referencingPrivate
, moduleRequest
);
503 if (!requestedModule
) {
507 if (requestedModule
->status() < expectedMinimumStatus
) {
508 ThrowUnexpectedModuleStatus(cx
, requestedModule
->status());
512 return requestedModule
;
515 // https://tc39.es/ecma262/#sec-resolveexport
516 // ES2023 16.2.1.6.3 ResolveExport
518 // Returns an value describing the location of the resolved export or indicating
521 // On success this returns a resolved binding record: { module, bindingName }
523 // There are two failure cases:
525 // - If no definition was found or the request is found to be circular, *null*
528 // - If the request is found to be ambiguous, the string `"ambiguous"` is
531 bool js::ModuleResolveExport(JSContext
* cx
, Handle
<ModuleObject
*> module
,
532 Handle
<JSAtom
*> exportName
,
533 MutableHandle
<Value
> result
) {
534 // Step 1. If resolveSet is not present, set resolveSet to a new empty List.
535 Rooted
<ResolveSet
> resolveSet(cx
);
537 return ::ModuleResolveExport(cx
, module
, exportName
, &resolveSet
, result
);
540 static bool CreateResolvedBindingObject(JSContext
* cx
,
541 Handle
<ModuleObject
*> module
,
542 Handle
<JSAtom
*> bindingName
,
543 MutableHandle
<Value
> result
) {
544 Rooted
<ResolvedBindingObject
*> obj(
545 cx
, ResolvedBindingObject::create(cx
, module
, bindingName
));
550 result
.setObject(*obj
);
554 static bool ModuleResolveExport(JSContext
* cx
, Handle
<ModuleObject
*> module
,
555 Handle
<JSAtom
*> exportName
,
556 MutableHandle
<ResolveSet
> resolveSet
,
557 MutableHandle
<Value
> result
) {
558 // Step 2. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do:
559 for (const auto& entry
: resolveSet
) {
560 // Step 2.a. If module and r.[[Module]] are the same Module Record and
561 // SameValue(exportName, r.[[ExportName]]) is true, then:
562 if (entry
.module() == module
&& entry
.exportName() == exportName
) {
563 // Step 2.a.i. Assert: This is a circular import request.
564 // Step 2.a.ii. Return null.
570 // Step 3. Append the Record { [[Module]]: module, [[ExportName]]: exportName
572 if (!resolveSet
.emplaceBack(module
, exportName
)) {
573 ReportOutOfMemory(cx
);
577 // Step 4. For each ExportEntry Record e of module.[[LocalExportEntries]], do:
578 for (const ExportEntry
& e
: module
->localExportEntries()) {
579 // Step 4.a. If SameValue(exportName, e.[[ExportName]]) is true, then:
580 if (exportName
== e
.exportName()) {
581 // Step 4.a.i. Assert: module provides the direct binding for this export.
582 // Step 4.a.ii. Return ResolvedBinding Record { [[Module]]: module,
583 // [[BindingName]]: e.[[LocalName]] }.
584 Rooted
<JSAtom
*> localName(cx
, e
.localName());
585 return CreateResolvedBindingObject(cx
, module
, localName
, result
);
589 // Step 5. For each ExportEntry Record e of module.[[IndirectExportEntries]],
591 Rooted
<ModuleRequestObject
*> moduleRequest(cx
);
592 Rooted
<ModuleObject
*> importedModule(cx
);
593 Rooted
<JSAtom
*> name(cx
);
594 for (const ExportEntry
& e
: module
->indirectExportEntries()) {
595 // Step 5.a. If SameValue(exportName, e.[[ExportName]]) is true, then:
596 if (exportName
== e
.exportName()) {
597 // Step 5.a.i. Let importedModule be ? HostResolveImportedModule(module,
598 // e.[[ModuleRequest]]).
599 moduleRequest
= e
.moduleRequest();
600 importedModule
= HostResolveImportedModule(cx
, module
, moduleRequest
,
601 ModuleStatus::Unlinked
);
602 if (!importedModule
) {
606 // Step 5.a.ii. If e.[[ImportName]] is all, then:
607 if (!e
.importName()) {
608 // Step 5.a.ii.1. Assert: module does not provide the direct binding for
610 // Step 5.a.ii.2. Return ResolvedBinding Record { [[Module]]:
611 // importedModule, [[BindingName]]: namespace }.
612 name
= cx
->names().star_namespace_star_
;
613 return CreateResolvedBindingObject(cx
, importedModule
, name
, result
);
615 // Step 5.a.iii.1. Assert: module imports a specific binding for this
617 // Step 5.a.iii.2. Return ?
618 // importedModule.ResolveExport(e.[[ImportName]],
620 name
= e
.importName();
621 return ModuleResolveExport(cx
, importedModule
, name
, resolveSet
,
627 // Step 6. If SameValue(exportName, "default") is true, then:
628 if (exportName
== cx
->names().default_
) {
629 // Step 6.a. Assert: A default export was not explicitly defined by this
631 // Step 6.b. Return null.
632 // Step 6.c. NOTE: A default export cannot be provided by an export * from
633 // "mod" declaration.
638 // Step 7. Let starResolution be null.
639 Rooted
<ResolvedBindingObject
*> starResolution(cx
);
641 // Step 8. For each ExportEntry Record e of module.[[StarExportEntries]], do:
642 Rooted
<Value
> resolution(cx
);
643 Rooted
<ResolvedBindingObject
*> binding(cx
);
644 for (const ExportEntry
& e
: module
->starExportEntries()) {
645 // Step 8.a. Let importedModule be ? HostResolveImportedModule(module,
646 // e.[[ModuleRequest]]).
647 moduleRequest
= e
.moduleRequest();
648 importedModule
= HostResolveImportedModule(cx
, module
, moduleRequest
,
649 ModuleStatus::Unlinked
);
650 if (!importedModule
) {
654 // Step 8.b. Let resolution be ? importedModule.ResolveExport(exportName,
656 if (!ModuleResolveExport(cx
, importedModule
, exportName
, resolveSet
,
661 // Step 8.c. If resolution is ambiguous, return ambiguous.
662 if (resolution
== StringValue(cx
->names().ambiguous
)) {
663 result
.set(resolution
);
667 // Step 8.d. If resolution is not null, then:
668 if (!resolution
.isNull()) {
669 // Step 8.d.i. Assert: resolution is a ResolvedBinding Record.
670 binding
= &resolution
.toObject().as
<ResolvedBindingObject
>();
672 // Step 8.d.ii. If starResolution is null, set starResolution to
674 if (!starResolution
) {
675 starResolution
= binding
;
677 // Step 8.d.iii. Else:
678 // Step 8.d.iii.1. Assert: There is more than one * import that includes
679 // the requested name.
680 // Step 8.d.iii.2. If resolution.[[Module]] and
681 // starResolution.[[Module]] are not the same Module
682 // Record, return ambiguous.
683 // Step 8.d.iii.3. If resolution.[[BindingName]] is namespace and
684 // starResolution.[[BindingName]] is not namespace, or
685 // if resolution.[[BindingName]] is not namespace and
686 // starResolution.[[BindingName]] is namespace, return
688 // Step 8.d.iii.4. If resolution.[[BindingName]] is a String,
689 // starResolution.[[BindingName]] is a String, and
690 // SameValue(resolution.[[BindingName]],
691 // starResolution.[[BindingName]]) is false, return
693 if (binding
->module() != starResolution
->module() ||
694 binding
->bindingName() != starResolution
->bindingName()) {
695 result
.set(StringValue(cx
->names().ambiguous
));
702 // Step 9. Return starResolution.
703 result
.setObjectOrNull(starResolution
);
707 // https://tc39.es/ecma262/#sec-getmodulenamespace
708 // ES2023 16.2.1.10 GetModuleNamespace
709 ModuleNamespaceObject
* js::GetOrCreateModuleNamespace(
710 JSContext
* cx
, Handle
<ModuleObject
*> module
) {
711 // Step 1. Assert: If module is a Cyclic Module Record, then module.[[Status]]
713 MOZ_ASSERT(module
->status() != ModuleStatus::Unlinked
);
715 // Step 2. Let namespace be module.[[Namespace]].
716 Rooted
<ModuleNamespaceObject
*> ns(cx
, module
->namespace_());
718 // Step 3. If namespace is empty, then:
720 // Step 3.a. Let exportedNames be ? module.GetExportedNames().
721 Rooted
<ModuleSet
> exportStarSet(cx
);
722 Rooted
<ExportNameVector
> exportedNames(cx
);
723 if (!ModuleGetExportedNames(cx
, module
, &exportStarSet
, &exportedNames
)) {
727 // Step 3.b. Let unambiguousNames be a new empty List.
728 Rooted
<UniquePtr
<ExportNameVector
>> unambiguousNames(
729 cx
, cx
->make_unique
<ExportNameVector
>());
730 if (!unambiguousNames
) {
734 // Step 3.c. For each element name of exportedNames, do:
735 Rooted
<JSAtom
*> name(cx
);
736 Rooted
<Value
> resolution(cx
);
737 for (JSAtom
* atom
: exportedNames
) {
740 // Step 3.c.i. Let resolution be ? module.ResolveExport(name).
741 if (!ModuleResolveExport(cx
, module
, name
, &resolution
)) {
745 // Step 3.c.ii. If resolution is a ResolvedBinding Record, append name to
747 if (resolution
.isObject() && !unambiguousNames
->append(name
)) {
748 ReportOutOfMemory(cx
);
753 // Step 3.d. Set namespace to ModuleNamespaceCreate(module,
754 // unambiguousNames).
755 ns
= ModuleNamespaceCreate(cx
, module
, &unambiguousNames
);
758 // Step 4. Return namespace.
762 static bool IsResolvedBinding(JSContext
* cx
, Handle
<Value
> resolution
) {
763 MOZ_ASSERT(resolution
.isObjectOrNull() ||
764 resolution
.toString() == cx
->names().ambiguous
);
765 return resolution
.isObject();
768 static void InitNamespaceBinding(JSContext
* cx
,
769 Handle
<ModuleEnvironmentObject
*> env
,
770 Handle
<JSAtom
*> name
,
771 Handle
<ModuleNamespaceObject
*> ns
) {
772 // The property already exists in the evironment but is not writable, so set
773 // the slot directly.
774 RootedId
id(cx
, AtomToId(name
));
775 mozilla::Maybe
<PropertyInfo
> prop
= env
->lookup(cx
, id
);
776 MOZ_ASSERT(prop
.isSome());
777 env
->setSlot(prop
->slot(), ObjectValue(*ns
));
780 struct AtomComparator
{
781 bool operator()(JSAtom
* a
, JSAtom
* b
, bool* lessOrEqualp
) {
782 int32_t result
= CompareStrings(a
, b
);
783 *lessOrEqualp
= (result
<= 0);
788 // https://tc39.es/ecma262/#sec-modulenamespacecreate
789 // ES2023 10.4.6.12 ModuleNamespaceCreate
790 static ModuleNamespaceObject
* ModuleNamespaceCreate(
791 JSContext
* cx
, Handle
<ModuleObject
*> module
,
792 MutableHandle
<UniquePtr
<ExportNameVector
>> exports
) {
793 // Step 1. Assert: module.[[Namespace]] is empty.
794 MOZ_ASSERT(!module
->namespace_());
796 // Step 6. Let sortedExports be a List whose elements are the elements of
797 // exports ordered as if an Array of the same values had been sorted
798 // using %Array.prototype.sort% using undefined as comparefn.
799 ExportNameVector scratch
;
800 if (!scratch
.resize(exports
->length())) {
801 ReportOutOfMemory(cx
);
804 MOZ_ALWAYS_TRUE(MergeSort(exports
->begin(), exports
->length(),
805 scratch
.begin(), AtomComparator()));
808 Rooted
<ModuleNamespaceObject
*> ns(
809 cx
, ModuleObject::createNamespace(cx
, module
, exports
));
814 // Pre-compute all binding mappings now instead of on each access.
816 // https://tc39.es/ecma262/#sec-module-namespace-exotic-objects-get-p-receiver
817 // ES2023 10.4.6.8 Module Namespace Exotic Object [[Get]]
818 Rooted
<JSAtom
*> name(cx
);
819 Rooted
<Value
> resolution(cx
);
820 Rooted
<ResolvedBindingObject
*> binding(cx
);
821 Rooted
<ModuleObject
*> importedModule(cx
);
822 Rooted
<ModuleNamespaceObject
*> importedNamespace(cx
);
823 Rooted
<JSAtom
*> bindingName(cx
);
824 for (JSAtom
* atom
: ns
->exports()) {
827 if (!ModuleResolveExport(cx
, module
, name
, &resolution
)) {
831 MOZ_ASSERT(IsResolvedBinding(cx
, resolution
));
832 binding
= &resolution
.toObject().as
<ResolvedBindingObject
>();
833 importedModule
= binding
->module();
834 bindingName
= binding
->bindingName();
836 if (bindingName
== cx
->names().star_namespace_star_
) {
837 importedNamespace
= GetOrCreateModuleNamespace(cx
, importedModule
);
838 if (!importedNamespace
) {
842 // The spec uses an immutable binding here but we have already generated
843 // bytecode for an indirect binding. Instead, use an indirect binding to
844 // "*namespace*" slot of the target environment.
845 Rooted
<ModuleEnvironmentObject
*> env(
846 cx
, &importedModule
->initialEnvironment());
847 InitNamespaceBinding(cx
, env
, bindingName
, importedNamespace
);
850 if (!ns
->addBinding(cx
, name
, importedModule
, bindingName
)) {
855 // Step 10. Return M.
859 static void ThrowResolutionError(JSContext
* cx
, Handle
<ModuleObject
*> module
,
860 Handle
<Value
> resolution
, bool isDirectImport
,
861 Handle
<JSAtom
*> name
, uint32_t line
,
862 JS::ColumnNumberZeroOrigin column
) {
863 MOZ_ASSERT(line
!= 0);
865 bool isAmbiguous
= resolution
== StringValue(cx
->names().ambiguous
);
867 static constexpr unsigned ErrorNumbers
[2][2] = {
868 {JSMSG_AMBIGUOUS_IMPORT
, JSMSG_MISSING_IMPORT
},
869 {JSMSG_AMBIGUOUS_INDIRECT_EXPORT
, JSMSG_MISSING_INDIRECT_EXPORT
}};
870 unsigned errorNumber
= ErrorNumbers
[isDirectImport
][isAmbiguous
];
872 const JSErrorFormatString
* errorString
=
873 GetErrorMessage(nullptr, errorNumber
);
874 MOZ_ASSERT(errorString
);
876 MOZ_ASSERT(errorString
->argCount
== 0);
877 Rooted
<JSString
*> message(cx
, JS_NewStringCopyZ(cx
, errorString
->format
));
882 Rooted
<JSString
*> separator(cx
, JS_NewStringCopyZ(cx
, ": "));
887 message
= ConcatStrings
<CanGC
>(cx
, message
, separator
);
892 message
= ConcatStrings
<CanGC
>(cx
, message
, name
);
897 RootedString
filename(cx
);
898 if (const char* chars
= module
->script()->filename()) {
900 JS_NewStringCopyUTF8Z(cx
, JS::ConstUTF8CharsZ(chars
, strlen(chars
)));
902 filename
= cx
->names().empty_
;
908 RootedValue
error(cx
);
909 if (!JS::CreateError(cx
, JSEXN_SYNTAXERR
, nullptr, filename
, line
,
910 JS::ColumnNumberOneOrigin(column
), nullptr, message
,
911 JS::NothingHandleValue
, &error
)) {
915 cx
->setPendingException(error
, nullptr);
918 // https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
919 // ES2023 16.2.1.6.4 InitializeEnvironment
920 bool js::ModuleInitializeEnvironment(JSContext
* cx
,
921 Handle
<ModuleObject
*> module
) {
922 MOZ_ASSERT(module
->status() == ModuleStatus::Linking
);
924 // Step 1. For each ExportEntry Record e of module.[[IndirectExportEntries]],
926 Rooted
<JSAtom
*> exportName(cx
);
927 Rooted
<Value
> resolution(cx
);
928 for (const ExportEntry
& e
: module
->indirectExportEntries()) {
929 // Step 1.a. Let resolution be ? module.ResolveExport(e.[[ExportName]]).
930 exportName
= e
.exportName();
931 if (!ModuleResolveExport(cx
, module
, exportName
, &resolution
)) {
935 // Step 1.b. If resolution is null or ambiguous, throw a SyntaxError
937 if (!IsResolvedBinding(cx
, resolution
)) {
938 ThrowResolutionError(cx
, module
, resolution
, false, exportName
,
939 e
.lineNumber(), e
.columnNumber());
944 // Step 5. Let env be NewModuleEnvironment(realm.[[GlobalEnv]]).
945 // Step 6. Set module.[[Environment]] to env.
946 // Note that we have already created the environment by this point.
947 Rooted
<ModuleEnvironmentObject
*> env(cx
, &module
->initialEnvironment());
949 // Step 7. For each ImportEntry Record in of module.[[ImportEntries]], do:
950 Rooted
<ModuleRequestObject
*> moduleRequest(cx
);
951 Rooted
<ModuleObject
*> importedModule(cx
);
952 Rooted
<JSAtom
*> importName(cx
);
953 Rooted
<JSAtom
*> localName(cx
);
954 Rooted
<ModuleObject
*> sourceModule(cx
);
955 Rooted
<JSAtom
*> bindingName(cx
);
956 for (const ImportEntry
& in
: module
->importEntries()) {
957 // Step 7.a. Let importedModule be ! HostResolveImportedModule(module,
958 // in.[[ModuleRequest]]).
959 moduleRequest
= in
.moduleRequest();
960 importedModule
= HostResolveImportedModule(cx
, module
, moduleRequest
,
961 ModuleStatus::Linking
);
962 if (!importedModule
) {
966 localName
= in
.localName();
967 importName
= in
.importName();
969 // Step 7.c. If in.[[ImportName]] is namespace-object, then:
971 // Step 7.c.i. Let namespace be ? GetModuleNamespace(importedModule).
972 Rooted
<ModuleNamespaceObject
*> ns(
973 cx
, GetOrCreateModuleNamespace(cx
, importedModule
));
978 // Step 7.c.ii. Perform ! env.CreateImmutableBinding(in.[[LocalName]],
979 // true). This happens when the environment is created.
981 // Step 7.c.iii. Perform ! env.InitializeBinding(in.[[LocalName]],
983 InitNamespaceBinding(cx
, env
, localName
, ns
);
986 // Step 7.d.i. Let resolution be ?
987 // importedModule.ResolveExport(in.[[ImportName]]).
988 if (!ModuleResolveExport(cx
, importedModule
, importName
, &resolution
)) {
992 // Step 7.d.ii. If resolution is null or ambiguous, throw a SyntaxError
994 if (!IsResolvedBinding(cx
, resolution
)) {
995 ThrowResolutionError(cx
, module
, resolution
, true, importName
,
996 in
.lineNumber(), in
.columnNumber());
1000 auto* binding
= &resolution
.toObject().as
<ResolvedBindingObject
>();
1001 sourceModule
= binding
->module();
1002 bindingName
= binding
->bindingName();
1004 // Step 7.d.iii. If resolution.[[BindingName]] is namespace, then:
1005 if (bindingName
== cx
->names().star_namespace_star_
) {
1006 // Step 7.d.iii.1. Let namespace be ?
1007 // GetModuleNamespace(resolution.[[Module]]).
1008 Rooted
<ModuleNamespaceObject
*> ns(
1009 cx
, GetOrCreateModuleNamespace(cx
, sourceModule
));
1014 // Step 7.d.iii.2. Perform !
1015 // env.CreateImmutableBinding(in.[[LocalName]], true).
1016 // Step 7.d.iii.3. Perform ! env.InitializeBinding(in.[[LocalName]],
1019 // This should be InitNamespaceBinding, but we have already generated
1020 // bytecode assuming an indirect binding. Instead, ensure a special
1021 // "*namespace*"" binding exists on the target module's environment. We
1022 // then generate an indirect binding to this synthetic binding.
1023 Rooted
<ModuleEnvironmentObject
*> sourceEnv(
1024 cx
, &sourceModule
->initialEnvironment());
1025 InitNamespaceBinding(cx
, sourceEnv
, bindingName
, ns
);
1026 if (!env
->createImportBinding(cx
, localName
, sourceModule
,
1031 // Step 7.d.iv. Else:
1032 // Step 7.d.iv.1. 1. Perform env.CreateImportBinding(in.[[LocalName]],
1033 // resolution.[[Module]], resolution.[[BindingName]]).
1034 if (!env
->createImportBinding(cx
, localName
, sourceModule
,
1044 // Some of these do not need to happen for practical purposes. For steps
1045 // 21-23, the bindings that can be handled in a similar way to regulars
1046 // scripts are done separately. Function Declarations are special due to
1047 // hoisting and are handled within this function. See ModuleScope and
1048 // ModuleEnvironmentObject for further details.
1050 // Step 24. For each element d of lexDeclarations, do:
1051 // Step 24.a. For each element dn of the BoundNames of d, do:
1052 // Step 24.a.iii. If d is a FunctionDeclaration, a GeneratorDeclaration, an
1053 // AsyncFunctionDeclaration, or an AsyncGeneratorDeclaration,
1055 // Step 24.a.iii.1 Let fo be InstantiateFunctionObject of d with arguments env
1057 // Step 24.a.iii.2. Perform ! env.InitializeBinding(dn, fo).
1058 return ModuleObject::instantiateFunctionDeclarations(cx
, module
);
1061 // https://tc39.es/ecma262/#sec-moduledeclarationlinking
1062 // ES2023 16.2.1.5.1 Link
1063 bool js::ModuleLink(JSContext
* cx
, Handle
<ModuleObject
*> module
) {
1064 // Step 1. Assert: module.[[Status]] is not linking or evaluating.
1065 ModuleStatus status
= module
->status();
1066 if (status
== ModuleStatus::Linking
|| status
== ModuleStatus::Evaluating
) {
1067 ThrowUnexpectedModuleStatus(cx
, status
);
1071 // Step 2. Let stack be a new empty List.
1072 Rooted
<ModuleVector
> stack(cx
);
1074 // Step 3. Let result be Completion(InnerModuleLinking(module, stack, 0)).
1076 bool ok
= InnerModuleLinking(cx
, module
, &stack
, 0, &ignored
);
1078 // Step 4. If result is an abrupt completion, then:
1080 // Step 4.a. For each Cyclic Module Record m of stack, do:
1081 for (ModuleObject
* m
: stack
) {
1082 // Step 4.a.i. Assert: m.[[Status]] is linking.
1083 MOZ_ASSERT(m
->status() == ModuleStatus::Linking
);
1084 // Step 4.a.ii. Set m.[[Status]] to unlinked.
1085 m
->setStatus(ModuleStatus::Unlinked
);
1086 m
->clearDfsIndexes();
1089 // Step 4.b. Assert: module.[[Status]] is unlinked.
1090 MOZ_ASSERT(module
->status() == ModuleStatus::Unlinked
);
1096 // Step 5. Assert: module.[[Status]] is linked, evaluating-async, or
1098 MOZ_ASSERT(module
->status() == ModuleStatus::Linked
||
1099 module
->status() == ModuleStatus::EvaluatingAsync
||
1100 module
->status() == ModuleStatus::Evaluated
);
1102 // Step 6. Assert: stack is empty.
1103 MOZ_ASSERT(stack
.empty());
1105 // Step 7. Return unused.
1109 // https://tc39.es/ecma262/#sec-InnerModuleLinking
1110 // ES2023 16.2.1.5.1.1 InnerModuleLinking
1111 static bool InnerModuleLinking(JSContext
* cx
, Handle
<ModuleObject
*> module
,
1112 MutableHandle
<ModuleVector
> stack
, size_t index
,
1114 // Step 2. If module.[[Status]] is linking, linked, evaluating-async, or
1116 if (module
->status() == ModuleStatus::Linking
||
1117 module
->status() == ModuleStatus::Linked
||
1118 module
->status() == ModuleStatus::EvaluatingAsync
||
1119 module
->status() == ModuleStatus::Evaluated
) {
1120 // Step 2.a. Return index.
1125 // Step 3. Assert: module.[[Status]] is unlinked.
1126 if (module
->status() != ModuleStatus::Unlinked
) {
1127 ThrowUnexpectedModuleStatus(cx
, module
->status());
1131 // Step 8. Append module to stack.
1132 // Do this before changing the status so that we can recover on failure.
1133 if (!stack
.append(module
)) {
1134 ReportOutOfMemory(cx
);
1138 // Step 4. Set module.[[Status]] to linking.
1139 module
->setStatus(ModuleStatus::Linking
);
1141 // Step 5. Set module.[[DFSIndex]] to index.
1142 module
->setDfsIndex(index
);
1144 // Step 6. Set module.[[DFSAncestorIndex]] to index.
1145 module
->setDfsAncestorIndex(index
);
1147 // Step 7. Set index to index + 1.
1150 // Step 9. For each String required that is an element of
1151 // module.[[RequestedModules]], do:
1152 Rooted
<ModuleRequestObject
*> moduleRequest(cx
);
1153 Rooted
<ModuleObject
*> requiredModule(cx
);
1154 for (const RequestedModule
& request
: module
->requestedModules()) {
1155 moduleRequest
= request
.moduleRequest();
1157 // Step 9.a. Let requiredModule be ? HostResolveImportedModule(module,
1159 requiredModule
= HostResolveImportedModule(cx
, module
, moduleRequest
,
1160 ModuleStatus::Unlinked
);
1161 if (!requiredModule
) {
1165 // Step 9.b. Set index to ? InnerModuleLinking(requiredModule, stack,
1167 if (!InnerModuleLinking(cx
, requiredModule
, stack
, index
, &index
)) {
1171 // Step 9.c. If requiredModule is a Cyclic Module Record, then:
1172 // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking, linked,
1173 // evaluating-async, or evaluated.
1174 MOZ_ASSERT(requiredModule
->status() == ModuleStatus::Linking
||
1175 requiredModule
->status() == ModuleStatus::Linked
||
1176 requiredModule
->status() == ModuleStatus::EvaluatingAsync
||
1177 requiredModule
->status() == ModuleStatus::Evaluated
);
1179 // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only if
1180 // requiredModule is in stack.
1181 MOZ_ASSERT((requiredModule
->status() == ModuleStatus::Linking
) ==
1182 ContainsElement(stack
, requiredModule
));
1184 // Step 9.c.iii. If requiredModule.[[Status]] is linking, then:
1185 if (requiredModule
->status() == ModuleStatus::Linking
) {
1186 // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to
1187 // min(module.[[DFSAncestorIndex]],
1188 // requiredModule.[[DFSAncestorIndex]]).
1189 module
->setDfsAncestorIndex(std::min(module
->dfsAncestorIndex(),
1190 requiredModule
->dfsAncestorIndex()));
1194 // Step 10. Perform ? module.InitializeEnvironment().
1195 if (!ModuleInitializeEnvironment(cx
, module
)) {
1199 // Step 11. Assert: module occurs exactly once in stack.
1200 MOZ_ASSERT(CountElements(stack
, module
) == 1);
1202 // Step 12. Assert: module.[[DFSAncestorIndex]] <= module.[[DFSIndex]].
1203 MOZ_ASSERT(module
->dfsAncestorIndex() <= module
->dfsIndex());
1205 // Step 13. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then
1206 if (module
->dfsAncestorIndex() == module
->dfsIndex()) {
1210 // Step 13.b. Repeat, while done is false:
1212 // Step 13.b.i. Let requiredModule be the last element in stack.
1213 // Step 13.b.ii. Remove the last element of stack.
1214 requiredModule
= stack
.popCopy();
1216 // Step 13.b.iv. Set requiredModule.[[Status]] to linked.
1217 requiredModule
->setStatus(ModuleStatus::Linked
);
1219 // Step 13.b.v. If requiredModule and module are the same Module Record,
1220 // set done to true.
1221 done
= requiredModule
== module
;
1225 // Step 14. Return index.
1230 // https://tc39.es/ecma262/#sec-moduleevaluation
1231 // ES2023 16.2.1.5.2 Evaluate
1232 bool js::ModuleEvaluate(JSContext
* cx
, Handle
<ModuleObject
*> moduleArg
,
1233 MutableHandle
<Value
> result
) {
1234 Rooted
<ModuleObject
*> module(cx
, moduleArg
);
1236 // Step 2. Assert: module.[[Status]] is linked, evaluating-async, or
1238 ModuleStatus status
= module
->status();
1239 if (status
!= ModuleStatus::Linked
&&
1240 status
!= ModuleStatus::EvaluatingAsync
&&
1241 status
!= ModuleStatus::Evaluated
) {
1242 ThrowUnexpectedModuleStatus(cx
, status
);
1246 // Note: we return early in the error case, as the spec assumes we can get the
1247 // cycle root of |module| which may not be available.
1248 if (module
->hadEvaluationError()) {
1249 Rooted
<PromiseObject
*> capability(cx
);
1250 if (!module
->hasTopLevelCapability()) {
1251 capability
= ModuleObject::createTopLevelCapability(cx
, module
);
1256 Rooted
<Value
> error(cx
, module
->evaluationError());
1257 if (!ModuleObject::topLevelCapabilityReject(cx
, module
, error
)) {
1262 capability
= module
->topLevelCapability();
1263 MOZ_ASSERT(JS::GetPromiseState(capability
) == JS::PromiseState::Rejected
);
1264 MOZ_ASSERT(JS::GetPromiseResult(capability
) == module
->evaluationError());
1265 result
.set(ObjectValue(*capability
));
1269 // Step 3. If module.[[Status]] is evaluating-async or evaluated, set module
1270 // to module.[[CycleRoot]].
1271 if (module
->status() == ModuleStatus::EvaluatingAsync
||
1272 module
->status() == ModuleStatus::Evaluated
) {
1273 module
= module
->getCycleRoot();
1276 // Step 4. If module.[[TopLevelCapability]] is not empty, then:
1277 if (module
->hasTopLevelCapability()) {
1278 // Step 4.a. Return module.[[TopLevelCapability]].[[Promise]].
1279 result
.set(ObjectValue(*module
->topLevelCapability()));
1283 // Step 5. Let stack be a new empty List.
1284 Rooted
<ModuleVector
> stack(cx
);
1286 // Step 6. Let capability be ! NewPromiseCapability(%Promise%).
1287 // Step 7. Set module.[[TopLevelCapability]] to capability.
1288 Rooted
<PromiseObject
*> capability(
1289 cx
, ModuleObject::createTopLevelCapability(cx
, module
));
1294 // Step 8. Let result be Completion(InnerModuleEvaluation(module, stack, 0)).
1296 bool ok
= InnerModuleEvaluation(cx
, module
, &stack
, 0, &ignored
);
1298 // Step 9. f result is an abrupt completion, then:
1300 // Attempt to take any pending exception, but make sure we still handle
1301 // uncatchable exceptions.
1302 Rooted
<Value
> error(cx
);
1303 if (cx
->isExceptionPending()) {
1304 std::ignore
= cx
->getPendingException(&error
);
1305 cx
->clearPendingException();
1308 // Step 9.a. For each Cyclic Module Record m of stack, do
1309 for (ModuleObject
* m
: stack
) {
1310 // Step 9.a.i. Assert: m.[[Status]] is evaluating.
1311 MOZ_ASSERT(m
->status() == ModuleStatus::Evaluating
);
1313 // Step 9.a.ii. Set m.[[Status]] to evaluated.
1314 // Step 9.a.iii. Set m.[[EvaluationError]] to result.
1315 m
->setEvaluationError(error
);
1318 // Handle OOM when appending to the stack or over-recursion errors.
1319 if (stack
.empty() && !module
->hadEvaluationError()) {
1320 module
->setEvaluationError(error
);
1323 // Step 9.b. Assert: module.[[Status]] is evaluated.
1324 MOZ_ASSERT(module
->status() == ModuleStatus::Evaluated
);
1326 // Step 9.c. Assert: module.[[EvaluationError]] is result.
1327 MOZ_ASSERT(module
->evaluationError() == error
);
1329 // Step 9.d. Perform ! Call(capability.[[Reject]], undefined,
1330 // result.[[Value]]).
1331 if (!ModuleObject::topLevelCapabilityReject(cx
, module
, error
)) {
1336 // Step 10.a. Assert: module.[[Status]] is evaluating-async or evaluated.
1337 MOZ_ASSERT(module
->status() == ModuleStatus::EvaluatingAsync
||
1338 module
->status() == ModuleStatus::Evaluated
);
1340 // Step 10.b. Assert: module.[[EvaluationError]] is empty.
1341 MOZ_ASSERT(!module
->hadEvaluationError());
1343 // Step 10.c. If module.[[AsyncEvaluation]] is false, then:
1344 if (module
->status() == ModuleStatus::Evaluated
) {
1345 // Step 10.c.ii. Perform ! Call(capability.[[Resolve]], undefined,
1347 if (!ModuleObject::topLevelCapabilityResolve(cx
, module
)) {
1352 // Step 10.d. Assert: stack is empty.
1353 MOZ_ASSERT(stack
.empty());
1356 // Step 11. Return capability.[[Promise]].
1357 result
.set(ObjectValue(*capability
));
1361 // https://tc39.es/ecma262/#sec-innermoduleevaluation
1362 // 16.2.1.5.2.1 InnerModuleEvaluation
1363 static bool InnerModuleEvaluation(JSContext
* cx
, Handle
<ModuleObject
*> module
,
1364 MutableHandle
<ModuleVector
> stack
,
1365 size_t index
, size_t* indexOut
) {
1366 // Step 2. If module.[[Status]] is evaluating-async or evaluated, then:
1367 if (module
->status() == ModuleStatus::EvaluatingAsync
||
1368 module
->status() == ModuleStatus::Evaluated
) {
1369 // Step 2.a. If module.[[EvaluationError]] is empty, return index.
1370 if (!module
->hadEvaluationError()) {
1375 // Step 2.b. Otherwise, return ? module.[[EvaluationError]].
1376 Rooted
<Value
> error(cx
, module
->evaluationError());
1377 cx
->setPendingException(error
, ShouldCaptureStack::Maybe
);
1381 // Step 3. If module.[[Status]] is evaluating, return index.
1382 if (module
->status() == ModuleStatus::Evaluating
) {
1387 // Step 4. Assert: module.[[Status]] is linked.
1388 MOZ_ASSERT(module
->status() == ModuleStatus::Linked
);
1390 // Step 10. Append module to stack.
1391 // Do this before changing the status so that we can recover on failure.
1392 if (!stack
.append(module
)) {
1393 ReportOutOfMemory(cx
);
1397 // Step 5. Set module.[[Status]] to evaluating.
1398 module
->setStatus(ModuleStatus::Evaluating
);
1400 // Step 6. Set module.[[DFSIndex]] to index.
1401 module
->setDfsIndex(index
);
1403 // Step 7. Set module.[[DFSAncestorIndex]] to index.
1404 module
->setDfsAncestorIndex(index
);
1406 // Step 8. Set module.[[PendingAsyncDependencies]] to 0.
1407 module
->setPendingAsyncDependencies(0);
1409 // Step 9. Set index to index + 1.
1412 // Step 11. For each String required of module.[[RequestedModules]], do:
1413 Rooted
<ModuleRequestObject
*> required(cx
);
1414 Rooted
<ModuleObject
*> requiredModule(cx
);
1415 for (const RequestedModule
& request
: module
->requestedModules()) {
1416 required
= request
.moduleRequest();
1418 // Step 11.a. Let requiredModule be ! HostResolveImportedModule(module,
1420 // Step 11.b. NOTE: Link must be completed successfully prior to invoking
1421 // this method, so every requested module is guaranteed to
1422 // resolve successfully.
1424 HostResolveImportedModule(cx
, module
, required
, ModuleStatus::Linked
);
1425 if (!requiredModule
) {
1429 // Step 11.c. Set index to ? InnerModuleEvaluation(requiredModule, stack,
1431 if (!InnerModuleEvaluation(cx
, requiredModule
, stack
, index
, &index
)) {
1435 // Step 11.d. If requiredModule is a Cyclic Module Record, then:
1436 // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating,
1437 // evaluating-async, or evaluated.
1438 MOZ_ASSERT(requiredModule
->status() == ModuleStatus::Evaluating
||
1439 requiredModule
->status() == ModuleStatus::EvaluatingAsync
||
1440 requiredModule
->status() == ModuleStatus::Evaluated
);
1442 // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and only
1443 // if requiredModule is in stack.
1444 MOZ_ASSERT((requiredModule
->status() == ModuleStatus::Evaluating
) ==
1445 ContainsElement(stack
, requiredModule
));
1447 // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then:
1448 if (requiredModule
->status() == ModuleStatus::Evaluating
) {
1449 // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to
1450 // min(module.[[DFSAncestorIndex]],
1451 // requiredModule.[[DFSAncestorIndex]]).
1452 module
->setDfsAncestorIndex(std::min(module
->dfsAncestorIndex(),
1453 requiredModule
->dfsAncestorIndex()));
1455 // Step 11.d.iv. Else:
1456 // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]].
1457 requiredModule
= requiredModule
->getCycleRoot();
1459 // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async
1461 MOZ_ASSERT(requiredModule
->status() >= ModuleStatus::EvaluatingAsync
||
1462 requiredModule
->status() == ModuleStatus::Evaluated
);
1464 // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty,
1465 // return ? requiredModule.[[EvaluationError]].
1466 if (requiredModule
->hadEvaluationError()) {
1467 Rooted
<Value
> error(cx
, requiredModule
->evaluationError());
1468 cx
->setPendingException(error
, ShouldCaptureStack::Maybe
);
1473 // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then:
1474 if (requiredModule
->isAsyncEvaluating() &&
1475 requiredModule
->status() != ModuleStatus::Evaluated
) {
1476 // Step 11.d.v.2. Append module to requiredModule.[[AsyncParentModules]].
1477 if (!ModuleObject::appendAsyncParentModule(cx
, requiredModule
, module
)) {
1481 // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to
1482 // module.[[PendingAsyncDependencies]] + 1.
1483 module
->setPendingAsyncDependencies(module
->pendingAsyncDependencies() +
1488 // Step 12. If module.[[PendingAsyncDependencies]] > 0 or module.[[HasTLA]] is
1490 if (module
->pendingAsyncDependencies() > 0 || module
->hasTopLevelAwait()) {
1491 // Step 12.a. Assert: module.[[AsyncEvaluation]] is false and was never
1492 // previously set to true.
1493 MOZ_ASSERT(!module
->isAsyncEvaluating());
1495 // Step 12.b. Set module.[[AsyncEvaluation]] to true.
1496 // Step 12.c. NOTE: The order in which module records have their
1497 // [[AsyncEvaluation]] fields transition to true is
1498 // significant. (See 16.2.1.5.2.4.)
1499 module
->setAsyncEvaluating();
1501 // Step 12.d. If module.[[PendingAsyncDependencies]] is 0, perform
1502 // ExecuteAsyncModule(module).
1503 if (module
->pendingAsyncDependencies() == 0) {
1504 if (!ExecuteAsyncModule(cx
, module
)) {
1509 // Step 13. Otherwise, perform ? module.ExecuteModule().
1510 if (!ModuleObject::execute(cx
, module
)) {
1515 // Step 14. Assert: module occurs exactly once in stack.
1516 MOZ_ASSERT(CountElements(stack
, module
) == 1);
1518 // Step 15. Assert: module.[[DFSAncestorIndex]] <= module.[[DFSIndex]].
1519 MOZ_ASSERT(module
->dfsAncestorIndex() <= module
->dfsIndex());
1521 // Step 16. If module.[[DFSAncestorIndex]] = module.[[DFSIndex]], then:
1522 if (module
->dfsAncestorIndex() == module
->dfsIndex()) {
1523 // Step 16.a. Let done be false.
1526 // Step 16.b. Repeat, while done is false:
1528 // Step 16.b.i. Let requiredModule be the last element in stack.
1529 // Step 16.b.ii. Remove the last element of stack.
1530 requiredModule
= stack
.popCopy();
1532 // Step 16.b.iv. If requiredModule.[[AsyncEvaluation]] is false, set
1533 // requiredModule.[[Status]] to evaluated.
1534 if (!requiredModule
->isAsyncEvaluating()) {
1535 requiredModule
->setStatus(ModuleStatus::Evaluated
);
1537 // Step 16.b.v. Otherwise, set requiredModule.[[Status]] to
1538 // evaluating-async.
1539 requiredModule
->setStatus(ModuleStatus::EvaluatingAsync
);
1542 // Step 16.b.vi. If requiredModule and module are the same Module Record,
1543 // set done to true.
1544 done
= requiredModule
== module
;
1546 // Step 16.b.vii. Set requiredModule.[[CycleRoot]] to module.
1547 requiredModule
->setCycleRoot(module
);
1551 // Step 17. Return index.
1556 // https://tc39.es/ecma262/#sec-execute-async-module
1557 // ES2023 16.2.1.5.2.2 ExecuteAsyncModule
1558 static bool ExecuteAsyncModule(JSContext
* cx
, Handle
<ModuleObject
*> module
) {
1559 // Step 1. Assert: module.[[Status]] is evaluating or evaluating-async.
1560 MOZ_ASSERT(module
->status() == ModuleStatus::Evaluating
||
1561 module
->status() == ModuleStatus::EvaluatingAsync
);
1563 // Step 2. Assert: module.[[HasTLA]] is true.
1564 MOZ_ASSERT(module
->hasTopLevelAwait());
1566 // Steps 3 - 8 are performed by the AsyncAwait opcode.
1568 // Step 9. Perform ! module.ExecuteModule(capability).
1569 // Step 10. Return unused.
1570 return ModuleObject::execute(cx
, module
);
1573 // https://tc39.es/ecma262/#sec-gather-available-ancestors
1574 // ES2023 16.2.1.5.2.3 GatherAvailableAncestors
1575 static bool GatherAvailableModuleAncestors(
1576 JSContext
* cx
, Handle
<ModuleObject
*> module
,
1577 MutableHandle
<ModuleVector
> execList
) {
1578 MOZ_ASSERT(module
->status() == ModuleStatus::EvaluatingAsync
);
1580 // Step 1. For each Cyclic Module Record m of module.[[AsyncParentModules]],
1582 Rooted
<ListObject
*> asyncParentModules(cx
, module
->asyncParentModules());
1583 Rooted
<ModuleObject
*> m(cx
);
1584 for (uint32_t i
= 0; i
!= asyncParentModules
->length(); i
++) {
1585 m
= &asyncParentModules
->getDenseElement(i
).toObject().as
<ModuleObject
>();
1587 // Step 1.a. If execList does not contain m and
1588 // m.[[CycleRoot]].[[EvaluationError]] is empty, then:
1590 // Note: we also check whether m.[[EvaluationError]] is empty since an error
1591 // in synchronous execution can prevent the CycleRoot field from being set.
1592 if (!m
->hadEvaluationError() && !m
->getCycleRoot()->hadEvaluationError() &&
1593 !ContainsElement(execList
, m
)) {
1594 // Step 1.a.i. Assert: m.[[Status]] is evaluating-async.
1595 MOZ_ASSERT(m
->status() == ModuleStatus::EvaluatingAsync
);
1597 // Step 1.a.ii. Assert: m.[[EvaluationError]] is empty.
1598 MOZ_ASSERT(!m
->hadEvaluationError());
1600 // Step 1.a.iii. Assert: m.[[AsyncEvaluation]] is true.
1601 MOZ_ASSERT(m
->isAsyncEvaluating());
1603 // Step 1.a.iv. Assert: m.[[PendingAsyncDependencies]] > 0.
1604 MOZ_ASSERT(m
->pendingAsyncDependencies() > 0);
1606 // Step 1.a.v. Set m.[[PendingAsyncDependencies]] to
1607 // m.[[PendingAsyncDependencies]] - 1.
1608 m
->setPendingAsyncDependencies(m
->pendingAsyncDependencies() - 1);
1610 // Step 1.a.vi. If m.[[PendingAsyncDependencies]] = 0, then:
1611 if (m
->pendingAsyncDependencies() == 0) {
1612 // Step 1.a.vi.1. Append m to execList.
1613 if (!execList
.append(m
)) {
1617 // Step 1.a.vi.2. If m.[[HasTLA]] is false, perform
1618 // GatherAvailableAncestors(m, execList).
1619 if (!m
->hasTopLevelAwait() &&
1620 !GatherAvailableModuleAncestors(cx
, m
, execList
)) {
1627 // Step 2. Return unused.
1631 struct EvalOrderComparator
{
1632 bool operator()(ModuleObject
* a
, ModuleObject
* b
, bool* lessOrEqualp
) {
1633 int32_t result
= int32_t(a
->getAsyncEvaluatingPostOrder()) -
1634 int32_t(b
->getAsyncEvaluatingPostOrder());
1635 *lessOrEqualp
= (result
<= 0);
1640 static void RejectExecutionWithPendingException(JSContext
* cx
,
1641 Handle
<ModuleObject
*> module
) {
1642 // If there is no exception pending then we have been interrupted or have
1643 // OOM'd and all bets are off. We reject the execution by throwing
1644 // undefined. Not much more we can do.
1645 RootedValue
exception(cx
);
1646 if (cx
->isExceptionPending()) {
1647 std::ignore
= cx
->getPendingException(&exception
);
1649 cx
->clearPendingException();
1650 AsyncModuleExecutionRejected(cx
, module
, exception
);
1653 // https://tc39.es/ecma262/#sec-async-module-execution-fulfilled
1654 // ES2023 16.2.1.5.2.4 AsyncModuleExecutionFulfilled
1655 void js::AsyncModuleExecutionFulfilled(JSContext
* cx
,
1656 Handle
<ModuleObject
*> module
) {
1657 // Step 1. If module.[[Status]] is evaluated, then:
1658 if (module
->status() == ModuleStatus::Evaluated
) {
1659 // Step 1.a. Assert: module.[[EvaluationError]] is not empty.
1660 MOZ_ASSERT(module
->hadEvaluationError());
1662 // Step 1.b. Return unused.
1666 // Step 2. Assert: module.[[Status]] is evaluating-async.
1667 MOZ_ASSERT(module
->status() == ModuleStatus::EvaluatingAsync
);
1669 // Step 3. Assert: module.[[AsyncEvaluation]] is true.
1670 MOZ_ASSERT(module
->isAsyncEvaluating());
1672 // Step 4. Assert: module.[[EvaluationError]] is empty.
1673 MOZ_ASSERT(!module
->hadEvaluationError());
1675 // The following steps are performed in a different order from the
1676 // spec. Gather available module ancestors before mutating the module object
1677 // as this can fail in our implementation.
1679 // Step 8. Let execList be a new empty List.
1680 Rooted
<ModuleVector
> execList(cx
);
1682 // Step 9. Perform GatherAvailableAncestors(module, execList).
1683 if (!GatherAvailableModuleAncestors(cx
, module
, &execList
)) {
1684 RejectExecutionWithPendingException(cx
, module
);
1688 // Step 10. Let sortedExecList be a List whose elements are the elements of
1689 // execList, in the order in which they had their [[AsyncEvaluation]]
1690 // fields set to true in InnerModuleEvaluation.
1692 Rooted
<ModuleVector
> scratch(cx
);
1693 if (!scratch
.resize(execList
.length())) {
1694 ReportOutOfMemory(cx
);
1695 RejectExecutionWithPendingException(cx
, module
);
1699 MOZ_ALWAYS_TRUE(MergeSort(execList
.begin(), execList
.length(),
1700 scratch
.begin(), EvalOrderComparator()));
1702 // Step 11. Assert: All elements of sortedExecList have their
1703 // [[AsyncEvaluation]] field set to true,
1704 // [[PendingAsyncDependencies]] field set to 0, and
1705 // [[EvaluationError]] field set to empty.
1707 for (ModuleObject
* m
: execList
) {
1708 MOZ_ASSERT(m
->isAsyncEvaluating());
1709 MOZ_ASSERT(m
->pendingAsyncDependencies() == 0);
1710 MOZ_ASSERT(!m
->hadEvaluationError());
1714 // Return to original order of steps.
1716 ModuleObject::onTopLevelEvaluationFinished(module
);
1718 // Step 6. Set module.[[Status]] to evaluated.
1719 module
->setStatus(ModuleStatus::Evaluated
);
1720 module
->clearAsyncEvaluatingPostOrder();
1722 // Step 7. If module.[[TopLevelCapability]] is not empty, then:
1723 if (module
->hasTopLevelCapability()) {
1724 // Step 7.a. Assert: module.[[CycleRoot]] is module.
1725 MOZ_ASSERT(module
->getCycleRoot() == module
);
1727 // Step 7.b. Perform ! Call(module.[[TopLevelCapability]].[[Resolve]],
1728 // undefined, undefined).
1729 if (!ModuleObject::topLevelCapabilityResolve(cx
, module
)) {
1730 // If Resolve fails, there's nothing more we can do here.
1731 cx
->clearPendingException();
1735 // Step 12. For each Cyclic Module Record m of sortedExecList, do:
1736 Rooted
<ModuleObject
*> m(cx
);
1737 for (ModuleObject
* obj
: execList
) {
1740 // Step 12.a. If m.[[Status]] is evaluated, then:
1741 if (m
->status() == ModuleStatus::Evaluated
) {
1742 // Step 12.a.i. Assert: m.[[EvaluationError]] is not empty.
1743 MOZ_ASSERT(m
->hadEvaluationError());
1744 } else if (m
->hasTopLevelAwait()) {
1745 // Step 12.b. Else if m.[[HasTLA]] is true, then:
1746 // Step 12.b.i. Perform ExecuteAsyncModule(m).
1747 MOZ_ALWAYS_TRUE(ExecuteAsyncModule(cx
, m
));
1750 // Step 12.c.i. Let result be m.ExecuteModule().
1751 bool ok
= ModuleObject::execute(cx
, m
);
1753 // Step 12.c.ii. If result is an abrupt completion, then:
1755 // Step 12.c.ii.1. Perform AsyncModuleExecutionRejected(m,
1756 // result.[[Value]]).
1757 RejectExecutionWithPendingException(cx
, m
);
1759 // Step 12.c.iii. Else:
1760 // Step 12.c.iii.1. Set m.[[Status]] to evaluated.
1761 m
->setStatus(ModuleStatus::Evaluated
);
1762 m
->clearAsyncEvaluatingPostOrder();
1764 // Step 12.c.iii.2. If m.[[TopLevelCapability]] is not empty, then:
1765 if (m
->hasTopLevelCapability()) {
1766 // Step 12.c.iii.2.a. Assert: m.[[CycleRoot]] is m.
1767 MOZ_ASSERT(m
->getCycleRoot() == m
);
1769 // Step 12.c.iii.2.b. Perform !
1770 // Call(m.[[TopLevelCapability]].[[Resolve]],
1771 // undefined, undefined).
1772 if (!ModuleObject::topLevelCapabilityResolve(cx
, m
)) {
1773 // If Resolve fails, there's nothing more we can do here.
1774 cx
->clearPendingException();
1781 // Step 13. Return unused.
1784 // https://tc39.es/ecma262/#sec-async-module-execution-rejected
1785 // ES2023 16.2.1.5.2.5 AsyncModuleExecutionRejected
1786 void js::AsyncModuleExecutionRejected(JSContext
* cx
,
1787 Handle
<ModuleObject
*> module
,
1788 HandleValue error
) {
1789 // Step 1. If module.[[Status]] is evaluated, then:
1790 if (module
->status() == ModuleStatus::Evaluated
) {
1791 // Step 1.a. Assert: module.[[EvaluationError]] is not empty
1792 MOZ_ASSERT(module
->hadEvaluationError());
1794 // Step 1.b. Return unused.
1798 // Step 2. Assert: module.[[Status]] is evaluating-async.
1799 MOZ_ASSERT(module
->status() == ModuleStatus::EvaluatingAsync
);
1801 // Step 3. Assert: module.[[AsyncEvaluation]] is true.
1802 MOZ_ASSERT(module
->isAsyncEvaluating());
1804 // Step 4. 4. Assert: module.[[EvaluationError]] is empty.
1805 MOZ_ASSERT(!module
->hadEvaluationError());
1807 ModuleObject::onTopLevelEvaluationFinished(module
);
1809 // Step 5. Set module.[[EvaluationError]] to ThrowCompletion(error).
1810 module
->setEvaluationError(error
);
1812 // Step 6. Set module.[[Status]] to evaluated.
1813 MOZ_ASSERT(module
->status() == ModuleStatus::Evaluated
);
1815 module
->clearAsyncEvaluatingPostOrder();
1817 // Step 7. For each Cyclic Module Record m of module.[[AsyncParentModules]],
1819 Rooted
<ListObject
*> parents(cx
, module
->asyncParentModules());
1820 Rooted
<ModuleObject
*> parent(cx
);
1821 for (uint32_t i
= 0; i
< parents
->length(); i
++) {
1822 parent
= &parents
->get(i
).toObject().as
<ModuleObject
>();
1824 // Step 7.a. Perform AsyncModuleExecutionRejected(m, error).
1825 AsyncModuleExecutionRejected(cx
, parent
, error
);
1828 // Step 8. If module.[[TopLevelCapability]] is not empty, then:
1829 if (module
->hasTopLevelCapability()) {
1830 // Step 8.a. Assert: module.[[CycleRoot]] is module.
1831 MOZ_ASSERT(module
->getCycleRoot() == module
);
1833 // Step 8.b. Perform ! Call(module.[[TopLevelCapability]].[[Reject]],
1834 // undefined, error).
1835 if (!ModuleObject::topLevelCapabilityReject(cx
, module
, error
)) {
1836 // If Reject fails, there's nothing more we can do here.
1837 cx
->clearPendingException();
1841 // Step 9. Return unused.