merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2017-02-01 13:14:23 +01:00
commit 005011be4c
119 changed files with 2666 additions and 930 deletions

View File

@ -718,8 +718,8 @@ var WebBrowserChrome = {
},
// Try to reload the currently active or currently loading page in a new process.
reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal) {
E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true);
reloadInFreshProcess(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aLoadFlags) {
E10SUtils.redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, true, aLoadFlags);
return true;
},

View File

@ -159,10 +159,12 @@ this.E10SUtils = {
if (aDocShell.QueryInterface(Ci.nsIDocShellTreeItem).sameTypeParent)
return true;
// If we are in a fresh process, and it wouldn't be content visible to
// change processes, we want to load into a new process so that we can throw
// If we are in a Large-Allocation process, and it wouldn't be content visible
// to change processes, we want to load into a new process so that we can throw
// this one out.
if (aDocShell.inFreshProcess && aDocShell.isOnlyToplevelInTabGroup) {
if (aDocShell.inLargeAllocProcess &&
!aDocShell.awaitingLargeAlloc &&
aDocShell.isOnlyToplevelInTabGroup) {
return false;
}
@ -170,7 +172,7 @@ this.E10SUtils = {
return this.shouldLoadURIInThisProcess(aURI);
},
redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aFreshProcess) {
redirectLoad(aDocShell, aURI, aReferrer, aTriggeringPrincipal, aFreshProcess, aFlags) {
// Retarget the load to the correct process
let messageManager = aDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIContentFrameMessageManager);
@ -179,7 +181,7 @@ this.E10SUtils = {
messageManager.sendAsyncMessage("Browser:LoadURI", {
loadOptions: {
uri: aURI.spec,
flags: Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
flags: aFlags || Ci.nsIWebNavigation.LOAD_FLAGS_NONE,
referrer: aReferrer ? aReferrer.spec : null,
triggeringPrincipal: aTriggeringPrincipal
? Utils.serializePrincipal(aTriggeringPrincipal)

View File

@ -14854,8 +14854,22 @@ nsDocShell::GetIsOnlyToplevelInTabGroup(bool* aResult)
}
NS_IMETHODIMP
nsDocShell::GetInFreshProcess(bool* aResult)
nsDocShell::GetAwaitingLargeAlloc(bool* aResult)
{
*aResult = TabChild::GetWasFreshProcess();
MOZ_ASSERT(aResult);
nsCOMPtr<nsITabChild> tabChild = GetTabChild();
if (!tabChild) {
*aResult = false;
return NS_OK;
}
*aResult = static_cast<TabChild*>(tabChild.get())->IsAwaitingLargeAlloc();
return NS_OK;
}
NS_IMETHODIMP
nsDocShell::GetInLargeAllocProcess(bool* aResult)
{
MOZ_ASSERT(aResult);
*aResult = TabChild::InLargeAllocProcess();
return NS_OK;
}

View File

@ -1127,7 +1127,14 @@ interface nsIDocShell : nsIDocShellTreeItem
[infallible] readonly attribute boolean isOnlyToplevelInTabGroup;
/**
* Returns `true` if this docshell was created by a Large-Allocation load.
* Returns `true` if this docshell is loaded within a Large-Allocation
* process.
*/
[infallible] readonly attribute boolean inFreshProcess;
[infallible] readonly attribute boolean inLargeAllocProcess;
/**
* Returns `true` if this docshell was created due to a Large-Allocation
* header, and has not seen the initiating load yet.
*/
[infallible] readonly attribute boolean awaitingLargeAlloc;
};

View File

@ -82,7 +82,7 @@ enum DOM4ErrorTypeCodeMap {
#define DOM4_MSG_DEF(name, message, nsresult) {(nsresult), name, #name, message},
#define DOM_MSG_DEF(val, message) {(val), NS_ERROR_GET_CODE(val), #val, message},
static const struct ResultStruct
static constexpr struct ResultStruct
{
nsresult mNSResult;
uint16_t mCode;

View File

@ -9709,12 +9709,43 @@ nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel)
TabChild* tabChild = TabChild::GetFrom(outer->AsOuter());
NS_ENSURE_TRUE(tabChild, false);
if (tabChild->TakeIsFreshProcess()) {
NS_WARNING("Already in a fresh process, ignoring Large-Allocation header!");
if (tabChild->TakeAwaitingLargeAlloc()) {
NS_WARNING("In a Large-Allocation TabChild, ignoring Large-Allocation header!");
outer->SetLargeAllocStatus(LargeAllocStatus::SUCCESS);
return false;
}
// On Win32 systems, we want to behave differently, so set the isWin32 bool to
// be true iff we are on win32.
#if defined(XP_WIN) && defined(_X86_)
const bool isWin32 = true;
#else
const bool isWin32 = false;
#endif
static bool sLargeAllocForceEnable = false;
static bool sCachedLargeAllocForceEnable = false;
if (!sCachedLargeAllocForceEnable) {
sCachedLargeAllocForceEnable = true;
mozilla::Preferences::AddBoolVarCache(&sLargeAllocForceEnable,
"dom.largeAllocation.forceEnable");
}
// We want to enable the large allocation header on 32-bit windows machines,
// and disable it on other machines, while still printing diagnostic messages.
// dom.largeAllocation.forceEnable can allow you to enable the process
// switching behavior of the Large-Allocation header on non 32-bit windows
// machines.
bool largeAllocEnabled = isWin32 || sLargeAllocForceEnable;
if (!largeAllocEnabled) {
NS_WARNING("dom.largeAllocation.forceEnable not set - "
"ignoring otherwise successful Large-Allocation header.");
// On platforms which aren't WIN32, we don't activate the largeAllocation
// header, instead we simply emit diagnostics into the console.
outer->SetLargeAllocStatus(LargeAllocStatus::NON_WIN32);
return false;
}
// At this point the fress process load should succeed! We just need to get
// ourselves a nsIWebBrowserChrome3 to ask to perform the reload. We should
// have one, as we have already confirmed that we are running in a content
@ -9738,10 +9769,25 @@ nsContentUtils::AttemptLargeAllocationLoad(nsIHttpChannel* aChannel)
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
// Get the channel's load flags, and use them to generate nsIWebNavigation
// load flags. We want to make sure to propagate the refresh and cache busting
// flags.
nsLoadFlags channelLoadFlags;
aChannel->GetLoadFlags(&channelLoadFlags);
uint32_t webnavLoadFlags = nsIWebNavigation::LOAD_FLAGS_NONE;
if (channelLoadFlags & nsIRequest::LOAD_BYPASS_CACHE) {
webnavLoadFlags |= nsIWebNavigation::LOAD_FLAGS_BYPASS_CACHE;
webnavLoadFlags |= nsIWebNavigation::LOAD_FLAGS_BYPASS_PROXY;
} else if (channelLoadFlags & nsIRequest::VALIDATE_ALWAYS) {
webnavLoadFlags |= nsIWebNavigation::LOAD_FLAGS_IS_REFRESH;
}
// Actually perform the cross process load
bool reloadSucceeded = false;
rv = wbc3->ReloadInFreshProcess(docShell, uri, referrer,
triggeringPrincipal, &reloadSucceeded);
triggeringPrincipal, webnavLoadFlags,
&reloadSucceeded);
NS_ENSURE_SUCCESS(rv, false);
return reloadSucceeded;

View File

@ -13928,6 +13928,10 @@ nsGlobalWindow::ReportLargeAllocStatus()
errorFlags = nsIScriptError::infoFlag;
message = "LargeAllocationSuccess";
break;
case LargeAllocStatus::NON_WIN32:
errorFlags = nsIScriptError::infoFlag;
message = "LargeAllocationNonWin32";
break;
case LargeAllocStatus::NON_GET:
message = "LargeAllocationNonGetRequest";
break;

View File

@ -42,72 +42,72 @@
using namespace mozilla;
const char * const sSelectAllString = "cmd_selectAll";
const char * const sSelectNoneString = "cmd_selectNone";
const char * const sCopyImageLocationString = "cmd_copyImageLocation";
const char * const sCopyImageContentsString = "cmd_copyImageContents";
const char * const sCopyImageString = "cmd_copyImage";
constexpr const char * sSelectAllString = "cmd_selectAll";
constexpr const char * sSelectNoneString = "cmd_selectNone";
constexpr const char * sCopyImageLocationString = "cmd_copyImageLocation";
constexpr const char * sCopyImageContentsString = "cmd_copyImageContents";
constexpr const char * sCopyImageString = "cmd_copyImage";
const char * const sScrollTopString = "cmd_scrollTop";
const char * const sScrollBottomString = "cmd_scrollBottom";
const char * const sScrollPageUpString = "cmd_scrollPageUp";
const char * const sScrollPageDownString = "cmd_scrollPageDown";
const char * const sScrollLineUpString = "cmd_scrollLineUp";
const char * const sScrollLineDownString = "cmd_scrollLineDown";
const char * const sScrollLeftString = "cmd_scrollLeft";
const char * const sScrollRightString = "cmd_scrollRight";
const char * const sMoveTopString = "cmd_moveTop";
const char * const sMoveBottomString = "cmd_moveBottom";
const char * const sMovePageUpString = "cmd_movePageUp";
const char * const sMovePageDownString = "cmd_movePageDown";
const char * const sLinePreviousString = "cmd_linePrevious";
const char * const sLineNextString = "cmd_lineNext";
const char * const sCharPreviousString = "cmd_charPrevious";
const char * const sCharNextString = "cmd_charNext";
constexpr const char * sScrollTopString = "cmd_scrollTop";
constexpr const char * sScrollBottomString = "cmd_scrollBottom";
constexpr const char * sScrollPageUpString = "cmd_scrollPageUp";
constexpr const char * sScrollPageDownString = "cmd_scrollPageDown";
constexpr const char * sScrollLineUpString = "cmd_scrollLineUp";
constexpr const char * sScrollLineDownString = "cmd_scrollLineDown";
constexpr const char * sScrollLeftString = "cmd_scrollLeft";
constexpr const char * sScrollRightString = "cmd_scrollRight";
constexpr const char * sMoveTopString = "cmd_moveTop";
constexpr const char * sMoveBottomString = "cmd_moveBottom";
constexpr const char * sMovePageUpString = "cmd_movePageUp";
constexpr const char * sMovePageDownString = "cmd_movePageDown";
constexpr const char * sLinePreviousString = "cmd_linePrevious";
constexpr const char * sLineNextString = "cmd_lineNext";
constexpr const char * sCharPreviousString = "cmd_charPrevious";
constexpr const char * sCharNextString = "cmd_charNext";
// These are so the browser can use editor navigation key bindings
// helps with accessibility (boolean pref accessibility.browsewithcaret)
const char * const sSelectCharPreviousString = "cmd_selectCharPrevious";
const char * const sSelectCharNextString = "cmd_selectCharNext";
constexpr const char * sSelectCharPreviousString = "cmd_selectCharPrevious";
constexpr const char * sSelectCharNextString = "cmd_selectCharNext";
const char * const sWordPreviousString = "cmd_wordPrevious";
const char * const sWordNextString = "cmd_wordNext";
const char * const sSelectWordPreviousString = "cmd_selectWordPrevious";
const char * const sSelectWordNextString = "cmd_selectWordNext";
constexpr const char * sWordPreviousString = "cmd_wordPrevious";
constexpr const char * sWordNextString = "cmd_wordNext";
constexpr const char * sSelectWordPreviousString = "cmd_selectWordPrevious";
constexpr const char * sSelectWordNextString = "cmd_selectWordNext";
const char * const sBeginLineString = "cmd_beginLine";
const char * const sEndLineString = "cmd_endLine";
const char * const sSelectBeginLineString = "cmd_selectBeginLine";
const char * const sSelectEndLineString = "cmd_selectEndLine";
constexpr const char * sBeginLineString = "cmd_beginLine";
constexpr const char * sEndLineString = "cmd_endLine";
constexpr const char * sSelectBeginLineString = "cmd_selectBeginLine";
constexpr const char * sSelectEndLineString = "cmd_selectEndLine";
const char * const sSelectLinePreviousString = "cmd_selectLinePrevious";
const char * const sSelectLineNextString = "cmd_selectLineNext";
constexpr const char * sSelectLinePreviousString = "cmd_selectLinePrevious";
constexpr const char * sSelectLineNextString = "cmd_selectLineNext";
const char * const sSelectPageUpString = "cmd_selectPageUp";
const char * const sSelectPageDownString = "cmd_selectPageDown";
constexpr const char * sSelectPageUpString = "cmd_selectPageUp";
constexpr const char * sSelectPageDownString = "cmd_selectPageDown";
const char * const sSelectTopString = "cmd_selectTop";
const char * const sSelectBottomString = "cmd_selectBottom";
constexpr const char * sSelectTopString = "cmd_selectTop";
constexpr const char * sSelectBottomString = "cmd_selectBottom";
// Physical-direction movement and selection commands
const char * const sMoveLeftString = "cmd_moveLeft";
const char * const sMoveRightString = "cmd_moveRight";
const char * const sMoveUpString = "cmd_moveUp";
const char * const sMoveDownString = "cmd_moveDown";
const char * const sMoveLeft2String = "cmd_moveLeft2";
const char * const sMoveRight2String = "cmd_moveRight2";
const char * const sMoveUp2String = "cmd_moveUp2";
const char * const sMoveDown2String = "cmd_moveDown2";
constexpr const char * sMoveLeftString = "cmd_moveLeft";
constexpr const char * sMoveRightString = "cmd_moveRight";
constexpr const char * sMoveUpString = "cmd_moveUp";
constexpr const char * sMoveDownString = "cmd_moveDown";
constexpr const char * sMoveLeft2String = "cmd_moveLeft2";
constexpr const char * sMoveRight2String = "cmd_moveRight2";
constexpr const char * sMoveUp2String = "cmd_moveUp2";
constexpr const char * sMoveDown2String = "cmd_moveDown2";
const char * const sSelectLeftString = "cmd_selectLeft";
const char * const sSelectRightString = "cmd_selectRight";
const char * const sSelectUpString = "cmd_selectUp";
const char * const sSelectDownString = "cmd_selectDown";
const char * const sSelectLeft2String = "cmd_selectLeft2";
const char * const sSelectRight2String = "cmd_selectRight2";
const char * const sSelectUp2String = "cmd_selectUp2";
const char * const sSelectDown2String = "cmd_selectDown2";
constexpr const char * sSelectLeftString = "cmd_selectLeft";
constexpr const char * sSelectRightString = "cmd_selectRight";
constexpr const char * sSelectUpString = "cmd_selectUp";
constexpr const char * sSelectDownString = "cmd_selectDown";
constexpr const char * sSelectLeft2String = "cmd_selectLeft2";
constexpr const char * sSelectRight2String = "cmd_selectRight2";
constexpr const char * sSelectUp2String = "cmd_selectUp2";
constexpr const char * sSelectDown2String = "cmd_selectDown2";
#if 0
#pragma mark -
@ -269,7 +269,7 @@ IsCaretOnInWindow(nsPIDOMWindowOuter* aWindow, nsISelectionController* aSelCont)
return caretOn;
}
static const struct BrowseCommand {
static constexpr struct BrowseCommand {
const char *reverse, *forward;
nsresult (NS_STDCALL nsISelectionController::*scroll)(bool);
nsresult (NS_STDCALL nsISelectionController::*move)(bool, bool);

View File

@ -107,7 +107,8 @@ enum class LargeAllocStatus : uint8_t
// to it.
NON_GET,
NON_E10S,
NOT_ONLY_TOPLEVEL_IN_TABGROUP
NOT_ONLY_TOPLEVEL_IN_TABGROUP,
NON_WIN32
};
} // namespace dom
} // namespace mozilla

View File

@ -65,6 +65,17 @@ using namespace mozilla::dom;
using JS::SourceBufferHolder;
static LazyLogModule gCspPRLog("CSP");
static LazyLogModule gScriptLoaderLog("ScriptLoader");
#define LOG_VERBOSE(args) \
MOZ_LOG(gScriptLoaderLog, mozilla::LogLevel::Verbose, args)
#define LOG(args) \
MOZ_LOG(gScriptLoaderLog, mozilla::LogLevel::Debug, args)
#define LOG_WARN(args) \
MOZ_LOG(gScriptLoaderLog, mozilla::LogLevel::Warning, args)
#define LOG_ERROR(args) \
MOZ_LOG(gScriptLoaderLog, mozilla::LogLevel::Error, args)
void
ImplCycleCollectionUnlink(nsScriptLoadRequestList& aField);
@ -89,8 +100,6 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsScriptLoadRequest)
nsScriptLoadRequest::~nsScriptLoadRequest()
{
js_free(mScriptTextBuf);
// We should always clean up any off-thread script parsing resources.
MOZ_ASSERT(!mOffThreadToken);
@ -739,9 +748,7 @@ nsScriptLoader::ProcessFetchedModuleSource(nsModuleLoadRequest* aRequest)
nsresult rv = CreateModuleScript(aRequest);
SetModuleFetchFinishedAndResumeWaitingRequests(aRequest, rv);
free(aRequest->mScriptTextBuf);
aRequest->mScriptTextBuf = nullptr;
aRequest->mScriptTextLength = 0;
aRequest->mScriptText.clearAndFree();
if (NS_SUCCEEDED(rv)) {
StartFetchingModuleDependencies(aRequest);
@ -1030,7 +1037,7 @@ nsScriptLoader::StartFetchingModuleAndDependencies(nsModuleLoadRequest* aRequest
RefPtr<GenericPromise> ready = childRequest->mReady.Ensure(__func__);
nsresult rv = StartLoad(childRequest, NS_LITERAL_STRING("module"), false);
nsresult rv = StartLoad(childRequest);
if (NS_FAILED(rv)) {
childRequest->mReady.Reject(rv, __func__);
return ready;
@ -1178,8 +1185,7 @@ nsScriptLoader::InstantiateModuleTree(nsModuleLoadRequest* aRequest)
}
nsresult
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead)
nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest)
{
MOZ_ASSERT(aRequest->IsLoading());
NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
@ -1252,9 +1258,9 @@ nsScriptLoader::StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
if (cos) {
if (aScriptFromHead &&
if (aRequest->mScriptFromHead &&
!(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
// synchronous head scripts block lading of most other non js/css
// synchronous head scripts block loading of most other non js/css
// content such as images
cos->AddClassFlags(nsIClassOfService::Leader);
} else if (!(script && script->GetScriptDeferred())) {
@ -1520,8 +1526,8 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
integrity);
if (!integrity.IsEmpty()) {
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
("nsScriptLoader::ProcessScriptElement, integrity=%s",
NS_ConvertUTF16toUTF8(integrity).get()));
("nsScriptLoader::ProcessScriptElement, integrity=%s",
NS_ConvertUTF16toUTF8(integrity).get()));
nsAutoCString sourceUri;
if (mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
@ -1536,10 +1542,10 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
request->mURI = scriptURI;
request->mIsInline = false;
request->mReferrerPolicy = ourRefPolicy;
// keep request->mScriptFromHead to false so we don't treat non preloaded
// scripts as blockers for full page load. See bug 792438.
// set aScriptFromHead to false so we don't treat non preloaded scripts as
// blockers for full page load. See bug 792438.
rv = StartLoad(request, type, false);
rv = StartLoad(request);
if (NS_FAILED(rv)) {
// Asynchronously report the load failure
NS_DispatchToCurrentThread(
@ -1856,7 +1862,7 @@ nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest)
return rv;
}
if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) {
if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptText.length())) {
return NS_ERROR_FAILURE;
}
@ -1865,14 +1871,16 @@ nsScriptLoader::AttemptAsyncScriptCompile(nsScriptLoadRequest* aRequest)
if (aRequest->IsModuleRequest()) {
if (!JS::CompileOffThreadModule(cx, options,
aRequest->mScriptTextBuf, aRequest->mScriptTextLength,
aRequest->mScriptText.begin(),
aRequest->mScriptText.length(),
OffThreadScriptLoaderCallback,
static_cast<void*>(runnable))) {
return NS_ERROR_OUT_OF_MEMORY;
}
} else {
if (!JS::CompileOffThread(cx, options,
aRequest->mScriptTextBuf, aRequest->mScriptTextLength,
aRequest->mScriptText.begin(),
aRequest->mScriptText.length(),
OffThreadScriptLoaderCallback,
static_cast<void*>(runnable))) {
return NS_ERROR_OUT_OF_MEMORY;
@ -1920,8 +1928,8 @@ nsScriptLoader::GetScriptSource(nsScriptLoadRequest* aRequest, nsAutoString& inl
SourceBufferHolder::NoOwnership);
}
return SourceBufferHolder(aRequest->mScriptTextBuf,
aRequest->mScriptTextLength,
return SourceBufferHolder(aRequest->mScriptText.begin(),
aRequest->mScriptText.length(),
SourceBufferHolder::NoOwnership);
}
@ -2018,9 +2026,7 @@ nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
}
// Free any source data.
free(aRequest->mScriptTextBuf);
aRequest->mScriptTextBuf = nullptr;
aRequest->mScriptTextLength = 0;
aRequest->mScriptText.clearAndFree();
return rv;
}
@ -2455,15 +2461,13 @@ nsScriptLoader::ConvertToUTF16(nsIChannel* aChannel, const uint8_t* aData,
nsresult
nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsScriptLoadRequest* aRequest,
nsresult aChannelStatus,
nsresult aSRIStatus,
mozilla::Vector<char16_t> &aString,
mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier)
{
nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext);
NS_ASSERTION(request, "null request in stream complete handler");
NS_ENSURE_TRUE(request, NS_ERROR_FAILURE);
NS_ASSERTION(aRequest, "null request in stream complete handler");
NS_ENSURE_TRUE(aRequest, NS_ERROR_FAILURE);
nsCOMPtr<nsIRequest> channelRequest;
aLoader->GetRequest(getter_AddRefs(channelRequest));
@ -2471,7 +2475,7 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
channel = do_QueryInterface(channelRequest);
nsresult rv = NS_OK;
if (!request->mIntegrity.IsEmpty() &&
if (!aRequest->mIntegrity.IsEmpty() &&
NS_SUCCEEDED((rv = aSRIStatus))) {
MOZ_ASSERT(aSRIDataVerifier);
MOZ_ASSERT(mReporter);
@ -2480,7 +2484,7 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
if (mDocument && mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
}
rv = aSRIDataVerifier->Verify(request->mIntegrity, channel, sourceUri,
rv = aSRIDataVerifier->Verify(aRequest->mIntegrity, channel, sourceUri,
mReporter);
mReporter->FlushConsoleReports(mDocument);
if (NS_FAILED(rv)) {
@ -2491,12 +2495,12 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
if (loadInfo->GetEnforceSRI()) {
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
("nsScriptLoader::OnStreamComplete, required SRI not found"));
("nsScriptLoader::OnStreamComplete, required SRI not found"));
nsCOMPtr<nsIContentSecurityPolicy> csp;
loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
nsAutoCString violationURISpec;
mDocument->GetDocumentURI()->GetAsciiSpec(violationURISpec);
uint32_t lineNo = request->mElement ? request->mElement->GetScriptLineNumber() : 0;
uint32_t lineNo = aRequest->mElement ? aRequest->mElement->GetScriptLineNumber() : 0;
csp->LogViolationDetails(
nsIContentSecurityPolicy::VIOLATION_TYPE_REQUIRE_SRI_FOR_SCRIPT,
NS_ConvertUTF8toUTF16(violationURISpec),
@ -2506,7 +2510,7 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
}
if (NS_SUCCEEDED(rv)) {
rv = PrepareLoadedRequest(request, aLoader, aChannelStatus, aString);
rv = PrepareLoadedRequest(aRequest, aLoader, aChannelStatus);
}
if (NS_FAILED(rv)) {
@ -2516,57 +2520,57 @@ nsScriptLoader::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
* array of blocked tracking nodes under its parent document.
*/
if (rv == NS_ERROR_TRACKING_URI) {
nsCOMPtr<nsIContent> cont = do_QueryInterface(request->mElement);
nsCOMPtr<nsIContent> cont = do_QueryInterface(aRequest->mElement);
mDocument->AddBlockedTrackingNode(cont);
}
if (request->mIsDefer) {
MOZ_ASSERT_IF(request->IsModuleRequest(),
request->AsModuleRequest()->IsTopLevel());
if (request->isInList()) {
RefPtr<nsScriptLoadRequest> req = mDeferRequests.Steal(request);
if (aRequest->mIsDefer) {
MOZ_ASSERT_IF(aRequest->IsModuleRequest(),
aRequest->AsModuleRequest()->IsTopLevel());
if (aRequest->isInList()) {
RefPtr<nsScriptLoadRequest> req = mDeferRequests.Steal(aRequest);
FireScriptAvailable(rv, req);
}
} else if (request->mIsAsync) {
MOZ_ASSERT_IF(request->IsModuleRequest(),
request->AsModuleRequest()->IsTopLevel());
if (request->isInList()) {
RefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(request);
} else if (aRequest->mIsAsync) {
MOZ_ASSERT_IF(aRequest->IsModuleRequest(),
aRequest->AsModuleRequest()->IsTopLevel());
if (aRequest->isInList()) {
RefPtr<nsScriptLoadRequest> req = mLoadingAsyncRequests.Steal(aRequest);
FireScriptAvailable(rv, req);
}
} else if (request->mIsNonAsyncScriptInserted) {
if (request->isInList()) {
} else if (aRequest->mIsNonAsyncScriptInserted) {
if (aRequest->isInList()) {
RefPtr<nsScriptLoadRequest> req =
mNonAsyncExternalScriptInsertedRequests.Steal(request);
mNonAsyncExternalScriptInsertedRequests.Steal(aRequest);
FireScriptAvailable(rv, req);
}
} else if (request->mIsXSLT) {
if (request->isInList()) {
RefPtr<nsScriptLoadRequest> req = mXSLTRequests.Steal(request);
} else if (aRequest->mIsXSLT) {
if (aRequest->isInList()) {
RefPtr<nsScriptLoadRequest> req = mXSLTRequests.Steal(aRequest);
FireScriptAvailable(rv, req);
}
} else if (request->IsModuleRequest()) {
nsModuleLoadRequest* modReq = request->AsModuleRequest();
} else if (aRequest->IsModuleRequest()) {
nsModuleLoadRequest* modReq = aRequest->AsModuleRequest();
MOZ_ASSERT(!modReq->IsTopLevel());
MOZ_ASSERT(!modReq->isInList());
modReq->Cancel();
FireScriptAvailable(rv, request);
} else if (mParserBlockingRequest == request) {
MOZ_ASSERT(!request->isInList());
FireScriptAvailable(rv, aRequest);
} else if (mParserBlockingRequest == aRequest) {
MOZ_ASSERT(!aRequest->isInList());
mParserBlockingRequest = nullptr;
UnblockParser(request);
UnblockParser(aRequest);
// Ensure that we treat request->mElement as our current parser-inserted
// Ensure that we treat aRequest->mElement as our current parser-inserted
// script while firing onerror on it.
MOZ_ASSERT(request->mElement->GetParserCreated());
MOZ_ASSERT(aRequest->mElement->GetParserCreated());
nsCOMPtr<nsIScriptElement> oldParserInsertedScript =
mCurrentParserInsertedScript;
mCurrentParserInsertedScript = request->mElement;
FireScriptAvailable(rv, request);
ContinueParserAsync(request);
mCurrentParserInsertedScript = aRequest->mElement;
FireScriptAvailable(rv, aRequest);
ContinueParserAsync(aRequest);
mCurrentParserInsertedScript = oldParserInsertedScript;
} else {
mPreloads.RemoveElement(request, PreloadRequestComparator());
mPreloads.RemoveElement(aRequest, PreloadRequestComparator());
}
}
@ -2620,8 +2624,7 @@ nsScriptLoader::MaybeMoveToLoadedList(nsScriptLoadRequest* aRequest)
nsresult
nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
nsIIncrementalStreamLoader* aLoader,
nsresult aStatus,
mozilla::Vector<char16_t> &aString)
nsresult aStatus)
{
if (NS_FAILED(aStatus)) {
return aStatus;
@ -2673,11 +2676,6 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
NS_ENSURE_SUCCESS(rv, rv);
}
if (!aString.empty()) {
aRequest->mScriptTextLength = aString.length();
aRequest->mScriptTextBuf = aString.extractOrCopyRawBuffer();
}
// This assertion could fire errorously if we ran out of memory when
// inserting the request in the array. However it's an unlikely case
// so if you see this assertion it is likely something else that is
@ -2791,8 +2789,8 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
SRIMetadata sriMetadata;
if (!aIntegrity.IsEmpty()) {
MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
("nsScriptLoader::PreloadURI, integrity=%s",
NS_ConvertUTF16toUTF8(aIntegrity).get()));
("nsScriptLoader::PreloadURI, integrity=%s",
NS_ConvertUTF16toUTF8(aIntegrity).get()));
nsAutoCString sourceUri;
if (mDocument->GetDocumentURI()) {
mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
@ -2806,8 +2804,9 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
request->mURI = aURI;
request->mIsInline = false;
request->mReferrerPolicy = aReferrerPolicy;
request->mScriptFromHead = aScriptFromHead;
nsresult rv = StartLoad(request, aType, aScriptFromHead);
nsresult rv = StartLoad(request);
if (NS_FAILED(rv)) {
return;
}
@ -2853,8 +2852,7 @@ nsScriptLoadHandler::nsScriptLoadHandler(nsScriptLoader *aScriptLoader,
mRequest(aRequest),
mSRIDataVerifier(aSRIDataVerifier),
mSRIStatus(NS_OK),
mDecoder(),
mBuffer()
mDecoder()
{}
nsScriptLoadHandler::~nsScriptLoadHandler()
@ -2884,8 +2882,8 @@ nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
*aConsumedLength = aDataLength;
// Decoder has already been initialized. -- trying to decode all loaded bytes.
nsresult rv = TryDecodeRawData(aData, aDataLength,
/* aEndOfStream = */ false);
nsresult rv = DecodeRawData(aData, aDataLength,
/* aEndOfStream = */ false);
NS_ENSURE_SUCCESS(rv, rv);
// If SRI is required for this load, appending new bytes to the hash.
@ -2897,9 +2895,9 @@ nsScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader,
}
nsresult
nsScriptLoadHandler::TryDecodeRawData(const uint8_t* aData,
uint32_t aDataLength,
bool aEndOfStream)
nsScriptLoadHandler::DecodeRawData(const uint8_t* aData,
uint32_t aDataLength,
bool aEndOfStream)
{
int32_t srcLen = aDataLength;
const char* src = reinterpret_cast<const char *>(aData);
@ -2909,25 +2907,25 @@ nsScriptLoadHandler::TryDecodeRawData(const uint8_t* aData,
NS_ENSURE_SUCCESS(rv, rv);
uint32_t haveRead = mBuffer.length();
uint32_t haveRead = mRequest->mScriptText.length();
CheckedInt<uint32_t> capacity = haveRead;
capacity += dstLen;
if (!capacity.isValid() || !mBuffer.reserve(capacity.value())) {
if (!capacity.isValid() || !mRequest->mScriptText.reserve(capacity.value())) {
return NS_ERROR_OUT_OF_MEMORY;
}
rv = mDecoder->Convert(src,
&srcLen,
mBuffer.begin() + haveRead,
&dstLen);
&srcLen,
mRequest->mScriptText.begin() + haveRead,
&dstLen);
NS_ENSURE_SUCCESS(rv, rv);
haveRead += dstLen;
MOZ_ASSERT(haveRead <= capacity.value(), "mDecoder produced more data than expected");
MOZ_ALWAYS_TRUE(mBuffer.resizeUninitialized(haveRead));
MOZ_ALWAYS_TRUE(mRequest->mScriptText.resizeUninitialized(haveRead));
return NS_OK;
}
@ -3027,8 +3025,8 @@ nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
DebugOnly<bool> encoderSet =
EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true);
MOZ_ASSERT(encoderSet);
DebugOnly<nsresult> rv = TryDecodeRawData(aData, aDataLength,
/* aEndOfStream = */ true);
DebugOnly<nsresult> rv = DecodeRawData(aData, aDataLength,
/* aEndOfStream = */ true);
// If SRI is required for this load, appending new bytes to the hash.
if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) {
@ -3038,5 +3036,5 @@ nsScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
// we have to mediate and use mRequest.
return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus,
mBuffer, mSRIDataVerifier);
mSRIDataVerifier);
}

View File

@ -74,6 +74,7 @@ public:
const mozilla::dom::SRIMetadata &aIntegrity)
: mKind(aKind),
mElement(aElement),
mScriptFromHead(false),
mProgress(Progress::Loading),
mIsInline(true),
mHasSourceMapURL(false),
@ -85,8 +86,7 @@ public:
mWasCompiledOMT(false),
mIsTracking(false),
mOffThreadToken(nullptr),
mScriptTextBuf(nullptr),
mScriptTextLength(0),
mScriptText(),
mJSVersion(aVersion),
mLineNo(1),
mCORSMode(aCORSMode),
@ -167,6 +167,7 @@ public:
const nsScriptKind mKind;
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?
bool mIsInline; // Is the script inline or loaded?
bool mHasSourceMapURL; // Does the HTTP header have a source map url?
@ -179,8 +180,9 @@ 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
char16_t* mScriptTextBuf; // Holds script text for non-inline scripts. Don't
size_t mScriptTextLength; // use nsString so we can give ownership to jsapi.
// Holds script text for non-inline scripts. Don't use nsString so we can give
// ownership to jsapi.
mozilla::Vector<char16_t> mScriptText;
uint32_t mJSVersion;
nsCOMPtr<nsIURI> mURI;
nsCOMPtr<nsIPrincipal> mOriginPrincipal;
@ -303,7 +305,7 @@ public:
}
/**
* Process a script element. This will include both loading the
* Process a script element. This will include both loading the
* source of the element if it is not inline and evaluating
* the script itself.
*
@ -336,7 +338,7 @@ public:
/**
* Whether the loader is enabled or not.
* When disabled, processing of new script elements is disabled.
* When disabled, processing of new script elements is disabled.
* Any call to ProcessScriptElement() will return false. Note that
* this DOES NOT disable currently loading or executing scripts.
*/
@ -408,13 +410,13 @@ public:
/**
* Handle the completion of a stream. This is called by the
* nsScriptLoadHandler object which observes the IncrementalStreamLoader
* loading the script.
* loading the script. The streamed content is expected to be stored on the
* aRequest argument.
*/
nsresult OnStreamComplete(nsIIncrementalStreamLoader* aLoader,
nsISupports* aContext,
nsScriptLoadRequest* aRequest,
nsresult aChannelStatus,
nsresult aSRIStatus,
mozilla::Vector<char16_t> &aString,
mozilla::dom::SRICheckDataVerifier* aSRIDataVerifier);
/**
@ -519,8 +521,7 @@ private:
/**
* Start a load for aRequest's URI.
*/
nsresult StartLoad(nsScriptLoadRequest *aRequest, const nsAString &aType,
bool aScriptFromHead);
nsresult StartLoad(nsScriptLoadRequest *aRequest);
/**
* Process any pending requests asynchronously (i.e. off an event) if there
@ -574,8 +575,7 @@ private:
uint32_t NumberOfProcessors();
nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
nsIIncrementalStreamLoader* aLoader,
nsresult aStatus,
mozilla::Vector<char16_t> &aString);
nsresult aStatus);
void AddDeferRequest(nsScriptLoadRequest* aRequest);
bool MaybeRemovedDeferRequests();
@ -673,10 +673,11 @@ private:
virtual ~nsScriptLoadHandler();
/*
* Try to decode some raw data.
* Once the charset is found by the EnsureDecoder function, we can
* incrementally convert the charset to the one expected by the JS Parser.
*/
nsresult TryDecodeRawData(const uint8_t* aData, uint32_t aDataLength,
bool aEndOfStream);
nsresult DecodeRawData(const uint8_t* aData, uint32_t aDataLength,
bool aEndOfStream);
/*
* Discover the charset by looking at the stream data, the script
@ -690,7 +691,7 @@ private:
// ScriptLoader which will handle the parsed script.
RefPtr<nsScriptLoader> mScriptLoader;
// The nsScriptLoadRequest for this load.
// The nsScriptLoadRequest for this load. Decoded data are accumulated on it.
RefPtr<nsScriptLoadRequest> mRequest;
// SRI data verifier.
@ -701,9 +702,6 @@ private:
// Unicode decoder for charset.
nsCOMPtr<nsIUnicodeDecoder> mDecoder;
// Accumulated decoded char buffer.
mozilla::Vector<char16_t> mBuffer;
};
class nsAutoScriptLoaderDisabler

View File

@ -15,11 +15,11 @@
namespace mozilla {
namespace dom {
static const nsUConvProp labelsEncodings[] = {
static constexpr nsUConvProp labelsEncodings[] = {
#include "labelsencodings.properties.h"
};
static const nsUConvProp encodingsGroups[] = {
static constexpr nsUConvProp encodingsGroups[] = {
#include "encodingsgroups.properties.h"
};

View File

@ -15,15 +15,15 @@
namespace mozilla {
namespace dom {
static const nsUConvProp localesFallbacks[] = {
static constexpr nsUConvProp localesFallbacks[] = {
#include "localesfallbacks.properties.h"
};
static const nsUConvProp domainsFallbacks[] = {
static constexpr nsUConvProp domainsFallbacks[] = {
#include "domainsfallbacks.properties.h"
};
static const nsUConvProp nonParticipatingDomains[] = {
static constexpr nsUConvProp nonParticipatingDomains[] = {
#include "nonparticipatingdomains.properties.h"
};

View File

@ -51,7 +51,7 @@ load 680922-1.xul
load 682058.xhtml
load 682460.html
load 738744.xhtml
asserts-if(stylo,6-30) load 741218.json # bug 1324634
load 741218.json
load 741250.xhtml
load 795221-1.html
asserts-if(stylo,1) load 795221-2.html # bug 1324702

View File

@ -579,8 +579,13 @@ ContentParent::JoinAllSubprocesses()
ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
ProcessPriority aPriority,
ContentParent* aOpener,
bool aLargeAllocationProcess)
bool aLargeAllocationProcess,
bool* aNew)
{
if (aNew) {
*aNew = false;
}
if (!sBrowserContentParents) {
sBrowserContentParents =
new nsClassHashtable<nsStringHashKey, nsTArray<ContentParent*>>;
@ -621,6 +626,9 @@ ContentParent::GetNewOrUsedBrowserProcess(const nsAString& aRemoteType,
}
RefPtr<ContentParent> p = new ContentParent(aOpener, contentProcessType);
if (aNew) {
*aNew = true;
}
if (!p->LaunchSubprocess(aPriority)) {
return nullptr;
@ -906,6 +914,7 @@ ContentParent::CreateBrowser(const TabContext& aContext,
openerTabId = TabParent::GetTabIdFrom(docShell);
}
bool newProcess = false;
RefPtr<nsIContentParent> constructorSender;
if (isInContentProcess) {
MOZ_ASSERT(aContext.IsMozBrowserElement());
@ -923,7 +932,7 @@ ContentParent::CreateBrowser(const TabContext& aContext,
constructorSender =
GetNewOrUsedBrowserProcess(remoteType, initialPriority, nullptr,
aFreshProcess);
aFreshProcess, &newProcess);
if (!constructorSender) {
return nullptr;
}
@ -971,7 +980,10 @@ ContentParent::CreateBrowser(const TabContext& aContext,
constructorSender->IsForBrowser());
if (aFreshProcess) {
Unused << browser->SendSetFreshProcess();
// Tell the TabChild object that it was created due to a Large-Allocation
// request, and whether or not that Large-Allocation request succeeded at
// creating a new content process.
Unused << browser->SendSetIsLargeAllocation(true, newProcess);
}
if (browser) {

View File

@ -148,7 +148,8 @@ public:
hal::ProcessPriority aPriority =
hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
ContentParent* aOpener = nullptr,
bool aLargeAllocationProcess = false);
bool aLargeAllocationProcess = false,
bool* anew = nullptr);
/**
* Get or create a content process for the given TabContext. aFrameElement

View File

@ -908,7 +908,7 @@ child:
* Tell the child that it is a fresh process created for a Large-Allocation
* load.
*/
async SetFreshProcess();
async SetIsLargeAllocation(bool aIsLA, bool aNewProcess);
/*
* FIXME: write protocol!

View File

@ -155,7 +155,7 @@ static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
static TabChildMap* sTabChildren;
bool TabChild::sWasFreshProcess = false;
bool TabChild::sInLargeAllocProcess = false;
TabChildBase::TabChildBase()
: mTabChildGlobal(nullptr)
@ -381,7 +381,7 @@ TabChild::TabChild(nsIContentChild* aManager,
, mParentIsActive(false)
, mDidSetRealShowInfo(false)
, mDidLoadURLInit(false)
, mIsFreshProcess(false)
, mAwaitingLA(false)
, mSkipKeyPress(false)
, mLayerObserverEpoch(0)
#if defined(XP_WIN) && defined(ACCESSIBILITY)
@ -3100,13 +3100,27 @@ TabChild::RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache)
}
mozilla::ipc::IPCResult
TabChild::RecvSetFreshProcess()
TabChild::RecvSetIsLargeAllocation(const bool& aIsLA, const bool& aNewProcess)
{
MOZ_ASSERT(!sWasFreshProcess, "Can only be a fresh process once!");
mIsFreshProcess = true;
mAwaitingLA = aIsLA;
sInLargeAllocProcess = aIsLA && aNewProcess;
return IPC_OK();
}
bool
TabChild::IsAwaitingLargeAlloc()
{
return mAwaitingLA;
}
bool
TabChild::TakeAwaitingLargeAlloc()
{
bool awaiting = mAwaitingLA;
mAwaitingLA = false;
return awaiting;
}
mozilla::plugins::PPluginWidgetChild*
TabChild::AllocPPluginWidgetChild()
{

View File

@ -661,30 +661,23 @@ public:
uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
#endif
bool TakeIsFreshProcess()
// These methods return `true` if this TabChild is currently awaiting a
// Large-Allocation header.
bool TakeAwaitingLargeAlloc();
bool IsAwaitingLargeAlloc();
// Returns `true` if this this process was created to load a docshell in a
// "Fresh Process". This value is initialized to `false`, and is set to `true`
// in RecvSetFreshProcess.
static bool InLargeAllocProcess()
{
if (mIsFreshProcess) {
MOZ_ASSERT(!sWasFreshProcess,
"At most one tabGroup may be a fresh process per process");
sWasFreshProcess = true;
mIsFreshProcess = false;
return true;
}
return false;
return sInLargeAllocProcess;
}
already_AddRefed<nsISHistory> GetRelatedSHistory();
mozilla::dom::TabGroup* TabGroup();
// Returns `true` if this this process was created to load a docshell in a
// "Fresh Process". This value is initialized to `false`, and is set to `true`
// in RecvSetFreshProcess.
static bool GetWasFreshProcess()
{
return sWasFreshProcess;
}
protected:
virtual ~TabChild();
@ -722,7 +715,8 @@ protected:
virtual mozilla::ipc::IPCResult RecvNotifyPartialSHistoryDeactive() override;
virtual mozilla::ipc::IPCResult RecvSetFreshProcess() override;
virtual mozilla::ipc::IPCResult RecvSetIsLargeAllocation(const bool& aIsLA,
const bool& aNewProcess) override;
private:
void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
@ -813,7 +807,7 @@ private:
CSSSize mUnscaledInnerSize;
bool mDidSetRealShowInfo;
bool mDidLoadURLInit;
bool mIsFreshProcess;
bool mAwaitingLA;
bool mSkipKeyPress;
@ -834,7 +828,7 @@ private:
uintptr_t mNativeWindowHandle;
#endif // defined(XP_WIN)
static bool sWasFreshProcess;
static bool sInLargeAllocProcess;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
};

View File

@ -1,6 +1,6 @@
load 341963-1.html
load 344874-1.html
load 344996-1.xhtml
asserts-if(stylo,4) load 457050-1.html # bug 1324634
load 457050-1.html
load 1018583.html
load 1180389.html

View File

@ -319,3 +319,5 @@ LargeAllocationNotOnlyToplevelInTabGroup=A Large-Allocation header was ignored d
# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name
LargeAllocationNonE10S=A Large-Allocation header was ignored due to the document not being loaded out of process.
GeolocationInsecureRequestIsForbidden=A Geolocation request can only be fulfilled in a secure context.
# LOCALIZATION NOTE: Do not translate "Large-Allocation", as it is a literal header name.
LargeAllocationNonWin32=This page would be loaded in a new process due to a Large-Allocation header, however Large-Allocation process creation is disabled on non-Win32 platforms.

View File

@ -11,4 +11,4 @@ load 570884.html
skip-if(!haveTestPlugin||http.platform!="X11") load 598862.html
skip-if(Android) load 626602-1.html # bug 908363
load 752340.html
asserts-if(stylo,1) load 843086.xhtml # bug 1324647
load 843086.xhtml

View File

@ -45,8 +45,12 @@
* fullscreen (has been deemed a security issue otherwise and therefore
* disabled by default)
*/
yield SpecialPowers.pushPrefEnv(
{ "set": [ ["full-screen-api.allow-trusted-requests-only", false] ] });
yield SpecialPowers.pushPrefEnv({
"set": [
["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.unprefix.enabled", true],
],
});
});
add_task(function* () {

View File

@ -45,8 +45,12 @@
* fullscreen (has been deemed a security issue otherwise and therefore
* disabled by default)
*/
yield SpecialPowers.pushPrefEnv(
{ "set": [ ["full-screen-api.allow-trusted-requests-only", false] ] });
yield SpecialPowers.pushPrefEnv({
"set": [
["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.unprefix.enabled", true],
],
});
});
add_task(function* () {

View File

@ -45,8 +45,12 @@
* fullscreen (has been deemed a security issue otherwise and therefore
* disabled by default)
*/
yield SpecialPowers.pushPrefEnv(
{ "set": [ ["full-screen-api.allow-trusted-requests-only", false] ] });
yield SpecialPowers.pushPrefEnv({
"set": [
["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.unprefix.enabled", true],
],
});
});
add_task(function* () {

View File

@ -45,8 +45,12 @@
* fullscreen (has been deemed a security issue otherwise and therefore
* disabled by default)
*/
yield SpecialPowers.pushPrefEnv(
{ "set": [ ["full-screen-api.allow-trusted-requests-only", false] ] });
yield SpecialPowers.pushPrefEnv({
"set": [
["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.unprefix.enabled", true],
],
});
});
add_task(function* () {

View File

@ -45,8 +45,12 @@
* fullscreen (has been deemed a security issue otherwise and therefore
* disabled by default)
*/
yield SpecialPowers.pushPrefEnv(
{ "set": [ ["full-screen-api.allow-trusted-requests-only", false] ] });
yield SpecialPowers.pushPrefEnv({
"set": [
["full-screen-api.allow-trusted-requests-only", false],
["full-screen-api.unprefix.enabled", true],
],
});
});
add_task(function* () {

View File

@ -12,6 +12,7 @@ support-files =
test_largeAllocation.html^headers^
test_largeAllocation2.html
test_largeAllocation2.html^headers^
helper_largeAllocation.js
!/dom/tests/mochitest/geolocation/network_geolocation.sjs
[browser_allocateGigabyte.js]
@ -35,8 +36,10 @@ skip-if = e10s
[browser_focus_steal_from_chrome.js]
[browser_focus_steal_from_chrome_during_mousedown.js]
[browser_frame_elements.js]
[browser_largeAllocation.js]
skip-if = true || !e10s # Large-Allocation requires e10s, turned off for e10s-multi Bug 1315042
[browser_largeAllocation_win32.js]
skip-if = !e10s || os != "win" || processor != "x86" # Large-Allocation requires e10s
[browser_largeAllocation_non_win32.js]
skip-if = !e10s || (os == "win" && processor == "x86") # Large-Allocation requires e10s
[browser_localStorage_privatestorageevent.js]
[browser_test__content.js]
[browser_test_new_window_from_content.js]

View File

@ -0,0 +1,39 @@
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper_largeAllocation.js", this);
// Force-enabling the Large-Allocation header
add_task(function*() {
info("Test 1 - force enabling the Large-Allocation header");
yield SpecialPowers.pushPrefEnv({
set: [
// Enable the header if it is disabled
["dom.largeAllocationHeader.enabled", true],
// Force-enable process creation with large-allocation, such that non
// win32 builds can test the behavior.
["dom.largeAllocation.forceEnable", true],
// Increase processCount.webLargeAllocation to avoid any races where
// processes aren't being cleaned up quickly enough.
["dom.ipc.processCount.webLargeAllocation", 20]
]
});
yield* largeAllocSuccessTests();
});
add_task(function*() {
info("Test 2 - not force enabling the Large-Allocation header");
yield SpecialPowers.pushPrefEnv({
set: [
// Enable the header if it is disabled
["dom.largeAllocationHeader.enabled", true],
// Force-enable process creation with large-allocation, such that non
// win32 builds can test the behavior.
["dom.largeAllocation.forceEnable", false],
// Increase processCount.webLargeAllocation to avoid any races where
// processes aren't being cleaned up quickly enough.
["dom.ipc.processCount.webLargeAllocation", 20]
]
});
yield* largeAllocFailTests();
});

View File

@ -0,0 +1,18 @@
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helper_largeAllocation.js", this);
add_task(function*() {
info("Test 1 - On win32 - no forceEnable pref");
yield SpecialPowers.pushPrefEnv({
set: [
// Enable the header if it is disabled
["dom.largeAllocationHeader.enabled", true],
// Increase processCount.webLargeAllocation to avoid any races where
// processes aren't being cleaned up quickly enough.
["dom.ipc.processCount.webLargeAllocation", 20]
]
});
yield* largeAllocSuccessTests();
});

View File

@ -7,7 +7,8 @@ const TEST_URI_2 = "http://example.com/browser/dom/tests/browser/test_largeAlloc
function expectProcessCreated() {
let os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
return new Promise(resolve => {
let kill; // A kill function which will disable the promise.
let promise = new Promise((resolve, reject) => {
let topic = "ipc:content-created";
function observer() {
os.removeObserver(observer, topic);
@ -15,7 +16,14 @@ function expectProcessCreated() {
resolve();
}
os.addObserver(observer, topic, /* weak = */ false);
kill = () => {
os.removeObserver(observer, topic);
ok(true, "Expect process created killed");
reject();
};
});
promise.kill = kill;
return promise;
}
function expectNoProcess() {
@ -38,13 +46,31 @@ function getPID(aBrowser) {
});
}
add_task(function*() {
function getInLAProc(aBrowser) {
return ContentTask.spawn(aBrowser, null, () => {
try {
return docShell.inLargeAllocProcess;
} catch (e) {
// This must be a non-remote browser, which means it is not fresh
return false;
}
});
}
function* largeAllocSuccessTests() {
// I'm terrible and put this set of tests into a single file, so I need a longer timeout
requestLongerTimeout(2);
// Check if we are on win32
let isWin32 = /Windows/.test(navigator.userAgent) && !/x64/.test(navigator.userAgent);
yield SpecialPowers.pushPrefEnv({
set: [
// Enable the header if it is disabled
["dom.largeAllocationHeader.enabled", true],
// Force-enable process creation with large-allocation, such that non
// win32 builds can test the behavior.
["dom.largeAllocation.forceEnable", !isWin32],
// Increase processCount.webLargeAllocation to avoid any races where
// processes aren't being cleaned up quickly enough.
["dom.ipc.processCount.webLargeAllocation", 20]
@ -55,6 +81,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 0");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
@ -67,6 +94,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "The pids should be different between the initial load and the new load");
is(true, yield getInLAProc(aBrowser));
});
// When a Large-Allocation document is loaded in an iframe, the header should
@ -74,6 +102,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 1");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
// Fail the test if we create a process
let stopExpectNoProcess = expectNoProcess();
@ -92,6 +121,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
is(pid1, pid2, "The PID should not have changed");
is(false, yield getInLAProc(aBrowser));
stopExpectNoProcess();
});
@ -100,6 +130,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("http://example.com", function*(aBrowser) {
info("Starting test 2");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
// Fail the test if we create a process
let stopExpectNoProcess = expectNoProcess();
@ -126,6 +157,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
is(pid1, pid2, "The PID should not have changed");
is(false, yield getInLAProc(aBrowser));
stopExpectNoProcess();
});
@ -134,6 +166,7 @@ add_task(function*() {
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 3");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
@ -146,8 +179,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2);
epc = expectProcessCreated();
is(true, yield getInLAProc(aBrowser));
yield BrowserTestUtils.browserLoaded(aBrowser);
@ -158,8 +190,10 @@ add_task(function*() {
let pid3 = yield getPID(aBrowser);
// We should have been kicked out of the large-allocation process by the
// load, meaning we're back in the first process.
is(pid1, pid3); // XXX: This may be flakey in multiple content process e10s?
// load, meaning we're back in a non-fresh process
is(false, yield getInLAProc(aBrowser));
epc = expectProcessCreated();
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
content.document.location = TEST_URI;
@ -171,12 +205,14 @@ add_task(function*() {
isnot(pid1, pid4);
isnot(pid2, pid4);
is(true, yield getInLAProc(aBrowser));
});
// Load Large-Allocation then about:blank load, then back button press should load from bfcache.
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 4");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
@ -189,8 +225,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
let stopExpectNoProcess = expectNoProcess();
is(true, yield getInLAProc(aBrowser));
yield BrowserTestUtils.browserLoaded(aBrowser);
@ -204,10 +239,8 @@ add_task(function*() {
let pid3 = yield getPID(aBrowser);
// We should have been kicked out of the large-allocation process by the
// load, meaning we're back in the first process.
is(pid1, pid3, "PIDs 1 and 3 should match");
stopExpectNoProcess();
// load, meaning we're back in a non-large-allocation process.
is(false, yield getInLAProc(aBrowser));
epc = expectProcessCreated();
@ -223,13 +256,15 @@ add_task(function*() {
isnot(pid1, pid4, "PID 4 shouldn't match PID 1");
isnot(pid2, pid4, "PID 4 shouldn't match PID 2");
isnot(pid3, pid4, "PID 4 shouldn't match PID 3");
is(true, yield getInLAProc(aBrowser));
});
// Two consecutive large-allocation loads should create two processes.
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 5");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([expectProcessCreated(),
BrowserTestUtils.browserLoaded(aBrowser)]);
@ -243,6 +278,7 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
is(true, yield getInLAProc(aBrowser));
let epc = expectProcessCreated();
@ -252,16 +288,31 @@ add_task(function*() {
yield epc;
// We just saw the creation of a new process. This is either the process we
// are interested in, or, in a multi-e10s situation, the normal content
// process which was created for the normal content to be loaded into as the
// browsing context was booted out of the fresh process. If we discover that
// this was not a fresh process, we'll need to wait for another process.
// Start listening now.
epc = expectProcessCreated();
if (!(yield getInLAProc(aBrowser))) {
yield epc;
} else {
epc.kill();
}
let pid3 = yield getPID(aBrowser);
isnot(pid1, pid3, "PIDs 1 and 3 should not match");
isnot(pid2, pid3, "PIDs 1 and 3 should not match");
isnot(pid2, pid3, "PIDs 2 and 3 should not match");
is(true, yield getInLAProc(aBrowser));
});
// Opening a window from the large-allocation window should prevent the process switch.
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 6");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([expectProcessCreated(),
BrowserTestUtils.browserLoaded(aBrowser)]);
@ -275,12 +326,12 @@ add_task(function*() {
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
yield BrowserTestUtils.synthesizeMouse("a", 0, 0, {}, aBrowser);
is(true, yield getInLAProc(aBrowser));
let stopExpectNoProcess = expectNoProcess();
yield ContentTask.spawn(aBrowser, null, () => {
this.__newWindow = content.window.open("about:blank");
content.document.location = "about:blank";
});
@ -289,12 +340,81 @@ add_task(function*() {
let pid3 = yield getPID(aBrowser);
is(pid3, pid2, "PIDs 2 and 3 should match");
is(true, yield getInLAProc(aBrowser));
stopExpectNoProcess();
is(gBrowser.tabs.length, 3, "There should be 3 tabs");
// Get rid of that other tab. It should always be the last one.
gBrowser.removeTab(gBrowser.tabs[2]);
yield ContentTask.spawn(aBrowser, null, () => {
ok(this.__newWindow, "The window should have been stored");
this.__newWindow.close();
});
});
});
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
info("Starting test 7");
yield SpecialPowers.pushPrefEnv({
set: [
["dom.ipc.processCount.webLargeAllocation", 1]
],
});
// Loading the first Large-Allocation tab should succeed as normal
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([expectProcessCreated(),
BrowserTestUtils.browserLoaded(aBrowser)]);
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
content.document.location = TEST_URI;
});
yield ready;
let pid2 = yield getPID(aBrowser);
isnot(pid1, pid2, "PIDs 1 and 2 should not match");
is(true, yield getInLAProc(aBrowser));
yield BrowserTestUtils.withNewTab("about:blank", function*(aBrowser) {
// The second one should load in a non-LA proc because the
// webLargeAllocation processes have been exhausted.
is(false, yield getInLAProc(aBrowser));
let ready = Promise.all([BrowserTestUtils.browserLoaded(aBrowser)]);
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
content.document.location = TEST_URI;
});
yield ready;
is(false, yield getInLAProc(aBrowser));
});
});
// XXX: Make sure to reset dom.ipc.processCount.webLargeAllocation if adding a
// test after the above test.
}
function* largeAllocFailTests() {
yield BrowserTestUtils.withNewTab("http://example.com", function*(aBrowser) {
info("Starting test 1");
let pid1 = yield getPID(aBrowser);
is(false, yield getInLAProc(aBrowser));
// Fail the test if we create a process
let stopExpectNoProcess = expectNoProcess();
yield ContentTask.spawn(aBrowser, TEST_URI, TEST_URI => {
content.document.location = TEST_URI;
});
yield BrowserTestUtils.browserLoaded(aBrowser);
let pid2 = yield getPID(aBrowser);
is(pid1, pid2, "The PID should not have changed");
is(false, yield getInLAProc(aBrowser));
stopExpectNoProcess();
});
}

View File

@ -1,9 +1,4 @@
<!doctype html>
<html>
<script>
function onClick() {
window.open("about:blank");
}
</script>
<body><a onclick="onClick()">clicky</a>Loaded in a new process!</body>
<body>Loaded in a new process!</body>
</html>

View File

@ -955,6 +955,7 @@ GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
}
ScopedGLDrawState autoStates(mGL);
const ScopedBindFramebuffer bindFB(mGL);
if (internalFBs) {
mGL->Screen()->BindFB_Internal(destFB);
} else {
@ -973,6 +974,7 @@ GLBlitHelper::DrawBlitTextureToFramebuffer(GLuint srcTex, GLuint destFB,
return;
}
const ScopedBindTexture bindTex(mGL, srcTex, srcTarget);
mGL->fDrawArrays(LOCAL_GL_TRIANGLE_STRIP, 0, 4);
}

View File

@ -331,6 +331,10 @@ public:
return mContextLost;
}
bool HasPBOState() const {
return (!IsGLES() || Version() >= 300);
}
/**
* If this context is double-buffered, returns TRUE.
*/
@ -3670,6 +3674,15 @@ public:
static bool ShouldSpew();
static bool ShouldDumpExts();
void Readback(SharedSurface* src, gfx::DataSourceSurface* dest);
////
void TexParams_SetClampNoMips(GLenum target = LOCAL_GL_TEXTURE_2D) {
fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
}
};
bool DoesStringMatch(const char* aString, const char* aWantedString);

View File

@ -495,12 +495,6 @@ ScopedGLDrawState::~ScopedGLDrawState()
////////////////////////////////////////////////////////////////////////
// ScopedPackState
static bool
HasPBOState(const GLContext* gl)
{
return (!gl->IsGLES() || gl->Version() >= 300);
}
ScopedPackState::ScopedPackState(GLContext* gl)
: ScopedGLWrapper<ScopedPackState>(gl)
{
@ -508,7 +502,7 @@ ScopedPackState::ScopedPackState(GLContext* gl)
if (mAlignment != 4) mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, 4);
if (!HasPBOState(mGL))
if (!mGL->HasPBOState())
return;
mGL->fGetIntegerv(LOCAL_GL_PIXEL_PACK_BUFFER_BINDING, (GLint*)&mPixelBuffer);
@ -527,7 +521,7 @@ ScopedPackState::UnwrapImpl()
{
mGL->fPixelStorei(LOCAL_GL_PACK_ALIGNMENT, mAlignment);
if (!HasPBOState(mGL))
if (!mGL->HasPBOState())
return;
mGL->fBindBuffer(LOCAL_GL_PIXEL_PACK_BUFFER, mPixelBuffer);
@ -536,5 +530,47 @@ ScopedPackState::UnwrapImpl()
mGL->fPixelStorei(LOCAL_GL_PACK_SKIP_ROWS, mSkipRows);
}
////////////////////////////////////////////////////////////////////////
// ScopedBindPBO
static GLuint
GetPBOBinding(GLContext* gl, GLenum target)
{
if (!gl->HasPBOState())
return 0;
GLenum targetBinding;
switch (target) {
case LOCAL_GL_PIXEL_PACK_BUFFER:
targetBinding = LOCAL_GL_PIXEL_PACK_BUFFER_BINDING;
break;
case LOCAL_GL_PIXEL_UNPACK_BUFFER:
targetBinding = LOCAL_GL_PIXEL_UNPACK_BUFFER_BINDING;
break;
default:
MOZ_CRASH();
}
return gl->GetIntAs<GLuint>(targetBinding);
}
ScopedBindPBO::ScopedBindPBO(GLContext* gl, GLenum target)
: ScopedGLWrapper<ScopedPackState>(gl)
, mTarget(target)
, mPBO(GetPBOBinding(mGL, mTarget))
{ }
void
ScopedBindPBO::UnwrapImpl()
{
if (!mGL->HasPBOState())
return;
mGL->fBindBuffer(mTarget, mPBO);
}
} /* namespace gl */
} /* namespace mozilla */

View File

@ -362,6 +362,22 @@ protected:
void UnwrapImpl();
};
struct ScopedBindPBO final
: public ScopedGLWrapper<ScopedPackState>
{
friend struct ScopedGLWrapper<ScopedPackState>;
protected:
const GLenum mTarget;
const GLuint mPBO;
public:
ScopedBindPBO(GLContext* gl, GLenum target);
protected:
void UnwrapImpl();
};
} /* namespace gl */
} /* namespace mozilla */

View File

@ -295,45 +295,88 @@ SharedSurface_D3D11Interop::Create(DXInterop2Device* interop,
return nullptr;
}
GLuint rbGL = 0;
gl->fGenRenderbuffers(1, &rbGL);
const auto lockHandle = interop->RegisterObject(texD3D, rbGL, LOCAL_GL_RENDERBUFFER,
GLuint interopRB = 0;
gl->fGenRenderbuffers(1, &interopRB);
const auto lockHandle = interop->RegisterObject(texD3D, interopRB,
LOCAL_GL_RENDERBUFFER,
LOCAL_WGL_ACCESS_WRITE_DISCARD_NV);
if (!lockHandle) {
NS_WARNING("Failed to register D3D object with WGL.");
gl->fDeleteRenderbuffers(1, &rbGL);
gl->fDeleteRenderbuffers(1, &interopRB);
return nullptr;
}
////
GLuint prodTex = 0;
GLuint interopFB = 0;
{
GLint samples = 0;
{
const ScopedBindRenderbuffer bindRB(gl, interopRB);
gl->fGetRenderbufferParameteriv(LOCAL_GL_RENDERBUFFER,
LOCAL_GL_RENDERBUFFER_SAMPLES, &samples);
}
if (samples > 0) { // Intel
// Intel's dx_interop GL-side textures have SAMPLES=1, likely because that's
// what the D3DTextures technically have. However, SAMPLES=1 is very different
// from SAMPLES=0 in GL.
// For more, see https://bugzilla.mozilla.org/show_bug.cgi?id=1325835
// Our ShSurf tex or rb must be single-sampled.
gl->fGenTextures(1, &prodTex);
const ScopedBindTexture bindTex(gl, prodTex);
gl->TexParams_SetClampNoMips();
const GLenum format = (hasAlpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB);
const ScopedBindPBO nullPBO(gl, LOCAL_GL_PIXEL_UNPACK_BUFFER);
gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, format, size.width, size.height, 0,
format, LOCAL_GL_UNSIGNED_BYTE, nullptr);
gl->fGenFramebuffers(1, &interopFB);
ScopedBindFramebuffer bindFB(gl, interopFB);
gl->fFramebufferRenderbuffer(LOCAL_GL_FRAMEBUFFER, LOCAL_GL_COLOR_ATTACHMENT0,
LOCAL_GL_RENDERBUFFER, interopRB);
MOZ_ASSERT(gl->fCheckFramebufferStatus(LOCAL_GL_FRAMEBUFFER) ==
LOCAL_GL_FRAMEBUFFER_COMPLETE);
}
}
////
typedef SharedSurface_D3D11Interop ptrT;
UniquePtr<ptrT> ret ( new ptrT(gl, size, hasAlpha, rbGL, interop, lockHandle,
texD3D, dxgiHandle) );
UniquePtr<ptrT> ret ( new ptrT(gl, size, hasAlpha, prodTex, interopFB, interopRB,
interop, lockHandle, texD3D, dxgiHandle) );
return Move(ret);
}
SharedSurface_D3D11Interop::SharedSurface_D3D11Interop(GLContext* gl,
const gfx::IntSize& size,
bool hasAlpha, GLuint rbGL,
bool hasAlpha, GLuint prodTex,
GLuint interopFB, GLuint interopRB,
DXInterop2Device* interop,
HANDLE lockHandle,
ID3D11Texture2D* texD3D,
HANDLE dxgiHandle)
: SharedSurface(SharedSurfaceType::DXGLInterop2,
AttachmentType::GLRenderbuffer,
prodTex ? AttachmentType::GLTexture
: AttachmentType::GLRenderbuffer,
gl,
size,
hasAlpha,
true)
, mProdRB(rbGL)
, mProdTex(prodTex)
, mInteropFB(interopFB)
, mInteropRB(interopRB)
, mInterop(interop)
, mLockHandle(lockHandle)
, mTexD3D(texD3D)
, mDXGIHandle(dxgiHandle)
, mNeedsFinish(gfxPrefs::WebGLDXGLNeedsFinish())
, mLockedForGL(false)
{ }
{
MOZ_ASSERT(bool(mProdTex) == bool(mInteropFB));
}
SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop()
{
@ -346,7 +389,9 @@ SharedSurface_D3D11Interop::~SharedSurface_D3D11Interop()
NS_WARNING("Failed to release mLockHandle, possibly leaking it.");
}
mGL->fDeleteRenderbuffers(1, &mProdRB);
mGL->fDeleteTextures(1, &mProdTex);
mGL->fDeleteFramebuffers(1, &mInteropFB);
mGL->fDeleteRenderbuffers(1, &mInteropRB);
}
void
@ -365,6 +410,11 @@ SharedSurface_D3D11Interop::ProducerReleaseImpl()
{
MOZ_ASSERT(mLockedForGL);
if (mProdTex) {
mGL->BlitHelper()->DrawBlitTextureToFramebuffer(mProdTex, mInteropFB, mSize,
mSize);
}
if (mNeedsFinish) {
mGL->fFinish();
} else {

View File

@ -20,7 +20,9 @@ class SharedSurface_D3D11Interop
: public SharedSurface
{
public:
const GLuint mProdRB;
const GLuint mProdTex;
const GLuint mInteropFB;
const GLuint mInteropRB;
const RefPtr<DXInterop2Device> mInterop;
const HANDLE mLockHandle;
const RefPtr<ID3D11Texture2D> mTexD3D;
@ -45,7 +47,9 @@ protected:
SharedSurface_D3D11Interop(GLContext* gl,
const gfx::IntSize& size,
bool hasAlpha,
GLuint renderbufferGL,
GLuint prodTex,
GLuint interopFB,
GLuint interopRB,
DXInterop2Device* interop,
HANDLE lockHandle,
ID3D11Texture2D* texD3D,
@ -61,7 +65,13 @@ public:
virtual void ProducerReleaseImpl() override;
virtual GLuint ProdRenderbuffer() override {
return mProdRB;
MOZ_ASSERT(!mProdTex);
return mInteropRB;
}
virtual GLuint ProdTexture() override {
MOZ_ASSERT(mProdTex);
return mProdTex;
}
virtual bool ToSurfaceDescriptor(layers::SurfaceDescriptor* const out_descriptor) override;

View File

@ -764,7 +764,7 @@ AssertModuleScopesMatch(ModuleObject* module)
}
void
ModuleObject::fixEnvironmentsAfterCompartmentMerge(JSContext* cx)
ModuleObject::fixEnvironmentsAfterCompartmentMerge()
{
AssertModuleScopesMatch(this);
initialEnvironment().fixEnclosingEnvironmentAfterCompartmentMerge(script()->global());

View File

@ -237,7 +237,7 @@ class ModuleObject : public NativeObject
#ifdef DEBUG
static bool IsFrozen(JSContext* cx, HandleModuleObject self);
#endif
void fixEnvironmentsAfterCompartmentMerge(JSContext* cx);
void fixEnvironmentsAfterCompartmentMerge();
JSScript* script() const;
Scope* enclosingScope() const;

View File

@ -580,7 +580,7 @@ ResolvePromise(JSContext* cx, Handle<PromiseObject*> promise, HandleValue valueO
// Now that everything else is done, do the things the debugger needs.
// Step 7 of RejectPromise implemented in onSettled.
promise->onSettled(cx);
PromiseObject::onSettled(cx, promise);
// Step 7 of FulfillPromise.
// Step 8 of RejectPromise.
@ -2578,14 +2578,14 @@ PromiseObject::dependentPromises(JSContext* cx, MutableHandle<GCVector<Value>> v
return true;
}
bool
PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
/* static */ bool
PromiseObject::resolve(JSContext* cx, Handle<PromiseObject*> promise, HandleValue resolutionValue)
{
MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC));
if (state() != JS::PromiseState::Pending)
MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC));
if (promise->state() != JS::PromiseState::Pending)
return true;
RootedObject resolveFun(cx, GetResolveFunctionFromPromise(this));
RootedObject resolveFun(cx, GetResolveFunctionFromPromise(promise));
RootedValue funVal(cx, ObjectValue(*resolveFun));
// For xray'd Promises, the resolve fun may have been created in another
@ -2601,14 +2601,14 @@ PromiseObject::resolve(JSContext* cx, HandleValue resolutionValue)
return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
}
bool
PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
/* static */ bool
PromiseObject::reject(JSContext* cx, Handle<PromiseObject*> promise, HandleValue rejectionValue)
{
MOZ_ASSERT(!PromiseHasAnyFlag(*this, PROMISE_FLAG_ASYNC));
if (state() != JS::PromiseState::Pending)
MOZ_ASSERT(!PromiseHasAnyFlag(*promise, PROMISE_FLAG_ASYNC));
if (promise->state() != JS::PromiseState::Pending)
return true;
RootedValue funVal(cx, this->getFixedSlot(PromiseSlot_RejectFunction));
RootedValue funVal(cx, promise->getFixedSlot(PromiseSlot_RejectFunction));
MOZ_ASSERT(IsCallable(funVal));
FixedInvokeArgs<1> args(cx);
@ -2618,10 +2618,9 @@ PromiseObject::reject(JSContext* cx, HandleValue rejectionValue)
return Call(cx, funVal, UndefinedHandleValue, args, &dummy);
}
void
PromiseObject::onSettled(JSContext* cx)
/* static */ void
PromiseObject::onSettled(JSContext* cx, Handle<PromiseObject*> promise)
{
Rooted<PromiseObject*> promise(cx, this);
RootedObject stack(cx);
if (cx->options().asyncStack() || cx->compartment()->isDebuggee()) {
if (!JS::CaptureCurrentStack(cx, &stack, JS::StackCapture(JS::AllFrames()))) {

View File

@ -66,10 +66,12 @@ class PromiseObject : public NativeObject
return getFixedSlot(PromiseSlot_ReactionsOrResult);
}
MOZ_MUST_USE bool resolve(JSContext* cx, HandleValue resolutionValue);
MOZ_MUST_USE bool reject(JSContext* cx, HandleValue rejectionValue);
static MOZ_MUST_USE bool resolve(JSContext* cx, Handle<PromiseObject*> promise,
HandleValue resolutionValue);
static MOZ_MUST_USE bool reject(JSContext* cx, Handle<PromiseObject*> promise,
HandleValue rejectionValue);
void onSettled(JSContext* cx);
static void onSettled(JSContext* cx, Handle<PromiseObject*> promise);
double allocationTime() { return getFixedSlot(PromiseSlot_AllocationTime).toNumber(); }
double resolutionTime() { return getFixedSlot(PromiseSlot_ResolutionTime).toNumber(); }

View File

@ -671,6 +671,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
if (!NameFunctions(cx, pn))
return false;
// XDR the newly delazified function.
if (script->scriptSource()->hasEncoder() &&
!script->scriptSource()->xdrEncodeFunction(cx, fun, sourceObject))
{
return false;
}
return true;
}

View File

@ -1569,6 +1569,318 @@ BytecodeEmitter::TDZCheckCache::noteTDZCheck(BytecodeEmitter* bce, JSAtom* name,
return true;
}
class MOZ_STACK_CLASS TryEmitter
{
public:
enum Kind {
TryCatch,
TryCatchFinally,
TryFinally
};
enum ShouldUseRetVal {
UseRetVal,
DontUseRetVal
};
enum ShouldUseControl {
UseControl,
DontUseControl,
};
private:
BytecodeEmitter* bce_;
Kind kind_;
ShouldUseRetVal retValKind_;
// Track jumps-over-catches and gosubs-to-finally for later fixup.
//
// When a finally block is active, non-local jumps (including
// jumps-over-catches) result in a GOSUB being written into the bytecode
// stream and fixed-up later.
//
// If ShouldUseControl is DontUseControl, all that handling is skipped.
// DontUseControl is used by yield*, that matches to the following:
// * has only one catch block
// * has no catch guard
// * has JSOP_GOTO at the end of catch-block
// * has no non-local-jump
// * doesn't use finally block for normal completion of try-block and
// catch-block
Maybe<TryFinallyControl> controlInfo_;
int depth_;
unsigned noteIndex_;
ptrdiff_t tryStart_;
JumpList catchAndFinallyJump_;
JumpTarget tryEnd_;
JumpTarget finallyStart_;
enum State {
Start,
Try,
TryEnd,
Catch,
CatchEnd,
Finally,
FinallyEnd,
End
};
State state_;
bool hasCatch() const {
return kind_ == TryCatch || kind_ == TryCatchFinally;
}
bool hasFinally() const {
return kind_ == TryCatchFinally || kind_ == TryFinally;
}
public:
TryEmitter(BytecodeEmitter* bce, Kind kind, ShouldUseRetVal retValKind = UseRetVal,
ShouldUseControl controlKind = UseControl)
: bce_(bce),
kind_(kind),
retValKind_(retValKind),
depth_(0),
noteIndex_(0),
tryStart_(0),
state_(Start)
{
if (controlKind == UseControl)
controlInfo_.emplace(bce_, hasFinally() ? StatementKind::Finally : StatementKind::Try);
finallyStart_.offset = 0;
}
bool emitJumpOverCatchAndFinally() {
if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
return false;
return true;
}
bool emitTry() {
MOZ_ASSERT(state_ == Start);
// Since an exception can be thrown at any place inside the try block,
// we need to restore the stack and the scope chain before we transfer
// the control to the exception handler.
//
// For that we store in a try note associated with the catch or
// finally block the stack depth upon the try entry. The interpreter
// uses this depth to properly unwind the stack and the scope chain.
depth_ = bce_->stackDepth;
// Record the try location, then emit the try block.
if (!bce_->newSrcNote(SRC_TRY, &noteIndex_))
return false;
if (!bce_->emit1(JSOP_TRY))
return false;
tryStart_ = bce_->offset();
state_ = Try;
return true;
}
private:
bool emitTryEnd() {
MOZ_ASSERT(state_ == Try);
MOZ_ASSERT(depth_ == bce_->stackDepth);
// GOSUB to finally, if present.
if (hasFinally() && controlInfo_) {
if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
return false;
}
// Source note points to the jump at the end of the try block.
if (!bce_->setSrcNoteOffset(noteIndex_, 0, bce_->offset() - tryStart_ + JSOP_TRY_LENGTH))
return false;
// Emit jump over catch and/or finally.
if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
return false;
if (!bce_->emitJumpTarget(&tryEnd_))
return false;
return true;
}
public:
bool emitCatch() {
if (state_ == Try) {
if (!emitTryEnd())
return false;
} else {
MOZ_ASSERT(state_ == Catch);
if (!emitCatchEnd(true))
return false;
}
MOZ_ASSERT(bce_->stackDepth == depth_);
if (retValKind_ == UseRetVal) {
// Clear the frame's return value that might have been set by the
// try block:
//
// eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
if (!bce_->emit1(JSOP_UNDEFINED))
return false;
if (!bce_->emit1(JSOP_SETRVAL))
return false;
}
state_ = Catch;
return true;
}
private:
bool emitCatchEnd(bool hasNext) {
MOZ_ASSERT(state_ == Catch);
if (!controlInfo_)
return true;
// gosub <finally>, if required.
if (hasFinally()) {
if (!bce_->emitJump(JSOP_GOSUB, &controlInfo_->gosubs))
return false;
MOZ_ASSERT(bce_->stackDepth == depth_);
}
// Jump over the remaining catch blocks. This will get fixed
// up to jump to after catch/finally.
if (!bce_->emitJump(JSOP_GOTO, &catchAndFinallyJump_))
return false;
// If this catch block had a guard clause, patch the guard jump to
// come here.
if (controlInfo_->guardJump.offset != -1) {
if (!bce_->emitJumpTargetAndPatch(controlInfo_->guardJump))
return false;
controlInfo_->guardJump.offset = -1;
// If this catch block is the last one, rethrow, delegating
// execution of any finally block to the exception handler.
if (!hasNext) {
if (!bce_->emit1(JSOP_EXCEPTION))
return false;
if (!bce_->emit1(JSOP_THROW))
return false;
}
}
return true;
}
public:
bool emitFinally(Maybe<uint32_t> finallyPos = Nothing()) {
MOZ_ASSERT(hasFinally());
if (state_ == Try) {
if (!emitTryEnd())
return false;
} else {
MOZ_ASSERT(state_ == Catch);
if (!emitCatchEnd(false))
return false;
}
MOZ_ASSERT(bce_->stackDepth == depth_);
if (!bce_->emitJumpTarget(&finallyStart_))
return false;
if (controlInfo_) {
// Fix up the gosubs that might have been emitted before non-local
// jumps to the finally code.
bce_->patchJumpsToTarget(controlInfo_->gosubs, finallyStart_);
// Indicate that we're emitting a subroutine body.
controlInfo_->setEmittingSubroutine();
}
if (finallyPos) {
if (!bce_->updateSourceCoordNotes(finallyPos.value()))
return false;
}
if (!bce_->emit1(JSOP_FINALLY))
return false;
if (retValKind_ == UseRetVal) {
if (!bce_->emit1(JSOP_GETRVAL))
return false;
// Clear the frame's return value to make break/continue return
// correct value even if there's no other statement before them:
//
// eval("x: try { 1 } finally { break x; }"); // undefined, not 1
if (!bce_->emit1(JSOP_UNDEFINED))
return false;
if (!bce_->emit1(JSOP_SETRVAL))
return false;
}
state_ = Finally;
return true;
}
private:
bool emitFinallyEnd() {
MOZ_ASSERT(state_ == Finally);
if (retValKind_ == UseRetVal) {
if (!bce_->emit1(JSOP_SETRVAL))
return false;
}
if (!bce_->emit1(JSOP_RETSUB))
return false;
bce_->hasTryFinally = true;
return true;
}
public:
bool emitEnd() {
if (state_ == Catch) {
MOZ_ASSERT(!hasFinally());
if (!emitCatchEnd(false))
return false;
} else {
MOZ_ASSERT(state_ == Finally);
MOZ_ASSERT(hasFinally());
if (!emitFinallyEnd())
return false;
}
MOZ_ASSERT(bce_->stackDepth == depth_);
// ReconstructPCStack needs a NOP here to mark the end of the last
// catch block.
if (!bce_->emit1(JSOP_NOP))
return false;
// Fix up the end-of-try/catch jumps to come here.
if (!bce_->emitJumpTargetAndPatch(catchAndFinallyJump_))
return false;
// Add the try note last, to let post-order give us the right ordering
// (first to last for a given nesting level, inner to outer by level).
if (hasCatch()) {
if (!bce_->tryNoteList.append(JSTRY_CATCH, depth_, tryStart_, tryEnd_.offset))
return false;
}
// If we've got a finally, mark try+catch region with additional
// trynote to catch exceptions (re)thrown from a catch block or
// for the try{}finally{} case.
if (hasFinally()) {
if (!bce_->tryNoteList.append(JSTRY_FINALLY, depth_, tryStart_, finallyStart_.offset))
return false;
}
state_ = End;
return true;
}
};
class MOZ_STACK_CLASS IfThenElseEmitter
{
BytecodeEmitter* bce_;
@ -6085,57 +6397,28 @@ BytecodeEmitter::emitCatch(ParseNode* pn)
MOZ_NEVER_INLINE bool
BytecodeEmitter::emitTry(ParseNode* pn)
{
// Track jumps-over-catches and gosubs-to-finally for later fixup.
//
// When a finally block is active, non-local jumps (including
// jumps-over-catches) result in a GOSUB being written into the bytecode
// stream and fixed-up later.
//
TryFinallyControl controlInfo(this, pn->pn_kid3 ? StatementKind::Finally : StatementKind::Try);
ParseNode* catchList = pn->pn_kid2;
ParseNode* finallyNode = pn->pn_kid3;
// Since an exception can be thrown at any place inside the try block,
// we need to restore the stack and the scope chain before we transfer
// the control to the exception handler.
//
// For that we store in a try note associated with the catch or
// finally block the stack depth upon the try entry. The interpreter
// uses this depth to properly unwind the stack and the scope chain.
//
int depth = stackDepth;
// Record the try location, then emit the try block.
unsigned noteIndex;
if (!newSrcNote(SRC_TRY, &noteIndex))
return false;
if (!emit1(JSOP_TRY))
return false;
ptrdiff_t tryStart = offset();
if (!emitTree(pn->pn_kid1))
return false;
MOZ_ASSERT(depth == stackDepth);
// GOSUB to finally, if present.
if (pn->pn_kid3) {
if (!emitJump(JSOP_GOSUB, &controlInfo.gosubs))
return false;
TryEmitter::Kind kind;
if (catchList) {
if (finallyNode)
kind = TryEmitter::TryCatchFinally;
else
kind = TryEmitter::TryCatch;
} else {
MOZ_ASSERT(finallyNode);
kind = TryEmitter::TryFinally;
}
TryEmitter tryCatch(this, kind);
// Source note points to the jump at the end of the try block.
if (!setSrcNoteOffset(noteIndex, 0, offset() - tryStart + JSOP_TRY_LENGTH))
if (!tryCatch.emitTry())
return false;
// Emit jump over catch and/or finally.
JumpList catchJump;
if (!emitJump(JSOP_GOTO, &catchJump))
return false;
JumpTarget tryEnd;
if (!emitJumpTarget(&tryEnd))
if (!emitTree(pn->pn_kid1))
return false;
// If this try has a catch block, emit it.
ParseNode* catchList = pn->pn_kid2;
if (catchList) {
MOZ_ASSERT(catchList->isKind(PNK_CATCHLIST));
@ -6165,110 +6448,26 @@ BytecodeEmitter::emitTry(ParseNode* pn)
// capturing exceptions thrown from catch{} blocks.
//
for (ParseNode* pn3 = catchList->pn_head; pn3; pn3 = pn3->pn_next) {
MOZ_ASSERT(this->stackDepth == depth);
// Clear the frame's return value that might have been set by the
// try block:
//
// eval("try { 1; throw 2 } catch(e) {}"); // undefined, not 1
if (!emit1(JSOP_UNDEFINED))
return false;
if (!emit1(JSOP_SETRVAL))
if (!tryCatch.emitCatch())
return false;
// Emit the lexical scope and catch body.
MOZ_ASSERT(pn3->isKind(PNK_LEXICALSCOPE));
if (!emitTree(pn3))
return false;
// gosub <finally>, if required.
if (pn->pn_kid3) {
if (!emitJump(JSOP_GOSUB, &controlInfo.gosubs))
return false;
MOZ_ASSERT(this->stackDepth == depth);
}
// Jump over the remaining catch blocks. This will get fixed
// up to jump to after catch/finally.
if (!emitJump(JSOP_GOTO, &catchJump))
return false;
// If this catch block had a guard clause, patch the guard jump to
// come here.
if (controlInfo.guardJump.offset != -1) {
if (!emitJumpTargetAndPatch(controlInfo.guardJump))
return false;
controlInfo.guardJump.offset = -1;
// If this catch block is the last one, rethrow, delegating
// execution of any finally block to the exception handler.
if (!pn3->pn_next) {
if (!emit1(JSOP_EXCEPTION))
return false;
if (!emit1(JSOP_THROW))
return false;
}
}
}
}
MOZ_ASSERT(this->stackDepth == depth);
// Emit the finally handler, if there is one.
JumpTarget finallyStart{ 0 };
if (pn->pn_kid3) {
if (!emitJumpTarget(&finallyStart))
if (finallyNode) {
if (!tryCatch.emitFinally(Some(finallyNode->pn_pos.begin)))
return false;
// Fix up the gosubs that might have been emitted before non-local
// jumps to the finally code.
patchJumpsToTarget(controlInfo.gosubs, finallyStart);
// Indicate that we're emitting a subroutine body.
controlInfo.setEmittingSubroutine();
if (!updateSourceCoordNotes(pn->pn_kid3->pn_pos.begin))
if (!emitTree(finallyNode))
return false;
if (!emit1(JSOP_FINALLY))
return false;
if (!emit1(JSOP_GETRVAL))
return false;
// Clear the frame's return value to make break/continue return
// correct value even if there's no other statement before them:
//
// eval("x: try { 1 } finally { break x; }"); // undefined, not 1
if (!emit1(JSOP_UNDEFINED))
return false;
if (!emit1(JSOP_SETRVAL))
return false;
if (!emitTree(pn->pn_kid3))
return false;
if (!emit1(JSOP_SETRVAL))
return false;
if (!emit1(JSOP_RETSUB))
return false;
hasTryFinally = true;
MOZ_ASSERT(this->stackDepth == depth);
}
// ReconstructPCStack needs a NOP here to mark the end of the last catch block.
if (!emit1(JSOP_NOP))
return false;
// Fix up the end-of-try/catch jumps to come here.
if (!emitJumpTargetAndPatch(catchJump))
return false;
// Add the try note last, to let post-order give us the right ordering
// (first to last for a given nesting level, inner to outer by level).
if (catchList && !tryNoteList.append(JSTRY_CATCH, depth, tryStart, tryEnd.offset))
return false;
// If we've got a finally, mark try+catch region with additional
// trynote to catch exceptions (re)thrown from a catch block or
// for the try{}finally{} case.
if (pn->pn_kid3 && !tryNoteList.append(JSTRY_FINALLY, depth, tryStart, finallyStart.offset))
if (!tryCatch.emitEnd())
return false;
return true;
@ -8052,17 +8251,15 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
int depth = stackDepth;
MOZ_ASSERT(depth >= 2);
JumpList send;
if (!emitJump(JSOP_GOTO, &send)) // goto send
TryEmitter tryCatch(this, TryEmitter::TryCatchFinally, TryEmitter::DontUseRetVal,
TryEmitter::DontUseControl);
if (!tryCatch.emitJumpOverCatchAndFinally()) // ITER RESULT
return false;
// Try prologue. // ITER RESULT
unsigned noteIndex;
if (!newSrcNote(SRC_TRY, &noteIndex))
return false;
JumpTarget tryStart{ offset() };
if (!emit1(JSOP_TRY)) // tryStart:
if (!tryCatch.emitTry()) // ITER RESULT
return false;
MOZ_ASSERT(this->stackDepth == depth);
// Load the generator object.
@ -8073,17 +8270,9 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
if (!emitYieldOp(JSOP_YIELD)) // ITER RECEIVED
return false;
// Try epilogue.
if (!setSrcNoteOffset(noteIndex, 0, offset() - tryStart.offset))
return false;
if (!emitJump(JSOP_GOTO, &send)) // goto send
if (!tryCatch.emitCatch()) // ITER RESULT
return false;
JumpTarget tryEnd;
if (!emitJumpTarget(&tryEnd)) // tryEnd:
return false;
// Catch location.
stackDepth = uint32_t(depth); // ITER RESULT
if (!emit1(JSOP_EXCEPTION)) // ITER RESULT EXCEPTION
return false;
@ -8130,36 +8319,19 @@ BytecodeEmitter::emitYieldStar(ParseNode* iter, ParseNode* gen)
if (!emitJump(JSOP_GOTO, &checkResult)) // goto checkResult
return false;
// The finally block, IteratorClose logic.
// IteratorClose logic.
if (!tryCatch.emitFinally())
return false;
JumpTarget finallyStart{ 0 };
if (!emitJumpTarget(&finallyStart))
return false;
if (!emit1(JSOP_FINALLY)) // ITER RESULT FTYPE FVALUE
return false;
if (!emitDupAt(3)) // ITER RESULT FTYPE FVALUE ITER
return false;
if (!emitIteratorClose(Some(tryStart))) // ITER RESULT FTYPE FVALUE
return false;
if (!emit1(JSOP_RETSUB)) // ITER RESULT
return false;
// Catch and finally epilogue.
// This is a peace offering to ReconstructPCStack. See the note in EmitTry.
if (!emit1(JSOP_NOP))
return false;
size_t tryStartOffset = tryStart.offset + JSOP_TRY_LENGTH;
if (!tryNoteList.append(JSTRY_CATCH, depth, tryStartOffset, tryEnd.offset))
return false;
if (!tryNoteList.append(JSTRY_FINALLY, depth, tryStartOffset, finallyStart.offset))
if (!tryCatch.emitEnd())
return false;
// After the try-catch-finally block: send the received value to the iterator.
if (!emitJumpTargetAndPatch(send)) // send:
return false;
// Send location.
// result = iter.next(received) // ITER RECEIVED
if (!emit1(JSOP_SWAP)) // RECEIVED ITER
return false;

View File

@ -3130,7 +3130,7 @@ Parser<ParseHandler>::functionDefinition(Node pn, InHandling inHandling,
RootedObject proto(context);
if (generatorKind == StarGenerator) {
// If we are off the main thread, the generator meta-objects have
// already been created by js::StartOffThreadParseScript, so cx will not
// already been created by js::StartOffThreadParseTask, so cx will not
// be necessary.
JSContext* cx = context->maybeJSContext();
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, context->global());

View File

@ -7,13 +7,21 @@ function evalWithCache(code, ctx) {
});
code = code instanceof Object ? code : cacheEntry(code);
var incremental = ctx.incremental || false;
// We create a new global ...
if (!("global" in ctx))
ctx.global = newGlobal({ cloneSingletons: true });
ctx.global = newGlobal({ cloneSingletons: !incremental });
if (!("isRunOnce" in ctx))
ctx.isRunOnce = true;
var ctx_save;
if (incremental)
ctx_save = Object.create(ctx, {saveIncrementalBytecode: { value: true } });
else
ctx_save = Object.create(ctx, {saveBytecode: { value: true } });
// Fetch the verification function from the evaluation context. This function
// is used to assert the state of the script/function after each run of the
// evaluate function.
@ -23,11 +31,11 @@ function evalWithCache(code, ctx) {
// might cause the program to run differently, and thus to have a different
// set of functions executed.
ctx.global.generation = 0;
var res1 = evaluate(code, Object.create(ctx, {saveBytecode: { value: true } }));
var res1 = evaluate(code, ctx_save);
checkAfter(ctx);
ctx.global.generation = 1;
var res2 = evaluate(code, Object.create(ctx, {loadBytecode: { value: true }, saveBytecode: { value: true } }));
var res2 = evaluate(code, Object.create(ctx_save, {loadBytecode: { value: true } }));
checkAfter(ctx);
ctx.global.generation = 2;

View File

@ -0,0 +1,59 @@
if (helperThreadCount() == 0)
quit();
function evalWithCacheLoadOffThread(code, ctx) {
ctx = ctx || {};
ctx = Object.create(ctx, {
fileName: { value: "evalWithCacheCode.js" },
lineNumber: { value: 0 }
});
code = code instanceof Object ? code : cacheEntry(code);
var incremental = ctx.incremental || false;
// We create a new global ...
if (!("global" in ctx))
ctx.global = newGlobal({ cloneSingletons: !incremental });
var ctx_save;
if (incremental)
ctx_save = Object.create(ctx, {saveIncrementalBytecode: { value: true } });
else
ctx_save = Object.create(ctx, {saveBytecode: { value: true } });
ctx.global.generation = 0;
evaluate(code, ctx_save);
offThreadDecodeScript(code, ctx);
ctx.global.eval(`runOffThreadDecodedScript()`);
}
var test;
// Decode a constant.
test = `
1;
`;
evalWithCacheLoadOffThread(test, {});
evalWithCacheLoadOffThread(test, { incremental: true });
// Decode object literals.
test = `
var obj = { a: 1, b: 2 };
obj.a++;
assertEq(obj.a, 2);
`;
evalWithCacheLoadOffThread(test, {});
evalWithCacheLoadOffThread(test, { incremental: true });
// Decode functions.
test = `
function g() {
return function f() { return 1; };
};
assertEq(g()(), 1);
`;
evalWithCacheLoadOffThread(test, {});
evalWithCacheLoadOffThread(test, { incremental: true });

View File

@ -0,0 +1,68 @@
load(libdir + 'bytecode-cache.js');
var test = "";
// Check that without cloneSingleton, we can safely encode object literal which
// have mutations.
test = `
var obj = { a: 1, b: 2 };
obj.a++;
assertEq(obj.a, 2);
`;
evalWithCache(test, {
incremental: true,
assertEqBytecode: true,
assertEqResult : true
});
// Encode lazy functions.
test = `
function f() { return 1; };
1;
`;
evalWithCache(test, {
incremental: true,
assertEqBytecode: true,
assertEqResult : true
});
// Encode delazified functions.
test = `
function f() { return 1; };
f();
`;
evalWithCache(test, {
incremental: true,
assertEqBytecode: true,
assertEqResult : true
});
// Encode inner functions.
test = `
function g() {
return function f() { return 1; };
};
g()();
`;
evalWithCache(test, {
incremental: true,
assertEqBytecode: true,
assertEqResult : true
});
// Extra zeal GCs can cause isRelazifiableFunction() to become true after we
// record its value by throwing away JIT code for the function.
gczeal(0);
// Relazified functions are encoded as non-lazy-script, when the encoding is
// incremental.
test = `
assertEq(isLazyFunction(f), generation == 0 || generation == 3);
function f() { return isRelazifiableFunction(f); };
expect = f();
assertEq(isLazyFunction(f), false);
relazifyFunctions(f);
assertEq(isLazyFunction(f), expect);
`;
evalWithCache(test, { incremental: true });

View File

@ -4074,7 +4074,7 @@ AnalyzePoppedThis(JSContext* cx, ObjectGroup* group,
// Add the property to the object, being careful not to update type information.
DebugOnly<unsigned> slotSpan = baseobj->slotSpan();
MOZ_ASSERT(!baseobj->containsPure(id));
if (!baseobj->addDataProperty(cx, id, baseobj->slotSpan(), JSPROP_ENUMERATE))
if (!NativeObject::addDataProperty(cx, baseobj, id, baseobj->slotSpan(), JSPROP_ENUMERATE))
return false;
MOZ_ASSERT(baseobj->slotSpan() != slotSpan);
MOZ_ASSERT(!baseobj->inDictionaryMode());

View File

@ -7919,7 +7919,9 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
// Don't generate a fast path if there have been bounds check failures
// and this access might be on a sparse property.
if (ElementAccessHasExtraIndexedProperty(this, obj) && failedBoundsCheck_) {
bool hasExtraIndexedProperty;
MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
if (hasExtraIndexedProperty && failedBoundsCheck_) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return Ok();
}
@ -7938,7 +7940,7 @@ IonBuilder::getElemTryDense(bool* emitted, MDefinition* obj, MDefinition* index)
return Ok();
}
JSObject*
AbortReasonOr<JSObject*>
IonBuilder::getStaticTypedArrayObject(MDefinition* obj, MDefinition* index)
{
Scalar::Type arrayType;
@ -7952,7 +7954,9 @@ IonBuilder::getStaticTypedArrayObject(MDefinition* obj, MDefinition* index)
return nullptr;
}
if (ElementAccessHasExtraIndexedProperty(this, obj)) {
bool hasExtraIndexedProperty;
MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
if (hasExtraIndexedProperty) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return nullptr;
}
@ -7982,7 +7986,8 @@ IonBuilder::getElemTryTypedStatic(bool* emitted, MDefinition* obj, MDefinition*
{
MOZ_ASSERT(*emitted == false);
JSObject* tarrObj = getStaticTypedArrayObject(obj, index);
JSObject* tarrObj;
MOZ_TRY_VAR(tarrObj, getStaticTypedArrayObject(obj, index));
if (!tarrObj)
return Ok();
@ -8312,9 +8317,12 @@ IonBuilder::jsop_getelem_dense(MDefinition* obj, MDefinition* index, JSValueType
// Reads which are on holes in the object do not have to bail out if
// undefined values have been observed at this access site and the access
// cannot hit another indexed property on the object or its prototypes.
bool readOutOfBounds =
types->hasType(TypeSet::UndefinedType()) &&
!ElementAccessHasExtraIndexedProperty(this, obj);
bool readOutOfBounds = false;
if (types->hasType(TypeSet::UndefinedType())) {
bool hasExtraIndexedProperty;
MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
readOutOfBounds = !hasExtraIndexedProperty;
}
MIRType knownType = MIRType::Value;
if (unboxedType == JSVAL_TYPE_MAGIC && barrier == BarrierKind::NoBarrier)
@ -8796,7 +8804,8 @@ IonBuilder::setElemTryTypedStatic(bool* emitted, MDefinition* object,
{
MOZ_ASSERT(*emitted == false);
JSObject* tarrObj = getStaticTypedArrayObject(object, index);
JSObject* tarrObj;
MOZ_TRY_VAR(tarrObj, getStaticTypedArrayObject(object, index));
if (!tarrObj)
return Ok();
@ -8896,7 +8905,9 @@ IonBuilder::setElemTryDense(bool* emitted, MDefinition* object,
// Don't generate a fast path if there have been bounds check failures
// and this access might be on a sparse property.
if (ElementAccessHasExtraIndexedProperty(this, object) && failedBoundsCheck_) {
bool hasExtraIndexedProperty;
MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, object));
if (hasExtraIndexedProperty && failedBoundsCheck_) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return Ok();
}
@ -8959,7 +8970,8 @@ IonBuilder::setElemTryCache(bool* emitted, MDefinition* object,
// from them. If TI can guard that there are no indexed properties on the prototype
// chain, we know that we anen't missing any setters by overwriting the hole with
// another value.
bool guardHoles = ElementAccessHasExtraIndexedProperty(this, object);
bool guardHoles;
MOZ_TRY_VAR(guardHoles, ElementAccessHasExtraIndexedProperty(this, object));
// Make sure the object being written to doesn't have copy on write elements.
const Class* clasp = object->resultTypeSet() ? object->resultTypeSet()->getKnownClass(constraints()) : nullptr;
@ -9001,11 +9013,12 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
// Writes which are on holes in the object do not have to bail out if they
// cannot hit another indexed property on the object or its prototypes.
bool hasNoExtraIndexedProperty = !ElementAccessHasExtraIndexedProperty(this, obj);
bool hasExtraIndexedProperty;
MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
bool mayBeFrozen = ElementAccessMightBeFrozen(constraints(), obj);
if (mayBeFrozen && !hasNoExtraIndexedProperty) {
if (mayBeFrozen && hasExtraIndexedProperty) {
// FallibleStoreElement does not know how to deal with extra indexed
// properties on the prototype. This case should be rare so we fall back
// to an IC.
@ -9062,7 +9075,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
// fallback to MFallibleStoreElement.
MInstruction* store;
MStoreElementCommon* common = nullptr;
if (writeHole && hasNoExtraIndexedProperty && !mayBeFrozen) {
if (writeHole && !hasExtraIndexedProperty && !mayBeFrozen) {
MStoreElementHole* ins = MStoreElementHole::New(alloc(), obj, elements, id, newValue, unboxedType);
store = ins;
common = ins;
@ -9070,7 +9083,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
current->add(ins);
current->push(value);
} else if (mayBeFrozen) {
MOZ_ASSERT(hasNoExtraIndexedProperty,
MOZ_ASSERT(!hasExtraIndexedProperty,
"FallibleStoreElement codegen assumes no extra indexed properties");
bool strict = IsStrictSetPC(pc);
@ -9085,7 +9098,7 @@ IonBuilder::jsop_setelem_dense(TemporaryTypeSet::DoubleConversion conversion,
MInstruction* initLength = initializedLength(obj, elements, unboxedType);
id = addBoundsCheck(id, initLength);
bool needsHoleCheck = !packed && !hasNoExtraIndexedProperty;
bool needsHoleCheck = !packed && hasExtraIndexedProperty;
if (unboxedType != JSVAL_TYPE_MAGIC) {
store = storeUnboxedValue(obj, elements, 0, id, unboxedType, newValue);
@ -12355,7 +12368,9 @@ IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
return Ok();
}
if (ElementAccessHasExtraIndexedProperty(this, obj))
bool hasExtraIndexedProperty;
MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
if (hasExtraIndexedProperty)
return Ok();
*emitted = true;

View File

@ -386,7 +386,7 @@ class IonBuilder
const LinearSum& byteOffset,
ReferenceTypeDescr::Type type,
PropertyName* name);
JSObject* getStaticTypedArrayObject(MDefinition* obj, MDefinition* index);
AbortReasonOr<JSObject*> getStaticTypedArrayObject(MDefinition* obj, MDefinition* index);
// jsop_setelem() helpers.
AbortReasonOr<Ok> setElemTryTypedArray(bool* emitted, MDefinition* object,

View File

@ -630,7 +630,9 @@ IonBuilder::inlineArrayPopShift(CallInfo& callInfo, MArrayPopShift::Mode mode)
return InliningStatus_NotInlined;
}
if (ArrayPrototypeHasIndexedProperty(this, script())) {
bool hasIndexedProperty;
MOZ_TRY_VAR(hasIndexedProperty, ArrayPrototypeHasIndexedProperty(this, script()));
if (hasIndexedProperty) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
@ -767,7 +769,9 @@ IonBuilder::inlineArrayPush(CallInfo& callInfo)
return InliningStatus_NotInlined;
}
if (ArrayPrototypeHasIndexedProperty(this, script())) {
bool hasIndexedProperty;
MOZ_TRY_VAR(hasIndexedProperty, ArrayPrototypeHasIndexedProperty(this, script()));
if (hasIndexedProperty) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
@ -859,7 +863,9 @@ IonBuilder::inlineArraySlice(CallInfo& callInfo)
}
// Watch out for indexed properties on the prototype.
if (ArrayPrototypeHasIndexedProperty(this, script())) {
bool hasIndexedProperty;
MOZ_TRY_VAR(hasIndexedProperty, ArrayPrototypeHasIndexedProperty(this, script()));
if (hasIndexedProperty) {
trackOptimizationOutcome(TrackedOutcome::ProtoIndexedProps);
return InliningStatus_NotInlined;
}
@ -2128,7 +2134,9 @@ IonBuilder::inlineDefineDataProperty(CallInfo& callInfo)
MDefinition* id = callInfo.getArg(1);
MDefinition* value = callInfo.getArg(2);
if (ElementAccessHasExtraIndexedProperty(this, obj))
bool hasExtraIndexedProperty;
MOZ_TRY_VAR(hasExtraIndexedProperty, ElementAccessHasExtraIndexedProperty(this, obj));
if (hasExtraIndexedProperty)
return InliningStatus_NotInlined;
// setElemTryDense will push the value as the result of the define instead

View File

@ -5998,7 +5998,7 @@ jit::ElementAccessMightBeFrozen(CompilerConstraintList* constraints, MDefinition
return !types || types->hasObjectFlags(constraints, OBJECT_FLAG_FROZEN);
}
bool
AbortReasonOr<bool>
jit::ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj)
{
TemporaryTypeSet* types = obj->resultTypeSet();
@ -6335,7 +6335,7 @@ jit::AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name,
}
}
static bool
AbortReasonOr<bool>
PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj)
{
do {
@ -6348,13 +6348,15 @@ PrototypeHasIndexedProperty(IonBuilder* builder, JSObject* obj)
if (index.nonData(builder->constraints()) || index.isOwnProperty(builder->constraints()))
return true;
obj = obj->staticPrototype();
if (!builder->alloc().ensureBallast())
return builder->abort(AbortReason::Alloc);
} while (obj);
return false;
}
// Whether Array.prototype, or an object on its proto chain, has an indexed property.
bool
AbortReasonOr<bool>
jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script)
{
if (JSObject* proto = script->global().maybeGetArrayPrototype())
@ -6363,7 +6365,7 @@ jit::ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script)
}
// Whether obj or any of its prototypes have an indexed property.
bool
AbortReasonOr<bool>
jit::TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types)
{
const Class* clasp = types->getKnownClass(builder->constraints());

View File

@ -14199,7 +14199,8 @@ bool ElementAccessIsTypedArray(CompilerConstraintList* constraints,
bool ElementAccessIsPacked(CompilerConstraintList* constraints, MDefinition* obj);
bool ElementAccessMightBeCopyOnWrite(CompilerConstraintList* constraints, MDefinition* obj);
bool ElementAccessMightBeFrozen(CompilerConstraintList* constraints, MDefinition* obj);
bool ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj);
AbortReasonOr<bool>
ElementAccessHasExtraIndexedProperty(IonBuilder* builder, MDefinition* obj);
MIRType DenseNativeElementType(CompilerConstraintList* constraints, MDefinition* obj);
BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx,
CompilerConstraintList* constraints,
@ -14224,8 +14225,10 @@ bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList*
MBasicBlock* current, MDefinition** pobj,
PropertyName* name, MDefinition** pvalue,
bool canModify, MIRType implicitType = MIRType::None);
bool ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script);
bool TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types);
AbortReasonOr<bool>
ArrayPrototypeHasIndexedProperty(IonBuilder* builder, JSScript* script);
AbortReasonOr<bool>
TypeCanHaveExtraIndexedProperties(IonBuilder* builder, TemporaryTypeSet* types);
} // namespace jit
} // namespace js

View File

@ -226,11 +226,13 @@ struct VMFunction
return count;
}
constexpr
VMFunction(void* wrapped, const char* name, uint32_t explicitArgs, uint32_t argumentProperties,
uint32_t argumentPassedInFloatRegs, uint64_t argRootTypes,
DataType outParam, RootType outParamRootType, DataType returnType,
uint32_t extraValuesToPop = 0, MaybeTailCall expectTailCall = NonTailCall)
: wrapped(wrapped),
: next(nullptr),
wrapped(wrapped),
name_(name),
explicitArgs(explicitArgs),
argumentProperties(argumentProperties),
@ -241,15 +243,26 @@ struct VMFunction
outParamRootType(outParamRootType),
extraValuesToPop(extraValuesToPop),
expectTailCall(expectTailCall)
{ }
VMFunction(const VMFunction& o)
: next(nullptr),
wrapped(o.wrapped),
name_(o.name_),
explicitArgs(o.explicitArgs),
argumentProperties(o.argumentProperties),
argumentPassedInFloatRegs(o.argumentPassedInFloatRegs),
outParam(o.outParam),
returnType(o.returnType),
argumentRootTypes(o.argumentRootTypes),
outParamRootType(o.outParamRootType),
extraValuesToPop(o.extraValuesToPop),
expectTailCall(o.expectTailCall)
{
// Check for valid failure/return type.
MOZ_ASSERT_IF(outParam != Type_Void, returnType == Type_Bool);
MOZ_ASSERT(returnType == Type_Bool ||
returnType == Type_Object);
}
VMFunction(const VMFunction& o) {
*this = o;
addToFunctions();
}

View File

@ -4166,6 +4166,31 @@ JS::CancelOffThreadModule(JSContext* cx, void* token)
HelperThreadState().cancelParseTask(cx, ParseTaskKind::Module, token);
}
JS_PUBLIC_API(bool)
JS::DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
OffThreadCompileCallback callback, void* callbackData)
{
MOZ_ASSERT(CanCompileOffThread(cx, options, buffer.length() - cursor));
return StartOffThreadDecodeScript(cx, options, buffer, cursor, callback, callbackData);
}
JS_PUBLIC_API(JSScript*)
JS::FinishOffThreadScriptDecoder(JSContext* cx, void* token)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
return HelperThreadState().finishScriptDecodeTask(cx, token);
}
JS_PUBLIC_API(void)
JS::CancelOffThreadScriptDecoder(JSContext* cx, void* token)
{
MOZ_ASSERT(cx);
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx));
HelperThreadState().cancelParseTask(cx, ParseTaskKind::ScriptDecode, token);
}
JS_PUBLIC_API(bool)
JS_CompileScript(JSContext* cx, const char* ascii, size_t length,
const JS::CompileOptions& options, MutableHandleScript script)
@ -4890,25 +4915,25 @@ JS::CallOriginalPromiseReject(JSContext* cx, JS::HandleValue rejectionValue)
}
JS_PUBLIC_API(bool)
JS::ResolvePromise(JSContext* cx, JS::HandleObject promise, JS::HandleValue resolutionValue)
JS::ResolvePromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue resolutionValue)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, promise, resolutionValue);
assertSameCompartment(cx, promiseObj, resolutionValue);
MOZ_ASSERT(promise->is<PromiseObject>());
return promise->as<PromiseObject>().resolve(cx, resolutionValue);
Handle<PromiseObject*> promise = promiseObj.as<PromiseObject>();
return PromiseObject::resolve(cx, promise, resolutionValue);
}
JS_PUBLIC_API(bool)
JS::RejectPromise(JSContext* cx, JS::HandleObject promise, JS::HandleValue rejectionValue)
JS::RejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue rejectionValue)
{
AssertHeapIsIdle(cx);
CHECK_REQUEST(cx);
assertSameCompartment(cx, promise, rejectionValue);
assertSameCompartment(cx, promiseObj, rejectionValue);
MOZ_ASSERT(promise->is<PromiseObject>());
return promise->as<PromiseObject>().reject(cx, rejectionValue);
Handle<PromiseObject*> promise = promiseObj.as<PromiseObject>();
return PromiseObject::reject(cx, promise, rejectionValue);
}
JS_PUBLIC_API(JSObject*)
@ -6755,6 +6780,26 @@ JS::DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer,
return decoder.resultCode();
}
JS_PUBLIC_API(bool)
JS::StartIncrementalEncoding(JSContext* cx, TranscodeBuffer& buffer, JS::HandleScript script)
{
if (!script)
return false;
if (!script->scriptSource()->xdrEncodeTopLevel(cx, buffer, script))
return false;
return true;
}
JS_PUBLIC_API(bool)
JS::FinishIncrementalEncoding(JSContext* cx, JS::HandleScript script)
{
if (!script)
return false;
if (!script->scriptSource()->xdrFinalizeEncoder())
return false;
return true;
}
JS_PUBLIC_API(void)
JS::SetBuildIdOp(JSContext* cx, JS::BuildIdOp buildIdOp)
{

View File

@ -3772,7 +3772,7 @@ namespace JS {
* addrefs/copies/tracing/etc.
*
* Furthermore, in some cases compile options are propagated from one entity to
* another (e.g. from a scriipt to a function defined in that script). This
* another (e.g. from a script to a function defined in that script). This
* involves copying over some, but not all, of the options.
*
* So, we have a class hierarchy that reflects these four use cases:
@ -4212,6 +4212,17 @@ FinishOffThreadModule(JSContext* cx, void* token);
extern JS_PUBLIC_API(void)
CancelOffThreadModule(JSContext* cx, void* token);
extern JS_PUBLIC_API(bool)
DecodeOffThreadScript(JSContext* cx, const ReadOnlyCompileOptions& options,
mozilla::Vector<uint8_t>& buffer /* TranscodeBuffer& */, size_t cursor,
OffThreadCompileCallback callback, void* callbackData);
extern JS_PUBLIC_API(JSScript*)
FinishOffThreadScriptDecoder(JSContext* cx, void* token);
extern JS_PUBLIC_API(void)
CancelOffThreadScriptDecoder(JSContext* cx, void* token);
/**
* Compile a function with envChain plus the global as its scope chain.
* envChain must contain objects in the current compartment of cx. The actual
@ -4587,7 +4598,7 @@ CallOriginalPromiseReject(JSContext* cx, JS::HandleValue rejectionValue);
* the Promise was created.
*/
extern JS_PUBLIC_API(bool)
ResolvePromise(JSContext* cx, JS::HandleObject promise, JS::HandleValue resolutionValue);
ResolvePromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue resolutionValue);
/**
* Rejects the given `promise` with the given `rejectionValue`.
@ -4596,7 +4607,7 @@ ResolvePromise(JSContext* cx, JS::HandleObject promise, JS::HandleValue resoluti
* the Promise was created.
*/
extern JS_PUBLIC_API(bool)
RejectPromise(JSContext* cx, JS::HandleObject promise, JS::HandleValue rejectionValue);
RejectPromise(JSContext* cx, JS::HandleObject promiseObj, JS::HandleValue rejectionValue);
/**
* Calls the current compartment's original Promise.prototype.then on the
@ -5987,8 +5998,10 @@ enum TranscodeResult
TranscodeResult_Failure_RunOnceNotSupported = TranscodeResult_Failure | 0x2,
TranscodeResult_Failure_AsmJSNotSupported = TranscodeResult_Failure | 0x3,
TranscodeResult_Failure_UnknownClassKind = TranscodeResult_Failure | 0x4,
TranscodeResult_Failure_WrongCompileOption = TranscodeResult_Failure | 0x5,
TranscodeResult_Failure_NotInterpretedFun = TranscodeResult_Failure | 0x6,
// A error, the JSContext has a pending exception.
// There is a pending exception on the context.
TranscodeResult_Throw = 0x200
};
@ -6006,6 +6019,25 @@ extern JS_PUBLIC_API(TranscodeResult)
DecodeInterpretedFunction(JSContext* cx, TranscodeBuffer& buffer, JS::MutableHandleFunction funp,
size_t cursorIndex = 0);
// Register an encoder on the given script source, such that all functions can
// be encoded as they are parsed. This strategy is used to avoid blocking the
// main thread in a non-interruptible way.
//
// The |script| argument of |StartIncrementalEncoding| and
// |FinishIncrementalEncoding| should be the top-level script returned either as
// an out-param of any of the |Compile| functions, or the result of
// |FinishOffThreadScript|.
//
// The |buffer| argument should not be used before until
// |FinishIncrementalEncoding| is called on the same script, and returns
// successfully. If any of these functions failed, the |buffer| content is
// undefined.
extern JS_PUBLIC_API(bool)
StartIncrementalEncoding(JSContext* cx, TranscodeBuffer& buffer, JS::HandleScript script);
extern JS_PUBLIC_API(bool)
FinishIncrementalEncoding(JSContext* cx, JS::HandleScript script);
} /* namespace JS */
namespace js {

View File

@ -595,7 +595,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
uint32_t length = lengthAndEncoding >> 1;
bool latin1 = lengthAndEncoding & 0x1;
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
JSAtom* atom;
if (latin1) {
const Latin1Char* chars = nullptr;
@ -624,7 +624,7 @@ js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
* most allocations here will be bigger than tempLifoAlloc's default
* chunk size.
*/
chars = cx->runtime()->pod_malloc<char16_t>(length);
chars = cx->pod_malloc<char16_t>(length);
if (!chars)
return false;
}

View File

@ -549,7 +549,7 @@ fun_resolve(JSContext* cx, HandleObject obj, HandleId id, bool* resolvedp)
template<XDRMode mode>
bool
js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
HandleScript enclosingScript, MutableHandleFunction objp)
HandleScriptSource sourceObject, MutableHandleFunction objp)
{
enum FirstWordFlag {
HasAtom = 0x1,
@ -563,21 +563,15 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
uint32_t firstword = 0; /* bitmask of FirstWordFlag */
uint32_t flagsword = 0; /* word for argument count and fun->flags */
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
RootedFunction fun(cx);
RootedScript script(cx);
Rooted<LazyScript*> lazy(cx);
if (mode == XDR_ENCODE) {
fun = objp;
if (!fun->isInterpreted()) {
JSAutoByteString funNameBytes;
if (const char* name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr,
JSMSG_NOT_SCRIPTED_FUNCTION, name);
}
return false;
}
if (!fun->isInterpreted())
return xdr->fail(JS::TranscodeResult_Failure_NotInterpretedFun);
if (fun->explicitName() || fun->hasCompileTimeName() || fun->hasGuessedAtom())
firstword |= HasAtom;
@ -609,6 +603,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
fun->environment() == nullptr);
}
// Everything added below can substituted by the non-lazy-script version of
// this function later.
js::AutoXDRTree funTree(xdr, xdr->getTreeKey(fun));
if (!xdr->codeUint32(&firstword))
return false;
@ -620,7 +618,11 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
if (mode == XDR_DECODE) {
RootedObject proto(cx);
if (firstword & IsStarGenerator) {
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
// If we are off the main thread, the generator meta-objects have
// already been created by js::StartOffThreadParseTask, so
// JSContext* will not be necessary.
JSContext* context = cx->maybeJSContext();
proto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(context, cx->global());
if (!proto)
return false;
}
@ -637,10 +639,10 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
}
if (firstword & IsLazy) {
if (!XDRLazyScript(xdr, enclosingScope, enclosingScript, fun, &lazy))
if (!XDRLazyScript(xdr, enclosingScope, sourceObject, fun, &lazy))
return false;
} else {
if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script))
if (!XDRScript(xdr, enclosingScope, sourceObject, fun, &script))
return false;
}
@ -665,10 +667,12 @@ js::XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
}
template bool
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, MutableHandleFunction);
js::XDRInterpretedFunction(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource,
MutableHandleFunction);
template bool
js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleScope, HandleScript, MutableHandleFunction);
js::XDRInterpretedFunction(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource,
MutableHandleFunction);
/* ES6 (04-25-16) 19.2.3.6 Function.prototype [ @@hasInstance ] */
bool

View File

@ -841,7 +841,7 @@ JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPring);
template<XDRMode mode>
bool
XDRInterpretedFunction(XDRState<mode>* xdr, HandleScope enclosingScope,
HandleScript enclosingScript, MutableHandleFunction objp);
HandleScriptSource sourceObject, MutableHandleFunction objp);
/*
* Report an error that call.thisv is not compatible with the specified class,

View File

@ -1143,7 +1143,7 @@ js::CloneObject(JSContext* cx, HandleObject obj, Handle<js::TaggedProto> proto)
}
static bool
GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
GetScriptArrayObjectElements(ExclusiveContext* cx, HandleObject obj, MutableHandle<GCVector<Value>> values)
{
MOZ_ASSERT(!obj->isSingleton());
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
@ -1161,7 +1161,7 @@ GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVe
}
static bool
GetScriptPlainObjectProperties(JSContext* cx, HandleObject obj,
GetScriptPlainObjectProperties(ExclusiveContext* cx, HandleObject obj,
MutableHandle<IdValueVector> properties)
{
if (obj->is<PlainObject>()) {
@ -1339,9 +1339,8 @@ js::XDRObjectLiteral(XDRState<mode>* xdr, MutableHandleObject obj)
{
/* NB: Keep this in sync with DeepCloneObjectLiteral. */
JSContext* cx = xdr->cx();
MOZ_ASSERT_IF(mode == XDR_ENCODE && obj->isSingleton(),
cx->compartment()->behaviors().getSingletonsAsTemplates());
ExclusiveContext* cx = xdr->cx();
assertSameCompartment(cx, obj);
// Distinguish between objects and array classes.
uint32_t isArray = 0;

View File

@ -73,7 +73,7 @@ template<XDRMode mode>
bool
js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
/*
* A script constant can be an arbitrary primitive value as they are used
@ -197,7 +197,7 @@ template<XDRMode mode>
static bool
XDRLazyClosedOverBindings(XDRState<mode>* xdr, MutableHandle<LazyScript*> lazy)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
RootedAtom atom(cx);
for (size_t i = 0; i < lazy->numClosedOverBindings(); i++) {
uint8_t endOfScopeSentinel;
@ -230,7 +230,7 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript());
MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
uint64_t packedFields;
{
@ -255,7 +255,8 @@ XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript scri
return false;
if (mode == XDR_DECODE) {
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
RootedScriptSource sourceObject(cx, &script->scriptSourceUnwrap());
lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, sourceObject,
packedFields, begin, end, lineno, column));
// As opposed to XDRLazyScript, we need to restore the runtime bits
@ -297,8 +298,9 @@ enum XDRClassKind {
template<XDRMode mode>
bool
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScript enclosingScript,
HandleFunction fun, MutableHandleScript scriptp)
js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
HandleScriptSource sourceObjectArg, HandleFunction fun,
MutableHandleScript scriptp)
{
/* NB: Keep this in sync with CopyScript. */
@ -341,17 +343,16 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
uint32_t scriptBits = 0;
uint32_t bodyScopeIndex = 0;
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
RootedScript script(cx);
natoms = nsrcnotes = 0;
nconsts = nobjects = nscopes = nregexps = ntrynotes = nscopenotes = nyieldoffsets = 0;
if (mode == XDR_ENCODE) {
script = scriptp.get();
MOZ_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
MOZ_ASSERT(script->functionNonDelazifying() == fun);
if (!fun && script->treatAsRunOnce()) {
if (!fun && script->treatAsRunOnce() && script->hasRunOnce()) {
// This is a toplevel or eval script that's runOnce. We want to
// make sure that we're not XDR-saving an object we emitted for
// JSOP_OBJECT that then got modified. So throw if we're not
@ -424,7 +425,8 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
scriptBits |= (1 << FunctionHasThisBinding);
if (script->functionHasExtraBodyVarScope())
scriptBits |= (1 << FunctionHasExtraBodyVarScope);
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
MOZ_ASSERT_IF(sourceObjectArg, sourceObjectArg->source() == script->scriptSource());
if (!sourceObjectArg)
scriptBits |= (1 << OwnSource);
if (script->isGeneratorExp())
scriptBits |= (1 << IsGeneratorExp);
@ -485,15 +487,32 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
if (!xdr->codeUint32(&scriptBits))
return false;
MOZ_ASSERT(!!(scriptBits & (1 << OwnSource)) == !sourceObjectArg);
RootedScriptSource sourceObject(cx, sourceObjectArg);
if (mode == XDR_DECODE) {
JSVersion version_ = JSVersion(version);
MOZ_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
CompileOptions options(cx);
options.setVersion(version_)
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
RootedScriptSource sourceObject(cx);
// 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.
mozilla::Maybe<CompileOptions> options;
if (xdr->hasOptions()) {
options.emplace(xdr->cx(), xdr->options());
if (options->version != version_ ||
options->noScriptRval != !!(scriptBits & (1 << NoScriptRval)) ||
options->selfHostingMode != !!(scriptBits & (1 << SelfHosted)))
{
return xdr->fail(JS::TranscodeResult_Failure_WrongCompileOption);
}
} else {
options.emplace(xdr->cx()->asJSContext());
(*options).setVersion(version_)
.setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
.setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
}
if (scriptBits & (1 << OwnSource)) {
ScriptSource* ss = cx->new_<ScriptSource>();
if (!ss)
@ -506,22 +525,22 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
* ScriptSourceObject, and those that are (element; elementAttributeName)
* aren't preserved by XDR. So this can be simple.
*/
CompileOptions options(cx);
ss->initFromOptions(cx, options);
ss->initFromOptions(cx, *options);
sourceObject = ScriptSourceObject::create(cx, ss);
if (!sourceObject ||
!ScriptSourceObject::initFromOptions(cx, sourceObject, options))
return false;
} else {
MOZ_ASSERT(enclosingScript);
// When decoding, all the scripts and the script source object
// are in the same compartment, so the script's source object
// should never be a cross-compartment wrapper.
MOZ_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
if (xdr->hasScriptSourceObjectOut()) {
// When the ScriptSourceObjectOut is provided by ParseTask, it
// is stored in a location which is traced by the GC.
*xdr->scriptSourceObjectOut() = sourceObject;
} else {
if (!sourceObject ||
!ScriptSourceObject::initFromOptions(cx->asJSContext(), sourceObject, *options))
{
return false;
}
}
}
script = JSScript::Create(cx, options, sourceObject, 0, 0);
script = JSScript::Create(cx, *options, sourceObject, 0, 0);
if (!script)
return false;
@ -529,6 +548,10 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
// decoded may iterate the static scope chain.
if (fun)
fun->initScript(script);
} else {
// When encoding, we do not mutate any of the JSScript or LazyScript, so
// we can safely unwrap it here.
sourceObject = &script->scriptSourceUnwrap();
}
if (mode == XDR_DECODE) {
@ -599,7 +622,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
if (scriptBits & (1 << OwnSource)) {
if (!script->scriptSource()->performXDR<mode>(xdr))
if (!sourceObject->source()->performXDR<mode>(xdr))
return false;
}
if (!xdr->codeUint32(&script->sourceStart_))
@ -829,7 +852,7 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
RootedFunction tmp(cx);
if (mode == XDR_ENCODE)
tmp = &(*objp)->as<JSFunction>();
if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
if (!XDRInterpretedFunction(xdr, funEnclosingScope, sourceObject, &tmp))
return false;
*objp = tmp;
break;
@ -899,27 +922,28 @@ js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope, HandleScrip
scriptp.set(script);
/* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
if (!fun)
Debugger::onNewScript(cx, script);
if (!fun && cx->isJSContext())
Debugger::onNewScript(cx->asJSContext(), script);
}
return true;
}
template bool
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript, HandleFunction,
js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource, HandleFunction,
MutableHandleScript);
template bool
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript, HandleFunction,
js::XDRScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource, HandleFunction,
MutableHandleScript);
template<XDRMode mode>
bool
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
HandleFunction fun, MutableHandle<LazyScript*> lazy)
js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
HandleScriptSource sourceObject, HandleFunction fun,
MutableHandle<LazyScript*> lazy)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
{
uint32_t begin;
@ -950,7 +974,7 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
}
if (mode == XDR_DECODE) {
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, sourceObject,
packedFields, begin, end, lineno, column));
if (!lazy)
return false;
@ -983,11 +1007,11 @@ js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript
}
template bool
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScript,
js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleScope, HandleScriptSource,
HandleFunction, MutableHandle<LazyScript*>);
template bool
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScript,
js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleScope, HandleScriptSource,
HandleFunction, MutableHandle<LazyScript*>);
void
@ -1888,6 +1912,64 @@ ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
info->numScripts++;
}
bool
ScriptSource::xdrEncodeTopLevel(ExclusiveContext* cx, JS::TranscodeBuffer& buffer,
HandleScript script)
{
xdrEncoder_ = js::MakeUnique<XDRIncrementalEncoder>(cx, buffer, buffer.length());
if (!xdrEncoder_) {
ReportOutOfMemory(cx);
return false;
}
MOZ_ASSERT(hasEncoder());
auto failureCase = mozilla::MakeScopeExit([&] {
xdrEncoder_.reset(nullptr);
});
if (!xdrEncoder_->init()) {
ReportOutOfMemory(cx);
return false;
}
RootedScript s(cx, script);
if (!xdrEncoder_->codeScript(&s))
return false;
failureCase.release();
return true;
}
bool
ScriptSource::xdrEncodeFunction(ExclusiveContext* cx, HandleFunction fun, HandleScriptSource sourceObject)
{
MOZ_ASSERT(sourceObject->source() == this);
MOZ_ASSERT(hasEncoder());
auto failureCase = mozilla::MakeScopeExit([&] {
xdrEncoder_.reset(nullptr);
});
RootedFunction f(cx, fun);
if (!xdrEncoder_->codeFunction(&f, sourceObject))
return false;
failureCase.release();
return true;
}
bool
ScriptSource::xdrFinalizeEncoder()
{
MOZ_ASSERT(hasEncoder());
auto cleanup = mozilla::MakeScopeExit([&] {
xdrEncoder_.reset(nullptr);
});
if (!xdrEncoder_->linearize())
return false;
return true;
}
template<XDRMode mode>
bool
ScriptSource::performXDR(XDRState<mode>* xdr)
@ -2027,7 +2109,10 @@ ScriptSource::performXDR(XDRState<mode>* xdr)
const char* fn = filename();
if (!xdr->codeCString(&fn))
return false;
if (mode == XDR_DECODE && !setFilename(xdr->cx(), fn))
// Note: If the decoder has an option, then the filename is defined by
// the CompileOption from the document.
MOZ_ASSERT_IF(mode == XDR_DECODE && xdr->hasOptions(), filename());
if (mode == XDR_DECODE && !xdr->hasOptions() && !setFilename(xdr->cx(), fn))
return false;
}
@ -4071,7 +4156,7 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
/* static */ LazyScript*
LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
HandleScript script, HandleScope enclosingScope,
HandleScript enclosingScript,
HandleScriptSource sourceObject,
uint64_t packedFields, uint32_t begin, uint32_t end,
uint32_t lineno, uint32_t column)
{
@ -4101,11 +4186,11 @@ LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
// values should only be non-null if we have a non-lazy enclosing script.
// AddLazyFunctionsForCompartment relies on the source object being null
// if we're nested inside another lazy function.
MOZ_ASSERT(!!enclosingScript == !!enclosingScope);
MOZ_ASSERT(!!sourceObject == !!enclosingScope);
MOZ_ASSERT(!res->sourceObject());
MOZ_ASSERT(!res->enclosingScope());
if (enclosingScript)
res->setEnclosingScopeAndSource(enclosingScope, &enclosingScript->scriptSourceUnwrap());
if (sourceObject)
res->setEnclosingScopeAndSource(enclosingScope, sourceObject);
MOZ_ASSERT(!res->hasScript());
if (script)

View File

@ -432,6 +432,11 @@ class ScriptSource
// memory management.
const char* introductionType_;
// The bytecode cache encoder is used to encode only the content of function
// which are delazified. If this value is not nullptr, then each delazified
// function should be recorded before their first execution.
UniquePtr<XDRIncrementalEncoder> xdrEncoder_;
// True if we can call JSRuntime::sourceHook to load the source on
// demand. If sourceRetrievable_ and hasSourceData() are false, it is not
// possible to get source at all.
@ -453,6 +458,7 @@ class ScriptSource
parameterListEnd_(0),
introducerFilename_(nullptr),
introductionType_(nullptr),
xdrEncoder_(nullptr),
sourceRetrievable_(false),
hasIntroductionOffset_(false)
{
@ -579,6 +585,29 @@ class ScriptSource
uint32_t parameterListEnd() const {
return parameterListEnd_;
}
// Return wether an XDR encoder is present or not.
bool hasEncoder() const { return bool(xdrEncoder_); }
// Create a new XDR encoder, and encode the top-level JSScript. The result
// of the encoding would be available in the |buffer| provided as argument,
// as soon as |xdrFinalize| is called and all xdr function calls returned
// successfully.
bool xdrEncodeTopLevel(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, HandleScript script);
// Encode a delazified JSFunction. In case of errors, the XDR encoder is
// freed and the |buffer| provided as argument to |xdrEncodeTopLevel| is
// considered undefined.
//
// The |sourceObject| argument is the object holding the current
// ScriptSource.
bool xdrEncodeFunction(ExclusiveContext* cx, HandleFunction fun,
HandleScriptSource sourceObject);
// Linearize the encoded content in the |buffer| provided as argument to
// |xdrEncodeTopLevel|, and free the XDR encoder. In case of errors, the
// |buffer| is considered undefined.
bool xdrFinalizeEncoder();
};
class ScriptSourceHolder
@ -683,12 +712,12 @@ AsyncKindFromBits(unsigned val) {
*/
template<XDRMode mode>
bool
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
HandleFunction fun, MutableHandleScript scriptp);
template<XDRMode mode>
bool
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScript enclosingScript,
XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope, HandleScriptSource sourceObject,
HandleFunction fun, MutableHandle<LazyScript*> lazy);
/*
@ -796,7 +825,7 @@ class JSScript : public js::gc::TenuredCell
friend
bool
js::XDRScript(js::XDRState<mode>* xdr, js::HandleScope enclosingScope,
js::HandleScript enclosingScript, js::HandleFunction fun,
js::HandleScriptSource sourceObject, js::HandleFunction fun,
js::MutableHandleScript scriptp);
friend bool
@ -1175,11 +1204,11 @@ class JSScript : public js::gc::TenuredCell
return offsetof(JSScript, funLength_);
}
size_t sourceStart() const {
uint32_t sourceStart() const {
return sourceStart_;
}
size_t sourceEnd() const {
uint32_t sourceEnd() const {
return sourceEnd_;
}
@ -2011,11 +2040,11 @@ class LazyScript : public gc::TenuredCell
// The "script" argument to this function can be null. If it's non-null,
// then this LazyScript should be associated with the given JSScript.
//
// The enclosingScript and enclosingScope arguments may be null if the
// The sourceObject and enclosingScope arguments may be null if the
// enclosing function is also lazy.
static LazyScript* Create(ExclusiveContext* cx, HandleFunction fun,
HandleScript script, HandleScope enclosingScope,
HandleScript enclosingScript,
HandleScriptSource sourceObject,
uint64_t packedData, uint32_t begin, uint32_t end,
uint32_t lineno, uint32_t column);

View File

@ -2998,8 +2998,8 @@ StringObject::assignInitialShape(ExclusiveContext* cx, Handle<StringObject*> obj
{
MOZ_ASSERT(obj->empty());
return obj->addDataProperty(cx, cx->names().length, LENGTH_SLOT,
JSPROP_PERMANENT | JSPROP_READONLY);
return NativeObject::addDataProperty(cx, obj, cx->names().length, LENGTH_SLOT,
JSPROP_PERMANENT | JSPROP_READONLY);
}
JSObject*
@ -3011,7 +3011,10 @@ js::InitStringClass(JSContext* cx, HandleObject obj)
Rooted<JSString*> empty(cx, cx->runtime()->emptyString);
RootedObject proto(cx, GlobalObject::createBlankPrototype(cx, global, &StringObject::class_));
if (!proto || !proto->as<StringObject>().init(cx, empty))
if (!proto)
return nullptr;
Handle<StringObject*> protoObj = proto.as<StringObject>();
if (!StringObject::init(cx, protoObj, empty))
return nullptr;
/* Now create the String function. */

View File

@ -170,6 +170,7 @@ struct ShellAsyncTasks
enum class ScriptKind
{
Script,
DecodeScript,
Module
};
@ -204,14 +205,32 @@ class OffThreadState {
return true;
}
bool startIfIdle(JSContext* cx, ScriptKind kind,
JS::TranscodeBuffer&& newXdr)
{
AutoLockMonitor alm(monitor);
if (state != IDLE)
return false;
MOZ_ASSERT(!token);
xdr = mozilla::Move(newXdr);
scriptKind = kind;
state = COMPILING;
return true;
}
void abandon(JSContext* cx) {
AutoLockMonitor alm(monitor);
MOZ_ASSERT(state == COMPILING);
MOZ_ASSERT(!token);
MOZ_ASSERT(source);
MOZ_ASSERT(source || !xdr.empty());
js_free(source);
if (source)
js_free(source);
source = nullptr;
xdr.clearAndFree();
state = IDLE;
}
@ -220,7 +239,7 @@ class OffThreadState {
AutoLockMonitor alm(monitor);
MOZ_ASSERT(state == COMPILING);
MOZ_ASSERT(!token);
MOZ_ASSERT(source);
MOZ_ASSERT(source || !xdr.empty());
MOZ_ASSERT(newToken);
token = newToken;
@ -238,9 +257,11 @@ class OffThreadState {
alm.wait();
}
MOZ_ASSERT(source);
js_free(source);
MOZ_ASSERT(source || !xdr.empty());
if (source)
js_free(source);
source = nullptr;
xdr.clearAndFree();
MOZ_ASSERT(token);
void* holdToken = token;
@ -249,12 +270,15 @@ class OffThreadState {
return holdToken;
}
JS::TranscodeBuffer& xdrBuffer() { return xdr; }
private:
Monitor monitor;
ScriptKind scriptKind;
State state;
void* token;
char16_t* source;
JS::TranscodeBuffer xdr;
};
#ifdef SINGLESTEP_PROFILING
@ -1563,6 +1587,14 @@ ConvertTranscodeResultToJSException(JSContext* cx, JS::TranscodeResult rv)
MOZ_ASSERT(!cx->isExceptionPending());
JS_ReportErrorASCII(cx, "Unknown class kind, go fix it.");
return false;
case JS::TranscodeResult_Failure_WrongCompileOption:
MOZ_ASSERT(!cx->isExceptionPending());
JS_ReportErrorASCII(cx, "Compile options differs from Compile options of the encoding");
return false;
case JS::TranscodeResult_Failure_NotInterpretedFun:
MOZ_ASSERT(!cx->isExceptionPending());
JS_ReportErrorASCII(cx, "Only interepreted functions are supported by XDR");
return false;
case JS::TranscodeResult_Throw:
MOZ_ASSERT(cx->isExceptionPending());
@ -1604,6 +1636,7 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
bool catchTermination = false;
bool loadBytecode = false;
bool saveBytecode = false;
bool saveIncrementalBytecode = false;
bool assertEqBytecode = false;
RootedObject callerGlobal(cx, cx->global());
@ -1667,6 +1700,11 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
if (!v.isUndefined())
saveBytecode = ToBoolean(v);
if (!JS_GetProperty(cx, opts, "saveIncrementalBytecode", &v))
return false;
if (!v.isUndefined())
saveIncrementalBytecode = ToBoolean(v);
if (!JS_GetProperty(cx, opts, "assertEqBytecode", &v))
return false;
if (!v.isUndefined())
@ -1674,12 +1712,17 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
// We cannot load or save the bytecode if we have no object where the
// bytecode cache is stored.
if (loadBytecode || saveBytecode) {
if (loadBytecode || saveBytecode || saveIncrementalBytecode) {
if (!cacheEntry) {
JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
"evaluate");
return false;
}
if (saveIncrementalBytecode && saveBytecode) {
JS_ReportErrorASCII(cx, "saveIncrementalBytecode and saveBytecode cannot be used"
" at the same time.");
return false;
}
}
}
@ -1757,6 +1800,15 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
if (!script->scriptSource()->setSourceMapURL(cx, smurl))
return false;
}
// If we want to save the bytecode incrementally, then we should
// register ahead the fact that every JSFunction which is being
// delazified should be encoded at the end of the delazification.
if (saveIncrementalBytecode) {
if (!StartIncrementalEncoding(cx, saveBuffer, script))
return false;
}
if (!JS_ExecuteScript(cx, script, args.rval())) {
if (catchTermination && !JS_IsExceptionPending(cx)) {
JSAutoCompartment ac1(cx, callerGlobal);
@ -1769,14 +1821,22 @@ Evaluate(JSContext* cx, unsigned argc, Value* vp)
return false;
}
// Encode the bytecode after the execution of the script.
if (saveBytecode) {
JS::TranscodeResult rv = JS::EncodeScript(cx, saveBuffer, script);
if (!ConvertTranscodeResultToJSException(cx, rv))
return false;
}
// Serialize the encoded bytecode, recorded before the execution, into a
// buffer which can be deserialized linearly.
if (saveIncrementalBytecode) {
if (!FinishIncrementalEncoding(cx, script))
return false;
}
}
if (saveBytecode) {
if (saveBytecode || saveIncrementalBytecode) {
// If we are both loading and saving, we assert that we are going to
// replace the current bytecode by the same stream of bytes.
if (loadBytecode && assertEqBytecode) {
@ -4373,6 +4433,109 @@ FinishOffThreadModule(JSContext* cx, unsigned argc, Value* vp)
return true;
}
static bool
OffThreadDecodeScript(JSContext* cx, unsigned argc, Value* vp)
{
if (!CanUseExtraThreads()) {
JS_ReportErrorASCII(cx, "Can't use offThreadDecodeScript with --no-threads");
return false;
}
CallArgs args = CallArgsFromVp(argc, vp);
if (args.length() < 1) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
"offThreadDecodeScript", "0", "s");
return false;
}
if (!args[0].isObject() || !CacheEntry_isCacheEntry(&args[0].toObject())) {
const char* typeName = InformalValueTypeName(args[0]);
JS_ReportErrorASCII(cx, "expected cache entry, got %s", typeName);
return false;
}
RootedObject cacheEntry(cx, &args[0].toObject());
JSAutoByteString fileNameBytes;
CompileOptions options(cx);
options.setIntroductionType("js shell offThreadDecodeScript")
.setFileAndLine("<string>", 1);
if (args.length() >= 2) {
if (args[1].isPrimitive()) {
JS_ReportErrorNumberASCII(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
"evaluate");
return false;
}
RootedObject opts(cx, &args[1].toObject());
if (!ParseCompileOptions(cx, options, opts, fileNameBytes))
return false;
}
// These option settings must override whatever the caller requested.
options.setIsRunOnce(true)
.setSourceIsLazy(false);
// We assume the caller wants caching if at all possible, ignoring
// heuristics that make sense for a real browser.
options.forceAsync = true;
JS::TranscodeBuffer loadBuffer;
uint32_t loadLength = 0;
uint8_t* loadData = nullptr;
loadData = CacheEntry_getBytecode(cacheEntry, &loadLength);
if (!loadData)
return false;
if (!loadBuffer.append(loadData, loadLength)) {
JS_ReportOutOfMemory(cx);
return false;
}
if (!JS::CanCompileOffThread(cx, options, loadLength)) {
JS_ReportErrorASCII(cx, "cannot compile code on worker thread");
return false;
}
ShellContext* sc = GetShellContext(cx);
if (!sc->offThreadState.startIfIdle(cx, ScriptKind::DecodeScript, mozilla::Move(loadBuffer))) {
JS_ReportErrorASCII(cx, "called offThreadDecodeScript without calling "
"runOffThreadDecodedScript to receive prior off-thread compilation");
return false;
}
if (!JS::DecodeOffThreadScript(cx, options, sc->offThreadState.xdrBuffer(), 0,
OffThreadCompileScriptCallback, sc))
{
sc->offThreadState.abandon(cx);
return false;
}
args.rval().setUndefined();
return true;
}
static bool
runOffThreadDecodedScript(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
if (OffThreadParsingMustWaitForGC(cx))
gc::FinishGC(cx);
ShellContext* sc = GetShellContext(cx);
void* token = sc->offThreadState.waitUntilDone(cx, ScriptKind::DecodeScript);
if (!token) {
JS_ReportErrorASCII(cx, "called runOffThreadDecodedScript when no compilation is pending");
return false;
}
RootedScript script(cx, JS::FinishOffThreadScriptDecoder(cx, token));
if (!script)
return false;
return JS_ExecuteScript(cx, script, args.rval());
}
struct MOZ_RAII FreeOnReturn
{
JSContext* cx;
@ -5953,6 +6116,19 @@ static const JSFunctionSpecWithHelp shell_functions[] = {
" Wait for off-thread compilation to complete. If an error occurred,\n"
" throw the appropriate exception; otherwise, return the module object"),
JS_FN_HELP("offThreadDecodeScript", OffThreadDecodeScript, 1, 0,
"offThreadDecodeScript(cacheEntry[, options])",
" Decode |code| on a helper thread. To wait for the compilation to finish\n"
" and run the code, call |runOffThreadScript|. If present, |options| may\n"
" have properties saying how the code should be compiled.\n"
" (see also offThreadCompileScript)\n"),
JS_FN_HELP("runOffThreadDecodedScript", runOffThreadDecodedScript, 0, 0,
"runOffThreadDecodedScript()",
" Wait for off-thread decoding to complete. If an error occurred,\n"
" throw the appropriate exception; otherwise, run the script and return\n"
" its value."),
JS_FN_HELP("timeout", Timeout, 1, 0,
"timeout([seconds], [func])",
" Get/Set the limit in seconds for the execution time for the current context.\n"

View File

@ -105,6 +105,10 @@ def parse_args():
help='Get tests from the given file.')
input_og.add_option('-x', '--exclude-file', action='append',
help='Exclude tests from the given file.')
input_og.add_option('--include', action='append', dest='requested_paths', default=[],
help='Include the given test file or directory.')
input_og.add_option('--exclude', action='append', dest='excluded_paths', default=[],
help='Exclude the given test file or directory.')
input_og.add_option('-d', '--exclude-random', dest='random',
action='store_false',
help='Exclude tests marked as "random."')
@ -160,7 +164,7 @@ def parse_args():
# Acquire the JS shell given on the command line.
options.js_shell = None
requested_paths = set()
requested_paths = set(options.requested_paths)
if len(args) > 0:
options.js_shell = abspath(args[0])
requested_paths |= set(args[1:])
@ -209,9 +213,10 @@ def parse_args():
requested_paths |= set(
[line.strip() for line in open(test_file).readlines()])
excluded_paths = set(options.excluded_paths)
# If files with lists of tests to exclude were specified, add them to the
# excluded tests set.
excluded_paths = set()
if options.exclude_file:
for filename in options.exclude_file:
try:

View File

@ -328,7 +328,7 @@ def _apply_external_manifests(filename, testcase, entries, xul_tester):
_parse_one(testcase, xul_tester)
def _is_test_file(path_from_root, basename, filename, requested_paths,
excluded_paths):
excluded_files, excluded_dirs):
# Any file whose basename matches something in this set is ignored.
EXCLUDED = set(('browser.js', 'shell.js', 'template.js',
'user.js', 'sta.js',
@ -351,17 +351,36 @@ def _is_test_file(path_from_root, basename, filename, requested_paths,
return False
# Skip excluded tests.
if filename in excluded_paths:
if filename in excluded_files:
return False
for dir in excluded_dirs:
if filename.startswith(dir + '/'):
return False
return True
def _split_files_and_dirs(location, paths):
"""Split up a set of paths into files and directories"""
files, dirs = set(), set()
for path in paths:
fullpath = os.path.join(location, path)
if path.endswith('/'):
dirs.add(path[0:-1])
elif os.path.isdir(fullpath):
dirs.add(path)
elif os.path.exists(fullpath):
files.add(path)
return files, dirs
def count_tests(location, requested_paths, excluded_paths):
count = 0
excluded_files, excluded_dirs = _split_files_and_dirs(location, excluded_paths)
for root, basename in _find_all_js_files(location, location):
filename = os.path.join(root, basename)
if _is_test_file(root, basename, filename, requested_paths, excluded_paths):
if _is_test_file(root, basename, filename, requested_paths, excluded_files, excluded_dirs):
count += 1
return count
@ -378,10 +397,12 @@ def load_reftests(location, requested_paths, excluded_paths, xul_tester, reldir=
manifestFile = os.path.join(location, 'jstests.list')
externalManifestEntries = _parse_external_manifest(manifestFile, '')
excluded_files, excluded_dirs = _split_files_and_dirs(location, excluded_paths)
for root, basename in _find_all_js_files(location, location):
# Get the full path and relative location of the file.
filename = os.path.join(root, basename)
if not _is_test_file(root, basename, filename, requested_paths, excluded_paths):
if not _is_test_file(root, basename, filename, requested_paths, excluded_files, excluded_dirs):
continue
# Skip empty files.

View File

@ -29,11 +29,11 @@ js::ErrorObject::assignInitialShape(ExclusiveContext* cx, Handle<ErrorObject*> o
{
MOZ_ASSERT(obj->empty());
if (!obj->addDataProperty(cx, cx->names().fileName, FILENAME_SLOT, 0))
if (!NativeObject::addDataProperty(cx, obj, cx->names().fileName, FILENAME_SLOT, 0))
return nullptr;
if (!obj->addDataProperty(cx, cx->names().lineNumber, LINENUMBER_SLOT, 0))
if (!NativeObject::addDataProperty(cx, obj, cx->names().lineNumber, LINENUMBER_SLOT, 0))
return nullptr;
return obj->addDataProperty(cx, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0);
return NativeObject::addDataProperty(cx, obj, cx->names().columnNumber, COLUMNNUMBER_SLOT, 0);
}
/* static */ bool
@ -57,7 +57,7 @@ js::ErrorObject::init(JSContext* cx, Handle<ErrorObject*> obj, JSExnType type,
// |new Error()|.
RootedShape messageShape(cx);
if (message) {
messageShape = obj->addDataProperty(cx, cx->names().message, MESSAGE_SLOT, 0);
messageShape = NativeObject::addDataProperty(cx, obj, cx->names().message, MESSAGE_SLOT, 0);
if (!messageShape)
return false;
MOZ_ASSERT(messageShape->slot() == MESSAGE_SLOT);

View File

@ -21,6 +21,7 @@
#include "vm/SharedImmutableStringsCache.h"
#include "vm/Time.h"
#include "vm/TraceLogging.h"
#include "vm/Xdr.h"
#include "jscntxtinlines.h"
#include "jscompartmentinlines.h"
@ -301,6 +302,18 @@ ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusi
{
}
ParseTask::ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
JS::OffThreadCompileCallback callback, void* callbackData)
: kind(kind), cx(cx), options(initCx), buffer(&buffer), cursor(cursor),
alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
exclusiveContextGlobal(exclusiveContextGlobal),
callback(callback), callbackData(callbackData),
script(nullptr), sourceObject(nullptr),
errors(cx), overRecursed(false), outOfMemory(false)
{
}
bool
ParseTask::init(JSContext* cx, const ReadOnlyCompileOptions& options)
{
@ -386,6 +399,29 @@ ModuleParseTask::parse()
script = module->script();
}
ScriptDecodeTask::ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
JS::OffThreadCompileCallback callback, void* callbackData)
: ParseTask(ParseTaskKind::ScriptDecode, cx, exclusiveContextGlobal, initCx,
buffer, cursor, callback, callbackData)
{
}
void
ScriptDecodeTask::parse()
{
RootedScript resultScript(cx);
XDROffThreadDecoder decoder(cx, alloc, &options, /* sourceObjectOut = */ &sourceObject,
*buffer, cursor);
decoder.codeScript(&resultScript);
MOZ_ASSERT(bool(resultScript) == (decoder.resultCode() == JS::TranscodeResult_Ok));
if (decoder.resultCode() == JS::TranscodeResult_Ok) {
script = resultScript.get();
} else {
sourceObject = nullptr;
}
}
void
js::CancelOffThreadParses(JSRuntime* rt)
{
@ -554,10 +590,10 @@ QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
return true;
}
template <typename TaskFunctor>
bool
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
StartOffThreadParseTask(JSContext* cx, const ReadOnlyCompileOptions& options,
ParseTaskKind kind, TaskFunctor& taskFunctor)
{
// Suppress GC so that calls below do not trigger a new incremental GC
// which could require barriers on the atoms compartment.
@ -565,7 +601,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Script, nogc);
JSObject* global = CreateGlobalForOffThreadParse(cx, kind, nogc);
if (!global)
return false;
@ -575,9 +611,7 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
if (!helpercx)
return false;
ScopedJSDeletePtr<ParseTask> task(
cx->new_<ScriptParseTask>(helpercx.get(), global, cx, chars, length,
callback, callbackData));
ScopedJSDeletePtr<ParseTask> task(taskFunctor(helpercx.get(), global));
if (!task)
return false;
@ -591,41 +625,41 @@ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& optio
return true;
}
bool
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
{
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptParseTask* {
return cx->new_<ScriptParseTask>(helpercx, global, cx, chars, length,
callback, callbackData);
};
return StartOffThreadParseTask(cx, options, ParseTaskKind::Script, functor);
}
bool
js::StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
{
// Suppress GC so that calls below do not trigger a new incremental GC
// which could require barriers on the atoms compartment.
gc::AutoSuppressGC nogc(cx);
gc::AutoAssertNoNurseryAlloc noNurseryAlloc(cx->runtime());
AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ModuleParseTask* {
return cx->new_<ModuleParseTask>(helpercx, global, cx, chars, length,
callback, callbackData);
};
return StartOffThreadParseTask(cx, options, ParseTaskKind::Module, functor);
}
JSObject* global = CreateGlobalForOffThreadParse(cx, ParseTaskKind::Module, nogc);
if (!global)
return false;
ScopedJSDeletePtr<ExclusiveContext> helpercx(
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
ExclusiveContext::Context_Exclusive, cx->options()));
if (!helpercx)
return false;
ScopedJSDeletePtr<ParseTask> task(
cx->new_<ModuleParseTask>(helpercx.get(), global, cx, chars, length,
callback, callbackData));
if (!task)
return false;
helpercx.forget();
if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
return false;
task.forget();
return true;
bool
js::StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
JS::TranscodeBuffer& buffer, size_t cursor,
JS::OffThreadCompileCallback callback, void* callbackData)
{
auto functor = [&](ExclusiveContext* helpercx, JSObject* global) -> ScriptDecodeTask* {
return cx->new_<ScriptDecodeTask>(helpercx, global, cx, buffer, cursor,
callback, callbackData);
};
return StartOffThreadParseTask(cx, options, ParseTaskKind::ScriptDecode, functor);
}
void
@ -1275,6 +1309,14 @@ GlobalHelperThreadState::finishScriptParseTask(JSContext* cx, void* token)
return script;
}
JSScript*
GlobalHelperThreadState::finishScriptDecodeTask(JSContext* cx, void* token)
{
JSScript* script = finishParseTask(cx, ParseTaskKind::ScriptDecode, token);
MOZ_ASSERT_IF(script, script->isGlobalCode());
return script;
}
JSObject*
GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
{
@ -1285,7 +1327,7 @@ GlobalHelperThreadState::finishModuleParseTask(JSContext* cx, void* token)
MOZ_ASSERT(script->module());
RootedModuleObject module(cx, script->module());
module->fixEnvironmentsAfterCompartmentMerge(cx);
module->fixEnvironmentsAfterCompartmentMerge();
if (!ModuleObject::Freeze(cx, module))
return nullptr;

View File

@ -19,6 +19,7 @@
#include "mozilla/TimeStamp.h"
#include "mozilla/Variant.h"
#include "jsapi.h"
#include "jscntxt.h"
#include "frontend/TokenStream.h"
@ -50,7 +51,8 @@ namespace wasm {
enum class ParseTaskKind
{
Script,
Module
Module,
ScriptDecode
};
// Per-process state for off thread work items.
@ -258,6 +260,7 @@ class GlobalHelperThreadState
public:
JSScript* finishScriptParseTask(JSContext* cx, void* token);
JSScript* finishScriptDecodeTask(JSContext* cx, void* token);
JSObject* finishModuleParseTask(JSContext* cx, void* token);
bool compressionInProgress(SourceCompressionTask* task, const AutoLockHelperThreadState& lock);
SourceCompressionTask* compressionTaskForSource(ScriptSource* ss, const AutoLockHelperThreadState& lock);
@ -504,6 +507,11 @@ StartOffThreadParseModule(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData);
bool
StartOffThreadDecodeScript(JSContext* cx, const ReadOnlyCompileOptions& options,
JS::TranscodeBuffer& buffer, size_t cursor,
JS::OffThreadCompileCallback callback, void* callbackData);
/*
* Called at the end of GC to enqueue any Parse tasks that were waiting on an
* atoms-zone GC to finish.
@ -556,8 +564,21 @@ struct ParseTask
ParseTaskKind kind;
ExclusiveContext* cx;
OwningCompileOptions options;
const char16_t* chars;
size_t length;
// Anonymous union, the only correct interpretation is provided by the
// ParseTaskKind value, or from the virtual parse function.
union {
struct {
const char16_t* chars;
size_t length;
};
struct {
// This should be a reference, but C++ prevents us from using union
// with references as it assumes the reference constness might be
// violated.
JS::TranscodeBuffer* const buffer;
size_t cursor;
};
};
LifoAlloc alloc;
// Rooted pointer to the global object used by 'cx'.
@ -584,6 +605,9 @@ struct ParseTask
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
JSContext* initCx, const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData);
ParseTask(ParseTaskKind kind, ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
JS::OffThreadCompileCallback callback, void* callbackData);
bool init(JSContext* cx, const ReadOnlyCompileOptions& options);
void activate(JSRuntime* rt);
@ -615,6 +639,14 @@ struct ModuleParseTask : public ParseTask
void parse() override;
};
struct ScriptDecodeTask : public ParseTask
{
ScriptDecodeTask(ExclusiveContext* cx, JSObject* exclusiveContextGlobal,
JSContext* initCx, JS::TranscodeBuffer& buffer, size_t cursor,
JS::OffThreadCompileCallback callback, void* callbackData);
void parse() override;
};
// Return whether, if a new parse task was started, it would need to wait for
// an in-progress GC to complete before starting.
extern bool

View File

@ -699,10 +699,10 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
*/
if (shape != obj->lastProperty()) {
shape = shape->previous();
if (!obj->removeProperty(cx, id))
if (!NativeObject::removeProperty(cx, obj, id))
return DenseElementResult::Failure;
} else {
if (!obj->removeProperty(cx, id))
if (!NativeObject::removeProperty(cx, obj, id))
return DenseElementResult::Failure;
shape = obj->lastProperty();
}
@ -718,7 +718,7 @@ NativeObject::maybeDensifySparseElements(js::ExclusiveContext* cx, HandleNativeO
* flag so that we will not start using sparse indexes again if we need
* to grow the object.
*/
if (!obj->clearFlag(cx, BaseShape::INDEXED))
if (!NativeObject::clearFlag(cx, obj, BaseShape::INDEXED))
return DenseElementResult::Failure;
return DenseElementResult::Success;
@ -1023,23 +1023,22 @@ NativeObject::freeSlot(ExclusiveContext* cx, uint32_t slot)
setSlot(slot, UndefinedValue());
}
Shape*
NativeObject::addDataProperty(ExclusiveContext* cx, jsid idArg, uint32_t slot, unsigned attrs)
/* static */ Shape*
NativeObject::addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
jsid idArg, uint32_t slot, unsigned attrs)
{
MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
RootedNativeObject self(cx, this);
RootedId id(cx, idArg);
return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0);
}
Shape*
NativeObject::addDataProperty(ExclusiveContext* cx, HandlePropertyName name,
uint32_t slot, unsigned attrs)
/* static */ Shape*
NativeObject::addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
HandlePropertyName name, uint32_t slot, unsigned attrs)
{
MOZ_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
RootedNativeObject self(cx, this);
RootedId id(cx, NameToId(name));
return addProperty(cx, self, id, nullptr, nullptr, slot, attrs, 0);
return addProperty(cx, obj, id, nullptr, nullptr, slot, attrs, 0);
}
template <AllowGC allowGC>
@ -1073,7 +1072,7 @@ CallAddPropertyHook(ExclusiveContext* cx, HandleNativeObject obj, HandleShape sh
RootedId id(cx, shape->propid());
if (!CallJSAddPropertyOp(cx->asJSContext(), addProperty, obj, id, value)) {
obj->removeProperty(cx, shape->propid());
NativeObject::removeProperty(cx, obj, shape->propid());
return false;
}
}
@ -2565,7 +2564,7 @@ js::NativeDeleteProperty(JSContext* cx, HandleNativeObject obj, HandleId id,
obj->setDenseElementHole(cx, JSID_TO_INT(id));
} else {
if (!obj->removeProperty(cx, id))
if (!NativeObject::removeProperty(cx, obj, id))
return false;
}

View File

@ -607,7 +607,7 @@ class NativeObject : public ShapedObject
}
bool shadowingShapeChange(ExclusiveContext* cx, const Shape& shape);
bool clearFlag(ExclusiveContext* cx, BaseShape::Flag flag);
static bool clearFlag(ExclusiveContext* cx, HandleNativeObject obj, BaseShape::Flag flag);
// The maximum number of slots in an object.
// |MAX_SLOTS_COUNT * sizeof(JS::Value)| shouldn't overflow
@ -734,10 +734,10 @@ class NativeObject : public ShapedObject
bool allowDictionary = true);
/* Add a data property whose id is not yet in this scope. */
Shape* addDataProperty(ExclusiveContext* cx,
jsid id_, uint32_t slot, unsigned attrs);
Shape* addDataProperty(ExclusiveContext* cx, HandlePropertyName name,
uint32_t slot, unsigned attrs);
static Shape* addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
jsid id_, uint32_t slot, unsigned attrs);
static Shape* addDataProperty(ExclusiveContext* cx, HandleNativeObject obj,
HandlePropertyName name, uint32_t slot, unsigned attrs);
/* Add or overwrite a property for id in this scope. */
static Shape*
@ -757,7 +757,7 @@ class NativeObject : public ShapedObject
unsigned attrs, JSGetterOp getter, JSSetterOp setter);
/* Remove the property named by id from this object. */
bool removeProperty(ExclusiveContext* cx, jsid id);
static bool removeProperty(ExclusiveContext* cx, HandleNativeObject obj, jsid id);
/* Clear the scope, making it empty. */
static void clear(ExclusiveContext* cx, HandleNativeObject obj);

View File

@ -298,7 +298,8 @@ RegExpObject::assignInitialShape(ExclusiveContext* cx, Handle<RegExpObject*> sel
JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
/* The lastIndex property alone is writable but non-configurable. */
return self->addDataProperty(cx, cx->names().lastIndex, LAST_INDEX_SLOT, JSPROP_PERMANENT);
return NativeObject::addDataProperty(cx, self, cx->names().lastIndex, LAST_INDEX_SLOT,
JSPROP_PERMANENT);
}
void
@ -1515,7 +1516,7 @@ js::XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp
if (mode == XDR_DECODE) {
RegExpFlag flags = RegExpFlag(flagsword);
RegExpObject* reobj = RegExpObject::create(xdr->cx(), source, flags, nullptr,
xdr->cx()->tempLifoAlloc());
xdr->lifoAlloc());
if (!reobj)
return false;

View File

@ -205,7 +205,7 @@ NewEmptyScopeData(ExclusiveContext* cx, uint32_t length = 0)
static bool
XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
RootedAtom atom(cx, bindingName->name());
bool hasAtom = !!atom;
@ -223,7 +223,7 @@ XDRBindingName(XDRState<XDR_ENCODE>* xdr, BindingName* bindingName)
static bool
XDRBindingName(XDRState<XDR_DECODE>* xdr, BindingName* bindingName)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
uint8_t u8;
if (!xdr->codeUint8(&u8))
@ -248,7 +248,7 @@ Scope::XDRSizedBindingNames(XDRState<mode>* xdr, Handle<ConcreteScope*> scope,
{
MOZ_ASSERT(!data);
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
uint32_t length;
if (mode == XDR_ENCODE)
@ -531,7 +531,7 @@ template <XDRMode mode>
LexicalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
MutableHandleScope scope)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
Rooted<Data*> data(cx);
if (!XDRSizedBindingNames<LexicalScope>(xdr, scope.as<LexicalScope>(), &data))
@ -703,7 +703,7 @@ template <XDRMode mode>
FunctionScope::XDR(XDRState<mode>* xdr, HandleFunction fun, HandleScope enclosing,
MutableHandleScope scope)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
Rooted<Data*> data(cx);
if (!XDRSizedBindingNames<FunctionScope>(xdr, scope.as<FunctionScope>(), &data))
return false;
@ -831,7 +831,7 @@ template <XDRMode mode>
VarScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
MutableHandleScope scope)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
Rooted<Data*> data(cx);
if (!XDRSizedBindingNames<VarScope>(xdr, scope.as<VarScope>(), &data))
return false;
@ -934,7 +934,7 @@ GlobalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, MutableHandleScope scope)
{
MOZ_ASSERT((mode == XDR_DECODE) == !scope);
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
Rooted<Data*> data(cx);
if (!XDRSizedBindingNames<GlobalScope>(xdr, scope.as<GlobalScope>(), &data))
return false;
@ -1059,7 +1059,7 @@ template <XDRMode mode>
EvalScope::XDR(XDRState<mode>* xdr, ScopeKind kind, HandleScope enclosing,
MutableHandleScope scope)
{
JSContext* cx = xdr->cx();
ExclusiveContext* cx = xdr->cx();
Rooted<Data*> data(cx);
{

View File

@ -968,16 +968,15 @@ NativeObject::changeProperty(ExclusiveContext* cx, HandleNativeObject obj, Handl
return newShape;
}
bool
NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
/* static */ bool
NativeObject::removeProperty(ExclusiveContext* cx, HandleNativeObject obj, jsid id_)
{
RootedId id(cx, id_);
RootedNativeObject self(cx, this);
AutoKeepShapeTables keep(cx);
ShapeTable::Entry* entry;
RootedShape shape(cx);
if (!Shape::search(cx, lastProperty(), id, keep, shape.address(), &entry))
if (!Shape::search(cx, obj->lastProperty(), id, keep, shape.address(), &entry))
return false;
if (!shape)
@ -987,10 +986,10 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* If shape is not the last property added, or the last property cannot
* be removed, switch to dictionary mode.
*/
if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) {
if (!self->toDictionaryMode(cx))
if (!obj->inDictionaryMode() && (shape != obj->lastProperty() || !obj->canRemoveLastProperty())) {
if (!obj->toDictionaryMode(cx))
return false;
ShapeTable* table = self->lastProperty()->maybeTable(keep);
ShapeTable* table = obj->lastProperty()->maybeTable(keep);
MOZ_ASSERT(table);
entry = &table->search<MaybeAdding::NotAdding>(shape->propid(), keep);
shape = entry->shape();
@ -1004,21 +1003,21 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* the object or table, so the remaining removal is infallible.
*/
RootedShape spare(cx);
if (self->inDictionaryMode()) {
if (obj->inDictionaryMode()) {
/* For simplicity, always allocate an accessor shape for now. */
spare = Allocate<AccessorShape>(cx);
if (!spare)
return false;
new (spare) Shape(shape->base()->unowned(), 0);
if (shape == self->lastProperty()) {
if (shape == obj->lastProperty()) {
/*
* Get an up to date unowned base shape for the new last property
* when removing the dictionary's last property. Information in
* base shapes for non-last properties may be out of sync with the
* object's state.
*/
RootedShape previous(cx, self->lastProperty()->parent);
StackBaseShape base(self->lastProperty()->base());
RootedShape previous(cx, obj->lastProperty()->parent);
StackBaseShape base(obj->lastProperty()->base());
BaseShape* nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
@ -1028,7 +1027,7 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
/* If shape has a slot, free its slot number. */
if (shape->hasSlot()) {
self->freeSlot(cx, shape->slot());
obj->freeSlot(cx, shape->slot());
if (cx->isJSContext())
++cx->asJSContext()->runtime()->propertyRemovals;
}
@ -1038,8 +1037,8 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* doubly linked list, hashed by lastProperty()->table. So we can edit the
* list and hash in place.
*/
if (self->inDictionaryMode()) {
ShapeTable* table = self->lastProperty()->maybeTable(keep);
if (obj->inDictionaryMode()) {
ShapeTable* table = obj->lastProperty()->maybeTable(keep);
MOZ_ASSERT(table);
if (entry->hadCollision()) {
@ -1056,23 +1055,23 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* checks not to alter significantly the complexity of the
* delete in debug builds, see bug 534493.
*/
Shape* aprop = self->lastProperty();
Shape* aprop = obj->lastProperty();
for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
MOZ_ASSERT_IF(aprop != shape, self->contains(cx, aprop));
MOZ_ASSERT_IF(aprop != shape, obj->contains(cx, aprop));
#endif
}
{
/* Remove shape from its non-circular doubly linked list. */
Shape* oldLastProp = self->lastProperty();
shape->removeFromDictionary(self);
Shape* oldLastProp = obj->lastProperty();
shape->removeFromDictionary(obj);
/* Hand off table from the old to new last property. */
oldLastProp->handoffTableTo(self->lastProperty());
oldLastProp->handoffTableTo(obj->lastProperty());
}
/* Generate a new shape for the object, infallibly. */
JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare));
JS_ALWAYS_TRUE(obj->generateOwnShape(cx, spare));
/* Consider shrinking table if its load factor is <= .25. */
uint32_t size = table->capacity();
@ -1085,11 +1084,11 @@ NativeObject::removeProperty(ExclusiveContext* cx, jsid id_)
* lazily make via a later hashify the exact table for the new property
* lineage.
*/
MOZ_ASSERT(shape == self->lastProperty());
self->removeLastProperty(cx);
MOZ_ASSERT(shape == obj->lastProperty());
obj->removeLastProperty(cx);
}
self->checkShapeConsistency();
obj->checkShapeConsistency();
return true;
}
@ -1133,7 +1132,7 @@ NativeObject::rollbackProperties(ExclusiveContext* cx, HandleNativeObject obj, u
if (slot < slotSpan)
break;
}
if (!obj->removeProperty(cx, obj->lastProperty()->propid()))
if (!NativeObject::removeProperty(cx, obj, obj->lastProperty()->propid()))
return false;
}
@ -1241,21 +1240,20 @@ JSObject::setFlags(ExclusiveContext* cx, HandleObject obj, BaseShape::Flag flags
return true;
}
bool
NativeObject::clearFlag(ExclusiveContext* cx, BaseShape::Flag flag)
/* static */ bool
NativeObject::clearFlag(ExclusiveContext* cx, HandleNativeObject obj, BaseShape::Flag flag)
{
MOZ_ASSERT(inDictionaryMode());
MOZ_ASSERT(obj->inDictionaryMode());
RootedNativeObject self(cx, &as<NativeObject>());
MOZ_ASSERT(self->lastProperty()->getObjectFlags() & flag);
MOZ_ASSERT(obj->lastProperty()->getObjectFlags() & flag);
StackBaseShape base(self->lastProperty());
StackBaseShape base(obj->lastProperty());
base.flags &= ~flag;
UnownedBaseShape* nbase = BaseShape::getUnowned(cx, base);
if (!nbase)
return false;
self->lastProperty()->base()->adoptUnowned(nbase);
obj->lastProperty()->base()->adoptUnowned(nbase);
return true;
}

View File

@ -15,31 +15,29 @@
namespace js {
inline bool
StringObject::init(JSContext* cx, HandleString str)
/* static */ inline bool
StringObject::init(JSContext* cx, Handle<StringObject*> obj, HandleString str)
{
MOZ_ASSERT(numFixedSlots() == 2);
MOZ_ASSERT(obj->numFixedSlots() == 2);
Rooted<StringObject*> self(cx, this);
if (!EmptyShape::ensureInitialCustomShape<StringObject>(cx, self))
if (!EmptyShape::ensureInitialCustomShape<StringObject>(cx, obj))
return false;
MOZ_ASSERT(self->lookup(cx, NameToId(cx->names().length))->slot() == LENGTH_SLOT);
MOZ_ASSERT(obj->lookup(cx, NameToId(cx->names().length))->slot() == LENGTH_SLOT);
self->setStringThis(str);
obj->setStringThis(str);
return true;
}
inline StringObject*
/* static */ inline StringObject*
StringObject::create(JSContext* cx, HandleString str, HandleObject proto, NewObjectKind newKind)
{
JSObject* obj = NewObjectWithClassProto(cx, &class_, proto, newKind);
if (!obj)
return nullptr;
Rooted<StringObject*> strobj(cx, &obj->as<StringObject>());
if (!strobj->init(cx, str))
if (!StringObject::init(cx, strobj, str))
return nullptr;
return strobj;
}

View File

@ -56,7 +56,7 @@ class StringObject : public NativeObject
}
private:
inline bool init(JSContext* cx, HandleString str);
static inline bool init(JSContext* cx, Handle<StringObject*> obj, HandleString str);
void setStringThis(JSString* str) {
MOZ_ASSERT(getReservedSlot(PRIMITIVE_VALUE_SLOT).isUndefined());

View File

@ -33,6 +33,10 @@
_(ParserCompileLazy) \
_(ParserCompileScript) \
_(ParserCompileModule) \
_(DecodeScript) \
_(DecodeFunction) \
_(EncodeScript) \
_(EncodeFunction) \
_(Scripts) \
_(VM) \
_(CompressSource) \

View File

@ -389,14 +389,14 @@ PropagatePropertyTypes(JSContext* cx, jsid id, ObjectGroup* oldGroup, ObjectGrou
static PlainObject*
MakeReplacementTemplateObject(JSContext* cx, HandleObjectGroup group, const UnboxedLayout &layout)
{
PlainObject* obj = NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
TenuredObject);
Rooted<PlainObject*> obj(cx, NewObjectWithGroup<PlainObject>(cx, group, layout.getAllocKind(),
TenuredObject));
if (!obj)
return nullptr;
for (size_t i = 0; i < layout.properties().length(); i++) {
const UnboxedLayout::Property& property = layout.properties()[i];
if (!obj->addDataProperty(cx, NameToId(property.name), i, JSPROP_ENUMERATE))
if (!NativeObject::addDataProperty(cx, obj, NameToId(property.name), i, JSPROP_ENUMERATE))
return nullptr;
MOZ_ASSERT(obj->slotSpan() == i + 1);
MOZ_ASSERT(!obj->inDictionaryMode());

View File

@ -11,19 +11,27 @@
#include <string.h>
#include "jsapi.h"
#include "jscntxt.h"
#include "jsscript.h"
#include "vm/Debugger.h"
#include "vm/EnvironmentObject.h"
#include "vm/TraceLogging.h"
using namespace js;
using mozilla::PodEqual;
template<XDRMode mode>
LifoAlloc&
XDRState<mode>::lifoAlloc() const {
return buf.cx()->asJSContext()->tempLifoAlloc();
}
template<XDRMode mode>
void
XDRState<mode>::postProcessContextErrors(JSContext* cx)
XDRState<mode>::postProcessContextErrors(ExclusiveContext* cx)
{
if (cx->isExceptionPending()) {
if (cx->isJSContext() && cx->asJSContext()->isExceptionPending()) {
MOZ_ASSERT(resultCode_ == JS::TranscodeResult_Ok);
resultCode_ = JS::TranscodeResult_Throw;
}
@ -41,7 +49,7 @@ XDRState<mode>::codeChars(const Latin1Char* chars, size_t nchars)
return true;
uint8_t* ptr = buf.write(nchars);
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
mozilla::PodCopy(ptr, chars, nchars);
return true;
@ -57,7 +65,7 @@ XDRState<mode>::codeChars(char16_t* chars, size_t nchars)
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(nbytes);
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
} else {
const uint8_t* ptr = buf.read(nbytes);
@ -71,11 +79,8 @@ static bool
VersionCheck(XDRState<mode>* xdr)
{
JS::BuildIdCharVector buildId;
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId)) {
JS_ReportErrorNumberASCII(xdr->cx(), GetErrorMessage, nullptr,
JSMSG_BUILD_ID_NOT_AVAILABLE);
return false;
}
if (!xdr->cx()->buildIdOp() || !xdr->cx()->buildIdOp()(&buildId))
return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
MOZ_ASSERT(!buildId.empty());
uint32_t buildIdLength;
@ -98,7 +103,7 @@ VersionCheck(XDRState<mode>* xdr)
// buildId.
if (!decodedBuildId.resize(buildIdLength)) {
ReportOutOfMemory(xdr->cx());
return false;
return xdr->fail(JS::TranscodeResult_Throw);
}
if (!xdr->codeBytes(decodedBuildId.begin(), buildIdLength))
@ -114,20 +119,35 @@ VersionCheck(XDRState<mode>* xdr)
template<XDRMode mode>
bool
XDRState<mode>::codeFunction(MutableHandleFunction funp)
XDRState<mode>::codeFunction(MutableHandleFunction funp, HandleScriptSource sourceObject)
{
if (mode == XDR_DECODE)
funp.set(nullptr);
TraceLoggerThread* logger = nullptr;
if (cx()->isJSContext())
logger = TraceLoggerForMainThread(cx()->asJSContext()->runtime());
else
logger = TraceLoggerForCurrentThread();
TraceLoggerTextId event =
mode == XDR_DECODE ? TraceLogger_DecodeFunction : TraceLogger_EncodeFunction;
AutoTraceLog tl(logger, event);
RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
if (mode == XDR_DECODE) {
MOZ_ASSERT(!sourceObject);
funp.set(nullptr);
} else if (getTreeKey(funp) != AutoXDRTree::noKey) {
MOZ_ASSERT(sourceObject);
scope = funp->nonLazyScript()->enclosingScope();
} else {
MOZ_ASSERT(!sourceObject);
MOZ_ASSERT(funp->nonLazyScript()->enclosingScope()->is<GlobalScope>());
}
if (!VersionCheck(this)) {
postProcessContextErrors(cx());
return false;
}
RootedScope scope(cx(), &cx()->global()->emptyGlobalScope());
if (!XDRInterpretedFunction(this, scope, nullptr, funp)) {
if (!XDRInterpretedFunction(this, scope, sourceObject, funp)) {
postProcessContextErrors(cx());
funp.set(nullptr);
return false;
@ -140,6 +160,17 @@ template<XDRMode mode>
bool
XDRState<mode>::codeScript(MutableHandleScript scriptp)
{
TraceLoggerThread* logger = nullptr;
if (cx()->isJSContext())
logger = TraceLoggerForMainThread(cx()->asJSContext()->runtime());
else
logger = TraceLoggerForCurrentThread();
TraceLoggerTextId event =
mode == XDR_DECODE ? TraceLogger_DecodeScript : TraceLogger_EncodeScript;
AutoTraceLog tl(logger, event);
AutoXDRTree scriptTree(this, getTopLevelTreeKey());
if (mode == XDR_DECODE)
scriptp.set(nullptr);
else
@ -168,3 +199,189 @@ XDRState<mode>::codeConstValue(MutableHandleValue vp)
template class js::XDRState<XDR_ENCODE>;
template class js::XDRState<XDR_DECODE>;
AutoXDRTree::AutoXDRTree(XDRCoderBase* xdr, AutoXDRTree::Key key)
: key_(key),
parent_(this),
xdr_(xdr)
{
if (key_ != AutoXDRTree::noKey)
xdr->createOrReplaceSubTree(this);
}
AutoXDRTree::~AutoXDRTree()
{
if (key_ != AutoXDRTree::noKey)
xdr_->endSubTree();
}
constexpr AutoXDRTree::Key AutoXDRTree::noKey;
constexpr AutoXDRTree::Key AutoXDRTree::noSubTree;
constexpr AutoXDRTree::Key AutoXDRTree::topLevel;
AutoXDRTree::Key
XDRIncrementalEncoder::getTopLevelTreeKey() const
{
return AutoXDRTree::topLevel;
}
AutoXDRTree::Key
XDRIncrementalEncoder::getTreeKey(JSFunction* fun) const
{
if (fun->isInterpretedLazy()) {
static_assert(sizeof(fun->lazyScript()->begin()) == 4 ||
sizeof(fun->lazyScript()->end()) == 4,
"AutoXDRTree key requires LazyScripts positions to be uint32");
return uint64_t(fun->lazyScript()->begin()) << 32 | fun->lazyScript()->end();
}
if (fun->isInterpreted()) {
static_assert(sizeof(fun->nonLazyScript()->sourceStart()) == 4 ||
sizeof(fun->nonLazyScript()->sourceEnd()) == 4,
"AutoXDRTree key requires JSScripts positions to be uint32");
return uint64_t(fun->nonLazyScript()->sourceStart()) << 32 | fun->nonLazyScript()->sourceEnd();
}
return AutoXDRTree::noKey;
}
bool
XDRIncrementalEncoder::init()
{
if (!tree_.init())
return false;
return true;
}
void
XDRIncrementalEncoder::createOrReplaceSubTree(AutoXDRTree* child)
{
AutoXDRTree* parent = scope_;
child->parent_ = parent;
scope_ = child;
if (oom_)
return;
size_t cursor = buf.cursor();
// End the parent slice here, set the key to the child.
if (parent) {
Slice& last = node_->back();
last.sliceLength = cursor - last.sliceBegin;
last.child = child->key_;
MOZ_ASSERT_IF(uint32_t(parent->key_) != 0,
uint32_t(parent->key_ >> 32) <= uint32_t(child->key_ >> 32) &&
uint32_t(child->key_) <= uint32_t(parent->key_));
}
// Create or replace the part with what is going to be encoded next.
SlicesTree::AddPtr p = tree_.lookupForAdd(child->key_);
SlicesNode tmp;
if (!p) {
// Create a new sub-tree node.
if (!tree_.add(p, child->key_, mozilla::Move(tmp))) {
oom_ = true;
return;
}
} else {
// Replace an exisiting sub-tree.
p->value() = mozilla::Move(tmp);
}
node_ = &p->value();
// Add content to the root of the new sub-tree,
// i-e an empty slice with no children.
if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree }))
MOZ_CRASH("SlicesNode have a reserved space of 1.");
}
void
XDRIncrementalEncoder::endSubTree()
{
AutoXDRTree* child = scope_;
AutoXDRTree* parent = child->parent_;
scope_ = parent;
if (oom_)
return;
size_t cursor = buf.cursor();
// End the child sub-tree.
Slice& last = node_->back();
last.sliceLength = cursor - last.sliceBegin;
MOZ_ASSERT(last.child == AutoXDRTree::noSubTree);
// Stop at the top-level.
if (!parent) {
node_ = nullptr;
return;
}
// Restore the parent node.
SlicesTree::Ptr p = tree_.lookup(parent->key_);
node_ = &p->value();
// Append the new slice in the parent node.
if (!node_->append(Slice { cursor, 0, AutoXDRTree::noSubTree })) {
oom_ = true;
return;
}
}
bool
XDRIncrementalEncoder::linearize()
{
if (oom_) {
ReportOutOfMemory(cx());
return fail(JS::TranscodeResult_Throw);
}
// Do not linearize while we are currently adding bytes.
MOZ_ASSERT(scope_ == nullptr);
// Visit the tree parts in a depth first order, to linearize the bits.
Vector<SlicesNode::ConstRange> depthFirst(cx());
SlicesTree::Ptr p = tree_.lookup(AutoXDRTree::topLevel);
MOZ_ASSERT(p);
if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
ReportOutOfMemory(cx());
return fail(JS::TranscodeResult_Throw);
}
while (!depthFirst.empty()) {
SlicesNode::ConstRange& iter = depthFirst.back();
Slice slice = iter.popCopyFront();
// These fields have different meaning, but they should be correlated if
// the tree is well formatted.
MOZ_ASSERT_IF(slice.child == AutoXDRTree::noSubTree, iter.empty());
if (iter.empty())
depthFirst.popBack();
// Copy the bytes associated with the current slice to the transcode
// buffer which would be serialized.
MOZ_ASSERT(slice.sliceBegin <= slices_.length());
MOZ_ASSERT(slice.sliceBegin + slice.sliceLength <= slices_.length());
if (!buffer_.append(slices_.begin() + slice.sliceBegin, slice.sliceLength)) {
ReportOutOfMemory(cx());
return fail(JS::TranscodeResult_Throw);
}
// If we are at the end, go to back to the parent script.
if (slice.child == AutoXDRTree::noSubTree)
continue;
// Visit the sub-parts before visiting the rest of the current slice.
SlicesTree::Ptr p = tree_.lookup(slice.child);
MOZ_ASSERT(p);
if (!depthFirst.append(((const SlicesNode&) p->value()).all())) {
ReportOutOfMemory(cx());
return fail(JS::TranscodeResult_Throw);
}
}
tree_.finish();
slices_.clearAndFree();
return true;
}

View File

@ -17,10 +17,10 @@ namespace js {
class XDRBuffer {
public:
XDRBuffer(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
XDRBuffer(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
: context_(cx), buffer_(buffer), cursor_(cursor) { }
JSContext* cx() const {
ExclusiveContext* cx() const {
return context_;
}
@ -43,7 +43,7 @@ class XDRBuffer {
uint8_t* write(size_t n) {
MOZ_ASSERT(n != 0);
if (!buffer_.growByUninitialized(n)) {
JS_ReportOutOfMemory(cx());
ReportOutOfMemory(cx());
return nullptr;
}
uint8_t* ptr = &buffer_[cursor_];
@ -51,30 +51,108 @@ class XDRBuffer {
return ptr;
}
size_t cursor() const {
return cursor_;
}
private:
JSContext* const context_;
ExclusiveContext* const context_;
JS::TranscodeBuffer& buffer_;
size_t cursor_;
};
class XDRCoderBase;
class XDRIncrementalEncoder;
// An AutoXDRTree is used to identify section encoded by an XDRIncrementalEncoder.
//
// Its primary goal is to identify functions, such that we can first encode them
// as LazyScript, and later replaced by them by their corresponding bytecode
// once delazified.
//
// As a convenience, this is also used to identify the top-level of the content
// encoded by an XDRIncrementalEncoder.
//
// Sections can be encoded any number of times in an XDRIncrementalEncoder, and
// the latest encoded version would replace all the previous one.
class MOZ_RAII AutoXDRTree
{
public:
// For a JSFunction, a tree key is defined as being:
// script()->begin << 32 | script()->end
//
// Based on the invariant that |begin <= end|, we can make special
// keys, such as the top-level script.
using Key = uint64_t;
AutoXDRTree(XDRCoderBase* xdr, Key key);
~AutoXDRTree();
// Indicate the lack of a key for the current tree.
static constexpr Key noKey = 0;
// Used to end the slices when there is no children.
static constexpr Key noSubTree = Key(1) << 32;
// Used as the root key of the tree in the hash map.
static constexpr Key topLevel = Key(2) << 32;
private:
friend class XDRIncrementalEncoder;
Key key_;
AutoXDRTree* parent_;
XDRCoderBase* xdr_;
};
class XDRCoderBase
{
protected:
XDRCoderBase() {}
public:
virtual AutoXDRTree::Key getTopLevelTreeKey() const { return AutoXDRTree::noKey; }
virtual AutoXDRTree::Key getTreeKey(JSFunction* fun) const { return AutoXDRTree::noKey; }
virtual void createOrReplaceSubTree(AutoXDRTree* child) {};
virtual void endSubTree() {};
};
/*
* XDR serialization state. All data is encoded in little endian.
*/
template <XDRMode mode>
class XDRState {
class XDRState : public XDRCoderBase
{
public:
XDRBuffer buf;
private:
JS::TranscodeResult resultCode_;
XDRState(JSContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
: buf(cx, buffer, cursor), resultCode_(JS::TranscodeResult_Ok) { }
public:
XDRState(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor = 0)
: buf(cx, buffer, cursor),
resultCode_(JS::TranscodeResult_Ok)
{
}
JSContext* cx() const {
virtual ~XDRState() {};
ExclusiveContext* cx() const {
return buf.cx();
}
virtual LifoAlloc& lifoAlloc() const;
virtual bool hasOptions() const { return false; }
virtual const ReadOnlyCompileOptions& options() {
MOZ_CRASH("does not have options");
}
virtual bool hasScriptSourceObjectOut() const { return false; }
virtual ScriptSourceObject** scriptSourceObjectOut() {
MOZ_CRASH("does not have scriptSourceObjectOut.");
}
// Record logical failures of XDR.
void postProcessContextErrors(JSContext* cx);
void postProcessContextErrors(ExclusiveContext* cx);
JS::TranscodeResult resultCode() const {
return resultCode_;
}
@ -88,7 +166,7 @@ class XDRState {
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(sizeof(*n));
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
*ptr = *n;
} else {
*n = *buf.read(sizeof(*n));
@ -100,7 +178,7 @@ class XDRState {
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(sizeof(*n));
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
mozilla::LittleEndian::writeUint16(ptr, *n);
} else {
const uint8_t* ptr = buf.read(sizeof(*n));
@ -113,7 +191,7 @@ class XDRState {
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(sizeof(*n));
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
mozilla::LittleEndian::writeUint32(ptr, *n);
} else {
const uint8_t* ptr = buf.read(sizeof(*n));
@ -126,7 +204,7 @@ class XDRState {
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(sizeof(*n));
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
mozilla::LittleEndian::writeUint64(ptr, *n);
} else {
const uint8_t* ptr = buf.read(sizeof(*n));
@ -173,7 +251,7 @@ class XDRState {
if (mode == XDR_ENCODE) {
uint8_t* ptr = buf.write(len);
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
memcpy(ptr, bytes, len);
} else {
memcpy(bytes, buf.read(len), len);
@ -192,7 +270,7 @@ class XDRState {
size_t n = strlen(*sp) + 1;
uint8_t* ptr = buf.write(n);
if (!ptr)
return false;
return fail(JS::TranscodeResult_Throw);
memcpy(ptr, *sp, n);
} else {
*sp = buf.readCString();
@ -203,7 +281,7 @@ class XDRState {
bool codeChars(const JS::Latin1Char* chars, size_t nchars);
bool codeChars(char16_t* chars, size_t nchars);
bool codeFunction(JS::MutableHandleFunction objp);
bool codeFunction(JS::MutableHandleFunction objp, HandleScriptSource sourceObject = nullptr);
bool codeScript(MutableHandleScript scriptp);
bool codeConstValue(MutableHandleValue vp);
};
@ -211,6 +289,141 @@ class XDRState {
using XDREncoder = XDRState<XDR_ENCODE>;
using XDRDecoder = XDRState<XDR_DECODE>;
class XDROffThreadDecoder : public XDRDecoder
{
const ReadOnlyCompileOptions* options_;
ScriptSourceObject** sourceObjectOut_;
LifoAlloc& alloc_;
public:
// Note, when providing an ExclusiveContext, where isJSContext is false,
// then the initialization of the ScriptSourceObject would remain
// incomplete. Thus, the sourceObjectOut must be used to finish the
// initialization with ScriptSourceObject::initFromOptions after the
// decoding.
//
// When providing a sourceObjectOut pointer, you have to ensure that it is
// marked by the GC to avoid dangling pointers.
XDROffThreadDecoder(ExclusiveContext* cx, LifoAlloc& alloc,
const ReadOnlyCompileOptions* options,
ScriptSourceObject** sourceObjectOut,
JS::TranscodeBuffer& buffer, size_t cursor = 0)
: XDRDecoder(cx, buffer, cursor),
options_(options),
sourceObjectOut_(sourceObjectOut),
alloc_(alloc)
{
MOZ_ASSERT(options);
MOZ_ASSERT(sourceObjectOut);
MOZ_ASSERT(*sourceObjectOut == nullptr);
}
LifoAlloc& lifoAlloc() const override {
return alloc_;
}
bool hasOptions() const override { return true; }
const ReadOnlyCompileOptions& options() override {
return *options_;
}
bool hasScriptSourceObjectOut() const override { return true; }
ScriptSourceObject** scriptSourceObjectOut() override {
return sourceObjectOut_;
}
};
class XDRIncrementalEncoder : public XDREncoder
{
// The incremental encoder encodes the content of scripts and functions in
// the XDRBuffer. It can be used to encode multiple times the same AutoXDRTree,
// and uses its key to identify which part to replace.
//
// Internally, this encoder keeps a tree representation of the scopes. Each
// node is composed of a vector of slices which are interleaved by child
// nodes.
//
// A slice corresponds to an index and a length within the content of the
// slices_ buffer. The index is updated when a slice is created, and the
// length is updated when the slice is ended, either by creating a new scope
// child, or by closing the scope and going back to the parent.
//
// +---+---+---+
// begin | | | |
// length | | | |
// child | . | . | . |
// +-|-+-|-+---+
// | |
// +---------+ +---------+
// | |
// v v
// +---+---+ +---+
// | | | | |
// | | | | |
// | . | . | | . |
// +-|-+---+ +---+
// |
// |
// |
// v
// +---+
// | |
// | |
// | . |
// +---+
//
//
// The tree key is used to identify the child nodes, and to make them
// easily replaceable.
//
// The tree is rooted at the |topLevel| key.
//
struct Slice {
size_t sliceBegin;
size_t sliceLength;
AutoXDRTree::Key child;
};
using SlicesNode = Vector<Slice, 1, SystemAllocPolicy>;
using SlicesTree = HashMap<AutoXDRTree::Key, SlicesNode, DefaultHasher<AutoXDRTree::Key>,
SystemAllocPolicy>;
// Last opened XDR-tree on the stack.
AutoXDRTree* scope_;
// Node corresponding to the opened scope.
SlicesNode* node_;
// Tree of slices.
SlicesTree tree_;
JS::TranscodeBuffer slices_;
JS::TranscodeBuffer& buffer_;
bool oom_;
public:
XDRIncrementalEncoder(ExclusiveContext* cx, JS::TranscodeBuffer& buffer, size_t cursor)
: XDREncoder(cx, slices_, 0),
scope_(nullptr),
node_(nullptr),
buffer_(buffer),
oom_(false)
{
MOZ_ASSERT(buffer.length() == cursor, "NYI");
}
virtual ~XDRIncrementalEncoder() {}
AutoXDRTree::Key getTopLevelTreeKey() const override;
AutoXDRTree::Key getTreeKey(JSFunction* fun) const override;
MOZ_MUST_USE bool init();
void createOrReplaceSubTree(AutoXDRTree* child) override;
void endSubTree() override;
// In the current XDRBuffer, move replaceable-parts to form a linear
// sequence of bytes.
MOZ_MUST_USE bool linearize();
};
} /* namespace js */
#endif /* vm_Xdr_h */

View File

@ -1752,7 +1752,7 @@ Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<Promise
if (!cx->getPendingException(&rejectionValue))
return false;
return promise->reject(cx, rejectionValue);
return PromiseObject::reject(cx, promise, rejectionValue);
}
RootedObject stack(cx, promise->allocationSite());
@ -1780,7 +1780,7 @@ Reject(JSContext* cx, const CompileArgs& args, UniqueChars error, Handle<Promise
return false;
RootedValue rejectionValue(cx, ObjectValue(*errorObj));
return promise->reject(cx, rejectionValue);
return PromiseObject::reject(cx, promise, rejectionValue);
}
static bool
@ -1792,7 +1792,7 @@ ResolveCompilation(JSContext* cx, Module& module, Handle<PromiseObject*> promise
return false;
RootedValue resolutionValue(cx, ObjectValue(*moduleObj));
return promise->resolve(cx, resolutionValue);
return PromiseObject::resolve(cx, promise, resolutionValue);
}
struct CompilePromiseTask : PromiseTask
@ -1827,7 +1827,7 @@ RejectWithPendingException(JSContext* cx, Handle<PromiseObject*> promise)
if (!GetAndClearException(cx, &rejectionValue))
return false;
return promise->reject(cx, rejectionValue);
return PromiseObject::reject(cx, promise, rejectionValue);
}
static bool
@ -1915,7 +1915,7 @@ ResolveInstantiation(JSContext* cx, Module& module, HandleObject importObj,
return false;
val = ObjectValue(*resultObj);
return promise->resolve(cx, val);
return PromiseObject::resolve(cx, promise, val);
}
struct InstantiatePromiseTask : CompilePromiseTask
@ -1981,7 +1981,7 @@ WebAssembly_instantiate(JSContext* cx, unsigned argc, Value* vp)
return RejectWithPendingException(cx, promise, callArgs);
RootedValue resolutionValue(cx, ObjectValue(*instanceObj));
if (!promise->resolve(cx, resolutionValue))
if (!PromiseObject::resolve(cx, promise, resolutionValue))
return false;
} else {
auto task = cx->make_unique<InstantiatePromiseTask>(cx, promise, importObj);

View File

@ -40,7 +40,8 @@ public:
~OverflowChangedTracker()
{
NS_ASSERTION(mEntryList.empty(), "Need to flush before destroying!");
// XXXheycam Temporarily downgrade this assertion (bug 1324647).
NS_ASSERTION_STYLO_WARNING(mEntryList.empty(), "Need to flush before destroying!");
}
/**

View File

@ -451,7 +451,7 @@ asserts(4-6) asserts-if(Android&&!asyncPan,2) load 898913.html # bug 847368
pref(layers.acceleration.disabled,true) pref(layers.force-active,true) load 919434.html
load 926728.html
load 930381.html
asserts-if(stylo,1) load 931450.html # bug 1324647
load 931450.html
load 931460-1.html
load 931464.html
load 935765-1.html
@ -460,7 +460,7 @@ load 942690.html
load 973390-1.html
load 1001237.html
load 1009036.html
asserts-if(stylo,1) load 1043163-1.html # bug 1324647
load 1043163-1.html
load 1061028.html
load 1107508-1.html
load 1116104.html

View File

@ -458,7 +458,7 @@ load 655462-1.html
load 656130-1.html
load 656130-2.html
load 660416.html
asserts-if(stylo,2) load 665853.html # bug 1324634
load 665853.html
load 667025.html
skip-if(stylo) load 673770.html # bug 1323652
load 679933-1.html
@ -575,9 +575,9 @@ pref(font.size.inflation.minTwips,200) load 1032450.html
load 1032613-1.svg
load 1032613-2.html
load 1037903.html
asserts-if(stylo,1) load 1039454-1.html # bug 1324647
load 1039454-1.html
load 1042489.html
asserts-if(stylo,1) load 1054010-1.html # bug 1324647
load 1054010-1.html
load 1058954-1.html
load 1134531.html
load 1134667.html
@ -585,7 +585,7 @@ load 1137723-1.html
load 1137723-2.html
load 1140268-1.html
load 1145768.html
asserts-if(stylo,1) load 1146103.html # bug 1324647
load 1146103.html
load 1146107.html
load 1146114.html
asserts(0-20) load 1153478.html # bug 1144852

View File

@ -57,6 +57,7 @@
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/ServoUtils.h"
#ifdef MOZ_XUL
#include "nsXULPrototypeCache.h"
@ -1877,9 +1878,11 @@ Loader::DoSheetComplete(SheetLoadData* aLoadData, nsresult aStatus,
aLoadData->mSheet->GetReferrerPolicy());
#ifdef DEBUG
SheetLoadData *loadingData;
NS_ASSERTION(mSheets->mLoadingDatas.Get(&key, &loadingData) &&
loadingData == aLoadData,
"Bad loading table");
// XXXheycam Temporarily downgrade this assertion (bug 1314045).
NS_ASSERTION_STYLO_WARNING(
mSheets->mLoadingDatas.Get(&key, &loadingData) &&
loadingData == aLoadData,
"Bad loading table");
#endif
mSheets->mLoadingDatas.Remove(&key);

View File

@ -79,4 +79,14 @@
#define MOZ_STYLO_FORWARD(method_, args_) \
MOZ_STYLO_FORWARD_CONCRETE(method_, args_, args_)
// Warning in MOZ_STYLO builds and non-fatally assert in regular builds.
#define NS_ASSERTION_STYLO_WARNING_EXPAND(X) X
#ifdef MOZ_STYLO
#define NS_ASSERTION_STYLO_WARNING(...) \
NS_ASSERTION_STYLO_WARNING_EXPAND(NS_WARNING_ASSERTION(__VA_ARGS__))
#else
#define NS_ASSERTION_STYLO_WARNING(...) \
NS_ASSERTION_STYLO_WARNING_EXPAND(NS_ASSERTION(__VA_ARGS__))
#endif
#endif // mozilla_ServoUtils_h

View File

@ -18,7 +18,7 @@ asserts-if(stylo,1) load 397022-1.html # bug 1324704
load 399289-1.svg
load 404470-1.html
load 411603-1.html
asserts-if(stylo,2) load 412588-1.html # bug 1324634
load 412588-1.html
load 413274-1.xhtml
load 416461-1.xul
load 418007-1.xhtml
@ -37,7 +37,7 @@ load 452150-1.xhtml
skip-if(stylo) load 456196.html # bug 132652
load 460209-1.html
load 460217-1.html
asserts-if(stylo,2) load 460323-1.html # bug 1324634
load 460323-1.html
load 466845-1.html
load 469432-1.xhtml
load 472195-1.html
@ -48,8 +48,8 @@ load 473892-1.html
load 473914-1.html
load 474377-1.xhtml
load 478321-1.xhtml
asserts-if(stylo,3) load 495269-1.html # bug 1324634
asserts-if(stylo,3) load 495269-2.html # bug 1324634
load 495269-1.html
load 495269-2.html
load 498036-1.html
load 509155-1.html
load 509156-1.html
@ -68,7 +68,7 @@ load 580685.html
load 585185-1.html
load 588627-1.html
load 592698-1.html
asserts-if(stylo,2) load 601437-1.html # bug 1324634
load 601437-1.html
load 601439-1.html
load 605689-1.html
load 611922-1.html
@ -100,7 +100,7 @@ load 840898.html
load 842134.html
load 861489-1.html
load 862113.html
asserts-if(stylo,5) load 867487.html # bug 1324634
load 867487.html
load 873222.html
asserts-if(stylo,2) load 880862.html # bug 1324701
load 894245-1.html
@ -130,7 +130,7 @@ load 1167782-1.html
load 1186768-1.xhtml
load 1200568-1.html
load 1206105-1.html
skip-if(stylo) load 1223688-1.html # bug 1323715
load 1223688-1.html
load 1223694-1.html
load 1226400-1.html
load 1227501-1.html

View File

@ -178,9 +178,7 @@ skip-if = toolkit == 'android' #bug 536603
[test_css_function_mismatched_parenthesis.html]
[test_css_loader_crossorigin_data_url.html]
[test_css_supports.html]
skip-if = stylo # bug 1323715
[test_css_supports_variables.html]
skip-if = stylo # bug 1323715
[test_default_bidi_css.html]
[test_default_computed_style.html]
[test_descriptor_storage.html]

View File

@ -23,7 +23,7 @@ load 337408-1.xul
load 338301-1.xhtml
load 338312-1.xhtml
load 340083-1.svg
asserts-if(stylo,1) load 340945-1.svg # bug 1324647
load 340945-1.svg
load 342923-1.html
load 343221-1.xhtml
load 344749-1.svg

View File

@ -2903,7 +2903,10 @@ pref("dom.ipc.processCount", 1);
pref("svg.disabled", false);
// Override default dom.ipc.processCount for some remote content process types.
pref("dom.ipc.processCount.webLargeAllocation", 2);
pref("dom.ipc.processCount.webLargeAllocation", 10);
// Enable the Large-Allocation header
pref("dom.largeAllocationHeader.enabled", true);
// Pref to control whether we use separate content processes for top-level load
// of file:// URIs.

View File

@ -244,7 +244,6 @@ static const char kUser32BeforeBlocklistParameter[] = "User32BeforeBlocklist=1\n
static const int kUser32BeforeBlocklistParameterLen =
sizeof(kUser32BeforeBlocklistParameter) - 1;
static DWORD sThreadLoadingXPCOMModule;
static bool sBlocklistInitAttempted;
static bool sBlocklistInitFailed;
static bool sUser32BeforeBlocklist;
@ -309,41 +308,6 @@ private:
void* mRealView;
};
bool
CheckASLR(const wchar_t* path)
{
bool retval = false;
HANDLE file = ::CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
nullptr);
if (file != INVALID_HANDLE_VALUE) {
HANDLE map = ::CreateFileMappingW(file, nullptr, PAGE_READONLY, 0, 0,
nullptr);
if (map) {
RVAMap<IMAGE_DOS_HEADER> peHeader(map, 0);
if (peHeader) {
RVAMap<IMAGE_NT_HEADERS> ntHeader(map, peHeader->e_lfanew);
if (ntHeader) {
// If the DLL has no code, permit it regardless of ASLR status.
if (ntHeader->OptionalHeader.SizeOfCode == 0) {
retval = true;
}
// Check to see if the DLL supports ASLR
else if ((ntHeader->OptionalHeader.DllCharacteristics &
IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE) != 0) {
retval = true;
}
}
}
::CloseHandle(map);
}
::CloseHandle(file);
}
return retval;
}
DWORD
GetTimestamp(const wchar_t* path)
{
@ -727,21 +691,6 @@ continue_loading:
printf_stderr("LdrLoadDll: continuing load... ('%S')\n", moduleFileName->Buffer);
#endif
if (GetCurrentThreadId() == sThreadLoadingXPCOMModule) {
// Check to ensure that the DLL has ASLR.
full_fname = getFullPath(filePath, fname);
if (!full_fname) {
// uh, we couldn't find the DLL at all, so...
printf_stderr("LdrLoadDll: Blocking load of '%s' (SearchPathW didn't find it?)\n", dllName);
return STATUS_DLL_NOT_FOUND;
}
if (!CheckASLR(full_fname.get())) {
printf_stderr("LdrLoadDll: Blocking load of '%s'. XPCOM components must support ASLR.\n", dllName);
return STATUS_DLL_NOT_FOUND;
}
}
return stub_LdrLoadDll(filePath, flags, moduleFileName, handle);
}
@ -783,17 +732,6 @@ DllBlocklist_Initialize()
}
}
MFBT_API void
DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread)
{
if (inXPCOMLoadOnMainThread) {
MOZ_ASSERT(sThreadLoadingXPCOMModule == 0, "Only one thread should be doing this");
sThreadLoadingXPCOMModule = GetCurrentThreadId();
} else {
sThreadLoadingXPCOMModule = 0;
}
}
MFBT_API void
DllBlocklist_WriteNotes(HANDLE file)
{

View File

@ -9,31 +9,14 @@
#if defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
#include <windows.h>
#include "mozilla/GuardObjects.h"
#include "mozilla/Attributes.h"
#include "mozilla/Types.h"
#define HAS_DLL_BLOCKLIST
MFBT_API void DllBlocklist_Initialize();
MFBT_API void DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread);
MFBT_API void DllBlocklist_WriteNotes(HANDLE file);
MFBT_API bool DllBlocklist_CheckStatus();
class MOZ_RAII AutoSetXPCOMLoadOnMainThread
{
public:
explicit AutoSetXPCOMLoadOnMainThread(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
DllBlocklist_SetInXPCOMLoadOnMainThread(true);
}
~AutoSetXPCOMLoadOnMainThread() {
DllBlocklist_SetInXPCOMLoadOnMainThread(false);
}
private:
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
};
#endif // defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))
#endif // mozilla_windowsdllblocklist_h

View File

@ -642,10 +642,9 @@ nsProtocolProxyService::PrefsChanged(nsIPrefBranch *prefBranch,
mFailedProxyTimeout);
if (!pref || !strcmp(pref, PROXY_PREF("no_proxies_on"))) {
rv = prefBranch->GetCharPref(PROXY_PREF("no_proxies_on"),
getter_Copies(tempString));
if (NS_SUCCEEDED(rv))
LoadHostFilters(tempString.get());
nsCString no_proxies;
proxy_GetStringPref(prefBranch, PROXY_PREF("no_proxies_on"), no_proxies);
LoadHostFilters(no_proxies.get());
}
// We're done if not using something that could give us a PAC URL

View File

@ -1,3 +1,3 @@
asserts-if(stylo,4) load 785753-1.html # bug 1324634
asserts-if(stylo,4) load 785753-2.html # bug 1324634
load 785753-1.html
load 785753-2.html
load 1274044-1.html

View File

@ -1,6 +1,6 @@
load 30885-1.html
load 30956-1.html
asserts-if(stylo,2) load 31392-1.html # bug 1324634
load 31392-1.html
load 31694-1.html
load 31940-1.html
load 32613-1.html

View File

@ -15,7 +15,7 @@ struct WhitelistedCNNICHash {
const uint8_t hash[CNNIC_WHITELIST_HASH_LEN];
};
static const struct WhitelistedCNNICHash WhitelistedCNNICHashes[] = {
static constexpr struct WhitelistedCNNICHash WhitelistedCNNICHashes[] = {
{
{ 0x00, 0xC5, 0x9F, 0x5E, 0xF3, 0xB4, 0x6D, 0xBC, 0xA0, 0xA8, 0xBB, 0xA5, 0x0A, 0x72, 0xD4, 0xE1,
0x83, 0x9A, 0x94, 0xFB, 0x1A, 0x58, 0x5A, 0xD7, 0x2A, 0x7A, 0xAC, 0x3C, 0x72, 0x56, 0x1F, 0xC0 },

View File

@ -32,7 +32,7 @@ struct CTLogOperatorInfo
const mozilla::ct::CTLogOperatorId id;
};
const CTLogInfo kCTLogList[] = {
constexpr CTLogInfo kCTLogList[] = {
{ "Google 'Pilot' log",
mozilla::ct::CTLogStatus::Included,
0, // no disqualification time
@ -189,7 +189,7 @@ const CTLogInfo kCTLogList[] = {
91 }
};
const CTLogOperatorInfo kCTLogOperatorList[] = {
constexpr CTLogOperatorInfo kCTLogOperatorList[] = {
{ "Google", 0 },
{ "DigiCert", 1 },
{ "Certly", 2 },

View File

@ -29,7 +29,7 @@ struct nsMyTrustedEVInfo
const char* oid_name; // Set this to null to signal an invalid structure,
// (We can't have an empty list, so we'll use a dummy entry)
SECOidTag oid_tag;
const unsigned char ev_root_sha256_fingerprint[SHA256_LENGTH];
unsigned char ev_root_sha256_fingerprint[SHA256_LENGTH];
const char* issuer_base64;
const char* serial_base64;
};

View File

@ -13,7 +13,7 @@ struct CertAuthorityHash {
const int32_t binNumber;
};
static const struct CertAuthorityHash ROOT_TABLE[] = {
static constexpr struct CertAuthorityHash ROOT_TABLE[] = {
{
/* Entrust_Root_Certification_Authority___EC1 */
{ 0x02, 0xED, 0x0E, 0xB2, 0x8C, 0x14, 0xDA, 0x45, 0x16, 0x5C, 0x56, 0x67, 0x91, 0x70, 0x0D, 0x64,

Some files were not shown because too many files have changed in this diff Show More