зеркало из https://github.com/mozilla/gecko-dev.git
Merge inbound to central, a=merge
MozReview-Commit-ID: E4oXfAM1mtd
This commit is contained in:
Коммит
65bbd0525a
|
@ -35,12 +35,12 @@ add_task(async function test() {
|
||||||
is(ContextualIdentityService.countContainerTabs(1), 2, "2 container tabs created with id 1");
|
is(ContextualIdentityService.countContainerTabs(1), 2, "2 container tabs created with id 1");
|
||||||
is(ContextualIdentityService.countContainerTabs(2), 1, "1 container tab created with id 2");
|
is(ContextualIdentityService.countContainerTabs(2), 1, "1 container tab created with id 2");
|
||||||
|
|
||||||
ContextualIdentityService.closeContainerTabs(1);
|
await ContextualIdentityService.closeContainerTabs(1);
|
||||||
is(ContextualIdentityService.countContainerTabs(), 1, "1 container tab created");
|
is(ContextualIdentityService.countContainerTabs(), 1, "1 container tab created");
|
||||||
is(ContextualIdentityService.countContainerTabs(1), 0, "0 container tabs created with id 1");
|
is(ContextualIdentityService.countContainerTabs(1), 0, "0 container tabs created with id 1");
|
||||||
is(ContextualIdentityService.countContainerTabs(2), 1, "1 container tab created with id 2");
|
is(ContextualIdentityService.countContainerTabs(2), 1, "1 container tab created with id 2");
|
||||||
|
|
||||||
ContextualIdentityService.closeContainerTabs();
|
await ContextualIdentityService.closeContainerTabs();
|
||||||
is(ContextualIdentityService.countContainerTabs(), 0, "0 container tabs at the end.");
|
is(ContextualIdentityService.countContainerTabs(), 0, "0 container tabs at the end.");
|
||||||
is(ContextualIdentityService.countContainerTabs(1), 0, "0 container tabs at the end with id 1.");
|
is(ContextualIdentityService.countContainerTabs(1), 0, "0 container tabs at the end with id 1.");
|
||||||
is(ContextualIdentityService.countContainerTabs(2), 0, "0 container tabs at the end with id 2.");
|
is(ContextualIdentityService.countContainerTabs(2), 0, "0 container tabs at the end with id 2.");
|
||||||
|
|
|
@ -40,7 +40,7 @@ let gContainersPane = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRemoveClick(button) {
|
async onRemoveClick(button) {
|
||||||
let userContextId = parseInt(button.getAttribute("value"), 10);
|
let userContextId = parseInt(button.getAttribute("value"), 10);
|
||||||
|
|
||||||
let count = ContextualIdentityService.countContainerTabs(userContextId);
|
let count = ContextualIdentityService.countContainerTabs(userContextId);
|
||||||
|
@ -62,7 +62,7 @@ let gContainersPane = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextualIdentityService.closeContainerTabs(userContextId);
|
await ContextualIdentityService.closeContainerTabs(userContextId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextualIdentityService.remove(userContextId);
|
ContextualIdentityService.remove(userContextId);
|
||||||
|
|
|
@ -7,8 +7,6 @@
|
||||||
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||||
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
Components.utils.import("resource://gre/modules/PluralForm.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
|
|
||||||
"resource://gre/modules/ContextualIdentityService.jsm");
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||||
"resource://gre/modules/PluralForm.jsm");
|
"resource://gre/modules/PluralForm.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
|
XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
|
||||||
|
|
|
@ -40,7 +40,7 @@ let gContainersPane = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onRemoveClick(button) {
|
async onRemoveClick(button) {
|
||||||
let userContextId = parseInt(button.getAttribute("value"), 10);
|
let userContextId = parseInt(button.getAttribute("value"), 10);
|
||||||
|
|
||||||
let count = ContextualIdentityService.countContainerTabs(userContextId);
|
let count = ContextualIdentityService.countContainerTabs(userContextId);
|
||||||
|
@ -62,7 +62,7 @@ let gContainersPane = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextualIdentityService.closeContainerTabs(userContextId);
|
await ContextualIdentityService.closeContainerTabs(userContextId);
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextualIdentityService.remove(userContextId);
|
ContextualIdentityService.remove(userContextId);
|
||||||
|
|
|
@ -87,6 +87,7 @@ var gPrivacyPane = {
|
||||||
|
|
||||||
let count = ContextualIdentityService.countContainerTabs();
|
let count = ContextualIdentityService.countContainerTabs();
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
|
ContextualIdentityService.notifyAllContainersCleared();
|
||||||
Services.prefs.setBoolPref("privacy.userContext.enabled", false);
|
Services.prefs.setBoolPref("privacy.userContext.enabled", false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -106,8 +107,10 @@ var gPrivacyPane = {
|
||||||
let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
|
let rv = Services.prompt.confirmEx(window, title, message, buttonFlags,
|
||||||
okButton, cancelButton, null, null, {});
|
okButton, cancelButton, null, null, {});
|
||||||
if (rv == 0) {
|
if (rv == 0) {
|
||||||
ContextualIdentityService.closeContainerTabs();
|
|
||||||
Services.prefs.setBoolPref("privacy.userContext.enabled", false);
|
Services.prefs.setBoolPref("privacy.userContext.enabled", false);
|
||||||
|
ContextualIdentityService.closeContainerTabs().then(() => {
|
||||||
|
ContextualIdentityService.notifyAllContainersCleared();
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,16 +12,15 @@ namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
|
||||||
Timeout::Timeout()
|
Timeout::Timeout()
|
||||||
: mCleared(false),
|
: mTimeoutId(0),
|
||||||
|
mFiringId(TimeoutManager::InvalidFiringId),
|
||||||
|
mPopupState(openAllowed),
|
||||||
|
mReason(Reason::eTimeoutOrInterval),
|
||||||
|
mNestingLevel(0),
|
||||||
|
mCleared(false),
|
||||||
mRunning(false),
|
mRunning(false),
|
||||||
mIsInterval(false),
|
mIsInterval(false),
|
||||||
mIsTracking(false),
|
mIsTracking(false)
|
||||||
mReason(Reason::eTimeoutOrInterval),
|
|
||||||
mTimeoutId(0),
|
|
||||||
mInterval(0),
|
|
||||||
mFiringId(TimeoutManager::InvalidFiringId),
|
|
||||||
mNestingLevel(0),
|
|
||||||
mPopupState(openAllowed)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ public:
|
||||||
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Timeout)
|
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(Timeout)
|
||||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Timeout)
|
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(Timeout)
|
||||||
|
|
||||||
enum class Reason
|
enum class Reason : uint8_t
|
||||||
{
|
{
|
||||||
eTimeoutOrInterval,
|
eTimeoutOrInterval,
|
||||||
eIdleCallbackTimeout,
|
eIdleCallbackTimeout,
|
||||||
|
@ -50,9 +50,52 @@ public:
|
||||||
// Can only be called when frozen.
|
// Can only be called when frozen.
|
||||||
const TimeDuration& TimeRemaining() const;
|
const TimeDuration& TimeRemaining() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// mWhen and mTimeRemaining can't be in a union, sadly, because they
|
||||||
|
// have constructors.
|
||||||
|
// Nominal time to run this timeout. Use only when timeouts are not
|
||||||
|
// frozen.
|
||||||
|
TimeStamp mWhen;
|
||||||
|
|
||||||
|
// Remaining time to wait. Used only when timeouts are frozen.
|
||||||
|
TimeDuration mTimeRemaining;
|
||||||
|
|
||||||
|
~Timeout() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Public member variables in this section. Please don't add to this list
|
||||||
|
// or mix methods with these. The interleaving public/private sections
|
||||||
|
// is necessary as we migrate members to private while still trying to
|
||||||
|
// keep decent binary packing.
|
||||||
|
|
||||||
// Window for which this timeout fires
|
// Window for which this timeout fires
|
||||||
RefPtr<nsGlobalWindow> mWindow;
|
RefPtr<nsGlobalWindow> mWindow;
|
||||||
|
|
||||||
|
// The language-specific information about the callback.
|
||||||
|
nsCOMPtr<nsITimeoutHandler> mScriptHandler;
|
||||||
|
|
||||||
|
// Interval
|
||||||
|
TimeDuration mInterval;
|
||||||
|
|
||||||
|
// Returned as value of setTimeout()
|
||||||
|
uint32_t mTimeoutId;
|
||||||
|
|
||||||
|
// Identifies which firing level this Timeout is being processed in
|
||||||
|
// when sync loops trigger nested firing.
|
||||||
|
uint32_t mFiringId;
|
||||||
|
|
||||||
|
// The popup state at timeout creation time if not created from
|
||||||
|
// another timeout
|
||||||
|
PopupControlState mPopupState;
|
||||||
|
|
||||||
|
// Used to allow several reasons for setting a timeout, where each
|
||||||
|
// 'Reason' value is using a possibly overlapping set of id:s.
|
||||||
|
Reason mReason;
|
||||||
|
|
||||||
|
// Between 0 and DOM_CLAMP_TIMEOUT_NESTING_LEVEL. Currently we don't
|
||||||
|
// care about nesting levels beyond that value.
|
||||||
|
uint8_t mNestingLevel;
|
||||||
|
|
||||||
// True if the timeout was cleared
|
// True if the timeout was cleared
|
||||||
bool mCleared;
|
bool mCleared;
|
||||||
|
|
||||||
|
@ -64,41 +107,6 @@ public:
|
||||||
|
|
||||||
// True if this is a timeout coming from a tracking script
|
// True if this is a timeout coming from a tracking script
|
||||||
bool mIsTracking;
|
bool mIsTracking;
|
||||||
|
|
||||||
// Used to allow several reasons for setting a timeout, where each
|
|
||||||
// 'Reason' value is using a possibly overlapping set of id:s.
|
|
||||||
Reason mReason;
|
|
||||||
|
|
||||||
// Returned as value of setTimeout()
|
|
||||||
uint32_t mTimeoutId;
|
|
||||||
|
|
||||||
// Interval
|
|
||||||
TimeDuration mInterval;
|
|
||||||
|
|
||||||
// Identifies which firing level this Timeout is being processed in
|
|
||||||
// when sync loops trigger nested firing.
|
|
||||||
uint32_t mFiringId;
|
|
||||||
|
|
||||||
uint32_t mNestingLevel;
|
|
||||||
|
|
||||||
// The popup state at timeout creation time if not created from
|
|
||||||
// another timeout
|
|
||||||
PopupControlState mPopupState;
|
|
||||||
|
|
||||||
// The language-specific information about the callback.
|
|
||||||
nsCOMPtr<nsITimeoutHandler> mScriptHandler;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// mWhen and mTimeRemaining can't be in a union, sadly, because they
|
|
||||||
// have constructors.
|
|
||||||
// Nominal time to run this timeout. Use only when timeouts are not
|
|
||||||
// frozen.
|
|
||||||
TimeStamp mWhen;
|
|
||||||
|
|
||||||
// Remaining time to wait. Used only when timeouts are frozen.
|
|
||||||
TimeDuration mTimeRemaining;
|
|
||||||
|
|
||||||
~Timeout() = default;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
@ -309,15 +309,14 @@ TimeoutManager::IsInvalidFiringId(uint32_t aFiringId) const
|
||||||
|
|
||||||
// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
|
// The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
|
||||||
// uses 5.
|
// uses 5.
|
||||||
#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
|
#define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5u
|
||||||
|
|
||||||
TimeDuration
|
TimeDuration
|
||||||
TimeoutManager::CalculateDelay(Timeout* aTimeout) const {
|
TimeoutManager::CalculateDelay(Timeout* aTimeout) const {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(aTimeout);
|
MOZ_DIAGNOSTIC_ASSERT(aTimeout);
|
||||||
TimeDuration result = aTimeout->mInterval;
|
TimeDuration result = aTimeout->mInterval;
|
||||||
|
|
||||||
if (aTimeout->mIsInterval ||
|
if (aTimeout->mNestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
|
||||||
aTimeout->mNestingLevel >= DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
|
|
||||||
result = TimeDuration::Max(
|
result = TimeDuration::Max(
|
||||||
result, TimeDuration::FromMilliseconds(gMinClampTimeoutValue));
|
result, TimeDuration::FromMilliseconds(gMinClampTimeoutValue));
|
||||||
}
|
}
|
||||||
|
@ -533,9 +532,8 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disallow negative intervals. If aIsInterval also disallow 0,
|
// Disallow negative intervals.
|
||||||
// because we use that as a "don't repeat" flag.
|
interval = std::max(0, interval);
|
||||||
interval = std::max(aIsInterval ? 1 : 0, interval);
|
|
||||||
|
|
||||||
// Make sure we don't proceed with an interval larger than our timer
|
// Make sure we don't proceed with an interval larger than our timer
|
||||||
// code can handle. (Note: we already forced |interval| to be non-negative,
|
// code can handle. (Note: we already forced |interval| to be non-negative,
|
||||||
|
@ -592,10 +590,8 @@ TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t nestingLevel = sNestingLevel + 1;
|
timeout->mNestingLevel = sNestingLevel < DOM_CLAMP_TIMEOUT_NESTING_LEVEL
|
||||||
if (!aIsInterval) {
|
? sNestingLevel + 1 : sNestingLevel;
|
||||||
timeout->mNestingLevel = nestingLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now clamp the actual interval we will use for the timer based on
|
// Now clamp the actual interval we will use for the timer based on
|
||||||
TimeDuration realInterval = CalculateDelay(timeout);
|
TimeDuration realInterval = CalculateDelay(timeout);
|
||||||
|
@ -986,6 +982,12 @@ TimeoutManager::RescheduleTimeout(Timeout* aTimeout,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Automatically increase the nesting level when a setInterval()
|
||||||
|
// is rescheduled just as if it was using a chained setTimeout().
|
||||||
|
if (aTimeout->mNestingLevel < DOM_CLAMP_TIMEOUT_NESTING_LEVEL) {
|
||||||
|
aTimeout->mNestingLevel += 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Compute time to next timeout for interval timer.
|
// Compute time to next timeout for interval timer.
|
||||||
// Make sure nextInterval is at least CalculateDelay().
|
// Make sure nextInterval is at least CalculateDelay().
|
||||||
TimeDuration nextInterval = CalculateDelay(aTimeout);
|
TimeDuration nextInterval = CalculateDelay(aTimeout);
|
||||||
|
|
|
@ -10545,7 +10545,7 @@ nsContentUtils::HtmlObjectContentTypeForMIMEType(const nsCString& aMIMEType,
|
||||||
return nsIObjectLoadingContent::TYPE_NULL;
|
return nsIObjectLoadingContent::TYPE_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ already_AddRefed<nsIEventTarget>
|
/* static */ already_AddRefed<nsISerialEventTarget>
|
||||||
nsContentUtils::GetEventTargetByLoadInfo(nsILoadInfo* aLoadInfo, TaskCategory aCategory)
|
nsContentUtils::GetEventTargetByLoadInfo(nsILoadInfo* aLoadInfo, TaskCategory aCategory)
|
||||||
{
|
{
|
||||||
if (NS_WARN_IF(!aLoadInfo)) {
|
if (NS_WARN_IF(!aLoadInfo)) {
|
||||||
|
@ -10555,7 +10555,7 @@ nsContentUtils::GetEventTargetByLoadInfo(nsILoadInfo* aLoadInfo, TaskCategory aC
|
||||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||||
aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
|
aLoadInfo->GetLoadingDocument(getter_AddRefs(domDoc));
|
||||||
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
|
||||||
nsCOMPtr<nsIEventTarget> target;
|
nsCOMPtr<nsISerialEventTarget> target;
|
||||||
if (doc) {
|
if (doc) {
|
||||||
if (DocGroup* group = doc->GetDocGroup()) {
|
if (DocGroup* group = doc->GetDocGroup()) {
|
||||||
target = group->EventTargetFor(aCategory);
|
target = group->EventTargetFor(aCategory);
|
||||||
|
|
|
@ -3028,7 +3028,7 @@ public:
|
||||||
bool aNoFakePlugin,
|
bool aNoFakePlugin,
|
||||||
nsIContent* aContent);
|
nsIContent* aContent);
|
||||||
|
|
||||||
static already_AddRefed<nsIEventTarget>
|
static already_AddRefed<nsISerialEventTarget>
|
||||||
GetEventTargetByLoadInfo(nsILoadInfo* aLoadInfo, mozilla::TaskCategory aCategory);
|
GetEventTargetByLoadInfo(nsILoadInfo* aLoadInfo, mozilla::TaskCategory aCategory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2598,8 +2598,18 @@ SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
|
||||||
{
|
{
|
||||||
int32_t pref = Preferences::GetInt(aPrefName, -1);
|
int32_t pref = Preferences::GetInt(aPrefName, -1);
|
||||||
// handle overflow and negative pref values
|
// handle overflow and negative pref values
|
||||||
uint32_t max = (pref <= 0 || pref >= 0x1000) ? -1 : (uint32_t)pref * 1024 * 1024;
|
CheckedInt<uint32_t> max = CheckedInt<uint32_t>(pref) * 1024 * 1024;
|
||||||
SetGCParameter(JSGC_MAX_BYTES, max);
|
SetGCParameter(JSGC_MAX_BYTES, max.isValid() ? max.value() : -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
SetMemoryNurseryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
|
||||||
|
{
|
||||||
|
int32_t pref = Preferences::GetInt(aPrefName, -1);
|
||||||
|
// handle overflow and negative pref values
|
||||||
|
CheckedInt<uint32_t> max = CheckedInt<uint32_t>(pref) * 1024;
|
||||||
|
SetGCParameter(JSGC_MAX_NURSERY_BYTES,
|
||||||
|
max.isValid() ? max.value() : JS::DefaultNurseryBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -2792,6 +2802,8 @@ nsJSContext::EnsureStatics()
|
||||||
|
|
||||||
Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback,
|
Preferences::RegisterCallbackAndCall(SetMemoryMaxPrefChangedCallback,
|
||||||
"javascript.options.mem.max");
|
"javascript.options.mem.max");
|
||||||
|
Preferences::RegisterCallbackAndCall(SetMemoryNurseryMaxPrefChangedCallback,
|
||||||
|
"javascript.options.mem.nursery.max_kb");
|
||||||
|
|
||||||
Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
|
Preferences::RegisterCallbackAndCall(SetMemoryGCModePrefChangedCallback,
|
||||||
"javascript.options.mem.gc_per_zone");
|
"javascript.options.mem.gc_per_zone");
|
||||||
|
|
|
@ -779,6 +779,8 @@ skip-if = e10s # Bug 1156489.
|
||||||
[test_text_wholeText.html]
|
[test_text_wholeText.html]
|
||||||
[test_textnode_normalize_in_selection.html]
|
[test_textnode_normalize_in_selection.html]
|
||||||
[test_textnode_split_in_selection.html]
|
[test_textnode_split_in_selection.html]
|
||||||
|
[test_timeout_clamp.html]
|
||||||
|
skip-if = debug == true && toolkit == 'android' # Timing dependent, skip slow debug android builds
|
||||||
[test_timer_flood.html]
|
[test_timer_flood.html]
|
||||||
[test_title.html]
|
[test_title.html]
|
||||||
[test_treewalker_nextsibling.xml]
|
[test_treewalker_nextsibling.xml]
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=1378586
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 1378586</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1378586">Mozilla Bug 1378586</a>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
// We need to clear our nesting level periodically. We do this by firing
|
||||||
|
// a postMessage() to get a runnable on the event loop without any setTimeout()
|
||||||
|
// nesting.
|
||||||
|
function clearNestingLevel() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
window.addEventListener('message', function onMessage() {
|
||||||
|
window.removeEventListener('message', onMessage);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
postMessage('done', '*');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function delayByTimeoutChain(iterations) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let count = 0;
|
||||||
|
function tick() {
|
||||||
|
count += 1;
|
||||||
|
if (count >= iterations) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setTimeout(tick, 0);
|
||||||
|
}
|
||||||
|
setTimeout(tick, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function delayByInterval(iterations) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
let count = 0;
|
||||||
|
function tick() {
|
||||||
|
count += 1;
|
||||||
|
if (count >= iterations) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInterval(tick, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use a very long clamp delay to make it easier to measure the change
|
||||||
|
// in automation. Some of our test servers are very slow and noisy.
|
||||||
|
const clampDelayMS = 10000;
|
||||||
|
|
||||||
|
// We expect that we will clamp on the 5th callback. This should
|
||||||
|
// be the same for both setTimeout() chains and setInterval().
|
||||||
|
const expectedClampIteration = 5;
|
||||||
|
|
||||||
|
async function runTests() {
|
||||||
|
// Things like pushPrefEnv() can use setTimeout() internally which may give
|
||||||
|
// us a nesting level. Clear the nesting level to start so this doesn't
|
||||||
|
// confuse the test.
|
||||||
|
await clearNestingLevel();
|
||||||
|
|
||||||
|
// Verify a setTimeout() chain clamps correctly
|
||||||
|
let start = performance.now();
|
||||||
|
await delayByTimeoutChain(expectedClampIteration);
|
||||||
|
let delta = performance.now() - start;
|
||||||
|
|
||||||
|
ok(delta >= clampDelayMS, "setTimeout() chain clamped");
|
||||||
|
ok(delta < (2*clampDelayMS), "setTimeout() chain did not clamp twice");
|
||||||
|
|
||||||
|
await clearNestingLevel();
|
||||||
|
|
||||||
|
// Verify setInterval() clamps correctly
|
||||||
|
start = performance.now();
|
||||||
|
await delayByInterval(expectedClampIteration);
|
||||||
|
delta = performance.now() - start;
|
||||||
|
|
||||||
|
ok(delta >= clampDelayMS, "setInterval() clamped");
|
||||||
|
ok(delta < (2*clampDelayMS), "setInterval() did not clamp twice");
|
||||||
|
|
||||||
|
await clearNestingLevel();
|
||||||
|
|
||||||
|
// Verfy a setTimeout() chain will continue to clamp past the first
|
||||||
|
// expected iteration.
|
||||||
|
const expectedDelay = (1 + expectedClampIteration) * clampDelayMS;
|
||||||
|
|
||||||
|
start = performance.now();
|
||||||
|
await delayByTimeoutChain(2 * expectedClampIteration);
|
||||||
|
delta = performance.now() - start;
|
||||||
|
|
||||||
|
ok(delta >= expectedDelay, "setTimeout() chain continued to clamp");
|
||||||
|
|
||||||
|
await clearNestingLevel();
|
||||||
|
|
||||||
|
// Verfy setInterval() will continue to clamp past the first expected
|
||||||
|
// iteration.
|
||||||
|
start = performance.now();
|
||||||
|
await delayByTimeoutChain(2 * expectedClampIteration);
|
||||||
|
delta = performance.now() - start;
|
||||||
|
|
||||||
|
ok(delta >= expectedDelay, "setInterval() continued to clamp");
|
||||||
|
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
SpecialPowers.pushPrefEnv({ 'set': [["dom.min_timeout_value", clampDelayMS]]},
|
||||||
|
runTests);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -685,7 +685,7 @@ DefineUnforgeableAttributes(JSContext* cx, JS::Handle<JSObject*> obj,
|
||||||
// funToString ObjectOps member for interface objects.
|
// funToString ObjectOps member for interface objects.
|
||||||
JSString*
|
JSString*
|
||||||
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
|
InterfaceObjectToString(JSContext* aCx, JS::Handle<JSObject*> aObject,
|
||||||
unsigned /* indent */)
|
bool /* isToSource */)
|
||||||
{
|
{
|
||||||
const js::Class* clasp = js::GetObjectClass(aObject);
|
const js::Class* clasp = js::GetObjectClass(aObject);
|
||||||
MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
|
MOZ_ASSERT(IsDOMIfaceAndProtoClass(clasp));
|
||||||
|
|
|
@ -41,7 +41,7 @@ codegen_dependencies := \
|
||||||
$(GLOBAL_DEPS) \
|
$(GLOBAL_DEPS) \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
include codegen.pp
|
-include codegen.pp
|
||||||
|
|
||||||
codegen.pp: $(codegen_dependencies)
|
codegen.pp: $(codegen_dependencies)
|
||||||
$(call py_action,webidl,$(srcdir))
|
$(call py_action,webidl,$(srcdir))
|
||||||
|
|
|
@ -2462,6 +2462,10 @@ TabChild::InternalSetDocShellIsActive(bool aIsActive, bool aPreserveLayers)
|
||||||
if (aIsActive) {
|
if (aIsActive) {
|
||||||
MakeVisible();
|
MakeVisible();
|
||||||
|
|
||||||
|
if (!docShell) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We don't use TabChildBase::GetPresShell() here because that would create
|
// We don't use TabChildBase::GetPresShell() here because that would create
|
||||||
// a content viewer if one doesn't exist yet. Creating a content viewer can
|
// a content viewer if one doesn't exist yet. Creating a content viewer can
|
||||||
// cause JS to run, which we want to avoid. nsIDocShell::GetPresShell
|
// cause JS to run, which we want to avoid. nsIDocShell::GetPresShell
|
||||||
|
|
|
@ -11,3 +11,4 @@ support-files =
|
||||||
[test_nosniff.html]
|
[test_nosniff.html]
|
||||||
[test_block_script_wrong_mime.html]
|
[test_block_script_wrong_mime.html]
|
||||||
[test_block_toplevel_data_navigation.html]
|
[test_block_toplevel_data_navigation.html]
|
||||||
|
skip-if = toolkit == 'android' # intermittent failure
|
||||||
|
|
|
@ -143,20 +143,12 @@ ServiceWorkerContainer::Register(const nsAString& aScriptURL,
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> baseURI;
|
nsCOMPtr<nsIURI> baseURI;
|
||||||
|
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
|
||||||
nsIDocument* doc = GetEntryDocument();
|
if (window) {
|
||||||
if (doc) {
|
baseURI = window->GetDocBaseURI();
|
||||||
baseURI = doc->GetBaseURI();
|
|
||||||
} else {
|
} else {
|
||||||
// XXXnsm. One of our devtools browser test calls register() from a content
|
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||||
// script where there is no valid entry document. Use the window to resolve
|
return nullptr;
|
||||||
// the uri in that case.
|
|
||||||
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
|
|
||||||
nsCOMPtr<nsPIDOMWindowOuter> outerWindow;
|
|
||||||
if (window && (outerWindow = window->GetOuterWindow()) &&
|
|
||||||
outerWindow->GetServiceWorkersTestingEnabled()) {
|
|
||||||
baseURI = window->GetDocBaseURI();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
#include "mozilla/mscom/EnsureMTA.h"
|
#include "mozilla/mscom/EnsureMTA.h"
|
||||||
|
#include "mozilla/SystemGroup.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
#include "nsError.h"
|
#include "nsError.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
@ -41,9 +42,10 @@ struct MainThreadRelease
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
DebugOnly<nsresult> rv =
|
DebugOnly<nsresult> rv =
|
||||||
NS_DispatchToMainThread(NewNonOwningRunnableMethod("mscom::MainThreadRelease",
|
SystemGroup::Dispatch("mscom::MainThreadRelease",
|
||||||
aPtr,
|
TaskCategory::Other,
|
||||||
&T::Release));
|
NewNonOwningRunnableMethod("mscom::MainThreadRelease",
|
||||||
|
aPtr, &T::Release));
|
||||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -436,7 +436,7 @@ typedef bool
|
||||||
* that object. A null return value means OOM.
|
* that object. A null return value means OOM.
|
||||||
*/
|
*/
|
||||||
typedef JSString*
|
typedef JSString*
|
||||||
(* JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, unsigned indent);
|
(* JSFunToStringOp)(JSContext* cx, JS::HandleObject obj, bool isToSource);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a lazy property named by id in obj by defining it directly in obj.
|
* Resolve a lazy property named by id in obj by defining it directly in obj.
|
||||||
|
|
|
@ -65,7 +65,7 @@ namespace JS {
|
||||||
D(RESET) \
|
D(RESET) \
|
||||||
D(OUT_OF_NURSERY) \
|
D(OUT_OF_NURSERY) \
|
||||||
D(EVICT_NURSERY) \
|
D(EVICT_NURSERY) \
|
||||||
D(UNUSED0) \
|
D(DELAYED_ATOMS_GC) \
|
||||||
D(SHARED_MEMORY_LIMIT) \
|
D(SHARED_MEMORY_LIMIT) \
|
||||||
D(UNUSED1) \
|
D(UNUSED1) \
|
||||||
D(INCREMENTAL_TOO_SLOW) \
|
D(INCREMENTAL_TOO_SLOW) \
|
||||||
|
|
|
@ -101,10 +101,14 @@ MOZ_ALWAYS_INLINE bool IsInsideNursery(const js::gc::Cell* cell);
|
||||||
namespace JS {
|
namespace JS {
|
||||||
struct Zone;
|
struct Zone;
|
||||||
|
|
||||||
/* Default size for the generational nursery in bytes. */
|
/*
|
||||||
|
* Default size for the generational nursery in bytes.
|
||||||
|
* This is the initial nursery size, when running in the browser this is
|
||||||
|
* updated by JS_SetGCParameter().
|
||||||
|
*/
|
||||||
const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
|
const uint32_t DefaultNurseryBytes = 16 * js::gc::ChunkSize;
|
||||||
|
|
||||||
/* Default maximum heap size in bytes to pass to JS_NewRuntime(). */
|
/* Default maximum heap size in bytes to pass to JS_NewContext(). */
|
||||||
const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
|
const uint32_t DefaultHeapMaxBytes = 32 * 1024 * 1024;
|
||||||
|
|
||||||
namespace shadow {
|
namespace shadow {
|
||||||
|
|
|
@ -50,7 +50,7 @@ JS_SetICUMemoryFunctions(JS_ICUAllocFn allocFn,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize SpiderMonkey, returning true only if initialization succeeded.
|
* Initialize SpiderMonkey, returning true only if initialization succeeded.
|
||||||
* Once this method has succeeded, it is safe to call JS_NewRuntime and other
|
* Once this method has succeeded, it is safe to call JS_NewContext and other
|
||||||
* JSAPI methods.
|
* JSAPI methods.
|
||||||
*
|
*
|
||||||
* This method must be called before any other JSAPI method is used on any
|
* This method must be called before any other JSAPI method is used on any
|
||||||
|
|
|
@ -330,7 +330,7 @@ class JS_FRIEND_API(BaseProxyHandler)
|
||||||
ESClass* cls) const;
|
ESClass* cls) const;
|
||||||
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
|
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
|
||||||
virtual const char* className(JSContext* cx, HandleObject proxy) const;
|
virtual const char* className(JSContext* cx, HandleObject proxy) const;
|
||||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
|
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const;
|
||||||
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const;
|
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const;
|
||||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
|
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
|
||||||
virtual void trace(JSTracer* trc, JSObject* proxy) const;
|
virtual void trace(JSTracer* trc, JSObject* proxy) const;
|
||||||
|
|
|
@ -140,7 +140,11 @@ class GCSchedulingTunables
|
||||||
*/
|
*/
|
||||||
UnprotectedData<size_t> gcMaxBytes_;
|
UnprotectedData<size_t> gcMaxBytes_;
|
||||||
|
|
||||||
/* Maximum nursery size for each zone group. */
|
/*
|
||||||
|
* Maximum nursery size for each zone group.
|
||||||
|
* Initially DefaultNurseryBytes and can be set by
|
||||||
|
* javascript.options.mem.nursery.max_kb
|
||||||
|
*/
|
||||||
ActiveThreadData<size_t> gcMaxNurseryBytes_;
|
ActiveThreadData<size_t> gcMaxNurseryBytes_;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -709,11 +713,7 @@ class GCRuntime
|
||||||
|
|
||||||
bool canChangeActiveContext(JSContext* cx);
|
bool canChangeActiveContext(JSContext* cx);
|
||||||
|
|
||||||
void triggerFullGCForAtoms() {
|
void triggerFullGCForAtoms(JSContext* cx);
|
||||||
MOZ_ASSERT(fullGCForAtomsRequested_);
|
|
||||||
fullGCForAtomsRequested_ = false;
|
|
||||||
MOZ_RELEASE_ASSERT(triggerGC(JS::gcreason::ALLOC_TRIGGER));
|
|
||||||
}
|
|
||||||
|
|
||||||
void runDebugGC();
|
void runDebugGC();
|
||||||
void notifyRootsRemoved();
|
void notifyRootsRemoved();
|
||||||
|
|
|
@ -646,6 +646,11 @@ js::Nursery::collect(JS::gcreason::Reason reason)
|
||||||
// because gcBytes >= gcMaxBytes.
|
// because gcBytes >= gcMaxBytes.
|
||||||
if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes())
|
if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes())
|
||||||
disable();
|
disable();
|
||||||
|
// Disable the nursery if the user changed the configuration setting. The
|
||||||
|
// nursery can only be re-enabled by resetting the configurationa and
|
||||||
|
// restarting firefox.
|
||||||
|
if (maxNurseryChunks_ == 0)
|
||||||
|
disable();
|
||||||
|
|
||||||
endProfile(ProfileKey::Total);
|
endProfile(ProfileKey::Total);
|
||||||
minorGcCount_++;
|
minorGcCount_++;
|
||||||
|
@ -924,6 +929,7 @@ js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRat
|
||||||
{
|
{
|
||||||
static const double GrowThreshold = 0.05;
|
static const double GrowThreshold = 0.05;
|
||||||
static const double ShrinkThreshold = 0.01;
|
static const double ShrinkThreshold = 0.01;
|
||||||
|
unsigned newMaxNurseryChunks;
|
||||||
|
|
||||||
// Shrink the nursery to its minimum size of we ran out of memory or
|
// Shrink the nursery to its minimum size of we ran out of memory or
|
||||||
// received a memory pressure event.
|
// received a memory pressure event.
|
||||||
|
@ -932,10 +938,30 @@ js::Nursery::maybeResizeNursery(JS::gcreason::Reason reason, double promotionRat
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef JS_GC_ZEAL
|
||||||
|
// This zeal mode disabled nursery resizing.
|
||||||
|
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
|
||||||
|
return;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
newMaxNurseryChunks = runtime()->gc.tunables.gcMaxNurseryBytes() >> ChunkShift;
|
||||||
|
if (newMaxNurseryChunks != maxNurseryChunks_) {
|
||||||
|
maxNurseryChunks_ = newMaxNurseryChunks;
|
||||||
|
/* The configured maximum nursery size is changing */
|
||||||
|
int extraChunks = numChunks() - newMaxNurseryChunks;
|
||||||
|
if (extraChunks > 0) {
|
||||||
|
/* We need to shrink the nursery */
|
||||||
|
shrinkAllocableSpace(extraChunks);
|
||||||
|
|
||||||
|
previousPromotionRate_ = promotionRate;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (promotionRate > GrowThreshold)
|
if (promotionRate > GrowThreshold)
|
||||||
growAllocableSpace();
|
growAllocableSpace();
|
||||||
else if (promotionRate < ShrinkThreshold && previousPromotionRate_ < ShrinkThreshold)
|
else if (promotionRate < ShrinkThreshold && previousPromotionRate_ < ShrinkThreshold)
|
||||||
shrinkAllocableSpace();
|
shrinkAllocableSpace(1);
|
||||||
|
|
||||||
previousPromotionRate_ = promotionRate;
|
previousPromotionRate_ = promotionRate;
|
||||||
}
|
}
|
||||||
|
@ -947,13 +973,13 @@ js::Nursery::growAllocableSpace()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
js::Nursery::shrinkAllocableSpace()
|
js::Nursery::shrinkAllocableSpace(unsigned removeNumChunks)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
#ifdef JS_GC_ZEAL
|
||||||
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
|
if (runtime()->hasZealMode(ZealMode::GenerationalGC))
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
updateNumChunks(Max(numChunks() - 1, 1u));
|
updateNumChunks(Max(numChunks() - removeNumChunks, 1u));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -468,7 +468,7 @@ class Nursery
|
||||||
/* Change the allocable space provided by the nursery. */
|
/* Change the allocable space provided by the nursery. */
|
||||||
void maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate);
|
void maybeResizeNursery(JS::gcreason::Reason reason, double promotionRate);
|
||||||
void growAllocableSpace();
|
void growAllocableSpace();
|
||||||
void shrinkAllocableSpace();
|
void shrinkAllocableSpace(unsigned removeNumChunks);
|
||||||
void minimizeAllocableSpace();
|
void minimizeAllocableSpace();
|
||||||
|
|
||||||
/* Profile recording and printing. */
|
/* Profile recording and printing. */
|
||||||
|
|
|
@ -43,6 +43,7 @@ JS::Zone::Zone(JSRuntime* rt, ZoneGroup* group)
|
||||||
markedAtoms_(group),
|
markedAtoms_(group),
|
||||||
atomCache_(group),
|
atomCache_(group),
|
||||||
externalStringCache_(group),
|
externalStringCache_(group),
|
||||||
|
functionToStringCache_(group),
|
||||||
usage(&rt->gc.usage),
|
usage(&rt->gc.usage),
|
||||||
threshold(),
|
threshold(),
|
||||||
gcDelayBytes(0),
|
gcDelayBytes(0),
|
||||||
|
|
|
@ -107,6 +107,31 @@ class MOZ_NON_TEMPORARY_CLASS ExternalStringCache
|
||||||
MOZ_ALWAYS_INLINE void put(JSString* s);
|
MOZ_ALWAYS_INLINE void put(JSString* s);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class MOZ_NON_TEMPORARY_CLASS FunctionToStringCache
|
||||||
|
{
|
||||||
|
struct Entry {
|
||||||
|
JSScript* script;
|
||||||
|
JSString* string;
|
||||||
|
|
||||||
|
void set(JSScript* scriptArg, JSString* stringArg) {
|
||||||
|
script = scriptArg;
|
||||||
|
string = stringArg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static const size_t NumEntries = 2;
|
||||||
|
mozilla::Array<Entry, NumEntries> entries_;
|
||||||
|
|
||||||
|
FunctionToStringCache(const FunctionToStringCache&) = delete;
|
||||||
|
void operator=(const FunctionToStringCache&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FunctionToStringCache() { purge(); }
|
||||||
|
void purge() { mozilla::PodArrayZero(entries_); }
|
||||||
|
|
||||||
|
MOZ_ALWAYS_INLINE JSString* lookup(JSScript* script) const;
|
||||||
|
MOZ_ALWAYS_INLINE void put(JSScript* script, JSString* string);
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace js
|
} // namespace js
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -210,11 +235,13 @@ struct Zone : public JS::shadow::Zone,
|
||||||
|
|
||||||
void scheduleGC() { MOZ_ASSERT(!CurrentThreadIsHeapBusy()); gcScheduled_ = true; }
|
void scheduleGC() { MOZ_ASSERT(!CurrentThreadIsHeapBusy()); gcScheduled_ = true; }
|
||||||
void unscheduleGC() { gcScheduled_ = false; }
|
void unscheduleGC() { gcScheduled_ = false; }
|
||||||
bool isGCScheduled() { return gcScheduled_ && canCollect(); }
|
bool isGCScheduled() { return gcScheduled_; }
|
||||||
|
|
||||||
void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
|
void setPreservingCode(bool preserving) { gcPreserveCode_ = preserving; }
|
||||||
bool isPreservingCode() const { return gcPreserveCode_; }
|
bool isPreservingCode() const { return gcPreserveCode_; }
|
||||||
|
|
||||||
|
// Whether this zone can currently be collected. This doesn't take account
|
||||||
|
// of AutoKeepAtoms for the atoms zone.
|
||||||
bool canCollect();
|
bool canCollect();
|
||||||
|
|
||||||
void changeGCState(GCState prev, GCState next) {
|
void changeGCState(GCState prev, GCState next) {
|
||||||
|
@ -443,6 +470,9 @@ struct Zone : public JS::shadow::Zone,
|
||||||
// Cache storing allocated external strings. Purged on GC.
|
// Cache storing allocated external strings. Purged on GC.
|
||||||
js::ZoneGroupOrGCTaskData<js::ExternalStringCache> externalStringCache_;
|
js::ZoneGroupOrGCTaskData<js::ExternalStringCache> externalStringCache_;
|
||||||
|
|
||||||
|
// Cache for Function.prototype.toString. Purged on GC.
|
||||||
|
js::ZoneGroupOrGCTaskData<js::FunctionToStringCache> functionToStringCache_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
js::SparseBitmap& markedAtoms() { return markedAtoms_.ref(); }
|
js::SparseBitmap& markedAtoms() { return markedAtoms_.ref(); }
|
||||||
|
|
||||||
|
@ -450,6 +480,8 @@ struct Zone : public JS::shadow::Zone,
|
||||||
|
|
||||||
js::ExternalStringCache& externalStringCache() { return externalStringCache_.ref(); };
|
js::ExternalStringCache& externalStringCache() { return externalStringCache_.ref(); };
|
||||||
|
|
||||||
|
js::FunctionToStringCache& functionToStringCache() { return functionToStringCache_.ref(); }
|
||||||
|
|
||||||
// Track heap usage under this Zone.
|
// Track heap usage under this Zone.
|
||||||
js::gc::HeapUsage usage;
|
js::gc::HeapUsage usage;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Exercise triggering GC of atoms zone while off-thread parsing is happening.
|
||||||
|
|
||||||
|
if (helperThreadCount() === 0)
|
||||||
|
quit();
|
||||||
|
|
||||||
|
gczeal(0);
|
||||||
|
|
||||||
|
// Reduce some GC parameters so that we can trigger a GC more easily.
|
||||||
|
gcparam('lowFrequencyHeapGrowth', 120);
|
||||||
|
gcparam('highFrequencyHeapGrowthMin', 120);
|
||||||
|
gcparam('highFrequencyHeapGrowthMax', 120);
|
||||||
|
gcparam('allocationThreshold', 1);
|
||||||
|
gc();
|
||||||
|
|
||||||
|
// Start an off-thread parse.
|
||||||
|
offThreadCompileScript("print('Finished')");
|
||||||
|
|
||||||
|
// Allocate lots of atoms, parsing occasionally.
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
print(i);
|
||||||
|
for (let j = 0; j < 10000; j++)
|
||||||
|
Symbol.for(i + 10 * j);
|
||||||
|
eval(`${i}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finish the off-thread parse.
|
||||||
|
runOffThreadScript();
|
|
@ -0,0 +1,20 @@
|
||||||
|
function test() {
|
||||||
|
var count = 0;
|
||||||
|
function f(x) {
|
||||||
|
"use strict";
|
||||||
|
if (x) {
|
||||||
|
Object.seal(this);
|
||||||
|
}
|
||||||
|
this[0] = 1;
|
||||||
|
}
|
||||||
|
for (var y of [1, 0, arguments, 1]) {
|
||||||
|
try {
|
||||||
|
var o = new f(y);
|
||||||
|
} catch (e) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEq(count, 3);
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
|
@ -1506,6 +1506,119 @@ BaselineCacheIRCompiler::emitStoreDenseElementHole()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
BaselineCacheIRCompiler::emitArrayPush()
|
||||||
|
{
|
||||||
|
ObjOperandId objId = reader.objOperandId();
|
||||||
|
ValOperandId rhsId = reader.valOperandId();
|
||||||
|
|
||||||
|
// Allocate the fixed registers first. These need to be fixed for
|
||||||
|
// callTypeUpdateIC.
|
||||||
|
AutoScratchRegister scratch(allocator, masm, R1.scratchReg());
|
||||||
|
ValueOperand val = allocator.useFixedValueRegister(masm, rhsId, R0);
|
||||||
|
|
||||||
|
Register obj = allocator.useRegister(masm, objId);
|
||||||
|
AutoScratchRegister scratchLength(allocator, masm);
|
||||||
|
|
||||||
|
FailurePath* failure;
|
||||||
|
if (!addFailurePath(&failure))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load obj->elements in scratch.
|
||||||
|
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
|
||||||
|
masm.load32(Address(scratch, ObjectElements::offsetOfLength()), scratchLength);
|
||||||
|
|
||||||
|
BaseObjectElementIndex element(scratch, scratchLength);
|
||||||
|
Address initLength(scratch, ObjectElements::offsetOfInitializedLength());
|
||||||
|
Address elementsFlags(scratch, ObjectElements::offsetOfFlags());
|
||||||
|
|
||||||
|
// Check for copy-on-write or frozen elements.
|
||||||
|
masm.branchTest32(Assembler::NonZero, elementsFlags,
|
||||||
|
Imm32(ObjectElements::COPY_ON_WRITE |
|
||||||
|
ObjectElements::FROZEN),
|
||||||
|
failure->label());
|
||||||
|
|
||||||
|
// Fail if length != initLength.
|
||||||
|
masm.branch32(Assembler::NotEqual, initLength, scratchLength, failure->label());
|
||||||
|
|
||||||
|
// If scratchLength < capacity, we can add a dense element inline. If not we
|
||||||
|
// need to allocate more elements.
|
||||||
|
Label capacityOk;
|
||||||
|
Address capacity(scratch, ObjectElements::offsetOfCapacity());
|
||||||
|
masm.branch32(Assembler::Above, capacity, scratchLength, &capacityOk);
|
||||||
|
|
||||||
|
// Check for non-writable array length. We only have to do this if
|
||||||
|
// index >= capacity.
|
||||||
|
masm.branchTest32(Assembler::NonZero, elementsFlags,
|
||||||
|
Imm32(ObjectElements::NONWRITABLE_ARRAY_LENGTH),
|
||||||
|
failure->label());
|
||||||
|
|
||||||
|
LiveRegisterSet save(GeneralRegisterSet::Volatile(), liveVolatileFloatRegs());
|
||||||
|
save.takeUnchecked(scratch);
|
||||||
|
masm.PushRegsInMask(save);
|
||||||
|
|
||||||
|
masm.setupUnalignedABICall(scratch);
|
||||||
|
masm.loadJSContext(scratch);
|
||||||
|
masm.passABIArg(scratch);
|
||||||
|
masm.passABIArg(obj);
|
||||||
|
masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, NativeObject::addDenseElementDontReportOOM));
|
||||||
|
masm.mov(ReturnReg, scratch);
|
||||||
|
|
||||||
|
masm.PopRegsInMask(save);
|
||||||
|
masm.branchIfFalseBool(scratch, failure->label());
|
||||||
|
|
||||||
|
// Load the reallocated elements pointer.
|
||||||
|
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
|
||||||
|
|
||||||
|
masm.bind(&capacityOk);
|
||||||
|
|
||||||
|
// Check if we have to convert a double element.
|
||||||
|
Label noConversion;
|
||||||
|
masm.branchTest32(Assembler::Zero, elementsFlags,
|
||||||
|
Imm32(ObjectElements::CONVERT_DOUBLE_ELEMENTS),
|
||||||
|
&noConversion);
|
||||||
|
|
||||||
|
// We need to convert int32 values being stored into doubles. Note that
|
||||||
|
// double arrays are only created by IonMonkey, so if we have no FP support
|
||||||
|
// Ion is disabled and there should be no double arrays.
|
||||||
|
if (cx_->runtime()->jitSupportsFloatingPoint) {
|
||||||
|
// It's fine to convert the value in place in Baseline. We can't do
|
||||||
|
// this in Ion.
|
||||||
|
masm.convertInt32ValueToDouble(val);
|
||||||
|
} else {
|
||||||
|
masm.assumeUnreachable("There shouldn't be double arrays when there is no FP support.");
|
||||||
|
}
|
||||||
|
|
||||||
|
masm.bind(&noConversion);
|
||||||
|
|
||||||
|
// Call the type update IC. After this everything must be infallible as we
|
||||||
|
// don't save all registers here.
|
||||||
|
LiveGeneralRegisterSet saveRegs;
|
||||||
|
saveRegs.add(obj);
|
||||||
|
saveRegs.add(val);
|
||||||
|
if (!callTypeUpdateIC(obj, val, scratch, saveRegs))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Reload obj->elements as callTypeUpdateIC used the scratch register.
|
||||||
|
masm.loadPtr(Address(obj, NativeObject::offsetOfElements()), scratch);
|
||||||
|
|
||||||
|
// Increment initLength and length.
|
||||||
|
Address length(scratch, ObjectElements::offsetOfLength());
|
||||||
|
masm.add32(Imm32(1), initLength);
|
||||||
|
masm.load32(length, scratchLength);
|
||||||
|
masm.add32(Imm32(1), length);
|
||||||
|
|
||||||
|
// Store the value.
|
||||||
|
masm.storeValue(val, element);
|
||||||
|
emitPostBarrierElement(obj, val, scratch, scratchLength);
|
||||||
|
|
||||||
|
// Return value is new length.
|
||||||
|
masm.add32(Imm32(1), scratchLength);
|
||||||
|
masm.tagValue(JSVAL_TYPE_INT32, scratchLength, val);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
BaselineCacheIRCompiler::emitStoreTypedElement()
|
BaselineCacheIRCompiler::emitStoreTypedElement()
|
||||||
{
|
{
|
||||||
|
@ -2098,9 +2211,10 @@ BaselineCacheIRCompiler::init(CacheKind kind)
|
||||||
static const size_t MaxOptimizedCacheIRStubs = 16;
|
static const size_t MaxOptimizedCacheIRStubs = 16;
|
||||||
|
|
||||||
ICStub*
|
ICStub*
|
||||||
jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
js::jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
CacheKind kind, ICStubEngine engine, JSScript* outerScript,
|
CacheKind kind, BaselineCacheIRStubKind stubKind,
|
||||||
ICFallbackStub* stub, bool* attached)
|
ICStubEngine engine, JSScript* outerScript,
|
||||||
|
ICFallbackStub* stub, bool* attached)
|
||||||
{
|
{
|
||||||
// We shouldn't GC or report OOM (or any other exception) here.
|
// We shouldn't GC or report OOM (or any other exception) here.
|
||||||
AutoAssertNoPendingException aanpe(cx);
|
AutoAssertNoPendingException aanpe(cx);
|
||||||
|
@ -2115,33 +2229,16 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
// unlimited number of stubs.
|
// unlimited number of stubs.
|
||||||
MOZ_ASSERT(stub->numOptimizedStubs() < MaxOptimizedCacheIRStubs);
|
MOZ_ASSERT(stub->numOptimizedStubs() < MaxOptimizedCacheIRStubs);
|
||||||
|
|
||||||
enum class CacheIRStubKind { Regular, Monitored, Updated };
|
uint32_t stubDataOffset = 0;
|
||||||
|
switch (stubKind) {
|
||||||
uint32_t stubDataOffset;
|
case BaselineCacheIRStubKind::Monitored:
|
||||||
CacheIRStubKind stubKind;
|
|
||||||
switch (kind) {
|
|
||||||
case CacheKind::Compare:
|
|
||||||
case CacheKind::In:
|
|
||||||
case CacheKind::HasOwn:
|
|
||||||
case CacheKind::BindName:
|
|
||||||
case CacheKind::TypeOf:
|
|
||||||
case CacheKind::GetIterator:
|
|
||||||
stubDataOffset = sizeof(ICCacheIR_Regular);
|
|
||||||
stubKind = CacheIRStubKind::Regular;
|
|
||||||
break;
|
|
||||||
case CacheKind::GetProp:
|
|
||||||
case CacheKind::GetElem:
|
|
||||||
case CacheKind::GetName:
|
|
||||||
case CacheKind::GetPropSuper:
|
|
||||||
case CacheKind::GetElemSuper:
|
|
||||||
case CacheKind::Call:
|
|
||||||
stubDataOffset = sizeof(ICCacheIR_Monitored);
|
stubDataOffset = sizeof(ICCacheIR_Monitored);
|
||||||
stubKind = CacheIRStubKind::Monitored;
|
|
||||||
break;
|
break;
|
||||||
case CacheKind::SetProp:
|
case BaselineCacheIRStubKind::Regular:
|
||||||
case CacheKind::SetElem:
|
stubDataOffset = sizeof(ICCacheIR_Regular);
|
||||||
|
break;
|
||||||
|
case BaselineCacheIRStubKind::Updated:
|
||||||
stubDataOffset = sizeof(ICCacheIR_Updated);
|
stubDataOffset = sizeof(ICCacheIR_Updated);
|
||||||
stubKind = CacheIRStubKind::Updated;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2186,7 +2283,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
|
for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
|
||||||
bool updated = false;
|
bool updated = false;
|
||||||
switch (stubKind) {
|
switch (stubKind) {
|
||||||
case CacheIRStubKind::Regular: {
|
case BaselineCacheIRStubKind::Regular: {
|
||||||
if (!iter->isCacheIR_Regular())
|
if (!iter->isCacheIR_Regular())
|
||||||
continue;
|
continue;
|
||||||
auto otherStub = iter->toCacheIR_Regular();
|
auto otherStub = iter->toCacheIR_Regular();
|
||||||
|
@ -2196,7 +2293,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CacheIRStubKind::Monitored: {
|
case BaselineCacheIRStubKind::Monitored: {
|
||||||
if (!iter->isCacheIR_Monitored())
|
if (!iter->isCacheIR_Monitored())
|
||||||
continue;
|
continue;
|
||||||
auto otherStub = iter->toCacheIR_Monitored();
|
auto otherStub = iter->toCacheIR_Monitored();
|
||||||
|
@ -2206,7 +2303,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
continue;
|
continue;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case CacheIRStubKind::Updated: {
|
case BaselineCacheIRStubKind::Updated: {
|
||||||
if (!iter->isCacheIR_Updated())
|
if (!iter->isCacheIR_Updated())
|
||||||
continue;
|
continue;
|
||||||
auto otherStub = iter->toCacheIR_Updated();
|
auto otherStub = iter->toCacheIR_Updated();
|
||||||
|
@ -2237,14 +2334,14 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
switch (stubKind) {
|
switch (stubKind) {
|
||||||
case CacheIRStubKind::Regular: {
|
case BaselineCacheIRStubKind::Regular: {
|
||||||
auto newStub = new(newStubMem) ICCacheIR_Regular(code, stubInfo);
|
auto newStub = new(newStubMem) ICCacheIR_Regular(code, stubInfo);
|
||||||
writer.copyStubData(newStub->stubDataStart());
|
writer.copyStubData(newStub->stubDataStart());
|
||||||
stub->addNewStub(newStub);
|
stub->addNewStub(newStub);
|
||||||
*attached = true;
|
*attached = true;
|
||||||
return newStub;
|
return newStub;
|
||||||
}
|
}
|
||||||
case CacheIRStubKind::Monitored: {
|
case BaselineCacheIRStubKind::Monitored: {
|
||||||
ICStub* monitorStub =
|
ICStub* monitorStub =
|
||||||
stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
|
stub->toMonitoredFallbackStub()->fallbackMonitorStub()->firstMonitorStub();
|
||||||
auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
|
auto newStub = new(newStubMem) ICCacheIR_Monitored(code, monitorStub, stubInfo);
|
||||||
|
@ -2253,7 +2350,7 @@ jit::AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
*attached = true;
|
*attached = true;
|
||||||
return newStub;
|
return newStub;
|
||||||
}
|
}
|
||||||
case CacheIRStubKind::Updated: {
|
case BaselineCacheIRStubKind::Updated: {
|
||||||
auto newStub = new(newStubMem) ICCacheIR_Updated(code, stubInfo);
|
auto newStub = new(newStubMem) ICCacheIR_Updated(code, stubInfo);
|
||||||
if (!newStub->initUpdatingChain(cx, stubSpace)) {
|
if (!newStub->initUpdatingChain(cx, stubSpace)) {
|
||||||
cx->recoverFromOutOfMemory();
|
cx->recoverFromOutOfMemory();
|
||||||
|
|
|
@ -17,8 +17,11 @@ namespace jit {
|
||||||
class ICFallbackStub;
|
class ICFallbackStub;
|
||||||
class ICStub;
|
class ICStub;
|
||||||
|
|
||||||
|
enum class BaselineCacheIRStubKind { Regular, Monitored, Updated };
|
||||||
|
|
||||||
ICStub* AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
ICStub* AttachBaselineCacheIRStub(JSContext* cx, const CacheIRWriter& writer,
|
||||||
CacheKind kind, ICStubEngine engine, JSScript* outerScript,
|
CacheKind kind, BaselineCacheIRStubKind stubKind,
|
||||||
|
ICStubEngine engine, JSScript* outerScript,
|
||||||
ICFallbackStub* stub, bool* attached);
|
ICFallbackStub* stub, bool* attached);
|
||||||
|
|
||||||
} // namespace jit
|
} // namespace jit
|
||||||
|
|
|
@ -814,6 +814,7 @@ DoGetElemFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback* stub_
|
||||||
&isTemporarilyUnoptimizable, lhs, rhs, lhs, CanAttachGetter::Yes);
|
&isTemporarilyUnoptimizable, lhs, rhs, lhs, CanAttachGetter::Yes);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Monitored,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
@ -886,6 +887,7 @@ DoGetElemSuperFallback(JSContext* cx, BaselineFrame* frame, ICGetElem_Fallback*
|
||||||
CanAttachGetter::Yes);
|
CanAttachGetter::Yes);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Monitored,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
@ -1051,6 +1053,7 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||||
&isTemporarilyUnoptimizable, objv, index, rhs);
|
&isTemporarilyUnoptimizable, objv, index, rhs);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Updated,
|
||||||
ICStubEngine::Baseline, frame->script(),
|
ICStubEngine::Baseline, frame->script(),
|
||||||
stub, &attached);
|
stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
|
@ -1114,6 +1117,7 @@ DoSetElemFallback(JSContext* cx, BaselineFrame* frame, ICSetElem_Fallback* stub_
|
||||||
&isTemporarilyUnoptimizable, objv, index, rhs);
|
&isTemporarilyUnoptimizable, objv, index, rhs);
|
||||||
if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
|
if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Updated,
|
||||||
ICStubEngine::Baseline, frame->script(),
|
ICStubEngine::Baseline, frame->script(),
|
||||||
stub, &attached);
|
stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
|
@ -1311,6 +1315,7 @@ DoInFallback(JSContext* cx, BaselineFrame* frame, ICIn_Fallback* stub_,
|
||||||
bool attached = false;
|
bool attached = false;
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Regular,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub)
|
if (newStub)
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
@ -1379,6 +1384,7 @@ DoHasOwnFallback(JSContext* cx, BaselineFrame* frame, ICHasOwn_Fallback* stub_,
|
||||||
bool attached = false;
|
bool attached = false;
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Regular,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub)
|
if (newStub)
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
@ -1450,6 +1456,7 @@ DoGetNameFallback(JSContext* cx, BaselineFrame* frame, ICGetName_Fallback* stub_
|
||||||
GetNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
|
GetNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Monitored,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub)
|
if (newStub)
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
@ -1529,6 +1536,7 @@ DoBindNameFallback(JSContext* cx, BaselineFrame* frame, ICBindName_Fallback* stu
|
||||||
BindNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
|
BindNameIRGenerator gen(cx, script, pc, stub->state().mode(), envChain, name);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Regular,
|
||||||
ICStubEngine::Baseline, script, stub,
|
ICStubEngine::Baseline, script, stub,
|
||||||
&attached);
|
&attached);
|
||||||
if (newStub)
|
if (newStub)
|
||||||
|
@ -1703,6 +1711,7 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
|
||||||
&isTemporarilyUnoptimizable, lhs, idVal, rhs);
|
&isTemporarilyUnoptimizable, lhs, idVal, rhs);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Updated,
|
||||||
ICStubEngine::Baseline, frame->script(),
|
ICStubEngine::Baseline, frame->script(),
|
||||||
stub, &attached);
|
stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
|
@ -1774,6 +1783,7 @@ DoSetPropFallback(JSContext* cx, BaselineFrame* frame, ICSetProp_Fallback* stub_
|
||||||
&isTemporarilyUnoptimizable, lhs, idVal, rhs);
|
&isTemporarilyUnoptimizable, lhs, idVal, rhs);
|
||||||
if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
|
if (gen.tryAttachAddSlotStub(oldGroup, oldShape)) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Updated,
|
||||||
ICStubEngine::Baseline, frame->script(),
|
ICStubEngine::Baseline, frame->script(),
|
||||||
stub, &attached);
|
stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
|
@ -2510,16 +2520,40 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
CallIRGenerator gen(cx, script, pc, stub->state().mode(), argc,
|
// Transition stub state to megamorphic or generic if warranted.
|
||||||
callee, callArgs.thisv(),
|
if (stub->state().maybeTransition())
|
||||||
HandleValueArray::fromMarkedLocation(argc, vp+2));
|
stub->discardStubs(cx);
|
||||||
bool optimizeAfterCall = false;
|
|
||||||
CallIRGenerator::OptStrategy optStrategy = gen.getOptStrategy(&optimizeAfterCall);
|
|
||||||
|
|
||||||
// Try attaching a call stub, if the CallIRGenerator has determined that this
|
bool canAttachStub = stub->state().canAttachStub();
|
||||||
// operation cannot be optimized after the call.
|
|
||||||
bool handled = false;
|
bool handled = false;
|
||||||
if (!optimizeAfterCall) {
|
|
||||||
|
// Only bother to try optimizing JSOP_CALL with CacheIR if the chain is still
|
||||||
|
// allowed to attach stubs.
|
||||||
|
if (canAttachStub) {
|
||||||
|
CallIRGenerator gen(cx, script, pc, stub, stub->state().mode(), argc,
|
||||||
|
callee, callArgs.thisv(),
|
||||||
|
HandleValueArray::fromMarkedLocation(argc, vp+2));
|
||||||
|
if (gen.tryAttachStub()) {
|
||||||
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
gen.cacheIRStubKind(),
|
||||||
|
ICStubEngine::Baseline,
|
||||||
|
script, stub, &handled);
|
||||||
|
|
||||||
|
if (newStub) {
|
||||||
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
|
||||||
|
// If it's an updated stub, initialize it.
|
||||||
|
if (gen.cacheIRStubKind() == BaselineCacheIRStubKind::Updated)
|
||||||
|
SetUpdateStubData(newStub->toCacheIR_Updated(), gen.typeCheckInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!handled)
|
||||||
|
stub->state().trackNotAttached();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try attaching a regular call stub, but only if the CacheIR attempt didn't add
|
||||||
|
// any stubs.
|
||||||
|
if (!handled) {
|
||||||
bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc);
|
bool createSingleton = ObjectGroup::useSingletonForNewObject(cx, script, pc);
|
||||||
if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false,
|
if (!TryAttachCallStub(cx, stub, script, pc, op, argc, vp, constructing, false,
|
||||||
createSingleton, &handled))
|
createSingleton, &handled))
|
||||||
|
@ -2568,17 +2602,6 @@ DoCallFallback(JSContext* cx, BaselineFrame* frame, ICCall_Fallback* stub_, uint
|
||||||
if (!stub->addMonitorStubForValue(cx, frame, types, res))
|
if (!stub->addMonitorStubForValue(cx, frame, types, res))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (optimizeAfterCall && !handled && optStrategy != CallIRGenerator::OptStrategy::None) {
|
|
||||||
if (gen.tryAttachStub()) {
|
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
|
||||||
ICStubEngine::Baseline, script, stub,
|
|
||||||
&handled);
|
|
||||||
if (newStub) {
|
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!handled) {
|
if (!handled) {
|
||||||
// If 'callee' is a potential Call_ConstStringSplit, try to attach an
|
// If 'callee' is a potential Call_ConstStringSplit, try to attach an
|
||||||
// optimized ConstStringSplit stub. Note that vp[0] now holds the return value
|
// optimized ConstStringSplit stub. Note that vp[0] now holds the return value
|
||||||
|
@ -4172,6 +4195,7 @@ DoGetIteratorFallback(JSContext* cx, BaselineFrame* frame, ICGetIterator_Fallbac
|
||||||
bool attached = false;
|
bool attached = false;
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Regular,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub)
|
if (newStub)
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
@ -4562,6 +4586,7 @@ DoTypeOfFallback(JSContext* cx, BaselineFrame* frame, ICTypeOf_Fallback* stub, H
|
||||||
bool attached = false;
|
bool attached = false;
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Regular,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub)
|
if (newStub)
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
|
|
@ -747,11 +747,11 @@ class ICCall_Fallback : public ICMonitoredFallbackStub
|
||||||
static const uint32_t MAX_OPTIMIZED_STUBS = 16;
|
static const uint32_t MAX_OPTIMIZED_STUBS = 16;
|
||||||
static const uint32_t MAX_SCRIPTED_STUBS = 7;
|
static const uint32_t MAX_SCRIPTED_STUBS = 7;
|
||||||
static const uint32_t MAX_NATIVE_STUBS = 7;
|
static const uint32_t MAX_NATIVE_STUBS = 7;
|
||||||
private:
|
|
||||||
|
|
||||||
|
private:
|
||||||
explicit ICCall_Fallback(JitCode* stubCode)
|
explicit ICCall_Fallback(JitCode* stubCode)
|
||||||
: ICMonitoredFallbackStub(ICStub::Call_Fallback, stubCode)
|
: ICMonitoredFallbackStub(ICStub::Call_Fallback, stubCode)
|
||||||
{ }
|
{}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void noteUnoptimizableCall() {
|
void noteUnoptimizableCall() {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
#include "mozilla/FloatingPoint.h"
|
#include "mozilla/FloatingPoint.h"
|
||||||
|
|
||||||
|
#include "jit/BaselineCacheIRCompiler.h"
|
||||||
#include "jit/BaselineIC.h"
|
#include "jit/BaselineIC.h"
|
||||||
#include "jit/CacheIRSpewer.h"
|
#include "jit/CacheIRSpewer.h"
|
||||||
#include "jit/IonCaches.h"
|
#include "jit/IonCaches.h"
|
||||||
|
@ -3747,62 +3748,34 @@ GetIteratorIRGenerator::tryAttachNativeIterator(ObjOperandId objId, HandleObject
|
||||||
}
|
}
|
||||||
|
|
||||||
CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||||
ICState::Mode mode, uint32_t argc,
|
ICCall_Fallback* stub, ICState::Mode mode, uint32_t argc,
|
||||||
HandleValue callee, HandleValue thisval, HandleValueArray args)
|
HandleValue callee, HandleValue thisval, HandleValueArray args)
|
||||||
: IRGenerator(cx, script, pc, CacheKind::Call, mode),
|
: IRGenerator(cx, script, pc, CacheKind::Call, mode),
|
||||||
argc_(argc),
|
argc_(argc),
|
||||||
callee_(callee),
|
callee_(callee),
|
||||||
thisval_(thisval),
|
thisval_(thisval),
|
||||||
args_(args),
|
args_(args),
|
||||||
cachedStrategy_()
|
typeCheckInfo_(cx, /* needsTypeBarrier = */ true),
|
||||||
|
cacheIRStubKind_(BaselineCacheIRStubKind::Regular)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
CallIRGenerator::OptStrategy
|
bool
|
||||||
CallIRGenerator::canOptimize()
|
CallIRGenerator::tryAttachStringSplit()
|
||||||
{
|
|
||||||
// Ensure callee is a function.
|
|
||||||
if (!callee_.isObject() || !callee_.toObject().is<JSFunction>())
|
|
||||||
return OptStrategy::None;
|
|
||||||
|
|
||||||
RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
|
|
||||||
|
|
||||||
OptStrategy strategy;
|
|
||||||
if ((strategy = canOptimizeStringSplit(calleeFunc)) != OptStrategy::None) {
|
|
||||||
return strategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OptStrategy::None;
|
|
||||||
}
|
|
||||||
|
|
||||||
CallIRGenerator::OptStrategy
|
|
||||||
CallIRGenerator::canOptimizeStringSplit(HandleFunction calleeFunc)
|
|
||||||
{
|
{
|
||||||
|
// Only optimize StringSplitString(str, str)
|
||||||
if (argc_ != 2 || !args_[0].isString() || !args_[1].isString())
|
if (argc_ != 2 || !args_[0].isString() || !args_[1].isString())
|
||||||
return OptStrategy::None;
|
return false;
|
||||||
|
|
||||||
// Just for now: if they're both atoms, then do not optimize using
|
// Just for now: if they're both atoms, then do not optimize using
|
||||||
// CacheIR and allow the legacy "ConstStringSplit" BaselineIC optimization
|
// CacheIR and allow the legacy "ConstStringSplit" BaselineIC optimization
|
||||||
// to proceed.
|
// to proceed.
|
||||||
if (args_[0].toString()->isAtom() && args_[1].toString()->isAtom())
|
if (args_[0].toString()->isAtom() && args_[1].toString()->isAtom())
|
||||||
return OptStrategy::None;
|
return false;
|
||||||
|
|
||||||
if (!calleeFunc->isNative())
|
|
||||||
return OptStrategy::None;
|
|
||||||
|
|
||||||
if (calleeFunc->native() != js::intrinsic_StringSplitString)
|
|
||||||
return OptStrategy::None;
|
|
||||||
|
|
||||||
return OptStrategy::StringSplit;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
CallIRGenerator::tryAttachStringSplit()
|
|
||||||
{
|
|
||||||
// Get the object group to use for this location.
|
// Get the object group to use for this location.
|
||||||
RootedObjectGroup group(cx_, ObjectGroupCompartment::getStringSplitStringGroup(cx_));
|
RootedObjectGroup group(cx_, ObjectGroupCompartment::getStringSplitStringGroup(cx_));
|
||||||
if (!group) {
|
if (!group)
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
AutoAssertNoPendingException aanpe(cx_);
|
AutoAssertNoPendingException aanpe(cx_);
|
||||||
Int32OperandId argcId(writer.setInputOperandId(0));
|
Int32OperandId argcId(writer.setInputOperandId(0));
|
||||||
|
@ -3810,15 +3783,14 @@ CallIRGenerator::tryAttachStringSplit()
|
||||||
// Ensure argc == 1.
|
// Ensure argc == 1.
|
||||||
writer.guardSpecificInt32Immediate(argcId, 2);
|
writer.guardSpecificInt32Immediate(argcId, 2);
|
||||||
|
|
||||||
// 1 argument only. Stack-layout here is (bottom to top):
|
// 2 arguments. Stack-layout here is (bottom to top):
|
||||||
//
|
//
|
||||||
// 3: Callee
|
// 3: Callee
|
||||||
// 2: ThisValue
|
// 2: ThisValue
|
||||||
// 1: Arg0
|
// 1: Arg0
|
||||||
// 0: Arg1 <-- Top of stack
|
// 0: Arg1 <-- Top of stack
|
||||||
|
|
||||||
// Ensure callee is an object and is the function that matches the callee optimized
|
// Ensure callee is the |String_split| native function.
|
||||||
// against during stub generation (i.e. the String_split function object).
|
|
||||||
ValOperandId calleeValId = writer.loadStackValue(3);
|
ValOperandId calleeValId = writer.loadStackValue(3);
|
||||||
ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
|
ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
|
||||||
writer.guardIsNativeFunction(calleeObjId, js::intrinsic_StringSplitString);
|
writer.guardIsNativeFunction(calleeObjId, js::intrinsic_StringSplitString);
|
||||||
|
@ -3835,42 +3807,164 @@ CallIRGenerator::tryAttachStringSplit()
|
||||||
writer.callStringSplitResult(arg0StrId, arg1StrId, group);
|
writer.callStringSplitResult(arg0StrId, arg1StrId, group);
|
||||||
writer.typeMonitorResult();
|
writer.typeMonitorResult();
|
||||||
|
|
||||||
|
cacheIRStubKind_ = BaselineCacheIRStubKind::Monitored;
|
||||||
|
trackAttached("StringSplitString");
|
||||||
|
|
||||||
|
TypeScript::Monitor(cx_, script_, pc_, TypeSet::ObjectType(group));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CallIRGenerator::OptStrategy
|
bool
|
||||||
CallIRGenerator::getOptStrategy(bool* optimizeAfterCall)
|
CallIRGenerator::tryAttachArrayPush()
|
||||||
{
|
{
|
||||||
if (!cachedStrategy_) {
|
// Only optimize on obj.push(val);
|
||||||
cachedStrategy_ = mozilla::Some(canOptimize());
|
if (argc_ != 1 || !thisval_.isObject())
|
||||||
}
|
return false;
|
||||||
if (optimizeAfterCall != nullptr) {
|
|
||||||
MOZ_ASSERT(cachedStrategy_.isSome());
|
|
||||||
switch (cachedStrategy_.value()) {
|
|
||||||
case OptStrategy::StringSplit:
|
|
||||||
*optimizeAfterCall = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
// Where |obj| is a native array.
|
||||||
*optimizeAfterCall = false;
|
RootedObject thisobj(cx_, &thisval_.toObject());
|
||||||
}
|
if (!thisobj->is<ArrayObject>())
|
||||||
}
|
return false;
|
||||||
return cachedStrategy_.value();
|
|
||||||
|
RootedArrayObject thisarray(cx_, &thisobj->as<ArrayObject>());
|
||||||
|
|
||||||
|
// And the object group for the array is not collecting preliminary objects.
|
||||||
|
if (thisobj->group()->maybePreliminaryObjects())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check for other indexed properties or class hooks.
|
||||||
|
if (!CanAttachAddElement(thisobj, /* isInit = */ false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Can't add new elements to arrays with non-writable length.
|
||||||
|
if (!thisarray->lengthIsWritable())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check that array is extensible.
|
||||||
|
if (!thisarray->nonProxyIsExtensible())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MOZ_ASSERT(!thisarray->getElementsHeader()->isFrozen(),
|
||||||
|
"Extensible arrays should not have frozen elements");
|
||||||
|
MOZ_ASSERT(thisarray->lengthIsWritable());
|
||||||
|
|
||||||
|
// After this point, we can generate code fine.
|
||||||
|
|
||||||
|
// Generate code.
|
||||||
|
AutoAssertNoPendingException aanpe(cx_);
|
||||||
|
Int32OperandId argcId(writer.setInputOperandId(0));
|
||||||
|
|
||||||
|
// Ensure argc == 1.
|
||||||
|
writer.guardSpecificInt32Immediate(argcId, 1);
|
||||||
|
|
||||||
|
// 1 argument only. Stack-layout here is (bottom to top):
|
||||||
|
//
|
||||||
|
// 2: Callee
|
||||||
|
// 1: ThisValue
|
||||||
|
// 0: Arg0 <-- Top of stack.
|
||||||
|
|
||||||
|
// Guard callee is the |js::array_push| native function.
|
||||||
|
ValOperandId calleeValId = writer.loadStackValue(2);
|
||||||
|
ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
|
||||||
|
writer.guardIsNativeFunction(calleeObjId, js::array_push);
|
||||||
|
|
||||||
|
// Guard this is an array object.
|
||||||
|
ValOperandId thisValId = writer.loadStackValue(1);
|
||||||
|
ObjOperandId thisObjId = writer.guardIsObject(thisValId);
|
||||||
|
writer.guardClass(thisObjId, GuardClassKind::Array);
|
||||||
|
|
||||||
|
// This is a soft assert, documenting the fact that we pass 'true'
|
||||||
|
// for needsTypeBarrier when constructing typeCheckInfo_ for CallIRGenerator.
|
||||||
|
// Can be removed safely if the assumption becomes false.
|
||||||
|
MOZ_ASSERT(typeCheckInfo_.needsTypeBarrier());
|
||||||
|
|
||||||
|
// Guard that the group and shape matches.
|
||||||
|
if (typeCheckInfo_.needsTypeBarrier())
|
||||||
|
writer.guardGroup(thisObjId, thisobj->group());
|
||||||
|
writer.guardShape(thisObjId, thisarray->shape());
|
||||||
|
|
||||||
|
// Guard proto chain shapes.
|
||||||
|
ShapeGuardProtoChain(writer, thisobj, thisObjId);
|
||||||
|
|
||||||
|
// arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
|
||||||
|
ValOperandId argId = writer.loadStackValue(0);
|
||||||
|
writer.arrayPush(thisObjId, argId);
|
||||||
|
|
||||||
|
writer.returnFromIC();
|
||||||
|
|
||||||
|
// Set the type-check info, and the stub kind to Updated
|
||||||
|
typeCheckInfo_.set(thisobj->group(), JSID_VOID);
|
||||||
|
|
||||||
|
cacheIRStubKind_ = BaselineCacheIRStubKind::Updated;
|
||||||
|
|
||||||
|
trackAttached("ArrayPush");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CallIRGenerator::tryAttachStub()
|
CallIRGenerator::tryAttachStub()
|
||||||
{
|
{
|
||||||
OptStrategy strategy = getOptStrategy();
|
// Only optimize when the mode is Specialized.
|
||||||
|
if (mode_ != ICState::Mode::Specialized)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (strategy == OptStrategy::StringSplit) {
|
// Ensure callee is a function.
|
||||||
return tryAttachStringSplit();
|
if (!callee_.isObject() || !callee_.toObject().is<JSFunction>())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
|
||||||
|
|
||||||
|
// Check for native-function optimizations.
|
||||||
|
if (calleeFunc->isNative()) {
|
||||||
|
|
||||||
|
if (calleeFunc->native() == js::intrinsic_StringSplitString) {
|
||||||
|
if (tryAttachStringSplit())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calleeFunc->native() == js::array_push) {
|
||||||
|
if (tryAttachArrayPush())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_ASSERT(strategy == OptStrategy::None);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallIRGenerator::trackAttached(const char* name)
|
||||||
|
{
|
||||||
|
#ifdef JS_CACHEIR_SPEW
|
||||||
|
CacheIRSpewer& sp = CacheIRSpewer::singleton();
|
||||||
|
if (sp.enabled()) {
|
||||||
|
LockGuard<Mutex> guard(sp.lock());
|
||||||
|
sp.beginCache(guard, *this);
|
||||||
|
sp.valueProperty(guard, "callee", callee_);
|
||||||
|
sp.valueProperty(guard, "thisval", thisval_);
|
||||||
|
sp.valueProperty(guard, "argc", Int32Value(argc_));
|
||||||
|
sp.attached(guard, name);
|
||||||
|
sp.endCache(guard);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CallIRGenerator::trackNotAttached()
|
||||||
|
{
|
||||||
|
#ifdef JS_CACHEIR_SPEW
|
||||||
|
CacheIRSpewer& sp = CacheIRSpewer::singleton();
|
||||||
|
if (sp.enabled()) {
|
||||||
|
LockGuard<Mutex> guard(sp.lock());
|
||||||
|
sp.beginCache(guard, *this);
|
||||||
|
sp.valueProperty(guard, "callee", callee_);
|
||||||
|
sp.valueProperty(guard, "thisval", thisval_);
|
||||||
|
sp.valueProperty(guard, "argc", Int32Value(argc_));
|
||||||
|
sp.endCache(guard);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||||
ICState::Mode mode, JSOp op,
|
ICState::Mode mode, JSOp op,
|
||||||
HandleValue lhsVal, HandleValue rhsVal)
|
HandleValue lhsVal, HandleValue rhsVal)
|
||||||
|
|
|
@ -19,6 +19,9 @@
|
||||||
namespace js {
|
namespace js {
|
||||||
namespace jit {
|
namespace jit {
|
||||||
|
|
||||||
|
|
||||||
|
enum class BaselineCacheIRStubKind;
|
||||||
|
|
||||||
// CacheIR is an (extremely simple) linear IR language for inline caches.
|
// CacheIR is an (extremely simple) linear IR language for inline caches.
|
||||||
// From this IR, we can generate machine code for Baseline or Ion IC stubs.
|
// From this IR, we can generate machine code for Baseline or Ion IC stubs.
|
||||||
//
|
//
|
||||||
|
@ -218,6 +221,7 @@ extern const char* CacheKindNames[];
|
||||||
_(StoreUnboxedProperty) \
|
_(StoreUnboxedProperty) \
|
||||||
_(StoreDenseElement) \
|
_(StoreDenseElement) \
|
||||||
_(StoreDenseElementHole) \
|
_(StoreDenseElementHole) \
|
||||||
|
_(ArrayPush) \
|
||||||
_(StoreTypedElement) \
|
_(StoreTypedElement) \
|
||||||
_(StoreUnboxedArrayElement) \
|
_(StoreUnboxedArrayElement) \
|
||||||
_(StoreUnboxedArrayElementHole) \
|
_(StoreUnboxedArrayElementHole) \
|
||||||
|
@ -820,6 +824,10 @@ class MOZ_RAII CacheIRWriter : public JS::CustomAutoRooter
|
||||||
writeOperandId(rhs);
|
writeOperandId(rhs);
|
||||||
buffer_.writeByte(handleAdd);
|
buffer_.writeByte(handleAdd);
|
||||||
}
|
}
|
||||||
|
void arrayPush(ObjOperandId obj, ValOperandId rhs) {
|
||||||
|
writeOpWithOperandId(CacheOp::ArrayPush, obj);
|
||||||
|
writeOperandId(rhs);
|
||||||
|
}
|
||||||
void callScriptedSetter(ObjOperandId obj, JSFunction* setter, ValOperandId rhs) {
|
void callScriptedSetter(ObjOperandId obj, JSFunction* setter, ValOperandId rhs) {
|
||||||
writeOpWithOperandId(CacheOp::CallScriptedSetter, obj);
|
writeOpWithOperandId(CacheOp::CallScriptedSetter, obj);
|
||||||
addStubField(uintptr_t(setter), StubField::Type::JSObject);
|
addStubField(uintptr_t(setter), StubField::Type::JSObject);
|
||||||
|
@ -1460,31 +1468,35 @@ class MOZ_RAII GetIteratorIRGenerator : public IRGenerator
|
||||||
|
|
||||||
class MOZ_RAII CallIRGenerator : public IRGenerator
|
class MOZ_RAII CallIRGenerator : public IRGenerator
|
||||||
{
|
{
|
||||||
public:
|
|
||||||
enum class OptStrategy {
|
|
||||||
None = 0,
|
|
||||||
StringSplit
|
|
||||||
};
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t argc_;
|
uint32_t argc_;
|
||||||
HandleValue callee_;
|
HandleValue callee_;
|
||||||
HandleValue thisval_;
|
HandleValue thisval_;
|
||||||
HandleValueArray args_;
|
HandleValueArray args_;
|
||||||
|
PropertyTypeCheckInfo typeCheckInfo_;
|
||||||
|
BaselineCacheIRStubKind cacheIRStubKind_;
|
||||||
|
|
||||||
mozilla::Maybe<OptStrategy> cachedStrategy_;
|
|
||||||
|
|
||||||
OptStrategy canOptimize();
|
|
||||||
OptStrategy canOptimizeStringSplit(HandleFunction calleeFunc);
|
|
||||||
bool tryAttachStringSplit();
|
bool tryAttachStringSplit();
|
||||||
|
bool tryAttachArrayPush();
|
||||||
|
|
||||||
|
void trackAttached(const char* name);
|
||||||
|
void trackNotAttached();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CallIRGenerator(JSContext* cx, HandleScript, jsbytecode* pc, ICState::Mode mode,
|
CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
|
||||||
|
ICCall_Fallback* stub, ICState::Mode mode,
|
||||||
uint32_t argc, HandleValue callee, HandleValue thisval,
|
uint32_t argc, HandleValue callee, HandleValue thisval,
|
||||||
HandleValueArray args);
|
HandleValueArray args);
|
||||||
|
|
||||||
OptStrategy getOptStrategy(bool* optimizeAfterCall = nullptr);
|
|
||||||
bool tryAttachStub();
|
bool tryAttachStub();
|
||||||
|
|
||||||
|
BaselineCacheIRStubKind cacheIRStubKind() const {
|
||||||
|
return cacheIRStubKind_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PropertyTypeCheckInfo* typeCheckInfo() const {
|
||||||
|
return &typeCheckInfo_;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class MOZ_RAII CompareIRGenerator : public IRGenerator
|
class MOZ_RAII CompareIRGenerator : public IRGenerator
|
||||||
|
|
|
@ -8473,10 +8473,11 @@ class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
|
||||||
{
|
{
|
||||||
LInstruction* ins_;
|
LInstruction* ins_;
|
||||||
Label rejoinStore_;
|
Label rejoinStore_;
|
||||||
|
bool strict_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit OutOfLineStoreElementHole(LInstruction* ins)
|
explicit OutOfLineStoreElementHole(LInstruction* ins, bool strict)
|
||||||
: ins_(ins)
|
: ins_(ins), strict_(strict)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT() ||
|
MOZ_ASSERT(ins->isStoreElementHoleV() || ins->isStoreElementHoleT() ||
|
||||||
ins->isFallibleStoreElementV() || ins->isFallibleStoreElementT());
|
ins->isFallibleStoreElementV() || ins->isFallibleStoreElementT());
|
||||||
|
@ -8491,6 +8492,9 @@ class OutOfLineStoreElementHole : public OutOfLineCodeBase<CodeGenerator>
|
||||||
Label* rejoinStore() {
|
Label* rejoinStore() {
|
||||||
return &rejoinStore_;
|
return &rejoinStore_;
|
||||||
}
|
}
|
||||||
|
bool strict() const {
|
||||||
|
return strict_;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -8579,7 +8583,8 @@ CodeGenerator::emitStoreElementHoleT(T* lir)
|
||||||
static_assert(std::is_same<T, LStoreElementHoleT>::value || std::is_same<T, LFallibleStoreElementT>::value,
|
static_assert(std::is_same<T, LStoreElementHoleT>::value || std::is_same<T, LFallibleStoreElementT>::value,
|
||||||
"emitStoreElementHoleT called with unexpected argument type");
|
"emitStoreElementHoleT called with unexpected argument type");
|
||||||
|
|
||||||
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
|
OutOfLineStoreElementHole* ool =
|
||||||
|
new(alloc()) OutOfLineStoreElementHole(lir, current->mir()->strict());
|
||||||
addOutOfLineCode(ool, lir->mir());
|
addOutOfLineCode(ool, lir->mir());
|
||||||
|
|
||||||
Register obj = ToRegister(lir->object());
|
Register obj = ToRegister(lir->object());
|
||||||
|
@ -8638,7 +8643,8 @@ CodeGenerator::emitStoreElementHoleV(T* lir)
|
||||||
static_assert(std::is_same<T, LStoreElementHoleV>::value || std::is_same<T, LFallibleStoreElementV>::value,
|
static_assert(std::is_same<T, LStoreElementHoleV>::value || std::is_same<T, LFallibleStoreElementV>::value,
|
||||||
"emitStoreElementHoleV called with unexpected parameter type");
|
"emitStoreElementHoleV called with unexpected parameter type");
|
||||||
|
|
||||||
OutOfLineStoreElementHole* ool = new(alloc()) OutOfLineStoreElementHole(lir);
|
OutOfLineStoreElementHole* ool =
|
||||||
|
new(alloc()) OutOfLineStoreElementHole(lir, current->mir()->strict());
|
||||||
addOutOfLineCode(ool, lir->mir());
|
addOutOfLineCode(ool, lir->mir());
|
||||||
|
|
||||||
Register obj = ToRegister(lir->object());
|
Register obj = ToRegister(lir->object());
|
||||||
|
@ -8898,7 +8904,7 @@ CodeGenerator::visitOutOfLineStoreElementHole(OutOfLineStoreElementHole* ool)
|
||||||
masm.bind(&callStub);
|
masm.bind(&callStub);
|
||||||
saveLive(ins);
|
saveLive(ins);
|
||||||
|
|
||||||
pushArg(Imm32(current->mir()->strict()));
|
pushArg(Imm32(ool->strict()));
|
||||||
pushArg(value);
|
pushArg(value);
|
||||||
if (index->isConstant())
|
if (index->isConstant())
|
||||||
pushArg(Imm32(ToInt32(index)));
|
pushArg(Imm32(ToInt32(index)));
|
||||||
|
|
|
@ -66,7 +66,9 @@ class ICState
|
||||||
size_t numOptimizedStubs() const { return numOptimizedStubs_; }
|
size_t numOptimizedStubs() const { return numOptimizedStubs_; }
|
||||||
|
|
||||||
MOZ_ALWAYS_INLINE bool canAttachStub() const {
|
MOZ_ALWAYS_INLINE bool canAttachStub() const {
|
||||||
MOZ_ASSERT(numOptimizedStubs_ <= MaxOptimizedStubs);
|
// Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs
|
||||||
|
// because old-style baseline ICs may attach more stubs than
|
||||||
|
// MaxOptimizedStubs allows.
|
||||||
if (mode_ == Mode::Generic || JitOptions.disableCacheIR)
|
if (mode_ == Mode::Generic || JitOptions.disableCacheIR)
|
||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
|
@ -78,7 +80,9 @@ class ICState
|
||||||
// If this returns true, we transitioned to a new mode and the caller
|
// If this returns true, we transitioned to a new mode and the caller
|
||||||
// should discard all stubs.
|
// should discard all stubs.
|
||||||
MOZ_MUST_USE MOZ_ALWAYS_INLINE bool maybeTransition() {
|
MOZ_MUST_USE MOZ_ALWAYS_INLINE bool maybeTransition() {
|
||||||
MOZ_ASSERT(numOptimizedStubs_ <= MaxOptimizedStubs);
|
// Note: we cannot assert that numOptimizedStubs_ <= MaxOptimizedStubs
|
||||||
|
// because old-style baseline ICs may attach more stubs than
|
||||||
|
// MaxOptimizedStubs allows.
|
||||||
if (mode_ == Mode::Generic)
|
if (mode_ == Mode::Generic)
|
||||||
return false;
|
return false;
|
||||||
if (numOptimizedStubs_ < MaxOptimizedStubs && numFailures_ < maxFailures())
|
if (numOptimizedStubs_ < MaxOptimizedStubs && numFailures_ < maxFailures())
|
||||||
|
|
|
@ -1782,6 +1782,13 @@ IonCacheIRCompiler::emitStoreDenseElementHole()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IonCacheIRCompiler::emitArrayPush()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT_UNREACHABLE("emitArrayPush not supported for IonCaches.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
IonCacheIRCompiler::emitStoreTypedElement()
|
IonCacheIRCompiler::emitStoreTypedElement()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1462,6 +1462,7 @@ DoCompareFallback(JSContext* cx, void* payload, ICCompare_Fallback* stub_, Handl
|
||||||
bool attached = false;
|
bool attached = false;
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Regular,
|
||||||
engine, script, stub, &attached);
|
engine, script, stub, &attached);
|
||||||
if (newStub)
|
if (newStub)
|
||||||
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
JitSpew(JitSpew_BaselineIC, " Attached CacheIR stub");
|
||||||
|
@ -2071,6 +2072,7 @@ DoGetPropFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback* stub_
|
||||||
&isTemporarilyUnoptimizable, val, idVal, val, CanAttachGetter::Yes);
|
&isTemporarilyUnoptimizable, val, idVal, val, CanAttachGetter::Yes);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Monitored,
|
||||||
ICStubEngine::Baseline, script,
|
ICStubEngine::Baseline, script,
|
||||||
stub, &attached);
|
stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
|
@ -2141,6 +2143,7 @@ DoGetPropSuperFallback(JSContext* cx, BaselineFrame* frame, ICGetProp_Fallback*
|
||||||
CanAttachGetter::Yes);
|
CanAttachGetter::Yes);
|
||||||
if (gen.tryAttachStub()) {
|
if (gen.tryAttachStub()) {
|
||||||
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
ICStub* newStub = AttachBaselineCacheIRStub(cx, gen.writerRef(), gen.cacheKind(),
|
||||||
|
BaselineCacheIRStubKind::Monitored,
|
||||||
ICStubEngine::Baseline, script,
|
ICStubEngine::Baseline, script,
|
||||||
stub, &attached);
|
stub, &attached);
|
||||||
if (newStub) {
|
if (newStub) {
|
||||||
|
|
|
@ -163,6 +163,10 @@ CodeGeneratorShared::generateEpilogue()
|
||||||
bool
|
bool
|
||||||
CodeGeneratorShared::generateOutOfLineCode()
|
CodeGeneratorShared::generateOutOfLineCode()
|
||||||
{
|
{
|
||||||
|
// OOL paths should not attempt to use |current| as it's the last block
|
||||||
|
// instead of the block corresponding to the OOL path.
|
||||||
|
current = nullptr;
|
||||||
|
|
||||||
for (size_t i = 0; i < outOfLineCode_.length(); i++) {
|
for (size_t i = 0; i < outOfLineCode_.length(); i++) {
|
||||||
// Add native => bytecode mapping entries for OOL sites.
|
// Add native => bytecode mapping entries for OOL sites.
|
||||||
// Not enabled on wasm yet since it doesn't contain bytecode mappings.
|
// Not enabled on wasm yet since it doesn't contain bytecode mappings.
|
||||||
|
|
|
@ -116,7 +116,7 @@ BEGIN_TEST(testXDR_source)
|
||||||
CHECK(script);
|
CHECK(script);
|
||||||
script = FreezeThaw(cx, script);
|
script = FreezeThaw(cx, script);
|
||||||
CHECK(script);
|
CHECK(script);
|
||||||
JSString* out = JS_DecompileScript(cx, script, "testing", 0);
|
JSString* out = JS_DecompileScript(cx, script);
|
||||||
CHECK(out);
|
CHECK(out);
|
||||||
bool equal;
|
bool equal;
|
||||||
CHECK(JS_StringEqualsAscii(cx, out, *s, &equal));
|
CHECK(JS_StringEqualsAscii(cx, out, *s, &equal));
|
||||||
|
|
|
@ -4599,7 +4599,7 @@ JS::CompileFunction(JSContext* cx, AutoObjectVector& envChain,
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSString*)
|
JS_PUBLIC_API(JSString*)
|
||||||
JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigned indent)
|
JS_DecompileScript(JSContext* cx, HandleScript script)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
||||||
|
|
||||||
|
@ -4608,7 +4608,7 @@ JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigne
|
||||||
script->ensureNonLazyCanonicalFunction();
|
script->ensureNonLazyCanonicalFunction();
|
||||||
RootedFunction fun(cx, script->functionNonDelazifying());
|
RootedFunction fun(cx, script->functionNonDelazifying());
|
||||||
if (fun)
|
if (fun)
|
||||||
return JS_DecompileFunction(cx, fun, indent);
|
return JS_DecompileFunction(cx, fun);
|
||||||
bool haveSource = script->scriptSource()->hasSourceData();
|
bool haveSource = script->scriptSource()->hasSourceData();
|
||||||
if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
|
if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -4617,13 +4617,13 @@ JS_DecompileScript(JSContext* cx, HandleScript script, const char* name, unsigne
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_PUBLIC_API(JSString*)
|
JS_PUBLIC_API(JSString*)
|
||||||
JS_DecompileFunction(JSContext* cx, HandleFunction fun, unsigned indent)
|
JS_DecompileFunction(JSContext* cx, HandleFunction fun)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
|
||||||
AssertHeapIsIdle();
|
AssertHeapIsIdle();
|
||||||
CHECK_REQUEST(cx);
|
CHECK_REQUEST(cx);
|
||||||
assertSameCompartment(cx, fun);
|
assertSameCompartment(cx, fun);
|
||||||
return FunctionToString(cx, fun, !(indent & JS_DONT_PRETTY_PRINT));
|
return FunctionToString(cx, fun, /* isToSource = */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
MOZ_NEVER_INLINE static bool
|
MOZ_NEVER_INLINE static bool
|
||||||
|
|
|
@ -999,7 +999,7 @@ JS_IsBuiltinFunctionConstructor(JSFunction* fun);
|
||||||
* It is important that SpiderMonkey be initialized, and the first context
|
* It is important that SpiderMonkey be initialized, and the first context
|
||||||
* be created, in a single-threaded fashion. Otherwise the behavior of the
|
* be created, in a single-threaded fashion. Otherwise the behavior of the
|
||||||
* library is undefined.
|
* library is undefined.
|
||||||
* See: http://developer.mozilla.org/en/docs/Category:JSAPI_Reference
|
* See: https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/JSAPI_reference
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Create a new runtime, with a single cooperative context for this thread.
|
// Create a new runtime, with a single cooperative context for this thread.
|
||||||
|
@ -4359,16 +4359,10 @@ CompileFunction(JSContext* cx, AutoObjectVector& envChain,
|
||||||
} /* namespace JS */
|
} /* namespace JS */
|
||||||
|
|
||||||
extern JS_PUBLIC_API(JSString*)
|
extern JS_PUBLIC_API(JSString*)
|
||||||
JS_DecompileScript(JSContext* cx, JS::Handle<JSScript*> script, const char* name, unsigned indent);
|
JS_DecompileScript(JSContext* cx, JS::Handle<JSScript*> script);
|
||||||
|
|
||||||
/*
|
|
||||||
* API extension: OR this into indent to avoid pretty-printing the decompiled
|
|
||||||
* source resulting from JS_DecompileFunction.
|
|
||||||
*/
|
|
||||||
#define JS_DONT_PRETTY_PRINT ((unsigned)0x8000)
|
|
||||||
|
|
||||||
extern JS_PUBLIC_API(JSString*)
|
extern JS_PUBLIC_API(JSString*)
|
||||||
JS_DecompileFunction(JSContext* cx, JS::Handle<JSFunction*> fun, unsigned indent);
|
JS_DecompileFunction(JSContext* cx, JS::Handle<JSFunction*> fun);
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -308,6 +308,7 @@ struct JSContext : public JS::RootingContext,
|
||||||
void addPendingOutOfMemory();
|
void addPendingOutOfMemory();
|
||||||
|
|
||||||
JSRuntime* runtime() { return runtime_; }
|
JSRuntime* runtime() { return runtime_; }
|
||||||
|
const JSRuntime* runtime() const { return runtime_; }
|
||||||
|
|
||||||
static size_t offsetOfCompartment() {
|
static size_t offsetOfCompartment() {
|
||||||
return offsetof(JSContext, compartment_);
|
return offsetof(JSContext, compartment_);
|
||||||
|
@ -567,6 +568,10 @@ struct JSContext : public JS::RootingContext,
|
||||||
// exclusive threads are running.
|
// exclusive threads are running.
|
||||||
js::ThreadLocalData<unsigned> keepAtoms;
|
js::ThreadLocalData<unsigned> keepAtoms;
|
||||||
|
|
||||||
|
bool canCollectAtoms() const {
|
||||||
|
return !keepAtoms && !runtime()->hasHelperThreadZones();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Pools used for recycling name maps and vectors when parsing and
|
// Pools used for recycling name maps and vectors when parsing and
|
||||||
// emitting bytecode. Purged on GC when there are no active script
|
// emitting bytecode. Purged on GC when there are no active script
|
||||||
|
@ -1256,8 +1261,8 @@ class MOZ_RAII AutoKeepAtoms
|
||||||
|
|
||||||
JSRuntime* rt = cx->runtime();
|
JSRuntime* rt = cx->runtime();
|
||||||
if (!cx->helperThread()) {
|
if (!cx->helperThread()) {
|
||||||
if (rt->gc.fullGCForAtomsRequested() && !cx->keepAtoms)
|
if (rt->gc.fullGCForAtomsRequested() && cx->canCollectAtoms())
|
||||||
rt->gc.triggerFullGCForAtoms();
|
rt->gc.triggerFullGCForAtoms(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -991,39 +991,50 @@ const Class JSFunction::class_ = {
|
||||||
const Class* const js::FunctionClassPtr = &JSFunction::class_;
|
const Class* const js::FunctionClassPtr = &JSFunction::class_;
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
|
js::FunctionToStringCache::lookup(JSScript* script) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < NumEntries; i++) {
|
||||||
|
if (entries_[i].script == script)
|
||||||
|
return entries_[i].string;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
js::FunctionToStringCache::put(JSScript* script, JSString* string)
|
||||||
|
{
|
||||||
|
for (size_t i = NumEntries - 1; i > 0; i--)
|
||||||
|
entries_[i] = entries_[i - 1];
|
||||||
|
|
||||||
|
entries_[0].set(script, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSString*
|
||||||
|
js::FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource)
|
||||||
{
|
{
|
||||||
if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
|
if (fun->isInterpretedLazy() && !JSFunction::getOrCreateScript(cx, fun))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (IsAsmJSModule(fun))
|
if (IsAsmJSModule(fun))
|
||||||
return AsmJSModuleToString(cx, fun, !prettyPrint);
|
return AsmJSModuleToString(cx, fun, isToSource);
|
||||||
if (IsAsmJSFunction(fun))
|
if (IsAsmJSFunction(fun))
|
||||||
return AsmJSFunctionToString(cx, fun);
|
return AsmJSFunctionToString(cx, fun);
|
||||||
|
|
||||||
if (IsWrappedAsyncFunction(fun)) {
|
if (IsWrappedAsyncFunction(fun)) {
|
||||||
RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
|
RootedFunction unwrapped(cx, GetUnwrappedAsyncFunction(fun));
|
||||||
return FunctionToString(cx, unwrapped, prettyPrint);
|
return FunctionToString(cx, unwrapped, isToSource);
|
||||||
}
|
}
|
||||||
if (IsWrappedAsyncGenerator(fun)) {
|
if (IsWrappedAsyncGenerator(fun)) {
|
||||||
RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
|
RootedFunction unwrapped(cx, GetUnwrappedAsyncGenerator(fun));
|
||||||
return FunctionToString(cx, unwrapped, prettyPrint);
|
return FunctionToString(cx, unwrapped, isToSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
StringBuffer out(cx);
|
|
||||||
RootedScript script(cx);
|
RootedScript script(cx);
|
||||||
|
|
||||||
if (fun->hasScript()) {
|
if (fun->hasScript()) {
|
||||||
script = fun->nonLazyScript();
|
script = fun->nonLazyScript();
|
||||||
if (script->isGeneratorExp()) {
|
if (MOZ_UNLIKELY(script->isGeneratorExp()))
|
||||||
if (!out.append("function genexp() {") ||
|
return NewStringCopyZ<CanGC>(cx, "function genexp() {\n [generator expression]\n}");
|
||||||
!out.append("\n [generator expression]\n") ||
|
|
||||||
!out.append("}"))
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return out.finishString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default class constructors are self-hosted, but have their source
|
// Default class constructors are self-hosted, but have their source
|
||||||
|
@ -1033,9 +1044,9 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
|
||||||
bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
|
bool haveSource = fun->isInterpreted() && (fun->isClassConstructor() ||
|
||||||
!fun->isSelfHostedBuiltin());
|
!fun->isSelfHostedBuiltin());
|
||||||
|
|
||||||
// If we're not in pretty mode, put parentheses around lambda functions
|
// If we're in toSource mode, put parentheses around lambda functions so
|
||||||
// so that eval returns lambda, not function statement.
|
// that eval returns lambda, not function statement.
|
||||||
bool addParentheses = haveSource && !prettyPrint && (fun->isLambda() && !fun->isArrow());
|
bool addParentheses = haveSource && isToSource && (fun->isLambda() && !fun->isArrow());
|
||||||
|
|
||||||
if (haveSource && !script->scriptSource()->hasSourceData() &&
|
if (haveSource && !script->scriptSource()->hasSourceData() &&
|
||||||
!JSScript::loadSource(cx, script->scriptSource(), &haveSource))
|
!JSScript::loadSource(cx, script->scriptSource(), &haveSource))
|
||||||
|
@ -1043,6 +1054,24 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast path for the common case, to avoid StringBuffer overhead.
|
||||||
|
if (!addParentheses && haveSource) {
|
||||||
|
FunctionToStringCache& cache = cx->zone()->functionToStringCache();
|
||||||
|
if (JSString* str = cache.lookup(script))
|
||||||
|
return str;
|
||||||
|
|
||||||
|
size_t start = script->toStringStart(), end = script->toStringEnd();
|
||||||
|
JSString* str = (end - start <= ScriptSource::SourceDeflateLimit)
|
||||||
|
? script->scriptSource()->substring(cx, start, end)
|
||||||
|
: script->scriptSource()->substringDontDeflate(cx, start, end);
|
||||||
|
if (!str)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
cache.put(script, str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuffer out(cx);
|
||||||
if (addParentheses) {
|
if (addParentheses) {
|
||||||
if (!out.append('('))
|
if (!out.append('('))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -1110,11 +1139,11 @@ js::FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPrint)
|
||||||
}
|
}
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
fun_toStringHelper(JSContext* cx, HandleObject obj, unsigned indent)
|
fun_toStringHelper(JSContext* cx, HandleObject obj, bool isToSource)
|
||||||
{
|
{
|
||||||
if (!obj->is<JSFunction>()) {
|
if (!obj->is<JSFunction>()) {
|
||||||
if (JSFunToStringOp op = obj->getOpsFunToString())
|
if (JSFunToStringOp op = obj->getOpsFunToString())
|
||||||
return op(cx, obj, indent);
|
return op(cx, obj, isToSource);
|
||||||
|
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||||
JSMSG_INCOMPATIBLE_PROTO,
|
JSMSG_INCOMPATIBLE_PROTO,
|
||||||
|
@ -1123,7 +1152,7 @@ fun_toStringHelper(JSContext* cx, HandleObject obj, unsigned indent)
|
||||||
}
|
}
|
||||||
|
|
||||||
RootedFunction fun(cx, &obj->as<JSFunction>());
|
RootedFunction fun(cx, &obj->as<JSFunction>());
|
||||||
return FunctionToString(cx, fun, indent != JS_DONT_PRETTY_PRINT);
|
return FunctionToString(cx, fun, isToSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -1146,16 +1175,11 @@ js::fun_toString(JSContext* cx, unsigned argc, Value* vp)
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
MOZ_ASSERT(IsFunctionObject(args.calleev()));
|
MOZ_ASSERT(IsFunctionObject(args.calleev()));
|
||||||
|
|
||||||
uint32_t indent = 0;
|
|
||||||
|
|
||||||
if (args.length() != 0 && !ToUint32(cx, args[0], &indent))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RootedObject obj(cx, ToObject(cx, args.thisv()));
|
RootedObject obj(cx, ToObject(cx, args.thisv()));
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
RootedString str(cx, fun_toStringHelper(cx, obj, indent));
|
JSString* str = fun_toStringHelper(cx, obj, /* isToSource = */ false);
|
||||||
if (!str)
|
if (!str)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1176,12 +1200,12 @@ fun_toSource(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
|
||||||
RootedString str(cx);
|
RootedString str(cx);
|
||||||
if (obj->isCallable())
|
if (obj->isCallable())
|
||||||
str = fun_toStringHelper(cx, obj, JS_DONT_PRETTY_PRINT);
|
str = fun_toStringHelper(cx, obj, /* isToSource = */ true);
|
||||||
else
|
else
|
||||||
str = ObjectToSource(cx, obj);
|
str = ObjectToSource(cx, obj);
|
||||||
|
|
||||||
if (!str)
|
if (!str)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
args.rval().setString(str);
|
args.rval().setString(str);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -673,7 +673,7 @@ static_assert(sizeof(JSFunction) == sizeof(js::shadow::Function),
|
||||||
"shadow interface must match actual interface");
|
"shadow interface must match actual interface");
|
||||||
|
|
||||||
extern JSString*
|
extern JSString*
|
||||||
fun_toStringHelper(JSContext* cx, js::HandleObject obj, unsigned indent);
|
fun_toStringHelper(JSContext* cx, js::HandleObject obj, bool isToSource);
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
|
@ -883,7 +883,7 @@ JSFunction::getExtendedSlot(size_t which) const
|
||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool prettyPring);
|
JSString* FunctionToString(JSContext* cx, HandleFunction fun, bool isToSource);
|
||||||
|
|
||||||
template<XDRMode mode>
|
template<XDRMode mode>
|
||||||
bool
|
bool
|
||||||
|
|
105
js/src/jsgc.cpp
105
js/src/jsgc.cpp
|
@ -2589,6 +2589,7 @@ GCRuntime::updateZonePointersToRelocatedCells(Zone* zone, AutoLockForExclusiveAc
|
||||||
comp->fixupAfterMovingGC();
|
comp->fixupAfterMovingGC();
|
||||||
|
|
||||||
zone->externalStringCache().purge();
|
zone->externalStringCache().purge();
|
||||||
|
zone->functionToStringCache().purge();
|
||||||
|
|
||||||
// Iterate through all cells that can contain relocatable pointers to update
|
// Iterate through all cells that can contain relocatable pointers to update
|
||||||
// them. Since updating each cell is independent we try to parallelize this
|
// them. Since updating each cell is independent we try to parallelize this
|
||||||
|
@ -3130,6 +3131,18 @@ GCRuntime::maybeGC(Zone* zone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GCRuntime::triggerFullGCForAtoms(JSContext* cx)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(fullGCForAtomsRequested_);
|
||||||
|
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||||
|
MOZ_ASSERT(!JS::CurrentThreadIsHeapCollecting());
|
||||||
|
MOZ_ASSERT(!cx->keepAtoms);
|
||||||
|
MOZ_ASSERT(!rt->hasHelperThreadZones());
|
||||||
|
fullGCForAtomsRequested_ = false;
|
||||||
|
MOZ_RELEASE_ASSERT(triggerGC(JS::gcreason::DELAYED_ATOMS_GC));
|
||||||
|
}
|
||||||
|
|
||||||
// Do all possible decommit immediately from the current thread without
|
// Do all possible decommit immediately from the current thread without
|
||||||
// releasing the GC lock or allocating any memory.
|
// releasing the GC lock or allocating any memory.
|
||||||
void
|
void
|
||||||
|
@ -3696,6 +3709,7 @@ GCRuntime::purgeRuntime(AutoLockForExclusiveAccess& lock)
|
||||||
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
|
for (GCZonesIter zone(rt); !zone.done(); zone.next()) {
|
||||||
zone->atomCache().clearAndShrink();
|
zone->atomCache().clearAndShrink();
|
||||||
zone->externalStringCache().purge();
|
zone->externalStringCache().purge();
|
||||||
|
zone->functionToStringCache().purge();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const CooperatingContext& target : rt->cooperatingContexts()) {
|
for (const CooperatingContext& target : rt->cooperatingContexts()) {
|
||||||
|
@ -3855,18 +3869,40 @@ RelazifyFunctions(Zone* zone, AllocKind kind)
|
||||||
static bool
|
static bool
|
||||||
ShouldCollectZone(Zone* zone, JS::gcreason::Reason reason)
|
ShouldCollectZone(Zone* zone, JS::gcreason::Reason reason)
|
||||||
{
|
{
|
||||||
// Normally we collect all scheduled zones.
|
// If we are repeating a GC because we noticed dead compartments haven't
|
||||||
if (reason != JS::gcreason::COMPARTMENT_REVIVED)
|
|
||||||
return zone->isGCScheduled();
|
|
||||||
|
|
||||||
// If we are repeating a GC becuase we noticed dead compartments haven't
|
|
||||||
// been collected, then only collect zones contianing those compartments.
|
// been collected, then only collect zones contianing those compartments.
|
||||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
|
if (reason == JS::gcreason::COMPARTMENT_REVIVED) {
|
||||||
if (comp->scheduledForDestruction)
|
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
|
||||||
return true;
|
if (comp->scheduledForDestruction)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
// Otherwise we only collect scheduled zones.
|
||||||
|
if (!zone->isGCScheduled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If canCollectAtoms() is false then either an instance of AutoKeepAtoms is
|
||||||
|
// currently on the stack or parsing is currently happening on another
|
||||||
|
// thread. In either case we don't have information about which atoms are
|
||||||
|
// roots, so we must skip collecting atoms.
|
||||||
|
//
|
||||||
|
// Note that only affects the first slice of an incremental GC since root
|
||||||
|
// marking is completed before we return to the mutator.
|
||||||
|
//
|
||||||
|
// Off-thread parsing is inhibited after the start of GC which prevents
|
||||||
|
// races between creating atoms during parsing and sweeping atoms on the
|
||||||
|
// active thread.
|
||||||
|
//
|
||||||
|
// Otherwise, we always schedule a GC in the atoms zone so that atoms which
|
||||||
|
// the other collected zones are using are marked, and we can update the
|
||||||
|
// set of atoms in use by the other collected zones at the end of the GC.
|
||||||
|
if (zone->isAtomsZone())
|
||||||
|
return TlsContext.get()->canCollectAtoms();
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
@ -3891,10 +3927,9 @@ GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOu
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
/* Set up which zones will be collected. */
|
/* Set up which zones will be collected. */
|
||||||
if (ShouldCollectZone(zone, reason)) {
|
if (ShouldCollectZone(zone, reason)) {
|
||||||
if (!zone->isAtomsZone()) {
|
MOZ_ASSERT(zone->canCollect());
|
||||||
any = true;
|
any = true;
|
||||||
zone->changeGCState(Zone::NoGC, Zone::Mark);
|
zone->changeGCState(Zone::NoGC, Zone::Mark);
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
*isFullOut = false;
|
*isFullOut = false;
|
||||||
}
|
}
|
||||||
|
@ -3921,30 +3956,10 @@ GCRuntime::prepareZonesForCollection(JS::gcreason::Reason reason, bool* isFullOu
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If keepAtoms() is true then either an instance of AutoKeepAtoms is
|
* Check that we do collect the atoms zone if we triggered a GC for that
|
||||||
* currently on the stack or parsing is currently happening on another
|
* purpose.
|
||||||
* thread. In either case we don't have information about which atoms are
|
|
||||||
* roots, so we must skip collecting atoms.
|
|
||||||
*
|
|
||||||
* Note that only affects the first slice of an incremental GC since root
|
|
||||||
* marking is completed before we return to the mutator.
|
|
||||||
*
|
|
||||||
* Off-thread parsing is inhibited after the start of GC which prevents
|
|
||||||
* races between creating atoms during parsing and sweeping atoms on the
|
|
||||||
* active thread.
|
|
||||||
*
|
|
||||||
* Otherwise, we always schedule a GC in the atoms zone so that atoms which
|
|
||||||
* the other collected zones are using are marked, and we can update the
|
|
||||||
* set of atoms in use by the other collected zones at the end of the GC.
|
|
||||||
*/
|
*/
|
||||||
if (!TlsContext.get()->keepAtoms || rt->hasHelperThreadZones()) {
|
MOZ_ASSERT_IF(reason == JS::gcreason::DELAYED_ATOMS_GC, atomsZone->isGCMarking());
|
||||||
Zone* atomsZone = rt->atomsCompartment(lock)->zone();
|
|
||||||
if (atomsZone->isGCScheduled()) {
|
|
||||||
MOZ_ASSERT(!atomsZone->isCollecting());
|
|
||||||
atomsZone->changeGCState(Zone::NoGC, Zone::Mark);
|
|
||||||
any = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check that at least one zone is scheduled for collection. */
|
/* Check that at least one zone is scheduled for collection. */
|
||||||
return any;
|
return any;
|
||||||
|
@ -6678,17 +6693,19 @@ GCRuntime::budgetIncrementalGC(bool nonincrementalByAPI, JS::gcreason::Reason re
|
||||||
bool reset = false;
|
bool reset = false;
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
|
||||||
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
|
if (zone->usage.gcBytes() >= zone->threshold.gcTriggerBytes()) {
|
||||||
|
MOZ_ASSERT(zone->isGCScheduled());
|
||||||
budget.makeUnlimited();
|
budget.makeUnlimited();
|
||||||
stats().nonincremental(AbortReason::GCBytesTrigger);
|
stats().nonincremental(AbortReason::GCBytesTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isIncrementalGCInProgress() && zone->isGCScheduled() != zone->wasGCStarted())
|
|
||||||
reset = true;
|
|
||||||
|
|
||||||
if (zone->isTooMuchMalloc()) {
|
if (zone->isTooMuchMalloc()) {
|
||||||
|
MOZ_ASSERT(zone->isGCScheduled());
|
||||||
budget.makeUnlimited();
|
budget.makeUnlimited();
|
||||||
stats().nonincremental(AbortReason::MallocBytesTrigger);
|
stats().nonincremental(AbortReason::MallocBytesTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isIncrementalGCInProgress() && zone->isGCScheduled() != zone->wasGCStarted())
|
||||||
|
reset = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reset)
|
if (reset)
|
||||||
|
@ -6709,16 +6726,22 @@ class AutoScheduleZonesForGC
|
||||||
if (rt->gc.gcMode() == JSGC_MODE_GLOBAL)
|
if (rt->gc.gcMode() == JSGC_MODE_GLOBAL)
|
||||||
zone->scheduleGC();
|
zone->scheduleGC();
|
||||||
|
|
||||||
/* This is a heuristic to avoid resets. */
|
// This is a heuristic to avoid resets.
|
||||||
if (rt->gc.isIncrementalGCInProgress() && zone->needsIncrementalBarrier())
|
if (rt->gc.isIncrementalGCInProgress() && zone->needsIncrementalBarrier())
|
||||||
zone->scheduleGC();
|
zone->scheduleGC();
|
||||||
|
|
||||||
/* This is a heuristic to reduce the total number of collections. */
|
// This is a heuristic to reduce the total number of collections.
|
||||||
if (zone->usage.gcBytes() >=
|
if (zone->usage.gcBytes() >=
|
||||||
zone->threshold.allocTrigger(rt->gc.schedulingState.inHighFrequencyGCMode()))
|
zone->threshold.allocTrigger(rt->gc.schedulingState.inHighFrequencyGCMode()))
|
||||||
{
|
{
|
||||||
zone->scheduleGC();
|
zone->scheduleGC();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This ensures we collect zones that have reached the malloc limit.
|
||||||
|
// TODO: Start collecting these zones earlier like we do for the GC
|
||||||
|
// bytes trigger above (bug 1384049).
|
||||||
|
if (zone->isTooMuchMalloc())
|
||||||
|
zone->scheduleGC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1209,7 +1209,7 @@ ToDisassemblySource(JSContext* cx, HandleValue v, JSAutoByteString* bytes)
|
||||||
|
|
||||||
if (obj.is<JSFunction>()) {
|
if (obj.is<JSFunction>()) {
|
||||||
RootedFunction fun(cx, &obj.as<JSFunction>());
|
RootedFunction fun(cx, &obj.as<JSFunction>());
|
||||||
JSString* str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
|
JSString* str = JS_DecompileFunction(cx, fun);
|
||||||
if (!str)
|
if (!str)
|
||||||
return false;
|
return false;
|
||||||
return bytes->encodeLatin1(cx, str);
|
return bytes->encodeLatin1(cx, str);
|
||||||
|
@ -2729,7 +2729,7 @@ GetPCCountJSON(JSContext* cx, const ScriptAndCounts& sac, StringBuffer& buf)
|
||||||
if (!AppendJSONProperty(buf, "text", NO_COMMA))
|
if (!AppendJSONProperty(buf, "text", NO_COMMA))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JSString* str = JS_DecompileScript(cx, script, nullptr, 0);
|
JSString* str = JS_DecompileScript(cx, script);
|
||||||
if (!str || !(str = StringToSource(cx, str)))
|
if (!str || !(str = StringToSource(cx, str)))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -1834,9 +1834,7 @@ ScriptSource::appendSubstring(JSContext* cx, StringBuffer& buf, size_t start, si
|
||||||
PinnedChars chars(cx, this, holder, start, len);
|
PinnedChars chars(cx, this, holder, start, len);
|
||||||
if (!chars.get())
|
if (!chars.get())
|
||||||
return false;
|
return false;
|
||||||
// Sources can be large and we don't want to check "is this char Latin1"
|
if (len > SourceDeflateLimit && !buf.ensureTwoByteChars())
|
||||||
// for each source code character, so inflate the buffer here.
|
|
||||||
if (len > 100 && !buf.ensureTwoByteChars())
|
|
||||||
return false;
|
return false;
|
||||||
return buf.append(chars.get(), len);
|
return buf.append(chars.get(), len);
|
||||||
}
|
}
|
||||||
|
|
|
@ -502,6 +502,10 @@ class ScriptSource
|
||||||
void movePendingCompressedSource();
|
void movePendingCompressedSource();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
// When creating a JSString* from TwoByte source characters, we don't try to
|
||||||
|
// to deflate to Latin1 for longer strings, because this can be slow.
|
||||||
|
static const size_t SourceDeflateLimit = 100;
|
||||||
|
|
||||||
explicit ScriptSource()
|
explicit ScriptSource()
|
||||||
: refs(0),
|
: refs(0),
|
||||||
data(SourceType(Missing())),
|
data(SourceType(Missing())),
|
||||||
|
|
|
@ -115,7 +115,7 @@ class JS_FRIEND_API(Wrapper) : public BaseProxyHandler
|
||||||
JS::IsArrayAnswer* answer) const override;
|
JS::IsArrayAnswer* answer) const override;
|
||||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||||
unsigned indent) const override;
|
bool isToSource) const override;
|
||||||
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
||||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
|
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
|
||||||
MutableHandleValue vp) const override;
|
MutableHandleValue vp) const override;
|
||||||
|
@ -209,7 +209,7 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
|
||||||
bool* bp) const override;
|
bool* bp) const override;
|
||||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||||
virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
|
virtual JSString* fun_toString(JSContext* cx, HandleObject wrapper,
|
||||||
unsigned indent) const override;
|
bool isToSource) const override;
|
||||||
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
||||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
|
||||||
|
|
||||||
|
@ -268,7 +268,8 @@ class JS_FRIEND_API(OpaqueCrossCompartmentWrapper) : public CrossCompartmentWrap
|
||||||
virtual bool isArray(JSContext* cx, HandleObject obj,
|
virtual bool isArray(JSContext* cx, HandleObject obj,
|
||||||
JS::IsArrayAnswer* answer) const override;
|
JS::IsArrayAnswer* answer) const override;
|
||||||
virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
|
virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
|
||||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
|
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||||
|
bool isToSource) const override;
|
||||||
|
|
||||||
static const OpaqueCrossCompartmentWrapper singleton;
|
static const OpaqueCrossCompartmentWrapper singleton;
|
||||||
};
|
};
|
||||||
|
|
|
@ -316,7 +316,7 @@ BaseProxyHandler::className(JSContext* cx, HandleObject proxy) const
|
||||||
}
|
}
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
|
BaseProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
|
||||||
{
|
{
|
||||||
if (proxy->isCallable())
|
if (proxy->isCallable())
|
||||||
return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}");
|
return JS_NewStringCopyZ(cx, "function () {\n [native code]\n}");
|
||||||
|
|
|
@ -449,12 +449,12 @@ CrossCompartmentWrapper::className(JSContext* cx, HandleObject wrapper) const
|
||||||
}
|
}
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, unsigned indent) const
|
CrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject wrapper, bool isToSource) const
|
||||||
{
|
{
|
||||||
RootedString str(cx);
|
RootedString str(cx);
|
||||||
{
|
{
|
||||||
AutoCompartment call(cx, wrappedObject(wrapper));
|
AutoCompartment call(cx, wrappedObject(wrapper));
|
||||||
str = Wrapper::fun_toString(cx, wrapper, indent);
|
str = Wrapper::fun_toString(cx, wrapper, isToSource);
|
||||||
if (!str)
|
if (!str)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,7 @@ DeadObjectProxy<CC>::className(JSContext* cx, HandleObject wrapper) const
|
||||||
|
|
||||||
template <DeadProxyIsCallableIsConstructorOption CC>
|
template <DeadProxyIsCallableIsConstructorOption CC>
|
||||||
JSString*
|
JSString*
|
||||||
DeadObjectProxy<CC>::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
|
DeadObjectProxy<CC>::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
|
||||||
{
|
{
|
||||||
ReportDead(cx);
|
ReportDead(cx);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -59,7 +59,8 @@ class DeadObjectProxy : public BaseProxyHandler
|
||||||
virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
|
virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls) const override;
|
||||||
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
|
virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
|
||||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
|
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||||
|
bool isToSource) const override;
|
||||||
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
||||||
|
|
||||||
virtual bool isCallable(JSObject* obj) const override {
|
virtual bool isCallable(JSObject* obj) const override {
|
||||||
|
|
|
@ -183,7 +183,7 @@ OpaqueCrossCompartmentWrapper::className(JSContext* cx,
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
OpaqueCrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject proxy,
|
OpaqueCrossCompartmentWrapper::fun_toString(JSContext* cx, HandleObject proxy,
|
||||||
unsigned indent) const
|
bool isToSource) const
|
||||||
{
|
{
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||||
js_Function_str, js_toString_str, "object");
|
js_Function_str, js_toString_str, "object");
|
||||||
|
|
|
@ -562,7 +562,7 @@ Proxy::className(JSContext* cx, HandleObject proxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
|
Proxy::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource)
|
||||||
{
|
{
|
||||||
if (!CheckRecursionLimit(cx))
|
if (!CheckRecursionLimit(cx))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -571,8 +571,8 @@ Proxy::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent)
|
||||||
BaseProxyHandler::GET, /* mayThrow = */ false);
|
BaseProxyHandler::GET, /* mayThrow = */ false);
|
||||||
// Do the safe thing if the policy rejects.
|
// Do the safe thing if the policy rejects.
|
||||||
if (!policy.allowed())
|
if (!policy.allowed())
|
||||||
return handler->BaseProxyHandler::fun_toString(cx, proxy, indent);
|
return handler->BaseProxyHandler::fun_toString(cx, proxy, isToSource);
|
||||||
return handler->fun_toString(cx, proxy, indent);
|
return handler->fun_toString(cx, proxy, isToSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExpShared*
|
RegExpShared*
|
||||||
|
|
|
@ -59,7 +59,7 @@ class Proxy
|
||||||
static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls);
|
static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClass* cls);
|
||||||
static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
|
static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
|
||||||
static const char* className(JSContext* cx, HandleObject proxy);
|
static const char* className(JSContext* cx, HandleObject proxy);
|
||||||
static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent);
|
static JSString* fun_toString(JSContext* cx, HandleObject proxy, bool isToSource);
|
||||||
static RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy);
|
static RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy);
|
||||||
static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
|
static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
|
||||||
|
|
||||||
|
|
|
@ -1260,7 +1260,7 @@ ScriptedProxyHandler::className(JSContext* cx, HandleObject proxy) const
|
||||||
}
|
}
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
|
ScriptedProxyHandler::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
|
||||||
{
|
{
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
|
||||||
js_Function_str, js_toString_str, "object");
|
js_Function_str, js_toString_str, "object");
|
||||||
|
|
|
@ -68,7 +68,7 @@ class ScriptedProxyHandler : public BaseProxyHandler
|
||||||
JS::IsArrayAnswer* answer) const override;
|
JS::IsArrayAnswer* answer) const override;
|
||||||
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
virtual const char* className(JSContext* cx, HandleObject proxy) const override;
|
||||||
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
|
||||||
unsigned indent) const override;
|
bool isToSource) const override;
|
||||||
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
virtual RegExpShared* regexp_toShared(JSContext* cx, HandleObject proxy) const override;
|
||||||
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
|
virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
|
||||||
MutableHandleValue vp) const override;
|
MutableHandleValue vp) const override;
|
||||||
|
|
|
@ -264,11 +264,11 @@ Wrapper::className(JSContext* cx, HandleObject proxy) const
|
||||||
}
|
}
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
Wrapper::fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const
|
Wrapper::fun_toString(JSContext* cx, HandleObject proxy, bool isToSource) const
|
||||||
{
|
{
|
||||||
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
|
||||||
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
RootedObject target(cx, proxy->as<ProxyObject>().target());
|
||||||
return fun_toStringHelper(cx, target, indent);
|
return fun_toStringHelper(cx, target, isToSource);
|
||||||
}
|
}
|
||||||
|
|
||||||
RegExpShared*
|
RegExpShared*
|
||||||
|
|
|
@ -4784,7 +4784,7 @@ DecompileFunction(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
|
RootedFunction fun(cx, &args[0].toObject().as<JSFunction>());
|
||||||
JSString* result = JS_DecompileFunction(cx, fun, 0);
|
JSString* result = JS_DecompileFunction(cx, fun);
|
||||||
if (!result)
|
if (!result)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setString(result);
|
args.rval().setString(result);
|
||||||
|
@ -4806,7 +4806,7 @@ DecompileThisScript(JSContext* cx, unsigned argc, Value* vp)
|
||||||
JSAutoCompartment ac(cx, iter.script());
|
JSAutoCompartment ac(cx, iter.script());
|
||||||
|
|
||||||
RootedScript script(cx, iter.script());
|
RootedScript script(cx, iter.script());
|
||||||
JSString* result = JS_DecompileScript(cx, script, "test", 0);
|
JSString* result = JS_DecompileScript(cx, script);
|
||||||
if (!result)
|
if (!result)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -887,8 +887,9 @@ JSRuntime::clearUsedByHelperThread(Zone* zone)
|
||||||
MOZ_ASSERT(zone->group()->usedByHelperThread);
|
MOZ_ASSERT(zone->group()->usedByHelperThread);
|
||||||
zone->group()->usedByHelperThread = false;
|
zone->group()->usedByHelperThread = false;
|
||||||
numHelperThreadZones--;
|
numHelperThreadZones--;
|
||||||
if (gc.fullGCForAtomsRequested() && !TlsContext.get())
|
JSContext* cx = TlsContext.get();
|
||||||
gc.triggerFullGCForAtoms();
|
if (gc.fullGCForAtomsRequested() && cx->canCollectAtoms())
|
||||||
|
gc.triggerFullGCForAtoms(cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
|
@ -8896,7 +8896,7 @@ js::IsAsmJSModuleLoadedFromCache(JSContext* cx, unsigned argc, Value* vp)
|
||||||
// asm.js toString/toSource support
|
// asm.js toString/toSource support
|
||||||
|
|
||||||
JSString*
|
JSString*
|
||||||
js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda)
|
js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool isToSource)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsAsmJSModule(fun));
|
MOZ_ASSERT(IsAsmJSModule(fun));
|
||||||
|
|
||||||
|
@ -8907,7 +8907,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
|
||||||
|
|
||||||
StringBuffer out(cx);
|
StringBuffer out(cx);
|
||||||
|
|
||||||
if (addParenToLambda && fun->isLambda() && !out.append("("))
|
if (isToSource && fun->isLambda() && !out.append("("))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
bool haveSource = source->hasSourceData();
|
bool haveSource = source->hasSourceData();
|
||||||
|
@ -8930,7 +8930,7 @@ js::AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addParenToLambda && fun->isLambda() && !out.append(")"))
|
if (isToSource && fun->isLambda() && !out.append(")"))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return out.finishString();
|
return out.finishString();
|
||||||
|
|
|
@ -80,7 +80,7 @@ extern JSString*
|
||||||
AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
|
AsmJSFunctionToString(JSContext* cx, HandleFunction fun);
|
||||||
|
|
||||||
extern JSString*
|
extern JSString*
|
||||||
AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool addParenToLambda);
|
AsmJSModuleToString(JSContext* cx, HandleFunction fun, bool isToSource);
|
||||||
|
|
||||||
// asm.js heap:
|
// asm.js heap:
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
#include "mozilla/layers/LayerManagerComposite.h"
|
#include "mozilla/layers/LayerManagerComposite.h"
|
||||||
#include "mozilla/layers/CompositorBridgeChild.h"
|
#include "mozilla/layers/CompositorBridgeChild.h"
|
||||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||||
|
#include "mozilla/webrender/WebRenderAPI.h"
|
||||||
#include "ClientLayerManager.h"
|
#include "ClientLayerManager.h"
|
||||||
#include "FrameLayerBuilder.h"
|
#include "FrameLayerBuilder.h"
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ typedef FrameMetrics::ViewID ViewID;
|
||||||
* from the nearest display item reference frame (which we assume will be inducing
|
* from the nearest display item reference frame (which we assume will be inducing
|
||||||
* a ContainerLayer).
|
* a ContainerLayer).
|
||||||
*/
|
*/
|
||||||
static nsIntPoint
|
static LayoutDeviceIntPoint
|
||||||
GetContentRectLayerOffset(nsIFrame* aContainerFrame, nsDisplayListBuilder* aBuilder)
|
GetContentRectLayerOffset(nsIFrame* aContainerFrame, nsDisplayListBuilder* aBuilder)
|
||||||
{
|
{
|
||||||
nscoord auPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
|
nscoord auPerDevPixel = aContainerFrame->PresContext()->AppUnitsPerDevPixel();
|
||||||
|
@ -61,7 +62,7 @@ GetContentRectLayerOffset(nsIFrame* aContainerFrame, nsDisplayListBuilder* aBuil
|
||||||
nsPoint frameOffset = aBuilder->ToReferenceFrame(aContainerFrame) +
|
nsPoint frameOffset = aBuilder->ToReferenceFrame(aContainerFrame) +
|
||||||
aContainerFrame->GetContentRectRelativeToSelf().TopLeft();
|
aContainerFrame->GetContentRectRelativeToSelf().TopLeft();
|
||||||
|
|
||||||
return frameOffset.ToNearestPixels(auPerDevPixel);
|
return LayoutDeviceIntPoint::FromAppUnitsToNearest(frameOffset, auPerDevPixel);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true iff |aManager| is a "temporary layer manager". They're
|
// Return true iff |aManager| is a "temporary layer manager". They're
|
||||||
|
@ -160,7 +161,6 @@ already_AddRefed<Layer>
|
||||||
RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder,
|
RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||||
nsIFrame* aFrame,
|
nsIFrame* aFrame,
|
||||||
LayerManager* aManager,
|
LayerManager* aManager,
|
||||||
const nsIntRect& aVisibleRect,
|
|
||||||
nsDisplayItem* aItem,
|
nsDisplayItem* aItem,
|
||||||
const ContainerLayerParameters& aContainerParameters)
|
const ContainerLayerParameters& aContainerParameters)
|
||||||
{
|
{
|
||||||
|
@ -200,7 +200,7 @@ RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
static_cast<RefLayer*>(layer.get())->SetReferentId(mLayersId);
|
static_cast<RefLayer*>(layer.get())->SetReferentId(mLayersId);
|
||||||
nsIntPoint offset = GetContentRectLayerOffset(aFrame, aBuilder);
|
LayoutDeviceIntPoint offset = GetContentRectLayerOffset(aFrame, aBuilder);
|
||||||
// We can only have an offset if we're a child of an inactive
|
// We can only have an offset if we're a child of an inactive
|
||||||
// container, but our display item is LAYER_ACTIVE_FORCE which
|
// container, but our display item is LAYER_ACTIVE_FORCE which
|
||||||
// forces all layers above to be active.
|
// forces all layers above to be active.
|
||||||
|
@ -374,12 +374,28 @@ nsDisplayRemote::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||||
LayerManager* aManager,
|
LayerManager* aManager,
|
||||||
const ContainerLayerParameters& aContainerParameters)
|
const ContainerLayerParameters& aContainerParameters)
|
||||||
{
|
{
|
||||||
int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
|
RefPtr<Layer> layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, this, aContainerParameters);
|
||||||
nsIntRect visibleRect = GetVisibleRect().ToNearestPixels(appUnitsPerDevPixel);
|
|
||||||
visibleRect += aContainerParameters.mOffset;
|
|
||||||
RefPtr<Layer> layer = mRemoteFrame->BuildLayer(aBuilder, mFrame, aManager, visibleRect, this, aContainerParameters);
|
|
||||||
if (layer && layer->AsContainerLayer()) {
|
if (layer && layer->AsContainerLayer()) {
|
||||||
layer->AsContainerLayer()->SetEventRegionsOverride(mEventRegionsOverride);
|
layer->AsContainerLayer()->SetEventRegionsOverride(mEventRegionsOverride);
|
||||||
}
|
}
|
||||||
return layer.forget();
|
return layer.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsDisplayRemote::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
|
||||||
|
const StackingContextHelper& aSc,
|
||||||
|
nsTArray<WebRenderParentCommand>& aParentCommands,
|
||||||
|
mozilla::layers::WebRenderLayerManager* aManager,
|
||||||
|
nsDisplayListBuilder* aDisplayListBuilder)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aManager->IsLayersFreeTransaction());
|
||||||
|
|
||||||
|
mozilla::LayoutDeviceRect visible = mozilla::LayoutDeviceRect::FromAppUnits(
|
||||||
|
GetVisibleRect(), mFrame->PresContext()->AppUnitsPerDevPixel());
|
||||||
|
visible += mozilla::layout::GetContentRectLayerOffset(mFrame, aDisplayListBuilder);
|
||||||
|
|
||||||
|
aBuilder.PushIFrame(aSc.ToRelativeLayoutRect(visible),
|
||||||
|
mozilla::wr::AsPipelineId(mRemoteFrame->GetLayersId()));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -72,7 +72,6 @@ public:
|
||||||
already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
|
already_AddRefed<Layer> BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||||
nsIFrame* aFrame,
|
nsIFrame* aFrame,
|
||||||
LayerManager* aManager,
|
LayerManager* aManager,
|
||||||
const nsIntRect& aVisibleRect,
|
|
||||||
nsDisplayItem* aItem,
|
nsDisplayItem* aItem,
|
||||||
const ContainerLayerParameters& aContainerParameters);
|
const ContainerLayerParameters& aContainerParameters);
|
||||||
|
|
||||||
|
@ -163,6 +162,12 @@ public:
|
||||||
BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
|
BuildLayer(nsDisplayListBuilder* aBuilder, LayerManager* aManager,
|
||||||
const ContainerLayerParameters& aContainerParameters) override;
|
const ContainerLayerParameters& aContainerParameters) override;
|
||||||
|
|
||||||
|
virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
|
||||||
|
const StackingContextHelper& aSc,
|
||||||
|
nsTArray<WebRenderParentCommand>& aParentCommands,
|
||||||
|
mozilla::layers::WebRenderLayerManager* aManager,
|
||||||
|
nsDisplayListBuilder* aDisplayListBuilder) override;
|
||||||
|
|
||||||
NS_DISPLAY_DECL_NAME("Remote", TYPE_REMOTE)
|
NS_DISPLAY_DECL_NAME("Remote", TYPE_REMOTE)
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -2091,10 +2091,7 @@ already_AddRefed<LayerManager> nsDisplayList::PaintRoot(nsDisplayListBuilder* aB
|
||||||
nsIDocument* document = presShell->GetDocument();
|
nsIDocument* document = presShell->GetDocument();
|
||||||
|
|
||||||
if (gfxPrefs::WebRenderLayersFree() &&
|
if (gfxPrefs::WebRenderLayersFree() &&
|
||||||
layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR &&
|
layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
|
||||||
// We don't yet support many display items used in chrome, so
|
|
||||||
// layers-free mode is only for content.
|
|
||||||
!presContext->IsChrome()) {
|
|
||||||
if (doBeginTransaction) {
|
if (doBeginTransaction) {
|
||||||
if (aCtx) {
|
if (aCtx) {
|
||||||
if (!layerManager->BeginTransactionWithTarget(aCtx)) {
|
if (!layerManager->BeginTransactionWithTarget(aCtx)) {
|
||||||
|
|
|
@ -1984,12 +1984,9 @@ fuzzy-if(Android,27,874) fuzzy-if(gtkWidget,14,29) == 1313772.xhtml 1313772-ref.
|
||||||
fuzzy(2,320000) == 1315113-1.html 1315113-1-ref.html
|
fuzzy(2,320000) == 1315113-1.html 1315113-1-ref.html
|
||||||
fuzzy(2,20000) == 1315113-2.html 1315113-2-ref.html
|
fuzzy(2,20000) == 1315113-2.html 1315113-2-ref.html
|
||||||
== 1315632-1.html 1315632-1-ref.html
|
== 1315632-1.html 1315632-1-ref.html
|
||||||
fuzzy(2,40000) fuzzy-if(webrender,26,691) == 1316719-1a.html 1316719-1-ref.html
|
fuzzy(2,40000) fuzzy-if(webrender,1-2,349-349) == 1316719-1a.html 1316719-1-ref.html
|
||||||
fuzzy(2,40000) fuzzy-if(webrender,26,691) == 1316719-1b.html 1316719-1-ref.html
|
fuzzy(2,40000) fuzzy-if(webrender,1-2,349-349) == 1316719-1b.html 1316719-1-ref.html
|
||||||
fuzzy(2,40000) == 1316719-1c.html 1316719-1-ref.html
|
fuzzy(2,40000) fuzzy-if(webrender,1-1,323-323) == 1316719-1c.html 1316719-1-ref.html
|
||||||
pref(layers.advanced.background-color,1) skip-if(!webrender) fuzzy-if(webrender,27,700) == 1316719-1a.html 1316719-1-ref.html
|
|
||||||
pref(layers.advanced.background-color,1) skip-if(!webrender) fuzzy-if(webrender,27,700) == 1316719-1b.html 1316719-1-ref.html
|
|
||||||
pref(layers.advanced.background-color,1) skip-if(!webrender) fuzzy-if(webrender,27,700) == 1316719-1c.html 1316719-1-ref.html
|
|
||||||
skip-if(Android) != 1318769-1.html 1318769-1-ref.html
|
skip-if(Android) != 1318769-1.html 1318769-1-ref.html
|
||||||
fails-if(styloVsGecko) == 1322512-1.html 1322512-1-ref.html
|
fails-if(styloVsGecko) == 1322512-1.html 1322512-1-ref.html
|
||||||
== 1330051.svg 1330051-ref.svg
|
== 1330051.svg 1330051-ref.svg
|
||||||
|
|
|
@ -37,7 +37,8 @@ test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceE
|
||||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-3.html textarea-3-ref.html
|
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-3.html textarea-3-ref.html
|
||||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == css-transform-1.html css-transform-1-ref.html
|
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == css-transform-1.html css-transform-1-ref.html
|
||||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == css-transform-2.html css-transform-2-ref.html
|
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == css-transform-2.html css-transform-2-ref.html
|
||||||
fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1764) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == container-with-clamping.html container-with-clamping-ref.html
|
# skipped - bug 1380830
|
||||||
|
fuzzy-if(asyncPan&&!layersGPUAccelerated,102,1764) skip test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == container-with-clamping.html container-with-clamping-ref.html
|
||||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) skip-if(styloVsGecko) load video-1.html
|
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) skip-if(styloVsGecko) load video-1.html
|
||||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-min-1.html intrinsic-min-1-ref.html
|
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-min-1.html intrinsic-min-1-ref.html
|
||||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-max-1.html intrinsic-max-1-ref.html
|
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-max-1.html intrinsic-max-1-ref.html
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#define mozilla_AllocPolicy_h
|
#define mozilla_AllocPolicy_h
|
||||||
|
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/TemplateLib.h"
|
#include "mozilla/TemplateLib.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
@ -128,6 +129,67 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A policy which always fails to allocate memory, returning nullptr. Methods
|
||||||
|
* which expect an existing allocation assert.
|
||||||
|
*
|
||||||
|
* This type should be used in situations where you want to use a MFBT type with
|
||||||
|
* inline storage, and don't want to allow it to allocate on the heap.
|
||||||
|
*/
|
||||||
|
class NeverAllocPolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
T* maybe_pod_malloc(size_t aNumElems)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* maybe_pod_calloc(size_t aNumElems)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* maybe_pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NeverAllocPolicy::maybe_pod_realloc");
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* pod_malloc(size_t aNumElems)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* pod_calloc(size_t aNumElems)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* pod_realloc(T* aPtr, size_t aOldSize, size_t aNewSize)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NeverAllocPolicy::pod_realloc");
|
||||||
|
}
|
||||||
|
|
||||||
|
void free_(void* aPtr)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NeverAllocPolicy::free_");
|
||||||
|
}
|
||||||
|
|
||||||
|
void reportAllocOverflow() const
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool checkSimulatedOOM() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
#endif /* mozilla_AllocPolicy_h */
|
#endif /* mozilla_AllocPolicy_h */
|
||||||
|
|
|
@ -1116,6 +1116,10 @@ public class BrowserApp extends GeckoApp
|
||||||
// We can't show the first run experience until Gecko has finished initialization (bug 1077583).
|
// We can't show the first run experience until Gecko has finished initialization (bug 1077583).
|
||||||
checkFirstrun(this, intent);
|
checkFirstrun(this, intent);
|
||||||
|
|
||||||
|
if (Versions.preJB) {
|
||||||
|
conditionallyNotifyEOL();
|
||||||
|
}
|
||||||
|
|
||||||
if (!IntentUtils.getIsInAutomationFromEnvironment(intent)) {
|
if (!IntentUtils.getIsInAutomationFromEnvironment(intent)) {
|
||||||
DawnHelper.conditionallyNotifyDawn(this);
|
DawnHelper.conditionallyNotifyDawn(this);
|
||||||
}
|
}
|
||||||
|
@ -3452,6 +3456,10 @@ public class BrowserApp extends GeckoApp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContextMenu(GeckoView view, int screenX, int screenY,
|
||||||
|
String uri, String elementSrc) {}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onPrepareOptionsMenu(Menu aMenu) {
|
public boolean onPrepareOptionsMenu(Menu aMenu) {
|
||||||
if (aMenu == null)
|
if (aMenu == null)
|
||||||
|
|
|
@ -567,4 +567,8 @@ public class CustomTabsActivity extends AppCompatActivity
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFullScreen(GeckoView view, boolean fullScreen) {}
|
public void onFullScreen(GeckoView view, boolean fullScreen) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContextMenu(GeckoView view, int screenX, int screenY,
|
||||||
|
String uri, String elementSrc) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ class GeckoViewContent extends GeckoViewContentModule {
|
||||||
addEventListener("MozDOMFullscreen:Exit", this, false);
|
addEventListener("MozDOMFullscreen:Exit", this, false);
|
||||||
addEventListener("MozDOMFullscreen:Exited", this, false);
|
addEventListener("MozDOMFullscreen:Exited", this, false);
|
||||||
addEventListener("MozDOMFullscreen:Request", this, false);
|
addEventListener("MozDOMFullscreen:Request", this, false);
|
||||||
|
addEventListener("contextmenu", this, { capture: true, passive: false });
|
||||||
|
|
||||||
this.messageManager.addMessageListener("GeckoView:DOMFullscreenEntered",
|
this.messageManager.addMessageListener("GeckoView:DOMFullscreenEntered",
|
||||||
this);
|
this);
|
||||||
|
@ -37,6 +38,7 @@ class GeckoViewContent extends GeckoViewContentModule {
|
||||||
removeEventListener("MozDOMFullscreen:Exit", this);
|
removeEventListener("MozDOMFullscreen:Exit", this);
|
||||||
removeEventListener("MozDOMFullscreen:Exited", this);
|
removeEventListener("MozDOMFullscreen:Exited", this);
|
||||||
removeEventListener("MozDOMFullscreen:Request", this);
|
removeEventListener("MozDOMFullscreen:Request", this);
|
||||||
|
removeEventListener("contextmenu", this);
|
||||||
|
|
||||||
this.messageManager.removeMessageListener("GeckoView:DOMFullscreenEntered",
|
this.messageManager.removeMessageListener("GeckoView:DOMFullscreenEntered",
|
||||||
this);
|
this);
|
||||||
|
@ -66,13 +68,35 @@ class GeckoViewContent extends GeckoViewContentModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEvent(aEvent) {
|
handleEvent(aEvent) {
|
||||||
if (aEvent.originalTarget.defaultView != content) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
debug("handleEvent " + aEvent.type);
|
debug("handleEvent " + aEvent.type);
|
||||||
|
|
||||||
switch (aEvent.type) {
|
switch (aEvent.type) {
|
||||||
|
case "contextmenu":
|
||||||
|
function nearestParentHref(node) {
|
||||||
|
while (node && !node.href) {
|
||||||
|
node = node.parentNode;
|
||||||
|
}
|
||||||
|
return node && node.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
let node = aEvent.target;
|
||||||
|
let hrefNode = nearestParentHref(node);
|
||||||
|
let isImageNode = node instanceof Ci.nsIDOMHTMLImageElement;
|
||||||
|
let isMediaNode = node instanceof Ci.nsIDOMHTMLMediaElement;
|
||||||
|
let msg = {
|
||||||
|
screenX: aEvent.screenX,
|
||||||
|
screenY: aEvent.screenY,
|
||||||
|
uri: hrefNode,
|
||||||
|
elementSrc: isImageNode || isMediaNode
|
||||||
|
? node.currentSrc || node.src
|
||||||
|
: null
|
||||||
|
};
|
||||||
|
|
||||||
|
if (hrefNode || isImageNode || isMediaNode) {
|
||||||
|
sendAsyncMessage("GeckoView:ContextMenu", msg);
|
||||||
|
aEvent.preventDefault();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "MozDOMFullscreen:Request":
|
case "MozDOMFullscreen:Request":
|
||||||
sendAsyncMessage("GeckoView:DOMFullscreenRequest");
|
sendAsyncMessage("GeckoView:DOMFullscreenRequest");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -96,6 +96,7 @@ public class GeckoView extends LayerView {
|
||||||
new GeckoViewHandler<ContentListener>(
|
new GeckoViewHandler<ContentListener>(
|
||||||
"GeckoViewContent", this,
|
"GeckoViewContent", this,
|
||||||
new String[]{
|
new String[]{
|
||||||
|
"GeckoView:ContextMenu",
|
||||||
"GeckoView:DOMTitleChanged",
|
"GeckoView:DOMTitleChanged",
|
||||||
"GeckoView:FullScreenEnter",
|
"GeckoView:FullScreenEnter",
|
||||||
"GeckoView:FullScreenExit"
|
"GeckoView:FullScreenExit"
|
||||||
|
@ -107,7 +108,13 @@ public class GeckoView extends LayerView {
|
||||||
final GeckoBundle message,
|
final GeckoBundle message,
|
||||||
final EventCallback callback) {
|
final EventCallback callback) {
|
||||||
|
|
||||||
if ("GeckoView:DOMTitleChanged".equals(event)) {
|
if ("GeckoView:ContextMenu".equals(event)) {
|
||||||
|
listener.onContextMenu(GeckoView.this,
|
||||||
|
message.getInt("screenX"),
|
||||||
|
message.getInt("screenY"),
|
||||||
|
message.getString("uri"),
|
||||||
|
message.getString("elementSrc"));
|
||||||
|
} else if ("GeckoView:DOMTitleChanged".equals(event)) {
|
||||||
listener.onTitleChange(GeckoView.this,
|
listener.onTitleChange(GeckoView.this,
|
||||||
message.getString("title"));
|
message.getString("title"));
|
||||||
} else if ("GeckoView:FullScreenEnter".equals(event)) {
|
} else if ("GeckoView:FullScreenEnter".equals(event)) {
|
||||||
|
@ -115,7 +122,6 @@ public class GeckoView extends LayerView {
|
||||||
} else if ("GeckoView:FullScreenExit".equals(event)) {
|
} else if ("GeckoView:FullScreenExit".equals(event)) {
|
||||||
listener.onFullScreen(GeckoView.this, false);
|
listener.onFullScreen(GeckoView.this, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1288,6 +1294,23 @@ public class GeckoView extends LayerView {
|
||||||
* @param fullScreen True if the page is in full screen mode.
|
* @param fullScreen True if the page is in full screen mode.
|
||||||
*/
|
*/
|
||||||
void onFullScreen(GeckoView view, boolean fullScreen);
|
void onFullScreen(GeckoView view, boolean fullScreen);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A user has initiated the context menu via long-press.
|
||||||
|
* This event is fired on links, (nested) images and (nested) media
|
||||||
|
* elements.
|
||||||
|
*
|
||||||
|
* @param view The GeckoView that initiated the callback.
|
||||||
|
* @param screenX The screen coordinates of the press.
|
||||||
|
* @param screenY The screen coordinates of the press.
|
||||||
|
* @param uri The URI of the pressed link, set for links and
|
||||||
|
* image-links.
|
||||||
|
* @param elementSrc The source URI of the pressed element, set for
|
||||||
|
* (nested) images and media elements.
|
||||||
|
*/
|
||||||
|
void onContextMenu(GeckoView view, int screenX, int screenY,
|
||||||
|
String uri, String elementSrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface NavigationListener {
|
public interface NavigationListener {
|
||||||
|
|
|
@ -133,6 +133,14 @@ public class GeckoViewActivity extends Activity {
|
||||||
getActionBar().show();
|
getActionBar().show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onContextMenu(GeckoView view, int screenX, int screenY,
|
||||||
|
String uri, String elementSrc) {
|
||||||
|
Log.d(LOGTAG, "onContextMenu screenX=" + screenX +
|
||||||
|
" screenY=" + screenY + " uri=" + uri +
|
||||||
|
" elementSrc=" + elementSrc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MyGeckoViewProgress implements GeckoView.ProgressListener {
|
private class MyGeckoViewProgress implements GeckoView.ProgressListener {
|
||||||
|
|
|
@ -38,6 +38,7 @@ class GeckoViewContent extends GeckoViewModule {
|
||||||
this.messageManager.addMessageListener("GeckoView:DOMFullscreenExit", this);
|
this.messageManager.addMessageListener("GeckoView:DOMFullscreenExit", this);
|
||||||
this.messageManager.addMessageListener("GeckoView:DOMFullscreenRequest", this);
|
this.messageManager.addMessageListener("GeckoView:DOMFullscreenRequest", this);
|
||||||
this.messageManager.addMessageListener("GeckoView:DOMTitleChanged", this);
|
this.messageManager.addMessageListener("GeckoView:DOMTitleChanged", this);
|
||||||
|
this.messageManager.addMessageListener("GeckoView:ContextMenu", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bundle event handler.
|
// Bundle event handler.
|
||||||
|
@ -59,6 +60,7 @@ class GeckoViewContent extends GeckoViewModule {
|
||||||
this.messageManager.removeMessageListener("GeckoView:DOMFullscreenExit", this);
|
this.messageManager.removeMessageListener("GeckoView:DOMFullscreenExit", this);
|
||||||
this.messageManager.removeMessageListener("GeckoView:DOMFullscreenRequest", this);
|
this.messageManager.removeMessageListener("GeckoView:DOMFullscreenRequest", this);
|
||||||
this.messageManager.removeMessageListener("GeckoView:DOMTitleChanged", this);
|
this.messageManager.removeMessageListener("GeckoView:DOMTitleChanged", this);
|
||||||
|
this.messageManager.removeMessageListener("GeckoView:ContextMenu", this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// DOM event handler
|
// DOM event handler
|
||||||
|
@ -83,6 +85,15 @@ class GeckoViewContent extends GeckoViewModule {
|
||||||
debug("receiveMessage " + aMsg.name);
|
debug("receiveMessage " + aMsg.name);
|
||||||
|
|
||||||
switch (aMsg.name) {
|
switch (aMsg.name) {
|
||||||
|
case "GeckoView:ContextMenu":
|
||||||
|
this.eventDispatcher.sendRequest({
|
||||||
|
type: aMsg.name,
|
||||||
|
screenX: aMsg.data.screenX,
|
||||||
|
screenY: aMsg.data.screenY,
|
||||||
|
elementSrc: aMsg.data.elementSrc,
|
||||||
|
uri: aMsg.data.uri
|
||||||
|
});
|
||||||
|
break;
|
||||||
case "GeckoView:DOMFullscreenExit":
|
case "GeckoView:DOMFullscreenExit":
|
||||||
this.window.QueryInterface(Ci.nsIInterfaceRequestor)
|
this.window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
.getInterface(Ci.nsIDOMWindowUtils)
|
.getInterface(Ci.nsIDOMWindowUtils)
|
||||||
|
|
|
@ -1440,6 +1440,7 @@ pref("javascript.options.discardSystemSource", false);
|
||||||
// Comment 32 and Bug 613551.
|
// Comment 32 and Bug 613551.
|
||||||
pref("javascript.options.mem.high_water_mark", 128);
|
pref("javascript.options.mem.high_water_mark", 128);
|
||||||
pref("javascript.options.mem.max", -1);
|
pref("javascript.options.mem.max", -1);
|
||||||
|
pref("javascript.options.mem.nursery.max_kb", -1);
|
||||||
pref("javascript.options.mem.gc_per_zone", true);
|
pref("javascript.options.mem.gc_per_zone", true);
|
||||||
pref("javascript.options.mem.gc_incremental", true);
|
pref("javascript.options.mem.gc_incremental", true);
|
||||||
pref("javascript.options.mem.gc_incremental_slice_ms", 5);
|
pref("javascript.options.mem.gc_incremental_slice_ms", 5);
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include "ExtensionProtocolHandler.h"
|
#include "ExtensionProtocolHandler.h"
|
||||||
|
|
||||||
#include "mozilla/AbstractThread.h"
|
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
#include "mozilla/ExtensionPolicyService.h"
|
#include "mozilla/ExtensionPolicyService.h"
|
||||||
#include "mozilla/FileUtils.h"
|
#include "mozilla/FileUtils.h"
|
||||||
|
@ -19,6 +18,7 @@
|
||||||
#include "FileDescriptor.h"
|
#include "FileDescriptor.h"
|
||||||
#include "FileDescriptorFile.h"
|
#include "FileDescriptorFile.h"
|
||||||
#include "LoadInfo.h"
|
#include "LoadInfo.h"
|
||||||
|
#include "nsContentUtils.h"
|
||||||
#include "nsServiceManagerUtils.h"
|
#include "nsServiceManagerUtils.h"
|
||||||
#include "nsIFile.h"
|
#include "nsIFile.h"
|
||||||
#include "nsIFileChannel.h"
|
#include "nsIFileChannel.h"
|
||||||
|
@ -109,6 +109,8 @@ class ExtensionStreamGetter : public RefCounted<ExtensionStreamGetter>
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(aURI);
|
MOZ_ASSERT(aURI);
|
||||||
MOZ_ASSERT(aLoadInfo);
|
MOZ_ASSERT(aLoadInfo);
|
||||||
|
|
||||||
|
SetupEventTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
// To use when getting an FD for a packed extension JAR file
|
// To use when getting an FD for a packed extension JAR file
|
||||||
|
@ -126,10 +128,21 @@ class ExtensionStreamGetter : public RefCounted<ExtensionStreamGetter>
|
||||||
MOZ_ASSERT(aLoadInfo);
|
MOZ_ASSERT(aLoadInfo);
|
||||||
MOZ_ASSERT(mJarChannel);
|
MOZ_ASSERT(mJarChannel);
|
||||||
MOZ_ASSERT(aJarFile);
|
MOZ_ASSERT(aJarFile);
|
||||||
|
|
||||||
|
SetupEventTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
~ExtensionStreamGetter() {}
|
~ExtensionStreamGetter() {}
|
||||||
|
|
||||||
|
void SetupEventTarget()
|
||||||
|
{
|
||||||
|
mMainThreadEventTarget =
|
||||||
|
nsContentUtils::GetEventTargetByLoadInfo(mLoadInfo, TaskCategory::Other);
|
||||||
|
if (!mMainThreadEventTarget) {
|
||||||
|
mMainThreadEventTarget = GetMainThreadSerialEventTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Get an input stream or file descriptor from the parent asynchronously.
|
// Get an input stream or file descriptor from the parent asynchronously.
|
||||||
Result<Ok, nsresult> GetAsync(nsIStreamListener* aListener,
|
Result<Ok, nsresult> GetAsync(nsIStreamListener* aListener,
|
||||||
nsIChannel* aChannel);
|
nsIChannel* aChannel);
|
||||||
|
@ -149,6 +162,7 @@ class ExtensionStreamGetter : public RefCounted<ExtensionStreamGetter>
|
||||||
nsCOMPtr<nsIFile> mJarFile;
|
nsCOMPtr<nsIFile> mJarFile;
|
||||||
nsCOMPtr<nsIStreamListener> mListener;
|
nsCOMPtr<nsIStreamListener> mListener;
|
||||||
nsCOMPtr<nsIChannel> mChannel;
|
nsCOMPtr<nsIChannel> mChannel;
|
||||||
|
nsCOMPtr<nsISerialEventTarget> mMainThreadEventTarget;
|
||||||
bool mIsJarChannel;
|
bool mIsJarChannel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -225,6 +239,7 @@ ExtensionStreamGetter::GetAsync(nsIStreamListener* aListener,
|
||||||
nsIChannel* aChannel)
|
nsIChannel* aChannel)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsNeckoChild());
|
MOZ_ASSERT(IsNeckoChild());
|
||||||
|
MOZ_ASSERT(mMainThreadEventTarget);
|
||||||
|
|
||||||
mListener = aListener;
|
mListener = aListener;
|
||||||
mChannel = aChannel;
|
mChannel = aChannel;
|
||||||
|
@ -241,7 +256,7 @@ ExtensionStreamGetter::GetAsync(nsIStreamListener* aListener,
|
||||||
if (mIsJarChannel) {
|
if (mIsJarChannel) {
|
||||||
// Request an FD for this moz-extension URI
|
// Request an FD for this moz-extension URI
|
||||||
gNeckoChild->SendGetExtensionFD(uri, loadInfo)->Then(
|
gNeckoChild->SendGetExtensionFD(uri, loadInfo)->Then(
|
||||||
AbstractThread::MainThread(),
|
mMainThreadEventTarget,
|
||||||
__func__,
|
__func__,
|
||||||
[self] (const FileDescriptor& fd) {
|
[self] (const FileDescriptor& fd) {
|
||||||
self->OnFD(fd);
|
self->OnFD(fd);
|
||||||
|
@ -255,7 +270,7 @@ ExtensionStreamGetter::GetAsync(nsIStreamListener* aListener,
|
||||||
|
|
||||||
// Request an input stream for this moz-extension URI
|
// Request an input stream for this moz-extension URI
|
||||||
gNeckoChild->SendGetExtensionStream(uri, loadInfo)->Then(
|
gNeckoChild->SendGetExtensionStream(uri, loadInfo)->Then(
|
||||||
AbstractThread::MainThread(),
|
mMainThreadEventTarget,
|
||||||
__func__,
|
__func__,
|
||||||
[self] (const OptionalIPCStream& stream) {
|
[self] (const OptionalIPCStream& stream) {
|
||||||
nsCOMPtr<nsIInputStream> inputStream;
|
nsCOMPtr<nsIInputStream> inputStream;
|
||||||
|
@ -277,6 +292,7 @@ ExtensionStreamGetter::OnStream(nsIInputStream* aStream)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(IsNeckoChild());
|
MOZ_ASSERT(IsNeckoChild());
|
||||||
MOZ_ASSERT(mListener);
|
MOZ_ASSERT(mListener);
|
||||||
|
MOZ_ASSERT(mMainThreadEventTarget);
|
||||||
|
|
||||||
// We must keep an owning reference to the listener
|
// We must keep an owning reference to the listener
|
||||||
// until we pass it on to AsyncRead.
|
// until we pass it on to AsyncRead.
|
||||||
|
@ -293,7 +309,8 @@ ExtensionStreamGetter::OnStream(nsIInputStream* aStream)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsIInputStreamPump> pump;
|
nsCOMPtr<nsIInputStreamPump> pump;
|
||||||
nsresult rv = NS_NewInputStreamPump(getter_AddRefs(pump), aStream);
|
nsresult rv = NS_NewInputStreamPump(getter_AddRefs(pump), aStream, -1, -1, 0,
|
||||||
|
0, false, mMainThreadEventTarget);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
mChannel->Cancel(NS_BINDING_ABORTED);
|
mChannel->Cancel(NS_BINDING_ABORTED);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = [
|
this.EXPORTED_SYMBOLS = [
|
||||||
"BulkKeyBundle",
|
"BulkKeyBundle",
|
||||||
"SyncKeyBundle"
|
|
||||||
];
|
];
|
||||||
|
|
||||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||||
|
@ -167,49 +166,3 @@ BulkKeyBundle.prototype = {
|
||||||
this.hmacKey = Utils.safeAtoB(value[1]);
|
this.hmacKey = Utils.safeAtoB(value[1]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a key pair derived from a Sync Key via HKDF.
|
|
||||||
*
|
|
||||||
* Instances of this type should be considered immutable. You create an
|
|
||||||
* instance by specifying the username and 26 character "friendly" Base32
|
|
||||||
* encoded Sync Key. The Sync Key is derived at instance creation time.
|
|
||||||
*
|
|
||||||
* If the username or Sync Key is invalid, an Error will be thrown.
|
|
||||||
*/
|
|
||||||
this.SyncKeyBundle = function SyncKeyBundle(username, syncKey) {
|
|
||||||
let log = Log.repository.getLogger("Sync.SyncKeyBundle");
|
|
||||||
log.info("SyncKeyBundle being created.");
|
|
||||||
KeyBundle.call(this);
|
|
||||||
|
|
||||||
this.generateFromKey(username, syncKey);
|
|
||||||
}
|
|
||||||
SyncKeyBundle.prototype = {
|
|
||||||
__proto__: KeyBundle.prototype,
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we've got a string, hash it into keys and store them.
|
|
||||||
*/
|
|
||||||
generateFromKey: function generateFromKey(username, syncKey) {
|
|
||||||
if (!username || (typeof username != "string")) {
|
|
||||||
throw new Error("Sync Key cannot be generated from non-string username.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!syncKey || (typeof syncKey != "string")) {
|
|
||||||
throw new Error("Sync Key cannot be generated from non-string key.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Utils.isPassphrase(syncKey)) {
|
|
||||||
throw new Error("Provided key is not a passphrase, cannot derive Sync " +
|
|
||||||
"Key Bundle.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expand the base32 Sync Key to an AES 256 and 256 bit HMAC key.
|
|
||||||
let prk = Utils.decodeKeyBase32(syncKey);
|
|
||||||
let info = HMAC_INPUT + username;
|
|
||||||
let okm = Utils.hkdfExpand(prk, info, 32 * 2);
|
|
||||||
this.encryptionKey = okm.slice(0, 32);
|
|
||||||
this.hmacKey = okm.slice(32, 64);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,8 @@ const EHTestsCommon = {
|
||||||
generateCredentialsChangedFailure() {
|
generateCredentialsChangedFailure() {
|
||||||
// Make sync fail due to changed credentials. We simply re-encrypt
|
// Make sync fail due to changed credentials. We simply re-encrypt
|
||||||
// the keys with a different Sync Key, without changing the local one.
|
// the keys with a different Sync Key, without changing the local one.
|
||||||
let newSyncKeyBundle = new SyncKeyBundle("johndoe", "23456234562345623456234562");
|
let newSyncKeyBundle = new BulkKeyBundle("crypto");
|
||||||
|
newSyncKeyBundle.generateRandom();
|
||||||
let keys = Service.collectionKeys.asWBO();
|
let keys = Service.collectionKeys.asWBO();
|
||||||
keys.encrypt(newSyncKeyBundle);
|
keys.encrypt(newSyncKeyBundle);
|
||||||
return keys.upload(Service.resource(Service.cryptoKeysURL));
|
return keys.upload(Service.resource(Service.cryptoKeysURL));
|
||||||
|
|
|
@ -117,54 +117,6 @@ add_test(function test_repeated_hmac() {
|
||||||
run_next_test();
|
run_next_test();
|
||||||
});
|
});
|
||||||
|
|
||||||
add_test(function test_sync_key_bundle_derivation() {
|
|
||||||
_("Ensure derivation from known values works.");
|
|
||||||
|
|
||||||
// The known values in this test were originally verified against Firefox
|
|
||||||
// Home.
|
|
||||||
let bundle = new SyncKeyBundle("st3fan", "q7ynpwq7vsc9m34hankbyi3s3i");
|
|
||||||
|
|
||||||
// These should be compared to the results from Home, as they once were.
|
|
||||||
let e = "14b8c09fa84e92729ee695160af6e0385f8f6215a25d14906e1747bdaa2de426";
|
|
||||||
let h = "370e3566245d79fe602a3adb5137e42439cd2a571235197e0469d7d541b07875";
|
|
||||||
|
|
||||||
let realE = Utils.bytesAsHex(bundle.encryptionKey);
|
|
||||||
let realH = Utils.bytesAsHex(bundle.hmacKey);
|
|
||||||
|
|
||||||
_("Real E: " + realE);
|
|
||||||
_("Real H: " + realH);
|
|
||||||
do_check_eq(realH, h);
|
|
||||||
do_check_eq(realE, e);
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
add_test(function test_keymanager() {
|
|
||||||
let testKey = "ababcdefabcdefabcdefabcdef";
|
|
||||||
let username = "john@example.com";
|
|
||||||
|
|
||||||
// Decode the key here to mirror what generateEntry will do,
|
|
||||||
// but pass it encoded into the KeyBundle call below.
|
|
||||||
|
|
||||||
let sha256inputE = "" + HMAC_INPUT + username + "\x01";
|
|
||||||
let key = Utils.makeHMACKey(Utils.decodeKeyBase32(testKey));
|
|
||||||
let encryptKey = sha256HMAC(sha256inputE, key);
|
|
||||||
|
|
||||||
let sha256inputH = encryptKey + HMAC_INPUT + username + "\x02";
|
|
||||||
let hmacKey = sha256HMAC(sha256inputH, key);
|
|
||||||
|
|
||||||
// Encryption key is stored in base64 for WeaveCrypto convenience.
|
|
||||||
do_check_eq(encryptKey, new SyncKeyBundle(username, testKey).encryptionKey);
|
|
||||||
do_check_eq(hmacKey, new SyncKeyBundle(username, testKey).hmacKey);
|
|
||||||
|
|
||||||
// Test with the same KeyBundle for both.
|
|
||||||
let obj = new SyncKeyBundle(username, testKey);
|
|
||||||
do_check_eq(hmacKey, obj.hmacKey);
|
|
||||||
do_check_eq(encryptKey, obj.encryptionKey);
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(async function test_ensureLoggedIn() {
|
add_task(async function test_ensureLoggedIn() {
|
||||||
let log = Log.repository.getLogger("Test");
|
let log = Log.repository.getLogger("Test");
|
||||||
Log.repository.rootLogger.addAppender(new Log.DumpAppender());
|
Log.repository.rootLogger.addAppender(new Log.DumpAppender());
|
||||||
|
|
|
@ -220,7 +220,8 @@ add_task(async function v5_upgrade() {
|
||||||
_("New meta/global: " + JSON.stringify(meta_global));
|
_("New meta/global: " + JSON.stringify(meta_global));
|
||||||
|
|
||||||
// Fill the keys with bad data.
|
// Fill the keys with bad data.
|
||||||
let badKeys = new SyncKeyBundle("foobar", "aaaaaaaaaaaaaaaaaaaaaaaaaa");
|
let badKeys = new BulkKeyBundle("crypto");
|
||||||
|
badKeys.generateRandom();
|
||||||
await update_server_keys(badKeys, "keys", "crypto/keys"); // v4
|
await update_server_keys(badKeys, "keys", "crypto/keys"); // v4
|
||||||
await update_server_keys(badKeys, "bulk", "crypto/bulk"); // v5
|
await update_server_keys(badKeys, "bulk", "crypto/bulk"); // v5
|
||||||
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[controller-on-reload.https.html]
|
|
||||||
type: testharness
|
|
||||||
[controller is set upon reload after registration]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
[url-parsing.https.html]
|
|
||||||
type: testharness
|
|
||||||
[register should use the relevant global of the object it was called on to resolve the script URL and the default scope URL]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[register should use the relevant global of the object it was called on to resolve the script URL and the given scope URL]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
[multiple-register.https.html]
|
|
||||||
type: testharness
|
|
||||||
[Subsequent registrations from a different iframe resolve to the different registration object but they refer to the same registration and workers]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
[registration-iframe.https.html]
|
|
||||||
type: testharness
|
|
||||||
[register method should use the "relevant global object" to parse its scriptURL and scope - normal case]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[register method should use the "relevant global object" to parse its scriptURL and scope - error case]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
||||||
[A scope url should start with the given script url]
|
|
||||||
expected: FAIL
|
|
||||||
|
|
|
@ -33,6 +33,30 @@ XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||||
"resource://gre/modules/FileUtils.jsm");
|
"resource://gre/modules/FileUtils.jsm");
|
||||||
|
|
||||||
|
function _TabRemovalObserver(resolver, tabParentIds) {
|
||||||
|
this._resolver = resolver;
|
||||||
|
this._tabParentIds = tabParentIds;
|
||||||
|
Services.obs.addObserver(this, "ipc:browser-destroyed");
|
||||||
|
}
|
||||||
|
|
||||||
|
_TabRemovalObserver.prototype = {
|
||||||
|
_resolver: null,
|
||||||
|
_tabParentIds: null,
|
||||||
|
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
|
||||||
|
|
||||||
|
observe(subject, topic, data) {
|
||||||
|
let tabParent = subject.QueryInterface(Ci.nsITabParent);
|
||||||
|
if (this._tabParentIds.has(tabParent.tabId)) {
|
||||||
|
this._tabParentIds.delete(tabParent.tabId);
|
||||||
|
if (this._tabParentIds.size == 0) {
|
||||||
|
Services.obs.removeObserver(this, "ipc:browser-destroyed");
|
||||||
|
this._resolver();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function _ContextualIdentityService(path) {
|
function _ContextualIdentityService(path) {
|
||||||
this.init(path);
|
this.init(path);
|
||||||
}
|
}
|
||||||
|
@ -309,9 +333,33 @@ _ContextualIdentityService.prototype = {
|
||||||
},
|
},
|
||||||
|
|
||||||
closeContainerTabs(userContextId = 0) {
|
closeContainerTabs(userContextId = 0) {
|
||||||
this._forEachContainerTab(function(tab, tabbrowser) {
|
return new Promise(resolve => {
|
||||||
tabbrowser.removeTab(tab);
|
let tabParentIds = new Set();
|
||||||
}, userContextId);
|
this._forEachContainerTab((tab, tabbrowser) => {
|
||||||
|
let frameLoader = tab.linkedBrowser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
||||||
|
|
||||||
|
// We don't have tabParent in non-e10s mode.
|
||||||
|
if (frameLoader.tabParent) {
|
||||||
|
tabParentIds.add(frameLoader.tabParent.tabId);
|
||||||
|
}
|
||||||
|
|
||||||
|
tabbrowser.removeTab(tab);
|
||||||
|
}, userContextId);
|
||||||
|
|
||||||
|
if (tabParentIds.size == 0) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
new _TabRemovalObserver(resolve, tabParentIds);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
notifyAllContainersCleared() {
|
||||||
|
for (let identity of this._identities) {
|
||||||
|
Services.obs.notifyObservers(null, "clear-origin-attributes-data",
|
||||||
|
JSON.stringify({ userContextId: identity.userContextId }));
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_forEachContainerTab(callback, userContextId = 0) {
|
_forEachContainerTab(callback, userContextId = 0) {
|
||||||
|
|
|
@ -48,12 +48,6 @@ const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessio
|
||||||
const LOGGER_NAME = "Toolkit.Telemetry";
|
const LOGGER_NAME = "Toolkit.Telemetry";
|
||||||
const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
|
const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
|
||||||
|
|
||||||
const PREF_BRANCH = "toolkit.telemetry.";
|
|
||||||
const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID";
|
|
||||||
const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
|
|
||||||
const PREF_UNIFIED = PREF_BRANCH + "unified";
|
|
||||||
const PREF_SHUTDOWN_PINGSENDER = PREF_BRANCH + "shutdownPingSender.enabled";
|
|
||||||
|
|
||||||
const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
|
const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
|
||||||
const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
|
const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs";
|
||||||
const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs";
|
const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs";
|
||||||
|
@ -65,7 +59,7 @@ const ABORTED_SESSION_FILE_NAME = "aborted-session-ping";
|
||||||
|
|
||||||
// Whether the FHR/Telemetry unification features are enabled.
|
// Whether the FHR/Telemetry unification features are enabled.
|
||||||
// Changing this pref requires a restart.
|
// Changing this pref requires a restart.
|
||||||
const IS_UNIFIED_TELEMETRY = Preferences.get(PREF_UNIFIED, false);
|
const IS_UNIFIED_TELEMETRY = Preferences.get(TelemetryUtils.Preferences.Unified, false);
|
||||||
|
|
||||||
// Maximum number of content payloads that we are willing to store.
|
// Maximum number of content payloads that we are willing to store.
|
||||||
const MAX_NUM_CONTENT_PAYLOADS = 10;
|
const MAX_NUM_CONTENT_PAYLOADS = 10;
|
||||||
|
@ -531,9 +525,6 @@ var TelemetryScheduler = {
|
||||||
this.EXPORTED_SYMBOLS = ["TelemetrySession"];
|
this.EXPORTED_SYMBOLS = ["TelemetrySession"];
|
||||||
|
|
||||||
this.TelemetrySession = Object.freeze({
|
this.TelemetrySession = Object.freeze({
|
||||||
Constants: Object.freeze({
|
|
||||||
PREF_PREVIOUS_BUILDID,
|
|
||||||
}),
|
|
||||||
/**
|
/**
|
||||||
* Send a ping to a test server. Used only for testing.
|
* Send a ping to a test server. Used only for testing.
|
||||||
*/
|
*/
|
||||||
|
@ -1516,12 +1507,12 @@ var Impl = {
|
||||||
|
|
||||||
// Record old value and update build ID preference if this is the first
|
// Record old value and update build ID preference if this is the first
|
||||||
// run with a new build ID.
|
// run with a new build ID.
|
||||||
let previousBuildId = Preferences.get(PREF_PREVIOUS_BUILDID, null);
|
let previousBuildId = Preferences.get(TelemetryUtils.Preferences.PreviousBuildID, null);
|
||||||
let thisBuildID = Services.appinfo.appBuildID;
|
let thisBuildID = Services.appinfo.appBuildID;
|
||||||
// If there is no previousBuildId preference, we send null to the server.
|
// If there is no previousBuildId preference, we send null to the server.
|
||||||
if (previousBuildId != thisBuildID) {
|
if (previousBuildId != thisBuildID) {
|
||||||
this._previousBuildId = previousBuildId;
|
this._previousBuildId = previousBuildId;
|
||||||
Preferences.set(PREF_PREVIOUS_BUILDID, thisBuildID);
|
Preferences.set(TelemetryUtils.Preferences.PreviousBuildID, thisBuildID);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.attachEarlyObservers();
|
this.attachEarlyObservers();
|
||||||
|
@ -1811,7 +1802,7 @@ var Impl = {
|
||||||
// browsing session on, to mitigate issues with "bot" profiles (see bug 1354482).
|
// browsing session on, to mitigate issues with "bot" profiles (see bug 1354482).
|
||||||
// Note: sending the "shutdown" ping using the pingsender is currently disabled
|
// Note: sending the "shutdown" ping using the pingsender is currently disabled
|
||||||
// due to a crash happening on OSX platforms. See bug 1357745 for context.
|
// due to a crash happening on OSX platforms. See bug 1357745 for context.
|
||||||
let sendWithPingsender = Preferences.get(PREF_SHUTDOWN_PINGSENDER, false) &&
|
let sendWithPingsender = Preferences.get(TelemetryUtils.Preferences.ShutdownPingSender, false) &&
|
||||||
!TelemetryReportingPolicy.isFirstRun();
|
!TelemetryReportingPolicy.isFirstRun();
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
|
|
|
@ -36,6 +36,7 @@ this.TelemetryUtils = {
|
||||||
Unified: "toolkit.telemetry.unified",
|
Unified: "toolkit.telemetry.unified",
|
||||||
NewProfilePingEnabled: "toolkit.telemetry.newProfilePing.enabled",
|
NewProfilePingEnabled: "toolkit.telemetry.newProfilePing.enabled",
|
||||||
NewProfilePingDelay: "toolkit.telemetry.newProfilePing.delay",
|
NewProfilePingDelay: "toolkit.telemetry.newProfilePing.delay",
|
||||||
|
PreviousBuildID: "toolkit.telemetry.previousBuildID",
|
||||||
|
|
||||||
// Log Preferences
|
// Log Preferences
|
||||||
LogLevel: "toolkit.telemetry.log.level",
|
LogLevel: "toolkit.telemetry.log.level",
|
||||||
|
|
|
@ -34,7 +34,7 @@ add_task(async function test_firstRun() {
|
||||||
let metadata = TelemetrySession.getMetadata();
|
let metadata = TelemetrySession.getMetadata();
|
||||||
do_check_false("previousBuildID" in metadata);
|
do_check_false("previousBuildID" in metadata);
|
||||||
let appBuildID = getAppInfo().appBuildID;
|
let appBuildID = getAppInfo().appBuildID;
|
||||||
let buildIDPref = Services.prefs.getCharPref(TelemetrySession.Constants.PREF_PREVIOUS_BUILDID);
|
let buildIDPref = Services.prefs.getCharPref(TelemetryUtils.Preferences.PreviousBuildID);
|
||||||
do_check_eq(appBuildID, buildIDPref);
|
do_check_eq(appBuildID, buildIDPref);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ add_task(async function test_newBuild() {
|
||||||
await TelemetryController.testReset();
|
await TelemetryController.testReset();
|
||||||
let metadata = TelemetrySession.getMetadata();
|
let metadata = TelemetrySession.getMetadata();
|
||||||
do_check_eq(metadata.previousBuildId, oldBuildID);
|
do_check_eq(metadata.previousBuildId, oldBuildID);
|
||||||
let buildIDPref = Services.prefs.getCharPref(TelemetrySession.Constants.PREF_PREVIOUS_BUILDID);
|
let buildIDPref = Services.prefs.getCharPref(TelemetryUtils.Preferences.PreviousBuildID);
|
||||||
do_check_eq(NEW_BUILD_ID, buildIDPref);
|
do_check_eq(NEW_BUILD_ID, buildIDPref);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
#include "ProfilerMarker.h"
|
#include "ProfilerMarker.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
ProfileBuffer::ProfileBuffer(int aEntrySize)
|
ProfileBuffer::ProfileBuffer(int aEntrySize)
|
||||||
: mEntries(mozilla::MakeUnique<ProfileBufferEntry[]>(aEntrySize))
|
: mEntries(mozilla::MakeUnique<ProfileBufferEntry[]>(aEntrySize))
|
||||||
, mWritePos(0)
|
, mWritePos(0)
|
||||||
|
@ -55,24 +57,6 @@ ProfileBuffer::AddThreadIdEntry(int aThreadId, LastSample* aLS)
|
||||||
AddEntry(ProfileBufferEntry::ThreadId(aThreadId));
|
AddEntry(ProfileBufferEntry::ThreadId(aThreadId));
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ProfileBuffer::AddDynamicStringEntry(const char* aStr)
|
|
||||||
{
|
|
||||||
size_t strLen = strlen(aStr) + 1; // +1 for the null terminator
|
|
||||||
for (size_t j = 0; j < strLen; ) {
|
|
||||||
// Store up to kNumChars characters in the entry.
|
|
||||||
char chars[ProfileBufferEntry::kNumChars];
|
|
||||||
size_t len = ProfileBufferEntry::kNumChars;
|
|
||||||
if (j + len >= strLen) {
|
|
||||||
len = strLen - j;
|
|
||||||
}
|
|
||||||
memcpy(chars, &aStr[j], len);
|
|
||||||
j += ProfileBufferEntry::kNumChars;
|
|
||||||
|
|
||||||
AddEntry(ProfileBufferEntry::DynamicStringFragment(chars));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ProfileBuffer::AddStoredMarker(ProfilerMarker *aStoredMarker)
|
ProfileBuffer::AddStoredMarker(ProfilerMarker *aStoredMarker)
|
||||||
{
|
{
|
||||||
|
@ -80,6 +64,51 @@ ProfileBuffer::AddStoredMarker(ProfilerMarker *aStoredMarker)
|
||||||
mStoredMarkers.insert(aStoredMarker);
|
mStoredMarkers.insert(aStoredMarker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProfileBuffer::CollectNativeLeafAddr(void* aAddr)
|
||||||
|
{
|
||||||
|
AddEntry(ProfileBufferEntry::NativeLeafAddr(aAddr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProfileBuffer::CollectJitReturnAddr(void* aAddr)
|
||||||
|
{
|
||||||
|
AddEntry(ProfileBufferEntry::JitReturnAddr(aAddr));
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ProfileBuffer::CollectCodeLocation(
|
||||||
|
const char* aLabel, const char* aStr, int aLineNumber,
|
||||||
|
const Maybe<js::ProfileEntry::Category>& aCategory)
|
||||||
|
{
|
||||||
|
AddEntry(ProfileBufferEntry::Label(aLabel));
|
||||||
|
|
||||||
|
if (aStr) {
|
||||||
|
// Store the string using one or more DynamicStringFragment entries.
|
||||||
|
size_t strLen = strlen(aStr) + 1; // +1 for the null terminator
|
||||||
|
for (size_t j = 0; j < strLen; ) {
|
||||||
|
// Store up to kNumChars characters in the entry.
|
||||||
|
char chars[ProfileBufferEntry::kNumChars];
|
||||||
|
size_t len = ProfileBufferEntry::kNumChars;
|
||||||
|
if (j + len >= strLen) {
|
||||||
|
len = strLen - j;
|
||||||
|
}
|
||||||
|
memcpy(chars, &aStr[j], len);
|
||||||
|
j += ProfileBufferEntry::kNumChars;
|
||||||
|
|
||||||
|
AddEntry(ProfileBufferEntry::DynamicStringFragment(chars));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aLineNumber != -1) {
|
||||||
|
AddEntry(ProfileBufferEntry::LineNumber(aLineNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aCategory.isSome()) {
|
||||||
|
AddEntry(ProfileBufferEntry::Category(int(*aCategory)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ProfileBuffer::DeleteExpiredStoredMarkers()
|
ProfileBuffer::DeleteExpiredStoredMarkers()
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "mozilla/RefPtr.h"
|
#include "mozilla/RefPtr.h"
|
||||||
#include "mozilla/RefCounted.h"
|
#include "mozilla/RefCounted.h"
|
||||||
|
|
||||||
class ProfileBuffer final
|
class ProfileBuffer final : public ProfilerStackCollector
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit ProfileBuffer(int aEntrySize);
|
explicit ProfileBuffer(int aEntrySize);
|
||||||
|
@ -42,9 +42,16 @@ public:
|
||||||
// record the resulting generation and index in |aLS| if it's non-null.
|
// record the resulting generation and index in |aLS| if it's non-null.
|
||||||
void AddThreadIdEntry(int aThreadId, LastSample* aLS = nullptr);
|
void AddThreadIdEntry(int aThreadId, LastSample* aLS = nullptr);
|
||||||
|
|
||||||
// Add to the buffer a dynamic string. It'll be spread across one or more
|
virtual mozilla::Maybe<uint32_t> Generation() override
|
||||||
// DynamicStringFragment entries.
|
{
|
||||||
void AddDynamicStringEntry(const char* aStr);
|
return mozilla::Some(mGeneration);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void CollectNativeLeafAddr(void* aAddr) override;
|
||||||
|
virtual void CollectJitReturnAddr(void* aAddr) override;
|
||||||
|
virtual void CollectCodeLocation(
|
||||||
|
const char* aLabel, const char* aStr, int aLineNumber,
|
||||||
|
const mozilla::Maybe<js::ProfileEntry::Category>& aCategory) override;
|
||||||
|
|
||||||
// Maximum size of a frameKey string that we'll handle.
|
// Maximum size of a frameKey string that we'll handle.
|
||||||
static const size_t kMaxFrameKeyLength = 512;
|
static const size_t kMaxFrameKeyLength = 512;
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
//
|
//
|
||||||
// - A "backtrace" sample is the simplest kind. It is done in response to an
|
// - A "backtrace" sample is the simplest kind. It is done in response to an
|
||||||
// API call (profiler_suspend_and_sample_thread()). It involves getting a
|
// API call (profiler_suspend_and_sample_thread()). It involves getting a
|
||||||
// stack trace and passing it to a callback function; it does not write to a
|
// stack trace via a ProfilerStackCollector; it does not write to a
|
||||||
// ProfileBuffer. The sampling is done from off-thread, and so uses
|
// ProfileBuffer. The sampling is done from off-thread, and so uses
|
||||||
// SuspendAndSampleAndResumeThread() to get the register values.
|
// SuspendAndSampleAndResumeThread() to get the register values.
|
||||||
|
|
||||||
|
@ -54,7 +54,9 @@
|
||||||
#include "nsIXULRuntime.h"
|
#include "nsIXULRuntime.h"
|
||||||
#include "nsDirectoryServiceUtils.h"
|
#include "nsDirectoryServiceUtils.h"
|
||||||
#include "nsDirectoryServiceDefs.h"
|
#include "nsDirectoryServiceDefs.h"
|
||||||
|
#include "nsJSPrincipals.h"
|
||||||
#include "nsMemoryReporterManager.h"
|
#include "nsMemoryReporterManager.h"
|
||||||
|
#include "nsScriptSecurityManager.h"
|
||||||
#include "nsXULAppAPI.h"
|
#include "nsXULAppAPI.h"
|
||||||
#include "nsProfilerStartParams.h"
|
#include "nsProfilerStartParams.h"
|
||||||
#include "ProfilerParent.h"
|
#include "ProfilerParent.h"
|
||||||
|
@ -657,17 +659,31 @@ public:
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static bool
|
||||||
AddPseudoEntry(PSLockRef aLock, NotNull<RacyThreadInfo*> aRacyInfo,
|
IsChromeJSScript(JSScript* aScript)
|
||||||
const js::ProfileEntry& entry, ProfileBuffer& aBuffer)
|
|
||||||
{
|
{
|
||||||
// WARNING: this function runs within the profiler's "critical section".
|
// WARNING: this function runs within the profiler's "critical section".
|
||||||
|
|
||||||
|
nsIScriptSecurityManager* const secman =
|
||||||
|
nsScriptSecurityManager::GetScriptSecurityManager();
|
||||||
|
NS_ENSURE_TRUE(secman, false);
|
||||||
|
|
||||||
|
JSPrincipals* const principals = JS_GetScriptPrincipals(aScript);
|
||||||
|
return secman->IsSystemPrincipal(nsJSPrincipals::get(principals));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
AddPseudoEntry(uint32_t aFeatures, NotNull<RacyThreadInfo*> aRacyInfo,
|
||||||
|
const js::ProfileEntry& entry,
|
||||||
|
ProfilerStackCollector& aCollector)
|
||||||
|
{
|
||||||
|
// WARNING: this function runs within the profiler's "critical section".
|
||||||
|
// WARNING: this function might be called while the profiler is inactive, and
|
||||||
|
// cannot rely on ActivePS.
|
||||||
|
|
||||||
MOZ_ASSERT(entry.kind() == js::ProfileEntry::Kind::CPP_NORMAL ||
|
MOZ_ASSERT(entry.kind() == js::ProfileEntry::Kind::CPP_NORMAL ||
|
||||||
entry.kind() == js::ProfileEntry::Kind::JS_NORMAL);
|
entry.kind() == js::ProfileEntry::Kind::JS_NORMAL);
|
||||||
|
|
||||||
aBuffer.AddEntry(ProfileBufferEntry::Label(entry.label()));
|
|
||||||
|
|
||||||
const char* dynamicString = entry.dynamicString();
|
const char* dynamicString = entry.dynamicString();
|
||||||
int lineno = -1;
|
int lineno = -1;
|
||||||
|
|
||||||
|
@ -675,28 +691,30 @@ AddPseudoEntry(PSLockRef aLock, NotNull<RacyThreadInfo*> aRacyInfo,
|
||||||
// |dynamicString|. Perhaps it shouldn't?
|
// |dynamicString|. Perhaps it shouldn't?
|
||||||
|
|
||||||
if (dynamicString) {
|
if (dynamicString) {
|
||||||
// Adjust the dynamic string as necessary.
|
bool isChromeJSEntry = false;
|
||||||
if (ActivePS::FeaturePrivacy(aLock)) {
|
|
||||||
dynamicString = "(private)";
|
|
||||||
} else if (strlen(dynamicString) >= ProfileBuffer::kMaxFrameKeyLength) {
|
|
||||||
dynamicString = "(too long)";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the string using one or more DynamicStringFragment entries.
|
|
||||||
aBuffer.AddDynamicStringEntry(dynamicString);
|
|
||||||
if (entry.isJs()) {
|
if (entry.isJs()) {
|
||||||
JSScript* script = entry.script();
|
// We call entry.script() repeatedly -- rather than storing the result in
|
||||||
if (script) {
|
// a local variable in order -- to avoid rooting hazards.
|
||||||
|
if (entry.script()) {
|
||||||
|
isChromeJSEntry = IsChromeJSScript(entry.script());
|
||||||
if (!entry.pc()) {
|
if (!entry.pc()) {
|
||||||
// The JIT only allows the top-most entry to have a nullptr pc.
|
// The JIT only allows the top-most entry to have a nullptr pc.
|
||||||
MOZ_ASSERT(&entry == &aRacyInfo->entries[aRacyInfo->stackSize() - 1]);
|
MOZ_ASSERT(&entry == &aRacyInfo->entries[aRacyInfo->stackSize() - 1]);
|
||||||
} else {
|
} else {
|
||||||
lineno = JS_PCToLineNumber(script, entry.pc());
|
lineno = JS_PCToLineNumber(entry.script(), entry.pc());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
lineno = entry.line();
|
lineno = entry.line();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adjust the dynamic string as necessary.
|
||||||
|
if (ProfilerFeature::HasPrivacy(aFeatures) && !isChromeJSEntry) {
|
||||||
|
dynamicString = "(private)";
|
||||||
|
} else if (strlen(dynamicString) >= ProfileBuffer::kMaxFrameKeyLength) {
|
||||||
|
dynamicString = "(too long)";
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// XXX: Bug 1010578. Don't assume a CPP entry and try to get the line for
|
// XXX: Bug 1010578. Don't assume a CPP entry and try to get the line for
|
||||||
// js entries as well.
|
// js entries as well.
|
||||||
|
@ -705,11 +723,8 @@ AddPseudoEntry(PSLockRef aLock, NotNull<RacyThreadInfo*> aRacyInfo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lineno != -1) {
|
aCollector.CollectCodeLocation(entry.label(), dynamicString, lineno,
|
||||||
aBuffer.AddEntry(ProfileBufferEntry::LineNumber(lineno));
|
Some(entry.category()));
|
||||||
}
|
|
||||||
|
|
||||||
aBuffer.AddEntry(ProfileBufferEntry::Category(int(entry.category())));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting MAX_NATIVE_FRAMES too high risks the unwinder wasting a lot of time
|
// Setting MAX_NATIVE_FRAMES too high risks the unwinder wasting a lot of time
|
||||||
|
@ -747,12 +762,17 @@ struct AutoWalkJSStack
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Merges the pseudo-stack, native stack, and JS stack, outputting the details
|
||||||
|
// to aCollector.
|
||||||
static void
|
static void
|
||||||
MergeStacksIntoProfile(PSLockRef aLock, bool aIsSynchronous,
|
MergeStacks(uint32_t aFeatures, bool aIsSynchronous,
|
||||||
const ThreadInfo& aThreadInfo, const Registers& aRegs,
|
const ThreadInfo& aThreadInfo, const Registers& aRegs,
|
||||||
const NativeStack& aNativeStack, ProfileBuffer& aBuffer)
|
const NativeStack& aNativeStack,
|
||||||
|
ProfilerStackCollector& aCollector)
|
||||||
{
|
{
|
||||||
// WARNING: this function runs within the profiler's "critical section".
|
// WARNING: this function runs within the profiler's "critical section".
|
||||||
|
// WARNING: this function might be called while the profiler is inactive, and
|
||||||
|
// cannot rely on ActivePS.
|
||||||
|
|
||||||
NotNull<RacyThreadInfo*> racyInfo = aThreadInfo.RacyInfo();
|
NotNull<RacyThreadInfo*> racyInfo = aThreadInfo.RacyInfo();
|
||||||
js::ProfileEntry* pseudoEntries = racyInfo->entries;
|
js::ProfileEntry* pseudoEntries = racyInfo->entries;
|
||||||
|
@ -767,10 +787,10 @@ MergeStacksIntoProfile(PSLockRef aLock, bool aIsSynchronous,
|
||||||
// ProfilingFrameIterator to avoid incorrectly resetting the generation of
|
// ProfilingFrameIterator to avoid incorrectly resetting the generation of
|
||||||
// sampled JIT entries inside the JS engine. See note below concerning 'J'
|
// sampled JIT entries inside the JS engine. See note below concerning 'J'
|
||||||
// entries.
|
// entries.
|
||||||
uint32_t startBufferGen;
|
uint32_t startBufferGen = UINT32_MAX;
|
||||||
startBufferGen = aIsSynchronous
|
if (!aIsSynchronous && aCollector.Generation().isSome()) {
|
||||||
? UINT32_MAX
|
startBufferGen = *aCollector.Generation();
|
||||||
: aBuffer.mGeneration;
|
}
|
||||||
uint32_t jsCount = 0;
|
uint32_t jsCount = 0;
|
||||||
JS::ProfilingFrameIterator::Frame jsFrames[MAX_JS_FRAMES];
|
JS::ProfilingFrameIterator::Frame jsFrames[MAX_JS_FRAMES];
|
||||||
|
|
||||||
|
@ -882,7 +902,7 @@ MergeStacksIntoProfile(PSLockRef aLock, bool aIsSynchronous,
|
||||||
// Pseudo-frames with the CPP_MARKER_FOR_JS kind are just annotations and
|
// Pseudo-frames with the CPP_MARKER_FOR_JS kind are just annotations and
|
||||||
// should not be recorded in the profile.
|
// should not be recorded in the profile.
|
||||||
if (pseudoEntry.kind() != js::ProfileEntry::Kind::CPP_MARKER_FOR_JS) {
|
if (pseudoEntry.kind() != js::ProfileEntry::Kind::CPP_MARKER_FOR_JS) {
|
||||||
AddPseudoEntry(aLock, racyInfo, pseudoEntry, aBuffer);
|
AddPseudoEntry(aFeatures, racyInfo, pseudoEntry, aCollector);
|
||||||
}
|
}
|
||||||
pseudoIndex++;
|
pseudoIndex++;
|
||||||
continue;
|
continue;
|
||||||
|
@ -908,13 +928,11 @@ MergeStacksIntoProfile(PSLockRef aLock, bool aIsSynchronous,
|
||||||
// with stale JIT code return addresses.
|
// with stale JIT code return addresses.
|
||||||
if (aIsSynchronous ||
|
if (aIsSynchronous ||
|
||||||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
|
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Wasm) {
|
||||||
aBuffer.AddEntry(ProfileBufferEntry::Label(""));
|
aCollector.CollectCodeLocation("", jsFrame.label, -1, Nothing());
|
||||||
aBuffer.AddDynamicStringEntry(jsFrame.label);
|
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
|
MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
|
||||||
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
|
jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
|
||||||
aBuffer.AddEntry(
|
aCollector.CollectJitReturnAddr(jsFrames[jsIndex].returnAddress);
|
||||||
ProfileBufferEntry::JitReturnAddr(jsFrames[jsIndex].returnAddress));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jsIndex--;
|
jsIndex--;
|
||||||
|
@ -926,7 +944,7 @@ MergeStacksIntoProfile(PSLockRef aLock, bool aIsSynchronous,
|
||||||
if (nativeStackAddr) {
|
if (nativeStackAddr) {
|
||||||
MOZ_ASSERT(nativeIndex >= 0);
|
MOZ_ASSERT(nativeIndex >= 0);
|
||||||
void* addr = (void*)aNativeStack.mPCs[nativeIndex];
|
void* addr = (void*)aNativeStack.mPCs[nativeIndex];
|
||||||
aBuffer.AddEntry(ProfileBufferEntry::NativeLeafAddr(addr));
|
aCollector.CollectNativeLeafAddr(addr);
|
||||||
}
|
}
|
||||||
if (nativeIndex >= 0) {
|
if (nativeIndex >= 0) {
|
||||||
nativeIndex--;
|
nativeIndex--;
|
||||||
|
@ -937,10 +955,11 @@ MergeStacksIntoProfile(PSLockRef aLock, bool aIsSynchronous,
|
||||||
//
|
//
|
||||||
// Do not do this for synchronous samples, which use their own
|
// Do not do this for synchronous samples, which use their own
|
||||||
// ProfileBuffers instead of the global one in CorePS.
|
// ProfileBuffers instead of the global one in CorePS.
|
||||||
if (!aIsSynchronous && context) {
|
if (!aIsSynchronous && context && aCollector.Generation().isSome()) {
|
||||||
MOZ_ASSERT(aBuffer.mGeneration >= startBufferGen);
|
MOZ_ASSERT(*aCollector.Generation() >= startBufferGen);
|
||||||
uint32_t lapCount = aBuffer.mGeneration - startBufferGen;
|
uint32_t lapCount = *aCollector.Generation() - startBufferGen;
|
||||||
JS::UpdateJSContextProfilerSampleBufferGen(context, aBuffer.mGeneration,
|
JS::UpdateJSContextProfilerSampleBufferGen(context,
|
||||||
|
*aCollector.Generation(),
|
||||||
lapCount);
|
lapCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -965,6 +984,8 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
|
||||||
const Registers& aRegs, NativeStack& aNativeStack)
|
const Registers& aRegs, NativeStack& aNativeStack)
|
||||||
{
|
{
|
||||||
// WARNING: this function runs within the profiler's "critical section".
|
// WARNING: this function runs within the profiler's "critical section".
|
||||||
|
// WARNING: this function might be called while the profiler is inactive, and
|
||||||
|
// cannot rely on ActivePS.
|
||||||
|
|
||||||
// Start with the current function. We use 0 as the frame number here because
|
// Start with the current function. We use 0 as the frame number here because
|
||||||
// the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N.
|
// the FramePointerStackWalk() and MozStackWalk() calls below will use 1..N.
|
||||||
|
@ -998,6 +1019,8 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
|
||||||
const Registers& aRegs, NativeStack& aNativeStack)
|
const Registers& aRegs, NativeStack& aNativeStack)
|
||||||
{
|
{
|
||||||
// WARNING: this function runs within the profiler's "critical section".
|
// WARNING: this function runs within the profiler's "critical section".
|
||||||
|
// WARNING: this function might be called while the profiler is inactive, and
|
||||||
|
// cannot rely on ActivePS.
|
||||||
|
|
||||||
const mcontext_t* mcontext = &aRegs.mContext->uc_mcontext;
|
const mcontext_t* mcontext = &aRegs.mContext->uc_mcontext;
|
||||||
mcontext_t savedContext;
|
mcontext_t savedContext;
|
||||||
|
@ -1077,6 +1100,8 @@ DoNativeBacktrace(PSLockRef aLock, const ThreadInfo& aThreadInfo,
|
||||||
const Registers& aRegs, NativeStack& aNativeStack)
|
const Registers& aRegs, NativeStack& aNativeStack)
|
||||||
{
|
{
|
||||||
// WARNING: this function runs within the profiler's "critical section".
|
// WARNING: this function runs within the profiler's "critical section".
|
||||||
|
// WARNING: this function might be called while the profiler is inactive, and
|
||||||
|
// cannot rely on ActivePS.
|
||||||
|
|
||||||
const mcontext_t* mc = &aRegs.mContext->uc_mcontext;
|
const mcontext_t* mc = &aRegs.mContext->uc_mcontext;
|
||||||
|
|
||||||
|
@ -1222,13 +1247,13 @@ DoSharedSample(PSLockRef aLock, bool aIsSynchronous,
|
||||||
if (ActivePS::FeatureStackWalk(aLock)) {
|
if (ActivePS::FeatureStackWalk(aLock)) {
|
||||||
DoNativeBacktrace(aLock, aThreadInfo, aRegs, nativeStack);
|
DoNativeBacktrace(aLock, aThreadInfo, aRegs, nativeStack);
|
||||||
|
|
||||||
MergeStacksIntoProfile(aLock, aIsSynchronous, aThreadInfo, aRegs,
|
MergeStacks(ActivePS::Features(aLock), aIsSynchronous, aThreadInfo, aRegs,
|
||||||
nativeStack, aBuffer);
|
nativeStack, aBuffer);
|
||||||
} else
|
} else
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
MergeStacksIntoProfile(aLock, aIsSynchronous, aThreadInfo, aRegs,
|
MergeStacks(ActivePS::Features(aLock), aIsSynchronous, aThreadInfo, aRegs,
|
||||||
nativeStack, aBuffer);
|
nativeStack, aBuffer);
|
||||||
|
|
||||||
if (ActivePS::FeatureLeaf(aLock)) {
|
if (ActivePS::FeatureLeaf(aLock)) {
|
||||||
aBuffer.AddEntry(ProfileBufferEntry::NativeLeafAddr((void*)aRegs.mPC));
|
aBuffer.AddEntry(ProfileBufferEntry::NativeLeafAddr((void*)aRegs.mPC));
|
||||||
|
@ -3046,5 +3071,62 @@ profiler_suspend_and_sample_thread(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NOTE: aCollector's methods will be called while the target thread is paused.
|
||||||
|
// Doing things in those methods like allocating -- which may try to claim
|
||||||
|
// locks -- is a surefire way to deadlock.
|
||||||
|
void
|
||||||
|
profiler_suspend_and_sample_thread(int aThreadId,
|
||||||
|
uint32_t aFeatures,
|
||||||
|
ProfilerStackCollector& aCollector,
|
||||||
|
bool aSampleNative /* = true */)
|
||||||
|
{
|
||||||
|
// Lock the profiler mutex
|
||||||
|
PSAutoLock lock(gPSMutex);
|
||||||
|
|
||||||
|
const CorePS::ThreadVector& liveThreads = CorePS::LiveThreads(lock);
|
||||||
|
for (uint32_t i = 0; i < liveThreads.size(); i++) {
|
||||||
|
ThreadInfo* info = liveThreads.at(i);
|
||||||
|
|
||||||
|
if (info->ThreadId() == aThreadId) {
|
||||||
|
if (info->IsMainThread()) {
|
||||||
|
aCollector.SetIsMainThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate the space for the native stack
|
||||||
|
NativeStack nativeStack;
|
||||||
|
|
||||||
|
// Suspend, sample, and then resume the target thread.
|
||||||
|
Sampler sampler(lock);
|
||||||
|
sampler.SuspendAndSampleAndResumeThread(lock, *info,
|
||||||
|
[&](const Registers& aRegs) {
|
||||||
|
// The target thread is now suspended. Collect a native backtrace, and
|
||||||
|
// call the callback.
|
||||||
|
bool isSynchronous = false;
|
||||||
|
#if defined(HAVE_NATIVE_UNWIND)
|
||||||
|
if (aSampleNative) {
|
||||||
|
DoNativeBacktrace(lock, *info, aRegs, nativeStack);
|
||||||
|
|
||||||
|
MergeStacks(aFeatures, isSynchronous, *info, aRegs, nativeStack,
|
||||||
|
aCollector);
|
||||||
|
} else
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
MergeStacks(aFeatures, isSynchronous, *info, aRegs, nativeStack,
|
||||||
|
aCollector);
|
||||||
|
|
||||||
|
if (ProfilerFeature::HasLeaf(aFeatures)) {
|
||||||
|
aCollector.CollectNativeLeafAddr((void*)aRegs.mPC);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// NOTE: Make sure to disable the sampler before it is destroyed, in case
|
||||||
|
// the profiler is running at the same time.
|
||||||
|
sampler.Disable(lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// END externally visible functions
|
// END externally visible functions
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -9,9 +9,13 @@
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
#include "mozilla/Mutex.h"
|
#include "mozilla/Mutex.h"
|
||||||
|
#include "mozilla/RefPtr.h"
|
||||||
|
#include "mozilla/SystemGroup.h"
|
||||||
|
|
||||||
using mozilla::Mutex;
|
using mozilla::Mutex;
|
||||||
using mozilla::MutexAutoLock;
|
using mozilla::MutexAutoLock;
|
||||||
|
using mozilla::SystemGroup;
|
||||||
|
using mozilla::TaskCategory;
|
||||||
using mozilla::TimeStamp;
|
using mozilla::TimeStamp;
|
||||||
|
|
||||||
class CheckResponsivenessTask : public mozilla::Runnable,
|
class CheckResponsivenessTask : public mozilla::Runnable,
|
||||||
|
@ -45,7 +49,9 @@ public:
|
||||||
|
|
||||||
// Dispatching can fail during early startup, particularly when
|
// Dispatching can fail during early startup, particularly when
|
||||||
// MOZ_PROFILER_STARTUP is used.
|
// MOZ_PROFILER_STARTUP is used.
|
||||||
nsresult rv = NS_DispatchToMainThread(this);
|
nsresult rv = SystemGroup::Dispatch("CheckResponsivenessTask",
|
||||||
|
TaskCategory::Other,
|
||||||
|
do_AddRef(this));
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
mHasEverBeenSuccessfullyDispatched = true;
|
mHasEverBeenSuccessfullyDispatched = true;
|
||||||
}
|
}
|
||||||
|
@ -66,6 +72,7 @@ public:
|
||||||
mLastTracerTime = TimeStamp::Now();
|
mLastTracerTime = TimeStamp::Now();
|
||||||
if (!mTimer) {
|
if (!mTimer) {
|
||||||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||||
|
mTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::Other));
|
||||||
}
|
}
|
||||||
mTimer->InitWithCallback(this, 16, nsITimer::TYPE_ONE_SHOT);
|
mTimer->InitWithCallback(this, 16, nsITimer::TYPE_ONE_SHOT);
|
||||||
|
|
||||||
|
@ -74,7 +81,9 @@ public:
|
||||||
|
|
||||||
NS_IMETHOD Notify(nsITimer* aTimer) final
|
NS_IMETHOD Notify(nsITimer* aTimer) final
|
||||||
{
|
{
|
||||||
NS_DispatchToMainThread(this);
|
SystemGroup::Dispatch("CheckResponsivenessTask",
|
||||||
|
TaskCategory::Other,
|
||||||
|
do_AddRef(this));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -81,6 +81,7 @@ if CONFIG['MOZ_GECKO_PROFILER']:
|
||||||
]
|
]
|
||||||
|
|
||||||
LOCAL_INCLUDES += [
|
LOCAL_INCLUDES += [
|
||||||
|
'/caps',
|
||||||
'/docshell/base',
|
'/docshell/base',
|
||||||
'/ipc/chromium/src',
|
'/ipc/chromium/src',
|
||||||
'/mozglue/linker',
|
'/mozglue/linker',
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "mozilla/Assertions.h"
|
#include "mozilla/Assertions.h"
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
#include "mozilla/GuardObjects.h"
|
#include "mozilla/GuardObjects.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
#include "mozilla/Sprintf.h"
|
#include "mozilla/Sprintf.h"
|
||||||
#include "mozilla/ThreadLocal.h"
|
#include "mozilla/ThreadLocal.h"
|
||||||
#include "mozilla/UniquePtr.h"
|
#include "mozilla/UniquePtr.h"
|
||||||
|
@ -267,11 +268,52 @@ typedef void ProfilerStackCallback(void** aPCs, size_t aCount, bool aIsMainThrea
|
||||||
// WARNING: The target thread is suspended during the callback. Do not try to
|
// WARNING: The target thread is suspended during the callback. Do not try to
|
||||||
// allocate or acquire any locks, or you could deadlock. The target thread will
|
// allocate or acquire any locks, or you could deadlock. The target thread will
|
||||||
// have resumed by the time this function returns.
|
// have resumed by the time this function returns.
|
||||||
|
//
|
||||||
|
// XXX: this function is in the process of being replaced with the other profiler_suspend_and_sample_thread() function.
|
||||||
PROFILER_FUNC_VOID(
|
PROFILER_FUNC_VOID(
|
||||||
profiler_suspend_and_sample_thread(int aThreadId,
|
profiler_suspend_and_sample_thread(int aThreadId,
|
||||||
const std::function<ProfilerStackCallback>& aCallback,
|
const std::function<ProfilerStackCallback>& aCallback,
|
||||||
bool aSampleNative = true))
|
bool aSampleNative = true))
|
||||||
|
|
||||||
|
// An object of this class is passed to profiler_suspend_and_sample_thread().
|
||||||
|
// For each stack frame, one of the Collect methods will be called.
|
||||||
|
class ProfilerStackCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// Some collectors need to worry about possibly overwriting previous
|
||||||
|
// generations of data. If that's not an issue, this can return Nothing,
|
||||||
|
// which is the default behaviour.
|
||||||
|
virtual mozilla::Maybe<uint32_t> Generation() { return mozilla::Nothing(); }
|
||||||
|
|
||||||
|
// This method will be called once if the thread being suspended is the main
|
||||||
|
// thread. Default behaviour is to do nothing.
|
||||||
|
virtual void SetIsMainThread() {}
|
||||||
|
|
||||||
|
// WARNING: The target thread is suspended when the Collect methods are
|
||||||
|
// called. Do not try to allocate or acquire any locks, or you could
|
||||||
|
// deadlock. The target thread will have resumed by the time this function
|
||||||
|
// returns.
|
||||||
|
|
||||||
|
virtual void CollectNativeLeafAddr(void* aAddr) = 0;
|
||||||
|
|
||||||
|
virtual void CollectJitReturnAddr(void* aAddr) = 0;
|
||||||
|
|
||||||
|
// aLabel is static and never null. aStr may be null. aLineNumber may be -1.
|
||||||
|
virtual void CollectCodeLocation(
|
||||||
|
const char* aLabel, const char* aStr, int aLineNumber,
|
||||||
|
const mozilla::Maybe<js::ProfileEntry::Category>& aCategory) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// This method suspends the thread identified by aThreadId, samples its
|
||||||
|
// pseudo-stack, JS stack, and (optionally) native stack, passing the collected
|
||||||
|
// frames into aCollector. aFeatures dictates which compiler features are used.
|
||||||
|
// |Privacy| and |Leaf| are the only relevant ones.
|
||||||
|
PROFILER_FUNC_VOID(
|
||||||
|
profiler_suspend_and_sample_thread(int aThreadId,
|
||||||
|
uint32_t aFeatures,
|
||||||
|
ProfilerStackCollector& aCollector,
|
||||||
|
bool aSampleNative = true))
|
||||||
|
|
||||||
struct ProfilerBacktraceDestructor
|
struct ProfilerBacktraceDestructor
|
||||||
{
|
{
|
||||||
#ifdef MOZ_GECKO_PROFILER
|
#ifdef MOZ_GECKO_PROFILER
|
||||||
|
|
|
@ -664,3 +664,70 @@ TEST(GeckoProfiler, Bug1355807)
|
||||||
|
|
||||||
profiler_stop();
|
profiler_stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GTestStackCollector final : public ProfilerStackCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
GTestStackCollector()
|
||||||
|
: mSetIsMainThread(0)
|
||||||
|
, mFrames(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual void SetIsMainThread() { mSetIsMainThread++; }
|
||||||
|
|
||||||
|
virtual void CollectNativeLeafAddr(void* aAddr) { mFrames++; }
|
||||||
|
virtual void CollectJitReturnAddr(void* aAddr) { mFrames++; }
|
||||||
|
virtual void CollectCodeLocation(
|
||||||
|
const char* aLabel, const char* aStr, int aLineNumber,
|
||||||
|
const mozilla::Maybe<js::ProfileEntry::Category>& aCategory) { mFrames++; }
|
||||||
|
|
||||||
|
int mSetIsMainThread;
|
||||||
|
int mFrames;
|
||||||
|
};
|
||||||
|
|
||||||
|
void DoSuspendAndSample(int aTid, nsIThread* aThread)
|
||||||
|
{
|
||||||
|
aThread->Dispatch(
|
||||||
|
NS_NewRunnableFunction(
|
||||||
|
"GeckoProfiler_SuspendAndSample_Test::TestBody",
|
||||||
|
[&]() {
|
||||||
|
uint32_t features = ProfilerFeature::Leaf;
|
||||||
|
GTestStackCollector collector;
|
||||||
|
profiler_suspend_and_sample_thread(aTid, features, collector,
|
||||||
|
/* sampleNative = */ true);
|
||||||
|
|
||||||
|
ASSERT_TRUE(collector.mSetIsMainThread == 1);
|
||||||
|
ASSERT_TRUE(collector.mFrames > 5); // approximate; must be > 0
|
||||||
|
}),
|
||||||
|
NS_DISPATCH_SYNC);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(GeckoProfiler, SuspendAndSample)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIThread> thread;
|
||||||
|
nsresult rv = NS_NewNamedThread("GeckoProfGTest", getter_AddRefs(thread));
|
||||||
|
ASSERT_TRUE(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
int tid = Thread::GetCurrentId();
|
||||||
|
|
||||||
|
ASSERT_TRUE(!profiler_is_active());
|
||||||
|
|
||||||
|
// Suspend and sample while the profiler is inactive.
|
||||||
|
DoSuspendAndSample(tid, thread);
|
||||||
|
|
||||||
|
uint32_t features = ProfilerFeature::JS | ProfilerFeature::Threads;
|
||||||
|
const char* filters[] = { "GeckoMain", "Compositor" };
|
||||||
|
|
||||||
|
profiler_start(PROFILER_DEFAULT_ENTRIES, PROFILER_DEFAULT_INTERVAL,
|
||||||
|
features, filters, MOZ_ARRAY_LENGTH(filters));
|
||||||
|
|
||||||
|
ASSERT_TRUE(profiler_is_active());
|
||||||
|
|
||||||
|
// Suspend and sample while the profiler is active.
|
||||||
|
DoSuspendAndSample(tid, thread);
|
||||||
|
|
||||||
|
profiler_stop();
|
||||||
|
|
||||||
|
ASSERT_TRUE(!profiler_is_active());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче