diff options
author | Moonchild <moonchild@palemoon.org> | 2020-07-04 16:28:30 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2020-07-10 18:30:48 +0000 |
commit | f6a6900a6b14d1d54da46370015b28d4d8a152a7 (patch) | |
tree | 6af248540f32f4427e9a01164804aca505aae379 /dom/script/ScriptLoader.cpp | |
parent | 40aad1a2209607b973170f4fb64548e5d8103402 (diff) | |
download | UXP-f6a6900a6b14d1d54da46370015b28d4d8a152a7.tar UXP-f6a6900a6b14d1d54da46370015b28d4d8a152a7.tar.gz UXP-f6a6900a6b14d1d54da46370015b28d4d8a152a7.tar.lz UXP-f6a6900a6b14d1d54da46370015b28d4d8a152a7.tar.xz UXP-f6a6900a6b14d1d54da46370015b28d4d8a152a7.zip |
Issue #618 - Further align error handling for module scripts with the spec
Ref: BZ 1388728
Diffstat (limited to 'dom/script/ScriptLoader.cpp')
-rw-r--r-- | dom/script/ScriptLoader.cpp | 237 |
1 files changed, 169 insertions, 68 deletions
diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index cf7f06e71..a53098974 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -418,25 +418,23 @@ void ScriptLoader::SetModuleFetchFinishedAndResumeWaitingRequests(ModuleLoadRequest *aRequest, nsresult aResult) { - // Update module map with the result of fetching a single module script. The - // module script pointer is nullptr on error. + // Update module map with the result of fetching a single module script. // // If any requests for the same URL are waiting on this one to complete, they // will have ModuleLoaded or LoadFailed on them when the promise is // resolved/rejected. This is set up in StartLoad. - MOZ_ASSERT(!aRequest->IsReadyToRun()); - RefPtr<GenericPromise::Private> promise; MOZ_ALWAYS_TRUE(mFetchingModules.Get(aRequest->mURI, getter_AddRefs(promise))); mFetchingModules.Remove(aRequest->mURI); - RefPtr<ModuleScript> ms(aRequest->mModuleScript); - MOZ_ASSERT(NS_SUCCEEDED(aResult) == (ms != nullptr)); - mFetchedModules.Put(aRequest->mURI, ms); + RefPtr<ModuleScript> moduleScript(aRequest->mModuleScript); + MOZ_ASSERT(NS_FAILED(aResult) == !moduleScript); + + mFetchedModules.Put(aRequest->mURI, moduleScript); if (promise) { - if (ms) { + if (moduleScript) { promise->Resolve(true, __func__); } else { promise->Reject(aResult, __func__); @@ -482,19 +480,29 @@ ScriptLoader::ProcessFetchedModuleSource(ModuleLoadRequest* aRequest) MOZ_ASSERT(!aRequest->mModuleScript); nsresult rv = CreateModuleScript(aRequest); + MOZ_ASSERT(NS_FAILED(rv) == !aRequest->mModuleScript); + SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv); free(aRequest->mScriptTextBuf); aRequest->mScriptTextBuf = nullptr; aRequest->mScriptTextLength = 0; - if (NS_SUCCEEDED(rv)) { + if (NS_FAILED(rv)) { + aRequest->LoadFailed(); + return rv; + } + + if (!aRequest->mModuleScript->IsErrored()) { StartFetchingModuleDependencies(aRequest); } - return rv; + return NS_OK; } +static nsresult +ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI>& aUrls); + nsresult ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) { @@ -548,9 +556,34 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) } } MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr)); - if (module) { - aRequest->mModuleScript = - new ModuleScript(this, aRequest->mBaseURL, module); + RefPtr<ModuleScript> moduleScript = new ModuleScript(this, aRequest->mBaseURL); + aRequest->mModuleScript = moduleScript; + + if (!module) { + // Compilation failed + MOZ_ASSERT(aes.HasException()); + JS::Rooted<JS::Value> error(cx); + if (!aes.StealException(&error)) { + aRequest->mModuleScript = nullptr; + return NS_ERROR_FAILURE; + } + + moduleScript->SetPreInstantiationError(error); + aRequest->ModuleErrored(); + return NS_OK; + } + + moduleScript->SetModuleRecord(module); + + // Validate requested modules and treat failure to resolve module specifiers + // the same as a parse error. + nsCOMArray<nsIURI> urls; + rv = ResolveRequestedModules(aRequest, urls); + if (NS_FAILED(rv)) { + // ResolveRequestedModules sets pre-instanitation error on failure. + MOZ_ASSERT(moduleScript->IsErrored()); + aRequest->ModuleErrored(); + return NS_OK; } } @@ -560,8 +593,8 @@ ScriptLoader::CreateModuleScript(ModuleLoadRequest* aRequest) } static bool -ThrowTypeError(JSContext* aCx, ModuleScript* aScript, - const nsString& aMessage) +CreateTypeError(JSContext* aCx, ModuleScript* aScript, const nsString& aMessage, + JS::MutableHandle<JS::Value> aError) { JS::Rooted<JSObject*> module(aCx, aScript->ModuleRecord()); JS::Rooted<JSScript*> script(aCx, JS::GetModuleScript(aCx, module)); @@ -576,17 +609,11 @@ ThrowTypeError(JSContext* aCx, ModuleScript* aScript, return false; } - JS::Rooted<JS::Value> error(aCx); - if (!JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr, - message, &error)) { - return false; - } - - JS_SetPendingException(aCx, error); - return false; + return JS::CreateError(aCx, JSEXN_TYPEERR, nullptr, filename, 0, 0, nullptr, + message, aError); } -static bool +static nsresult HandleResolveFailure(JSContext* aCx, ModuleScript* aScript, const nsAString& aSpecifier) { @@ -595,19 +622,13 @@ HandleResolveFailure(JSContext* aCx, ModuleScript* aScript, nsAutoString message(NS_LITERAL_STRING("Error resolving module specifier: ")); message.Append(aSpecifier); - return ThrowTypeError(aCx, aScript, message); -} - -static bool -HandleModuleNotFound(JSContext* aCx, ModuleScript* aScript, - const nsAString& aSpecifier) -{ - // TODO: How can we get the line number of the failed import? - - nsAutoString message(NS_LITERAL_STRING("Resolved module not found in map: ")); - message.Append(aSpecifier); + JS::Rooted<JS::Value> error(aCx); + if (!CreateTypeError(aCx, aScript, message, &error)) { + return NS_ERROR_OUT_OF_MEMORY; + } - return ThrowTypeError(aCx, aScript, message); + aScript->SetPreInstantiationError(error); + return NS_OK; } static already_AddRefed<nsIURI> @@ -705,7 +726,8 @@ ResolveRequestedModules(ModuleLoadRequest* aRequest, nsCOMArray<nsIURI> &aUrls) ModuleScript* ms = aRequest->mModuleScript; nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(ms, specifier); if (!uri) { - HandleResolveFailure(cx, ms, specifier); + nsresult rv = HandleResolveFailure(cx, ms, specifier); + NS_ENSURE_SUCCESS(rv, rv); return NS_ERROR_FAILURE; } @@ -724,6 +746,7 @@ void ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) { MOZ_ASSERT(aRequest->mModuleScript); + MOZ_ASSERT(!aRequest->mModuleScript->IsErrored()); MOZ_ASSERT(!aRequest->IsReadyToRun()); aRequest->mProgress = ModuleLoadRequest::Progress::FetchingImports; @@ -731,7 +754,7 @@ ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) nsCOMArray<nsIURI> urls; nsresult rv = ResolveRequestedModules(aRequest, urls); if (NS_FAILED(rv)) { - aRequest->LoadFailed(); + aRequest->ModuleErrored(); return; } @@ -755,7 +778,7 @@ ScriptLoader::StartFetchingModuleDependencies(ModuleLoadRequest* aRequest) GenericPromise::All(AbstractThread::GetCurrent(), importsReady); allReady->Then(AbstractThread::GetCurrent(), __func__, aRequest, &ModuleLoadRequest::DependenciesLoaded, - &ModuleLoadRequest::LoadFailed); + &ModuleLoadRequest::ModuleErrored); } RefPtr<GenericPromise> @@ -778,6 +801,7 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, nsresult rv = StartLoad(childRequest, NS_LITERAL_STRING("module"), false); if (NS_FAILED(rv)) { + MOZ_ASSERT(!childRequest->mModuleScript); childRequest->mReady.Reject(rv, __func__); return ready; } @@ -786,6 +810,7 @@ ScriptLoader::StartFetchingModuleAndDependencies(ModuleLoadRequest* aRequest, return ready; } +// 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier) bool HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) { @@ -800,25 +825,25 @@ HostResolveImportedModule(JSContext* aCx, unsigned argc, JS::Value* vp) MOZ_ASSERT(script->ModuleRecord() == module); // Let url be the result of resolving a module specifier given referencing - // module script and specifier. If the result is failure, throw a TypeError - // exception and abort these steps. + // module script and specifier. nsAutoJSString string; if (!string.init(aCx, specifier)) { return false; } nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string); - if (!uri) { - return HandleResolveFailure(aCx, script, string); - } - // Let resolved module script be the value of the entry in module map whose - // key is url. If no such entry exists, throw a TypeError exception and abort - // these steps. + // This cannot fail because resolving a module specifier must have been + // previously successful with these same two arguments. + MOZ_ASSERT(uri, "Failed to resolve previously-resolved module specifier"); + + // Let resolved module script be moduleMap[url]. (This entry must exist for us + // to have gotten to this point.) + ModuleScript* ms = script->Loader()->GetFetchedModule(uri); - if (!ms) { - return HandleModuleNotFound(aCx, script, string); - } + MOZ_ASSERT(ms, "Resolved module not found in module map"); + + MOZ_ASSERT(!ms->IsErrored()); *vp = JS::ObjectValue(*ms->ModuleRecord()); return true; @@ -843,9 +868,35 @@ EnsureModuleResolveHook(JSContext* aCx) } void +ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest) +{ + RefPtr<ModuleScript> moduleScript = aRequest->mModuleScript; + if (moduleScript && !moduleScript->IsErrored()) { + for (auto childRequest : aRequest->mImports) { + ModuleScript* childScript = childRequest->mModuleScript; + if (!childScript) { + // Load error + aRequest->mModuleScript = nullptr; + return; + } else if (childScript->IsErrored()) { + // Script error + moduleScript->SetPreInstantiationError(childScript->Error()); + return; + } + } + } +} + +void ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) { if (aRequest->IsTopLevel()) { + ModuleScript* moduleScript = aRequest->mModuleScript; + if (moduleScript && !moduleScript->IsErrored()) { + if (!InstantiateModuleTree(aRequest)) { + aRequest->mModuleScript = nullptr; + } + } MaybeMoveToLoadedList(aRequest); ProcessPendingRequests(); } @@ -855,6 +906,43 @@ ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) } } +bool +ScriptLoader::InstantiateModuleTree(ModuleLoadRequest* aRequest) +{ + // Instantiate a top-level module and record any error. + + MOZ_ASSERT(aRequest); + MOZ_ASSERT(aRequest->IsTopLevel()); + + ModuleScript* moduleScript = aRequest->mModuleScript; + MOZ_ASSERT(moduleScript); + MOZ_ASSERT(moduleScript->ModuleRecord()); + + nsAutoMicroTask mt; + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(moduleScript->ModuleRecord()))) { + return false; + } + + nsresult rv = EnsureModuleResolveHook(jsapi.cx()); + NS_ENSURE_SUCCESS(rv, false); + + JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord()); + bool ok = NS_SUCCEEDED(nsJSUtils::ModuleInstantiate(jsapi.cx(), module)); + + if (!ok) { + MOZ_ASSERT(jsapi.HasException()); + JS::RootedValue exception(jsapi.cx()); + if (!jsapi.StealException(&exception)) { + return false; + } + MOZ_ASSERT(!exception.isUndefined()); + // Ignore the exception. It will be recorded in the module record. + } + + return true; +} + nsresult ScriptLoader::StartLoad(ScriptLoadRequest *aRequest, const nsAString &aType, bool aScriptFromHead) @@ -1364,13 +1452,19 @@ ScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) ModuleLoadRequest* modReq = request->AsModuleRequest(); modReq->mBaseURL = mDocument->GetDocBaseURI(); rv = CreateModuleScript(modReq); - NS_ENSURE_SUCCESS(rv, false); - StartFetchingModuleDependencies(modReq); + MOZ_ASSERT(NS_FAILED(rv) == !modReq->mModuleScript); + if (NS_FAILED(rv)) { + modReq->LoadFailed(); + return false; + } if (aElement->GetScriptAsync()) { mLoadingAsyncRequests.AppendElement(request); } else { AddDeferRequest(request); } + if (!modReq->mModuleScript->IsErrored()) { + StartFetchingModuleDependencies(modReq); + } return false; } request->mProgress = ScriptLoadRequest::Progress::Ready; @@ -1448,11 +1542,7 @@ ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest) if (aRequest->IsModuleRequest()) { MOZ_ASSERT(aRequest->mOffThreadToken); ModuleLoadRequest* request = aRequest->AsModuleRequest(); - nsresult rv = ProcessFetchedModuleSource(request); - if (NS_FAILED(rv)) { - request->LoadFailed(); - } - return rv; + return ProcessFetchedModuleSource(request); } aRequest->SetReady(); @@ -1624,7 +1714,7 @@ ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) if (aRequest->IsModuleRequest() && !aRequest->AsModuleRequest()->mModuleScript) { - // There was an error parsing a module script. Nothing to do here. + // There was an error fetching a module script. Nothing to do here. FireScriptAvailable(NS_ERROR_FAILURE, aRequest); return NS_OK; } @@ -1846,8 +1936,8 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block nsAutoMicroTask mt; AutoEntryScript aes(globalObject, "<script> element", true); - JS::Rooted<JSObject*> global(aes.cx(), - globalObject->GetGlobalJSObject()); + JSContext* cx = aes.cx(); + JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject()); bool oldProcessingScriptTag = context->GetProcessingScriptTag(); context->SetProcessingScriptTag(true); @@ -1868,27 +1958,38 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) } if (aRequest->IsModuleRequest()) { - rv = EnsureModuleResolveHook(aes.cx()); + rv = EnsureModuleResolveHook(cx); NS_ENSURE_SUCCESS(rv, rv); ModuleLoadRequest* request = aRequest->AsModuleRequest(); MOZ_ASSERT(request->mModuleScript); MOZ_ASSERT(!request->mOffThreadToken); - JS::Rooted<JSObject*> module(aes.cx(), - request->mModuleScript->ModuleRecord()); + + ModuleScript* moduleScript = request->mModuleScript; + if (moduleScript->IsErrored()) { + // Module has an error status + JS::Rooted<JS::Value> error(cx, moduleScript->Error()); + JS_SetPendingException(cx, error); + return NS_OK; // An error is reported by AutoEntryScript. + } + + JS::Rooted<JSObject*> module(cx, moduleScript->ModuleRecord()); MOZ_ASSERT(module); - rv = nsJSUtils::ModuleInstantiate(aes.cx(), module); - if (NS_SUCCEEDED(rv)) { - rv = nsJSUtils::ModuleEvaluate(aes.cx(), module); + + rv = nsJSUtils::ModuleEvaluate(cx, module); + MOZ_ASSERT(NS_FAILED(rv) == aes.HasException()); + if (NS_FAILED(rv)) { + // Evaluation failed + rv = NS_OK; // An error is reported by AutoEntryScript. } } else { - JS::CompileOptions options(aes.cx()); + JS::CompileOptions options(cx); rv = FillCompileOptionsForRequest(aes, aRequest, global, &options); if (NS_SUCCEEDED(rv)) { nsAutoString inlineData; SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); - rv = nsJSUtils::EvaluateString(aes.cx(), srcBuf, global, options, + rv = nsJSUtils::EvaluateString(cx, srcBuf, global, options, aRequest->OffThreadTokenPtr()); } } |