mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
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:
parent
3aed59e248
commit
70c40a1658
@ -21,7 +21,6 @@
|
||||
#include "js/Conversions.h"
|
||||
#include "js/experimental/JSStencil.h"
|
||||
#include "js/HeapAPI.h"
|
||||
#include "js/OffThreadScriptCompilation.h"
|
||||
#include "js/ProfilingCategory.h"
|
||||
#include "js/Promise.h"
|
||||
#include "js/SourceText.h"
|
||||
@ -30,6 +29,7 @@
|
||||
#include "js/Wrapper.h"
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/dom/ScriptLoadContext.h"
|
||||
#include "mozilla/Likely.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsTPromiseFlatString.h"
|
||||
@ -87,8 +87,7 @@ JSExecutionContext::JSExecutionContext(
|
||||
}
|
||||
}
|
||||
|
||||
nsresult JSExecutionContext::JoinOffThread(
|
||||
JS::OffThreadToken** aOffThreadToken) {
|
||||
nsresult JSExecutionContext::JoinOffThread(ScriptLoadContext* aContext) {
|
||||
if (mSkip) {
|
||||
return mRv;
|
||||
}
|
||||
@ -96,9 +95,7 @@ nsresult JSExecutionContext::JoinOffThread(
|
||||
MOZ_ASSERT(!mWantsReturnValue);
|
||||
|
||||
JS::InstantiationStorage storage;
|
||||
RefPtr<JS::Stencil> stencil =
|
||||
JS::FinishOffThreadStencil(mCx, *aOffThreadToken, &storage);
|
||||
*aOffThreadToken = nullptr; // Mark the token as having been finished.
|
||||
RefPtr<JS::Stencil> stencil = aContext->StealOffThreadResult(mCx, &storage);
|
||||
if (!stencil) {
|
||||
mSkip = true;
|
||||
mRv = EvaluationExceptionToNSResult(mCx);
|
||||
|
@ -8,7 +8,6 @@
|
||||
#define DOM_BASE_JSEXECUTIONCONTEXT_H_
|
||||
|
||||
#include "js/GCVector.h"
|
||||
#include "js/OffThreadScriptCompilation.h"
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/Value.h"
|
||||
#include "js/experimental/JSStencil.h"
|
||||
@ -30,6 +29,8 @@ union Utf8Unit;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class ScriptLoadContext;
|
||||
|
||||
class MOZ_STACK_CLASS JSExecutionContext final {
|
||||
// Register stack annotations for the Gecko profiler.
|
||||
mozilla::AutoProfilerLabel mAutoProfilerLabel;
|
||||
@ -128,9 +129,9 @@ class MOZ_STACK_CLASS JSExecutionContext final {
|
||||
}
|
||||
|
||||
// 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
|
||||
// thread.
|
||||
[[nodiscard]] nsresult JoinOffThread(JS::OffThreadToken** aOffThreadToken);
|
||||
// this function will take the result of the off-thread operation and move it
|
||||
// to the main thread.
|
||||
[[nodiscard]] nsresult JoinOffThread(ScriptLoadContext* aContext);
|
||||
|
||||
// Compile a script contained in a SourceText.
|
||||
nsresult Compile(JS::SourceText<char16_t>& aSrcBuf);
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include "js/experimental/JSStencil.h" // JS::Stencil, JS::CompileModuleScriptToStencil, JS::InstantiateModuleStencil
|
||||
#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/OffThreadScriptCompilation.h"
|
||||
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
||||
#include "js/Realm.h"
|
||||
#include "js/SourceText.h"
|
||||
@ -150,11 +149,8 @@ nsresult ModuleLoader::CompileFetchedModule(
|
||||
ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleOut) {
|
||||
if (aRequest->GetScriptLoadContext()->mWasCompiledOMT) {
|
||||
JS::InstantiationStorage storage;
|
||||
RefPtr<JS::Stencil> stencil = JS::FinishOffThreadStencil(
|
||||
aCx, aRequest->GetScriptLoadContext()->mOffThreadToken, &storage);
|
||||
|
||||
aRequest->GetScriptLoadContext()->mOffThreadToken = nullptr;
|
||||
|
||||
RefPtr<JS::Stencil> stencil =
|
||||
aRequest->GetScriptLoadContext()->StealOffThreadResult(aCx, &storage);
|
||||
if (!stencil) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||
|
||||
#include "js/OffThreadScriptCompilation.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/loader/LoadContextBase.h"
|
||||
#include "js/loader/ModuleLoadRequest.h"
|
||||
@ -37,7 +36,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadContext)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ScriptLoadContext,
|
||||
JS::loader::LoadContextBase)
|
||||
MOZ_ASSERT(!tmp->mOffThreadToken);
|
||||
MOZ_ASSERT(!tmp->mCompileOrDecodeTask);
|
||||
MOZ_ASSERT(!tmp->mRunnable);
|
||||
tmp->MaybeUnblockOnload();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
@ -62,7 +61,6 @@ ScriptLoadContext::ScriptLoadContext()
|
||||
mInCompilingList(false),
|
||||
mIsTracking(false),
|
||||
mWasCompiledOMT(false),
|
||||
mOffThreadToken(nullptr),
|
||||
mRunnable(nullptr),
|
||||
mLineNo(1),
|
||||
mColumnNo(0),
|
||||
@ -72,8 +70,8 @@ ScriptLoadContext::ScriptLoadContext()
|
||||
ScriptLoadContext::~ScriptLoadContext() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
// Off-thread parsing must have completed by this point.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mOffThreadToken && !mRunnable);
|
||||
// Off-thread parsing must have completed or cancelled by this point.
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mCompileOrDecodeTask && !mRunnable);
|
||||
|
||||
mRequest = nullptr;
|
||||
|
||||
@ -96,15 +94,13 @@ void ScriptLoadContext::MaybeUnblockOnload() {
|
||||
void ScriptLoadContext::MaybeCancelOffThreadScript() {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!mOffThreadToken) {
|
||||
if (!mCompileOrDecodeTask) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel parse if it hasn't been started yet or wait for it to finish and
|
||||
// clean up finished parse data.
|
||||
JSContext* cx = danger::GetJSContext();
|
||||
JS::CancelOffThreadToken(cx, mOffThreadToken);
|
||||
mOffThreadToken = nullptr;
|
||||
// Cancel the task if it hasn't been started yet or wait for it to finish.
|
||||
mCompileOrDecodeTask->Cancel();
|
||||
mCompileOrDecodeTask = nullptr;
|
||||
|
||||
// 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
|
||||
@ -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
|
||||
|
@ -8,11 +8,15 @@
|
||||
#define mozilla_dom_ScriptLoadContext_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/SourceText.h"
|
||||
#include "js/Transcoding.h" // JS::TranscodeResult
|
||||
#include "js/TypeDecls.h"
|
||||
#include "js/loader/LoadContextBase.h"
|
||||
#include "js/loader/ScriptKind.h"
|
||||
#include "mozilla/AlreadyAddRefed.h"
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/CORSMode.h"
|
||||
@ -20,9 +24,12 @@
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/MaybeOneOf.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/PreloaderBase.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||
#include "mozilla/TaskController.h" // mozilla::Task
|
||||
#include "mozilla/Utf8.h" // mozilla::Utf8Unit
|
||||
#include "mozilla/Variant.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "nsCOMPtr.h"
|
||||
@ -30,10 +37,7 @@
|
||||
#include "nsIScriptElement.h"
|
||||
|
||||
class nsICacheInfoChannel;
|
||||
|
||||
namespace JS {
|
||||
class OffThreadToken;
|
||||
} // namespace JS
|
||||
struct JSContext;
|
||||
|
||||
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,
|
||||
public PreloaderBase {
|
||||
protected:
|
||||
@ -95,10 +164,6 @@ class ScriptLoadContext : public JS::loader::LoadContextBase,
|
||||
|
||||
bool CompileStarted() const;
|
||||
|
||||
JS::OffThreadToken** OffThreadTokenPtr() {
|
||||
return mOffThreadToken ? &mOffThreadToken : nullptr;
|
||||
}
|
||||
|
||||
bool IsTracking() const { return mIsTracking; }
|
||||
void SetIsTracking() {
|
||||
MOZ_ASSERT(!mIsTracking);
|
||||
@ -154,6 +219,11 @@ class ScriptLoadContext : public JS::loader::LoadContextBase,
|
||||
|
||||
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.
|
||||
bool mScriptFromHead; // Synchronous head script block loading of other non
|
||||
// js/css content.
|
||||
@ -170,9 +240,12 @@ class ScriptLoadContext : public JS::loader::LoadContextBase,
|
||||
bool mWasCompiledOMT; // True if the script has been compiled off main
|
||||
// thread.
|
||||
|
||||
// Off-thread parsing token. Set at the start of off-thread parsing and
|
||||
// cleared when the result of the parse is used.
|
||||
JS::OffThreadToken* mOffThreadToken;
|
||||
// Task that performs off-thread compilation or off-thread decode.
|
||||
// This field is used to take the result of the task, or cancel the task.
|
||||
//
|
||||
// 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
|
||||
// completes.
|
||||
|
@ -23,19 +23,21 @@
|
||||
#include "js/ColumnNumber.h" // #include "js/CompilationAndEvaluation.h"
|
||||
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/ContextOptions.h" // JS::ContextOptionsRef
|
||||
#include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
|
||||
#include "js/CompileOptions.h" // JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::OwningDecodeOptions, JS::DelazificationOption
|
||||
#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/loader/ScriptLoadRequest.h"
|
||||
#include "ScriptCompression.h"
|
||||
#include "js/loader/LoadedScript.h"
|
||||
#include "js/loader/ModuleLoadRequest.h"
|
||||
#include "js/MemoryFunctions.h"
|
||||
#include "js/Modules.h"
|
||||
#include "js/OffThreadScriptCompilation.h"
|
||||
#include "js/PropertyAndElement.h" // JS_DefineProperty
|
||||
#include "js/Realm.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/Transcoding.h"
|
||||
#include "js/Transcoding.h" // JS::TranscodeRange, JS::TranscodeResult, JS::IsTranscodeFailureResult
|
||||
#include "js/Utility.h"
|
||||
#include "xpcpublic.h"
|
||||
#include "GeckoProfiler.h"
|
||||
@ -51,6 +53,7 @@
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/SRILogHelper.h"
|
||||
#include "mozilla/dom/WindowContext.h"
|
||||
#include "mozilla/Mutex.h" // mozilla::Mutex
|
||||
#include "mozilla/net/UrlClassifierFeatureFactory.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/StaticPrefs_dom.h"
|
||||
@ -89,12 +92,14 @@
|
||||
#include "nsMimeTypes.h"
|
||||
#include "mozilla/ConsoleReportCollector.h"
|
||||
#include "mozilla/CycleCollectedJSContext.h"
|
||||
#include "mozilla/EventQueue.h"
|
||||
#include "mozilla/LoadInfo.h"
|
||||
#include "ReferrerInfo.h"
|
||||
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/ScopeExit.h"
|
||||
#include "mozilla/TaskController.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/TimeStamp.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
@ -1481,7 +1486,7 @@ nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
|
||||
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
|
||||
"Processing requests when running scripts is unsafe.");
|
||||
|
||||
if (!aRequest->GetScriptLoadContext()->mOffThreadToken &&
|
||||
if (!aRequest->GetScriptLoadContext()->mCompileOrDecodeTask &&
|
||||
!aRequest->GetScriptLoadContext()->CompileStarted()) {
|
||||
bool couldCompile = false;
|
||||
nsresult rv = AttemptOffThreadScriptCompile(aRequest, &couldCompile);
|
||||
@ -1498,13 +1503,10 @@ nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
|
||||
return ProcessRequest(aRequest);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class OffThreadCompilationCompleteRunnable : public Runnable {
|
||||
nsMainThreadPtrHandle<ScriptLoadRequest> mRequest;
|
||||
nsMainThreadPtrHandle<ScriptLoader> mLoader;
|
||||
nsCOMPtr<nsISerialEventTarget> mEventTarget;
|
||||
JS::OffThreadToken* mToken;
|
||||
TimeStamp mStartTime;
|
||||
TimeStamp mStopTime;
|
||||
|
||||
@ -1514,8 +1516,7 @@ class OffThreadCompilationCompleteRunnable : public Runnable {
|
||||
: Runnable("dom::OffThreadCompilationCompleteRunnable"),
|
||||
mRequest(
|
||||
new nsMainThreadPtrHolder<ScriptLoadRequest>("mRequest", aRequest)),
|
||||
mLoader(new nsMainThreadPtrHolder<ScriptLoader>("mLoader", aLoader)),
|
||||
mToken(nullptr) {
|
||||
mLoader(new nsMainThreadPtrHolder<ScriptLoader>("mLoader", aLoader)) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (DocGroup* docGroup = aLoader->GetDocGroup()) {
|
||||
mEventTarget = docGroup->EventTargetFor(TaskCategory::Other);
|
||||
@ -1525,11 +1526,6 @@ class OffThreadCompilationCompleteRunnable : public Runnable {
|
||||
void RecordStartTime() { mStartTime = TimeStamp::Now(); }
|
||||
void RecordStopTime() { mStopTime = TimeStamp::Now(); }
|
||||
|
||||
void SetToken(JS::OffThreadToken* aToken) {
|
||||
MOZ_ASSERT(aToken && !mToken);
|
||||
mToken = aToken;
|
||||
}
|
||||
|
||||
static void Dispatch(
|
||||
already_AddRefed<OffThreadCompilationCompleteRunnable>&& aSelf) {
|
||||
RefPtr<OffThreadCompilationCompleteRunnable> self = aSelf;
|
||||
@ -1540,8 +1536,6 @@ class OffThreadCompilationCompleteRunnable : public Runnable {
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
||||
ScriptLoadRequest* aRequest, bool* aCouldCompileOut) {
|
||||
// If speculative parsing is enabled, the request may not be ready to run if
|
||||
@ -1578,8 +1572,17 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
||||
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 (!JS::CanCompileOffThread(cx, options, aRequest->ScriptTextLength())) {
|
||||
if (!StaticPrefs::javascript_options_parallel_parsing() ||
|
||||
aRequest->ScriptTextLength() < OffThreadMinimumTextLength) {
|
||||
TRACE_FOR_TEST(aRequest->GetScriptLoadContext()->GetScriptElement(),
|
||||
"scriptloader_main_thread_compile");
|
||||
return NS_OK;
|
||||
@ -1589,8 +1592,8 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
||||
|
||||
size_t length =
|
||||
aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset;
|
||||
JS::DecodeOptions decodeOptions(options);
|
||||
if (!JS::CanDecodeOffThread(cx, decodeOptions, length)) {
|
||||
if (!StaticPrefs::javascript_options_parallel_parsing() ||
|
||||
length < OffThreadMinimumBytecodeLength) {
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
@ -1598,23 +1601,18 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
||||
RefPtr<OffThreadCompilationCompleteRunnable> runnable =
|
||||
new OffThreadCompilationCompleteRunnable(aRequest, this);
|
||||
|
||||
// Emulate dispatch. CompileOffThreadModule will call
|
||||
// OffThreadCompilationCompleteCallback were we will emulate run.
|
||||
LogRunnable::LogDispatch(runnable);
|
||||
|
||||
runnable->RecordStartTime();
|
||||
|
||||
JS::OffThreadToken* token = nullptr;
|
||||
rv = StartOffThreadCompilation(cx, aRequest, options, runnable, &token);
|
||||
rv = StartOffThreadCompilation(cx, aRequest, options, runnable);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(token);
|
||||
|
||||
aRequest->GetScriptLoadContext()->mOffThreadToken = token;
|
||||
aRequest->GetScriptLoadContext()->mRunnable = runnable;
|
||||
|
||||
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
|
||||
// request.
|
||||
aRequest->mState = ScriptLoadRequest::State::Compiling;
|
||||
@ -1635,22 +1633,239 @@ nsresult ScriptLoader::AttemptOffThreadScriptCompile(
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static inline nsresult CompileResultForToken(void* aToken) {
|
||||
return aToken ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
||||
CompileOrDecodeTask::CompileOrDecodeTask(
|
||||
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(
|
||||
JSContext* aCx, ScriptLoadRequest* aRequest, JS::CompileOptions& aOptions,
|
||||
Runnable* aRunnable, JS::OffThreadToken** aTokenOut) {
|
||||
const JS::OffThreadCompileCallback callback =
|
||||
OffThreadCompilationCompleteCallback;
|
||||
|
||||
OffThreadCompilationCompleteRunnable* aRunnable) {
|
||||
if (aRequest->IsBytecode()) {
|
||||
JS::DecodeOptions decodeOptions(aOptions);
|
||||
*aTokenOut = JS::DecodeStencilOffThread(
|
||||
aCx, decodeOptions, aRequest->mScriptBytecode,
|
||||
aRequest->mBytecodeOffset, callback, aRunnable);
|
||||
return CompileResultForToken(*aTokenOut);
|
||||
JS::TranscodeRange range(
|
||||
aRequest->mScriptBytecode.begin() + aRequest->mBytecodeOffset,
|
||||
aRequest->mScriptBytecode.length() - aRequest->mBytecodeOffset);
|
||||
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;
|
||||
@ -1672,14 +1887,13 @@ nsresult ScriptLoader::StartOffThreadCompilation(
|
||||
}
|
||||
|
||||
if (aRequest->IsModuleRequest()) {
|
||||
auto compile = [&](auto& source) {
|
||||
return JS::CompileModuleToStencilOffThread(aCx, aOptions, source,
|
||||
callback, aRunnable);
|
||||
};
|
||||
|
||||
MOZ_ASSERT(!maybeSource.empty());
|
||||
*aTokenOut = maybeSource.mapNonEmpty(compile);
|
||||
return CompileResultForToken(*aTokenOut);
|
||||
RefPtr<ModuleCompileTask> compileTask =
|
||||
new ModuleCompileTask(std::move(maybeSource), aRunnable);
|
||||
rv = compileTask->Init(aOptions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aRequest->GetScriptLoadContext()->mCompileOrDecodeTask = compileTask;
|
||||
TaskController::Get()->AddTask(compileTask.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (StaticPrefs::dom_expose_test_interfaces()) {
|
||||
@ -1704,27 +1918,13 @@ nsresult ScriptLoader::StartOffThreadCompilation(
|
||||
}
|
||||
}
|
||||
|
||||
auto compile = [&](auto& source) {
|
||||
return JS::CompileToStencilOffThread(aCx, aOptions, source, callback,
|
||||
aRunnable);
|
||||
};
|
||||
|
||||
MOZ_ASSERT(!maybeSource.empty());
|
||||
*aTokenOut = maybeSource.mapNonEmpty(compile);
|
||||
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());
|
||||
RefPtr<ScriptCompileTask> compileTask =
|
||||
new ScriptCompileTask(std::move(maybeSource), aRunnable);
|
||||
rv = compileTask->Init(aOptions);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
aRequest->GetScriptLoadContext()->mCompileOrDecodeTask = compileTask;
|
||||
TaskController::Get()->AddTask(compileTask.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1733,13 +1933,12 @@ OffThreadCompilationCompleteRunnable::Run() {
|
||||
|
||||
RefPtr<ScriptLoadContext> context = mRequest->GetScriptLoadContext();
|
||||
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
|
||||
// when this method returns.
|
||||
context->mRunnable = nullptr;
|
||||
|
||||
if (!context->mOffThreadToken) {
|
||||
if (!context->mCompileOrDecodeTask) {
|
||||
// Request has been cancelled by MaybeCancelOffThreadScript.
|
||||
return NS_OK;
|
||||
}
|
||||
@ -1783,7 +1982,7 @@ nsresult ScriptLoader::ProcessOffThreadRequest(ScriptLoadRequest* aRequest) {
|
||||
}
|
||||
|
||||
if (aRequest->IsModuleRequest()) {
|
||||
MOZ_ASSERT(aRequest->GetScriptLoadContext()->mOffThreadToken);
|
||||
MOZ_ASSERT(aRequest->GetScriptLoadContext()->mCompileOrDecodeTask);
|
||||
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
||||
return request->OnFetchComplete(NS_OK);
|
||||
}
|
||||
@ -1929,11 +2128,11 @@ nsresult ScriptLoader::ProcessRequest(ScriptLoadRequest* aRequest) {
|
||||
mCurrentParserInsertedScript = oldParserInsertedScript;
|
||||
}
|
||||
|
||||
if (aRequest->GetScriptLoadContext()->mOffThreadToken) {
|
||||
if (aRequest->GetScriptLoadContext()->mCompileOrDecodeTask) {
|
||||
// The request was parsed off-main-thread, but the result of the off
|
||||
// thread parse was not actually needed to process the request
|
||||
// (disappearing window, some other error, ...). Finish the
|
||||
// request to avoid leaks in the JS engine.
|
||||
// request to avoid leaks.
|
||||
MOZ_ASSERT(!aRequest->IsModuleRequest());
|
||||
aRequest->GetScriptLoadContext()->MaybeCancelOffThreadScript();
|
||||
}
|
||||
@ -2281,11 +2480,10 @@ nsresult ScriptLoader::CompileOrDecodeClassicScript(
|
||||
|
||||
nsresult rv;
|
||||
if (aRequest->IsBytecode()) {
|
||||
if (aRequest->GetScriptLoadContext()->mOffThreadToken) {
|
||||
LOG(("ScriptLoadRequest (%p): Decode Bytecode & Join and Execute",
|
||||
if (aRequest->GetScriptLoadContext()->mCompileOrDecodeTask) {
|
||||
LOG(("ScriptLoadRequest (%p): Decode Bytecode & instantiate and Execute",
|
||||
aRequest));
|
||||
rv = aExec.JoinOffThread(
|
||||
&aRequest->GetScriptLoadContext()->mOffThreadToken);
|
||||
rv = aExec.JoinOffThread(aRequest->GetScriptLoadContext());
|
||||
} else {
|
||||
LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute", aRequest));
|
||||
AUTO_PROFILER_MARKER_TEXT("BytecodeDecodeMainThread", JS,
|
||||
@ -2305,15 +2503,14 @@ nsresult ScriptLoader::CompileOrDecodeClassicScript(
|
||||
bool encodeBytecode = ShouldCacheBytecode(aRequest);
|
||||
aExec.SetEncodeBytecode(encodeBytecode);
|
||||
|
||||
if (aRequest->GetScriptLoadContext()->mOffThreadToken) {
|
||||
if (aRequest->GetScriptLoadContext()->mCompileOrDecodeTask) {
|
||||
// Off-main-thread parsing.
|
||||
LOG(
|
||||
("ScriptLoadRequest (%p): Join (off-thread parsing) and "
|
||||
("ScriptLoadRequest (%p): instantiate off-thread result and "
|
||||
"Execute",
|
||||
aRequest));
|
||||
MOZ_ASSERT(aRequest->IsTextSource());
|
||||
rv =
|
||||
aExec.JoinOffThread(&aRequest->GetScriptLoadContext()->mOffThreadToken);
|
||||
rv = aExec.JoinOffThread(aRequest->GetScriptLoadContext());
|
||||
} else {
|
||||
// Main thread parsing (inline and small scripts)
|
||||
LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
|
||||
|
@ -104,6 +104,8 @@ class AsyncCompileShutdownObserver final : public nsIObserver {
|
||||
// Script loader implementation
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
class OffThreadCompilationCompleteRunnable;
|
||||
|
||||
class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
||||
class MOZ_STACK_CLASS AutoCurrentScriptUpdater {
|
||||
public:
|
||||
@ -132,6 +134,9 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
||||
friend class AutoCurrentScriptUpdater;
|
||||
|
||||
public:
|
||||
using MaybeSourceText =
|
||||
mozilla::MaybeOneOf<JS::SourceText<char16_t>, JS::SourceText<Utf8Unit>>;
|
||||
|
||||
explicit ScriptLoader(Document* aDocument);
|
||||
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
@ -569,14 +574,9 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
||||
nsresult AttemptOffThreadScriptCompile(ScriptLoadRequest* aRequest,
|
||||
bool* aCouldCompileOut);
|
||||
|
||||
nsresult StartOffThreadCompilation(JSContext* aCx,
|
||||
ScriptLoadRequest* aRequest,
|
||||
JS::CompileOptions& aOptions,
|
||||
Runnable* aRunnable,
|
||||
JS::OffThreadToken** aTokenOut);
|
||||
|
||||
static void OffThreadCompilationCompleteCallback(JS::OffThreadToken* aToken,
|
||||
void* aCallbackData);
|
||||
nsresult StartOffThreadCompilation(
|
||||
JSContext* aCx, ScriptLoadRequest* aRequest, JS::CompileOptions& aOptions,
|
||||
OffThreadCompilationCompleteRunnable* aRunnable);
|
||||
|
||||
nsresult ProcessRequest(ScriptLoadRequest* aRequest);
|
||||
nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
|
||||
@ -678,9 +678,6 @@ class ScriptLoader final : public JS::loader::ScriptLoaderInterface {
|
||||
|
||||
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
|
||||
// execution of the script.
|
||||
static bool ShouldCacheBytecode(ScriptLoadRequest* aRequest);
|
||||
|
@ -1259,7 +1259,7 @@ nsresult ModuleLoaderBase::EvaluateModuleInContext(
|
||||
ModuleLoadRequest* request = aRequest->AsModuleRequest();
|
||||
MOZ_ASSERT(request->mModuleScript);
|
||||
MOZ_ASSERT_IF(request->HasScriptLoadContext(),
|
||||
!request->GetScriptLoadContext()->mOffThreadToken);
|
||||
!request->GetScriptLoadContext()->mCompileOrDecodeTask);
|
||||
|
||||
ModuleScript* moduleScript = request->mModuleScript;
|
||||
if (moduleScript->HasErrorToRethrow()) {
|
||||
|
@ -89,6 +89,11 @@ struct InstantiationStorage {
|
||||
|
||||
~InstantiationStorage();
|
||||
|
||||
void operator=(InstantiationStorage&& other) {
|
||||
gcOutput_ = other.gcOutput_;
|
||||
other.gcOutput_ = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
InstantiationStorage(const InstantiationStorage& other) = delete;
|
||||
void operator=(const InstantiationStorage& aOther) = delete;
|
||||
|
Loading…
Reference in New Issue
Block a user