diff options
author | Brian Smith <brian@dbsoft.org> | 2023-04-20 10:15:52 -0500 |
---|---|---|
committer | Brian Smith <brian@dbsoft.org> | 2023-04-27 13:34:48 -0500 |
commit | e29f4488b1d8d33f3ca04531177d1c1690f57577 (patch) | |
tree | 9a96ac5db0e3c7b8b2edf2cc416eebd3bf61ffad /dom | |
parent | 8431e52405bf4cd9d34eae9940cb665c7faa2c7e (diff) | |
download | uxp-e29f4488b1d8d33f3ca04531177d1c1690f57577.tar.gz |
Issue #1691 - Part 7f: Split up compile and execute so we can use ClassicScript.
https://bugzilla.mozilla.org/show_bug.cgi?id=1342012
Refactor nsJSUtils::ExecutionContext to separate compilation and execution steps and allow extraction of compiled JSScript.
https://bugzilla.mozilla.org/show_bug.cgi?id=1366773
Move buffer argument from JS::StartIncrementalEncoding to JS::FinishIncrementalEncoding.
(cherry picked from commit d4b520b08b958e116dfeeffdc61f3425610d1ce9)
Diffstat (limited to 'dom')
-rw-r--r-- | dom/base/nsJSUtils.cpp | 129 | ||||
-rw-r--r-- | dom/base/nsJSUtils.h | 82 | ||||
-rw-r--r-- | dom/script/ScriptLoader.cpp | 17 |
3 files changed, 131 insertions, 97 deletions
diff --git a/dom/base/nsJSUtils.cpp b/dom/base/nsJSUtils.cpp index efd2614caf..467a5b0b53 100644 --- a/dom/base/nsJSUtils.cpp +++ b/dom/base/nsJSUtils.cpp @@ -138,12 +138,14 @@ nsJSUtils::ExecutionContext::ExecutionContext(JSContext* aCx, , mCompartment(aCx, aGlobal) , mRetValue(aCx) , mScopeChain(aCx) + , mScript(aCx) , mRv(NS_OK) , mSkip(false) , mCoerceToString(false) #ifdef DEBUG , mWantsReturnValue(false) , mExpectScopeChain(false) + , mScriptUsed(false) #endif { MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); @@ -188,8 +190,7 @@ nsJSUtils::ExecutionContext::SetScopeChain( } nsresult -nsJSUtils::ExecutionContext::JoinAndExec(void **aOffThreadToken, - JS::MutableHandle<JSScript*> aScript) +nsJSUtils::ExecutionContext::JoinCompile(void** aOffThreadToken) { if (mSkip) { return mRv; @@ -197,45 +198,66 @@ nsJSUtils::ExecutionContext::JoinAndExec(void **aOffThreadToken, MOZ_ASSERT(!mWantsReturnValue); MOZ_ASSERT(!mExpectScopeChain); - aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken)); + MOZ_ASSERT(!mScript); + mScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken)); *aOffThreadToken = nullptr; // Mark the token as having been finished. - if (!aScript || !JS_ExecuteScript(mCx, mScopeChain, aScript)) { + if (!mScript) { mSkip = true; mRv = EvaluationExceptionToNSResult(mCx); return mRv; } + if (!StartIncrementalEncoding(mCx, mScript)) { + mSkip = true; + mRv = EvaluationExceptionToNSResult(mCx); + return mRv; + } + return NS_OK; } nsresult -nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions, - JS::SourceBufferHolder& aSrcBuf) +nsJSUtils::ExecutionContext::Compile(JS::CompileOptions& aCompileOptions, + JS::SourceBufferHolder& aSrcBuf) { if (mSkip) { return mRv; } - - MOZ_ASSERT_IF(aCompileOptions.versionSet, - aCompileOptions.version != JSVERSION_UNKNOWN); + MOZ_ASSERT(aSrcBuf.get()); MOZ_ASSERT(mRetValue.isUndefined()); #ifdef DEBUG mWantsReturnValue = !aCompileOptions.noScriptRval; #endif - MOZ_ASSERT(!mCoerceToString || mWantsReturnValue); - if (!JS::Evaluate(mCx, mScopeChain, aCompileOptions, aSrcBuf, &mRetValue)) { + + MOZ_ASSERT(!mScript); + bool compiled = true; + if (mScopeChain.length() == 0) { + compiled = JS::Compile(mCx, aCompileOptions, aSrcBuf, &mScript); + } else { + compiled = JS::CompileForNonSyntacticScope(mCx, aCompileOptions, aSrcBuf, + &mScript); + } + + MOZ_ASSERT_IF(compiled, mScript); + if (!compiled) { mSkip = true; mRv = EvaluationExceptionToNSResult(mCx); return mRv; } - + + if (!StartIncrementalEncoding(mCx, mScript)) { + mSkip = true; + mRv = EvaluationExceptionToNSResult(mCx); + return mRv; + } + return NS_OK; } nsresult -nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions, - const nsAString& aScript) +nsJSUtils::ExecutionContext::Compile(JS::CompileOptions& aCompileOptions, + const nsAString& aScript) { if (mSkip) { return mRv; @@ -244,21 +266,20 @@ nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions, const nsPromiseFlatString& flatScript = PromiseFlatString(aScript); JS::SourceBufferHolder srcBuf(flatScript.get(), aScript.Length(), JS::SourceBufferHolder::NoOwnership); - return CompileAndExec(aCompileOptions, srcBuf); + return Compile(aCompileOptions, srcBuf); } nsresult -nsJSUtils::ExecutionContext::DecodeAndExec(JS::CompileOptions& aCompileOptions, - mozilla::Vector<uint8_t>& aBytecodeBuf, - size_t aBytecodeIndex) +nsJSUtils::ExecutionContext::Decode(JS::CompileOptions& aCompileOptions, + mozilla::Vector<uint8_t>& aBytecodeBuf, + size_t aBytecodeIndex) { if (mSkip) { return mRv; } MOZ_ASSERT(!mWantsReturnValue); - JS::Rooted<JSScript*> script(mCx); - JS::TranscodeResult tr = JS::DecodeScript(mCx, aBytecodeBuf, &script, aBytecodeIndex); + JS::TranscodeResult tr = JS::DecodeScript(mCx, aBytecodeBuf, &mScript, aBytecodeIndex); // These errors are external parameters which should be handled before the // decoding phase, and which are the only reasons why you might want to // fallback on decoding failures. @@ -270,17 +291,11 @@ nsJSUtils::ExecutionContext::DecodeAndExec(JS::CompileOptions& aCompileOptions, return mRv; } - if (!JS_ExecuteScript(mCx, mScopeChain, script)) { - mSkip = true; - mRv = EvaluationExceptionToNSResult(mCx); - return mRv; - } - return mRv; } nsresult -nsJSUtils::ExecutionContext::DecodeJoinAndExec(void **aOffThreadToken) +nsJSUtils::ExecutionContext::JoinDecode(void **aOffThreadToken) { if (mSkip) { return mRv; @@ -288,10 +303,9 @@ nsJSUtils::ExecutionContext::DecodeJoinAndExec(void **aOffThreadToken) MOZ_ASSERT(!mWantsReturnValue); MOZ_ASSERT(!mExpectScopeChain); - JS::Rooted<JSScript*> script(mCx); - script.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken)); + mScript.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken)); *aOffThreadToken = nullptr; // Mark the token as having been finished. - if (!script || !JS_ExecuteScript(mCx, mScopeChain, script)) { + if (!mScript) { mSkip = true; mRv = EvaluationExceptionToNSResult(mCx); return mRv; @@ -300,54 +314,53 @@ nsJSUtils::ExecutionContext::DecodeJoinAndExec(void **aOffThreadToken) return NS_OK; } -nsresult -nsJSUtils::ExecutionContext::JoinEncodeAndExec(void **aOffThreadToken, - mozilla::Vector<uint8_t>& aBytecodeBuf, - JS::MutableHandle<JSScript*> aScript) -{ - MOZ_ASSERT_IF(aOffThreadToken, !mWantsReturnValue); - aScript.set(JS::FinishOffThreadScript(mCx, *aOffThreadToken)); - *aOffThreadToken = nullptr; // Mark the token as having been finished. - if (!aScript) { - mSkip = true; - mRv = EvaluationExceptionToNSResult(mCx); - return mRv; - } +JSScript* nsJSUtils::ExecutionContext::GetScript() { +#ifdef DEBUG + MOZ_ASSERT(!mSkip); + MOZ_ASSERT(mScript); + mScriptUsed = true; +#endif - if (!StartIncrementalEncoding(mCx, aBytecodeBuf, aScript)) { - mSkip = true; - mRv = EvaluationExceptionToNSResult(mCx); + return mScript; +} + +nsresult nsJSUtils::ExecutionContext::ExecScript() { + if (mSkip) { return mRv; } - if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) { + MOZ_ASSERT(mScript); + + if (!JS_ExecuteScript(mCx, mScopeChain, mScript)) { mSkip = true; mRv = EvaluationExceptionToNSResult(mCx); return mRv; } - return mRv; + return NS_OK; } nsresult -nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue) +nsJSUtils::ExecutionContext::ExecScript(JS::MutableHandle<JS::Value> aRetValue) { - MOZ_ASSERT(aRetValue.isUndefined()); if (mSkip) { - // Repeat earlier result, as NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW are not - // failures cases. -#ifdef DEBUG - mWantsReturnValue = false; -#endif - return mRv; + return mRv; } + MOZ_ASSERT(mScript); MOZ_ASSERT(mWantsReturnValue); + + if (!JS_ExecuteScript(mCx, mScopeChain, mScript, aRetValue)) { + mSkip = true; + mRv = EvaluationExceptionToNSResult(mCx); + return mRv; + } + #ifdef DEBUG mWantsReturnValue = false; #endif - if (mCoerceToString && !mRetValue.isUndefined()) { - JSString* str = JS::ToString(mCx, mRetValue); + if (mCoerceToString && !aRetValue.isUndefined()) { + JSString* str = JS::ToString(mCx, aRetValue); if (!str) { // ToString can be a function call, so an exception can be raised while // executing the function. diff --git a/dom/base/nsJSUtils.h b/dom/base/nsJSUtils.h index ce6f5a2283..b1b2b601a6 100644 --- a/dom/base/nsJSUtils.h +++ b/dom/base/nsJSUtils.h @@ -76,6 +76,9 @@ public: // Scope chain in which the execution takes place. JS::AutoObjectVector mScopeChain; + // The compiled script. + JS::Rooted<JSScript*> mScript; + // returned value forwarded when we have to interupt the execution eagerly // with mSkip. nsresult mRv; @@ -92,6 +95,8 @@ public: bool mWantsReturnValue; bool mExpectScopeChain; + + bool mScriptUsed; #endif public: @@ -105,8 +110,12 @@ public: ExecutionContext(ExecutionContext&&) = delete; ~ExecutionContext() { - // This flag is resetted, when the returned value is extracted. - MOZ_ASSERT(!mWantsReturnValue); + // This flag is reset when the returned value is extracted. + MOZ_ASSERT_IF(!mSkip, !mWantsReturnValue); + + // If encoding was started we expect the script to have been + // used when ending the encoding. + MOZ_ASSERT_IF(mEncodeBytecode && mScript && mRv == NS_OK, mScriptUsed); } // The returned value would be converted to a string if the @@ -119,10 +128,41 @@ public: // Set the scope chain in which the code should be executed. void SetScopeChain(const JS::AutoObjectVector& aScopeChain); - // Copy the returned value in the mutable handle argument, in case of a + // After getting a notification that an off-thread compilation terminated, + // this function will take the result of the parser and move it to the main + // thread. + MOZ_MUST_USE nsresult JoinCompile(void** aOffThreadToken); + + // Compile a script contained in a SourceText. + nsresult Compile(JS::CompileOptions& aCompileOptions, + JS::SourceBufferHolder& aSrcBuf); + + // Compile a script contained in a string. + nsresult Compile(JS::CompileOptions& aCompileOptions, + const nsAString& aScript); + + // Decode a script contained in a buffer. + nsresult Decode(JS::CompileOptions& aCompileOptions, + mozilla::Vector<uint8_t>& aBytecodeBuf, + size_t aBytecodeIndex); + + // After getting a notification that an off-thread decoding terminated, this + // function will get the result of the decoder and move it to the main + // thread. + nsresult JoinDecode(void** aOffThreadToken); + + // Get a successfully compiled script. + JSScript* GetScript(); + + // Execute the compiled script and ignore the return value. + MOZ_MUST_USE nsresult ExecScript(); + + // Execute the compiled script a get the return value. + // + // Copy the returned value into the mutable handle argument. In case of a // evaluation failure either during the execution or the conversion of the - // result to a string, the nsresult would be set to the corresponding result - // code, and the mutable handle argument would remain unchanged. + // result to a string, the nsresult is be set to the corresponding result + // code and the mutable handle argument remains unchanged. // // The value returned in the mutable handle argument is part of the // compartment given as argument to the ExecutionContext constructor. If the @@ -131,37 +171,7 @@ public: MOZ_MUST_USE nsresult ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue); - // After getting a notification that an off-thread compilation terminated, - // this function will take the result of the parser by moving it to the main - // thread before starting the execution of the script. - // - // The compiled script would be returned in the |aScript| out-param. - MOZ_MUST_USE nsresult JoinAndExec(void **aOffThreadToken, - JS::MutableHandle<JSScript*> aScript); - - // Compile a script contained in a SourceBuffer, and execute it. - nsresult CompileAndExec(JS::CompileOptions& aCompileOptions, - JS::SourceBufferHolder& aSrcBuf); - - // Compile a script contained in a string, and execute it. - nsresult CompileAndExec(JS::CompileOptions& aCompileOptions, - const nsAString& aScript); - - // Decode a script contained in a buffer, and execute it. - MOZ_MUST_USE nsresult DecodeAndExec(JS::CompileOptions& aCompileOptions, - mozilla::Vector<uint8_t>& aBytecodeBuf, - size_t aBytecodeIndex); - - // After getting a notification that an off-thread decoding terminated, this - // function will get the result of the decoder by moving it to the main - // thread before starting the execution of the script. - MOZ_MUST_USE nsresult DecodeJoinAndExec(void **aOffThreadToken); - - // Similar to JoinAndExec, except that in addition to fecthing the source, - // we register the fact that we plan to encode its bytecode later. - MOZ_MUST_USE nsresult JoinEncodeAndExec(void **aOffThreadToken, - mozilla::Vector<uint8_t>& aBytecodeBuf, - JS::MutableHandle<JSScript*> aScript); + MOZ_MUST_USE nsresult ExecScript(JS::MutableHandle<JS::Value> aRetValue); }; struct MOZ_STACK_CLASS EvaluateOptions { diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index a260f49055..2fd94a3dd9 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -2347,12 +2347,23 @@ ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) { nsJSUtils::ExecutionContext exec(cx, global); if (aRequest->mOffThreadToken) { - JS::Rooted<JSScript*> script(cx); - rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script); + rv = exec.JoinDecode(&aRequest->mOffThreadToken); } else { nsAutoString inlineData; SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData); - rv = exec.CompileAndExec(options, srcBuf); + rv = exec.Compile(options, srcBuf); + } + if (rv == NS_OK) { + JS::Rooted<JSScript*> script(cx); + script = exec.GetScript(); + + // Create a ClassicScript object and associate it with the + // JSScript. + RefPtr<ClassicScript> classicScript = new ClassicScript( + aRequest->mFetchOptions, aRequest->mBaseURL); + classicScript->AssociateWithScript(script); + + rv = exec.ExecScript(); } } } |