mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-04 11:26:09 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
005011be4c
@ -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;
|
||||
},
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
|
@ -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"
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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!
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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* () {
|
||||
|
@ -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* () {
|
||||
|
@ -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* () {
|
||||
|
@ -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* () {
|
||||
|
@ -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* () {
|
||||
|
@ -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]
|
||||
|
39
dom/tests/browser/browser_largeAllocation_non_win32.js
Normal file
39
dom/tests/browser/browser_largeAllocation_non_win32.js
Normal 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();
|
||||
});
|
18
dom/tests/browser/browser_largeAllocation_win32.js
Normal file
18
dom/tests/browser/browser_largeAllocation_win32.js
Normal 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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -764,7 +764,7 @@ AssertModuleScopesMatch(ModuleObject* module)
|
||||
}
|
||||
|
||||
void
|
||||
ModuleObject::fixEnvironmentsAfterCompartmentMerge(JSContext* cx)
|
||||
ModuleObject::fixEnvironmentsAfterCompartmentMerge()
|
||||
{
|
||||
AssertModuleScopesMatch(this);
|
||||
initialEnvironment().fixEnclosingEnvironmentAfterCompartmentMerge(script()->global());
|
||||
|
@ -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;
|
||||
|
@ -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()))) {
|
||||
|
@ -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(); }
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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, ¬eIndex_))
|
||||
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, ¬eIndex))
|
||||
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, ¬eIndex))
|
||||
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;
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
59
js/src/jit-test/tests/xdr/decode-off-thread.js
Normal file
59
js/src/jit-test/tests/xdr/decode-off-thread.js
Normal 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 });
|
||||
|
||||
|
68
js/src/jit-test/tests/xdr/incremental-encoder.js
Normal file
68
js/src/jit-test/tests/xdr/incremental-encoder.js
Normal 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 });
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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. */
|
||||
|
@ -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"
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -33,6 +33,10 @@
|
||||
_(ParserCompileLazy) \
|
||||
_(ParserCompileScript) \
|
||||
_(ParserCompileModule) \
|
||||
_(DecodeScript) \
|
||||
_(DecodeFunction) \
|
||||
_(EncodeScript) \
|
||||
_(EncodeFunction) \
|
||||
_(Scripts) \
|
||||
_(VM) \
|
||||
_(CompressSource) \
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
}
|
||||
|
245
js/src/vm/Xdr.h
245
js/src/vm/Xdr.h
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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!");
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 },
|
||||
|
@ -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 },
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user