Bug 1837964 - Part 3: Split nsXULPrototypeScript::Compile into nsXULPrototypeScript::{Compile,CompileMaybeOffThread}. r=smaug

Given the text ownership is different and the behavior is also different,
it's better having separate functions.

Also this simplifies the later patch that touches off-thread case.

Differential Revision: https://phabricator.services.mozilla.com/D181205
This commit is contained in:
Tooru Fujisawa 2023-07-06 09:37:18 +00:00
parent dbc77b97e8
commit af843d8fd0
4 changed files with 86 additions and 43 deletions

View File

@ -855,9 +855,8 @@ PrototypeDocumentContentSink::OnStreamComplete(nsIStreamLoader* aLoader,
rv = ScriptLoader::ConvertToUTF8(channel, string, stringLen, u""_ns,
mDocument, units, unitsLength);
if (NS_SUCCEEDED(rv)) {
rv = mCurrentScriptProto->Compile(units.release(), unitsLength,
JS::SourceOwnership::TakeOwnership, uri,
1, mDocument, this);
rv = mCurrentScriptProto->CompileMaybeOffThread(
std::move(units), unitsLength, uri, 1, mDocument, this);
if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasStencil()) {
mOffThreadCompiling = true;
mDocument->BlockOnload();

View File

@ -416,8 +416,8 @@ XULContentSinkImpl::HandleEndElement(const char16_t* aName) {
script->mOutOfLine = false;
if (doc) {
script->Compile(mText, mTextLength, JS::SourceOwnership::Borrowed,
mDocumentURL, script->mLineNo, doc);
script->Compile(mText, mTextLength, mDocumentURL, script->mLineNo,
doc);
}
}

View File

@ -1610,11 +1610,20 @@ static nsresult ReadStencil(nsIObjectInputStream* aStream, JSContext* aCx,
return rv;
}
void nsXULPrototypeScript::FillCompileOptions(JS::CompileOptions& options) {
void nsXULPrototypeScript::FillCompileOptions(JS::CompileOptions& aOptions,
const char* aFilename,
uint32_t aLineNo) {
// NOTE: This method shouldn't change any field which also exists in
// JS::InstantiateOptions. If such field is added,
// nsXULPrototypeScript::InstantiateScript should also call this method.
// If the script was inline, tell the JS parser to save source for
// Function.prototype.toSource(). If it's out of line, we retrieve the
// source from the files on demand.
options.setSourceIsLazy(mOutOfLine);
aOptions.setSourceIsLazy(mOutOfLine);
aOptions.setIntroductionType(mOutOfLine ? "srcScript" : "inlineScript")
.setFileAndLine(aFilename, mOutOfLine ? 1 : aLineNo);
}
nsresult nsXULPrototypeScript::Serialize(
@ -1853,26 +1862,18 @@ static void OffThreadScriptReceiverCallback(JS::OffThreadToken* aToken,
NS_DispatchToMainThread(notify);
}
template <typename Unit>
nsresult nsXULPrototypeScript::Compile(
const Unit* aText, size_t aTextLength, JS::SourceOwnership aOwnership,
nsIURI* aURI, uint32_t aLineNo, Document* aDocument,
nsIOffThreadScriptReceiver* aOffThreadReceiver /* = nullptr */) {
// We'll compile the script in the compilation scope.
nsresult nsXULPrototypeScript::Compile(const char16_t* aText,
size_t aTextLength, nsIURI* aURI,
uint32_t aLineNo, Document* aDocument) {
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
if (aOwnership == JS::SourceOwnership::TakeOwnership) {
// In this early-exit case -- before the |srcBuf.init| call will
// own |aText| -- we must relinquish ownership manually.
js_free(const_cast<Unit*>(aText));
}
return NS_ERROR_UNEXPECTED;
}
JSContext* cx = jsapi.cx();
JS::SourceText<Unit> srcBuf;
if (NS_WARN_IF(!srcBuf.init(cx, aText, aTextLength, aOwnership))) {
JS::SourceText<char16_t> srcBuf;
if (NS_WARN_IF(!srcBuf.init(cx, aText, aTextLength,
JS::SourceOwnership::Borrowed))) {
return NS_ERROR_FAILURE;
}
@ -1882,15 +1883,45 @@ nsresult nsXULPrototypeScript::Compile(
return rv;
}
// Ok, compile it to create a prototype script object!
JS::CompileOptions options(cx);
FillCompileOptions(options);
options.setIntroductionType(mOutOfLine ? "srcScript" : "inlineScript")
.setFileAndLine(urlspec.get(), mOutOfLine ? 1 : aLineNo);
FillCompileOptions(options, urlspec.get(), aLineNo);
JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
RefPtr<JS::Stencil> stencil =
JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
if (!stencil) {
return NS_ERROR_OUT_OF_MEMORY;
}
Set(stencil);
return NS_OK;
}
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) {
nsresult nsXULPrototypeScript::CompileMaybeOffThread(
mozilla::UniquePtr<mozilla::Utf8Unit[], JS::FreePolicy>&& aText,
size_t aTextLength, nsIURI* aURI, uint32_t aLineNo, Document* aDocument,
nsIOffThreadScriptReceiver* aOffThreadReceiver) {
MOZ_ASSERT(aOffThreadReceiver);
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
return NS_ERROR_UNEXPECTED;
}
JSContext* cx = jsapi.cx();
JS::SourceText<mozilla::Utf8Unit> srcBuf;
if (NS_WARN_IF(!srcBuf.init(cx, std::move(aText), aTextLength))) {
return NS_ERROR_FAILURE;
}
nsAutoCString urlspec;
nsresult rv = aURI->GetSpec(urlspec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
JS::CompileOptions options(cx);
FillCompileOptions(options, urlspec.get(), aLineNo);
if (JS::CanCompileOffThread(cx, options, aTextLength)) {
if (!JS::CompileToStencilOffThread(
cx, options, srcBuf, OffThreadScriptReceiverCallback,
static_cast<void*>(aOffThreadReceiver))) {
@ -1909,21 +1940,11 @@ nsresult nsXULPrototypeScript::Compile(
return NS_OK;
}
template nsresult nsXULPrototypeScript::Compile<char16_t>(
const char16_t* aText, size_t aTextLength, JS::SourceOwnership aOwnership,
nsIURI* aURI, uint32_t aLineNo, Document* aDocument,
nsIOffThreadScriptReceiver* aOffThreadReceiver);
template nsresult nsXULPrototypeScript::Compile<Utf8Unit>(
const Utf8Unit* aText, size_t aTextLength, JS::SourceOwnership aOwnership,
nsIURI* aURI, uint32_t aLineNo, Document* aDocument,
nsIOffThreadScriptReceiver* aOffThreadReceiver);
nsresult nsXULPrototypeScript::InstantiateScript(
JSContext* aCx, JS::MutableHandle<JSScript*> aScript) {
MOZ_ASSERT(mStencil);
JS::CompileOptions options(aCx);
FillCompileOptions(options);
JS::InstantiateOptions instantiateOptions(options);
aScript.set(JS::InstantiateGlobalStencil(aCx, instantiateOptions, mStencil));
if (!aScript) {

View File

@ -20,11 +20,13 @@
#include "js/SourceText.h"
#include "js/TracingAPI.h"
#include "js/TypeDecls.h"
#include "js/Utility.h" // JS::FreePolicy
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/BasicEvents.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/DOMString.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/FragmentOrElement.h"
@ -213,7 +215,8 @@ class nsXULPrototypeScript : public nsXULPrototypeNode {
private:
virtual ~nsXULPrototypeScript() = default;
void FillCompileOptions(JS::CompileOptions& options);
void FillCompileOptions(JS::CompileOptions& aOptions, const char* aFilename,
uint32_t aLineNo);
public:
virtual nsresult Serialize(
@ -228,11 +231,31 @@ class nsXULPrototypeScript : public nsXULPrototypeNode {
nsresult DeserializeOutOfLine(nsIObjectInputStream* aInput,
nsXULPrototypeDocument* aProtoDoc);
template <typename Unit>
nsresult Compile(const Unit* aText, size_t aTextLength,
JS::SourceOwnership aOwnership, nsIURI* aURI,
uint32_t aLineNo, mozilla::dom::Document* aDocument,
nsIOffThreadScriptReceiver* aOffThreadReceiver = nullptr);
// Compile given JS source text synchronously.
//
// This method doesn't take the ownership of aText, but borrows during the
// compilation.
//
// If successfully compiled, `HasStencil()` returns true.
nsresult Compile(const char16_t* aText, size_t aTextLength, nsIURI* aURI,
uint32_t aLineNo, mozilla::dom::Document* aDocument);
// Compile given JS source text possibly in off-thread.
//
// This method takes the ownership of aText.
//
// If this doesn't use off-thread compilation and successfully compiled,
// `HasStencil()` returns true.
//
// If this uses off-thread compilation, `HasStencil()` returns false, and
// once the compilation finishes, aOffThreadReceiver gets notified with the
// compiled stencil. The callback is responsible for calling `Set()` with
// the stencil.
nsresult CompileMaybeOffThread(
mozilla::UniquePtr<mozilla::Utf8Unit[], JS::FreePolicy>&& aText,
size_t aTextLength, nsIURI* aURI, uint32_t aLineNo,
mozilla::dom::Document* aDocument,
nsIOffThreadScriptReceiver* aOffThreadReceiver);
void Set(JS::Stencil* aStencil);