Backed out 3 changesets (bug 1718194, bug 1718623, bug 1718481) for causing leaks. CLOSED TREE

Backed out changeset 1cd8bdf1fc92 (bug 1718481)
Backed out changeset aa56fe2c069d (bug 1718194)
Backed out changeset f7cb7313d1c7 (bug 1718623)
This commit is contained in:
Butkovits Atila 2021-09-18 15:47:45 +03:00
parent 3a98545701
commit 8957144a0f
24 changed files with 387 additions and 296 deletions

View File

@ -16215,6 +16215,12 @@ bool Document::IsExtensionPage() const {
BasePrincipal::Cast(NodePrincipal())->AddonPolicy();
}
void Document::TraceProtos(JSTracer* aTrc) {
if (mPrototypeDocument) {
mPrototypeDocument->TraceProtos(aTrc);
}
}
/**
* Retrieves the classification of the Flash plugins in the document based on
* the classification lists. For more information, see

View File

@ -5279,6 +5279,8 @@ class Document : public nsINode,
nsRefPtrHashtable<nsRefPtrHashKey<Element>, nsXULPrototypeElement>
mL10nProtoElements;
void TraceProtos(JSTracer* aTrc);
float GetSavedResolutionBeforeMVM() { return mSavedResolutionBeforeMVM; }
void SetSavedResolutionBeforeMVM(float aResolution) {
mSavedResolutionBeforeMVM = aResolution;

View File

@ -429,7 +429,20 @@ nsresult nsCCUncollectableMarker::Observe(nsISupports* aSubject,
return NS_OK;
}
void mozilla::dom::TraceBlackJS(JSTracer* aTrc) {
void mozilla::dom::TraceBlackJS(JSTracer* aTrc, bool aIsShutdownGC) {
#ifdef MOZ_XUL
// Mark the scripts held in the XULPrototypeCache. This is required to keep
// the JS script in the cache live across GC.
nsXULPrototypeCache* cache = nsXULPrototypeCache::MaybeGetInstance();
if (cache) {
if (aIsShutdownGC) {
cache->FlushScripts();
} else {
cache->MarkInGC(aTrc);
}
}
#endif
if (!nsCCUncollectableMarker::sGeneration) {
return;
}
@ -491,6 +504,13 @@ void mozilla::dom::TraceBlackJS(JSTracer* aTrc) {
}
}
}
#ifdef MOZ_XUL
Document* doc = window->GetExtantDoc();
if (doc) {
doc->TraceProtos(aTrc);
}
#endif
}
}
}

View File

@ -41,7 +41,7 @@ class nsCCUncollectableMarker final : public nsIObserver {
namespace mozilla {
namespace dom {
void TraceBlackJS(JSTracer* aTrc);
void TraceBlackJS(JSTracer* aTrc, bool aIsShutdownGC);
} // namespace dom
} // namespace mozilla

View File

@ -15,20 +15,15 @@
#include "ContentChild.h"
#include "ErrorList.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/Unused.h"
#include "base/process_util.h"
#include "chrome/common/ipc_channel.h"
#include "js/CallAndConstruct.h" // JS::IsCallable, JS_CallFunctionValue
#include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h"
#include "js/experimental/JSStencil.h"
#include "js/GCVector.h"
#include "js/JSON.h"
#include "js/PropertyAndElement.h" // JS_GetProperty
#include "js/RootingAPI.h"
#include "js/SourceText.h"
#include "js/StructuredClone.h"
#include "js/TypeDecls.h"
#include "js/Wrapper.h"
#include "jsapi.h"
#include "jsfriendapi.h"
@ -1173,11 +1168,6 @@ void nsMessageManagerScriptExecutor::Shutdown() {
}
}
static void FillCompileOptionsForCachedStencil(JS::CompileOptions& aOptions) {
ScriptPreloader::FillCompileOptionsForCachedStencil(aOptions);
aOptions.setNonSyntacticScope(true);
}
void nsMessageManagerScriptExecutor::LoadScriptInternal(
JS::Handle<JSObject*> aMessageManager, const nsAString& aURL,
bool aRunInUniqueScope) {
@ -1188,59 +1178,48 @@ void nsMessageManagerScriptExecutor::LoadScriptInternal(
return;
}
RefPtr<JS::Stencil> stencil;
JS::RootingContext* rcx = RootingCx();
JS::Rooted<JSScript*> script(rcx);
nsMessageManagerScriptHolder* holder = sCachedScripts->Get(aURL);
if (holder) {
stencil = holder->mStencil;
script = holder->mScript;
} else {
stencil =
TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, aMessageManager);
TryCacheLoadAndCompileScript(aURL, aRunInUniqueScope, aMessageManager,
&script);
}
AutoEntryScript aes(aMessageManager, "message manager script load");
JSContext* cx = aes.cx();
if (stencil) {
JS::CompileOptions options(cx);
FillCompileOptionsForCachedStencil(options);
if (JS::StencilCanLazilyParse(stencil)) {
// See TryCacheLoadAndCompileScript.
options.setSourceIsLazy(false);
}
JS::Rooted<JSScript*> script(
cx, JS::InstantiateGlobalStencil(cx, options, stencil));
if (script) {
if (aRunInUniqueScope) {
JS::Rooted<JSObject*> scope(cx);
bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager,
script, &scope);
if (ok) {
// Force the scope to stay alive.
mAnonymousGlobalScopes.AppendElement(scope);
}
} else {
JS::RootedValue rval(cx);
JS::RootedVector<JSObject*> envChain(cx);
if (!envChain.append(aMessageManager)) {
return;
}
Unused << JS_ExecuteScript(cx, envChain, script, &rval);
if (script) {
if (aRunInUniqueScope) {
JS::Rooted<JSObject*> scope(cx);
bool ok = js::ExecuteInFrameScriptEnvironment(cx, aMessageManager, script,
&scope);
if (ok) {
// Force the scope to stay alive.
mAnonymousGlobalScopes.AppendElement(scope);
}
} else {
JS::RootedValue rval(cx);
JS::RootedVector<JSObject*> envChain(cx);
if (!envChain.append(aMessageManager)) {
return;
}
JS::CloneAndExecuteScript(cx, envChain, script, &rval);
}
}
}
already_AddRefed<JS::Stencil>
nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
void nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
const nsAString& aURL, bool aRunInUniqueScope,
JS::Handle<JSObject*> aMessageManager) {
JS::Handle<JSObject*> aMessageManager,
JS::MutableHandle<JSScript*> aScriptp) {
nsCString url = NS_ConvertUTF16toUTF8(aURL);
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), url);
if (NS_FAILED(rv)) {
return nullptr;
return;
}
bool hasFlags;
@ -1248,7 +1227,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
&hasFlags);
if (NS_FAILED(rv) || !hasFlags) {
NS_WARNING("Will not load a frame script!");
return nullptr;
return;
}
// If this script won't be cached, or there is only one of this type of
@ -1271,13 +1250,14 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
// compartment alive.
AutoJSAPI jsapi;
if (!jsapi.Init(isRunOnce ? aMessageManager : xpc::CompilationScope())) {
return nullptr;
return;
}
JSContext* cx = jsapi.cx();
JS::CompileOptions options(cx);
FillCompileOptionsForCachedStencil(options);
ScriptPreloader::FillCompileOptionsForCachedStencil(options);
options.setFileAndLine(url.get(), 1);
options.setNonSyntacticScope(true);
RefPtr<JS::Stencil> stencil;
if (useScriptPreloader) {
@ -1293,12 +1273,12 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
nsIContentPolicy::TYPE_INTERNAL_FRAME_MESSAGEMANAGER_SCRIPT);
if (!channel) {
return nullptr;
return;
}
nsCOMPtr<nsIInputStream> input;
rv = channel->Open(getter_AddRefs(input));
NS_ENSURE_SUCCESS(rv, nullptr);
NS_ENSURE_SUCCESS_VOID(rv);
nsString dataString;
char16_t* dataStringBuf = nullptr;
size_t dataStringLength = 0;
@ -1306,7 +1286,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
nsCString buffer;
uint64_t written;
if (NS_FAILED(NS_ReadInputStreamToString(input, buffer, -1, &written))) {
return nullptr;
return;
}
uint32_t size = (uint32_t)std::min(written, (uint64_t)UINT32_MAX);
@ -1316,7 +1296,7 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
}
if (!dataStringBuf || dataStringLength == 0) {
return nullptr;
return;
}
// If we are not encoding to the ScriptPreloader cache, we can now relax the
@ -1329,17 +1309,25 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
JS::SourceText<char16_t> srcBuf;
if (!srcBuf.init(cx, std::move(srcChars), dataStringLength)) {
return nullptr;
return;
}
stencil = JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
if (!stencil) {
return nullptr;
return;
}
}
MOZ_ASSERT(stencil);
JS::Rooted<JSScript*> script(
cx, JS::InstantiateGlobalStencil(cx, options, stencil));
if (!script) {
return;
}
aScriptp.set(script);
if (useScriptPreloader) {
ScriptPreloader::GetChildSingleton().NoteStencil(url, url, stencil,
isRunOnce);
@ -1348,12 +1336,10 @@ nsMessageManagerScriptExecutor::TryCacheLoadAndCompileScript(
// preloader cache, not the session cache.
if (!isRunOnce) {
// Root the object also for caching.
auto* holder = new nsMessageManagerScriptHolder(stencil);
auto* holder = new nsMessageManagerScriptHolder(cx, script);
sCachedScripts->InsertOrUpdate(aURL, holder);
}
}
return stencil.forget();
}
void nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks,

View File

@ -11,7 +11,7 @@
#include <string.h>
#include <utility>
#include "ErrorList.h"
#include "js/experimental/JSStencil.h"
#include "js/RootingAPI.h"
#include "js/TypeDecls.h"
#include "js/Value.h"
#include "mozilla/AlreadyAddRefed.h"
@ -308,14 +308,14 @@ class nsSameProcessAsyncMessageBase {
class nsScriptCacheCleaner;
struct nsMessageManagerScriptHolder {
explicit nsMessageManagerScriptHolder(JS::Stencil* aStencil)
: mStencil(aStencil) {
nsMessageManagerScriptHolder(JSContext* aCx, JSScript* aScript)
: mScript(aCx, aScript) {
MOZ_COUNT_CTOR(nsMessageManagerScriptHolder);
}
MOZ_COUNTED_DTOR(nsMessageManagerScriptHolder)
RefPtr<JS::Stencil> mStencil;
JS::PersistentRooted<JSScript*> mScript;
};
class nsMessageManagerScriptExecutor {
@ -335,9 +335,10 @@ class nsMessageManagerScriptExecutor {
void DidCreateScriptLoader();
void LoadScriptInternal(JS::Handle<JSObject*> aMessageManager,
const nsAString& aURL, bool aRunInUniqueScope);
already_AddRefed<JS::Stencil> TryCacheLoadAndCompileScript(
const nsAString& aURL, bool aRunInUniqueScope,
JS::Handle<JSObject*> aMessageManager);
void TryCacheLoadAndCompileScript(const nsAString& aURL,
bool aRunInUniqueScope,
JS::Handle<JSObject*> aMessageManager,
JS::MutableHandle<JSScript*> aScriptp);
bool Init();
void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
void Unlink();

View File

@ -12,8 +12,6 @@
#include "nsISupports.h"
#include "nsCOMPtr.h"
#include "jspubtd.h"
#include "js/experimental/JSStencil.h"
#include "mozilla/RefPtr.h"
class nsIScriptGlobalObject;
@ -79,12 +77,11 @@ class nsIOffThreadScriptReceiver : public nsISupports {
NS_DECLARE_STATIC_IID_ACCESSOR(NS_IOFFTHREADSCRIPTRECEIVER_IID)
/**
* Notify this object that a previous Compile call specifying this as
* Notify this object that a previous CompileScript call specifying this as
* aOffThreadReceiver has completed. The script being passed in must be
* rooted before any call which could trigger GC.
*/
NS_IMETHOD OnScriptCompileComplete(JS::Stencil* aStencil,
nsresult aStatus) = 0;
NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIOffThreadScriptReceiver,

View File

@ -50,13 +50,11 @@
#include "mozilla/LoadInfo.h"
#include "mozilla/PresShell.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/RefPtr.h"
#include "nsXULPrototypeCache.h"
#include "nsXULElement.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "js/CompilationAndEvaluation.h"
#include "js/experimental/JSStencil.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -568,7 +566,7 @@ nsresult PrototypeDocumentContentSink::ResumeWalkInternal() {
// If the script cannot be loaded, just keep going!
if (NS_SUCCEEDED(rv) && blocked) return NS_OK;
} else if (scriptproto->HasStencil()) {
} else if (scriptproto->HasScriptObject()) {
// An inline script
rv = ExecuteScript(scriptproto);
if (NS_FAILED(rv)) return rv;
@ -727,7 +725,7 @@ nsresult PrototypeDocumentContentSink::LoadScript(
bool isChromeDoc = IsChromeURI(mDocumentURI);
if (isChromeDoc && aScriptProto->HasStencil()) {
if (isChromeDoc && aScriptProto->HasScriptObject()) {
rv = ExecuteScript(aScriptProto);
// Ignore return value from execution, and don't block
@ -741,15 +739,15 @@ nsresult PrototypeDocumentContentSink::LoadScript(
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
if (isChromeDoc && useXULCache) {
RefPtr<JS::Stencil> newStencil =
nsXULPrototypeCache::GetInstance()->GetStencil(aScriptProto->mSrcURI);
if (newStencil) {
JSScript* newScriptObject =
nsXULPrototypeCache::GetInstance()->GetScript(aScriptProto->mSrcURI);
if (newScriptObject) {
// The script language for a proto must remain constant - we
// can't just change it for this unexpected language.
aScriptProto->Set(newStencil);
aScriptProto->Set(newScriptObject);
}
if (aScriptProto->HasStencil()) {
if (aScriptProto->HasScriptObject()) {
rv = ExecuteScript(aScriptProto);
// Ignore return value from execution, and don't block
@ -758,8 +756,8 @@ nsresult PrototypeDocumentContentSink::LoadScript(
}
}
// Release stencil from FastLoad since we decided against using them
aScriptProto->Set(nullptr);
// Release script objects from FastLoad since we decided against using them
aScriptProto->UnlinkJSObjects();
// Set the current script prototype so that OnStreamComplete can report
// the right file if there are errors in the script.
@ -843,7 +841,7 @@ PrototypeDocumentContentSink::OnStreamComplete(nsIStreamLoader* aLoader,
// be writing a new FastLoad file. If we were reading this script
// from the FastLoad file, XULContentSinkImpl::OpenScript (over in
// nsXULContentSink.cpp) would have already deserialized a non-null
// script->mStencil, causing control flow at the top of LoadScript
// script->mScriptObject, causing control flow at the top of LoadScript
// not to reach here.
nsCOMPtr<nsIURI> uri = mCurrentScriptProto->mSrcURI;
@ -869,7 +867,7 @@ PrototypeDocumentContentSink::OnStreamComplete(nsIStreamLoader* aLoader,
rv = mCurrentScriptProto->Compile(units, unitsLength,
JS::SourceOwnership::TakeOwnership, uri,
1, mDocument, this);
if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasStencil()) {
if (NS_SUCCEEDED(rv) && !mCurrentScriptProto->HasScriptObject()) {
mOffThreadCompiling = true;
mDocument->BlockOnload();
return NS_OK;
@ -877,11 +875,11 @@ PrototypeDocumentContentSink::OnStreamComplete(nsIStreamLoader* aLoader,
}
}
return OnScriptCompileComplete(mCurrentScriptProto->GetStencil(), rv);
return OnScriptCompileComplete(mCurrentScriptProto->GetScriptObject(), rv);
}
NS_IMETHODIMP
PrototypeDocumentContentSink::OnScriptCompileComplete(JS::Stencil* aStencil,
PrototypeDocumentContentSink::OnScriptCompileComplete(JSScript* aScript,
nsresult aStatus) {
// The mCurrentScriptProto may have been cleared out by another
// PrototypeDocumentContentSink.
@ -891,9 +889,8 @@ PrototypeDocumentContentSink::OnScriptCompileComplete(JS::Stencil* aStencil,
// When compiling off thread the script will not have been attached to the
// script proto yet.
if (aStencil && !mCurrentScriptProto->HasStencil()) {
mCurrentScriptProto->Set(aStencil);
}
if (aScript && !mCurrentScriptProto->HasScriptObject())
mCurrentScriptProto->Set(aScript);
// Allow load events to be fired once off thread compilation finishes.
if (mOffThreadCompiling) {
@ -946,9 +943,11 @@ PrototypeDocumentContentSink::OnScriptCompileComplete(JS::Stencil* aStencil,
// the true crime story.)
bool useXULCache = nsXULPrototypeCache::GetInstance()->IsEnabled();
if (useXULCache && IsChromeURI(mDocumentURI) && scriptProto->HasStencil()) {
nsXULPrototypeCache::GetInstance()->PutStencil(scriptProto->mSrcURI,
scriptProto->GetStencil());
if (useXULCache && IsChromeURI(mDocumentURI) &&
scriptProto->HasScriptObject()) {
JS::Rooted<JSScript*> script(RootingCx(), scriptProto->GetScriptObject());
nsXULPrototypeCache::GetInstance()->PutScript(scriptProto->mSrcURI,
script);
}
// ignore any evaluation errors
}
@ -971,7 +970,7 @@ PrototypeDocumentContentSink::OnScriptCompileComplete(JS::Stencil* aStencil,
*docp = doc->mNextSrcLoadWaiter;
doc->mNextSrcLoadWaiter = nullptr;
if (aStatus == NS_BINDING_ABORTED && !scriptProto->HasStencil()) {
if (aStatus == NS_BINDING_ABORTED && !scriptProto->HasScriptObject()) {
// If the previous doc load was aborted, we want to try loading
// again for the next doc. Otherwise, one abort would lead to all
// subsequent waiting docs to abort as well.
@ -982,7 +981,7 @@ PrototypeDocumentContentSink::OnScriptCompileComplete(JS::Stencil* aStencil,
}
// Execute only if we loaded and compiled successfully, then resume
if (NS_SUCCEEDED(aStatus) && scriptProto->HasStencil()) {
if (NS_SUCCEEDED(aStatus) && scriptProto->HasScriptObject()) {
doc->ExecuteScript(scriptProto);
}
doc->ResumeWalk();
@ -1011,22 +1010,22 @@ nsresult PrototypeDocumentContentSink::ExecuteScript(
// Execute the precompiled script with the given version
nsAutoMicroTask mt;
// We're about to run script via JS_ExecuteScript, so we need an
// We're about to run script via JS::CloneAndExecuteScript, so we need an
// AutoEntryScript. This is Gecko specific and not in any spec.
AutoEntryScript aes(scriptGlobalObject, "precompiled XUL <script> element");
JSContext* cx = aes.cx();
JS::Rooted<JSScript*> scriptObject(cx);
rv = aScript->InstantiateScript(cx, &scriptObject);
NS_ENSURE_SUCCESS(rv, rv);
JS::Rooted<JSScript*> scriptObject(cx, aScript->GetScriptObject());
NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
NS_ENSURE_TRUE(xpc::Scriptability::Get(global).Allowed(), NS_OK);
// On failure, ~AutoScriptEntry will handle exceptions, so
// The script is in the compilation scope. Clone it into the target scope
// and execute it. On failure, ~AutoScriptEntry will handle exceptions, so
// there is no need to manually check the return value.
JS::RootedValue rval(cx);
Unused << JS_ExecuteScript(cx, scriptObject, &rval);
JS::CloneAndExecuteScript(cx, scriptObject, &rval);
return NS_OK;
}

View File

@ -21,8 +21,6 @@
#include "nsIScriptContext.h"
#include "nsICSSLoaderObserver.h"
#include "mozilla/Logging.h"
#include "js/experimental/JSStencil.h"
#include "mozilla/RefPtr.h"
class nsIURI;
class nsIChannel;
@ -88,7 +86,7 @@ class PrototypeDocumentContentSink final : public nsIStreamLoaderObserver,
nsresult aStatus) override;
// nsIOffThreadScriptReceiver
NS_IMETHOD OnScriptCompileComplete(JS::Stencil* aStencil,
NS_IMETHOD OnScriptCompileComplete(JSScript* aScript,
nsresult aStatus) override;
nsresult OnPrototypeLoadDone(nsXULPrototypeDocument* aPrototype);

View File

@ -427,7 +427,7 @@ XULContentSinkImpl::HandleEndElement(const char16_t* aName) {
static_cast<nsXULPrototypeScript*>(node.get());
// If given a src= attribute, we must ignore script tag content.
if (!script->mSrcURI && !script->HasStencil()) {
if (!script->mSrcURI && !script->HasScriptObject()) {
nsCOMPtr<Document> doc = do_QueryReferent(mDocument);
script->mOutOfLine = false;

View File

@ -19,13 +19,10 @@
#include "XULTreeElement.h"
#include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h"
#include "js/experimental/JSStencil.h"
#include "js/OffThreadScriptCompilation.h"
#include "js/SourceText.h"
#include "js/Transcoding.h"
#include "js/Utility.h"
#include "jsapi.h"
#include "mozilla/Assertions.h"
#include "mozilla/ArrayIterator.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DeclarationBlock.h"
@ -40,7 +37,6 @@
#include "mozilla/MouseEvents.h"
#include "mozilla/OwningNonNull.h"
#include "mozilla/PresShell.h"
#include "mozilla/RefPtr.h"
#include "mozilla/StaticAnalysisFunctions.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/URLExtraData.h"
@ -1211,6 +1207,8 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(nsXULPrototypeNode)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsXULPrototypeNode)
if (tmp->mType == nsXULPrototypeNode::eType_Element) {
static_cast<nsXULPrototypeElement*>(tmp)->Unlink();
} else if (tmp->mType == nsXULPrototypeNode::eType_Script) {
static_cast<nsXULPrototypeScript*>(tmp)->UnlinkJSObjects();
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode)
@ -1233,6 +1231,10 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXULPrototypeNode)
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsXULPrototypeNode)
if (tmp->mType == nsXULPrototypeNode::eType_Script) {
nsXULPrototypeScript* script = static_cast<nsXULPrototypeScript*>(tmp);
script->Trace(aCallbacks, aClosure);
}
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsXULPrototypeNode, AddRef)
@ -1341,7 +1343,7 @@ nsresult nsXULPrototypeElement::Serialize(
rv = tmp;
}
if (script->HasStencil()) {
if (script->HasScriptObject()) {
// This may return NS_OK without muxing script->mSrcURI's
// data into the cache file, in the case where that
// muxed document is already there (written by a prior
@ -1563,6 +1565,17 @@ void nsXULPrototypeElement::Unlink() {
mChildren.Clear();
}
void nsXULPrototypeElement::TraceAllScripts(JSTracer* aTrc) {
for (uint32_t i = 0; i < mChildren.Length(); ++i) {
nsXULPrototypeNode* child = mChildren[i];
if (child->mType == nsXULPrototypeNode::eType_Element) {
static_cast<nsXULPrototypeElement*>(child)->TraceAllScripts(aTrc);
} else if (child->mType == nsXULPrototypeNode::eType_Script) {
static_cast<nsXULPrototypeScript*>(child)->TraceScriptObject(aTrc);
}
}
}
//----------------------------------------------------------------------
//
// nsXULPrototypeScript
@ -1574,103 +1587,9 @@ nsXULPrototypeScript::nsXULPrototypeScript(uint32_t aLineNo)
mSrcLoading(false),
mOutOfLine(true),
mSrcLoadWaiters(nullptr),
mStencil(nullptr) {}
mScriptObject(nullptr) {}
static nsresult WriteStencil(nsIObjectOutputStream* aStream, JSContext* aCx,
const JS::ReadOnlyCompileOptions& aOptions,
JS::Stencil* aStencil) {
uint8_t flags = 0; // We don't have flags anymore.
nsresult rv = aStream->Write8(flags);
if (NS_FAILED(rv)) {
return rv;
}
JS::TranscodeBuffer buffer;
JS::TranscodeResult code;
code = JS::EncodeStencil(aCx, aOptions, aStencil, buffer);
if (code != JS::TranscodeResult::Ok) {
if (code == JS::TranscodeResult::Throw) {
JS_ClearPendingException(aCx);
return NS_ERROR_OUT_OF_MEMORY;
}
MOZ_ASSERT(IsTranscodeFailureResult(code));
return NS_ERROR_FAILURE;
}
size_t size = buffer.length();
if (size > UINT32_MAX) {
return NS_ERROR_FAILURE;
}
rv = aStream->Write32(size);
if (NS_SUCCEEDED(rv)) {
// Ideally we could just pass "buffer" here. See bug 1566574.
rv = aStream->WriteBytes(Span(buffer.begin(), size));
}
return rv;
}
static nsresult ReadStencil(nsIObjectInputStream* aStream, JSContext* aCx,
const JS::ReadOnlyCompileOptions& aOptions,
JS::Stencil** aStencilOut) {
uint8_t flags;
nsresult rv = aStream->Read8(&flags);
if (NS_FAILED(rv)) {
return rv;
}
// We don't serialize mutedError-ness of scripts, which is fine as long as
// we only serialize system and XUL-y things. We can detect this by checking
// where the caller wants us to deserialize.
//
// CompilationScope() could theoretically GC, so get that out of the way
// before comparing to the cx global.
JSObject* loaderGlobal = xpc::CompilationScope();
MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(aCx) ||
JS::CurrentGlobalOrNull(aCx) == loaderGlobal);
uint32_t size;
rv = aStream->Read32(&size);
if (NS_FAILED(rv)) {
return rv;
}
char* data;
rv = aStream->ReadBytes(size, &data);
if (NS_FAILED(rv)) {
return rv;
}
JS::TranscodeRange range(reinterpret_cast<uint8_t*>(data), size);
{
JS::TranscodeResult code;
RefPtr<JS::Stencil> stencil;
code = JS::DecodeStencil(aCx, aOptions, range, getter_AddRefs(stencil));
if (code == JS::TranscodeResult::Ok) {
stencil.forget(aStencilOut);
} else {
if (code == JS::TranscodeResult::Throw) {
JS_ClearPendingException(aCx);
return NS_ERROR_OUT_OF_MEMORY;
}
MOZ_ASSERT(IsTranscodeFailureResult(code));
return NS_ERROR_FAILURE;
}
}
return rv;
}
void nsXULPrototypeScript::FillCompileOptions(JS::CompileOptions& options) {
// 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);
}
nsXULPrototypeScript::~nsXULPrototypeScript() { UnlinkJSObjects(); }
nsresult nsXULPrototypeScript::Serialize(
nsIObjectOutputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
@ -1682,9 +1601,9 @@ nsresult nsXULPrototypeScript::Serialize(
return NS_ERROR_UNEXPECTED;
}
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mStencil,
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mScriptObject,
"script source still loading when serializing?!");
if (!mStencil) return NS_ERROR_FAILURE;
if (!mScriptObject) return NS_ERROR_FAILURE;
// Write basic prototype data
nsresult rv;
@ -1694,12 +1613,9 @@ nsresult nsXULPrototypeScript::Serialize(
if (NS_FAILED(rv)) return rv;
JSContext* cx = jsapi.cx();
JS::Rooted<JSScript*> script(cx, mScriptObject);
MOZ_ASSERT(xpc::CompilationScope() == JS::CurrentGlobalOrNull(cx));
JS::CompileOptions options(cx);
FillCompileOptions(options);
return WriteStencil(aStream, cx, options, mStencil);
return nsContentUtils::XPConnect()->WriteScript(aStream, cx, script);
}
nsresult nsXULPrototypeScript::SerializeOutOfLine(
@ -1740,12 +1656,19 @@ nsresult nsXULPrototypeScript::SerializeOutOfLine(
return rv;
}
void nsXULPrototypeScript::FillCompileOptions(JS::CompileOptions& options) {
// 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);
}
nsresult nsXULPrototypeScript::Deserialize(
nsIObjectInputStream* aStream, nsXULPrototypeDocument* aProtoDoc,
nsIURI* aDocumentURI,
const nsTArray<RefPtr<mozilla::dom::NodeInfo>>* aNodeInfos) {
nsresult rv;
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mStencil,
NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nullptr || !mScriptObject,
"prototype script not well-initialized when deserializing?!");
// Read basic prototype data
@ -1764,10 +1687,11 @@ nsresult nsXULPrototypeScript::Deserialize(
JS::CompileOptions options(cx);
FillCompileOptions(options);
RefPtr<JS::Stencil> newStencil;
rv = ReadStencil(aStream, cx, options, getter_AddRefs(newStencil));
JS::Rooted<JSScript*> newScriptObject(cx);
rv = nsContentUtils::XPConnect()->ReadScript(aStream, cx, options,
newScriptObject.address());
NS_ENSURE_SUCCESS(rv, rv);
Set(newStencil);
Set(newScriptObject);
return NS_OK;
}
@ -1794,14 +1718,12 @@ nsresult nsXULPrototypeScript::DeserializeOutOfLine(
useXULCache = cache->IsEnabled();
if (useXULCache) {
RefPtr<JS::Stencil> newStencil = cache->GetStencil(mSrcURI);
if (newStencil) {
Set(newStencil);
}
JSScript* newScriptObject = cache->GetScript(mSrcURI);
if (newScriptObject) Set(newScriptObject);
}
}
if (!mStencil) {
if (!mScriptObject) {
if (mSrcURI) {
rv = cache->GetInputStream(mSrcURI, getter_AddRefs(objectInput));
}
@ -1809,7 +1731,7 @@ nsresult nsXULPrototypeScript::DeserializeOutOfLine(
// to do anything else in that case, I think.
// We do reflect errors into rv, but our caller may want to
// ignore our return value, because mStencil will be null
// ignore our return value, because mScriptObject will be null
// after any error, and that suffices to cause the script to
// be reloaded (from the src= URI, if any) and recompiled.
// We're better off slow-loading than bailing out due to a
@ -1819,7 +1741,8 @@ nsresult nsXULPrototypeScript::DeserializeOutOfLine(
if (NS_SUCCEEDED(rv)) {
if (useXULCache && mSrcURI && mSrcURI->SchemeIs("chrome")) {
cache->PutStencil(mSrcURI, GetStencil());
JS::Rooted<JSScript*> script(RootingCx(), GetScriptObject());
cache->PutScript(mSrcURI, script);
}
cache->FinishInputStream(mSrcURI);
} else {
@ -1880,7 +1803,7 @@ NS_IMETHODIMP
NotifyOffThreadScriptCompletedRunnable::Run() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<JS::Stencil> stencil;
JS::Rooted<JSScript*> script(RootingCx());
{
AutoJSAPI jsapi;
if (!jsapi.Init(xpc::CompilationScope())) {
@ -1889,7 +1812,7 @@ NotifyOffThreadScriptCompletedRunnable::Run() {
return NS_ERROR_UNEXPECTED;
}
JSContext* cx = jsapi.cx();
stencil = JS::FinishOffThreadCompileToStencil(cx, mToken);
script = JS::FinishOffThreadScript(cx, mToken);
}
if (!sReceivers) {
@ -1903,8 +1826,8 @@ NotifyOffThreadScriptCompletedRunnable::Run() {
std::move((*sReceivers)[index]);
sReceivers->RemoveElementAt(index);
return receiver->OnScriptCompileComplete(stencil,
stencil ? NS_OK : NS_ERROR_FAILURE);
return receiver->OnScriptCompileComplete(script,
script ? NS_OK : NS_ERROR_FAILURE);
}
static void OffThreadScriptReceiverCallback(JS::OffThreadToken* aToken,
@ -1955,43 +1878,38 @@ nsresult nsXULPrototypeScript::Compile(
JS::Rooted<JSObject*> scope(cx, JS::CurrentGlobalOrNull(cx));
if (aOffThreadReceiver && JS::CanCompileOffThread(cx, options, aTextLength)) {
if (!JS::CompileToStencilOffThread(
cx, options, srcBuf, OffThreadScriptReceiverCallback,
static_cast<void*>(aOffThreadReceiver))) {
JS_ClearPendingException(cx);
if (!JS::CompileOffThread(cx, options, srcBuf,
OffThreadScriptReceiverCallback,
static_cast<void*>(aOffThreadReceiver))) {
return NS_ERROR_OUT_OF_MEMORY;
}
NotifyOffThreadScriptCompletedRunnable::NoteReceiver(aOffThreadReceiver);
} else {
RefPtr<JS::Stencil> stencil =
JS::CompileGlobalScriptToStencil(cx, options, srcBuf);
if (!stencil) {
JS::Rooted<JSScript*> script(cx, JS::Compile(cx, options, srcBuf));
if (!script) {
return NS_ERROR_OUT_OF_MEMORY;
}
Set(stencil);
Set(script);
}
return NS_OK;
}
nsresult nsXULPrototypeScript::InstantiateScript(
JSContext* aCx, JS::MutableHandleScript aScript) {
MOZ_ASSERT(mStencil);
JS::CompileOptions options(aCx);
FillCompileOptions(options);
// We don't need setIntroductionType and setFileAndLine here, unlike
// nsXULPrototypeScript::Compile.
// mStencil already contains the information.
aScript.set(JS::InstantiateGlobalStencil(aCx, options, mStencil));
if (!aScript) {
JS_ClearPendingException(aCx);
return NS_ERROR_OUT_OF_MEMORY;
void nsXULPrototypeScript::UnlinkJSObjects() {
if (mScriptObject) {
mozilla::DropJSObjects(this);
}
return NS_OK;
}
void nsXULPrototypeScript::Set(JS::Stencil* aStencil) { mStencil = aStencil; }
void nsXULPrototypeScript::Set(JSScript* aObject) {
MOZ_ASSERT(!mScriptObject, "Leaking script object.");
if (!aObject) {
mScriptObject = nullptr;
return;
}
mScriptObject = aObject;
mozilla::HoldJSObjects(this);
}
//----------------------------------------------------------------------
//

View File

@ -15,11 +15,9 @@
#include <stdint.h>
#include <stdio.h>
#include "ErrorList.h"
#include "js/experimental/JSStencil.h"
#include "js/RootingAPI.h"
#include "js/SourceText.h"
#include "js/TracingAPI.h"
#include "js/TypeDecls.h"
#include "mozilla/AlreadyAddRefed.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
@ -50,6 +48,7 @@
#include "nscore.h"
class JSObject;
class JSScript;
class nsAttrValueOrString;
class nsIControllers;
class nsIObjectInputStream;
@ -196,6 +195,9 @@ class nsXULPrototypeElement : public nsXULPrototypeNode {
void Unlink();
// Trace all scripts held by this element and its children.
void TraceAllScripts(JSTracer* aTrc);
nsPrototypeArray mChildren;
RefPtr<mozilla::dom::NodeInfo> mNodeInfo;
@ -212,7 +214,7 @@ class nsXULPrototypeScript : public nsXULPrototypeNode {
explicit nsXULPrototypeScript(uint32_t aLineNo);
private:
virtual ~nsXULPrototypeScript() = default;
virtual ~nsXULPrototypeScript();
void FillCompileOptions(JS::CompileOptions& options);
@ -234,15 +236,26 @@ class nsXULPrototypeScript : public nsXULPrototypeNode {
uint32_t aLineNo, mozilla::dom::Document* aDocument,
nsIOffThreadScriptReceiver* aOffThreadReceiver = nullptr);
void UnlinkStencil() { mStencil = nullptr; }
void UnlinkJSObjects();
void Set(JS::Stencil* aStencil);
void Set(JSScript* aObject);
bool HasStencil() { return mStencil; }
bool HasScriptObject() {
// Conversion to bool doesn't trigger mScriptObject's read barrier.
return mScriptObject;
}
JS::Stencil* GetStencil() { return mStencil.get(); }
JSScript* GetScriptObject() { return mScriptObject; }
nsresult InstantiateScript(JSContext* aCx, JS::MutableHandleScript aScript);
void TraceScriptObject(JSTracer* aTrc) {
JS::TraceEdge(aTrc, &mScriptObject, "active window XUL prototype script");
}
void Trace(const TraceCallbacks& aCallbacks, void* aClosure) {
if (mScriptObject) {
aCallbacks.Trace(&mScriptObject, "mScriptObject", aClosure);
}
}
nsCOMPtr<nsIURI> mSrcURI;
uint32_t mLineNo;
@ -251,7 +264,7 @@ class nsXULPrototypeScript : public nsXULPrototypeNode {
mozilla::dom::PrototypeDocumentContentSink*
mSrcLoadWaiters; // [OWNER] but not COMPtr
private:
RefPtr<JS::Stencil> mStencil;
JS::Heap<JSScript*> mScriptObject;
};
class nsXULPrototypeText : public nsXULPrototypeNode {

View File

@ -20,7 +20,6 @@
#include "nsAppDirectoryServiceDefs.h"
#include "js/experimental/JSStencil.h"
#include "js/TracingAPI.h"
#include "mozilla/StyleSheetInlines.h"
@ -28,7 +27,6 @@
#include "mozilla/scache/StartupCache.h"
#include "mozilla/scache/StartupCacheUtils.h"
#include "mozilla/Telemetry.h"
#include "mozilla/RefPtr.h"
#include "mozilla/intl/LocaleService.h"
using namespace mozilla;
@ -75,6 +73,8 @@ nsXULPrototypeCache* nsXULPrototypeCache::sInstance = nullptr;
nsXULPrototypeCache::nsXULPrototypeCache() = default;
nsXULPrototypeCache::~nsXULPrototypeCache() { FlushScripts(); }
NS_IMPL_ISUPPORTS(nsXULPrototypeCache, nsIObserver)
/* static */
@ -165,18 +165,19 @@ nsresult nsXULPrototypeCache::PutPrototype(nsXULPrototypeDocument* aDocument) {
return NS_OK;
}
JS::Stencil* nsXULPrototypeCache::GetStencil(nsIURI* aURI) {
if (auto* entry = mStencilTable.GetEntry(aURI)) {
return entry->mStencil;
JSScript* nsXULPrototypeCache::GetScript(nsIURI* aURI) {
if (auto* entry = mScriptTable.GetEntry(aURI)) {
return entry->mScript.get();
}
return nullptr;
}
nsresult nsXULPrototypeCache::PutStencil(nsIURI* aURI, JS::Stencil* aStencil) {
MOZ_ASSERT(aStencil, "Need a non-NULL stencil");
nsresult nsXULPrototypeCache::PutScript(nsIURI* aURI,
JS::Handle<JSScript*> aScriptObject) {
MOZ_ASSERT(aScriptObject, "Need a non-NULL script");
#ifdef DEBUG_BUG_392650
if (mStencilTable.Get(aURI)) {
if (mScriptTable.Get(aURI)) {
nsAutoCString scriptName;
aURI->GetSpec(scriptName);
nsAutoCString message("Loaded script ");
@ -186,14 +187,16 @@ nsresult nsXULPrototypeCache::PutStencil(nsIURI* aURI, JS::Stencil* aStencil) {
}
#endif
mStencilTable.PutEntry(aURI)->mStencil = aStencil;
mScriptTable.PutEntry(aURI)->mScript.set(aScriptObject);
return NS_OK;
}
void nsXULPrototypeCache::FlushScripts() { mScriptTable.Clear(); }
void nsXULPrototypeCache::Flush() {
mPrototypeTable.Clear();
mStencilTable.Clear();
mScriptTable.Clear();
}
bool nsXULPrototypeCache::IsEnabled() { return !gDisableXULCache; }
@ -458,6 +461,12 @@ void nsXULPrototypeCache::MarkInCCGeneration(uint32_t aGeneration) {
}
}
void nsXULPrototypeCache::MarkInGC(JSTracer* aTrc) {
for (auto& entry : mScriptTable) {
JS::TraceEdge(aTrc, &entry.mScript, "nsXULPrototypeCache script");
}
}
MOZ_DEFINE_MALLOC_SIZE_OF(CacheMallocSizeOf)
static void ReportSize(const nsCString& aPath, size_t aAmount,
@ -487,8 +496,8 @@ void nsXULPrototypeCache::CollectMemoryReports(
other += sInstance->mPrototypeTable.ShallowSizeOfExcludingThis(mallocSizeOf);
// TODO Report content in mPrototypeTable?
other += sInstance->mStencilTable.ShallowSizeOfExcludingThis(mallocSizeOf);
// TODO Report content inside mStencilTable?
other += sInstance->mScriptTable.ShallowSizeOfExcludingThis(mallocSizeOf);
// TODO Report content inside mScriptTable?
other +=
sInstance->mStartupCacheURITable.ShallowSizeOfExcludingThis(mallocSizeOf);

View File

@ -16,8 +16,6 @@
#include "nsIStorageStream.h"
#include "mozilla/scache/StartupCache.h"
#include "js/experimental/JSStencil.h"
#include "mozilla/RefPtr.h"
class nsIHandleReportCallback;
namespace mozilla {
@ -58,8 +56,8 @@ class nsXULPrototypeCache : public nsIObserver {
nsXULPrototypeDocument* GetPrototype(nsIURI* aURI);
nsresult PutPrototype(nsXULPrototypeDocument* aDocument);
JS::Stencil* GetStencil(nsIURI* aURI);
nsresult PutStencil(nsIURI* aURI, JS::Stencil* aStencil);
JSScript* GetScript(nsIURI* aURI);
nsresult PutScript(nsIURI* aURI, JS::Handle<JSScript*> aScriptObject);
/**
* Write the XUL prototype document to a cache file. The proto must be
@ -83,6 +81,8 @@ class nsXULPrototypeCache : public nsIObserver {
static void ReleaseGlobals() { NS_IF_RELEASE(sInstance); }
void MarkInCCGeneration(uint32_t aGeneration);
void MarkInGC(JSTracer* aTrc);
void FlushScripts();
static void CollectMemoryReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData);
@ -92,22 +92,26 @@ class nsXULPrototypeCache : public nsIObserver {
void** aResult);
nsXULPrototypeCache();
virtual ~nsXULPrototypeCache() = default;
virtual ~nsXULPrototypeCache();
static nsXULPrototypeCache* sInstance;
nsRefPtrHashtable<nsURIHashKey, nsXULPrototypeDocument>
mPrototypeTable; // owns the prototypes
class StencilHashKey : public nsURIHashKey {
class ScriptHashKey : public nsURIHashKey {
public:
explicit StencilHashKey(const nsIURI* aKey) : nsURIHashKey(aKey) {}
StencilHashKey(StencilHashKey&&) = default;
explicit ScriptHashKey(const nsIURI* aKey) : nsURIHashKey(aKey) {}
ScriptHashKey(ScriptHashKey&&) = default;
RefPtr<JS::Stencil> mStencil;
// Mark ALLOW_MEMMOVE as false, as hash tables containing JS:Heap<T>
// values must be copied rather than memmoved.
enum { ALLOW_MEMMOVE = false };
JS::Heap<JSScript*> mScript;
};
nsTHashtable<StencilHashKey> mStencilTable;
nsTHashtable<ScriptHashKey> mScriptTable;
// URIs already written to the startup cache, to prevent double-caching.
nsTHashtable<nsURIHashKey> mStartupCacheURITable;

View File

@ -42,7 +42,11 @@ uint32_t nsXULPrototypeDocument::gRefCnt;
//
nsXULPrototypeDocument::nsXULPrototypeDocument()
: mRoot(nullptr), mLoaded(false), mCCGeneration(0), mWasL10nCached(false) {
: mRoot(nullptr),
mLoaded(false),
mCCGeneration(0),
mGCNumber(0),
mWasL10nCached(false) {
++gRefCnt;
}
@ -423,6 +427,21 @@ nsresult nsXULPrototypeDocument::NotifyLoadDone() {
return NS_OK;
}
void nsXULPrototypeDocument::TraceProtos(JSTracer* aTrc) {
// Only trace the protos once per GC if we are marking.
if (aTrc->isMarkingTracer()) {
uint32_t currentGCNumber = aTrc->gcNumberForMarking();
if (mGCNumber == currentGCNumber) {
return;
}
mGCNumber = currentGCNumber;
}
if (mRoot) {
mRoot->TraceAllScripts(aTrc);
}
}
void nsXULPrototypeDocument::SetIsL10nCached(bool aIsCached) {
mWasL10nCached = aIsCached;
}

View File

@ -94,6 +94,8 @@ class nsXULPrototypeDocument final : public nsISerializable {
NS_DECL_CYCLE_COLLECTION_CLASS(nsXULPrototypeDocument)
void TraceProtos(JSTracer* aTrc);
bool WasL10nCached() { return mWasL10nCached; };
void SetIsL10nCached(bool aIsCached);
@ -112,6 +114,7 @@ class nsXULPrototypeDocument final : public nsISerializable {
RefPtr<nsNodeInfoManager> mNodeInfoManager;
uint32_t mCCGeneration;
uint32_t mGCNumber;
nsXULPrototypeDocument();
virtual ~nsXULPrototypeDocument();

View File

@ -87,9 +87,6 @@ extern JS_PUBLIC_API JSScript* InstantiateGlobalStencil(
// decoding.
extern JS_PUBLIC_API bool StencilIsBorrowed(Stencil* stencil);
// Return true if the stencil is lazily parsed.
extern JS_PUBLIC_API bool StencilCanLazilyParse(Stencil* stencil);
// Instantiate a module Stencil and return the associated object. Inside the
// engine this is a js::ModuleObject.
extern JS_PUBLIC_API JSObject* InstantiateModuleStencil(

View File

@ -4044,10 +4044,6 @@ JS_PUBLIC_API bool JS::StencilIsBorrowed(Stencil* stencil) {
return stencil->hasExternalDependency;
}
JS_PUBLIC_API bool JS::StencilCanLazilyParse(Stencil* stencil) {
return stencil->canLazilyParse;
}
JSObject* JS::InstantiateModuleStencil(
JSContext* cx, const JS::ReadOnlyCompileOptions& optionsInput,
JS::Stencil* stencil) {

View File

@ -4455,9 +4455,8 @@ static JSScript* CopyScriptImpl(JSContext* cx, HandleScript src,
SourceExtent extent = src->extent();
ImmutableScriptFlags flags = src->immutableFlags();
// The source/target of clone should agree with syntactic scope.
MOZ_ASSERT(flags.hasFlag(JSScript::ImmutableFlags::HasNonSyntacticScope) ==
scopes[0]->hasOnChain(ScopeKind::NonSyntactic));
flags.setFlag(JSScript::ImmutableFlags::HasNonSyntacticScope,
scopes[0]->hasOnChain(ScopeKind::NonSyntactic));
// FunctionFlags and ImmutableScriptFlags should agree on self-hosting status.
MOZ_ASSERT_IF(functionOrGlobal->is<JSFunction>(),

View File

@ -1787,6 +1787,13 @@
* The result is always `undefined` except when the name refers to a
* binding in a non-syntactic `with` environment.
*
* Note: The frontend has to emit `JSOp::GImplicitThis` (and not
* `JSOp::Undefined`) for global unqualified function calls, even when
* `CompileOptions::nonSyntacticScope == false`, because later
* `js::CloneGlobalScript` can be called with `ScopeKind::NonSyntactic` to
* clone the script into a non-syntactic environment, with the bytecode
* reused, unchanged.
*
* Category: Functions
* Type: Calls
* Operands: uint32_t nameIndex

View File

@ -25,6 +25,7 @@ class nsWrapperCache;
[ptr] native JSContextPtr(JSContext);
[ptr] native JSObjectPtr(JSObject);
[ptr] native JSScriptPtr(JSScript);
[ptr] native nsWrapperCachePtr(nsWrapperCache);
native JSHandleId(JS::Handle<jsid>);
[ref] native const_JSReadOnlyCompileOptionsRef(const JS::ReadOnlyCompileOptions);
@ -258,4 +259,14 @@ interface nsIXPConnect : nsISupports
[noscript] jsval evalInSandboxObject(in AString source, in string filename,
in JSContextPtr cx,
in JSObjectPtr sandbox);
[noscript] void writeScript(in nsIObjectOutputStream aStream,
in JSContextPtr aJSContext,
in JSScriptPtr aJSScript);
[noscript] JSScriptPtr readScript(in nsIObjectInputStream aStream,
in JSContextPtr aJSContext,
in const_JSReadOnlyCompileOptionsRef aOptions);
[infallible] readonly attribute boolean isShuttingDown;
};

View File

@ -695,7 +695,7 @@ void XPCJSRuntime::TraceNativeBlackRoots(JSTracer* trc) {
}
}
dom::TraceBlackJS(trc);
dom::TraceBlackJS(trc, nsIXPConnect::XPConnect()->GetIsShuttingDown());
}
void XPCJSRuntime::TraceAdditionalNativeGrayRoots(JSTracer* trc) {

View File

@ -70,7 +70,7 @@ const char XPC_SCRIPT_ERROR_CONTRACTID[] = "@mozilla.org/scripterror;1";
/***************************************************************************/
nsXPConnect::nsXPConnect() {
nsXPConnect::nsXPConnect() : mShuttingDown(false) {
#ifdef MOZ_GECKO_PROFILER
JS::SetProfilingThreadCallbacks(profiler_register_thread,
profiler_unregister_thread);
@ -111,6 +111,7 @@ nsXPConnect::~nsXPConnect() {
// get by with only the second GC. :-(
mRuntime->GarbageCollect(JS::GCReason::XPCONNECT_SHUTDOWN);
mShuttingDown = true;
XPCWrappedNativeScope::SystemIsBeingShutDown();
mRuntime->SystemIsBeingShutDown();
@ -918,6 +919,110 @@ void SetLocationForGlobal(JSObject* global, nsIURI* locationURI) {
} // namespace xpc
NS_IMETHODIMP
nsXPConnect::WriteScript(nsIObjectOutputStream* stream, JSContext* cx,
JSScript* scriptArg) {
RootedScript script(cx, scriptArg);
uint8_t flags = 0; // We don't have flags anymore.
nsresult rv = stream->Write8(flags);
if (NS_FAILED(rv)) {
return rv;
}
TranscodeBuffer buffer;
TranscodeResult code;
code = EncodeScript(cx, buffer, script);
if (code != TranscodeResult::Ok) {
if (code == TranscodeResult::Throw) {
JS_ClearPendingException(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
MOZ_ASSERT(IsTranscodeFailureResult(code));
return NS_ERROR_FAILURE;
}
size_t size = buffer.length();
if (size > UINT32_MAX) {
return NS_ERROR_FAILURE;
}
rv = stream->Write32(size);
if (NS_SUCCEEDED(rv)) {
// Ideally we could just pass "buffer" here. See bug 1566574.
rv = stream->WriteBytes(Span(buffer.begin(), size));
}
return rv;
}
NS_IMETHODIMP
nsXPConnect::ReadScript(nsIObjectInputStream* stream, JSContext* cx,
const JS::ReadOnlyCompileOptions& options,
JSScript** scriptp) {
uint8_t flags;
nsresult rv = stream->Read8(&flags);
if (NS_FAILED(rv)) {
return rv;
}
// We don't serialize mutedError-ness of scripts, which is fine as long as
// we only serialize system and XUL-y things. We can detect this by checking
// where the caller wants us to deserialize.
//
// CompilationScope() could theoretically GC, so get that out of the way
// before comparing to the cx global.
JSObject* loaderGlobal = xpc::CompilationScope();
MOZ_RELEASE_ASSERT(nsContentUtils::IsSystemCaller(cx) ||
CurrentGlobalOrNull(cx) == loaderGlobal);
uint32_t size;
rv = stream->Read32(&size);
if (NS_FAILED(rv)) {
return rv;
}
char* data;
rv = stream->ReadBytes(size, &data);
if (NS_FAILED(rv)) {
return rv;
}
TranscodeBuffer buffer;
buffer.replaceRawBuffer(reinterpret_cast<uint8_t*>(data), size);
{
TranscodeResult code;
Rooted<JSScript*> script(cx);
code = DecodeScript(cx, options, buffer, &script);
if (code == TranscodeResult::Ok) {
*scriptp = script.get();
} else {
if (code == TranscodeResult::Throw) {
JS_ClearPendingException(cx);
return NS_ERROR_OUT_OF_MEMORY;
}
MOZ_ASSERT(IsTranscodeFailureResult(code));
return NS_ERROR_FAILURE;
}
}
return rv;
}
NS_IMETHODIMP
nsXPConnect::GetIsShuttingDown(bool* aIsShuttingDown) {
if (!aIsShuttingDown) {
return NS_ERROR_INVALID_ARG;
}
*aIsShuttingDown = mShuttingDown;
return NS_OK;
}
// static
nsIXPConnect* nsIXPConnect::XPConnect() {
// Do a release-mode assert that we're not doing anything significant in

View File

@ -249,6 +249,7 @@ class nsXPConnect final : public nsIXPConnect {
XPCJSContext* mContext = nullptr;
XPCJSRuntime* mRuntime = nullptr;
bool mShuttingDown;
friend class nsIXPConnect;