Merge m-c to autoland, a=merge

MozReview-Commit-ID: FvBazP0CwKm
This commit is contained in:
Wes Kocher 2017-04-21 17:39:39 -07:00
commit c296c00a82
71 changed files with 2515 additions and 1158 deletions

View File

@ -20,12 +20,12 @@
#include "nsIURI.h"
#include "nsIContentViewer.h"
#include "nsIObserverService.h"
#include "prclist.h"
#include "mozilla/Services.h"
#include "nsTArray.h"
#include "nsCOMArray.h"
#include "nsDocShell.h"
#include "mozilla/Attributes.h"
#include "mozilla/LinkedList.h"
#include "nsISHEntry.h"
#include "nsISHTransaction.h"
#include "nsISHistoryListener.h"
@ -49,7 +49,7 @@ static const char* kObservedPrefs[] = {
static int32_t gHistoryMaxSize = 50;
// List of all SHistory objects, used for content viewer cache eviction
static PRCList gSHistoryList;
static LinkedList<nsSHistory> gSHistoryList;
// Max viewers allowed total, across all SHistory objects - negative default
// means we will calculate how many viewers to cache based on total memory
int32_t nsSHistory::sHistoryMaxTotalViewers = -1;
@ -239,13 +239,11 @@ nsSHistory::nsSHistory()
, mRootDocShell(nullptr)
{
// Add this new SHistory object to the list
PR_APPEND_LINK(this, &gSHistoryList);
gSHistoryList.insertBack(this);
}
nsSHistory::~nsSHistory()
{
// Remove this SHistory object from the list
PR_REMOVE_LINK(this);
}
NS_IMPL_ADDREF(nsSHistory)
@ -357,8 +355,6 @@ nsSHistory::Startup()
}
}
// Initialize the global list of all SHistory objects
PR_INIT_CLIST(&gSHistoryList);
return NS_OK;
}
@ -1186,9 +1182,7 @@ nsSHistory::GloballyEvictContentViewers()
nsTArray<TransactionAndDistance> transactions;
PRCList* listEntry = PR_LIST_HEAD(&gSHistoryList);
while (listEntry != &gSHistoryList) {
nsSHistory* shist = static_cast<nsSHistory*>(listEntry);
for (auto shist : gSHistoryList) {
// Maintain a list of the transactions which have viewers and belong to
// this particular shist object. We'll add this list to the global list,
@ -1249,7 +1243,6 @@ nsSHistory::GloballyEvictContentViewers()
// We've found all the transactions belonging to shist which have viewers.
// Add those transactions to our global list and move on.
transactions.AppendElements(shTransactions);
listEntry = PR_NEXT_LINK(shist);
}
// We now have collected all cached content viewers. First check that we

View File

@ -16,7 +16,7 @@
#include "nsWeakPtr.h"
#include "nsIPartialSHistoryListener.h"
#include "prclist.h"
#include "mozilla/LinkedList.h"
class nsIDocShell;
class nsSHEnumerator;
@ -24,7 +24,7 @@ class nsSHistoryObserver;
class nsISHEntry;
class nsISHTransaction;
class nsSHistory final : public PRCList,
class nsSHistory final : public mozilla::LinkedListElement<nsSHistory>,
public nsISHistory,
public nsISHistoryInternal,
public nsIWebNavigation

View File

@ -193,7 +193,7 @@ nsJSUtils::ExecutionContext::SetScopeChain(
}
nsresult
nsJSUtils::ExecutionContext::SyncAndExec(void **aOffThreadToken,
nsJSUtils::ExecutionContext::JoinAndExec(void **aOffThreadToken,
JS::MutableHandle<JSScript*> aScript)
{
if (mSkip) {
@ -252,6 +252,88 @@ nsJSUtils::ExecutionContext::CompileAndExec(JS::CompileOptions& aCompileOptions,
return CompileAndExec(aCompileOptions, srcBuf);
}
nsresult
nsJSUtils::ExecutionContext::DecodeAndExec(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);
// 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.
MOZ_ASSERT(tr != JS::TranscodeResult_Failure_BadBuildId &&
tr != JS::TranscodeResult_Failure_WrongCompileOption);
if (tr != JS::TranscodeResult_Ok) {
mSkip = true;
mRv = NS_ERROR_DOM_JS_DECODING_ERROR;
return mRv;
}
if (!JS_ExecuteScript(mCx, mScopeChain, script)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return mRv;
}
nsresult
nsJSUtils::ExecutionContext::DecodeJoinAndExec(void **aOffThreadToken)
{
if (mSkip) {
return mRv;
}
MOZ_ASSERT(!mWantsReturnValue);
MOZ_ASSERT(!mExpectScopeChain);
JS::Rooted<JSScript*> script(mCx);
script.set(JS::FinishOffThreadScriptDecoder(mCx, *aOffThreadToken));
*aOffThreadToken = nullptr; // Mark the token as having been finished.
if (!script || !JS_ExecuteScript(mCx, mScopeChain, script)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
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;
}
if (!StartIncrementalEncoding(mCx, aBytecodeBuf, aScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
if (!JS_ExecuteScript(mCx, mScopeChain, aScript)) {
mSkip = true;
mRv = EvaluationExceptionToNSResult(mCx);
return mRv;
}
return mRv;
}
nsresult
nsJSUtils::ExecutionContext::ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue)
{

View File

@ -140,11 +140,11 @@ public:
ExtractReturnValue(JS::MutableHandle<JS::Value> aRetValue);
// After getting a notification that an off-thread compilation terminated,
// this function will synchronize the result by moving it to the main thread
// before starting the execution of the script.
// 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 SyncAndExec(void **aOffThreadToken,
MOZ_MUST_USE nsresult JoinAndExec(void **aOffThreadToken,
JS::MutableHandle<JSScript*> aScript);
// Compile a script contained in a SourceBuffer, and execute it.
@ -154,6 +154,22 @@ public:
// 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);
};
static nsresult CompileModule(JSContext* aCx,

View File

@ -33,6 +33,7 @@
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIClassOfService.h"
#include "nsICacheInfoChannel.h"
#include "nsITimedChannel.h"
#include "nsIScriptElement.h"
#include "nsIDOMHTMLScriptElement.h"
@ -56,9 +57,11 @@
#include "mozilla/dom/EncodingUtils.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/Unused.h"
#include "nsIScriptError.h"
#include "nsIOutputStream.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -77,6 +80,13 @@ static LazyLogModule gScriptLoaderLog("ScriptLoader");
#define LOG_ERROR(args) \
MOZ_LOG(gScriptLoaderLog, mozilla::LogLevel::Error, args)
#define LOG_ENABLED() MOZ_LOG_TEST(gScriptLoaderLog, mozilla::LogLevel::Debug)
// These are the Alternate Data MIME type used by the nsScriptLoader to
// register and read bytecode out of the nsCacheInfoChannel.
static NS_NAMED_LITERAL_CSTRING(
kBytecodeMimeType, "javascript/moz-bytecode-" NS_STRINGIFY(MOZ_BUILDID));
static NS_NAMED_LITERAL_CSTRING(kNullMimeType, "javascript/null");
void
ImplCycleCollectionUnlink(nsScriptLoadRequestList& aField);
@ -87,6 +97,52 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
const char* aName,
uint32_t aFlags = 0);
// This macro is used to wrap a tracing mechanism which is scheduling events
// which are then used by the JavaScript code of test cases to track the code
// path to verify the optimizations are working as expected.
#define TRACE_FOR_TEST(elem, str) \
PR_BEGIN_MACRO \
nsresult rv = NS_OK; \
rv = TestingDispatchEvent(elem, NS_LITERAL_STRING(str)); \
NS_ENSURE_SUCCESS(rv, rv); \
PR_END_MACRO
#define TRACE_FOR_TEST_BOOL(elem, str) \
PR_BEGIN_MACRO \
nsresult rv = NS_OK; \
rv = TestingDispatchEvent(elem, NS_LITERAL_STRING(str)); \
NS_ENSURE_SUCCESS(rv, false); \
PR_END_MACRO
#define TRACE_FOR_TEST_NONE(elem, str) \
PR_BEGIN_MACRO \
TestingDispatchEvent(elem, NS_LITERAL_STRING(str)); \
PR_END_MACRO
static nsresult
TestingDispatchEvent(nsIScriptElement* aScriptElement,
const nsAString& aEventType)
{
static bool sExposeTestInterfaceEnabled = false;
static bool sExposeTestInterfacePrefCached = false;
if (!sExposeTestInterfacePrefCached) {
sExposeTestInterfacePrefCached = true;
Preferences::AddBoolVarCache(&sExposeTestInterfaceEnabled,
"dom.expose_test_interfaces",
false);
}
if (!sExposeTestInterfaceEnabled) {
return NS_OK;
}
nsCOMPtr<nsINode> target(do_QueryInterface(aScriptElement));
if (!target) {
return NS_OK;
}
RefPtr<AsyncEventDispatcher> dispatcher =
new AsyncEventDispatcher(target, aEventType, true, false);
return dispatcher->PostDOMEvent();
}
//////////////////////////////////////////////////////////////
// nsScriptLoadRequest
//////////////////////////////////////////////////////////////
@ -94,11 +150,24 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsScriptLoadRequest)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_0(nsScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_CLASS(nsScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheInfo)
tmp->DropBytecodeCacheReferences();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheInfo)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsScriptLoadRequest)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScript)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
nsScriptLoadRequest::~nsScriptLoadRequest()
{
// We should always clean up any off-thread script parsing resources.
@ -107,6 +176,10 @@ nsScriptLoadRequest::~nsScriptLoadRequest()
// But play it safe in release builds and try to clean them up here
// as a fail safe.
MaybeCancelOffThreadScript();
if (mScript) {
DropBytecodeCacheReferences();
}
}
void
@ -133,7 +206,15 @@ nsScriptLoadRequest::MaybeCancelOffThreadScript()
}
JSContext* cx = danger::GetJSContext();
JS::CancelOffThreadScript(cx, mOffThreadToken);
// Follow the same conditions as nsScriptLoader::AttemptAsyncScriptCompile
if (IsModuleRequest()) {
JS::CancelOffThreadModule(cx, mOffThreadToken);
} else if (IsSource()) {
JS::CancelOffThreadScript(cx, mOffThreadToken);
} else {
MOZ_ASSERT(IsBytecode());
JS::CancelOffThreadScriptDecoder(cx, mOffThreadToken);
}
mOffThreadToken = nullptr;
}
@ -527,6 +608,7 @@ nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
mDeferEnabled(false),
mDocumentParsingDone(false),
mBlockingDOMContentLoaded(false),
mLoadEventFired(false),
mReporter(new ConsoleReportCollector())
{
}
@ -1199,11 +1281,46 @@ nsScriptLoader::InstantiateModuleTree(nsModuleLoadRequest* aRequest)
return true;
}
static bool
IsBytecodeCacheEnabled()
{
static bool sExposeTestInterfaceEnabled = false;
static bool sExposeTestInterfacePrefCached = false;
if (!sExposeTestInterfacePrefCached) {
sExposeTestInterfacePrefCached = true;
Preferences::AddBoolVarCache(&sExposeTestInterfaceEnabled,
"dom.script_loader.bytecode_cache.enabled",
false);
}
return sExposeTestInterfaceEnabled;
}
nsresult
nsScriptLoader::RestartLoad(nsScriptLoadRequest *aRequest)
{
MOZ_ASSERT(aRequest->IsBytecode());
aRequest->mScriptBytecode.clearAndFree();
TRACE_FOR_TEST(aRequest->mElement, "scriptloader_fallback");
// Start a new channel from which we explicitly request to stream the source
// instead of the bytecode.
aRequest->mProgress = nsScriptLoadRequest::Progress::Loading_Source;
nsresult rv = StartLoad(aRequest);
if (NS_FAILED(rv)) {
return rv;
}
// Close the current channel and this ScriptLoadHandler as we created a new
// one for the same request.
return NS_BINDING_RETARGETED;
}
nsresult
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest)
{
MOZ_ASSERT(aRequest->IsLoading());
NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
aRequest->mDataType = nsScriptLoadRequest::DataType::Unknown;
// If this document is sandboxed without 'allow-scripts', abort.
if (mDocument->HasScriptsBlockedBySandbox()) {
@ -1281,6 +1398,28 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest)
NS_ENSURE_SUCCESS(rv, rv);
// To avoid decoding issues, the JSVersion is explicitly guarded here, and the
// build-id is part of the kBytecodeMimeType constant.
aRequest->mCacheInfo = nullptr;
nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(channel));
if (cic && IsBytecodeCacheEnabled() && aRequest->mJSVersion == JSVERSION_DEFAULT) {
if (!aRequest->IsLoadingSource()) {
// Inform the HTTP cache that we prefer to have information coming from the
// bytecode cache instead of the sources, if such entry is already registered.
LOG(("ScriptLoadRequest (%p): Maybe request bytecode", aRequest));
cic->PreferAlternativeDataType(kBytecodeMimeType);
} else {
// If we are explicitly loading from the sources, such as after a
// restarted request, we might still want to save the bytecode after.
//
// The following tell the cache to look for an alternative data type which
// does not exist, such that we can later save the bytecode with a
// different alternative data type.
LOG(("ScriptLoadRequest (%p): Request saving bytecode later", aRequest));
cic->PreferAlternativeDataType(kNullMimeType);
}
}
nsIScriptElement *script = aRequest->mElement;
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
@ -1713,6 +1852,9 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
request->mIsInline = true;
request->mURI = mDocument->GetDocumentURI();
request->mLineNo = aElement->GetScriptLineNumber();
request->mProgress = nsScriptLoadRequest::Progress::Loading_Source;
request->mDataType = nsScriptLoadRequest::DataType::Source;
TRACE_FOR_TEST_BOOL(request->mElement, "scriptloader_load_source");
if (request->IsModuleRequest()) {
nsModuleLoadRequest* modReq = request->AsModuleRequest();
@ -1909,7 +2051,10 @@ nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest)
return rv;
}
if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptText.length())) {
size_t len = aRequest->IsSource()
? aRequest->mScriptText.length()
: aRequest->mScriptBytecode.length();
if (!JS::CanCompileOffThread(cx, options, len)) {
return NS_ERROR_FAILURE;
}
@ -1917,6 +2062,7 @@ nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest)
new NotifyOffThreadScriptLoadCompletedRunnable(aRequest, this);
if (aRequest->IsModuleRequest()) {
MOZ_ASSERT(aRequest->IsSource());
if (!JS::CompileOffThreadModule(cx, options,
aRequest->mScriptText.begin(),
aRequest->mScriptText.length(),
@ -1924,7 +2070,7 @@ nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest)
static_cast<void*>(runnable))) {
return NS_ERROR_OUT_OF_MEMORY;
}
} else {
} else if (aRequest->IsSource()) {
if (!JS::CompileOffThread(cx, options,
aRequest->mScriptText.begin(),
aRequest->mScriptText.length(),
@ -1932,9 +2078,21 @@ nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest)
static_cast<void*>(runnable))) {
return NS_ERROR_OUT_OF_MEMORY;
}
} else {
MOZ_ASSERT(aRequest->IsBytecode());
if (!JS::DecodeOffThreadScript(cx, options,
aRequest->mScriptBytecode,
aRequest->mBytecodeOffset,
OffThreadScriptLoaderCallback,
static_cast<void*>(runnable))) {
return NS_ERROR_OUT_OF_MEMORY;
}
}
mDocument->BlockOnload();
// Once the compilation is finished, an event would be added to the event loop
// to call nsScriptLoader::ProcessOffThreadRequest with the same request.
aRequest->mProgress = nsScriptLoadRequest::Progress::Compiling;
Unused << runnable.forget();
@ -2072,7 +2230,8 @@ nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
aRequest->MaybeCancelOffThreadScript();
}
// Free any source data.
// Free any source data, but keep the bytecode content as we might have to
// save it later.
aRequest->mScriptText.clearAndFree();
return rv;
@ -2172,12 +2331,30 @@ nsScriptLoader::FillCompileOptionsForRequest(const AutoJSAPI&jsapi,
aOptions->setElement(&elementVal.toObject());
}
// When testing, we want to force use of the bytecode cache.
static bool sForceBytecodeCacheEnabled = false;
static bool sForceBytecodeCachePrefCached = false;
if (!sForceBytecodeCachePrefCached) {
sForceBytecodeCachePrefCached = true;
Preferences::AddBoolVarCache(&sForceBytecodeCacheEnabled,
"dom.script_loader.force_bytecode_cache",
false);
}
// At the moment, the bytecode cache is only triggered if a script is large
// enough to be parsed out of the main thread. Thus, for testing purposes, we
// force parsing any script out of the main thread.
if (sForceBytecodeCacheEnabled) {
aOptions->forceAsync = true;
}
return NS_OK;
}
nsresult
nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest)
{
MOZ_ASSERT(aRequest->IsReadyToRun());
// We need a document to evaluate scripts.
if (!mDocument) {
return NS_ERROR_FAILURE;
@ -2237,6 +2414,10 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest)
}
if (aRequest->IsModuleRequest()) {
// When a module is already loaded, it is not feched a second time and the
// mDataType of the request might remain set to DataType::Unknown.
MOZ_ASSERT(!aRequest->IsBytecode());
LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
nsModuleLoadRequest* request = aRequest->AsModuleRequest();
MOZ_ASSERT(request->mModuleScript);
MOZ_ASSERT(!request->mOffThreadToken);
@ -2251,39 +2432,278 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest)
MOZ_ASSERT(module);
rv = nsJSUtils::ModuleEvaluation(aes.cx(), module);
}
aRequest->mCacheInfo = nullptr;
} else {
JS::CompileOptions options(aes.cx());
rv = FillCompileOptionsForRequest(aes, aRequest, global, &options);
if (NS_SUCCEEDED(rv)) {
{
if (aRequest->IsBytecode()) {
TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
nsJSUtils::ExecutionContext exec(aes.cx(), global);
if (aRequest->mOffThreadToken) {
JS::Rooted<JSScript*> script(aes.cx());
rv = exec.SyncAndExec(&aRequest->mOffThreadToken, &script);
LOG(("ScriptLoadRequest (%p): Decode Bytecode & Join and Execute", aRequest));
rv = exec.DecodeJoinAndExec(&aRequest->mOffThreadToken);
} else {
LOG(("ScriptLoadRequest (%p): Decode Bytecode and Execute", aRequest));
rv = exec.DecodeAndExec(options, aRequest->mScriptBytecode,
aRequest->mBytecodeOffset);
}
// We do not expect to be saving anything when we already have some
// bytecode.
MOZ_ASSERT(!aRequest->mCacheInfo);
} else {
MOZ_ASSERT(aRequest->IsSource());
if (aRequest->mOffThreadToken) {
// Off-main-thread parsing.
LOG(("ScriptLoadRequest (%p): Join (off-thread parsing) and Execute",
aRequest));
{
nsJSUtils::ExecutionContext exec(aes.cx(), global);
JS::Rooted<JSScript*> script(aes.cx());
if (!aRequest->mCacheInfo) {
TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
rv = exec.JoinAndExec(&aRequest->mOffThreadToken, &script);
LOG(("ScriptLoadRequest (%p): Cannot cache anything (cacheInfo = nullptr)",
aRequest));
} else {
TRACE_FOR_TEST(aRequest->mElement, "scriptloader_encode_and_execute");
MOZ_ASSERT(aRequest->mBytecodeOffset ==
aRequest->mScriptBytecode.length());
rv = exec.JoinEncodeAndExec(&aRequest->mOffThreadToken,
aRequest->mScriptBytecode,
&script);
// Queue the current script load request to later save the bytecode.
if (NS_SUCCEEDED(rv)) {
aRequest->mScript = script;
HoldJSObjects(aRequest);
RegisterForBytecodeEncoding(aRequest);
} else {
LOG(("ScriptLoadRequest (%p): Cannot cache anything (rv = %X, script = %p, cacheInfo = %p)",
aRequest, unsigned(rv), script.get(), aRequest->mCacheInfo.get()));
TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_bytecode_failed");
aRequest->mCacheInfo = nullptr;
}
}
}
} else {
// Main thread parsing (inline and small scripts)
LOG(("ScriptLoadRequest (%p): Compile And Exec", aRequest));
nsJSUtils::ExecutionContext exec(aes.cx(), global);
nsAutoString inlineData;
SourceBufferHolder srcBuf = GetScriptSource(aRequest, inlineData);
TRACE_FOR_TEST(aRequest->mElement, "scriptloader_execute");
rv = exec.CompileAndExec(options, srcBuf);
aRequest->mCacheInfo = nullptr;
}
}
}
}
// Even if we are not saving the bytecode of the current script, we have
// to trigger the encoding of the bytecode, as the current script can
// call functions of a script for which we are recording the bytecode.
MaybeTriggerBytecodeEncoding();
}
context->SetProcessingScriptTag(oldProcessingScriptTag);
return rv;
}
void
nsScriptLoader::RegisterForBytecodeEncoding(nsScriptLoadRequest* aRequest)
{
MOZ_ASSERT(aRequest->mCacheInfo);
MOZ_ASSERT(aRequest->mScript);
mBytecodeEncodingQueue.AppendElement(aRequest);
}
void
nsScriptLoader::LoadEventFired()
{
mLoadEventFired = true;
MaybeTriggerBytecodeEncoding();
}
void
nsScriptLoader::MaybeTriggerBytecodeEncoding()
{
// We wait for the load event to be fired before saving the bytecode of
// any script to the cache. It is quite common to have load event
// listeners trigger more JavaScript execution, that we want to save as
// part of this start-up bytecode cache.
if (!mLoadEventFired) {
return;
}
// No need to fire any event if there is no bytecode to be saved.
if (mBytecodeEncodingQueue.isEmpty()) {
return;
}
// Wait until all scripts are loaded before saving the bytecode, such that
// we capture most of the intialization of the page.
if (HasPendingRequests()) {
return;
}
// Create a new runnable dedicated to encoding the content of the bytecode
// of all enqueued scripts. In case of failure, we give-up on encoding the
// bytecode.
nsCOMPtr<nsIRunnable> encoder =
NewRunnableMethod("nsScriptLoader::EncodeBytecode",
this, &nsScriptLoader::EncodeBytecode);
if (NS_FAILED(NS_DispatchToCurrentThread(encoder))) {
GiveUpBytecodeEncoding();
}
}
void
nsScriptLoader::EncodeBytecode()
{
// If any script got added in the previous loop cycle, wait until all
// remaining script executions are completed, such that we capture most of
// the initialization.
if (HasPendingRequests()) {
return;
}
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
if (!globalObject) {
GiveUpBytecodeEncoding();
return;
}
nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
if (!context) {
GiveUpBytecodeEncoding();
return;
}
AutoEntryScript aes(globalObject, "encode bytecode", true);
RefPtr<nsScriptLoadRequest> request;
while (!mBytecodeEncodingQueue.isEmpty()) {
request = mBytecodeEncodingQueue.StealFirst();
EncodeRequestBytecode(aes.cx(), request);
request->mScriptBytecode.clearAndFree();
request->DropBytecodeCacheReferences();
}
}
void
nsScriptLoader::EncodeRequestBytecode(JSContext* aCx, nsScriptLoadRequest* aRequest)
{
nsresult rv = NS_OK;
MOZ_ASSERT(aRequest->mCacheInfo);
auto bytecodeFailed = mozilla::MakeScopeExit([&]() {
TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_bytecode_failed");
});
JS::RootedScript script(aCx, aRequest->mScript);
if (!JS::FinishIncrementalEncoding(aCx, script)) {
LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode",
aRequest));
return;
}
if (aRequest->mScriptBytecode.length() >= UINT32_MAX) {
LOG(("ScriptLoadRequest (%p): Bytecode cache is too large to be decoded correctly.",
aRequest));
return;
}
// Open the output stream to the cache entry alternate data storage. This
// might fail if the stream is already open by another request, in which
// case, we just ignore the current one.
nsCOMPtr<nsIOutputStream> output;
rv = aRequest->mCacheInfo->OpenAlternativeOutputStream(kBytecodeMimeType,
getter_AddRefs(output));
if (NS_FAILED(rv)) {
LOG(("ScriptLoadRequest (%p): Cannot open bytecode cache (rv = %X, output = %p)",
aRequest, unsigned(rv), output.get()));
return;
}
MOZ_ASSERT(output);
auto closeOutStream = mozilla::MakeScopeExit([&]() {
nsresult rv = output->Close();
LOG(("ScriptLoadRequest (%p): Closing (rv = %X)",
aRequest, unsigned(rv)));
});
uint32_t n;
rv = output->Write(reinterpret_cast<char*>(aRequest->mScriptBytecode.begin()),
aRequest->mScriptBytecode.length(), &n);
LOG(("ScriptLoadRequest (%p): Write bytecode cache (rv = %X, length = %u, written = %u)",
aRequest, unsigned(rv), unsigned(aRequest->mScriptBytecode.length()), n));
if (NS_FAILED(rv)) {
return;
}
bytecodeFailed.release();
TRACE_FOR_TEST_NONE(aRequest->mElement, "scriptloader_bytecode_saved");
}
void
nsScriptLoadRequest::DropBytecodeCacheReferences()
{
mCacheInfo = nullptr;
mScript = nullptr;
DropJSObjects(this);
}
void
nsScriptLoader::GiveUpBytecodeEncoding()
{
// Ideally we prefer to properly end the incremental encoder, such that we
// would not keep a large buffer around. If we cannot, we fallback on the
// removal of all request from the current list.
nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
if (globalObject) {
nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
if (context) {
AutoEntryScript aes(globalObject, "give-up bytecode encoding", true);
JS::RootedScript script(aes.cx());
while (!mBytecodeEncodingQueue.isEmpty()) {
RefPtr<nsScriptLoadRequest> request = mBytecodeEncodingQueue.StealFirst();
LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode", request.get()));
TRACE_FOR_TEST_NONE(request->mElement, "scriptloader_bytecode_failed");
script.set(request->mScript);
Unused << JS::FinishIncrementalEncoding(aes.cx(), script);
request->mScriptBytecode.clearAndFree();
request->DropBytecodeCacheReferences();
}
return;
}
}
while (!mBytecodeEncodingQueue.isEmpty()) {
RefPtr<nsScriptLoadRequest> request = mBytecodeEncodingQueue.StealFirst();
LOG(("ScriptLoadRequest (%p): Cannot serialize bytecode", request.get()));
TRACE_FOR_TEST_NONE(request->mElement, "scriptloader_bytecode_failed");
// Note: Do not clear the mScriptBytecode buffer, because the incremental
// encoder owned by the ScriptSource object still has a reference to this
// buffer. This reference would be removed as soon as the ScriptSource
// object would be GC.
request->mCacheInfo = nullptr;
}
}
bool
nsScriptLoader::HasPendingRequests()
{
return mParserBlockingRequest ||
!mXSLTRequests.isEmpty() ||
!mLoadedAsyncRequests.isEmpty() ||
!mNonAsyncExternalScriptInsertedRequests.isEmpty() ||
!mDeferRequests.isEmpty() ||
!mPendingChildLoaders.IsEmpty();
}
void
nsScriptLoader::ProcessPendingRequestsAsync()
{
if (mParserBlockingRequest ||
!mXSLTRequests.isEmpty() ||
!mLoadedAsyncRequests.isEmpty() ||
!mNonAsyncExternalScriptInsertedRequests.isEmpty() ||
!mDeferRequests.isEmpty() ||
!mPendingChildLoaders.IsEmpty()) {
if (HasPendingRequests()) {
nsCOMPtr<nsIRunnable> task = NewRunnableMethod(this,
&nsScriptLoader::ProcessPendingRequests);
if (mDocument) {
@ -2574,6 +2994,47 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
bool sriOk = NS_SUCCEEDED(rv);
// If we are loading from source, save the computed SRI hash or a dummy SRI
// hash in case we are going to save the bytecode of this script in the cache.
if (sriOk && aRequest->IsSource()) {
MOZ_ASSERT(aRequest->mScriptBytecode.empty());
// If the integrity metadata does not correspond to a valid hash function,
// IsComplete would be false.
if (!aRequest->mIntegrity.IsEmpty() && aSRIDataVerifier->IsComplete()) {
// Encode the SRI computed hash.
uint32_t len = aSRIDataVerifier->DataSummaryLength();
if (!aRequest->mScriptBytecode.growBy(len)) {
return NS_ERROR_OUT_OF_MEMORY;
}
aRequest->mBytecodeOffset = len;
DebugOnly<nsresult> res = aSRIDataVerifier->ExportDataSummary(
aRequest->mScriptBytecode.length(),
aRequest->mScriptBytecode.begin());
MOZ_ASSERT(NS_SUCCEEDED(res));
} else {
// Encode a dummy SRI hash.
uint32_t len = SRICheckDataVerifier::EmptyDataSummaryLength();
if (!aRequest->mScriptBytecode.growBy(len)) {
return NS_ERROR_OUT_OF_MEMORY;
}
aRequest->mBytecodeOffset = len;
DebugOnly<nsresult> res = SRICheckDataVerifier::ExportEmptyDataSummary(
aRequest->mScriptBytecode.length(),
aRequest->mScriptBytecode.begin());
MOZ_ASSERT(NS_SUCCEEDED(res));
}
// Verify that the exported and predicted length correspond.
mozilla::DebugOnly<uint32_t> srilen;
MOZ_ASSERT(NS_SUCCEEDED(SRICheckDataVerifier::DataSummaryLength(
aRequest->mScriptBytecode.length(),
aRequest->mScriptBytecode.begin(),
&srilen)));
MOZ_ASSERT(srilen == aRequest->mBytecodeOffset);
}
if (sriOk) {
rv = PrepareLoadedRequest(aRequest, aLoader, aChannelStatus);
}
@ -2607,7 +3068,12 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
mDocument->AddBlockedTrackingNode(cont);
}
if (aRequest->mIsDefer) {
if (aChannelStatus == NS_BINDING_RETARGETED) {
// When loading bytecode, we verify the SRI hash. If it does not match the
// one from the document we restart the load, forcing us to load the
// source instead. This test makes sure we do not remove the current
// request from the following lists when we restart the load.
} else if (aRequest->mIsDefer) {
MOZ_ASSERT_IF(aRequest->IsModuleRequest(),
aRequest->AsModuleRequest()->IsTopLevel());
if (aRequest->isInList()) {
@ -2716,6 +3182,7 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
if (aRequest->IsCanceled()) {
return NS_BINDING_ABORTED;
}
MOZ_ASSERT(aRequest->IsLoading());
// If we don't have a document, then we need to abort further
// evaluation.
@ -2775,6 +3242,7 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
"aRequest should be pending!");
if (aRequest->IsModuleRequest()) {
MOZ_ASSERT(aRequest->IsSource());
nsModuleLoadRequest* request = aRequest->AsModuleRequest();
// When loading a module, only responses with a JavaScript MIME type are
@ -2802,7 +3270,7 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
aRequest->SetReady();
// If this is currently blocking the parser, attempt to compile it off-main-thread.
if (aRequest == mParserBlockingRequest && (NumberOfProcessors() > 1)) {
if (aRequest == mParserBlockingRequest && NumberOfProcessors() > 1) {
MOZ_ASSERT(!aRequest->IsModuleRequest());
nsresult rv = AttemptAsyncScriptCompile(aRequest);
if (rv == NS_OK) {
@ -2936,7 +3404,10 @@ nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader,
mSRIDataVerifier(aSRIDataVerifier),
mSRIStatus(NS_OK),
mDecoder()
{}
{
MOZ_ASSERT(mRequest->IsUnknownDataType());
MOZ_ASSERT(mRequest->IsLoading());
}
nsScriptLoadHandler::~nsScriptLoadHandler()
{}
@ -2956,22 +3427,42 @@ nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
return NS_OK;
}
if (!EnsureDecoder(aLoader, aData, aDataLength,
/* aEndOfStream = */ false)) {
return NS_OK;
nsresult rv = NS_OK;
if (mRequest->IsUnknownDataType()) {
rv = EnsureKnownDataType(aLoader);
NS_ENSURE_SUCCESS(rv, rv);
}
// Below we will/shall consume entire data chunk.
*aConsumedLength = aDataLength;
if (mRequest->IsSource()) {
if (!EnsureDecoder(aLoader, aData, aDataLength,
/* aEndOfStream = */ false)) {
return NS_OK;
}
// Decoder has already been initialized. -- trying to decode all loaded bytes.
nsresult rv = DecodeRawData(aData, aDataLength,
/* aEndOfStream = */ false);
NS_ENSURE_SUCCESS(rv, rv);
// Below we will/shall consume entire data chunk.
*aConsumedLength = aDataLength;
// If SRI is required for this load, appending new bytes to the hash.
if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
// Decoder has already been initialized. -- trying to decode all loaded bytes.
rv = DecodeRawData(aData, aDataLength, /* aEndOfStream = */ false);
NS_ENSURE_SUCCESS(rv, rv);
// If SRI is required for this load, appending new bytes to the hash.
if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
}
} else {
MOZ_ASSERT(mRequest->IsBytecode());
if (!mRequest->mScriptBytecode.append(aData, aDataLength)) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aConsumedLength = aDataLength;
rv = MaybeDecodeSRI();
if (NS_FAILED(rv)) {
nsCOMPtr<nsIRequest> channelRequest;
aLoader->GetRequest(getter_AddRefs(channelRequest));
return channelRequest->Cancel(mScriptLoader->RestartLoad(mRequest));
}
}
return rv;
@ -3115,6 +3606,68 @@ nsScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader,
return true;
}
nsresult
nsScriptLoadHandler::MaybeDecodeSRI()
{
if (!mSRIDataVerifier || mSRIDataVerifier->IsComplete() || NS_FAILED(mSRIStatus)) {
return NS_OK;
}
// Skip until the content is large enough to be decoded.
if (mRequest->mScriptBytecode.length() <= mSRIDataVerifier->DataSummaryLength()) {
return NS_OK;
}
mSRIStatus = mSRIDataVerifier->ImportDataSummary(
mRequest->mScriptBytecode.length(), mRequest->mScriptBytecode.begin());
if (NS_FAILED(mSRIStatus)) {
// We are unable to decode the hash contained in the alternate data which
// contains the bytecode, or it does not use the same algorithm.
LOG(("nsScriptLoadHandler::MaybeDecodeSRI, failed to decode SRI, restart request"));
return mSRIStatus;
}
mRequest->mBytecodeOffset = mSRIDataVerifier->DataSummaryLength();
return NS_OK;
}
nsresult
nsScriptLoadHandler::EnsureKnownDataType(nsIIncrementalStreamLoader *aLoader)
{
MOZ_ASSERT(mRequest->IsUnknownDataType());
MOZ_ASSERT(mRequest->IsLoading());
if (mRequest->IsLoadingSource()) {
mRequest->mDataType = nsScriptLoadRequest::DataType::Source;
TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
return NS_OK;
}
nsCOMPtr<nsIRequest> req;
nsresult rv = aLoader->GetRequest(getter_AddRefs(req));
MOZ_ASSERT(req, "StreamLoader's request went away prematurely");
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(req));
if (cic) {
nsAutoCString altDataType;
cic->GetAlternativeDataType(altDataType);
if (altDataType == kBytecodeMimeType) {
mRequest->mDataType = nsScriptLoadRequest::DataType::Bytecode;
TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_bytecode");
} else {
mRequest->mDataType = nsScriptLoadRequest::DataType::Source;
TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
}
} else {
mRequest->mDataType = nsScriptLoadRequest::DataType::Source;
TRACE_FOR_TEST(mRequest->mElement, "scriptloader_load_source");
}
MOZ_ASSERT(!mRequest->IsUnknownDataType());
MOZ_ASSERT(mRequest->IsLoading());
return NS_OK;
}
NS_IMETHODIMP
nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
@ -3122,20 +3675,92 @@ nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
uint32_t aDataLength,
const uint8_t* aData)
{
if (!mRequest->IsCanceled()) {
DebugOnly<bool> encoderSet =
EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true);
MOZ_ASSERT(encoderSet);
DebugOnly<nsresult> rv = DecodeRawData(aData, aDataLength,
/* aEndOfStream = */ true);
nsresult rv = NS_OK;
if (LOG_ENABLED()) {
nsAutoCString url;
mRequest->mURI->GetAsciiSpec(url);
LOG(("ScriptLoadRequest (%p): Stream complete (url = %s)",
mRequest.get(), url.get()));
}
// If SRI is required for this load, appending new bytes to the hash.
if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
nsCOMPtr<nsIRequest> channelRequest;
aLoader->GetRequest(getter_AddRefs(channelRequest));
if (!mRequest->IsCanceled()) {
if (mRequest->IsUnknownDataType()) {
rv = EnsureKnownDataType(aLoader);
NS_ENSURE_SUCCESS(rv, rv);
}
if (mRequest->IsSource()) {
DebugOnly<bool> encoderSet =
EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true);
MOZ_ASSERT(encoderSet);
rv = DecodeRawData(aData, aDataLength, /* aEndOfStream = */ true);
NS_ENSURE_SUCCESS(rv, rv);
LOG(("ScriptLoadRequest (%p): Source length = %u",
mRequest.get(), unsigned(mRequest->mScriptText.length())));
// If SRI is required for this load, appending new bytes to the hash.
if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData);
}
} else {
MOZ_ASSERT(mRequest->IsBytecode());
if (!mRequest->mScriptBytecode.append(aData, aDataLength)) {
return NS_ERROR_OUT_OF_MEMORY;
}
LOG(("ScriptLoadRequest (%p): Bytecode length = %u",
mRequest.get(), unsigned(mRequest->mScriptBytecode.length())));
// If we abort while decoding the SRI, we fallback on explictly requesting
// the source. Thus, we should not continue in
// nsScriptLoader::OnStreamComplete, which removes the request from the
// waiting lists.
rv = MaybeDecodeSRI();
if (NS_FAILED(rv)) {
return channelRequest->Cancel(mScriptLoader->RestartLoad(mRequest));
}
// The bytecode cache always starts with the SRI hash, thus even if there
// is no SRI data verifier instance, we still want to skip the hash.
rv = SRICheckDataVerifier::DataSummaryLength(mRequest->mScriptBytecode.length(),
mRequest->mScriptBytecode.begin(),
&mRequest->mBytecodeOffset);
if (NS_FAILED(rv)) {
return channelRequest->Cancel(mScriptLoader->RestartLoad(mRequest));
}
}
}
// Everything went well, keep the CacheInfoChannel alive such that we can
// later save the bytecode on the cache entry.
if (NS_SUCCEEDED(rv) && mRequest->IsSource() && IsBytecodeCacheEnabled()) {
mRequest->mCacheInfo = do_QueryInterface(channelRequest);
LOG(("ScriptLoadRequest (%p): nsICacheInfoChannel = %p",
mRequest.get(), mRequest->mCacheInfo.get()));
}
// we have to mediate and use mRequest.
return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus,
mSRIDataVerifier);
rv = mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus,
mSRIDataVerifier);
// In case of failure, clear the mCacheInfoChannel to avoid keeping it alive.
if (NS_FAILED(rv)) {
mRequest->mCacheInfo = nullptr;
}
return rv;
}
#undef TRACE_FOR_TEST
#undef TRACE_FOR_TEST_BOOL
#undef TRACE_FOR_TEST_NONE
#undef LOG_ENABLED
#undef LOG_ERROR
#undef LOG_WARN
#undef LOG
#undef LOG_VERBOSE

View File

@ -19,6 +19,7 @@
#include "nsCycleCollectionParticipant.h"
#include "nsTArray.h"
#include "nsAutoPtr.h"
#include "nsICacheInfoChannel.h"
#include "nsIDocument.h"
#include "nsIIncrementalStreamLoader.h"
#include "nsURIHashKey.h"
@ -76,6 +77,7 @@ public:
mElement(aElement),
mScriptFromHead(false),
mProgress(Progress::Loading),
mDataType(DataType::Unknown),
mIsInline(true),
mHasSourceMapURL(false),
mIsDefer(false),
@ -87,6 +89,8 @@ public:
mIsTracking(false),
mOffThreadToken(nullptr),
mScriptText(),
mScriptBytecode(),
mBytecodeOffset(0),
mJSVersion(aVersion),
mLineNo(1),
mCORSMode(aCORSMode),
@ -96,7 +100,7 @@ public:
}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsScriptLoadRequest)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsScriptLoadRequest)
bool IsModuleRequest() const
{
@ -143,24 +147,48 @@ public:
mIsTracking = true;
}
enum class Progress {
Loading,
enum class Progress : uint8_t {
Loading, // Request either source or bytecode
Loading_Source, // Explicitly Request source stream
Compiling,
FetchingImports,
Ready
};
bool IsReadyToRun() const {
return mProgress == Progress::Ready;
}
bool IsLoading() const {
return mProgress == Progress::Loading;
return mProgress == Progress::Loading ||
mProgress == Progress::Loading_Source;
}
bool IsLoadingSource() const {
return mProgress == Progress::Loading_Source;
}
bool InCompilingStage() const {
return mProgress == Progress::Compiling ||
(IsReadyToRun() && mWasCompiledOMT);
}
// Type of data provided by the nsChannel.
enum class DataType : uint8_t {
Unknown,
Source,
Bytecode
};
bool IsUnknownDataType() const {
return mDataType == DataType::Unknown;
}
bool IsSource() const {
return mDataType == DataType::Source;
}
bool IsBytecode() const {
return mDataType == DataType::Bytecode;
}
void MaybeCancelOffThreadScript();
void DropBytecodeCacheReferences();
using super::getNext;
using super::isInList;
@ -169,6 +197,7 @@ public:
nsCOMPtr<nsIScriptElement> mElement;
bool mScriptFromHead; // Synchronous head script block loading of other non js/css content.
Progress mProgress; // Are we still waiting for a load to complete?
DataType mDataType; // Does this contain Source or Bytecode?
bool mIsInline; // Is the script inline or loaded?
bool mHasSourceMapURL; // Does the HTTP header have a source map url?
bool mIsDefer; // True if we live in mDeferRequests.
@ -180,9 +209,20 @@ public:
bool mIsTracking; // True if the script comes from a source on our tracking protection list.
void* mOffThreadToken; // Off-thread parsing token.
nsString mSourceMapURL; // Holds source map url for loaded scripts
// Holds the top-level JSScript that corresponds to the current source, once
// it is parsed, and planned to be saved in the bytecode cache.
JS::Heap<JSScript*> mScript;
// Holds script text for non-inline scripts. Don't use nsString so we can give
// ownership to jsapi.
mozilla::Vector<char16_t> mScriptText;
// Holds the SRI serialized hash and the script bytecode for non-inline
// scripts.
mozilla::Vector<uint8_t> mScriptBytecode;
uint32_t mBytecodeOffset; // Offset of the bytecode in mScriptBytecode
uint32_t mJSVersion;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mOriginPrincipal;
@ -191,6 +231,10 @@ public:
const mozilla::CORSMode mCORSMode;
const mozilla::dom::SRIMetadata mIntegrity;
mozilla::net::ReferrerPolicy mReferrerPolicy;
// Holds the Cache information, which is used to register the bytecode
// on the cache entry, such that we can load it the next time.
nsCOMPtr<nsICacheInfoChannel> mCacheInfo;
};
class nsScriptLoadRequestList : private mozilla::LinkedList<nsScriptLoadRequest>
@ -434,6 +478,11 @@ public:
nsresult aSRIStatus,
mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier);
/**
* Returns wether any request is queued, and not executed yet.
*/
bool HasPendingRequests();
/**
* Processes any pending requests that are ready for processing.
*/
@ -503,6 +552,13 @@ public:
return mDocument->GetDocGroup();
}
/**
* Register the fact that we saw the load event, and that we need to save the
* bytecode at the next loop cycle unless new scripts are waiting in the
* pipeline.
*/
void LoadEventFired();
private:
virtual ~nsScriptLoader();
@ -538,6 +594,13 @@ private:
*/
nsresult StartLoad(nsScriptLoadRequest *aRequest);
/**
* Abort the current stream, and re-start with a new load request from scratch
* without requesting any alternate data. Returns NS_BINDING_RETARGETED on
* success, as this error code is used to abort the input stream.
*/
nsresult RestartLoad(nsScriptLoadRequest *aRequest);
/**
* Process any pending requests asynchronously (i.e. off an event) if there
* are any. Note that this is a no-op if there aren't any currently pending
@ -581,6 +644,30 @@ private:
nsScriptLoadRequest* aRequest);
nsresult EvaluateScript(nsScriptLoadRequest* aRequest);
/**
* Queue the current script load request to be saved, when the page
* initialization ends. The page initialization end is defined as being the
* time when the load event got received, and when no more scripts are waiting
* to be executed.
*/
void RegisterForBytecodeEncoding(nsScriptLoadRequest* aRequest);
/**
* Check if all conditions are met, i-e that the onLoad event fired and that
* no more script have to be processed. If all conditions are met, queue an
* event to encode all the bytecode and save them on the cache.
*/
void MaybeTriggerBytecodeEncoding();
/**
* Iterate over all script load request and save the bytecode of executed
* functions on the cache provided by the channel.
*/
void EncodeBytecode();
void EncodeRequestBytecode(JSContext* aCx, nsScriptLoadRequest* aRequest);
void GiveUpBytecodeEncoding();
already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalObject();
nsresult FillCompileOptionsForRequest(const mozilla::dom::AutoJSAPI& jsapi,
nsScriptLoadRequest* aRequest,
@ -635,6 +722,10 @@ private:
nsScriptLoadRequestList mXSLTRequests;
RefPtr<nsScriptLoadRequest> mParserBlockingRequest;
// List of script load request that are holding a buffer which has to be saved
// on the cache.
nsScriptLoadRequestList mBytecodeEncodingQueue;
// In mRequests, the additional information here is stored by the element.
struct PreloadInfo {
RefPtr<nsScriptLoadRequest> mRequest;
@ -668,6 +759,7 @@ private:
bool mDeferEnabled;
bool mDocumentParsingDone;
bool mBlockingDOMContentLoaded;
bool mLoadEventFired;
// Module map
nsRefPtrHashtable<nsURIHashKey, mozilla::GenericPromise::Private> mFetchingModules;
@ -708,6 +800,15 @@ private:
const uint8_t* aData, uint32_t aDataLength,
bool aEndOfStream, nsCString& oCharset);
/*
* When streaming bytecode, we have the opportunity to fallback early if SRI
* does not match the expectation of the document.
*/
nsresult MaybeDecodeSRI();
// Query the channel to find the data type associated with the input stream.
nsresult EnsureKnownDataType(nsIIncrementalStreamLoader *aLoader);
// ScriptLoader which will handle the parsed script.
RefPtr<nsScriptLoader> mScriptLoader;

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Add a tag script to save the bytecode</title>
</head>
<body>
<script id="watchme" src="file_js_cache.js"></script>
</body>
</html>

View File

@ -0,0 +1,5 @@
function baz() {}
function bar() {}
function foo() { bar() }
foo();

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Save the bytecode when all scripts are executed</title>
</head>
<body>
<script id="watchme" src="file_js_cache_save_after_load.js"></script>
</body>
</html>

View File

@ -0,0 +1,15 @@
function send_ping() {
window.dispatchEvent(new Event("ping"));
}
send_ping(); // ping (=1)
window.addEventListener("load", function () {
send_ping(); // ping (=2)
// Append a script which should call |foo|, before the encoding of this script
// bytecode.
var script = document.createElement("script");
script.type = "text/javascript";
script.innerText = "send_ping();"; // ping (=3)
document.head.appendChild(script);
});

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Add a tag script to save the bytecode</title>
</head>
<body>
<script id="watchme" src="file_js_cache.js"
integrity="sha384-8YSwN2ywq1SVThihWhj7uTVZ4UeIDwo3GgdPYnug+C+OS0oa6kH2IXBclwMaDJFb">
</script>
</body>
</html>

View File

@ -732,6 +732,14 @@ skip-if = toolkit == 'android'
[test_root_iframe.html]
[test_screen_orientation.html]
[test_script_loader_crossorigin_data_url.html]
[test_script_loader_js_cache.html]
support-files =
file_js_cache.html
file_js_cache_with_sri.html
file_js_cache.js
file_js_cache_save_after_load.html
file_js_cache_save_after_load.js
skip-if = (os == 'linux' || toolkit == 'android') # mochitest are executed on a single core
[test_selection_with_anon_trees.html]
skip-if = (toolkit == 'android' || asan) # bug 1330526
[test_setInterval_uncatchable_exception.html]

View File

@ -0,0 +1,195 @@
<!DOCTYPE html>
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=900784 -->
<!-- The JS bytecode cache is not supposed to be observable. To make it
observable, the nsScriptLoader is instrumented to trigger events on the
script tag. These events are followed to reconstruct the code path taken by
the script loader and associate a simple name which is checked in these
test cases.
-->
<head>
<meta charset="utf-8">
<title>Test for saving and loading bytecode in/from the necko cache</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script type="application/javascript">
// This is the state machine of the trace events produced by the
// nsScriptLoader. This state machine is used to give a name to each
// code path, such that we can assert each code path with a single word.
var scriptLoaderStateMachine = {
"scriptloader_load_source": {
"scriptloader_encode_and_execute": {
"scriptloader_bytecode_saved": "bytecode_saved",
"scriptloader_bytecode_failed": "bytecode_failed"
},
"scriptloader_execute": "source_exec"
},
"scriptloader_load_bytecode": {
"scriptloader_fallback": {
// Replicate the top-level state machine without
// "scriptloader_load_bytecode" transition.
"scriptloader_load_source": {
"scriptloader_encode_and_execute": {
"scriptloader_bytecode_saved": "fallback_bytecode_saved",
"scriptloader_bytecode_failed": "fallback_bytecode_failed"
},
"scriptloader_execute": "fallback_source_exec"
}
},
"scriptloader_execute": "bytecode_exec"
}
};
function flushNeckoCache() {
return new Promise (resolve => {
// We need to do a GC pass to ensure the cache entry has been freed.
SpecialPowers.gc();
var nsICacheTesting = SpecialPowers.Ci.nsICacheTesting;
var cacheTesting = SpecialPowers.Services.cache2;
cacheTesting = cacheTesting.QueryInterface(nsICacheTesting);
cacheTesting.flush(() => { resolve(); });
});
};
function WaitForScriptTagEvent(url) {
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
var stateMachine = scriptLoaderStateMachine;
var stateHistory = [];
var stateMachineResolve, stateMachineReject;
var statePromise = new Promise((resolve, reject) => {
stateMachineResolve = resolve;
stateMachineReject = reject;
});
var ping = 0;
// Walk the script loader state machine with the emitted events.
function log_event(evt) {
// If we have multiple script tags in the loaded source, make sure
// we only watch a single one.
if (evt.target.id != "watchme")
return;
dump("## ScriptLoader event: " + evt.type + "\n");
stateHistory.push(evt.type)
if (typeof stateMachine == "object")
stateMachine = stateMachine[evt.type];
if (typeof stateMachine == "string") {
// We arrived to a final state, report the name of it.
var result = stateMachine;
if (ping) {
result = `${result} & ping(=${ping})`;
}
stateMachineResolve(result);
} else if (stateMachine === undefined) {
// We followed an unknown transition, report the known history.
stateMachineReject(stateHistory);
}
}
var iwin = iframe.contentWindow;
iwin.addEventListener("scriptloader_load_source", log_event);
iwin.addEventListener("scriptloader_load_bytecode", log_event);
iwin.addEventListener("scriptloader_generate_bytecode", log_event);
iwin.addEventListener("scriptloader_execute", log_event);
iwin.addEventListener("scriptloader_encode_and_execute", log_event);
iwin.addEventListener("scriptloader_bytecode_saved", log_event);
iwin.addEventListener("scriptloader_bytecode_failed", log_event);
iwin.addEventListener("scriptloader_fallback", log_event);
iwin.addEventListener("ping", (evt) => {
ping += 1;
dump(`## Content event: ${evt.type} (=${ping})\n`);
});
iframe.src = url;
statePromise.then(() => {
document.body.removeChild(iframe);
});
return statePromise;
}
promise_test(async function() {
// Setting dom.expose_test_interfaces pref causes the
// nsScriptLoadRequest to fire event on script tags, with information
// about its internal state. The nsScriptLoader source send events to
// trace these and resolve a promise with the path taken by the
// script loader.
//
// Setting dom.script_loader.force_bytecode_cache causes the
// nsScriptLoadRequest to force all the conditions necessary to make a
// script be saved as bytecode in the alternate data storage provided
// by the channel (necko cache).
await SpecialPowers.pushPrefEnv({set: [
['dom.script_loader.bytecode_cache.enabled', true],
['dom.expose_test_interfaces', true],
['dom.script_loader.force_bytecode_cache', true]
]});
// Load the test page, and verify that the code path taken by the
// nsScriptLoadRequest corresponds to the code path which is loading a
// source and saving it as bytecode.
var stateMachineResult = WaitForScriptTagEvent("file_js_cache.html");
assert_equals(await stateMachineResult, "bytecode_saved",
"[1] ScriptLoadRequest status after the first visit");
// When the bytecode is saved, we have to flush the cache to read it.
await flushNeckoCache();
// Reload the same test page, and verify that the code path taken by
// the nsScriptLoadRequest corresponds to the code path which is
// loading bytecode and executing it.
stateMachineResult = WaitForScriptTagEvent("file_js_cache.html");
assert_equals(await stateMachineResult, "bytecode_exec",
"[2] ScriptLoadRequest status after the second visit");
// Load another page which loads the same script with an SRI, while
// the cached bytecode does not have any. This should fallback to
// loading the source before saving the bytecode once more.
stateMachineResult = WaitForScriptTagEvent("file_js_cache_with_sri.html");
assert_equals(await stateMachineResult, "fallback_bytecode_saved",
"[3] ScriptLoadRequest status after the SRI hash");
// When the bytecode is saved, we have to flush the cache to read it.
await flushNeckoCache();
// Loading a page, which has the same SRI should verify the SRI and
// continue by executing the bytecode.
var stateMachineResult1 = WaitForScriptTagEvent("file_js_cache_with_sri.html");
// Loading a page which does not have a SRI while we have one in the
// cache should not change anything. We should also be able to load
// the cache simultanesouly.
var stateMachineResult2 = WaitForScriptTagEvent("file_js_cache.html");
assert_equals(await stateMachineResult1, "bytecode_exec",
"[4] ScriptLoadRequest status after same SRI hash");
assert_equals(await stateMachineResult2, "bytecode_exec",
"[5] ScriptLoadRequest status after visit with no SRI");
}, "Check the JS bytecode cache");
promise_test(async function() {
// (see above)
await SpecialPowers.pushPrefEnv({set: [
['dom.script_loader.bytecode_cache.enabled', true],
['dom.expose_test_interfaces', true],
['dom.script_loader.force_bytecode_cache', true]
]});
// The test page add a new script which generate a "ping" event, which
// should be recorded before the bytecode is stored in the cache.
var stateMachineResult =
WaitForScriptTagEvent("file_js_cache_save_after_load.html");
assert_equals(await stateMachineResult, "bytecode_saved & ping(=3)",
"Wait on all scripts to be executed");
}, "Save bytecode after the initialization of the page");
done();
</script>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=900784">Mozilla Bug 900784</a>
</body>
</html>

View File

@ -893,7 +893,8 @@ imgCacheEntry::imgCacheEntry(imgLoader* loader, imgRequest* request,
// will set this to false.
mEvicted(true),
mHasNoProxies(true),
mForcePrincipalCheck(forcePrincipalCheck)
mForcePrincipalCheck(forcePrincipalCheck),
mInUse(false)
{ }
imgCacheEntry::~imgCacheEntry()
@ -1914,6 +1915,10 @@ imgLoader::RemoveFromCache(const ImageCacheKey& aKey)
RefPtr<imgCacheEntry> entry;
if (cache.Get(aKey, getter_AddRefs(entry)) && entry) {
if (MOZ_UNLIKELY(entry->GetInUse())) {
gfxCriticalNoteOnce << "RemoveFromCache(key) removing inuse cache entry";
}
cache.Remove(aKey);
MOZ_ASSERT(!entry->Evicted(), "Evicting an already-evicted cache entry!");
@ -1954,6 +1959,10 @@ imgLoader::RemoveFromCache(imgCacheEntry* entry)
"imgLoader::RemoveFromCache", "entry's uri",
key.Spec());
if (MOZ_UNLIKELY(entry->GetInUse())) {
gfxCriticalNoteOnce << "RemoveFromCache(entry) removing inuse cache entry";
}
cache.Remove(key);
if (entry->HasNoProxies()) {
@ -2183,6 +2192,7 @@ imgLoader::LoadImage(nsIURI* aURI,
imgCacheTable& cache = GetCache(key);
if (cache.Get(key, getter_AddRefs(entry)) && entry) {
entry->SetInUse(true);
if (ValidateEntry(entry, aURI, aInitialDocumentURI, aReferrerURI,
aReferrerPolicy, aLoadGroup, aObserver, aLoadingDocument,
requestFlags, aContentPolicyType, true, _retval,
@ -2201,14 +2211,17 @@ imgLoader::LoadImage(nsIURI* aURI,
if (mCacheTracker) {
if (MOZ_UNLIKELY(!entry->GetExpirationState()->IsTracked())) {
bool inCache = false;
bool cacheHasEntry = false;
RefPtr<imgCacheEntry> e;
if (cache.Get(key, getter_AddRefs(e)) && e) {
cacheHasEntry = true;
inCache = (e == entry);
}
gfxCriticalNoteOnce << "entry with no proxies is no in tracker "
<< "request->HasConsumers() "
<< (request->HasConsumers() ? "true" : "false")
<< " inCache " << (inCache ? "true" : "false");
<< " inCache " << (inCache ? "true" : "false")
<< " cacheHasEntry " << (cacheHasEntry ? "true" : "false");
}
mCacheTracker->MarkUsed(entry);
@ -2217,7 +2230,11 @@ imgLoader::LoadImage(nsIURI* aURI,
entry->Touch();
entry->SetInUse(false);
} else {
entry->SetInUse(false);
// We can't use this entry. We'll try to load it off the network, and if
// successful, overwrite the old entry in the cache with a new one.
entry = nullptr;
@ -2423,6 +2440,8 @@ imgLoader::LoadImageWithChannel(nsIChannel* channel,
// of post data.
imgCacheTable& cache = GetCache(key);
if (cache.Get(key, getter_AddRefs(entry)) && entry) {
entry->SetInUse(true);
// We don't want to kick off another network load. So we ask
// ValidateEntry to only do validation without creating a new proxy. If
// it says that the entry isn't valid any more, we'll only use the entry
@ -2457,6 +2476,7 @@ imgLoader::LoadImageWithChannel(nsIChannel* channel,
}
if (!bUseCacheCopy) {
entry->SetInUse(false);
entry = nullptr;
} else {
request = entry->GetRequest();
@ -2479,6 +2499,9 @@ imgLoader::LoadImageWithChannel(nsIChannel* channel,
}
}
}
if (entry) {
entry->SetInUse(false);
}
}
}

View File

@ -144,6 +144,14 @@ public:
return mForcePrincipalCheck;
}
bool GetInUse() const {
return mInUse;
}
void SetInUse(bool aInUse) {
mInUse = aInUse;
}
imgLoader* Loader() const
{
return mLoader;
@ -178,6 +186,7 @@ private: // data
bool mEvicted : 1;
bool mHasNoProxies : 1;
bool mForcePrincipalCheck : 1;
bool mInUse : 1;
};
#include <vector>

View File

@ -515,7 +515,8 @@ struct RuntimeSizes
macro(_, MallocHeap, sharedImmutableStringsCache) \
macro(_, MallocHeap, sharedIntlData) \
macro(_, MallocHeap, uncompressedSourceCache) \
macro(_, MallocHeap, scriptData)
macro(_, MallocHeap, scriptData) \
macro(_, MallocHeap, tracelogger)
RuntimeSizes()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -649,6 +650,7 @@ struct ZoneStats
macro(Other, MallocHeap, regExpSharedsMallocHeap) \
macro(Other, MallocHeap, typePool) \
macro(Other, MallocHeap, baselineStubsOptimized) \
macro(Other, MallocHeap, cachedCFG) \
macro(Other, MallocHeap, uniqueIdMap) \
macro(Other, MallocHeap, shapeTables)
@ -961,7 +963,10 @@ AddSizeOfTab(JSContext* cx, JS::HandleObject obj, mozilla::MallocSizeOf mallocSi
extern JS_PUBLIC_API(bool)
AddServoSizeOf(JSContext* cx, mozilla::MallocSizeOf mallocSizeOf,
ObjectPrivateVisitor *opv, ServoSizes *sizes);
ObjectPrivateVisitor* opv, ServoSizes* sizes);
extern JS_PUBLIC_API(void)
CollectTraceLoggerStateStats(RuntimeStats* rtStats);
} // namespace JS

View File

@ -197,7 +197,6 @@ namespace JS {
_(CantInlineTooManyArgs) \
_(CantInlineNeedsArgsObj) \
_(CantInlineDebuggee) \
_(CantInlineUnknownProps) \
_(CantInlineExceededDepth) \
_(CantInlineExceededTotalBytecodeLength) \
_(CantInlineBigCaller) \

View File

@ -1726,7 +1726,7 @@ namespace {
class ASTSerializer
{
JSContext* cx;
Parser<FullParseHandler>* parser;
Parser<FullParseHandler, char16_t>* parser;
NodeBuilder builder;
DebugOnly<uint32_t> lineno;
@ -1832,7 +1832,7 @@ class ASTSerializer
return builder.init(userobj);
}
void setParser(Parser<FullParseHandler>* p) {
void setParser(Parser<FullParseHandler, char16_t>* p) {
parser = p;
builder.setTokenStream(&p->tokenStream);
}
@ -3707,9 +3707,10 @@ reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
UsedNameTracker usedNames(cx);
if (!usedNames.init())
return false;
Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars.begin().get(),
chars.length(), /* foldConstants = */ false, usedNames,
nullptr, nullptr);
Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options,
chars.begin().get(), chars.length(),
/* foldConstants = */ false, usedNames, nullptr,
nullptr);
if (!parser.checkOptions())
return false;

63
js/src/ds/Nestable.h Normal file
View File

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef ds_Nestable_h
#define ds_Nestable_h
namespace js {
// A base class for nestable structures.
template <typename Concrete>
class MOZ_STACK_CLASS Nestable
{
Concrete** stack_;
Concrete* enclosing_;
protected:
explicit Nestable(Concrete** stack)
: stack_(stack),
enclosing_(*stack)
{
*stack_ = static_cast<Concrete*>(this);
}
// These method are protected. Some derived classes, such as ParseContext,
// do not expose the ability to walk the stack.
Concrete* enclosing() const {
return enclosing_;
}
template <typename Predicate /* (Concrete*) -> bool */>
static Concrete* findNearest(Concrete* it, Predicate predicate) {
while (it && !predicate(it))
it = it->enclosing();
return it;
}
template <typename T>
static T* findNearest(Concrete* it) {
while (it && !it->template is<T>())
it = it->enclosing();
return it ? &it->template as<T>() : nullptr;
}
template <typename T, typename Predicate /* (T*) -> bool */>
static T* findNearest(Concrete* it, Predicate predicate) {
while (it && (!it->template is<T>() || !predicate(&it->template as<T>())))
it = it->enclosing();
return it ? &it->template as<T>() : nullptr;
}
public:
~Nestable() {
MOZ_ASSERT(*stack_ == static_cast<Concrete*>(this));
*stack_ = enclosing_;
}
};
} // namespace js
#endif /* ds_Nestable_h */

View File

@ -84,8 +84,8 @@ class MOZ_STACK_CLASS BytecodeCompiler
ScriptSource* scriptSource;
Maybe<UsedNameTracker> usedNames;
Maybe<Parser<SyntaxParseHandler>> syntaxParser;
Maybe<Parser<FullParseHandler>> parser;
Maybe<Parser<SyntaxParseHandler, char16_t>> syntaxParser;
Maybe<Parser<FullParseHandler, char16_t>> parser;
Directives directives;
TokenStream::Position startPosition;
@ -236,8 +236,7 @@ BytecodeCompiler::createParser()
if (canLazilyParse()) {
syntaxParser.emplace(cx, alloc, options, sourceBuffer.get(), sourceBuffer.length(),
/* foldConstants = */ false, *usedNames,
(Parser<SyntaxParseHandler>*) nullptr, (LazyScript*) nullptr);
/* foldConstants = */ false, *usedNames, nullptr, nullptr);
if (!syntaxParser->checkOptions())
return false;
@ -673,8 +672,9 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
UsedNameTracker usedNames(cx);
if (!usedNames.init())
return false;
Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
/* foldConstants = */ true, usedNames, nullptr, lazy);
Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
/* foldConstants = */ true, usedNames, nullptr,
lazy);
if (!parser.checkOptions())
return false;

View File

@ -28,6 +28,7 @@
#include "jstypes.h"
#include "jsutil.h"
#include "ds/Nestable.h"
#include "frontend/Parser.h"
#include "frontend/TokenStream.h"
#include "vm/Debugger.h"
@ -2127,7 +2128,7 @@ class ForOfLoopControl : public LoopControl
};
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
Parser<FullParseHandler>* parser, SharedContext* sc,
Parser<FullParseHandler, char16_t>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript,
uint32_t lineNum, EmitterMode emitterMode)
: sc(sc),
@ -2167,7 +2168,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
}
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
Parser<FullParseHandler>* parser, SharedContext* sc,
Parser<FullParseHandler, char16_t>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript,
TokenPos bodyPosition, EmitterMode emitterMode)
: BytecodeEmitter(parent, parser, sc, script, lazyScript,

View File

@ -23,10 +23,10 @@
namespace js {
namespace frontend {
class FullParseHandler;
template <typename CharT> class FullParseHandler;
class ObjectBox;
class ParseNode;
template <typename ParseHandler> class Parser;
template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
class SharedContext;
class TokenStream;
@ -206,7 +206,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
};
EmitSection prologue, main, *current;
Parser<FullParseHandler>* const parser;
Parser<FullParseHandler, char16_t>* const parser;
PooledMapPtr<AtomIndexMap> atomIndices; /* literals indexed for mapping */
unsigned firstLine; /* first line, for JSScript::initFromEmitter */
@ -280,14 +280,14 @@ struct MOZ_STACK_CLASS BytecodeEmitter
* tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
* destruction.
*/
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
EmitterMode emitterMode = Normal);
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, char16_t>* parser,
SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
uint32_t lineNum, EmitterMode emitterMode = Normal);
// An alternate constructor that uses a TokenPos for the starting
// line and that sets functionBodyEndPos as well.
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript,
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, char16_t>* parser,
SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
TokenPos bodyPosition, EmitterMode emitterMode = Normal);
MOZ_MUST_USE bool init();

View File

@ -519,10 +519,11 @@ Boolish(ParseNode* pn)
}
static bool
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda);
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda);
static bool
FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
// Conditions fold like any other expression...
@ -553,7 +554,7 @@ FoldCondition(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& pars
}
static bool
FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_TYPEOFEXPR));
@ -589,7 +590,7 @@ FoldTypeOfExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_DELETEEXPR));
@ -612,7 +613,7 @@ FoldDeleteExpr(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_DELETEELEM));
@ -637,7 +638,7 @@ FoldDeleteElement(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& pars
}
static bool
FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_DELETEPROP));
@ -659,7 +660,7 @@ FoldDeleteProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& par
}
static bool
FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_NOT));
@ -694,7 +695,7 @@ FoldNot(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_BITNOT) || node->isKind(PNK_POS) || node->isKind(PNK_NEG),
@ -728,7 +729,7 @@ FoldUnaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& pa
}
static bool
FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_PREINCREMENT) ||
@ -738,18 +739,18 @@ FoldIncrementDecrement(JSContext* cx, ParseNode* node, Parser<FullParseHandler>&
MOZ_ASSERT(node->isArity(PN_UNARY));
ParseNode*& target = node->pn_kid;
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
if (!Fold(cx, &target, parser, inGenexpLambda))
return false;
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler>::PermitAssignmentToFunctionCalls));
MOZ_ASSERT(parser.isValidSimpleAssignmentTarget(target, Parser<FullParseHandler, char16_t>::PermitAssignmentToFunctionCalls));
return true;
}
static bool
FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
ParseNode* node = *nodePtr;
@ -832,7 +833,7 @@ FoldAndOr(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
}
static bool
FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
ParseNode** nextNode = nodePtr;
@ -902,7 +903,7 @@ FoldConditional(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& pa
}
static bool
FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
ParseNode** nextNode = nodePtr;
@ -1006,7 +1007,7 @@ FoldIf(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
}
static bool
FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldFunction(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_FUNCTION));
@ -1069,7 +1070,7 @@ ComputeBinary(ParseNodeKind kind, double left, double right)
}
static bool
FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser)
FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser)
{
MOZ_ASSERT(node->isKind(PNK_MODULE));
MOZ_ASSERT(node->isArity(PN_CODE));
@ -1080,7 +1081,7 @@ FoldModule(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser)
}
static bool
FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_SUB) ||
@ -1152,7 +1153,7 @@ FoldBinaryArithmetic(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& p
}
static bool
FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_POW));
@ -1196,7 +1197,7 @@ FoldExponentiation(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& par
}
static bool
FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler>& parser,
FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(list->isArity(PN_LIST));
@ -1216,7 +1217,7 @@ FoldList(JSContext* cx, ParseNode* list, Parser<FullParseHandler>& parser,
}
static bool
FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_RETURN));
@ -1231,7 +1232,7 @@ FoldReturn(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_TRY));
@ -1255,7 +1256,7 @@ FoldTry(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_CATCH));
@ -1279,7 +1280,7 @@ FoldCatch(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_CLASS));
@ -1300,7 +1301,7 @@ FoldClass(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
ParseNode* node = *nodePtr;
@ -1376,7 +1377,7 @@ FoldElement(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser
}
static bool
FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
ParseNode* node = *nodePtr;
@ -1527,7 +1528,7 @@ FoldAdd(JSContext* cx, ParseNode** nodePtr, Parser<FullParseHandler>& parser,
}
static bool
FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_CALL) || node->isKind(PNK_SUPERCALL) ||
@ -1562,7 +1563,7 @@ FoldCall(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_FORIN) || node->isKind(PNK_FOROF));
@ -1574,7 +1575,7 @@ FoldForInOrOf(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_FORHEAD));
@ -1604,7 +1605,7 @@ FoldForHead(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
static bool
FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_DOT));
@ -1622,7 +1623,7 @@ FoldDottedProperty(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& par
}
static bool
FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
MOZ_ASSERT(node->isKind(PNK_NAME));
@ -1635,7 +1636,8 @@ FoldName(JSContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
}
bool
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGenexpLambda)
Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, char16_t>& parser,
bool inGenexpLambda)
{
if (!CheckRecursionLimit(cx))
return false;
@ -1925,8 +1927,9 @@ Fold(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>& parser, bool inGe
return false;
}
template<typename CharT>
bool
frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>* parser)
frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser)
{
// Don't constant-fold inside "use asm" code, as this could create a parse
// tree that doesn't type-check as asm.js.
@ -1936,3 +1939,7 @@ frontend::FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>
AutoTraceLog traceLog(TraceLoggerForCurrentThread(cx), TraceLogger_BytecodeFoldConstants);
return Fold(cx, pnp, *parser, false);
}
template bool
frontend::FoldConstants(JSContext* cx, ParseNode** pnp,
Parser<FullParseHandler, char16_t>* parser);

View File

@ -12,6 +12,9 @@
namespace js {
namespace frontend {
template <typename CharT> class FullParseHandler;
template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
// Perform constant folding on the given AST. For example, the program
// `print(2 + 2)` would become `print(4)`.
//
@ -25,12 +28,14 @@ namespace frontend {
// return false;
// if (!FoldConstants(cx, &pn, parser))
// return false;
template<typename CharT>
MOZ_MUST_USE bool
FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler>* parser);
FoldConstants(JSContext* cx, ParseNode** pnp, Parser<FullParseHandler, CharT>* parser);
template<typename CharT>
inline MOZ_MUST_USE bool
FoldConstants(JSContext* cx, SyntaxParseHandler::Node* pnp,
Parser<SyntaxParseHandler>* parser)
FoldConstants(JSContext* cx, typename SyntaxParseHandler<CharT>::Node* pnp,
Parser<SyntaxParseHandler, CharT>* parser)
{
return true;
}

View File

@ -18,17 +18,16 @@
namespace js {
namespace frontend {
template <typename ParseHandler>
template <template <typename CharT> class ParseHandler, typename CharT>
class Parser;
class SyntaxParseHandler;
template <typename CharT> class SyntaxParseHandler;
// Parse handler used when generating a full parse tree for all code which the
// parser encounters.
class FullParseHandler
class FullParseHandlerBase
{
ParseNodeAllocator allocator;
TokenStream& tokenStream;
ParseNode* allocParseNode(size_t size) {
MOZ_ASSERT(size == sizeof(ParseNode));
@ -53,15 +52,6 @@ class FullParseHandler
size_t lazyClosedOverBindingIndex;
public:
/*
* If non-nullptr, points to a syntax parser which can be used for inner
* functions. Cleared if language features not handled by the syntax parser
* are encountered, in which case all future activity will use the full
* parser.
*/
Parser<SyntaxParseHandler>* syntaxParser;
/* new_ methods for creating parse nodes. These report OOM on context. */
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
@ -93,22 +83,17 @@ class FullParseHandler
isParenthesizedDestructuringPattern(node);
}
FullParseHandler(JSContext* cx, LifoAlloc& alloc,
TokenStream& tokenStream, Parser<SyntaxParseHandler>* syntaxParser,
LazyScript* lazyOuterFunction)
FullParseHandlerBase(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
: allocator(cx, alloc),
tokenStream(tokenStream),
lazyOuterFunction_(cx, lazyOuterFunction),
lazyInnerFunctionIndex(0),
lazyClosedOverBindingIndex(0),
syntaxParser(syntaxParser)
lazyClosedOverBindingIndex(0)
{}
static ParseNode* null() { return nullptr; }
ParseNode* freeTree(ParseNode* pn) { return allocator.freeTree(pn); }
void prepareNodeForMutation(ParseNode* pn) { return allocator.prepareNodeForMutation(pn); }
const Token& currentToken() { return tokenStream.currentToken(); }
ParseNode* newName(PropertyName* name, const TokenPos& pos, JSContext* cx)
{
@ -149,7 +134,7 @@ class FullParseHandler
if (!callSite)
return null();
Node propExpr = newArrayLiteral(getPosition(callSite).begin);
Node propExpr = newArrayLiteral(callSite->pn_pos.begin);
if (!propExpr)
return null();
@ -796,11 +781,8 @@ class FullParseHandler
MOZ_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
}
void setPosition(ParseNode* pn, const TokenPos& pos) {
pn->pn_pos = pos;
}
TokenPos getPosition(ParseNode* pn) {
return pn->pn_pos;
uint32_t getFunctionNameOffset(ParseNode* func, TokenStreamBase& ts) {
return func->pn_pos.begin;
}
bool isDeclarationKind(ParseNodeKind kind) {
@ -922,10 +904,6 @@ class FullParseHandler
node->setOp(node->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
}
void disableSyntaxParser() {
syntaxParser = nullptr;
}
bool canSkipLazyInnerFunctions() {
return !!lazyOuterFunction_;
}
@ -946,8 +924,9 @@ class FullParseHandler
};
inline bool
FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody)
FullParseHandlerBase::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
ParseNode* catchName, ParseNode* catchGuard,
ParseNode* catchBody)
{
ParseNode* catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody);
if (!catchpn)
@ -958,7 +937,8 @@ FullParseHandler::addCatchBlock(ParseNode* catchList, ParseNode* lexicalScope,
}
inline bool
FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, ParseNode* defaultValue)
FullParseHandlerBase::setLastFunctionFormalParameterDefault(ParseNode* funcpn,
ParseNode* defaultValue)
{
ParseNode* arg = funcpn->pn_body->last();
ParseNode* pn = newBinary(PNK_ASSIGN, arg, defaultValue, JSOP_NOP);
@ -986,7 +966,7 @@ FullParseHandler::setLastFunctionFormalParameterDefault(ParseNode* funcpn, Parse
}
inline bool
FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
FullParseHandlerBase::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
{
pn->pn_expr = init;
pn->setOp(JSOP_SETNAME);
@ -996,6 +976,35 @@ FullParseHandler::finishInitializerAssignment(ParseNode* pn, ParseNode* init)
return true;
}
template<typename CharT>
class FullParseHandler : public FullParseHandlerBase
{
using SyntaxParser = Parser<SyntaxParseHandler, CharT>;
/*
* If non-nullptr, points to a syntax parser which can be used for inner
* functions. Cleared if language features not handled by the syntax parser
* are encountered, in which case all future activity will use the full
* parser.
*/
SyntaxParser* syntaxParser;
public:
FullParseHandler(JSContext* cx, LifoAlloc& alloc, SyntaxParser* syntaxParser,
LazyScript* lazyOuterFunction)
: FullParseHandlerBase(cx, alloc, lazyOuterFunction),
syntaxParser(syntaxParser)
{}
SyntaxParser* getSyntaxParser() const {
return syntaxParser;
}
void disableSyntaxParser() {
syntaxParser = nullptr;
}
};
} // namespace frontend
} // namespace js

View File

@ -572,7 +572,7 @@ ParseNodeAllocator::allocNode()
ParseNode*
ParseNode::appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right,
FullParseHandler* handler, ParseContext* pc)
FullParseHandlerBase* handler, ParseContext* pc)
{
// The asm.js specification is written in ECMAScript grammar terms that
// specify *only* a binary tree. It's a royal pain to implement the asm.js

View File

@ -16,7 +16,7 @@ namespace js {
namespace frontend {
class ParseContext;
class FullParseHandler;
class FullParseHandlerBase;
class FunctionBox;
class ObjectBox;
@ -610,7 +610,7 @@ class ParseNode
*/
static ParseNode*
appendOrCreateList(ParseNodeKind kind, JSOp op, ParseNode* left, ParseNode* right,
FullParseHandler* handler, ParseContext* pc);
FullParseHandlerBase* handler, ParseContext* pc);
inline PropertyName* name() const;
inline JSAtom* atom() const;

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
#include "jsiter.h"
#include "jspubtd.h"
#include "ds/Nestable.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/FullParseHandler.h"
#include "frontend/NameAnalysisTypes.h"
@ -29,6 +30,10 @@ class ModuleObject;
namespace frontend {
class ParserBase;
template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
/*
* The struct ParseContext stores information about the current parsing context,
* which is part of the parser state (see the field Parser::pc). The current
@ -127,12 +132,7 @@ class ParseContext : public Nestable<ParseContext>
using Nestable<Scope>::enclosing;
template <typename ParseHandler>
explicit Scope(Parser<ParseHandler>* parser)
: Nestable<Scope>(&parser->pc->innermostScope_),
declared_(parser->context->frontendCollectionPool()),
id_(parser->usedNames.nextScopeId())
{ }
explicit inline Scope(ParserBase* parser);
void dump(ParseContext* pc);
@ -142,7 +142,7 @@ class ParseContext : public Nestable<ParseContext>
MOZ_MUST_USE bool init(ParseContext* pc) {
if (id_ == UINT32_MAX) {
pc->tokenStream_.reportError(JSMSG_NEED_DIET, js_script_str);
pc->tokenStream_.reportErrorNoOffset(JSMSG_NEED_DIET, js_script_str);
return false;
}
@ -258,12 +258,7 @@ class ParseContext : public Nestable<ParseContext>
class VarScope : public Scope
{
public:
template <typename ParseHandler>
explicit VarScope(Parser<ParseHandler>* parser)
: Scope(parser)
{
useAsVarScope(parser->pc);
}
explicit inline VarScope(ParserBase* parser);
};
private:
@ -274,7 +269,7 @@ class ParseContext : public Nestable<ParseContext>
SharedContext* sc_;
// TokenStream used for error reporting.
TokenStream& tokenStream_;
TokenStreamBase& tokenStream_;
// The innermost statement, i.e., top of the statement stack.
Statement* innermostStatement_;
@ -363,11 +358,11 @@ class ParseContext : public Nestable<ParseContext>
bool funHasReturnVoid;
public:
template <typename ParseHandler>
ParseContext(Parser<ParseHandler>* prs, SharedContext* sc, Directives* newDirectives)
template <template <typename CharT> class ParseHandler, typename CharT>
ParseContext(Parser<ParseHandler, CharT>* prs, SharedContext* sc, Directives* newDirectives)
: Nestable<ParseContext>(&prs->pc),
traceLog_(sc->context,
mozilla::IsSame<ParseHandler, FullParseHandler>::value
mozilla::IsSame<ParseHandler<CharT>, FullParseHandler<CharT>>::value
? TraceLogger_ParsingFull
: TraceLogger_ParsingSyntax,
prs->tokenStream),
@ -767,7 +762,7 @@ class UsedNameTracker
}
};
template <typename ParseHandler>
template <class Parser>
class AutoAwaitIsKeyword;
class ParserBase : public StrictModeGetter
@ -955,16 +950,38 @@ class ParserBase : public StrictModeGetter
ObjectBox* newObjectBox(JSObject* obj);
mozilla::Maybe<GlobalScope::Data*> newGlobalScopeData(ParseContext::Scope& scope);
mozilla::Maybe<ModuleScope::Data*> newModuleScopeData(ParseContext::Scope& scope);
mozilla::Maybe<EvalScope::Data*> newEvalScopeData(ParseContext::Scope& scope);
mozilla::Maybe<FunctionScope::Data*> newFunctionScopeData(ParseContext::Scope& scope,
bool hasParameterExprs);
mozilla::Maybe<VarScope::Data*> newVarScopeData(ParseContext::Scope& scope);
mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
protected:
enum InvokedPrediction { PredictUninvoked = false, PredictInvoked = true };
enum ForInitLocation { InForInit, NotInForInit };
};
template <typename ParseHandler>
inline
ParseContext::Scope::Scope(ParserBase* parser)
: Nestable<Scope>(&parser->pc->innermostScope_),
declared_(parser->context->frontendCollectionPool()),
id_(parser->usedNames.nextScopeId())
{ }
inline
ParseContext::VarScope::VarScope(ParserBase* parser)
: Scope(parser)
{
useAsVarScope(parser->pc);
}
template <template<typename CharT> class ParseHandler, typename CharT>
class Parser final : public ParserBase, private JS::AutoGCRooter
{
private:
using Node = typename ParseHandler::Node;
using Node = typename ParseHandler<CharT>::Node;
/*
* A class for temporarily stashing errors while parsing continues.
@ -1026,7 +1043,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
unsigned errorNumber_;
};
Parser<ParseHandler>& parser_;
Parser<ParseHandler, CharT>& parser_;
Error exprError_;
Error destructuringError_;
@ -1051,7 +1068,7 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
void transferErrorTo(ErrorKind kind, PossibleError* other);
public:
explicit PossibleError(Parser<ParseHandler>& parser);
explicit PossibleError(Parser<ParseHandler, CharT>& parser);
// Set a pending destructuring error. Only a single error may be set
// per instance, i.e. subsequent calls to this method are ignored and
@ -1080,18 +1097,18 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
public:
/* State specific to the kind of parse being performed. */
ParseHandler handler;
ParseHandler<CharT> handler;
void prepareNodeForMutation(Node node) { handler.prepareNodeForMutation(node); }
void freeTree(Node node) { handler.freeTree(node); }
public:
Parser(JSContext* cx, LifoAlloc& alloc, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
Parser<SyntaxParseHandler>* syntaxParser, LazyScript* lazyOuterFunction);
const CharT* chars, size_t length, bool foldConstants, UsedNameTracker& usedNames,
Parser<SyntaxParseHandler, CharT>* syntaxParser, LazyScript* lazyOuterFunction);
~Parser();
friend class AutoAwaitIsKeyword<ParseHandler>;
friend class AutoAwaitIsKeyword<Parser>;
void setAwaitIsKeyword(bool isKeyword);
bool checkOptions();
@ -1499,13 +1516,6 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
// Required on Scope exit.
bool propagateFreeNamesAndMarkClosedOverBindings(ParseContext::Scope& scope);
mozilla::Maybe<GlobalScope::Data*> newGlobalScopeData(ParseContext::Scope& scope);
mozilla::Maybe<ModuleScope::Data*> newModuleScopeData(ParseContext::Scope& scope);
mozilla::Maybe<EvalScope::Data*> newEvalScopeData(ParseContext::Scope& scope);
mozilla::Maybe<FunctionScope::Data*> newFunctionScopeData(ParseContext::Scope& scope,
bool hasParameterExprs);
mozilla::Maybe<VarScope::Data*> newVarScopeData(ParseContext::Scope& scope);
mozilla::Maybe<LexicalScope::Data*> newLexicalScopeData(ParseContext::Scope& scope);
Node finishLexicalScope(ParseContext::Scope& scope, Node body);
Node propertyName(YieldHandling yieldHandling, Node propList,
@ -1532,22 +1542,22 @@ class Parser final : public ParserBase, private JS::AutoGCRooter
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
}
static Node null() { return ParseHandler::null(); }
static Node null() { return ParseHandler<CharT>::null(); }
JSAtom* prefixAccessorName(PropertyType propType, HandleAtom propAtom);
bool asmJS(Node list);
};
template <typename ParseHandler>
template <class Parser>
class MOZ_STACK_CLASS AutoAwaitIsKeyword
{
private:
Parser<ParseHandler>* parser_;
Parser* parser_;
bool oldAwaitIsKeyword_;
public:
AutoAwaitIsKeyword(Parser<ParseHandler>* parser, bool awaitIsKeyword) {
AutoAwaitIsKeyword(Parser* parser, bool awaitIsKeyword) {
parser_ = parser;
oldAwaitIsKeyword_ = parser_->awaitIsKeyword_;
parser_->setAwaitIsKeyword(awaitIsKeyword);

View File

@ -61,56 +61,6 @@ StatementKindIsUnlabeledBreakTarget(StatementKind kind)
return StatementKindIsLoop(kind) || kind == StatementKind::Switch;
}
// A base class for nestable structures in the frontend, such as statements
// and scopes.
template <typename Concrete>
class MOZ_STACK_CLASS Nestable
{
Concrete** stack_;
Concrete* enclosing_;
protected:
explicit Nestable(Concrete** stack)
: stack_(stack),
enclosing_(*stack)
{
*stack_ = static_cast<Concrete*>(this);
}
// These method are protected. Some derived classes, such as ParseContext,
// do not expose the ability to walk the stack.
Concrete* enclosing() const {
return enclosing_;
}
template <typename Predicate /* (Concrete*) -> bool */>
static Concrete* findNearest(Concrete* it, Predicate predicate) {
while (it && !predicate(it))
it = it->enclosing();
return it;
}
template <typename T>
static T* findNearest(Concrete* it) {
while (it && !it->template is<T>())
it = it->enclosing();
return it ? &it->template as<T>() : nullptr;
}
template <typename T, typename Predicate /* (T*) -> bool */>
static T* findNearest(Concrete* it, Predicate predicate) {
while (it && (!it->template is<T>() || !predicate(&it->template as<T>())))
it = it->enclosing();
return it ? &it->template as<T>() : nullptr;
}
public:
~Nestable() {
MOZ_ASSERT(*stack_ == static_cast<Concrete*>(this));
*stack_ = enclosing_;
}
};
// These flags apply to both global and function contexts.
class AnyContextFlags
{

View File

@ -7,17 +7,20 @@
#ifndef frontend_SyntaxParseHandler_h
#define frontend_SyntaxParseHandler_h
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include <string.h>
#include "jscntxt.h"
#include "frontend/ParseNode.h"
#include "frontend/TokenStream.h"
namespace js {
namespace frontend {
template <typename ParseHandler>
template <template <typename CharT> class ParseHandler, typename CharT>
class Parser;
// Parse handler used when processing the syntax in a block of code, to generate
@ -30,12 +33,11 @@ class Parser;
// several times faster than doing a full parse/emit, and lazy parsing improves
// both performance and memory usage significantly when pages contain large
// amounts of code that never executes (which happens often).
class SyntaxParseHandler
class SyntaxParseHandlerBase
{
// Remember the last encountered name or string literal during syntax parses.
JSAtom* lastAtom;
TokenPos lastStringPos;
TokenStream& tokenStream;
public:
enum Node {
@ -170,11 +172,8 @@ class SyntaxParseHandler
}
public:
SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc,
TokenStream& tokenStream, Parser<SyntaxParseHandler>* syntaxParser,
LazyScript* lazyOuterFunction)
: lastAtom(nullptr),
tokenStream(tokenStream)
SyntaxParseHandlerBase(JSContext* cx, LifoAlloc& alloc, LazyScript* lazyOuterFunction)
: lastAtom(nullptr)
{}
static Node null() { return NodeFailure; }
@ -403,9 +402,13 @@ class SyntaxParseHandler
void setEndPosition(Node pn, Node oth) {}
void setEndPosition(Node pn, uint32_t end) {}
void setPosition(Node pn, const TokenPos& pos) {}
TokenPos getPosition(Node pn) {
return tokenStream.currentToken().pos;
uint32_t getFunctionNameOffset(Node func, TokenStreamBase& ts) {
// XXX This offset isn't relevant to the offending function name. But
// we may not *have* that function name around, because of how lazy
// parsing works -- the actual name could be outside
// |tokenStream.userbuf|'s observed region. So the current offset
// is the best we can do.
return ts.currentToken().pos.begin;
}
Node newList(ParseNodeKind kind, const TokenPos& pos, JSOp op = JSOP_NOP) {
@ -618,7 +621,7 @@ class SyntaxParseHandler
return false;
}
JSAtom* nextLazyClosedOverBinding() {
MOZ_CRASH("SyntaxParseHandler::canSkipLazyClosedOverBindings must return false");
MOZ_CRASH("SyntaxParseHandlerBase::canSkipLazyClosedOverBindings must return false");
}
void adjustGetToSet(Node node) {}
@ -627,6 +630,26 @@ class SyntaxParseHandler
}
};
template<typename CharT>
class SyntaxParseHandler : public SyntaxParseHandlerBase
{
public:
// Using frontend::SyntaxParseHandler versus SyntaxParseHandler shouldn't
// be necessary per C++11 [temp.local]p1: in template argument lists inside
// a template class, the template class name refers to the template (i.e.
// SyntaxParseHandler) and not to the particular instantiation of the
// template class (i.e. SyntaxParseHandler<CharT>).
//
// Unfortunately, some versions of clang and MSVC are buggy in this regard.
// So we refer to SyntaxParseHandler with a qualified name.
SyntaxParseHandler(JSContext* cx, LifoAlloc& alloc,
Parser<frontend::SyntaxParseHandler, CharT>* syntaxParser,
LazyScript* lazyOuterFunction)
: SyntaxParseHandlerBase(cx, alloc, lazyOuterFunction)
{}
};
} // namespace frontend
} // namespace js

View File

@ -179,6 +179,7 @@ struct Zone : public JS::shadow::Zone,
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* typePool,
size_t* baselineStubsOptimized,
size_t* cachedCFG,
size_t* uniqueIdMap,
size_t* shapeTables,
size_t* atomsMarkBitmaps);

View File

@ -1550,8 +1550,8 @@ GetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId)
void
GetPropIRGenerator::trackAttached(const char* name)
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);
@ -1566,8 +1566,8 @@ GetPropIRGenerator::trackAttached(const char* name)
void
GetPropIRGenerator::trackNotAttached()
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);
@ -2114,8 +2114,8 @@ InIRGenerator::tryAttachStub()
void
InIRGenerator::trackAttached(const char* name)
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);
@ -2131,8 +2131,8 @@ InIRGenerator::trackAttached(const char* name)
void
InIRGenerator::trackNotAttached()
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);
@ -2292,8 +2292,8 @@ HasOwnIRGenerator::tryAttachStub()
void
HasOwnIRGenerator::trackAttached(const char* name)
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);
@ -2308,8 +2308,8 @@ HasOwnIRGenerator::trackAttached(const char* name)
void
HasOwnIRGenerator::trackNotAttached()
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);
@ -2673,8 +2673,8 @@ SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId
void
SetPropIRGenerator::trackAttached(const char* name)
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);
@ -2690,8 +2690,8 @@ SetPropIRGenerator::trackAttached(const char* name)
void
SetPropIRGenerator::trackNotAttached()
{
#ifdef JS_JITSPEW
CacheIRSpewer& sp = GetCacheIRSpewerSingleton();
#ifdef JS_CACHEIR_SPEW
CacheIRSpewer& sp = CacheIRSpewer::singleton();
if (sp.enabled()) {
LockGuard<Mutex> guard(sp.lock());
sp.beginCache(guard, *this);

View File

@ -4,7 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifdef JS_JITSPEW
#ifdef JS_CACHEIR_SPEW
#include "jit/CacheIRSpewer.h"
@ -30,13 +30,7 @@
using namespace js;
using namespace js::jit;
CacheIRSpewer cacheIRspewer;
CacheIRSpewer&
jit::GetCacheIRSpewerSingleton()
{
return cacheIRspewer;
}
CacheIRSpewer CacheIRSpewer::cacheIRspewer;
CacheIRSpewer::CacheIRSpewer()
: outputLock(mutexid::CacheIRSpewer)
@ -174,4 +168,4 @@ CacheIRSpewer::endCache(LockGuard<Mutex>&)
json.ref().endObject();
}
#endif /* JS_JITSPEW */
#endif /* JS_CACHEIR_SPEW */

View File

@ -7,7 +7,7 @@
#ifndef jit_CacheIRSpewer_h
#define jit_CacheIRSpewer_h
#ifdef JS_JITSPEW
#ifdef JS_CACHEIR_SPEW
#include "mozilla/Maybe.h"
@ -25,11 +25,15 @@ class CacheIRSpewer
Mutex outputLock;
Fprinter output;
mozilla::Maybe<JSONPrinter> json;
static CacheIRSpewer cacheIRspewer;
public:
CacheIRSpewer();
~CacheIRSpewer();
static CacheIRSpewer& singleton() { return cacheIRspewer; }
bool init();
bool enabled() { return json.isSome(); }
@ -42,12 +46,9 @@ class CacheIRSpewer
void endCache(LockGuard<Mutex>&);
};
CacheIRSpewer&
GetCacheIRSpewerSingleton();
} // namespace jit
} // namespace js
#endif /* JS_JITSPEW */
#endif /* JS_CACHEIR_SPEW */
#endif /* jit_CacheIRSpewer_h */

View File

@ -9178,16 +9178,20 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
{
Address groupAddr(temp2, offsetof(ReceiverGuard, group));
Address shapeAddr(temp2, offsetof(ReceiverGuard, shape));
Label guardDone, shapeMismatch, noExpando;
Label guardDone, unboxedObject, noExpando;
// This is a guard for an unboxed object.
masm.branchPtr(Assembler::NotEqual, groupAddr, ImmWord(0), &unboxedObject);
// Guard for a normal object, make sure the shape matches.
masm.loadObjShape(obj, temp1);
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, &shapeMismatch);
masm.branchPtr(Assembler::NotEqual, shapeAddr, temp1, ool->entry());
// Ensure the object does not have any elements. The presence of dense
// elements is not captured by the shape tests above.
branchIfNotEmptyObjectElements(obj, ool->entry());
masm.jump(&guardDone);
masm.bind(&shapeMismatch);
masm.bind(&unboxedObject);
masm.loadObjGroup(obj, temp1);
masm.branchPtr(Assembler::NotEqual, groupAddr, temp1, ool->entry());
masm.loadPtr(Address(obj, UnboxedPlainObject::offsetOfExpando()), temp1);
@ -9215,14 +9219,20 @@ CodeGenerator::visitIteratorStartO(LIteratorStartO* lir)
masm.loadObjProto(temp1, temp1);
masm.branchTestPtr(Assembler::NonZero, temp1, temp1, ool->entry());
// Write barrier for stores to the iterator. We only need to take a write
// barrier if NativeIterator::obj is actually going to change.
// Write barrier for stores to the iterator. The iterator JSObject is never
// nursery allocated. Put this in the whole cell buffer when writing a
// nursery pointer into it.
{
// Bug 867815: Unconditionally take this out- of-line so that we do not
// have to post-barrier the store to NativeIter::obj. This just needs
// JIT support for the Cell* buffer.
Address objAddr(niTemp, offsetof(NativeIterator, obj));
masm.branchPtr(Assembler::NotEqual, objAddr, obj, ool->entry());
Label skipBarrier;
masm.branchPtrInNurseryChunk(Assembler::NotEqual, obj, temp1, &skipBarrier);
LiveRegisterSet temps;
temps.add(temp1);
temps.add(temp2);
saveVolatile(temps);
emitPostWriteBarrier(output);
restoreVolatile(temps);
masm.bind(&skipBarrier);
}
// Mark iterator as active.

View File

@ -912,9 +912,9 @@ FlowAliasAnalysis::computeBlockStores(MBasicBlock* block)
if (!blockInfo)
return false;
// First block depends on the first instruction.
if (block->id() == 0) {
MDefinition* firstIns = *graph_.entryBlock()->begin();
// First and osr block depends on the first instruction.
if (block == graph_.entryBlock() || block == graph_.osrBlock()) {
MDefinition* firstIns = *block->begin();
if (!blockInfo->append(firstIns))
return false;
return true;

View File

@ -22,6 +22,7 @@
#include "jit/BaselineFrame.h"
#include "jit/BaselineInspector.h"
#include "jit/BaselineJIT.h"
#include "jit/CacheIRSpewer.h"
#include "jit/CodeGenerator.h"
#include "jit/EagerSimdUnbox.h"
#include "jit/EdgeCaseAnalysis.h"
@ -174,7 +175,15 @@ jit::InitializeIon()
{
if (!TlsJitContext.init())
return false;
CheckLogging();
#ifdef JS_CACHEIR_SPEW
const char* env = getenv("CACHEIR_LOGS");
if (env && env[0])
CacheIRSpewer::singleton().init();
#endif
#if defined(JS_CODEGEN_ARM)
InitARMFlags();
#endif

View File

@ -502,12 +502,6 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
return DontInline(inlineScript, "Script is debuggee");
}
TypeSet::ObjectKey* targetKey = TypeSet::ObjectKey::get(target);
if (targetKey->unknownProperties()) {
trackOptimizationOutcome(TrackedOutcome::CantInlineUnknownProps);
return DontInline(inlineScript, "Target type has unknown properties");
}
return InliningDecision_Inline;
}
@ -4010,10 +4004,6 @@ IonBuilder::makeInliningDecision(JSObject* targetArg, CallInfo& callInfo)
// End of heuristics, we will inline this function.
// TI calls ObjectStateChange to trigger invalidation of the caller.
TypeSet::ObjectKey* targetKey = TypeSet::ObjectKey::get(target);
targetKey->watchStateChangeForInlinedCall(constraints());
outerBuilder->inlinedBytecodeLength_ += targetScript->length();
return InliningDecision_Inline;

View File

@ -19,7 +19,6 @@
#include "jsprf.h"
#include "jit/CacheIRSpewer.h"
#include "jit/Ion.h"
#include "jit/MIR.h"
#include "jit/MIRGenerator.h"
@ -459,8 +458,6 @@ jit::CheckLogging()
" bl-dbg-osr Baseline debug mode on stack recompile messages\n"
" bl-all All baseline spew\n"
"\n"
" cacheir-logs CacheIR IC attach logging\n"
"\n"
);
exit(0);
/*NOTREACHED*/
@ -561,9 +558,6 @@ jit::CheckLogging()
EnableChannel(JitSpew_BaselineDebugModeOSR);
}
if (ContainsFlag(env, "cacheir-logs"))
GetCacheIRSpewerSingleton().init();
JitSpewPrinter().init(stderr);
}

View File

@ -4258,10 +4258,10 @@ JS_BufferIsCompilableUnit(JSContext* cx, HandleObject obj, const char* utf8, siz
frontend::UsedNameTracker usedNames(cx);
if (!usedNames.init())
return false;
frontend::Parser<frontend::FullParseHandler> parser(cx, cx->tempLifoAlloc(),
options, chars, length,
/* foldConstants = */ true,
usedNames, nullptr, nullptr);
frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
options, chars, length,
/* foldConstants = */ true,
usedNames, nullptr, nullptr);
JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
if (!parser.checkOptions() || !parser.parse()) {
// We ran into an error. If it was because we ran out of source, we

View File

@ -559,6 +559,10 @@ NewPropertyIteratorObject(JSContext* cx, unsigned flags)
PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();
// CodeGenerator::visitIteratorStartO assumes the iterator object is not
// inside the nursery when deciding whether a barrier is necessary.
MOZ_ASSERT(!js::gc::IsInsideNursery(res));
MOZ_ASSERT(res->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
return res;
}

View File

@ -505,9 +505,10 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
// When loading from the bytecode cache, we get the CompileOption from
// the document, which specify the version to use. If the version does
// not match, then we should fail.
// not match, then we should fail. This only applies to the top-level,
// and not its inner functions.
mozilla::Maybe<CompileOptions> options;
if (xdr->hasOptions()) {
if (xdr->hasOptions() && (scriptBits & (1 << OwnSource))) {
options.emplace(xdr->cx(), xdr->options());
if (options->version != version_ ||
options->noScriptRval != !!(scriptBits & (1 << NoScriptRval)) ||
@ -516,9 +517,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
return xdr->fail(JS::TranscodeResult_Failure_WrongCompileOption);
}
} else {
options.emplace(xdr->cx());
(*options).setVersion(version_)
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
options.emplace(xdr->cx(), version_);
(*options).setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
}

View File

@ -638,10 +638,11 @@ FINAL_LIBRARY = 'js'
if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_BINARYDATA'] = True
if CONFIG['NIGHTLY_BUILD']:
DEFINES['ENABLE_SIMD'] = True
if CONFIG['MOZ_DEBUG'] or CONFIG['NIGHTLY_BUILD']:
DEFINES['JS_CACHEIR_SPEW'] = True
# Also set in shell/moz.build
DEFINES['ENABLE_SHARED_ARRAY_BUFFER'] = True

View File

@ -4412,8 +4412,9 @@ Parse(JSContext* cx, unsigned argc, Value* vp)
UsedNameTracker usedNames(cx);
if (!usedNames.init())
return false;
Parser<FullParseHandler> parser(cx, cx->tempLifoAlloc(), options, chars, length,
/* foldConstants = */ true, usedNames, nullptr, nullptr);
Parser<FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(), options, chars, length,
/* foldConstants = */ true, usedNames, nullptr,
nullptr);
if (!parser.checkOptions())
return false;
@ -4462,9 +4463,9 @@ SyntaxParse(JSContext* cx, unsigned argc, Value* vp)
UsedNameTracker usedNames(cx);
if (!usedNames.init())
return false;
Parser<frontend::SyntaxParseHandler> parser(cx, cx->tempLifoAlloc(),
options, chars, length, false,
usedNames, nullptr, nullptr);
Parser<frontend::SyntaxParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
options, chars, length, false,
usedNames, nullptr, nullptr);
if (!parser.checkOptions())
return false;

View File

@ -5302,10 +5302,11 @@ Debugger::isCompilableUnit(JSContext* cx, unsigned argc, Value* vp)
frontend::UsedNameTracker usedNames(cx);
if (!usedNames.init())
return false;
frontend::Parser<frontend::FullParseHandler> parser(cx, cx->tempLifoAlloc(),
options, chars.twoByteChars(),
length, /* foldConstants = */ true,
usedNames, nullptr, nullptr);
frontend::Parser<frontend::FullParseHandler, char16_t> parser(cx, cx->tempLifoAlloc(),
options, chars.twoByteChars(),
length,
/* foldConstants = */ true,
usedNames, nullptr, nullptr);
JS::WarningReporter older = JS::SetWarningReporter(cx, nullptr);
if (!parser.checkOptions() || !parser.parse()) {
// We ran into an error. If it was because we ran out of memory we report

View File

@ -322,6 +322,7 @@ StatsZoneCallback(JSRuntime* rt, void* data, Zone* zone)
zone->addSizeOfIncludingThis(rtStats->mallocSizeOf_,
&zStats.typePool,
&zStats.baselineStubsOptimized,
&zStats.cachedCFG,
&zStats.uniqueIdMap,
&zStats.shapeTables,
&rtStats->runtime.atomsMarkBitmaps);
@ -877,6 +878,15 @@ JS::PeakSizeOfTemporary(const JSContext* cx)
return cx->tempLifoAlloc().peakSizeOfExcludingThis();
}
JS_PUBLIC_API(void)
JS::CollectTraceLoggerStateStats(RuntimeStats* rtStats)
{
#ifdef JS_TRACE_LOGGING
rtStats->runtime.tracelogger += SizeOfTraceLogState(rtStats->mallocSizeOf_);
rtStats->runtime.tracelogger += SizeOfTraceLogGraphState(rtStats->mallocSizeOf_);
#endif
}
namespace JS {
class SimpleJSRuntimeStats : public JS::RuntimeStats
@ -936,8 +946,8 @@ AddSizeOfTab(JSContext* cx, HandleObject obj, MallocSizeOf mallocSizeOf, ObjectP
}
JS_PUBLIC_API(bool)
AddServoSizeOf(JSContext* cx, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv,
ServoSizes *sizes)
AddServoSizeOf(JSContext* cx, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor* opv,
ServoSizes* sizes)
{
SimpleJSRuntimeStats rtStats(mallocSizeOf);

View File

@ -44,6 +44,8 @@
#include "js/MemoryMetrics.h"
#include "js/SliceBudget.h"
#include "vm/Debugger.h"
#include "vm/TraceLogging.h"
#include "vm/TraceLoggingGraph.h"
#include "wasm/WasmSignalHandlers.h"
#include "jscntxtinlines.h"
@ -470,6 +472,10 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim
rtSizes->contexts += cx->sizeOfExcludingThis(mallocSizeOf);
rtSizes->temporary += cx->tempLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
rtSizes->interpreterStack += cx->interpreterStack().sizeOfExcludingThis(mallocSizeOf);
#ifdef JS_TRACE_LOGGING
if (cx->traceLogger)
rtSizes->tracelogger += cx->traceLogger->sizeOfIncludingThis(mallocSizeOf);
#endif
}
if (MathCache* cache = caches().maybeGetMathCache())

View File

@ -7,6 +7,7 @@
#include "vm/TraceLogging.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/ScopeExit.h"
#include <string.h>
@ -103,6 +104,12 @@ EnsureTraceLoggerState()
return true;
}
size_t
js::SizeOfTraceLogState(mozilla::MallocSizeOf mallocSizeOf)
{
return traceLoggerState ? traceLoggerState->sizeOfIncludingThis(mallocSizeOf) : 0;
}
void
js::DestroyTraceLoggerThreadState()
{
@ -216,6 +223,25 @@ TraceLoggerThread::silentFail(const char* error)
enabled_ = 0;
}
size_t
TraceLoggerThread::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
size_t size = 0;
#ifdef DEBUG
size += graphStack.sizeOfExcludingThis(mallocSizeOf);
#endif
size += events.sizeOfExcludingThis(mallocSizeOf);
if (graph.get())
size += graph->sizeOfIncludingThis(mallocSizeOf);
return size;
}
size_t
TraceLoggerThread::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
bool
TraceLoggerThread::enable(JSContext* cx)
{
@ -312,6 +338,23 @@ TraceLoggerThreadState::maybeEventText(uint32_t id)
return p->value()->string();
}
size_t
TraceLoggerThreadState::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
{
LockGuard<Mutex> guard(lock);
// Do not count threadLoggers since they are counted by JSContext::traceLogger.
size_t size = 0;
size += pointerMap.sizeOfExcludingThis(mallocSizeOf);
if (textIdPayloads.initialized()) {
size += textIdPayloads.sizeOfExcludingThis(mallocSizeOf);
for (TextIdHashMap::Range r = textIdPayloads.all(); !r.empty(); r.popFront())
r.front().value()->sizeOfIncludingThis(mallocSizeOf);
}
return size;
}
bool
TraceLoggerThread::textIdIsScriptEvent(uint32_t id)
{

View File

@ -9,6 +9,7 @@
#include "mozilla/GuardObjects.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "jsalloc.h"
@ -228,6 +229,12 @@ class TraceLoggerEventPayload {
MOZ_ASSERT(CurrentThreadOwnsTraceLoggerThreadStateLock());
pointerCount_--;
}
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(string_.get());
}
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
};
// Per thread trace logger state.
@ -276,6 +283,9 @@ class TraceLoggerThread : public mozilla::LinkedListElement<TraceLoggerThread>
void silentFail(const char* error);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
bool fail(JSContext* cx, const char* error);
@ -436,6 +446,11 @@ class TraceLoggerThreadState
TraceLoggerEventPayload* getOrCreateEventPayload(JSScript* script);
TraceLoggerEventPayload* getOrCreateEventPayload(const char* filename, size_t lineno,
size_t colno, const void* p);
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
#endif
};
@ -540,6 +555,8 @@ inline void TraceLogStopEventPrivate(TraceLoggerThread* logger, uint32_t id) {
#endif
}
size_t SizeOfTraceLogState(mozilla::MallocSizeOf mallocSizeOf);
// Automatic logging at the start and end of function call.
class MOZ_RAII AutoTraceLog
{

View File

@ -14,6 +14,7 @@
#endif
#include "mozilla/EndianUtils.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/ScopeExit.h"
#include "jsstr.h"
@ -178,6 +179,12 @@ TraceLoggerGraphState::nextLoggerId()
return numLoggers++;
}
size_t
TraceLoggerGraphState::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
{
return 0;
}
static bool
EnsureTraceLoggerGraphState()
{
@ -196,6 +203,12 @@ EnsureTraceLoggerGraphState()
return true;
}
size_t
js::SizeOfTraceLogGraphState(mozilla::MallocSizeOf mallocSizeOf)
{
return traceLoggerGraphState ? traceLoggerGraphState->sizeOfIncludingThis(mallocSizeOf) : 0;
}
void
js::DestroyTraceLoggerGraphState()
{
@ -644,4 +657,17 @@ TraceLoggerGraph::addTextId(uint32_t id, const char* text)
failed = true;
}
size_t
TraceLoggerGraph::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
size_t size = 0;
size += tree.sizeOfExcludingThis(mallocSizeOf);
size += stack.sizeOfExcludingThis(mallocSizeOf);
return size;
}
size_t
TraceLoggerGraph::sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
#undef getpid

View File

@ -7,6 +7,8 @@
#ifndef TraceLoggingGraph_h
#define TraceLoggingGraph_h
#include "mozilla/MemoryReporting.h"
#include "js/TypeDecls.h"
#include "vm/MutexIDs.h"
#include "vm/TraceLoggingTypes.h"
@ -61,6 +63,7 @@
namespace js {
void DestroyTraceLoggerGraphState();
size_t SizeOfTraceLogGraphState(mozilla::MallocSizeOf mallocSizeOf);
} // namespace js
class TraceLoggerGraphState
@ -94,6 +97,11 @@ class TraceLoggerGraphState
uint32_t nextLoggerId();
uint32_t pid() { return pid_; }
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
}
};
class TraceLoggerGraph
@ -218,6 +226,9 @@ class TraceLoggerGraph
uint32_t nextTextId() { return nextTextId_; }
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
private:
bool failed = false;
bool enabled = false;

View File

@ -261,6 +261,10 @@ class ContinuousSpace {
void clear() {
size_ = 0;
}
size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
return mallocSizeOf(data_);
}
};
// The layout of the event log in memory and in the log file.

View File

@ -1494,6 +1494,15 @@ js::FinishCompilation(JSContext* cx, HandleScript script, CompilerConstraintList
succeeded = false;
}
// Add this compilation to the inlinedCompilations list of each inlined
// script, so we can invalidate it on changes to stack type sets.
if (entry.script != script) {
if (!entry.script->types()->addInlinedCompilation(*precompileInfo)) {
ReportOutOfMemory(cx);
return false;
}
}
// If necessary, add constraints to trigger invalidation on the script
// after any future changes to the stack type sets.
if (entry.script->hasFreezeConstraints())
@ -1900,37 +1909,6 @@ ObjectGroup::initialHeap(CompilerConstraintList* constraints)
namespace {
// Constraint which triggers recompilation on any type change in an inlined
// script. The freeze constraints added to stack type sets will only directly
// invalidate the script containing those stack type sets. To invalidate code
// for scripts into which the base script was inlined, ObjectStateChange is used.
class ConstraintDataFreezeObjectForInlinedCall
{
public:
ConstraintDataFreezeObjectForInlinedCall()
{}
const char* kind() { return "freezeObjectForInlinedCall"; }
bool invalidateOnNewType(TypeSet::Type type) { return false; }
bool invalidateOnNewPropertyState(TypeSet* property) { return false; }
bool invalidateOnNewObjectState(ObjectGroup* group) {
// We don't keep track of the exact dependencies the caller has on its
// inlined scripts' type sets, so always invalidate the caller.
return true;
}
bool constraintHolds(JSContext* cx,
const HeapTypeSetKey& property, TemporaryTypeSet* expected)
{
return true;
}
bool shouldSweep() { return false; }
JSCompartment* maybeCompartment() { return nullptr; }
};
// Constraint which triggers recompilation when a typed array's data becomes
// invalid.
class ConstraintDataFreezeObjectForTypedArrayData
@ -2004,16 +1982,6 @@ class ConstraintDataFreezeObjectForUnboxedConvertedToNative
} /* anonymous namespace */
void
TypeSet::ObjectKey::watchStateChangeForInlinedCall(CompilerConstraintList* constraints)
{
HeapTypeSetKey objectProperty = property(JSID_EMPTY);
LifoAlloc* alloc = constraints->alloc();
typedef CompilerConstraintInstance<ConstraintDataFreezeObjectForInlinedCall> T;
constraints->add(alloc->new_<T>(alloc, objectProperty, ConstraintDataFreezeObjectForInlinedCall()));
}
void
TypeSet::ObjectKey::watchStateChangeForTypedArrayData(CompilerConstraintList* constraints)
{
@ -2609,11 +2577,12 @@ TypeZone::addPendingRecompile(JSContext* cx, JSScript* script)
if (script->hasIonScript())
addPendingRecompile(cx, script->ionScript()->recompileInfo());
// When one script is inlined into another the caller listens to state
// changes on the callee's script, so trigger these to force recompilation
// of any such callers.
if (script->functionNonDelazifying() && !script->functionNonDelazifying()->hasLazyGroup())
ObjectStateChange(cx, script->functionNonDelazifying()->group(), false);
// Trigger recompilation of any callers inlining this script.
if (TypeScript* types = script->types()) {
for (RecompileInfo info : types->inlinedCompilations())
addPendingRecompile(cx, info);
types->inlinedCompilations().clearAndFree();
}
}
#ifdef JS_CRASH_DIAGNOSTICS
@ -4437,6 +4406,19 @@ JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
TypeZone& types = zone()->types;
// Sweep the inlinedCompilations Vector.
{
RecompileInfoVector& inlinedCompilations = types_->inlinedCompilations();
size_t dest = 0;
for (size_t i = 0; i < inlinedCompilations.length(); i++) {
if (inlinedCompilations[i].shouldSweep(types))
continue;
inlinedCompilations[dest] = inlinedCompilations[i];
dest++;
}
inlinedCompilations.shrinkTo(dest);
}
// Destroy all type information attached to the script if desired. We can
// only do this if nothing has been compiled for the script, which will be
// the case unless the script has been compiled since we started sweeping.
@ -4475,21 +4457,25 @@ JSScript::maybeSweepTypes(AutoClearTypeInferenceStateOnOOM* oom)
void
TypeScript::destroy()
{
js_free(this);
js_delete(this);
}
void
Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* typePool,
size_t* baselineStubsOptimized,
size_t* cachedCFG,
size_t* uniqueIdMap,
size_t* shapeTables,
size_t* atomsMarkBitmaps)
{
*typePool += types.typeLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
if (jitZone()) {
// These functions return pointers to struct that are embedded within
// JitZone, which is why we use sizeOfExcludingThis().
*baselineStubsOptimized +=
jitZone()->optimizedStubSpace()->sizeOfExcludingThis(mallocSizeOf);
*cachedCFG += jitZone()->cfgSpace()->sizeOfExcludingThis(mallocSizeOf);
}
*uniqueIdMap += uniqueIds().sizeOfExcludingThis(mallocSizeOf);
*shapeTables += baseShapes().sizeOfExcludingThis(mallocSizeOf)

View File

@ -261,7 +261,6 @@ class TypeSet
bool unknownProperties();
bool hasFlags(CompilerConstraintList* constraints, ObjectGroupFlags flags);
bool hasStableClassAndProto(CompilerConstraintList* constraints);
void watchStateChangeForInlinedCall(CompilerConstraintList* constraints);
void watchStateChangeForTypedArrayData(CompilerConstraintList* constraints);
void watchStateChangeForUnboxedConvertedToNative(CompilerConstraintList* constraints);
HeapTypeSetKey property(jsid id);
@ -1085,15 +1084,123 @@ inline bool isInlinableCall(jsbytecode* pc);
bool
ClassCanHaveExtraProperties(const Class* clasp);
/*
* Information about the result of the compilation of a script. This structure
* stored in the TypeCompartment is indexed by the RecompileInfo. This
* indirection enables the invalidation of all constraints related to the same
* compilation.
*/
class CompilerOutput
{
// If this compilation has not been invalidated, the associated script and
// kind of compilation being performed.
JSScript* script_;
// Whether this compilation is about to be invalidated.
bool pendingInvalidation_ : 1;
// During sweeping, the list of compiler outputs is compacted and invalidated
// outputs are removed. This gives the new index for a valid compiler output.
uint32_t sweepIndex_ : 31;
public:
static const uint32_t INVALID_SWEEP_INDEX = static_cast<uint32_t>(1 << 31) - 1;
CompilerOutput()
: script_(nullptr),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
explicit CompilerOutput(JSScript* script)
: script_(script),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
JSScript* script() const { return script_; }
inline jit::IonScript* ion() const;
bool isValid() const {
return script_ != nullptr;
}
void invalidate() {
script_ = nullptr;
}
void setPendingInvalidation() {
pendingInvalidation_ = true;
}
bool pendingInvalidation() {
return pendingInvalidation_;
}
void setSweepIndex(uint32_t index) {
if (index >= INVALID_SWEEP_INDEX)
MOZ_CRASH();
sweepIndex_ = index;
}
uint32_t sweepIndex() {
MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
return sweepIndex_;
}
};
class RecompileInfo
{
// Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
// depending on the generation value.
uint32_t outputIndex : 31;
// If out of sync with the TypeZone's generation, this index is for the
// zone's sweepCompilerOutputs rather than compilerOutputs.
uint32_t generation : 1;
public:
RecompileInfo(uint32_t outputIndex, uint32_t generation)
: outputIndex(outputIndex), generation(generation)
{}
RecompileInfo()
: outputIndex(JS_BITMASK(31)), generation(0)
{}
bool operator==(const RecompileInfo& other) const {
return outputIndex == other.outputIndex && generation == other.generation;
}
CompilerOutput* compilerOutput(TypeZone& types) const;
CompilerOutput* compilerOutput(JSContext* cx) const;
bool shouldSweep(TypeZone& types);
};
// The RecompileInfoVector has a MinInlineCapacity of one so that invalidating a
// single IonScript doesn't require an allocation.
typedef Vector<RecompileInfo, 1, SystemAllocPolicy> RecompileInfoVector;
/* Persistent type information for a script, retained across GCs. */
class TypeScript
{
friend class ::JSScript;
// The freeze constraints added to stack type sets will only directly
// invalidate the script containing those stack type sets. This Vector
// contains compilations that inlined this script, so we can invalidate
// them as well.
RecompileInfoVector inlinedCompilations_;
// Variable-size array
StackTypeSet typeArray_[1];
public:
RecompileInfoVector& inlinedCompilations() {
return inlinedCompilations_;
}
MOZ_MUST_USE bool addInlinedCompilation(RecompileInfo info) {
if (!inlinedCompilations_.empty() && inlinedCompilations_.back() == info)
return true;
return inlinedCompilations_.append(info);
}
/* Array of type sets for variables and JOF_TYPESET ops. */
StackTypeSet* typeArray() const {
// Ensure typeArray_ is the last data member of TypeScript.
@ -1236,95 +1343,6 @@ class HeapTypeSetKey
bool couldBeConstant(CompilerConstraintList* constraints);
};
/*
* Information about the result of the compilation of a script. This structure
* stored in the TypeCompartment is indexed by the RecompileInfo. This
* indirection enables the invalidation of all constraints related to the same
* compilation.
*/
class CompilerOutput
{
// If this compilation has not been invalidated, the associated script and
// kind of compilation being performed.
JSScript* script_;
// Whether this compilation is about to be invalidated.
bool pendingInvalidation_ : 1;
// During sweeping, the list of compiler outputs is compacted and invalidated
// outputs are removed. This gives the new index for a valid compiler output.
uint32_t sweepIndex_ : 31;
public:
static const uint32_t INVALID_SWEEP_INDEX = static_cast<uint32_t>(1 << 31) - 1;
CompilerOutput()
: script_(nullptr),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
explicit CompilerOutput(JSScript* script)
: script_(script),
pendingInvalidation_(false), sweepIndex_(INVALID_SWEEP_INDEX)
{}
JSScript* script() const { return script_; }
inline jit::IonScript* ion() const;
bool isValid() const {
return script_ != nullptr;
}
void invalidate() {
script_ = nullptr;
}
void setPendingInvalidation() {
pendingInvalidation_ = true;
}
bool pendingInvalidation() {
return pendingInvalidation_;
}
void setSweepIndex(uint32_t index) {
if (index >= INVALID_SWEEP_INDEX)
MOZ_CRASH();
sweepIndex_ = index;
}
uint32_t sweepIndex() {
MOZ_ASSERT(sweepIndex_ != INVALID_SWEEP_INDEX);
return sweepIndex_;
}
};
class RecompileInfo
{
// Index in the TypeZone's compilerOutputs or sweepCompilerOutputs arrays,
// depending on the generation value.
uint32_t outputIndex : 31;
// If out of sync with the TypeZone's generation, this index is for the
// zone's sweepCompilerOutputs rather than compilerOutputs.
uint32_t generation : 1;
public:
RecompileInfo(uint32_t outputIndex, uint32_t generation)
: outputIndex(outputIndex), generation(generation)
{}
RecompileInfo()
: outputIndex(JS_BITMASK(31)), generation(0)
{}
CompilerOutput* compilerOutput(TypeZone& types) const;
CompilerOutput* compilerOutput(JSContext* cx) const;
bool shouldSweep(TypeZone& types);
};
// The RecompileInfoVector has a MinInlineCapacity of one so that invalidating a
// single IonScript doesn't require an allocation.
typedef Vector<RecompileInfo, 1, SystemAllocPolicy> RecompileInfoVector;
struct AutoEnterAnalysis;
class TypeZone

View File

@ -24,12 +24,16 @@
namespace js {
namespace frontend {
template <typename ParseHandler> class Parser;
class ParseContext;
class FullParseHandler;
class ParseNode;
class ParseContext;
class ParseNode;
template <template <typename CharT> class ParseHandler, typename CharT> class Parser;
template <typename CharT> class FullParseHandler;
}
typedef frontend::Parser<frontend::FullParseHandler> AsmJSParser;
using AsmJSParser = frontend::Parser<frontend::FullParseHandler, char16_t>;
// This function takes over parsing of a function starting with "use asm". The
// return value indicates whether an error was reported which the caller should

View File

@ -194,9 +194,6 @@ template<> struct RegTypeOf<MIRType::Double> {
static constexpr RegTypeName value = RegTypeName::Float64;
};
static constexpr int32_t TlsSlotSize = sizeof(void*);
static constexpr int32_t TlsSlotOffset = TlsSlotSize;
BaseLocalIter::BaseLocalIter(const ValTypeVector& locals,
size_t argsLength,
bool debugEnabled)

View File

@ -1537,6 +1537,10 @@ ReportZoneStats(const JS::ZoneStats& zStats,
zStats.baselineStubsOptimized,
"The Baseline JIT's optimized IC stubs (excluding code).");
ZCREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("jit-cached-cfg"),
zStats.cachedCFG,
"The cached CFG to construct Ion code out of it.");
size_t stringsNotableAboutMemoryGCHeap = 0;
size_t stringsNotableAboutMemoryMallocHeap = 0;
@ -2463,6 +2467,8 @@ JSReporter::CollectReports(WindowPaths* windowPaths,
return;
}
JS::CollectTraceLoggerStateStats(&rtStats);
size_t xpcJSRuntimeSize = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
size_t wrappedJSSize = xpcrt->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
@ -2499,6 +2505,11 @@ JSReporter::CollectReports(WindowPaths* windowPaths,
KIND_OTHER, rtTotal,
"The sum of all measurements under 'explicit/js-non-window/runtime/'.");
// Report the numbers for memory used by tracelogger.
REPORT_BYTES(NS_LITERAL_CSTRING("tracelogger"),
KIND_OTHER, rtStats.runtime.tracelogger,
"The memory used for the tracelogger, including the graph and events.");
// Report the numbers for memory outside of compartments.
REPORT_BYTES(NS_LITERAL_CSTRING("js-main-runtime/gc-heap/unused-chunks"),

View File

@ -1082,6 +1082,12 @@ nsDocumentViewer::LoadComplete(nsresult aStatus)
}
}
// Release the JS bytecode cache from its wait on the load event, and
// potentially dispatch the encoding of the bytecode.
if (mDocument && mDocument->ScriptLoader()) {
mDocument->ScriptLoader()->LoadEventFired();
}
nsJSContext::LoadEnd();
// It's probably a good idea to GC soon since we have finished loading.

View File

@ -1951,7 +1951,7 @@ FrameLayerBuilder::RemoveFrameFromLayerManager(const nsIFrame* aFrame,
if (t) {
PaintedDisplayItemLayerUserData* paintedData =
static_cast<PaintedDisplayItemLayerUserData*>(t->GetUserData(&gPaintedDisplayItemLayerUserData));
if (paintedData) {
if (paintedData && data->mGeometry) {
nsRegion old = data->mGeometry->ComputeInvalidationRegion();
nsIntRegion rgn = old.ScaleToOutsidePixels(paintedData->mXScale, paintedData->mYScale, paintedData->mAppUnitsPerDevPixel);
rgn.MoveBy(-GetTranslationForPaintedLayer(t));
@ -1992,6 +1992,7 @@ FrameLayerBuilder::StoreOptimizedLayerForFrame(nsDisplayItem* aItem, Layer* aLay
DisplayItemData* data = GetDisplayItemDataForManager(aItem, aLayer->Manager());
NS_ASSERTION(data, "Must have already stored data for this item!");
data->mOptLayer = aLayer;
data->mItem = nullptr;
}
void
@ -3143,7 +3144,6 @@ ContainerState::PrepareImageLayer(PaintedLayerData* aData)
imageLayer->SetClipRect(Nothing());
}
mLayerBuilder->StoreOptimizedLayerForFrame(aData->mImage, imageLayer);
FLB_LOG_PAINTED_LAYER_DECISION(aData,
" Selected image layer=%p\n", imageLayer.get());
@ -3248,6 +3248,10 @@ void ContainerState::FinishPaintedLayerData(PaintedLayerData& aData, FindOpaqueB
data->mLayer->SetVisibleRegion(LayerIntRegion());
data->mLayer->InvalidateRegion(data->mLayer->GetValidRegion().GetBounds());
data->mLayer->SetEventRegions(EventRegions());
for (auto& item : data->mAssignedDisplayItems) {
mLayerBuilder->StoreOptimizedLayerForFrame(item.mItem, layer);
}
}
}
@ -4541,7 +4545,9 @@ FrameLayerBuilder::ComputeGeometryChangeForItem(DisplayItemData* aData)
{
nsDisplayItem *item = aData->mItem;
PaintedLayer* paintedLayer = aData->mLayer->AsPaintedLayer();
if (!item || !paintedLayer) {
// If aData->mOptLayer is presence, means this item has been optimized to the separate
// layer. Thus, skip geometry change calculation.
if (aData->mOptLayer || !item || !paintedLayer) {
aData->EndUpdate();
return;
}

View File

@ -5,7 +5,7 @@
== 540247-1.xul 540247-1.xul
fails == 543681-1.html 543681-1.html
fails-if(stylo&&!browserIsRemote) == 1243409-1.html 1243409-1.html
== test-image-layers.html test-image-layers.html
skip-if(stylo) == test-image-layers.html test-image-layers.html
== test-image-layers-multiple-displayitem.html test-image-layers-multiple-displayitem.html
pref(layout.animated-image-layers.enabled,true) skip-if(Android||gtkWidget) == test-animated-image-layers.html test-animated-image-layers.html
skip-if(stylo) == test-animated-image-layers-background.html test-animated-image-layers-background.html

View File

@ -578,8 +578,10 @@ WebrtcVideoConduit::ConfigureSendMediaCodec(const VideoCodecConfig* codecConfig)
video_stream.height = height >> idx;
video_stream.max_framerate = mSendingFramerate;
auto& simulcastEncoding = codecConfig->mSimulcastEncodings[idx];
// leave vector temporal_layer_thresholds_bps empty
video_stream.temporal_layer_thresholds_bps.clear();
// The underlying code (as of 49 and 57) actually ignores the values in
// the array, and uses the size of the array + 1. Chrome uses 3 for
// temporal layers when simulcast is in use (see simulcast.cc)
video_stream.temporal_layer_thresholds_bps.resize(streamCount > 1 ? 3 : 1);
// Calculate these first
video_stream.max_bitrate_bps = MinIgnoreZero(simulcastEncoding.constraints.maxBr,
kDefaultMaxBitrate_bps);

View File

@ -23,7 +23,6 @@
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/Preferences.h"
#include "mozilla/Telemetry.h"
#include "nsITabChild.h"
#include "private/pprio.h"
#include "nsInputStreamPump.h"
@ -805,12 +804,6 @@ nsJARChannel::AsyncOpen(nsIStreamListener *listener, nsISupports *ctx)
return NS_ERROR_UNSAFE_CONTENT_TYPE;
}
static bool reportedRemoteJAR = false;
if (!reportedRemoteJAR) {
reportedRemoteJAR = true;
Telemetry::Accumulate(Telemetry::REMOTE_JAR_PROTOCOL_USED, 1);
}
// kick off an async download of the base URI...
nsCOMPtr<nsIStreamListener> downloader = new MemoryDownloader(this);
uint32_t loadFlags =

View File

@ -2752,11 +2752,7 @@ pref("layout.css.text-justify.enabled", true);
// Is support for CSS "float: inline-{start,end}" and
// "clear: inline-{start,end}" enabled?
#if defined(MOZ_B2G) || !defined(RELEASE_OR_BETA)
pref("layout.css.float-logical-values.enabled", true);
#else
pref("layout.css.float-logical-values.enabled", false);
#endif
// Is support for the CSS4 image-orientation property enabled?
pref("layout.css.image-orientation.enabled", true);

View File

@ -283,7 +283,7 @@ nsSegmentEncoder::InitUnicodeEncoder()
//----------------------------------------------------------------------------
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
static PRCList gAllURLs;
static LinkedList<nsStandardURL> gAllURLs;
#endif
nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL)
@ -307,12 +307,9 @@ nsStandardURL::nsStandardURL(bool aSupportsFileURL, bool aTrackURL)
mParser = net_GetStdURLParser();
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
memset(&mDebugCList, 0, sizeof(mDebugCList));
if (NS_IsMainThread()) {
if (aTrackURL) {
PR_APPEND_LINK(&mDebugCList, &gAllURLs);
} else {
PR_INIT_CLIST(&mDebugCList);
gAllURLs.insertBack(this);
}
}
#endif
@ -331,13 +328,6 @@ nsStandardURL::~nsStandardURL()
if (mHostA) {
free(mHostA);
}
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
if (NS_IsMainThread()) {
if (!PR_CLIST_IS_EMPTY(&mDebugCList)) {
PR_REMOVE_LINK(&mDebugCList);
}
}
#endif
}
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
@ -349,12 +339,12 @@ struct DumpLeakedURLs {
DumpLeakedURLs::~DumpLeakedURLs()
{
MOZ_ASSERT(NS_IsMainThread());
if (!PR_CLIST_IS_EMPTY(&gAllURLs)) {
if (!gAllURLs.isEmpty()) {
printf("Leaked URLs:\n");
for (PRCList *l = PR_LIST_HEAD(&gAllURLs); l != &gAllURLs; l = PR_NEXT_LINK(l)) {
nsStandardURL *url = reinterpret_cast<nsStandardURL*>(reinterpret_cast<char*>(l) - offsetof(nsStandardURL, mDebugCList));
for (auto url : gAllURLs) {
url->PrintSpec();
}
gAllURLs.clear();
}
}
#endif
@ -370,10 +360,6 @@ nsStandardURL::InitGlobalObjects()
#endif
PrefsChanged(prefBranch, nullptr);
}
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
PR_INIT_CLIST(&gAllURLs);
#endif
}
void

View File

@ -16,8 +16,8 @@
#include "nsURLHelper.h"
#include "nsIClassInfo.h"
#include "nsISizeOf.h"
#include "prclist.h"
#include "mozilla/Attributes.h"
#include "mozilla/LinkedList.h"
#include "mozilla/MemoryReporting.h"
#include "nsIIPCSerializableURI.h"
#include "nsISensitiveInfoHiddenURI.h"
@ -48,6 +48,9 @@ class nsStandardURL : public nsIFileURL
, public nsISizeOf
, public nsIIPCSerializableURI
, public nsISensitiveInfoHiddenURI
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
, public LinkedListElement<nsStandardURL>
#endif
{
protected:
virtual ~nsStandardURL();
@ -304,7 +307,6 @@ private:
public:
#ifdef DEBUG_DUMP_URLS_AT_SHUTDOWN
PRCList mDebugCList;
void PrintSpec() const { printf(" %s\n", mSpec.get()); }
#endif

View File

@ -36,6 +36,7 @@ import zipfile
import httplib
import urlparse
import hashlib
import zlib
if os.name == 'nt':
try:
import win32file
@ -58,7 +59,7 @@ from mozharness.base.log import SimpleFileLogger, MultiFileLogger, \
LogMixin, OutputParser, DEBUG, INFO, ERROR, FATAL
class FetchedIncorrectFilesize(Exception):
class ContentLengthMismatch(Exception):
pass
@ -355,8 +356,8 @@ class ScriptMixin(PlatformMixin):
Raises:
IOError: When the url points to a file on disk and cannot be found
FetchedIncorrectFilesize: When the size of the fetched file does not match the
expected file size.
ContentLengthMismatch: When the length of the retrieved content does not match the
Content-Length response header.
ValueError: When the scheme of a url is not what is expected.
Returns:
@ -369,7 +370,7 @@ class ScriptMixin(PlatformMixin):
if not os.path.isfile(url):
raise IOError('Could not find file to extract: {}'.format(url))
expected_file_size = os.stat(url.replace('file://', '')).st_size
content_length = os.stat(url.replace('file://', '')).st_size
# In case we're referrencing a file without file://
if parsed_url.scheme == '':
@ -392,26 +393,39 @@ class ScriptMixin(PlatformMixin):
response = urllib2.urlopen(request, timeout=30)
if parsed_url.scheme in ('http', 'https'):
expected_file_size = int(response.headers.get('Content-Length'))
content_length = int(response.headers.get('Content-Length'))
file_contents = response.read()
obtained_file_size = len(file_contents)
self.info('Expected file size: {}'.format(expected_file_size))
self.info('Obtained file size: {}'.format(obtained_file_size))
response_body = response.read()
response_body_size = len(response_body)
if obtained_file_size != expected_file_size:
raise FetchedIncorrectFilesize(
'The expected file size is {} while we got instead {}'.format(
expected_file_size, obtained_file_size)
self.info('Content-Length response header: {}'.format(content_length))
self.info('Bytes received: {}'.format(response_body_size))
if response_body_size != content_length:
raise ContentLengthMismatch(
'The retrieved Content-Length header declares a body length of {} bytes, while we actually retrieved {} bytes'.format(
content_length, response_body_size)
)
if response.info().get('Content-Encoding') == 'gzip':
self.info('Content-Encoding is "gzip", so decompressing response body')
# See http://www.zlib.net/manual.html#Advanced
# section "ZEXTERN int ZEXPORT inflateInit2 OF....":
# Add 32 to windowBits to enable zlib and gzip decoding with automatic
# header detection, or add 16 to decode only the gzip format (the zlib
# format will return a Z_DATA_ERROR).
# Adding 16 since we only wish to support gzip encoding.
file_contents = zlib.decompress(response_body, zlib.MAX_WBITS|16)
else:
file_contents = response_body
# Use BytesIO instead of StringIO
# http://stackoverflow.com/questions/34162017/unzip-buffer-with-python/34162395#34162395
return BytesIO(file_contents)
def _download_file(self, url, file_name):
""" Helper script for download_file()
""" Helper function for download_file()
Additionaly this function logs all exceptions as warnings before
re-raising them
@ -443,7 +457,14 @@ class ScriptMixin(PlatformMixin):
if f.info().get('content-length') is not None:
f_length = int(f.info()['content-length'])
got_length = 0
local_file = open(file_name, 'wb')
if f.info().get('Content-Encoding') == 'gzip':
# Note, we'll download the full compressed content into its own
# file, since that allows the gzip library to seek through it.
# Once downloaded, we'll decompress it into the real target
# file, and delete the compressed version.
local_file = open(file_name + '.gz', 'wb')
else:
local_file = open(file_name, 'wb')
while True:
block = f.read(1024 ** 2)
if not block:
@ -454,6 +475,18 @@ class ScriptMixin(PlatformMixin):
if f_length is not None:
got_length += len(block)
local_file.close()
if f.info().get('Content-Encoding') == 'gzip':
# Decompress file into target location, then remove compressed version
with open(file_name, 'wb') as f_out:
# On some execution paths, this could be called with python 2.6
# whereby gzip.open(...) cannot be used with a 'with' statement.
# So let's do this the python 2.6 way...
try:
f_in = gzip.open(file_name + '.gz', 'rb')
shutil.copyfileobj(f_in, f_out)
finally:
f_in.close()
os.remove(file_name + '.gz')
return file_name
except urllib2.HTTPError, e:
self.warning("Server returned status %s %s for %s" % (str(e.code), str(e), url))
@ -666,7 +699,7 @@ class ScriptMixin(PlatformMixin):
httplib.BadStatusLine,
socket.timeout,
socket.error,
FetchedIncorrectFilesize,
ContentLengthMismatch,
),
sleeptime=30,
attempts=5,

View File

@ -10729,13 +10729,6 @@
"bug_numbers": [1238433],
"description": "Percentage of frames decoded frames dropped in an HTMLVideoElement"
},
"REMOTE_JAR_PROTOCOL_USED": {
"alert_emails": ["dev-platform@lists.mozilla.org"],
"expires_in_version": "55",
"bug_numbers": [1255934],
"kind": "count",
"description": "Remote JAR protocol usage"
},
"MEDIA_DECODER_BACKEND_USED": {
"alert_emails": ["danderson@mozilla.com"],
"bug_numbers": [1259695],

View File

@ -2004,7 +2004,6 @@
"PWMGR_MANAGE_DELETED_ALL",
"PWMGR_MANAGE_SORTED",
"REJECTED_MESSAGE_MANAGER_MESSAGE",
"REMOTE_JAR_PROTOCOL_USED",
"REQUESTS_OF_ORIGINAL_CONTENT",
"SANDBOX_REJECTED_SYSCALLS",
"SEARCH_COUNTS",

View File

@ -302,20 +302,24 @@ var snapshotFormatters = {
delete data.info;
}
if (AppConstants.NIGHTLY_BUILD || AppConstants.MOZ_DEV_EDITION) {
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let gpuProcessPid = windowUtils.gpuProcessPid;
let windowUtils = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let gpuProcessPid = windowUtils.gpuProcessPid;
if (gpuProcessPid != -1) {
let gpuProcessKillButton = $.new("button");
if (gpuProcessPid != -1) {
let gpuProcessKillButton = null;
if (AppConstants.NIGHTLY_BUILD || AppConstants.MOZ_DEV_EDITION) {
gpuProcessKillButton = $.new("button");
gpuProcessKillButton.addEventListener("click", function() {
windowUtils.terminateGPUProcess();
});
gpuProcessKillButton.textContent = strings.GetStringFromName("gpuProcessKillButton");
addRow("diagnostics", "GPUProcessPid", gpuProcessPid);
}
addRow("diagnostics", "GPUProcessPid", gpuProcessPid);
if (gpuProcessKillButton) {
addRow("diagnostics", "GPUProcess", [gpuProcessKillButton]);
}
}

View File

@ -670,6 +670,11 @@ with modules["DOM"]:
errors["NS_ERROR_DOM_INVALID_STATE_XHR_CHUNKED_RESPONSETYPES_UNSUPPORTED_FOR_SYNC"] = FAILURE(1024)
errors["NS_ERROR_DOM_INVALID_ACCESS_XHR_TIMEOUT_AND_RESPONSETYPE_UNSUPPORTED_FOR_SYNC"] = FAILURE(1025)
# When manipulating the bytecode cache with the JS API, some transcoding
# errors, such as a different bytecode format can cause failures of the
# decoding process.
errors["NS_ERROR_DOM_JS_DECODING_ERROR"] = FAILURE(1026)
# May be used to indicate when e.g. setting a property value didn't
# actually change the value, like for obj.foo = "bar"; obj.foo = "bar";
# the second assignment throws NS_SUCCESS_DOM_NO_OPERATION.