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.cpp223
1 files changed, 150 insertions, 73 deletions
diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp
index f461e7bec1..917083a238 100644
--- a/js/src/vm/Modules.cpp
+++ b/js/src/vm/Modules.cpp
@@ -9,7 +9,8 @@
#include "vm/Modules.h"
#include "mozilla/Assertions.h" // MOZ_ASSERT
-#include "mozilla/Utf8.h" // mozilla::Utf8Unit
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Utf8.h" // mozilla::Utf8Unit
#include <stdint.h> // uint32_t
@@ -42,6 +43,12 @@ using namespace js;
using mozilla::Utf8Unit;
+static bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module);
+static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module,
+ MutableHandle<Value> result);
+static bool SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> module,
+ MutableHandle<Value> result);
+
////////////////////////////////////////////////////////////////////////////////
// Public API
JS_PUBLIC_API JS::ModuleResolveHook JS::GetModuleResolveHook(JSRuntime* rt) {
@@ -184,7 +191,7 @@ JS_PUBLIC_API bool JS::ModuleLink(JSContext* cx, Handle<JSObject*> moduleArg) {
CHECK_THREAD(cx);
cx->releaseCheck(moduleArg);
- return js::ModuleLink(cx, moduleArg.as<ModuleObject>());
+ return ::ModuleLink(cx, moduleArg.as<ModuleObject>());
}
JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx,
@@ -194,11 +201,17 @@ JS_PUBLIC_API bool JS::ModuleEvaluate(JSContext* cx,
CHECK_THREAD(cx);
cx->releaseCheck(moduleRecord);
+ cx->isEvaluatingModule++;
+ auto guard = mozilla::MakeScopeExit([cx] {
+ MOZ_ASSERT(cx->isEvaluatingModule != 0);
+ cx->isEvaluatingModule--;
+ });
+
if (moduleRecord.as<ModuleObject>()->hasSyntheticModuleFields()) {
return SyntheticModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval);
}
- return js::ModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval);
+ return ::ModuleEvaluate(cx, moduleRecord.as<ModuleObject>(), rval);
}
JS_PUBLIC_API bool JS::ThrowOnModuleEvaluationFailure(
@@ -327,6 +340,11 @@ JS_PUBLIC_API void JS::ClearModuleEnvironment(JSObject* moduleObj) {
}
}
+JS_PUBLIC_API bool JS::ModuleIsLinked(JSObject* moduleObj) {
+ AssertHeapIsIdle();
+ return moduleObj->as<ModuleObject>().status() != ModuleStatus::Unlinked;
+}
+
////////////////////////////////////////////////////////////////////////////////
// Internal implementation
@@ -356,14 +374,17 @@ static ModuleObject* HostResolveImportedModule(
JSContext* cx, Handle<ModuleObject*> module,
Handle<ModuleRequestObject*> moduleRequest,
ModuleStatus expectedMinimumStatus);
-static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
- Handle<JSAtom*> exportName,
- MutableHandle<ResolveSet> resolveSet,
- MutableHandle<Value> result);
+static bool CyclicModuleResolveExport(JSContext* cx,
+ Handle<ModuleObject*> module,
+ Handle<JSAtom*> exportName,
+ MutableHandle<ResolveSet> resolveSet,
+ MutableHandle<Value> result,
+ ModuleErrorInfo* errorInfoOut = nullptr);
static bool SyntheticModuleResolveExport(JSContext* cx,
Handle<ModuleObject*> module,
Handle<JSAtom*> exportName,
- MutableHandle<Value> result);
+ MutableHandle<Value> result,
+ ModuleErrorInfo* errorInfoOut);
static ModuleNamespaceObject* ModuleNamespaceCreate(
JSContext* cx, Handle<ModuleObject*> module,
MutableHandle<UniquePtr<ExportNameVector>> exports);
@@ -575,17 +596,20 @@ static ModuleObject* HostResolveImportedModule(
// - If the request is found to be ambiguous, the string `"ambiguous"` is
// returned.
//
-bool js::ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
- Handle<JSAtom*> exportName,
- MutableHandle<Value> result) {
+static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
+ Handle<JSAtom*> exportName,
+ MutableHandle<Value> result,
+ ModuleErrorInfo* errorInfoOut = nullptr) {
if (module->hasSyntheticModuleFields()) {
- return ::SyntheticModuleResolveExport(cx, module, exportName, result);
+ return SyntheticModuleResolveExport(cx, module, exportName, result,
+ errorInfoOut);
}
// Step 1. If resolveSet is not present, set resolveSet to a new empty List.
Rooted<ResolveSet> resolveSet(cx);
- return ::ModuleResolveExport(cx, module, exportName, &resolveSet, result);
+ return CyclicModuleResolveExport(cx, module, exportName, &resolveSet, result,
+ errorInfoOut);
}
static bool CreateResolvedBindingObject(JSContext* cx,
@@ -602,10 +626,12 @@ static bool CreateResolvedBindingObject(JSContext* cx,
return true;
}
-static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
- Handle<JSAtom*> exportName,
- MutableHandle<ResolveSet> resolveSet,
- MutableHandle<Value> result) {
+static bool CyclicModuleResolveExport(JSContext* cx,
+ Handle<ModuleObject*> module,
+ Handle<JSAtom*> exportName,
+ MutableHandle<ResolveSet> resolveSet,
+ MutableHandle<Value> result,
+ ModuleErrorInfo* errorInfoOut) {
// Step 2. For each Record { [[Module]], [[ExportName]] } r of resolveSet, do:
for (const auto& entry : resolveSet) {
// Step 2.a. If module and r.[[Module]] are the same Module Record and
@@ -614,6 +640,9 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
// Step 2.a.i. Assert: This is a circular import request.
// Step 2.a.ii. Return null.
result.setNull();
+ if (errorInfoOut) {
+ errorInfoOut->setCircularImport(cx, module);
+ }
return true;
}
}
@@ -669,8 +698,8 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
// importedModule.ResolveExport(e.[[ImportName]],
// resolveSet).
name = e.importName();
- return ModuleResolveExport(cx, importedModule, name, resolveSet,
- result);
+ return CyclicModuleResolveExport(cx, importedModule, name, resolveSet,
+ result, errorInfoOut);
}
}
}
@@ -683,6 +712,9 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
// Step 6.c. NOTE: A default export cannot be provided by an export * from
// "mod" declaration.
result.setNull();
+ if (errorInfoOut) {
+ errorInfoOut->setImportedModule(cx, module);
+ }
return true;
}
@@ -704,8 +736,8 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
// Step 8.b. Let resolution be ? importedModule.ResolveExport(exportName,
// resolveSet).
- if (!ModuleResolveExport(cx, importedModule, exportName, resolveSet,
- &resolution)) {
+ if (!CyclicModuleResolveExport(cx, importedModule, exportName, resolveSet,
+ &resolution, errorInfoOut)) {
return false;
}
@@ -744,6 +776,12 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
if (binding->module() != starResolution->module() ||
binding->bindingName() != starResolution->bindingName()) {
result.set(StringValue(cx->names().ambiguous));
+
+ if (errorInfoOut) {
+ Rooted<ModuleObject*> module1(cx, starResolution->module());
+ Rooted<ModuleObject*> module2(cx, binding->module());
+ errorInfoOut->setForAmbiguousImport(cx, module, module1, module2);
+ }
return true;
}
}
@@ -752,6 +790,9 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
// Step 9. Return starResolution.
result.setObjectOrNull(starResolution);
+ if (!starResolution && errorInfoOut) {
+ errorInfoOut->setImportedModule(cx, module);
+ }
return true;
}
@@ -759,10 +800,14 @@ static bool ModuleResolveExport(JSContext* cx, Handle<ModuleObject*> module,
static bool SyntheticModuleResolveExport(JSContext* cx,
Handle<ModuleObject*> module,
Handle<JSAtom*> exportName,
- MutableHandle<Value> result) {
+ MutableHandle<Value> result,
+ ModuleErrorInfo* errorInfoOut) {
// Step 2. If module.[[ExportNames]] does not contain exportName, return null.
if (!ContainsElement(module->syntheticExportNames(), exportName)) {
result.setNull();
+ if (errorInfoOut) {
+ errorInfoOut->setImportedModule(cx, module);
+ }
return true;
}
@@ -923,63 +968,93 @@ static ModuleNamespaceObject* ModuleNamespaceCreate(
return ns;
}
-static void ThrowResolutionError(JSContext* cx, Handle<ModuleObject*> module,
- Handle<Value> resolution, bool isDirectImport,
- Handle<JSAtom*> name, uint32_t line,
- JS::ColumnNumberOneOrigin column) {
- MOZ_ASSERT(line != 0);
+void ModuleErrorInfo::setImportedModule(JSContext* cx,
+ ModuleObject* importedModule) {
+ imported = importedModule->filename();
+}
- bool isAmbiguous = resolution == StringValue(cx->names().ambiguous);
+void ModuleErrorInfo::setCircularImport(JSContext* cx,
+ ModuleObject* importedModule) {
+ setImportedModule(cx, importedModule);
+ isCircular = true;
+}
- // ErrorNumbers:
- // | MISSING | AMBIGUOUS |
- // ---------+---------+-----------+
- // INDIRECT |
- // DIRECT |
- static constexpr unsigned ErrorNumbers[2][2] = {
- {JSMSG_MISSING_INDIRECT_EXPORT, JSMSG_AMBIGUOUS_INDIRECT_EXPORT},
- {JSMSG_MISSING_IMPORT, JSMSG_AMBIGUOUS_IMPORT}};
- unsigned errorNumber = ErrorNumbers[isDirectImport][isAmbiguous];
-
- const JSErrorFormatString* errorString =
- GetErrorMessage(nullptr, errorNumber);
- MOZ_ASSERT(errorString);
-
- MOZ_ASSERT(errorString->argCount == 0);
- Rooted<JSString*> message(cx, JS_NewStringCopyZ(cx, errorString->format));
- if (!message) {
+void ModuleErrorInfo::setForAmbiguousImport(JSContext* cx,
+ ModuleObject* importedModule,
+ ModuleObject* module1,
+ ModuleObject* module2) {
+ setImportedModule(cx, importedModule);
+ entry1 = module1->filename();
+ entry2 = module2->filename();
+}
+
+static void CreateErrorNumberMessageUTF8(JSContext* cx, unsigned errorNumber,
+ JSErrorReport* reportOut, ...) {
+ va_list ap;
+ va_start(ap, reportOut);
+ AutoReportFrontendContext fc(cx);
+ if (!ExpandErrorArgumentsVA(&fc, GetErrorMessage, nullptr, errorNumber,
+ ArgumentsAreUTF8, reportOut, ap)) {
+ ReportOutOfMemory(cx);
return;
}
- Rooted<JSString*> separator(cx, JS_NewStringCopyZ(cx, ": "));
- if (!separator) {
+ va_end(ap);
+}
+
+static void ThrowResolutionError(JSContext* cx, Handle<ModuleObject*> module,
+ Handle<Value> resolution, Handle<JSAtom*> name,
+ ModuleErrorInfo* errorInfo) {
+ MOZ_ASSERT(errorInfo);
+ auto chars = StringToNewUTF8CharsZ(cx, *name);
+ if (!chars) {
+ ReportOutOfMemory(cx);
return;
}
- message = ConcatStrings<CanGC>(cx, message, separator);
- if (!message) {
- return;
+ bool isAmbiguous = resolution == StringValue(cx->names().ambiguous);
+
+ unsigned errorNumber;
+ if (errorInfo->isCircular) {
+ errorNumber = JSMSG_MODULE_CIRCULAR_IMPORT;
+ } else if (isAmbiguous) {
+ errorNumber = JSMSG_MODULE_AMBIGUOUS;
+ } else {
+ errorNumber = JSMSG_MODULE_NO_EXPORT;
+ }
+
+ JSErrorReport report;
+ report.isWarning_ = false;
+ report.errorNumber = errorNumber;
+
+ if (errorNumber == JSMSG_MODULE_AMBIGUOUS) {
+ CreateErrorNumberMessageUTF8(cx, errorNumber, &report, errorInfo->imported,
+ chars.get(), errorInfo->entry1,
+ errorInfo->entry2);
+ } else {
+ CreateErrorNumberMessageUTF8(cx, errorNumber, &report, errorInfo->imported,
+ chars.get());
}
- message = ConcatStrings<CanGC>(cx, message, name);
+ Rooted<JSString*> message(cx, report.newMessageString(cx));
if (!message) {
+ ReportOutOfMemory(cx);
return;
}
- RootedString filename(cx);
- if (const char* chars = module->script()->filename()) {
- filename =
- JS_NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(chars, strlen(chars)));
- } else {
- filename = cx->names().empty_;
- }
+ const char* file = module->filename();
+ RootedString filename(
+ cx, JS_NewStringCopyUTF8Z(cx, JS::ConstUTF8CharsZ(file, strlen(file))));
if (!filename) {
+ ReportOutOfMemory(cx);
return;
}
RootedValue error(cx);
- if (!JS::CreateError(cx, JSEXN_SYNTAXERR, nullptr, filename, line, column,
- nullptr, message, JS::NothingHandleValue, &error)) {
+ if (!JS::CreateError(cx, JSEXN_SYNTAXERR, nullptr, filename,
+ errorInfo->lineNumber, errorInfo->columnNumber, nullptr,
+ message, JS::NothingHandleValue, &error)) {
+ ReportOutOfMemory(cx);
return;
}
@@ -988,8 +1063,8 @@ static void ThrowResolutionError(JSContext* cx, Handle<ModuleObject*> module,
// https://tc39.es/ecma262/#sec-source-text-module-record-initialize-environment
// ES2023 16.2.1.6.4 InitializeEnvironment
-bool js::ModuleInitializeEnvironment(JSContext* cx,
- Handle<ModuleObject*> module) {
+static bool ModuleInitializeEnvironment(JSContext* cx,
+ Handle<ModuleObject*> module) {
MOZ_ASSERT(module->status() == ModuleStatus::Linking);
// Step 1. For each ExportEntry Record e of module.[[IndirectExportEntries]],
@@ -1002,15 +1077,15 @@ bool js::ModuleInitializeEnvironment(JSContext* cx,
// Step 1.b. Let resolution be ? module.ResolveExport(e.[[ExportName]]).
exportName = e.exportName();
- if (!ModuleResolveExport(cx, module, exportName, &resolution)) {
+ ModuleErrorInfo errorInfo{e.lineNumber(), e.columnNumber()};
+ if (!ModuleResolveExport(cx, module, exportName, &resolution, &errorInfo)) {
return false;
}
// Step 1.c. If resolution is either null or AMBIGUOUS, throw a SyntaxError
// exception.
if (!IsResolvedBinding(cx, resolution)) {
- ThrowResolutionError(cx, module, resolution, false, exportName,
- e.lineNumber(), e.columnNumber());
+ ThrowResolutionError(cx, module, resolution, exportName, &errorInfo);
return false;
}
}
@@ -1059,15 +1134,16 @@ bool js::ModuleInitializeEnvironment(JSContext* cx,
// Step 7.d. Else:
// Step 7.d.i. Let resolution be ?
// importedModule.ResolveExport(in.[[ImportName]]).
- if (!ModuleResolveExport(cx, importedModule, importName, &resolution)) {
+ ModuleErrorInfo errorInfo{in.lineNumber(), in.columnNumber()};
+ if (!ModuleResolveExport(cx, importedModule, importName, &resolution,
+ &errorInfo)) {
return false;
}
// Step 7.d.ii. If resolution is null or ambiguous, throw a SyntaxError
// exception.
if (!IsResolvedBinding(cx, resolution)) {
- ThrowResolutionError(cx, module, resolution, true, importName,
- in.lineNumber(), in.columnNumber());
+ ThrowResolutionError(cx, module, resolution, importName, &errorInfo);
return false;
}
@@ -1134,7 +1210,7 @@ bool js::ModuleInitializeEnvironment(JSContext* cx,
// https://tc39.es/ecma262/#sec-moduledeclarationlinking
// ES2023 16.2.1.5.1 Link
-bool js::ModuleLink(JSContext* cx, Handle<ModuleObject*> module) {
+static bool ModuleLink(JSContext* cx, Handle<ModuleObject*> module) {
// Step 1. Assert: module.[[Status]] is not linking or evaluating.
ModuleStatus status = module->status();
if (status == ModuleStatus::Linking || status == ModuleStatus::Evaluating) {
@@ -1313,8 +1389,9 @@ static bool InnerModuleLinking(JSContext* cx, Handle<ModuleObject*> module,
return true;
}
-bool js::SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
- MutableHandle<Value> result) {
+static bool SyntheticModuleEvaluate(JSContext* cx,
+ Handle<ModuleObject*> moduleArg,
+ MutableHandle<Value> result) {
// Steps 1-12 happens elsewhere in the engine.
// Step 13. Let pc be ! NewPromiseCapability(%Promise%).
@@ -1337,8 +1414,8 @@ bool js::SyntheticModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
// https://tc39.es/ecma262/#sec-moduleevaluation
// ES2023 16.2.1.5.2 Evaluate
-bool js::ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
- MutableHandle<Value> result) {
+static bool ModuleEvaluate(JSContext* cx, Handle<ModuleObject*> moduleArg,
+ MutableHandle<Value> result) {
Rooted<ModuleObject*> module(cx, moduleArg);
// Step 2. Assert: module.[[Status]] is linked, evaluating-async, or