Bug 1845638 - Part 1: Use JS::FrontendContext APIs and TaskController in compilation and decode in ScriptLoader. r=smaug,bthrall
Differential Revision: https://phabricator.services.mozilla.com/D184896
This commit is contained in:
@@ -21,7 +21,6 @@
|
|||||||
#include "js/Conversions.h"
|
#include "js/Conversions.h"
|
||||||
#include "js/experimental/JSStencil.h"
|
#include "js/experimental/JSStencil.h"
|
||||||
#include "js/HeapAPI.h"
|
#include "js/HeapAPI.h"
|
||||||
#include "js/OffThreadScriptCompilation.h"
|
|
||||||
#include "js/ProfilingCategory.h"
|
#include "js/ProfilingCategory.h"
|
||||||
#include "js/Promise.h"
|
#include "js/Promise.h"
|
||||||
#include "js/SourceText.h"
|
#include "js/SourceText.h"
|
||||||
@@ -30,6 +29,7 @@
|
|||||||
#include "js/Wrapper.h"
|
#include "js/Wrapper.h"
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
#include "mozilla/CycleCollectedJSContext.h"
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
|
#include "mozilla/dom/ScriptLoadContext.h"
|
||||||
#include "mozilla/Likely.h"
|
#include "mozilla/Likely.h"
|
||||||
#include "nsContentUtils.h"
|
#include "nsContentUtils.h"
|
||||||
#include "nsTPromiseFlatString.h"
|
#include "nsTPromiseFlatString.h"
|
||||||
@@ -87,8 +87,7 @@ JSExecutionContext::JSExecutionContext(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult JSExecutionContext::JoinOffThread(
|
nsresult JSExecutionContext::JoinOffThread(ScriptLoadContext* aContext) {
|
||||||
JS::OffThreadToken** aOffThreadToken) {
|
|
||||||
if (mSkip) {
|
if (mSkip) {
|
||||||
return mRv;
|
return mRv;
|
||||||
}
|
}
|
||||||
@@ -96,9 +95,7 @@ nsresult JSExecutionContext::JoinOffThread(
|
|||||||
MOZ_ASSERT(!mWantsReturnValue);
|
MOZ_ASSERT(!mWantsReturnValue);
|
||||||
|
|
||||||
JS::InstantiationStorage storage;
|
JS::InstantiationStorage storage;
|
||||||
RefPtr<JS::Stencil> stencil =
|
RefPtr<JS::Stencil> stencil = aContext->StealOffThreadResult(mCx, &storage);
|
||||||
JS::FinishOffThreadStencil(mCx, *aOffThreadToken, &storage);
|
|
||||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
|
||||||
if (!stencil) {
|
if (!stencil) {
|
||||||
mSkip = true;
|
mSkip = true;
|
||||||
mRv = EvaluationExceptionToNSResult(mCx);
|
mRv = EvaluationExceptionToNSResult(mCx);
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#define DOM_BASE_JSEXECUTIONCONTEXT_H_
|
#define DOM_BASE_JSEXECUTIONCONTEXT_H_
|
||||||
|
|
||||||
#include "js/GCVector.h"
|
#include "js/GCVector.h"
|
||||||
#include "js/OffThreadScriptCompilation.h"
|
|
||||||
#include "js/TypeDecls.h"
|
#include "js/TypeDecls.h"
|
||||||
#include "js/Value.h"
|
#include "js/Value.h"
|
||||||
#include "js/experimental/JSStencil.h"
|
#include "js/experimental/JSStencil.h"
|
||||||
@@ -30,6 +29,8 @@ union Utf8Unit;
|
|||||||
|
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
|
class ScriptLoadContext;
|
||||||
|
|
||||||
class MOZ_STACK_CLASS JSExecutionContext final {
|
class MOZ_STACK_CLASS JSExecutionContext final {
|
||||||
// Register stack annotations for the Gecko profiler.
|
// Register stack annotations for the Gecko profiler.
|
||||||
mozilla::AutoProfilerLabel mAutoProfilerLabel;
|
mozilla::AutoProfilerLabel mAutoProfilerLabel;
|
||||||
@@ -128,9 +129,9 @@ class MOZ_STACK_CLASS JSExecutionContext final {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// After getting a notification that an off-thread compile/decode finished,
|
// After getting a notification that an off-thread compile/decode finished,
|
||||||
// this function will take the result of the parser and move it to the main
|
// this function will take the result of the off-thread operation and move it
|
||||||
// thread.
|
// to the main thread.
|
||||||
[[nodiscard]] nsresult JoinOffThread(JS::OffThreadToken** aOffThreadToken);
|
[[nodiscard]] nsresult JoinOffThread(ScriptLoadContext* aContext);
|
||||||
|
|
||||||
// Compile a script contained in a SourceText.
|
// Compile a script contained in a SourceText.
|
||||||
nsresult Compile(JS::SourceText<char16_t>& aSrcBuf);
|
nsresult Compile(JS::SourceText<char16_t>& aSrcBuf);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@
|
|||||||
#include "js/experimental/JSStencil.h" // JS::Stencil, JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil
|
#include "js/experimental/JSStencil.h" // JS::Stencil, JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil
|
||||||
#include "js/MemoryFunctions.h"
|
#include "js/MemoryFunctions.h"
|
||||||
#include "js/Modules.h" // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
|
#include "js/Modules.h" // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook
|
||||||
#include "js/OffThreadScriptCompilation.h"
|
|
||||||
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
||||||
#include "js/Realm.h"
|
#include "js/Realm.h"
|
||||||
#include "js/SourceText.h"
|
#include "js/SourceText.h"
|
||||||
@@ -150,11 +149,8 @@ nsresult ModuleLoader::CompileFetchedModule(
|
|||||||
ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleOut) {
|
ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleOut) {
|
||||||
if (aRequest->GetScriptLoadContext()->mWasCompiledOMT) {
|
if (aRequest->GetScriptLoadContext()->mWasCompiledOMT) {
|
||||||
JS::InstantiationStorage storage;
|
JS::InstantiationStorage storage;
|
||||||
RefPtr<JS::Stencil> stencil = JS::FinishOffThreadStencil(
|
RefPtr<JS::Stencil> stencil =
|
||||||
aCx, aRequest->GetScriptLoadContext()->mOffThreadToken, &storage);
|
aRequest->GetScriptLoadContext()->StealOffThreadResult(aCx, &storage);
|
||||||
|
|
||||||
aRequest->GetScriptLoadContext()->mOffThreadToken = nullptr;
|
|
||||||
|
|
||||||
if (!stencil) {
|
if (!stencil) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "mozilla/Unused.h"
|
#include "mozilla/Unused.h"
|
||||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||||
|
|
||||||
#include "js/OffThreadScriptCompilation.h"
|
|
||||||
#include "js/SourceText.h"
|
#include "js/SourceText.h"
|
||||||
#include "js/loader/LoadContextBase.h"
|
#include "js/loader/LoadContextBase.h"
|
||||||
#include "js/loader/ModuleLoadRequest.h"
|
#include "js/loader/ModuleLoadRequest.h"
|
||||||
@@ -37,7 +36,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadContext)
|
|||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScriptLoadContext,
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScriptLoadContext,
|
||||||
JS::loader::LoadContextBase)
|
JS::loader::LoadContextBase)
|
||||||
MOZ_ASSERT(!tmp->mOffThreadToken);
|
MOZ_ASSERT(!tmp->mCompileOrDecodeTask);
|
||||||
MOZ_ASSERT(!tmp->mRunnable);
|
MOZ_ASSERT(!tmp->mRunnable);
|
||||||
tmp->MaybeUnblockOnload();
|
tmp->MaybeUnblockOnload();
|
||||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||||
@@ -62,7 +61,6 @@ ScriptLoadContext::ScriptLoadContext()
|
|||||||
mInCompilingList(false),
|
mInCompilingList(false),
|
||||||
mIsTracking(false),
|
mIsTracking(false),
|
||||||
mWasCompiledOMT(false),
|
mWasCompiledOMT(false),
|
||||||
mOffThreadToken(nullptr),
|
|
||||||
mRunnable(nullptr),
|
mRunnable(nullptr),
|
||||||
mLineNo(1),
|
mLineNo(1),
|
||||||
mColumnNo(0),
|
mColumnNo(0),
|
||||||
@@ -72,8 +70,8 @@ ScriptLoadContext::ScriptLoadContext()
|
|||||||
ScriptLoadContext::~ScriptLoadContext() {
|
ScriptLoadContext::~ScriptLoadContext() {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
// Off-thread parsing must have completed by this point.
|
// Off-thread parsing must have completed or cancelled by this point.
|
||||||
MOZ_DIAGNOSTIC_ASSERT(!mOffThreadToken && !mRunnable);
|
MOZ_DIAGNOSTIC_ASSERT(!mCompileOrDecodeTask && !mRunnable);
|
||||||
|
|
||||||
mRequest = nullptr;
|
mRequest = nullptr;
|
||||||
|
|
||||||
@@ -96,15 +94,13 @@ void ScriptLoadContext::MaybeUnblockOnload() {
|
|||||||
void ScriptLoadContext::MaybeCancelOffThreadScript() {
|
void ScriptLoadContext::MaybeCancelOffThreadScript() {
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
if (!mOffThreadToken) {
|
if (!mCompileOrDecodeTask) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel parse if it hasn't been started yet or wait for it to finish and
|
// Cancel the task if it hasn't been started yet or wait for it to finish.
|
||||||
// clean up finished parse data.
|
mCompileOrDecodeTask->Cancel();
|
||||||
JSContext* cx = danger::GetJSContext();
|
mCompileOrDecodeTask = nullptr;
|
||||||
JS::CancelOffThreadToken(cx, mOffThreadToken);
|
|
||||||
mOffThreadToken = nullptr;
|
|
||||||
|
|
||||||
// Clear the pointer to the runnable. It may still run later if we didn't
|
// Clear the pointer to the runnable. It may still run later if we didn't
|
||||||
// cancel in time. In this case the runnable is held live by the reference
|
// cancel in time. In this case the runnable is held live by the reference
|
||||||
@@ -214,4 +210,12 @@ void ScriptLoadContext::GetProfilerLabel(nsACString& aOutString) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
already_AddRefed<JS::Stencil> ScriptLoadContext::StealOffThreadResult(
|
||||||
|
JSContext* aCx, JS::InstantiationStorage* aInstantiationStorage) {
|
||||||
|
RefPtr<CompileOrDecodeTask> compileOrDecodeTask =
|
||||||
|
mCompileOrDecodeTask.forget();
|
||||||
|
|
||||||
|
return compileOrDecodeTask->StealResult(aCx, aInstantiationStorage);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace mozilla::dom
|
} // namespace mozilla::dom
|
||||||
|
|||||||
@@ -8,11 +8,15 @@
|
|||||||
#define mozilla_dom_ScriptLoadContext_h
|
#define mozilla_dom_ScriptLoadContext_h
|
||||||
|
|
||||||
#include "js/AllocPolicy.h"
|
#include "js/AllocPolicy.h"
|
||||||
|
#include "js/CompileOptions.h" // JS::OwningCompileOptions
|
||||||
|
#include "js/experimental/JSStencil.h" // JS::FrontendContext, JS::Stencil, JS::InstantiationStorage
|
||||||
#include "js/RootingAPI.h"
|
#include "js/RootingAPI.h"
|
||||||
#include "js/SourceText.h"
|
#include "js/SourceText.h"
|
||||||
|
#include "js/Transcoding.h" // JS::TranscodeResult
|
||||||
#include "js/TypeDecls.h"
|
#include "js/TypeDecls.h"
|
||||||
#include "js/loader/LoadContextBase.h"
|
#include "js/loader/LoadContextBase.h"
|
||||||
#include "js/loader/ScriptKind.h"
|
#include "js/loader/ScriptKind.h"
|
||||||
|
#include "mozilla/AlreadyAddRefed.h"
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/CORSMode.h"
|
#include "mozilla/CORSMode.h"
|
||||||
@@ -20,8 +24,11 @@
|
|||||||
#include "mozilla/LinkedList.h"
|
#include "mozilla/LinkedList.h"
|
||||||
#include "mozilla/Maybe.h"
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/MaybeOneOf.h"
|
#include "mozilla/MaybeOneOf.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
#include "mozilla/PreloaderBase.h"
|
#include "mozilla/PreloaderBase.h"
|
||||||
|
#include "mozilla/RefPtr.h"
|
||||||
#include "mozilla/StaticPrefs_dom.h"
|
#include "mozilla/StaticPrefs_dom.h"
|
||||||
|
#include "mozilla/TaskController.h" // mozilla::Task
|
||||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||||
#include "mozilla/Variant.h"
|
#include "mozilla/Variant.h"
|
||||||
#include "mozilla/Vector.h"
|
#include "mozilla/Vector.h"
|
||||||
@@ -30,10 +37,7 @@
|
|||||||
#include "nsIScriptElement.h"
|
#include "nsIScriptElement.h"
|
||||||
|
|
||||||
class nsICacheInfoChannel;
|
class nsICacheInfoChannel;
|
||||||
|
struct JSContext;
|
||||||
namespace JS {
|
|
||||||
class OffThreadToken;
|
|
||||||
} // namespace JS
|
|
||||||
|
|
||||||
namespace mozilla::dom {
|
namespace mozilla::dom {
|
||||||
|
|
||||||
@@ -75,6 +79,71 @@ class Element;
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
class OffThreadCompilationCompleteRunnable;
|
||||||
|
|
||||||
|
// Base class for the off-thread compile or off-thread decode tasks.
|
||||||
|
class CompileOrDecodeTask : public mozilla::Task {
|
||||||
|
protected:
|
||||||
|
explicit CompileOrDecodeTask(
|
||||||
|
OffThreadCompilationCompleteRunnable* aCompleteRunnable);
|
||||||
|
virtual ~CompileOrDecodeTask();
|
||||||
|
|
||||||
|
nsresult InitFrontendContext();
|
||||||
|
|
||||||
|
void DidRunTask(const MutexAutoLock& aProofOfLock,
|
||||||
|
RefPtr<JS::Stencil>&& aStencil);
|
||||||
|
|
||||||
|
bool IsCancelled(const MutexAutoLock& aProofOfLock) const {
|
||||||
|
return !mCompleteRunnable;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Returns the result of the compilation or decode if it was successful.
|
||||||
|
// Returns nullptr otherwise, and sets pending exception on JSContext.
|
||||||
|
//
|
||||||
|
// aInstantiationStorage receives the storage allocated off main thread
|
||||||
|
// on successful case.
|
||||||
|
already_AddRefed<JS::Stencil> StealResult(
|
||||||
|
JSContext* aCx, JS::InstantiationStorage* aInstantiationStorage);
|
||||||
|
|
||||||
|
// Cancel the task.
|
||||||
|
// If the task is already running, this waits for the task to finish.
|
||||||
|
void Cancel();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
static constexpr size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
|
||||||
|
|
||||||
|
// This mutex is locked during running the task or cancelling task.
|
||||||
|
mozilla::Mutex mMutex;
|
||||||
|
|
||||||
|
// The result of decode task, to distinguish throwing case and decode error.
|
||||||
|
JS::TranscodeResult mResult = JS::TranscodeResult::Ok;
|
||||||
|
|
||||||
|
// An option used to compile the code, or the equivalent for decode.
|
||||||
|
// This holds the filename pointed by errors reported to JS::FrontendContext.
|
||||||
|
JS::OwningCompileOptions mOptions;
|
||||||
|
|
||||||
|
// Owning-pointer for the context associated with the script compilation.
|
||||||
|
//
|
||||||
|
// The context is allocated on main thread in InitFrontendContext method,
|
||||||
|
// and is freed on any thread in the destructor.
|
||||||
|
JS::FrontendContext* mFrontendContext = nullptr;
|
||||||
|
|
||||||
|
// The pointed OffThreadCompilationCompleteRunnable is kept alive by
|
||||||
|
// ScriptLoadContext::mRunnable.
|
||||||
|
//
|
||||||
|
// This shouldn't be RefPtr, given this task can be freed off main thread.
|
||||||
|
//
|
||||||
|
// If this task is cancelled before running, this field is cleared.
|
||||||
|
OffThreadCompilationCompleteRunnable* mCompleteRunnable;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// The result of the compilation or decode.
|
||||||
|
RefPtr<JS::Stencil> mStencil;
|
||||||
|
|
||||||
|
JS::InstantiationStorage mInstantiationStorage;
|
||||||
|
};
|
||||||
|
|
||||||
class ScriptLoadContext : public JS::loader::LoadContextBase,
|
class ScriptLoadContext : public JS::loader::LoadContextBase,
|
||||||
public PreloaderBase {
|
public PreloaderBase {
|
||||||
protected:
|
protected:
|
||||||
@@ -95,10 +164,6 @@ class ScriptLoadContext : public JS::loader::LoadContextBase,
|
|||||||
|
|
||||||
bool CompileStarted() const;
|
bool CompileStarted() const;
|
||||||
|
|
||||||
JS::OffThreadToken** OffThreadTokenPtr() {
|
|
||||||
return mOffThreadToken ? &mOffThreadToken : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsTracking() const { return mIsTracking; }
|
bool IsTracking() const { return mIsTracking; }
|
||||||
void SetIsTracking() {
|
void SetIsTracking() {
|
||||||
MOZ_ASSERT(!mIsTracking);
|
MOZ_ASSERT(!mIsTracking);
|
||||||
@@ -154,6 +219,11 @@ class ScriptLoadContext : public JS::loader::LoadContextBase,
|
|||||||
|
|
||||||
void MaybeCancelOffThreadScript();
|
void MaybeCancelOffThreadScript();
|
||||||
|
|
||||||
|
// Finish the off-main-thread compilation and return the result, or
|
||||||
|
// convert the compilation error to runtime error.
|
||||||
|
already_AddRefed<JS::Stencil> StealOffThreadResult(
|
||||||
|
JSContext* aCx, JS::InstantiationStorage* aInstantiationStorage);
|
||||||
|
|
||||||
ScriptMode mScriptMode; // Whether this is a blocking, defer or async script.
|
ScriptMode mScriptMode; // Whether this is a blocking, defer or async script.
|
||||||
bool mScriptFromHead; // Synchronous head script block loading of other non
|
bool mScriptFromHead; // Synchronous head script block loading of other non
|
||||||
// js/css content.
|
// js/css content.
|
||||||
@@ -170,9 +240,12 @@ class ScriptLoadContext : public JS::loader::LoadContextBase,
|
|||||||
bool mWasCompiledOMT; // True if the script has been compiled off main
|
bool mWasCompiledOMT; // True if the script has been compiled off main
|
||||||
// thread.
|
// thread.
|
||||||
|
|
||||||
// Off-thread parsing token. Set at the start of off-thread parsing and
|
// Task that performs off-thread compilation or off-thread decode.
|
||||||
// cleared when the result of the parse is used.
|
// This field is used to take the result of the task, or cancel the task.
|
||||||
JS::OffThreadToken* mOffThreadToken;
|
//
|
||||||
|
// Set to non-null on the task creation, and set to null when taking the
|
||||||
|
// result or cancelling the task.
|
||||||
|
RefPtr<CompileOrDecodeTask> mCompileOrDecodeTask;
|
||||||
|
|
||||||
// Runnable that is dispatched to the main thread when off-thread compilation
|
// Runnable that is dispatched to the main thread when off-thread compilation
|
||||||
// completes.
|
// completes.
|
||||||
|
|||||||
@@ -23,7 +23,10 @@
|
|||||||
#include "js/ColumnNumber.h" // #include "js/CompilationAndEvaluation.h"
|
#include "js/ColumnNumber.h" // #include "js/CompilationAndEvaluation.h"
|
||||||
|
|
||||||
#include "js/CompilationAndEvaluation.h"
|
#include "js/CompilationAndEvaluation.h"
|
||||||
|
#include "js/CompileOptions.h" // JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::OwningDecodeOptions, JS::DelazificationOption
|
||||||
#include "js/ContextOptions.h" // JS::ContextOptionsRef
|
#include "js/ContextOptions.h" // JS::ContextOptionsRef
|
||||||
|
#include "js/experimental/JSStencil.h" // JS::Stencil, JS::InstantiationStorage
|
||||||
|
#include "js/experimental/CompileScript.h" // JS::FrontendContext, JS::NewFrontendContext, JS::DestroyFrontendContext, JS::SetNativeStackQuota, JS::CompilationStorage, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::DecodeStencil, JS::PrepareForInstantiate
|
||||||
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
||||||
#include "js/loader/ScriptLoadRequest.h"
|
#include "js/loader/ScriptLoadRequest.h"
|
||||||
#include "ScriptCompression.h"
|
#include "ScriptCompression.h"
|
||||||
@@ -31,11 +34,10 @@
|
|||||||
#include "js/loader/ModuleLoadRequest.h"
|
#include "js/loader/ModuleLoadRequest.h"
|
||||||
#include "js/MemoryFunctions.h"
|
#include "js/MemoryFunctions.h"
|
||||||
#include "js/Modules.h"
|
#include "js/Modules.h"
|
||||||
#include "js/OffThreadScriptCompilation.h"
|
|
||||||
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
||||||
#include "js/Realm.h"
|
#include "js/Realm.h"
|
||||||
#include "js/SourceText.h"
|
#include "js/SourceText.h"
|
||||||
#include "js/Transcoding.h"
|
#include "js/Transcoding.h" // JS::TranscodeRange, JS::TranscodeResult, JS::IsTranscodeFailureResult
|
||||||
#include "js/Utility.h"
|
#include "js/Utility.h"
|
||||||
#include "xpcpublic.h"
|
#include "xpcpublic.h"
|
||||||
#include "GeckoProfiler.h"
|
#include "GeckoProfiler.h"
|
||||||
@@ -51,6 +53,7 @@
|
|||||||
#include "mozilla/dom/ScriptSettings.h"
|
#include "mozilla/dom/ScriptSettings.h"
|
||||||
#include "mozilla/dom/SRILogHelper.h"
|
#include "mozilla/dom/SRILogHelper.h"
|
||||||
#include "mozilla/dom/WindowContext.h"
|
#include "mozilla/dom/WindowContext.h"
|
||||||
|
#include "mozilla/Mutex.h" // mozilla::Mutex
|
||||||
#include "mozilla/net/UrlClassifierFeatureFactory.h"
|
#include "mozilla/net/UrlClassifierFeatureFactory.h"
|
||||||
#include "mozilla/Preferences.h"
|
#include "mozilla/Preferences.h"
|
||||||
#include "mozilla/StaticPrefs_dom.h"
|
#include "mozilla/StaticPrefs_dom.h"
|
||||||
@@ -89,12 +92,14 @@
|
|||||||
#include "nsMimeTypes.h"
|
#include "nsMimeTypes.h"
|
||||||
#include "mozilla/ConsoleReportCollector.h"
|
#include "mozilla/ConsoleReportCollector.h"
|
||||||
#include "mozilla/CycleCollectedJSContext.h"
|
#include "mozilla/CycleCollectedJSContext.h"
|
||||||
|
#include "mozilla/EventQueue.h"
|
||||||
#include "mozilla/LoadInfo.h"
|
#include "mozilla/LoadInfo.h"
|
||||||
#include "ReferrerInfo.h"
|
#include "ReferrerInfo.h"
|
||||||
|
|
||||||
#include "mozilla/AsyncEventDispatcher.h"
|
#include "mozilla/AsyncEventDispatcher.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/ScopeExit.h"
|
#include "mozilla/ScopeExit.h"
|
||||||
|
#include "mozilla/TaskController.h"
|
||||||
#include "mozilla/Telemetry.h"
|
#include "mozilla/Telemetry.h"
|
||||||
#include "mozilla/TimeStamp.h"
|
#include "mozilla/TimeStamp.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
@@ -1481,7 +1486,7 @@ nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
|
|||||||
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
|
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
|
||||||
"Processing requests when running scripts is unsafe.");
|
"Processing requests when running scripts is unsafe.");
|
||||||
|
|
||||||
if (!aRequest->GetScriptLoadContext()->mOffThreadToken &&
|
if (!aRequest->GetScriptLoadContext()->mCompileOrDecodeTask &&
|
||||||
!aRequest->GetScriptLoadContext()->CompileStarted()) {
|
!aRequest->GetScriptLoadContext()->CompileStarted()) {
|
||||||
bool couldCompile = false;
|
bool couldCompile = false;
|
||||||
nsresult rv = AttemptOffThreadScriptCompile(aRequest, &couldCompile);
|
nsresult rv = AttemptOffThreadScriptCompile(aRequest, &couldCompile);
|
||||||
@@ -1498,13 +1503,10 @@ nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
|
|||||||
return ProcessRequest(aRequest);
|
return ProcessRequest(aRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
class OffThreadCompilationCompleteRunnable : public Runnable {
|
class OffThreadCompilationCompleteRunnable : public Runnable {
|
||||||
nsMainThreadPtrHandle<ScriptLoadRequest> mRequest;
|
nsMainThreadPtrHandle<ScriptLoadRequest> mRequest;
|
||||||
nsMainThreadPtrHandle<ScriptLoader> mLoader;
|
nsMainThreadPtrHandle<ScriptLoader> mLoader;
|
||||||
nsCOMPtr<nsISerialEventTarget> mEventTarget;
|
nsCOMPtr<nsISerialEventTarget> mEventTarget;
|
||||||
JS::OffThreadToken* mToken;
|
|
||||||
TimeStamp mStartTime;
|
TimeStamp mStartTime;
|
||||||
TimeStamp mStopTime;
|
TimeStamp mStopTime;
|
||||||
|
|
||||||
@@ -1514,8 +1516,7 @@ class OffThreadCompilationCompleteRunnable : public Runnable {
|
|||||||
: Runnable("dom::OffThreadCompilationCompleteRunnable"),
|
: Runnable("dom::OffThreadCompilationCompleteRunnable"),
|
||||||
mRequest(
|
mRequest(
|
||||||
new nsMainThreadPtrHolder<ScriptLoadRequest>("mRequest", aRequest)),
|
new nsMainThreadPtrHolder<ScriptLoadRequest>("mRequest", aRequest)),
|
||||||
mLoader(new nsMainThreadPtrHolder<ScriptLoader>("mLoader", aLoader)),
|
mLoader(new nsMainThreadPtrHolder<ScriptLoader>("mLoader", aLoader)) {
|
||||||
mToken(nullptr) {
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
if (DocGroup* docGroup = aLoader->GetDocGroup()) {
|
if (DocGroup* docGroup = aLoader->GetDocGroup()) {
|
||||||
mEventTarget = docGroup->EventTargetFor(TaskCategory::Other);
|
mEventTarget = docGroup->EventTargetFor(TaskCategory::Other);
|
||||||
@@ -1525,11 +1526,6 @@ class OffThreadCompilationCompleteRunnable : public Runnable {
|
|||||||
void RecordStartTime() { mStartTime = TimeStamp::Now(); }
|
void RecordStartTime() { mStartTime = TimeStamp::Now(); }
|
||||||
void RecordStopTime() { mStopTime = TimeStamp::Now(); }
|
void RecordStopTime() { mStopTime = TimeStamp::Now(); }
|
||||||
|
|
||||||
void SetToken(JS::OffThreadToken* aToken) {
|
|
||||||
MOZ_ASSERT(aToken && !mToken);
|
|
||||||
mToken = aToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Dispatch(
|
static void Dispatch(
|
||||||
already_AddRefed<OffThreadCompilationCompleteRunnable>&& aSelf) {
|
already_AddRefed<OffThreadCompilationCompleteRunnable>&& aSelf) {
|
||||||
RefPtr<OffThreadCompilationCompleteRunnable> self = aSelf;
|
RefPtr<OffThreadCompilationCompleteRunnable> self = aSelf;
|
||||||
@@ -1540,8 +1536,6 @@ class OffThreadCompilationCompleteRunnable : public Runnable {
|
|||||||
NS_DECL_NSIRUNNABLE
|
NS_DECL_NSIRUNNABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
} /* anonymous namespace */
|
|
||||||
|
|
||||||
nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
||||||
ScriptLoadRequest* aRequest, bool* aCouldCompileOut) {
|
ScriptLoadRequest* aRequest, bool* aCouldCompileOut) {
|
||||||
// If speculative parsing is enabled, the request may not be ready to run if
|
// If speculative parsing is enabled, the request may not be ready to run if
|
||||||
@@ -1578,8 +1572,17 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
|||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This uses the same heuristics and the same threshold as the
|
||||||
|
// JS::CanCompileOffThread / JS::CanDecodeOffThread APIs, but the
|
||||||
|
// heuristics needs to be updated to reflect the change regarding the
|
||||||
|
// Stencil API, and also the thread management on the consumer side
|
||||||
|
// (bug 1846160).
|
||||||
|
static constexpr size_t OffThreadMinimumTextLength = 5 * 1000;
|
||||||
|
static constexpr size_t OffThreadMinimumBytecodeLength = 5 * 1000;
|
||||||
|
|
||||||
if (aRequest->IsTextSource()) {
|
if (aRequest->IsTextSource()) {
|
||||||
if (!JS::CanCompileOffThread(cx, options, aRequest->ScriptTextLength())) {
|
if (!StaticPrefs::javascript_options_parallel_parsing() ||
|
||||||
|
aRequest->ScriptTextLength() < OffThreadMinimumTextLength) {
|
||||||
TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
|
TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
|
||||||
"scriptloader_main_thread_compile");
|
"scriptloader_main_thread_compile");
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
@@ -1589,8 +1592,8 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
|||||||
|
|
||||||
size_t length =
|
size_t length =
|
||||||
aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset;
|
aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset;
|
||||||
JS::DecodeOptions decodeOptions(options);
|
if (!StaticPrefs::javascript_options_parallel_parsing() ||
|
||||||
if (!JS::CanDecodeOffThread(cx, decodeOptions, length)) {
|
length < OffThreadMinimumBytecodeLength) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1598,23 +1601,18 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
|||||||
RefPtr<OffThreadCompilationCompleteRunnable> runnable =
|
RefPtr<OffThreadCompilationCompleteRunnable> runnable =
|
||||||
new OffThreadCompilationCompleteRunnable(aRequest, this);
|
new OffThreadCompilationCompleteRunnable(aRequest, this);
|
||||||
|
|
||||||
// Emulate dispatch. CompileOffThreadModule will call
|
|
||||||
// OffThreadCompilationCompleteCallback were we will emulate run.
|
|
||||||
LogRunnable::LogDispatch(runnable);
|
LogRunnable::LogDispatch(runnable);
|
||||||
|
|
||||||
runnable->RecordStartTime();
|
runnable->RecordStartTime();
|
||||||
|
|
||||||
JS::OffThreadToken* token = nullptr;
|
rv = StartOffThreadCompilation(cx, aRequest, options, runnable);
|
||||||
rv = StartOffThreadCompilation(cx, aRequest, options, runnable, &token);
|
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
MOZ_ASSERT(token);
|
|
||||||
|
|
||||||
aRequest->GetScriptLoadContext()->mOffThreadToken = token;
|
|
||||||
aRequest->GetScriptLoadContext()->mRunnable = runnable;
|
aRequest->GetScriptLoadContext()->mRunnable = runnable;
|
||||||
|
|
||||||
aRequest->GetScriptLoadContext()->BlockOnload(mDocument);
|
aRequest->GetScriptLoadContext()->BlockOnload(mDocument);
|
||||||
|
|
||||||
// Once the compilation is finished, a callback will dispatch the runnable to
|
// Once the compilation is finished, the runnable will be dispatched to
|
||||||
// the main thread to call ScriptLoader::ProcessOffThreadRequest for the
|
// the main thread to call ScriptLoader::ProcessOffThreadRequest for the
|
||||||
// request.
|
// request.
|
||||||
aRequest->mState = ScriptLoadRequest::State::Compiling;
|
aRequest->mState = ScriptLoadRequest::State::Compiling;
|
||||||
@@ -1635,22 +1633,239 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
|||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline nsresult CompileResultForToken(void* aToken) {
|
CompileOrDecodeTask::CompileOrDecodeTask(
|
||||||
return aToken ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
OffThreadCompilationCompleteRunnable* aCompleteRunnable)
|
||||||
|
: Task(Kind::OffMainThreadOnly, EventQueuePriority::Normal),
|
||||||
|
mMutex("CompileOrDecodeTask"),
|
||||||
|
mOptions(JS::OwningCompileOptions::ForFrontendContext()),
|
||||||
|
mCompleteRunnable(aCompleteRunnable) {}
|
||||||
|
|
||||||
|
CompileOrDecodeTask::~CompileOrDecodeTask() {
|
||||||
|
if (mFrontendContext) {
|
||||||
|
JS::DestroyFrontendContext(mFrontendContext);
|
||||||
|
mFrontendContext = nullptr;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(!mCompleteRunnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsresult CompileOrDecodeTask::InitFrontendContext() {
|
||||||
|
mFrontendContext = JS::NewFrontendContext();
|
||||||
|
if (!mFrontendContext) {
|
||||||
|
mCompleteRunnable = nullptr;
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompileOrDecodeTask::DidRunTask(const MutexAutoLock& aProofOfLock,
|
||||||
|
RefPtr<JS::Stencil>&& aStencil) {
|
||||||
|
if (aStencil) {
|
||||||
|
if (!JS::PrepareForInstantiate(mFrontendContext, *aStencil,
|
||||||
|
mInstantiationStorage)) {
|
||||||
|
aStencil = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mStencil = std::move(aStencil);
|
||||||
|
|
||||||
|
RefPtr<OffThreadCompilationCompleteRunnable> runnable = mCompleteRunnable;
|
||||||
|
mCompleteRunnable = nullptr;
|
||||||
|
|
||||||
|
LogRunnable::Run run(runnable);
|
||||||
|
|
||||||
|
runnable->RecordStopTime();
|
||||||
|
|
||||||
|
OffThreadCompilationCompleteRunnable::Dispatch(runnable.forget());
|
||||||
|
}
|
||||||
|
|
||||||
|
already_AddRefed<JS::Stencil> CompileOrDecodeTask::StealResult(
|
||||||
|
JSContext* aCx, JS::InstantiationStorage* aInstantiationStorage) {
|
||||||
|
JS::FrontendContext* fc = mFrontendContext;
|
||||||
|
mFrontendContext = nullptr;
|
||||||
|
auto destroyFrontendContext =
|
||||||
|
mozilla::MakeScopeExit([&]() { JS::DestroyFrontendContext(fc); });
|
||||||
|
|
||||||
|
MOZ_ASSERT(fc);
|
||||||
|
|
||||||
|
if (JS::HadFrontendErrors(fc)) {
|
||||||
|
(void)JS::ConvertFrontendErrorsToRuntimeErrors(aCx, fc, mOptions);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mStencil && JS::IsTranscodeFailureResult(mResult)) {
|
||||||
|
// Decode failure with bad content isn't reported as error.
|
||||||
|
JS_ReportErrorASCII(aCx, "failed to decode cache");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report warnings.
|
||||||
|
if (!JS::ConvertFrontendErrorsToRuntimeErrors(aCx, fc, mOptions)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(mStencil,
|
||||||
|
"If this task is cancelled, StealResult shouldn't be called");
|
||||||
|
|
||||||
|
// This task is started and finished successfully.
|
||||||
|
*aInstantiationStorage = std::move(mInstantiationStorage);
|
||||||
|
|
||||||
|
return mStencil.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompileOrDecodeTask::Cancel() {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
mCompleteRunnable = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class CompilationTarget { Script, Module };
|
||||||
|
|
||||||
|
template <CompilationTarget target>
|
||||||
|
class ScriptOrModuleCompileTask final : public CompileOrDecodeTask {
|
||||||
|
public:
|
||||||
|
ScriptOrModuleCompileTask(ScriptLoader::MaybeSourceText&& aMaybeSource,
|
||||||
|
OffThreadCompilationCompleteRunnable* aRunnable)
|
||||||
|
: CompileOrDecodeTask(aRunnable), mMaybeSource(std::move(aMaybeSource)) {}
|
||||||
|
|
||||||
|
nsresult Init(JS::CompileOptions& aOptions) {
|
||||||
|
nsresult rv = InitFrontendContext();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (!mOptions.copy(mFrontendContext, aOptions)) {
|
||||||
|
mCompleteRunnable = nullptr;
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Run() override {
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
if (IsCancelled(lock)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<JS::Stencil> stencil = Compile();
|
||||||
|
|
||||||
|
DidRunTask(lock, std::move(stencil));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
already_AddRefed<JS::Stencil> Compile() {
|
||||||
|
JS::SetNativeStackQuota(mFrontendContext, kDefaultStackQuota);
|
||||||
|
|
||||||
|
JS::CompilationStorage compileStorage;
|
||||||
|
auto compile = [&](auto& source) {
|
||||||
|
if constexpr (target == CompilationTarget::Script) {
|
||||||
|
return JS::CompileGlobalScriptToStencil(mFrontendContext, mOptions,
|
||||||
|
source, compileStorage);
|
||||||
|
}
|
||||||
|
return JS::CompileModuleScriptToStencil(mFrontendContext, mOptions,
|
||||||
|
source, compileStorage);
|
||||||
|
};
|
||||||
|
return mMaybeSource.mapNonEmpty(compile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
|
||||||
|
bool GetName(nsACString& aName) override {
|
||||||
|
if constexpr (target == CompilationTarget::Script) {
|
||||||
|
aName.AssignLiteral("ScriptCompileTask");
|
||||||
|
} else {
|
||||||
|
aName.AssignLiteral("ModuleCompileTask");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
ScriptLoader::MaybeSourceText mMaybeSource;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ScriptCompileTask =
|
||||||
|
class ScriptOrModuleCompileTask<CompilationTarget::Script>;
|
||||||
|
using ModuleCompileTask =
|
||||||
|
class ScriptOrModuleCompileTask<CompilationTarget::Module>;
|
||||||
|
|
||||||
|
class ScriptDecodeTask final : public CompileOrDecodeTask {
|
||||||
|
public:
|
||||||
|
ScriptDecodeTask(const JS::TranscodeRange& aRange,
|
||||||
|
OffThreadCompilationCompleteRunnable* aRunnable)
|
||||||
|
: CompileOrDecodeTask(aRunnable), mRange(aRange) {}
|
||||||
|
|
||||||
|
nsresult Init(JS::DecodeOptions& aOptions) {
|
||||||
|
nsresult rv = InitFrontendContext();
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
if (!mDecodeOptions.copy(mFrontendContext, aOptions)) {
|
||||||
|
mCompleteRunnable = nullptr;
|
||||||
|
return NS_ERROR_OUT_OF_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Run() override {
|
||||||
|
MutexAutoLock lock(mMutex);
|
||||||
|
|
||||||
|
if (IsCancelled(lock)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<JS::Stencil> stencil = Decode();
|
||||||
|
|
||||||
|
JS::OwningCompileOptions compileOptions(
|
||||||
|
(JS::OwningCompileOptions::ForFrontendContext()));
|
||||||
|
mOptions.steal(std::move(mDecodeOptions));
|
||||||
|
|
||||||
|
DidRunTask(lock, std::move(stencil));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
already_AddRefed<JS::Stencil> Decode() {
|
||||||
|
// NOTE: JS::DecodeStencil doesn't need the stack quota.
|
||||||
|
|
||||||
|
JS::CompilationStorage compileStorage;
|
||||||
|
RefPtr<JS::Stencil> stencil;
|
||||||
|
mResult = JS::DecodeStencil(mFrontendContext, mDecodeOptions, mRange,
|
||||||
|
getter_AddRefs(stencil));
|
||||||
|
return stencil.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
#ifdef MOZ_COLLECTING_RUNNABLE_TELEMETRY
|
||||||
|
bool GetName(nsACString& aName) override {
|
||||||
|
aName.AssignLiteral("ScriptDecodeTask");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
JS::OwningDecodeOptions mDecodeOptions;
|
||||||
|
|
||||||
|
JS::TranscodeRange mRange;
|
||||||
|
};
|
||||||
|
|
||||||
nsresult ScriptLoader::StartOffThreadCompilation(
|
nsresult ScriptLoader::StartOffThreadCompilation(
|
||||||
JSContext* aCx, ScriptLoadRequest* aRequest, JS::CompileOptions& aOptions,
|
JSContext* aCx, ScriptLoadRequest* aRequest, JS::CompileOptions& aOptions,
|
||||||
Runnable* aRunnable, JS::OffThreadToken** aTokenOut) {
|
OffThreadCompilationCompleteRunnable* aRunnable) {
|
||||||
const JS::OffThreadCompileCallback callback =
|
|
||||||
OffThreadCompilationCompleteCallback;
|
|
||||||
|
|
||||||
if (aRequest->IsBytecode()) {
|
if (aRequest->IsBytecode()) {
|
||||||
JS::DecodeOptions decodeOptions(aOptions);
|
JS::DecodeOptions decodeOptions(aOptions);
|
||||||
*aTokenOut = JS::DecodeStencilOffThread(
|
JS::TranscodeRange range(
|
||||||
aCx, decodeOptions, aRequest->mScriptBytecode,
|
aRequest->mScriptBytecode.begin() + aRequest->mBytecodeOffset,
|
||||||
aRequest->mBytecodeOffset, callback, aRunnable);
|
aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset);
|
||||||
return CompileResultForToken(*aTokenOut);
|
RefPtr<ScriptDecodeTask> decodeTask =
|
||||||
|
new ScriptDecodeTask(range, aRunnable);
|
||||||
|
nsresult rv = decodeTask->Init(decodeOptions);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
aRequest->GetScriptLoadContext()->mCompileOrDecodeTask = decodeTask;
|
||||||
|
TaskController::Get()->AddTask(decodeTask.forget());
|
||||||
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaybeSourceText maybeSource;
|
MaybeSourceText maybeSource;
|
||||||
@@ -1672,14 +1887,13 @@ nsresult ScriptLoader::StartOffThreadCompilation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (aRequest->IsModuleRequest()) {
|
if (aRequest->IsModuleRequest()) {
|
||||||
auto compile = [&](auto& source) {
|
RefPtr<ModuleCompileTask> compileTask =
|
||||||
return JS::CompileModuleToStencilOffThread(aCx, aOptions, source,
|
new ModuleCompileTask(std::move(maybeSource), aRunnable);
|
||||||
callback, aRunnable);
|
rv = compileTask->Init(aOptions);
|
||||||
};
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
aRequest->GetScriptLoadContext()->mCompileOrDecodeTask = compileTask;
|
||||||
MOZ_ASSERT(!maybeSource.empty());
|
TaskController::Get()->AddTask(compileTask.forget());
|
||||||
*aTokenOut = maybeSource.mapNonEmpty(compile);
|
return NS_OK;
|
||||||
return CompileResultForToken(*aTokenOut);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StaticPrefs::dom_expose_test_interfaces()) {
|
if (StaticPrefs::dom_expose_test_interfaces()) {
|
||||||
@@ -1704,27 +1918,13 @@ nsresult ScriptLoader::StartOffThreadCompilation(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto compile = [&](auto& source) {
|
RefPtr<ScriptCompileTask> compileTask =
|
||||||
return JS::CompileToStencilOffThread(aCx, aOptions, source, callback,
|
new ScriptCompileTask(std::move(maybeSource), aRunnable);
|
||||||
aRunnable);
|
rv = compileTask->Init(aOptions);
|
||||||
};
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
aRequest->GetScriptLoadContext()->mCompileOrDecodeTask = compileTask;
|
||||||
MOZ_ASSERT(!maybeSource.empty());
|
TaskController::Get()->AddTask(compileTask.forget());
|
||||||
*aTokenOut = maybeSource.mapNonEmpty(compile);
|
return NS_OK;
|
||||||
return CompileResultForToken(*aTokenOut);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScriptLoader::OffThreadCompilationCompleteCallback(
|
|
||||||
JS::OffThreadToken* aToken, void* aCallbackData) {
|
|
||||||
RefPtr<OffThreadCompilationCompleteRunnable> aRunnable =
|
|
||||||
static_cast<OffThreadCompilationCompleteRunnable*>(aCallbackData);
|
|
||||||
|
|
||||||
LogRunnable::Run run(aRunnable);
|
|
||||||
|
|
||||||
aRunnable->RecordStopTime();
|
|
||||||
aRunnable->SetToken(aToken);
|
|
||||||
|
|
||||||
OffThreadCompilationCompleteRunnable::Dispatch(aRunnable.forget());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
@@ -1733,13 +1933,12 @@ OffThreadCompilationCompleteRunnable::Run() {
|
|||||||
|
|
||||||
RefPtr<ScriptLoadContext> context = mRequest->GetScriptLoadContext();
|
RefPtr<ScriptLoadContext> context = mRequest->GetScriptLoadContext();
|
||||||
MOZ_ASSERT_IF(context->mRunnable, context->mRunnable == this);
|
MOZ_ASSERT_IF(context->mRunnable, context->mRunnable == this);
|
||||||
MOZ_ASSERT_IF(context->mOffThreadToken, context->mOffThreadToken == mToken);
|
|
||||||
|
|
||||||
// Clear the pointer to the runnable. The final reference will be released
|
// Clear the pointer to the runnable. The final reference will be released
|
||||||
// when this method returns.
|
// when this method returns.
|
||||||
context->mRunnable = nullptr;
|
context->mRunnable = nullptr;
|
||||||
|
|
||||||
if (!context->mOffThreadToken) {
|
if (!context->mCompileOrDecodeTask) {
|
||||||
// Request has been cancelled by MaybeCancelOffThreadScript.
|
// Request has been cancelled by MaybeCancelOffThreadScript.
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@@ -1783,7 +1982,7 @@ nsresult ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (aRequest->IsModuleRequest()) {
|
if (aRequest->IsModuleRequest()) {
|
||||||
MOZ_ASSERT(aRequest->GetScriptLoadContext()->mOffThreadToken);
|
MOZ_ASSERT(aRequest->GetScriptLoadContext()->mCompileOrDecodeTask);
|
||||||
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
||||||
return request->OnFetchComplete(NS_OK);
|
return request->OnFetchComplete(NS_OK);
|
||||||
}
|
}
|
||||||
@@ -1929,11 +2128,11 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
|
|||||||
mCurrentParserInsertedScript = oldParserInsertedScript;
|
mCurrentParserInsertedScript = oldParserInsertedScript;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aRequest->GetScriptLoadContext()->mOffThreadToken) {
|
if (aRequest->GetScriptLoadContext()->mCompileOrDecodeTask) {
|
||||||
// The request was parsed off-main-thread, but the result of the off
|
// The request was parsed off-main-thread, but the result of the off
|
||||||
// thread parse was not actually needed to process the request
|
// thread parse was not actually needed to process the request
|
||||||
// (disappearing window, some other error, ...). Finish the
|
// (disappearing window, some other error, ...). Finish the
|
||||||
// request to avoid leaks in the JS engine.
|
// request to avoid leaks.
|
||||||
MOZ_ASSERT(!aRequest->IsModuleRequest());
|
MOZ_ASSERT(!aRequest->IsModuleRequest());
|
||||||
aRequest->GetScriptLoadContext()->MaybeCancelOffThreadScript();
|
aRequest->GetScriptLoadContext()->MaybeCancelOffThreadScript();
|
||||||
}
|
}
|
||||||
@@ -2281,11 +2480,10 @@ nsresult ScriptLoader::CompileOrDecodeClassicScript(
|
|||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
if (aRequest->IsBytecode()) {
|
if (aRequest->IsBytecode()) {
|
||||||
if (aRequest->GetScriptLoadContext()->mOffThreadToken) {
|
if (aRequest->GetScriptLoadContext()->mCompileOrDecodeTask) {
|
||||||
LOG(("ScriptLoadRequest (%p): Decode Bytecode & Join and Execute",
|
LOG(("ScriptLoadRequest (%p): Decode Bytecode & instantiate and Execute",
|
||||||
aRequest));
|
aRequest));
|
||||||
rv = aExec.JoinOffThread(
|
rv = aExec.JoinOffThread(aRequest->GetScriptLoadContext());
|
||||||
&aRequest->GetScriptLoadContext()->mOffThreadToken);
|
|
||||||
} else {
|
} else {
|
||||||
LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute", aRequest));
|
LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute", aRequest));
|
||||||
AUTO_PROFILER_MARKER_TEXT("BytecodeDecodeMainThread", JS,
|
AUTO_PROFILER_MARKER_TEXT("BytecodeDecodeMainThread", JS,
|
||||||
@@ -2305,15 +2503,14 @@ nsresult ScriptLoader::CompileOrDecodeClassicScript(
|
|||||||
bool encodeBytecode = ShouldCacheBytecode(aRequest);
|
bool encodeBytecode = ShouldCacheBytecode(aRequest);
|
||||||
aExec.SetEncodeBytecode(encodeBytecode);
|
aExec.SetEncodeBytecode(encodeBytecode);
|
||||||
|
|
||||||
if (aRequest->GetScriptLoadContext()->mOffThreadToken) {
|
if (aRequest->GetScriptLoadContext()->mCompileOrDecodeTask) {
|
||||||
// Off-main-thread parsing.
|
// Off-main-thread parsing.
|
||||||
LOG(
|
LOG(
|
||||||
("ScriptLoadRequest (%p): Join (off-thread parsing) and "
|
("ScriptLoadRequest (%p): instantiate off-thread result and "
|
||||||
"Execute",
|
"Execute",
|
||||||
aRequest));
|
aRequest));
|
||||||
MOZ_ASSERT(aRequest->IsTextSource());
|
MOZ_ASSERT(aRequest->IsTextSource());
|
||||||
rv =
|
rv = aExec.JoinOffThread(aRequest->GetScriptLoadContext());
|
||||||
aExec.JoinOffThread(&aRequest->GetScriptLoadContext()->mOffThreadToken);
|
|
||||||
} else {
|
} else {
|
||||||
// Main thread parsing (inline and small scripts)
|
// Main thread parsing (inline and small scripts)
|
||||||
LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
|
LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
|
||||||
|
|||||||
@@ -104,6 +104,8 @@ class AsyncCompileShutdownObserver final : public nsIObserver {
|
|||||||
// Script loader implementation
|
// Script loader implementation
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class OffThreadCompilationCompleteRunnable;
|
||||||
|
|
||||||
class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
||||||
class MOZ_STACK_CLASS AutoCurrentScriptUpdater {
|
class MOZ_STACK_CLASS AutoCurrentScriptUpdater {
|
||||||
public:
|
public:
|
||||||
@@ -132,6 +134,9 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
|||||||
friend class AutoCurrentScriptUpdater;
|
friend class AutoCurrentScriptUpdater;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
using MaybeSourceText =
|
||||||
|
mozilla::MaybeOneOf<JS::SourceText<char16_t>, JS::SourceText<Utf8Unit>>;
|
||||||
|
|
||||||
explicit ScriptLoader(Document* aDocument);
|
explicit ScriptLoader(Document* aDocument);
|
||||||
|
|
||||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
@@ -569,14 +574,9 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
|||||||
nsresult AttemptOffThreadScriptCompile(ScriptLoadRequest* aRequest,
|
nsresult AttemptOffThreadScriptCompile(ScriptLoadRequest* aRequest,
|
||||||
bool* aCouldCompileOut);
|
bool* aCouldCompileOut);
|
||||||
|
|
||||||
nsresult StartOffThreadCompilation(JSContext* aCx,
|
nsresult StartOffThreadCompilation(
|
||||||
ScriptLoadRequest* aRequest,
|
JSContext* aCx, ScriptLoadRequest* aRequest, JS::CompileOptions& aOptions,
|
||||||
JS::CompileOptions& aOptions,
|
OffThreadCompilationCompleteRunnable* aRunnable);
|
||||||
Runnable* aRunnable,
|
|
||||||
JS::OffThreadToken** aTokenOut);
|
|
||||||
|
|
||||||
static void OffThreadCompilationCompleteCallback(JS::OffThreadToken* aToken,
|
|
||||||
void* aCallbackData);
|
|
||||||
|
|
||||||
nsresult ProcessRequest(ScriptLoadRequest* aRequest);
|
nsresult ProcessRequest(ScriptLoadRequest* aRequest);
|
||||||
nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
|
nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
|
||||||
@@ -678,9 +678,6 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
|||||||
|
|
||||||
void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest);
|
void MaybeMoveToLoadedList(ScriptLoadRequest* aRequest);
|
||||||
|
|
||||||
using MaybeSourceText =
|
|
||||||
mozilla::MaybeOneOf<JS::SourceText<char16_t>, JS::SourceText<Utf8Unit>>;
|
|
||||||
|
|
||||||
// Returns wether we should save the bytecode of this script after the
|
// Returns wether we should save the bytecode of this script after the
|
||||||
// execution of the script.
|
// execution of the script.
|
||||||
static bool ShouldCacheBytecode(ScriptLoadRequest* aRequest);
|
static bool ShouldCacheBytecode(ScriptLoadRequest* aRequest);
|
||||||
|
|||||||
@@ -1259,7 +1259,7 @@ nsresult ModuleLoaderBase::EvaluateModuleInContext(
|
|||||||
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
||||||
MOZ_ASSERT(request->mModuleScript);
|
MOZ_ASSERT(request->mModuleScript);
|
||||||
MOZ_ASSERT_IF(request->HasScriptLoadContext(),
|
MOZ_ASSERT_IF(request->HasScriptLoadContext(),
|
||||||
!request->GetScriptLoadContext()->mOffThreadToken);
|
!request->GetScriptLoadContext()->mCompileOrDecodeTask);
|
||||||
|
|
||||||
ModuleScript* moduleScript = request->mModuleScript;
|
ModuleScript* moduleScript = request->mModuleScript;
|
||||||
if (moduleScript->HasErrorToRethrow()) {
|
if (moduleScript->HasErrorToRethrow()) {
|
||||||
|
|||||||
@@ -89,6 +89,11 @@ struct InstantiationStorage {
|
|||||||
|
|
||||||
~InstantiationStorage();
|
~InstantiationStorage();
|
||||||
|
|
||||||
|
void operator=(InstantiationStorage&& other) {
|
||||||
|
gcOutput_ = other.gcOutput_;
|
||||||
|
other.gcOutput_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
InstantiationStorage(const InstantiationStorage& other) = delete;
|
InstantiationStorage(const InstantiationStorage& other) = delete;
|
||||||
void operator=(const InstantiationStorage& aOther) = delete;
|
void operator=(const InstantiationStorage& aOther) = delete;
|
||||||
|
|||||||
Reference in New Issue
Block a user