mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Merge m-c to autoland, a=merge
MozReview-Commit-ID: FvBazP0CwKm
This commit is contained in:
commit
c296c00a82
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
10
dom/base/test/file_js_cache.html
Normal file
10
dom/base/test/file_js_cache.html
Normal 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>
|
5
dom/base/test/file_js_cache.js
Normal file
5
dom/base/test/file_js_cache.js
Normal file
@ -0,0 +1,5 @@
|
||||
function baz() {}
|
||||
function bar() {}
|
||||
function foo() { bar() }
|
||||
foo();
|
||||
|
10
dom/base/test/file_js_cache_save_after_load.html
Normal file
10
dom/base/test/file_js_cache_save_after_load.html
Normal 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>
|
15
dom/base/test/file_js_cache_save_after_load.js
Normal file
15
dom/base/test/file_js_cache_save_after_load.js
Normal 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);
|
||||
});
|
12
dom/base/test/file_js_cache_with_sri.html
Normal file
12
dom/base/test/file_js_cache_with_sri.html
Normal 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>
|
@ -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]
|
||||
|
195
dom/base/test/test_script_loader_js_cache.html
Normal file
195
dom/base/test/test_script_loader_js_cache.html
Normal 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>
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
|
@ -197,7 +197,6 @@ namespace JS {
|
||||
_(CantInlineTooManyArgs) \
|
||||
_(CantInlineNeedsArgsObj) \
|
||||
_(CantInlineDebuggee) \
|
||||
_(CantInlineUnknownProps) \
|
||||
_(CantInlineExceededDepth) \
|
||||
_(CantInlineExceededTotalBytecodeLength) \
|
||||
_(CantInlineBigCaller) \
|
||||
|
@ -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
63
js/src/ds/Nestable.h
Normal 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 */
|
@ -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;
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
@ -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);
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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())
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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"),
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 =
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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],
|
||||
|
@ -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",
|
||||
|
@ -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]);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user