summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Modules.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/vm/Modules.cpp')
-rw-r--r--js/src/vm/Modules.cpp269
1 files changed, 203 insertions, 66 deletions
diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp
index 87546cf280..f461e7bec1 100644
--- a/js/src/vm/Modules.cpp
+++ b/js/src/vm/Modules.cpp
@@ -15,7 +15,9 @@
#include "jstypes.h" // JS_PUBLIC_API
+#include "builtin/JSON.h" // js::ParseJSONWithReviver
#include "builtin/ModuleObject.h" // js::FinishDynamicModuleImport, js::{,Requested}ModuleObject
+#include "builtin/Promise.h" // js::CreatePromiseObjectForAsync, js::AsyncFunctionReturned
#include "ds/Sort.h"
#include "frontend/BytecodeCompiler.h" // js::frontend::CompileModule
#include "frontend/FrontendContext.h" // js::AutoReportFrontendContext
@@ -33,6 +35,8 @@
#include "vm/JSAtomUtils-inl.h" // AtomToId
#include "vm/JSContext-inl.h" // JSContext::{c,releaseC}heck
+#include "vm/JSObject-inl.h"
+#include "vm/NativeObject-inl.h"
using namespace js;
@@ -120,6 +124,46 @@ JS_PUBLIC_API JSObject* JS::CompileModule(JSContext* cx,
return CompileModuleHelper(cx, options, srcBuf);
}
+JS_PUBLIC_API JSObject* JS::CompileJsonModule(
+ JSContext* cx, const ReadOnlyCompileOptions& options,
+ SourceText<char16_t>& srcBuf) {
+ MOZ_ASSERT(!cx->zone()->isAtomsZone());
+ AssertHeapIsIdle();
+ CHECK_THREAD(cx);
+
+ JS::RootedValue jsonValue(cx);
+ auto charRange =
+ mozilla::Range<const char16_t>(srcBuf.get(), srcBuf.length());
+ if (!js::ParseJSONWithReviver(cx, charRange, NullHandleValue, &jsonValue)) {
+ return nullptr;
+ }
+
+ Rooted<ExportNameVector> exportNames(cx);
+ if (!exportNames.reserve(1)) {
+ return nullptr;
+ }
+ exportNames.infallibleAppend(cx->names().default_);
+
+ Rooted<ModuleObject*> moduleObject(
+ cx, ModuleObject::createSynthetic(cx, &exportNames));
+ if (!moduleObject) {
+ return nullptr;
+ }
+
+ Rooted<GCVector<Value>> exportValues(cx, GCVector<Value>(cx));
+ if (!exportValues.reserve(1)) {
+ return nullptr;
+ }
+ exportValues.infallibleAppend(jsonValue);
+
+ if (!ModuleObject::createSyntheticEnvironment(cx, moduleObject,
+ exportValues)) {
+ return nullptr;
+ }
+
+ return moduleObject;
+}
+
JS_PUBLIC_API void JS::SetModulePrivate(JSObject* module, const Value& value) {
JSRuntime* rt = module->zone()->runtimeFromMainThread();
module->as<ModuleObject>().scriptSourceObject()->setPrivate(rt, value);
@@ -150,6 +194,10 @@ JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx,
CHECK_THREAD(cx);
cx->releaseCheck(moduleRecord);
+ if (moduleRecord.as<ModuleObject>()->hasSyntheticModuleFields()) {
+ return SyntheticModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval);
+ }
+
return js::ModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval);
}
@@ -312,6 +360,10 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
Handle<JSAtom*> exportName,
MutableHandle<ResolveSet> resolveSet,
MutableHandle<Value> result);
+static bool SyntheticModuleResolveExport(JSContext* cx,
+ Handle<ModuleObject*> module,
+ Handle<JSAtom*> exportName,
+ MutableHandle<Value> result);
static ModuleNamespaceObject* ModuleNamespaceCreate(
JSContext* cx, Handle<ModuleObject*> module,
MutableHandle<UniquePtr<ExportNameVector>> exports);
@@ -345,7 +397,7 @@ static const char* ModuleStatusName(ModuleStatus status) {
}
}
-static bool ContainsElement(Handle<ExportNameVector> list, JSAtom* atom) {
+static bool ContainsElement(const ExportNameVector& list, JSAtom* atom) {
for (JSAtom* a : list) {
if (a == atom) {
return true;
@@ -378,6 +430,20 @@ static size_t CountElements(Handle<ModuleVector> stack, ModuleObject* module) {
}
#endif
+// https://tc39.es/proposal-json-modules/#sec-smr-getexportednames
+static bool SyntheticModuleGetExportedNames(
+ JSContext* cx, Handle<ModuleObject*> module,
+ MutableHandle<ExportNameVector> exportedNames) {
+ MOZ_ASSERT(exportedNames.empty());
+
+ if (!exportedNames.appendAll(module->syntheticExportNames())) {
+ ReportOutOfMemory(cx);
+ return false;
+ }
+
+ return true;
+}
+
// https://tc39.es/ecma262/#sec-getexportednames
// ES2023 16.2.1.6.2 GetExportedNames
static bool ModuleGetExportedNames(
@@ -387,6 +453,10 @@ static bool ModuleGetExportedNames(
// Step 4. Let exportedNames be a new empty List.
MOZ_ASSERT(exportedNames.empty());
+ if (module->hasSyntheticModuleFields()) {
+ return SyntheticModuleGetExportedNames(cx, module, exportedNames);
+ }
+
// Step 2. If exportStarSet contains module, then:
if (exportStarSet.has(module)) {
// Step 2.a. We've reached the starting point of an export * circularity.
@@ -482,12 +552,10 @@ static ModuleObject* HostResolveImportedModule(
if (!requestedModule) {
return nullptr;
}
-
if (requestedModule->status() < expectedMinimumStatus) {
ThrowUnexpectedModuleStatus(cx, requestedModule->status());
return nullptr;
}
-
return requestedModule;
}
@@ -510,6 +578,10 @@ static ModuleObject* HostResolveImportedModule(
bool js::ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
Handle<JSAtom*> exportName,
MutableHandle<Value> result) {
+ if (module->hasSyntheticModuleFields()) {
+ return ::SyntheticModuleResolveExport(cx, module, exportName, result);
+ }
+
// Step 1. If resolveSet is not present, set resolveSet to a new empty List.
Rooted<ResolveSet> resolveSet(cx);
@@ -683,6 +755,22 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
return true;
}
+// https://tc39.es/proposal-json-modules/#sec-smr-resolveexport
+static bool SyntheticModuleResolveExport(JSContext* cx,
+ Handle<ModuleObject*> module,
+ Handle<JSAtom*> exportName,
+ MutableHandle<Value> result) {
+ // Step 2. If module.[[ExportNames]] does not contain exportName, return null.
+ if (!ContainsElement(module->syntheticExportNames(), exportName)) {
+ result.setNull();
+ return true;
+ }
+
+ // Step 3. Return ResolvedBinding Record { [[Module]]: module,
+ // [[BindingName]]: exportName }.
+ return CreateResolvedBindingObject(cx, module, exportName, result);
+}
+
// https://tc39.es/ecma262/#sec-getmodulenamespace
// ES2023 16.2.1.10 GetModuleNamespace
ModuleNamespaceObject* js::GetOrCreateModuleNamespace(
@@ -1097,6 +1185,14 @@ bool js::ModuleLink(JSContext* cx, Handle<ModuleObject*> module) {
static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
MutableHandle<ModuleVector> stack, size_t index,
size_t* indexOut) {
+ // Step 1. If module is not a Cyclic Module Record, then
+ if (!module->hasCyclicModuleFields()) {
+ // Step 1.a. Perform ? module.Link(). (Skipped)
+ // Step 2.b. Return index.
+ *indexOut = index;
+ return true;
+ }
+
// Step 2. If module.[[Status]] is linking, linked, evaluating-async, or
// evaluated, then:
if (module->status() == ModuleStatus::Linking ||
@@ -1155,25 +1251,29 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
}
// Step 9.c. If requiredModule is a Cyclic Module Record, then:
- // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking, linked,
- // evaluating-async, or evaluated.
- MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking ||
- requiredModule->status() == ModuleStatus::Linked ||
- requiredModule->status() == ModuleStatus::EvaluatingAsync ||
- requiredModule->status() == ModuleStatus::Evaluated);
-
- // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only if
- // requiredModule is in stack.
- MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) ==
- ContainsElement(stack, requiredModule));
-
- // Step 9.c.iii. If requiredModule.[[Status]] is linking, then:
- if (requiredModule->status() == ModuleStatus::Linking) {
- // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to
- // min(module.[[DFSAncestorIndex]],
- // requiredModule.[[DFSAncestorIndex]]).
- module->setDfsAncestorIndex(std::min(module->dfsAncestorIndex(),
- requiredModule->dfsAncestorIndex()));
+ if (requiredModule->hasCyclicModuleFields()) {
+ // Step 9.c.i. Assert: requiredModule.[[Status]] is either linking,
+ // linked,
+ // evaluating-async, or evaluated.
+ MOZ_ASSERT(requiredModule->status() == ModuleStatus::Linking ||
+ requiredModule->status() == ModuleStatus::Linked ||
+ requiredModule->status() == ModuleStatus::EvaluatingAsync ||
+ requiredModule->status() == ModuleStatus::Evaluated);
+
+ // Step 9.c.ii. Assert: requiredModule.[[Status]] is linking if and only
+ // if
+ // requiredModule is in stack.
+ MOZ_ASSERT((requiredModule->status() == ModuleStatus::Linking) ==
+ ContainsElement(stack, requiredModule));
+
+ // Step 9.c.iii. If requiredModule.[[Status]] is linking, then:
+ if (requiredModule->status() == ModuleStatus::Linking) {
+ // Step 9.c.iii.1. Set module.[[DFSAncestorIndex]] to
+ // min(module.[[DFSAncestorIndex]],
+ // requiredModule.[[DFSAncestorIndex]]).
+ module->setDfsAncestorIndex(std::min(
+ module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex()));
+ }
}
}
@@ -1213,6 +1313,28 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
return true;
}
+bool js::SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
+ MutableHandle<Value> result) {
+ // Steps 1-12 happens elsewhere in the engine.
+
+ // Step 13. Let pc be ! NewPromiseCapability(%Promise%).
+ Rooted<PromiseObject*> resultPromise(cx, CreatePromiseObjectForAsync(cx));
+ if (!resultPromise) {
+ return false;
+ }
+
+ // Step 14. IfAbruptRejectPromise(result, pc) (Skipped)
+
+ // 15. Perform ! pc.[[Resolve]](result).
+ if (!AsyncFunctionReturned(cx, resultPromise, result)) {
+ return false;
+ }
+
+ // 16. Return pc.[[Promise]].
+ result.set(ObjectValue(*resultPromise));
+ return true;
+}
+
// https://tc39.es/ecma262/#sec-moduleevaluation
// ES2023 16.2.1.5.2 Evaluate
bool js::ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
@@ -1349,6 +1471,17 @@ bool js::ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
MutableHandle<ModuleVector> stack,
size_t index, size_t* indexOut) {
+ // Step 1: If module is not a Cyclic Module Record, then
+ if (!module->hasCyclicModuleFields()) {
+ // Step 1.a. Let promise be ! module.Evaluate(). (Skipped)
+ // Step 1.b. Assert: promise.[[PromiseState]] is not pending. (Skipped)
+ // Step 1.c. If promise.[[PromiseState]] is rejected, then (Skipped)
+ // Step 1.c.i Return ThrowCompletion(promise.[[PromiseResult]]). (Skipped)
+ // Step 1.d. Return index.
+ *indexOut = index;
+ return true;
+ }
+
// Step 2. If module.[[Status]] is evaluating-async or evaluated, then:
if (module->status() == ModuleStatus::EvaluatingAsync ||
module->status() == ModuleStatus::Evaluated) {
@@ -1419,55 +1552,59 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle<ModuleObject*> module,
}
// Step 11.d. If requiredModule is a Cyclic Module Record, then:
- // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating,
- // evaluating-async, or evaluated.
- MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating ||
- requiredModule->status() == ModuleStatus::EvaluatingAsync ||
- requiredModule->status() == ModuleStatus::Evaluated);
-
- // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and only
- // if requiredModule is in stack.
- MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) ==
- ContainsElement(stack, requiredModule));
-
- // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then:
- if (requiredModule->status() == ModuleStatus::Evaluating) {
- // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to
- // min(module.[[DFSAncestorIndex]],
- // requiredModule.[[DFSAncestorIndex]]).
- module->setDfsAncestorIndex(std::min(module->dfsAncestorIndex(),
- requiredModule->dfsAncestorIndex()));
- } else {
- // Step 11.d.iv. Else:
- // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]].
- requiredModule = requiredModule->getCycleRoot();
-
- // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async
- // or evaluated.
- MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync ||
+ if (requiredModule->hasCyclicModuleFields()) {
+ // Step 11.d.i. Assert: requiredModule.[[Status]] is either evaluating,
+ // evaluating-async, or evaluated.
+ MOZ_ASSERT(requiredModule->status() == ModuleStatus::Evaluating ||
+ requiredModule->status() == ModuleStatus::EvaluatingAsync ||
requiredModule->status() == ModuleStatus::Evaluated);
- // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty,
- // return ? requiredModule.[[EvaluationError]].
- if (requiredModule->hadEvaluationError()) {
- Rooted<Value> error(cx, requiredModule->evaluationError());
- cx->setPendingException(error, ShouldCaptureStack::Maybe);
- return false;
+ // Step 11.d.ii. Assert: requiredModule.[[Status]] is evaluating if and
+ // only if requiredModule is in stack.
+ MOZ_ASSERT((requiredModule->status() == ModuleStatus::Evaluating) ==
+ ContainsElement(stack, requiredModule));
+
+ // Step 11.d.iii. If requiredModule.[[Status]] is evaluating, then:
+ if (requiredModule->status() == ModuleStatus::Evaluating) {
+ // Step 11.d.iii.1. Set module.[[DFSAncestorIndex]] to
+ // min(module.[[DFSAncestorIndex]],
+ // requiredModule.[[DFSAncestorIndex]]).
+ module->setDfsAncestorIndex(std::min(
+ module->dfsAncestorIndex(), requiredModule->dfsAncestorIndex()));
+ } else {
+ // Step 11.d.iv. Else:
+ // Step 11.d.iv.1. Set requiredModule to requiredModule.[[CycleRoot]].
+ requiredModule = requiredModule->getCycleRoot();
+
+ // Step 11.d.iv.2. Assert: requiredModule.[[Status]] is evaluating-async
+ // or evaluated.
+ MOZ_ASSERT(requiredModule->status() >= ModuleStatus::EvaluatingAsync ||
+ requiredModule->status() == ModuleStatus::Evaluated);
+
+ // Step 11.d.iv.3. If requiredModule.[[EvaluationError]] is not empty,
+ // return ? requiredModule.[[EvaluationError]].
+ if (requiredModule->hadEvaluationError()) {
+ Rooted<Value> error(cx, requiredModule->evaluationError());
+ cx->setPendingException(error, ShouldCaptureStack::Maybe);
+ return false;
+ }
}
- }
- // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then:
- if (requiredModule->isAsyncEvaluating() &&
- requiredModule->status() != ModuleStatus::Evaluated) {
- // Step 11.d.v.2. Append module to requiredModule.[[AsyncParentModules]].
- if (!ModuleObject::appendAsyncParentModule(cx, requiredModule, module)) {
- return false;
- }
+ // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then:
+ if (requiredModule->isAsyncEvaluating() &&
+ requiredModule->status() != ModuleStatus::Evaluated) {
+ // Step 11.d.v.2. Append module to
+ // requiredModule.[[AsyncParentModules]].
+ if (!ModuleObject::appendAsyncParentModule(cx, requiredModule,
+ module)) {
+ return false;
+ }
- // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to
- // module.[[PendingAsyncDependencies]] + 1.
- module->setPendingAsyncDependencies(module->pendingAsyncDependencies() +
- 1);
+ // Step 11.d.v.1. Set module.[[PendingAsyncDependencies]] to
+ // module.[[PendingAsyncDependencies]] + 1.
+ module->setPendingAsyncDependencies(module->pendingAsyncDependencies() +
+ 1);
+ }
}
}