This commit is contained in:
Richard Newman 2012-02-18 23:36:35 -08:00
Родитель 64c9c39aca a4ac4954c0
Коммит 4bc82cb040
102 изменённых файлов: 3318 добавлений и 1224 удалений

Просмотреть файл

@ -1,5 +1,5 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1328822681000"> <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1329176667000">
<emItems> <emItems>
<emItem blockID="i58" id="webmaster@buzzzzvideos.info"> <emItem blockID="i58" id="webmaster@buzzzzvideos.info">
<versionRange minVersion="0" maxVersion="*"> <versionRange minVersion="0" maxVersion="*">
@ -27,8 +27,12 @@
</targetApplication> </targetApplication>
</versionRange> </versionRange>
</emItem> </emItem>
<emItem blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}"> <emItem blockID="i65" id="activity@facebook.com">
<versionRange minVersion="1.1b1" maxVersion="1.1b1"> <versionRange minVersion="0" maxVersion="*">
</versionRange>
</emItem>
<emItem blockID="i66" id="youtubeer@youtuber.com">
<versionRange minVersion="0" maxVersion="*">
</versionRange> </versionRange>
</emItem> </emItem>
<emItem blockID="i54" id="applebeegifts@mozilla.doslash.org"> <emItem blockID="i54" id="applebeegifts@mozilla.doslash.org">
@ -122,8 +126,11 @@
<versionRange minVersion="0" maxVersion="*"> <versionRange minVersion="0" maxVersion="*">
</versionRange> </versionRange>
</emItem> </emItem>
<emItem blockID="i56" id="flash@adobe.com"> <emItem blockID="i23" id="firefox@bandoo.com">
<versionRange minVersion="0" maxVersion="*"> <versionRange minVersion="5.0" maxVersion="5.0" severity="1">
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
<versionRange minVersion="3.7a1pre" maxVersion="*" />
</targetApplication>
</versionRange> </versionRange>
</emItem> </emItem>
<emItem blockID="i55" id="youtube@youtube7.com"> <emItem blockID="i55" id="youtube@youtube7.com">
@ -178,11 +185,8 @@
<versionRange minVersion="2.2" maxVersion="2.2"> <versionRange minVersion="2.2" maxVersion="2.2">
</versionRange> </versionRange>
</emItem> </emItem>
<emItem blockID="i23" id="firefox@bandoo.com"> <emItem blockID="i56" id="flash@adobe.com">
<versionRange minVersion="5.0" maxVersion="5.0" severity="1"> <versionRange minVersion="0" maxVersion="*">
<targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
<versionRange minVersion="3.7a1pre" maxVersion="*" />
</targetApplication>
</versionRange> </versionRange>
</emItem> </emItem>
<emItem blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}"> <emItem blockID="i45" id="{22119944-ED35-4ab1-910B-E619EA06A115}">
@ -192,6 +196,10 @@
</targetApplication> </targetApplication>
</versionRange> </versionRange>
</emItem> </emItem>
<emItem blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
<versionRange minVersion="1.1b1" maxVersion="1.1b1">
</versionRange>
</emItem>
<emItem blockID="i3" id="langpack-vi-VN@firefox.mozilla.org"> <emItem blockID="i3" id="langpack-vi-VN@firefox.mozilla.org">
<versionRange minVersion="2.0" maxVersion="2.0"> <versionRange minVersion="2.0" maxVersion="2.0">
</versionRange> </versionRange>

Просмотреть файл

@ -3551,12 +3551,14 @@
// it triggers will correctly update our URL bar. // it triggers will correctly update our URL bar.
this.tabbrowser.selectedTab = newTab; this.tabbrowser.selectedTab = newTab;
} else { } else {
let url = browserDragAndDrop.drop(event, { }); // Pass true to disallow dropping javascript: or data: urls
let url;
try {
url = browserDragAndDrop.drop(event, { }, true);
} catch (ex) {}
// valid urls don't contain spaces ' '; if we have a space it isn't a valid url. // valid urls don't contain spaces ' '; if we have a space it isn't a valid url.
// Also disallow dropping javascript: or data: urls--bail out if (!url || url.indexOf(" ") != -1)
if (!url || !url.length || url.indexOf(" ", 0) != -1 ||
/^\s*(javascript|data):/.test(url))
return; return;
let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground"); let bgLoad = Services.prefs.getBoolPref("browser.tabs.loadInBackground");

Просмотреть файл

@ -268,6 +268,7 @@ _BROWSER_FILES = \
browser_aboutSyncProgress.js \ browser_aboutSyncProgress.js \
browser_middleMouse_inherit.js \ browser_middleMouse_inherit.js \
redirect_bug623155.sjs \ redirect_bug623155.sjs \
browser_tabDrop.js \
$(NULL) $(NULL)
ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT)) ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))

Просмотреть файл

@ -0,0 +1,71 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
waitForExplicitFinish();
let newTab = gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
registerCleanupFunction(function () {
gBrowser.removeTab(newTab);
});
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let chromeUtils = {};
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", chromeUtils);
let tabContainer = gBrowser.tabContainer;
var receivedDropCount = 0;
function dropListener() {
receivedDropCount++;
if (receivedDropCount == triggeredDropCount) {
is(openedTabs, validDropCount, "correct number of tabs were opened");
executeSoon(finish);
}
}
tabContainer.addEventListener("drop", dropListener, false);
registerCleanupFunction(function () {
tabContainer.removeEventListener("drop", dropListener, false);
});
var openedTabs = 0;
function tabOpenListener(e) {
openedTabs++;
let tab = e.target;
executeSoon(function () {
gBrowser.removeTab(tab);
});
}
tabContainer.addEventListener("TabOpen", tabOpenListener, false);
registerCleanupFunction(function () {
tabContainer.removeEventListener("TabOpen", tabOpenListener, false);
});
var triggeredDropCount = 0;
var validDropCount = 0;
function drop(text, valid) {
triggeredDropCount++;
if (valid)
validDropCount++;
executeSoon(function () {
// A drop type of "link" onto an existing tab would normally trigger a
// load in that same tab, but tabbrowser code in _getDragTargetTab treats
// drops on the outer edges of a tab differently (loading a new tab
// instead). The events created by synthesizeDrop have all of their
// coordinates set to 0 (screenX/screenY), so they're treated as drops
// on the outer edge of the tab, thus they open new tabs.
chromeUtils.synthesizeDrop(newTab, newTab, [[{type: "text/plain", data: text}]], "link", window, EventUtils);
});
}
// Begin and end with valid drops to make sure we wait for all drops before
// ending the test
drop("mochi.test/first", true);
drop("javascript:'bad'");
drop("jAvascript:'bad'");
drop("space bad");
drop("mochi.test/second", true);
drop("data:text/html,bad");
drop("mochi.test/third", true);
}

Просмотреть файл

@ -109,7 +109,6 @@ _TEST_FILES = \
test_bug495300.html \ test_bug495300.html \
test_bug686942.html \ test_bug686942.html \
test_can_play_type.html \ test_can_play_type.html \
test_closing_connections.html \
test_constants.html \ test_constants.html \
test_controls.html \ test_controls.html \
test_currentTime.html \ test_currentTime.html \
@ -176,6 +175,8 @@ endif
# test_mixed_principals.html # test_mixed_principals.html
# Disabled since we don't play Wave files standalone, for now # Disabled since we don't play Wave files standalone, for now
# test_audioDocumentTitle.html # test_audioDocumentTitle.html
# Bug 634564:
# test_closing_connections.html \
# sample files # sample files
_TEST_FILES += \ _TEST_FILES += \

Просмотреть файл

@ -4119,9 +4119,7 @@ nsDocShell::DisplayLoadError(nsresult aError, nsIURI *aURI,
// Display the error as a page or an alert prompt // Display the error as a page or an alert prompt
NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE); NS_ENSURE_FALSE(messageStr.IsEmpty(), NS_ERROR_FAILURE);
// Note: For now, display an alert instead of an error page if we have no if (mUseErrorPages) {
// URI object. Missing URI objects are handled badly by session history.
if (mUseErrorPages && aURI) {
// Display an error page // Display an error page
LoadErrorPage(aURI, aURL, errorPage.get(), error.get(), LoadErrorPage(aURI, aURL, errorPage.get(), error.get(),
messageStr.get(), cssClass.get(), aFailedChannel); messageStr.get(), cssClass.get(), aFailedChannel);
@ -4190,6 +4188,10 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
} }
else if (aURL) else if (aURL)
{ {
// We need a URI object to store a session history entry, so make up a URI
nsresult rv = NS_NewURI(getter_AddRefs(mFailedURI), "about:blank");
NS_ENSURE_SUCCESS(rv, rv);
CopyUTF16toUTF8(aURL, url); CopyUTF16toUTF8(aURL, url);
} }
else else

Просмотреть файл

@ -2091,6 +2091,13 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName,
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsDOMWindowUtils::IsIncrementalGCEnabled(JSContext* cx, bool* aResult)
{
*aResult = js::IsIncrementalGCEnabled(JS_GetRuntime(cx));
return NS_OK;
}
NS_IMETHODIMP NS_IMETHODIMP
nsDOMWindowUtils::StartPCCountProfiling(JSContext* cx) nsDOMWindowUtils::StartPCCountProfiling(JSContext* cx)
{ {

Просмотреть файл

@ -135,6 +135,9 @@ static PRLogModuleInfo* gJSDiagnostics;
// doing the first GC. // doing the first GC.
#define NS_FIRST_GC_DELAY 10000 // ms #define NS_FIRST_GC_DELAY 10000 // ms
// Maximum amount of time that should elapse between incremental GC slices
#define NS_INTERSLICE_GC_DELAY 100 // ms
// The amount of time we wait between a request to CC (after GC ran) // The amount of time we wait between a request to CC (after GC ran)
// and doing the actual CC. // and doing the actual CC.
#define NS_CC_DELAY 5000 // ms #define NS_CC_DELAY 5000 // ms
@ -154,6 +157,9 @@ static nsITimer *sCCTimer;
static PRTime sLastCCEndTime; static PRTime sLastCCEndTime;
static bool sGCHasRun; static bool sGCHasRun;
static bool sCCLockedOut;
static js::GCSliceCallback sPrevGCSliceCallback;
// The number of currently pending document loads. This count isn't // The number of currently pending document loads. This count isn't
// guaranteed to always reflect reality and can't easily as we don't // guaranteed to always reflect reality and can't easily as we don't
@ -3274,6 +3280,11 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
return; return;
} }
if (sCCLockedOut) {
// We're in the middle of an incremental GC; finish it first
nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal);
}
SAMPLE_LABEL("GC", "CycleCollectNow"); SAMPLE_LABEL("GC", "CycleCollectNow");
NS_TIME_FUNCTION_MIN(1.0); NS_TIME_FUNCTION_MIN(1.0);
@ -3357,7 +3368,7 @@ GCTimerFired(nsITimer *aTimer, void *aClosure)
NS_RELEASE(sGCTimer); NS_RELEASE(sGCTimer);
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure); uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason), nsGCNormal); nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason), nsGCIncremental);
} }
void void
@ -3375,6 +3386,9 @@ CCTimerFired(nsITimer *aTimer, void *aClosure)
if (sDidShutdown) { if (sDidShutdown) {
return; return;
} }
if (sCCLockedOut) {
return;
}
++sCCTimerFireCount; ++sCCTimerFireCount;
if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) { if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) {
PRUint32 suspected = nsCycleCollector_suspectedCount(); PRUint32 suspected = nsCycleCollector_suspectedCount();
@ -3443,7 +3457,7 @@ nsJSContext::LoadEnd()
// static // static
void void
nsJSContext::PokeGC(js::gcreason::Reason aReason) nsJSContext::PokeGC(js::gcreason::Reason aReason, int aDelay)
{ {
if (sGCTimer) { if (sGCTimer) {
// There's already a timer for GC'ing, just return // There's already a timer for GC'ing, just return
@ -3460,9 +3474,11 @@ nsJSContext::PokeGC(js::gcreason::Reason aReason)
static bool first = true; static bool first = true;
sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason), sGCTimer->InitWithFuncCallback(GCTimerFired, reinterpret_cast<void *>(aReason),
first aDelay
? aDelay
: (first
? NS_FIRST_GC_DELAY ? NS_FIRST_GC_DELAY
: NS_GC_DELAY, : NS_GC_DELAY),
nsITimer::TYPE_ONE_SHOT); nsITimer::TYPE_ONE_SHOT);
first = false; first = false;
@ -3549,11 +3565,11 @@ nsJSContext::GC(js::gcreason::Reason aReason)
} }
static void static void
DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescription &aDesc)
{ {
NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread"); NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread");
if (sPostGCEventsToConsole) { if (aDesc.logMessage && sPostGCEventsToConsole) {
PRTime now = PR_Now(); PRTime now = PR_Now();
PRTime delta = 0; PRTime delta = 0;
if (sFirstCollectionTime) { if (sFirstCollectionTime) {
@ -3565,13 +3581,30 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) %s"); NS_NAMED_LITERAL_STRING(kFmt, "GC(T+%.1f) %s");
nsString msg; nsString msg;
msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), msg.Adopt(nsTextFormatter::smprintf(kFmt.get(),
double(delta) / PR_USEC_PER_SEC, status)); double(delta) / PR_USEC_PER_SEC,
aDesc.logMessage));
nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID); nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
if (cs) { if (cs) {
cs->LogStringMessage(msg.get()); cs->LogStringMessage(msg.get());
} }
} }
// Prevent cycle collections during incremental GC.
if (aProgress == js::GC_CYCLE_BEGIN) {
sCCLockedOut = true;
} else if (aProgress == js::GC_CYCLE_END) {
sCCLockedOut = false;
}
// The GC has more work to do, so schedule another GC slice.
if (aProgress == js::GC_SLICE_END) {
nsJSContext::KillGCTimer();
nsJSContext::KillCCTimer();
nsJSContext::PokeGC(js::gcreason::INTER_SLICE_GC, NS_INTERSLICE_GC_DELAY);
}
if (aProgress == js::GC_CYCLE_END) {
sCCollectedWaitingForGC = 0; sCCollectedWaitingForGC = 0;
sCleanupSinceLastGC = false; sCleanupSinceLastGC = false;
@ -3584,7 +3617,7 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
// result of last-ditch or MaybeGC. In both cases its // result of last-ditch or MaybeGC. In both cases its
// probably a time of heavy activity and we want to delay // probably a time of heavy activity and we want to delay
// the full GC, but we do want it to happen eventually. // the full GC, but we do want it to happen eventually.
if (comp) { if (aDesc.isCompartment) {
nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT); nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT);
// We poked the GC, so we can kill any pending CC here. // We poked the GC, so we can kill any pending CC here.
@ -3592,20 +3625,24 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status)
} }
} else { } else {
// If this was a full GC, poke the CC to run soon. // If this was a full GC, poke the CC to run soon.
if (!comp) { if (!aDesc.isCompartment) {
sGCHasRun = true; sGCHasRun = true;
nsJSContext::MaybePokeCC(); nsJSContext::MaybePokeCC();
} }
} }
// If we didn't end up scheduling a GC, make sure that we release GC buffers // If we didn't end up scheduling a GC, make sure that we release GC buffers
// soon after canceling previous shrinking attempt // soon after canceling previous shrinking attempt.
nsJSContext::KillShrinkGCBuffersTimer(); nsJSContext::KillShrinkGCBuffersTimer();
if (!sGCTimer) { if (!sGCTimer) {
nsJSContext::PokeShrinkGCBuffers(); nsJSContext::PokeShrinkGCBuffers();
} }
} }
if (sPrevGCSliceCallback)
(*sPrevGCSliceCallback)(aRt, aProgress, aDesc);
}
// Script object mananagement - note duplicate implementation // Script object mananagement - note duplicate implementation
// in nsJSRuntime below... // in nsJSRuntime below...
nsresult nsresult
@ -3697,6 +3734,7 @@ nsJSRuntime::Startup()
// initialize all our statics, so that we can restart XPCOM // initialize all our statics, so that we can restart XPCOM
sGCTimer = sCCTimer = nsnull; sGCTimer = sCCTimer = nsnull;
sGCHasRun = false; sGCHasRun = false;
sCCLockedOut = false;
sLastCCEndTime = 0; sLastCCEndTime = 0;
sPendingLoadCount = 0; sPendingLoadCount = 0;
sLoadingInProgress = false; sLoadingInProgress = false;
@ -3768,10 +3806,27 @@ SetMemoryMaxPrefChangedCallback(const char* aPrefName, void* aClosure)
static int static int
SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure) SetMemoryGCModePrefChangedCallback(const char* aPrefName, void* aClosure)
{ {
bool enableCompartmentGC = Preferences::GetBool(aPrefName); PRBool enableCompartmentGC = Preferences::GetBool("javascript.options.mem.gc_per_compartment");
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, enableCompartmentGC PRBool enableIncrementalGC = Preferences::GetBool("javascript.options.mem.gc_incremental");
? JSGC_MODE_COMPARTMENT JSGCMode mode;
: JSGC_MODE_GLOBAL); if (enableIncrementalGC) {
mode = JSGC_MODE_INCREMENTAL;
} else if (enableCompartmentGC) {
mode = JSGC_MODE_COMPARTMENT;
} else {
mode = JSGC_MODE_GLOBAL;
}
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MODE, mode);
return 0;
}
static int
SetMemoryGCSliceTimePrefChangedCallback(const char* aPrefName, void* aClosure)
{
PRInt32 pref = Preferences::GetInt(aPrefName, -1);
// handle overflow and negative pref values
if (pref > 0 && pref < 100000)
JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_SLICE_TIME_BUDGET, pref);
return 0; return 0;
} }
@ -3858,7 +3913,7 @@ nsJSRuntime::Init()
// Let's make sure that our main thread is the same as the xpcom main thread. // Let's make sure that our main thread is the same as the xpcom main thread.
NS_ASSERTION(NS_IsMainThread(), "bad"); NS_ASSERTION(NS_IsMainThread(), "bad");
::JS_SetGCFinishedCallback(sRuntime, DOMGCFinishedCallback); sPrevGCSliceCallback = js::SetGCSliceCallback(sRuntime, DOMGCSliceCallback);
JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime); JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime);
NS_ASSERTION(callbacks, "SecMan should have set security callbacks!"); NS_ASSERTION(callbacks, "SecMan should have set security callbacks!");
@ -3903,6 +3958,16 @@ nsJSRuntime::Init()
SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_per_compartment", SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_per_compartment",
nsnull); nsnull);
Preferences::RegisterCallback(SetMemoryGCModePrefChangedCallback,
"javascript.options.mem.gc_incremental");
SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_incremental",
nsnull);
Preferences::RegisterCallback(SetMemoryGCSliceTimePrefChangedCallback,
"javascript.options.mem.gc_incremental_slice_ms");
SetMemoryGCSliceTimePrefChangedCallback("javascript.options.mem.gc_incremental_slice_ms",
nsnull);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs) if (!obs)
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;

Просмотреть файл

@ -188,7 +188,7 @@ public:
static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull, static void CycleCollectNow(nsICycleCollectorListener *aListener = nsnull,
PRInt32 aExtraForgetSkippableCalls = 0); PRInt32 aExtraForgetSkippableCalls = 0);
static void PokeGC(js::gcreason::Reason aReason); static void PokeGC(js::gcreason::Reason aReason, int aDelay = 0);
static void KillGCTimer(); static void KillGCTimer();
static void PokeShrinkGCBuffers(); static void PokeShrinkGCBuffers();

Просмотреть файл

@ -70,7 +70,7 @@ interface nsIDOMFile;
interface nsIFile; interface nsIFile;
interface nsIDOMTouch; interface nsIDOMTouch;
[scriptable, uuid(ab6e9c71-8aa1-40bb-8bf9-65e16429055f)] [scriptable, uuid(73b48170-55d5-11e1-b86c-0800200c9a66)]
interface nsIDOMWindowUtils : nsISupports { interface nsIDOMWindowUtils : nsISupports {
/** /**
@ -992,6 +992,12 @@ interface nsIDOMWindowUtils : nsISupports {
[optional] out long aDBRefCnt, [optional] out long aDBRefCnt,
[optional] out long aSliceRefCnt); [optional] out long aSliceRefCnt);
/**
* Return whether incremental GC has been disabled due to a binary add-on.
*/
[implicit_jscontext]
boolean isIncrementalGCEnabled();
/** /**
* Begin opcode-level profiling of all JavaScript execution in the window's * Begin opcode-level profiling of all JavaScript execution in the window's
* runtime. * runtime.

Просмотреть файл

@ -179,7 +179,7 @@ CreateNPObjectMember(NPP npp, JSContext *cx, JSObject *obj, NPObject *npobj,
static JSClass sNPObjectJSWrapperClass = static JSClass sNPObjectJSWrapperClass =
{ {
NPRUNTIME_JSCLASS_NAME, NPRUNTIME_JSCLASS_NAME,
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE, JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE | JSCLASS_NEW_ENUMERATE,
NPObjWrapper_AddProperty, NPObjWrapper_DelProperty, NPObjWrapper_AddProperty, NPObjWrapper_DelProperty,
NPObjWrapper_GetProperty, NPObjWrapper_SetProperty, NPObjWrapper_GetProperty, NPObjWrapper_SetProperty,
(JSEnumerateOp)NPObjWrapper_newEnumerate, (JSEnumerateOp)NPObjWrapper_newEnumerate,

Просмотреть файл

@ -3683,10 +3683,13 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame)
container->SetCurrentImage(nsnull); container->SetCurrentImage(nsnull);
} }
// If we had an old frame and we're not going to have a new one then #if defined(XP_MACOSX) && !defined(NP_NO_QUICKDRAW)
// we should unregister for some things.
if (!aFrame) { if (!aFrame) {
// Unregister scroll position listeners // At this point we had a frame but it is going away and we're not getting a new one.
// Unregister for a scroll position listening, which is only required for Carbon
// event model plugins on Mac OS X. It's OK to unregister when we didn't register,
// so don't be strict about unregistering. Better to unregister when we didn't have to
// than to not unregister when we should.
for (nsIFrame* f = mObjectFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { for (nsIFrame* f = mObjectFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
nsIScrollableFrame* sf = do_QueryFrame(f); nsIScrollableFrame* sf = do_QueryFrame(f);
if (sf) { if (sf) {
@ -3694,14 +3697,19 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame)
} }
} }
} }
#endif
// Make sure the old frame isn't holding a reference to us. // Make sure the old frame isn't holding a reference to us.
mObjectFrame->SetInstanceOwner(nsnull); mObjectFrame->SetInstanceOwner(nsnull);
} else { } else {
// Scroll position listening is only required for Carbon event model plugins on Mac OS X.
// Note that we probably have a crash bug in the way we register/unregister, bug 723190.
// Bug 723190 is mitigated by limiting registration to Carbon event model plugins.
#if defined(XP_MACOSX) && !defined(NP_NO_QUICKDRAW)
if (aFrame) { if (aFrame) {
// We didn't have an object frame before but we do now! // We didn't have an object frame before but we do now. We need to register a scroll
// We need to register a scroll position listener on every scrollable // position listener on every scrollable frame up to the top.
// frame up to the top if (GetEventModel() == NPEventModelCarbon) {
for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) { for (nsIFrame* f = aFrame; f; f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
nsIScrollableFrame* sf = do_QueryFrame(f); nsIScrollableFrame* sf = do_QueryFrame(f);
if (sf) { if (sf) {
@ -3710,6 +3718,8 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame)
} }
} }
} }
#endif
}
// Swap in the new frame (or no frame) // Swap in the new frame (or no frame)
mObjectFrame = aFrame; mObjectFrame = aFrame;

Просмотреть файл

@ -233,6 +233,8 @@ nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent)
"JSEventListener has wrong script context?"); "JSEventListener has wrong script context?");
#endif #endif
nsCOMPtr<nsIVariant> vrv; nsCOMPtr<nsIVariant> vrv;
xpc_UnmarkGrayObject(mScopeObject);
xpc_UnmarkGrayObject(mHandler);
rv = mContext->CallEventHandler(mTarget, mScopeObject, mHandler, iargv, rv = mContext->CallEventHandler(mTarget, mScopeObject, mHandler, iargv,
getter_AddRefs(vrv)); getter_AddRefs(vrv));

Просмотреть файл

@ -24,21 +24,20 @@ var ConsoleObserver = {
if (aTopic == "console-storage-cache-event") { if (aTopic == "console-storage-cache-event") {
apiCallCount ++; apiCallCount ++;
if (apiCallCount == 4) { if (apiCallCount == 4) {
// remove the observer so we don't trigger this test again
Services.obs.removeObserver(this, "console-storage-cache-event");
try { try {
var tab = gBrowser.selectedTab; let tab = gBrowser.selectedTab;
let browser = gBrowser.selectedBrowser; let browser = gBrowser.selectedBrowser;
let win = XPCNativeWrapper.unwrap(browser.contentWindow); let win = XPCNativeWrapper.unwrap(browser.contentWindow);
let windowID = getWindowId(win); let windowID = getWindowId(win);
let messages = ConsoleAPIStorage.getEvents(windowID); let messages = ConsoleAPIStorage.getEvents(windowID);
ok(messages.length >= 4, "Some messages found in the storage service"); ok(messages.length >= 4, "Some messages found in the storage service");
ConsoleAPIStorage.clearEvents(); ConsoleAPIStorage.clearEvents();
messages = ConsoleAPIStorage.getEvents(windowID); messages = ConsoleAPIStorage.getEvents(windowID);
ok(messages.length == 0, "Cleared Storage, no events found"); is(messages.length, 0, "Cleared Storage");
// remove the observer so we don't trigger this test again
Services.obs.removeObserver(this, "console-storage-cache-event");
// make sure a closed window's events are in fact removed from the // make sure a closed window's events are in fact removed from the
// storage cache // storage cache
@ -52,7 +51,7 @@ var ConsoleObserver = {
executeSoon(function () { executeSoon(function () {
// use the old windowID again to see if we have any stray cached messages // use the old windowID again to see if we have any stray cached messages
messages = ConsoleAPIStorage.getEvents(windowID); messages = ConsoleAPIStorage.getEvents(windowID);
ok(messages.length == 0, "0 events found, tab close is clearing the cache"); is(messages.length, 0, "tab close is clearing the cache");
finish(); finish();
}); });
} catch (ex) { } catch (ex) {
@ -60,17 +59,15 @@ var ConsoleObserver = {
dump(ex.stack + "\n\n\n"); dump(ex.stack + "\n\n\n");
} }
} }
} }
} }
}; };
function tearDown() function tearDown()
{ {
while (gBrowser.tabs.length > 1) { while (gBrowser.tabs.length > 1)
gBrowser.removeCurrentTab(); gBrowser.removeCurrentTab();
} }
}
function test() function test()
{ {
@ -86,10 +83,7 @@ function test()
browser.addEventListener("DOMContentLoaded", function onLoad(event) { browser.addEventListener("DOMContentLoaded", function onLoad(event) {
browser.removeEventListener("DOMContentLoaded", onLoad, false); browser.removeEventListener("DOMContentLoaded", onLoad, false);
executeSoon(function test_executeSoon() { executeSoon(function test_executeSoon() {
var contentWin = browser.contentWindow; let win = XPCNativeWrapper.unwrap(browser.contentWindow);
let win = XPCNativeWrapper.unwrap(contentWin);
win.console.log("this", "is", "a", "log message"); win.console.log("this", "is", "a", "log message");
win.console.info("this", "is", "a", "info message"); win.console.info("this", "is", "a", "info message");
win.console.warn("this", "is", "a", "warn message"); win.console.warn("this", "is", "a", "warn message");

Просмотреть файл

@ -107,6 +107,9 @@ struct Listener : PRCList
static void static void
Remove(JSContext* aCx, Listener* aListener) Remove(JSContext* aCx, Listener* aListener)
{ {
if (js::IsIncrementalBarrierNeeded(aCx))
js::IncrementalValueBarrier(aListener->mListenerVal);
PR_REMOVE_LINK(aListener); PR_REMOVE_LINK(aListener);
JS_free(aCx, aListener); JS_free(aCx, aListener);
} }

Просмотреть файл

@ -300,7 +300,7 @@ private:
JSClass Worker::sClass = { JSClass Worker::sClass = {
"Worker", "Worker",
JSCLASS_HAS_PRIVATE, JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL,
NULL, NULL, NULL, Trace, NULL NULL, NULL, NULL, Trace, NULL
@ -415,7 +415,7 @@ private:
JSClass ChromeWorker::sClass = { JSClass ChromeWorker::sClass = {
"ChromeWorker", "ChromeWorker",
JSCLASS_HAS_PRIVATE, JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, NULL, NULL, NULL,
NULL, NULL, NULL, Trace, NULL NULL, NULL, NULL, Trace, NULL

Просмотреть файл

@ -799,7 +799,7 @@ private:
JSClass DedicatedWorkerGlobalScope::sClass = { JSClass DedicatedWorkerGlobalScope::sClass = {
"DedicatedWorkerGlobalScope", "DedicatedWorkerGlobalScope",
JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE, JSCLASS_GLOBAL_FLAGS | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub, JS_EnumerateStub, reinterpret_cast<JSResolveOp>(Resolve), JS_ConvertStub,
Finalize, NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL Finalize, NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL

Просмотреть файл

@ -220,7 +220,7 @@ private:
JSClass XMLHttpRequestUpload::sClass = { JSClass XMLHttpRequestUpload::sClass = {
"XMLHttpRequestUpload", "XMLHttpRequestUpload",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL
@ -769,7 +769,7 @@ private:
JSClass XMLHttpRequest::sClass = { JSClass XMLHttpRequest::sClass = {
"XMLHttpRequest", "XMLHttpRequest",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize,
NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL NULL, NULL, NULL, NULL, NULL, NULL, Trace, NULL

Просмотреть файл

@ -107,8 +107,8 @@
#define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer" #define JSD_AUTOREG_ENTRY "JSDebugger Startup Observer"
#define JSD_STARTUP_ENTRY "JSDebugger Startup Observer" #define JSD_STARTUP_ENTRY "JSDebugger Startup Observer"
static JSBool static void
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status); jsds_GCSliceCallbackProc (JSRuntime *rt, js::GCProgress progress, const js::GCDescription &desc);
/******************************************************************************* /*******************************************************************************
* global vars * global vars
@ -129,8 +129,8 @@ PRUint32 gFrameCount = 0;
#endif #endif
static jsdService *gJsds = 0; static jsdService *gJsds = 0;
static JSGCCallback gLastGCProc = jsds_GCCallbackProc; static js::GCSliceCallback gPrevGCSliceCallback = jsds_GCSliceCallbackProc;
static JSGCStatus gGCStatus = JSGC_END; static bool gGCRunning = false;
static struct DeadScript { static struct DeadScript {
PRCList links; PRCList links;
@ -460,11 +460,8 @@ jsds_FilterHook (JSDContext *jsdc, JSDThreadState *state)
*******************************************************************************/ *******************************************************************************/
static void static void
jsds_NotifyPendingDeadScripts (JSContext *cx) jsds_NotifyPendingDeadScripts (JSRuntime *rt)
{ {
#ifdef CAUTIOUS_SCRIPTHOOK
JSRuntime *rt = JS_GetRuntime(cx);
#endif
jsdService *jsds = gJsds; jsdService *jsds = gJsds;
nsCOMPtr<jsdIScriptHook> hook; nsCOMPtr<jsdIScriptHook> hook;
@ -511,31 +508,23 @@ jsds_NotifyPendingDeadScripts (JSContext *cx)
} }
} }
static JSBool static void
jsds_GCCallbackProc (JSContext *cx, JSGCStatus status) jsds_GCSliceCallbackProc (JSRuntime *rt, js::GCProgress progress, const js::GCDescription &desc)
{ {
#ifdef DEBUG_verbose if (progress == js::GC_CYCLE_END || progress == js::GC_SLICE_END) {
printf ("new gc status is %i\n", status); NS_ASSERTION(gGCRunning, "GC slice callback was missed");
#endif
if (status == JSGC_END) {
/* just to guard against reentering. */
gGCStatus = JSGC_BEGIN;
while (gDeadScripts) while (gDeadScripts)
jsds_NotifyPendingDeadScripts (cx); jsds_NotifyPendingDeadScripts (rt);
gGCRunning = false;
} else {
NS_ASSERTION(!gGCRunning, "should not re-enter GC");
gGCRunning = true;
} }
gGCStatus = status; if (gPrevGCSliceCallback)
if (gLastGCProc && !gLastGCProc (cx, status)) { (*gPrevGCSliceCallback)(rt, progress, desc);
/*
* If gLastGCProc returns false, then the GC will abort without making
* another callback with status=JSGC_END, so set the status to JSGC_END
* here.
*/
gGCStatus = JSGC_END;
return JS_FALSE;
}
return JS_TRUE;
} }
static uintN static uintN
@ -751,7 +740,7 @@ jsds_ScriptHookProc (JSDContext* jsdc, JSDScript* jsdscript, JSBool creating,
jsdis->Invalidate(); jsdis->Invalidate();
if (gGCStatus == JSGC_END) { if (!gGCRunning) {
nsCOMPtr<jsdIScriptHook> hook; nsCOMPtr<jsdIScriptHook> hook;
gJsds->GetScriptHook(getter_AddRefs(hook)); gJsds->GetScriptHook(getter_AddRefs(hook));
if (!hook) if (!hook)
@ -2580,9 +2569,9 @@ jsdService::ActivateDebugger (JSRuntime *rt)
mRuntime = rt; mRuntime = rt;
if (gLastGCProc == jsds_GCCallbackProc) if (gPrevGCSliceCallback == jsds_GCSliceCallbackProc)
/* condition indicates that the callback proc has not been set yet */ /* condition indicates that the callback proc has not been set yet */
gLastGCProc = JS_SetGCCallbackRT (rt, jsds_GCCallbackProc); gPrevGCSliceCallback = js::SetGCSliceCallback (rt, jsds_GCSliceCallbackProc);
mCx = JSD_DebuggerOnForUser (rt, NULL, NULL); mCx = JSD_DebuggerOnForUser (rt, NULL, NULL);
if (!mCx) if (!mCx)
@ -2652,19 +2641,14 @@ jsdService::Off (void)
return NS_ERROR_NOT_INITIALIZED; return NS_ERROR_NOT_INITIALIZED;
if (gDeadScripts) { if (gDeadScripts) {
if (gGCStatus != JSGC_END) if (gGCRunning)
return NS_ERROR_NOT_AVAILABLE; return NS_ERROR_NOT_AVAILABLE;
JSContext *cx = JSD_GetDefaultJSContext(mCx); JSContext *cx = JSD_GetDefaultJSContext(mCx);
while (gDeadScripts) while (gDeadScripts)
jsds_NotifyPendingDeadScripts (cx); jsds_NotifyPendingDeadScripts (JS_GetRuntime(cx));
} }
/*
if (gLastGCProc != jsds_GCCallbackProc)
JS_SetGCCallbackRT (mRuntime, gLastGCProc);
*/
DeactivateDebugger(); DeactivateDebugger();
#ifdef DEBUG #ifdef DEBUG
@ -3374,7 +3358,7 @@ jsdService::~jsdService()
mThrowHook = nsnull; mThrowHook = nsnull;
mTopLevelHook = nsnull; mTopLevelHook = nsnull;
mFunctionHook = nsnull; mFunctionHook = nsnull;
gGCStatus = JSGC_END; gGCRunning = false;
Off(); Off();
gJsds = nsnull; gJsds = nsnull;
} }

Просмотреть файл

@ -119,7 +119,6 @@ CPPSRCS = \
jsfun.cpp \ jsfun.cpp \
jsgc.cpp \ jsgc.cpp \
jsgcmark.cpp \ jsgcmark.cpp \
jsgcstats.cpp \
jscrashreport.cpp \ jscrashreport.cpp \
jshash.cpp \ jshash.cpp \
jsinfer.cpp \ jsinfer.cpp \
@ -193,7 +192,6 @@ INSTALLED_HEADERS = \
jsfriendapi.h \ jsfriendapi.h \
jsgc.h \ jsgc.h \
jscell.h \ jscell.h \
jsgcstats.h \
jshash.h \ jshash.h \
jslock.h \ jslock.h \
json.h \ json.h \

Просмотреть файл

@ -146,7 +146,7 @@ HashableValue::equals(const HashableValue &other) const
Class MapObject::class_ = { Class MapObject::class_ = {
"Map", "Map",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Map), JSCLASS_HAS_CACHED_PROTO(JSProto_Map),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
@ -297,7 +297,7 @@ js_InitMapClass(JSContext *cx, JSObject *obj)
Class SetObject::class_ = { Class SetObject::class_ = {
"Set", "Set",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Set), JSCLASS_HAS_CACHED_PROTO(JSProto_Set),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */

Просмотреть файл

@ -255,7 +255,7 @@ static JSClass sCDataProtoClass = {
static JSClass sCTypeClass = { static JSClass sCTypeClass = {
"CType", "CType",
JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS), JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CTYPE_SLOTS),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CType::Finalize,
NULL, NULL, CType::ConstructData, CType::ConstructData, NULL, NULL, NULL, CType::ConstructData, CType::ConstructData, NULL,
@ -272,7 +272,7 @@ static JSClass sCDataClass = {
static JSClass sCClosureClass = { static JSClass sCClosureClass = {
"CClosure", "CClosure",
JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS), JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(CCLOSURE_SLOTS),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, CClosure::Finalize,
NULL, NULL, NULL, NULL, NULL, NULL, CClosure::Trace, NULL NULL, NULL, NULL, NULL, NULL, NULL, CClosure::Trace, NULL

Просмотреть файл

@ -257,7 +257,7 @@ Parser::trace(JSTracer *trc)
{ {
ObjectBox *objbox = traceListHead; ObjectBox *objbox = traceListHead;
while (objbox) { while (objbox) {
MarkObjectRoot(trc, objbox->object, "parser.object"); MarkObjectRoot(trc, &objbox->object, "parser.object");
if (objbox->isFunctionBox) if (objbox->isFunctionBox)
static_cast<FunctionBox *>(objbox)->bindings.trace(trc); static_cast<FunctionBox *>(objbox)->bindings.trace(trc);
objbox = objbox->traceLink; objbox = objbox->traceLink;

Просмотреть файл

@ -266,6 +266,31 @@ HeapId::operator=(const HeapId &v)
return *this; return *this;
} }
inline const Value &
ReadBarrieredValue::get() const
{
if (value.isObject())
JSObject::readBarrier(&value.toObject());
else if (value.isString())
JSString::readBarrier(value.toString());
else
JS_ASSERT(!value.isMarkable());
return value;
}
inline
ReadBarrieredValue::operator const Value &() const
{
return get();
}
inline JSObject &
ReadBarrieredValue::toObject() const
{
return get().toObject();
}
} /* namespace js */ } /* namespace js */
#endif /* jsgc_barrier_inl_h___ */ #endif /* jsgc_barrier_inl_h___ */

Просмотреть файл

@ -406,6 +406,7 @@ class HeapId
bool operator!=(jsid id) const { return value != id; } bool operator!=(jsid id) const { return value != id; }
jsid get() const { return value; } jsid get() const { return value; }
jsid *unsafeGet() { return &value; }
operator jsid() const { return value; } operator jsid() const { return value; }
private: private:
@ -456,6 +457,20 @@ class ReadBarriered
operator MarkablePtr<U>() const { return MarkablePtr<U>(value); } operator MarkablePtr<U>() const { return MarkablePtr<U>(value); }
}; };
class ReadBarrieredValue
{
Value value;
public:
ReadBarrieredValue() : value(UndefinedValue()) {}
ReadBarrieredValue(const Value &value) : value(value) {}
inline const Value &get() const;
inline operator const Value &() const;
inline JSObject &toObject() const;
};
} }
#endif /* jsgc_barrier_h___ */ #endif /* jsgc_barrier_h___ */

Просмотреть файл

@ -38,9 +38,10 @@
* ***** END LICENSE BLOCK ***** */ * ***** END LICENSE BLOCK ***** */
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <stdarg.h>
#include "jscntxt.h" #include "jscntxt.h"
#include "jscompartment.h"
#include "jscrashformat.h" #include "jscrashformat.h"
#include "jscrashreport.h" #include "jscrashreport.h"
#include "jsprf.h" #include "jsprf.h"
@ -69,78 +70,114 @@ ExplainReason(gcreason::Reason reason)
} }
} }
Statistics::ColumnInfo::ColumnInfo(const char *title, double t, double total) void
: title(title) Statistics::fmt(const char *f, ...)
{ {
JS_snprintf(str, sizeof(str), "%.1f", t); va_list va;
JS_snprintf(totalStr, sizeof(totalStr), "%.1f", total); size_t off = strlen(buffer);
width = 6;
}
Statistics::ColumnInfo::ColumnInfo(const char *title, double t) va_start(va, f);
: title(title) JS_vsnprintf(buffer + off, BUFFER_SIZE - off, f, va);
{ va_end(va);
JS_snprintf(str, sizeof(str), "%.1f", t);
strcpy(totalStr, "n/a");
width = 6;
} }
Statistics::ColumnInfo::ColumnInfo(const char *title, unsigned int data)
: title(title)
{
JS_snprintf(str, sizeof(str), "%d", data);
strcpy(totalStr, "n/a");
width = 4;
}
Statistics::ColumnInfo::ColumnInfo(const char *title, const char *data)
: title(title)
{
JS_ASSERT(strlen(data) < sizeof(str));
strcpy(str, data);
strcpy(totalStr, "n/a ");
width = 0;
}
static const int NUM_COLUMNS = 17;
void void
Statistics::makeTable(ColumnInfo *cols) Statistics::fmtIfNonzero(const char *name, double t)
{ {
int i = 0; if (t) {
if (needComma)
fmt(", ");
fmt("%s: %.1f", name, t);
needComma = true;
}
}
cols[i++] = ColumnInfo("Type", compartment ? "Comp" : "Glob"); void
Statistics::formatPhases(int64_t *times)
{
needComma = false;
fmtIfNonzero("mark", t(times[PHASE_MARK]));
fmtIfNonzero("mark-roots", t(times[PHASE_MARK_ROOTS]));
fmtIfNonzero("mark-delayed", t(times[PHASE_MARK_DELAYED]));
fmtIfNonzero("mark-other", t(times[PHASE_MARK_OTHER]));
fmtIfNonzero("sweep", t(times[PHASE_SWEEP]));
fmtIfNonzero("sweep-obj", t(times[PHASE_SWEEP_OBJECT]));
fmtIfNonzero("sweep-string", t(times[PHASE_SWEEP_STRING]));
fmtIfNonzero("sweep-script", t(times[PHASE_SWEEP_SCRIPT]));
fmtIfNonzero("sweep-shape", t(times[PHASE_SWEEP_SHAPE]));
fmtIfNonzero("discard-code", t(times[PHASE_DISCARD_CODE]));
fmtIfNonzero("discard-analysis", t(times[PHASE_DISCARD_ANALYSIS]));
fmtIfNonzero("xpconnect", t(times[PHASE_XPCONNECT]));
fmtIfNonzero("deallocate", t(times[PHASE_DESTROY]));
}
cols[i++] = ColumnInfo("Total", t(PHASE_GC), total(PHASE_GC)); /* Except for the first and last, slices of less than 12ms are not reported. */
cols[i++] = ColumnInfo("Wait", beginDelay(PHASE_MARK, PHASE_GC)); static const int64_t SLICE_MIN_REPORT_TIME = 12 * PRMJ_USEC_PER_MSEC;
cols[i++] = ColumnInfo("Mark", t(PHASE_MARK), total(PHASE_MARK));
cols[i++] = ColumnInfo("Sweep", t(PHASE_SWEEP), total(PHASE_SWEEP));
cols[i++] = ColumnInfo("FinObj", t(PHASE_SWEEP_OBJECT), total(PHASE_SWEEP_OBJECT));
cols[i++] = ColumnInfo("FinStr", t(PHASE_SWEEP_STRING), total(PHASE_SWEEP_STRING));
cols[i++] = ColumnInfo("FinScr", t(PHASE_SWEEP_SCRIPT), total(PHASE_SWEEP_SCRIPT));
cols[i++] = ColumnInfo("FinShp", t(PHASE_SWEEP_SHAPE), total(PHASE_SWEEP_SHAPE));
cols[i++] = ColumnInfo("DisCod", t(PHASE_DISCARD_CODE), total(PHASE_DISCARD_CODE));
cols[i++] = ColumnInfo("DisAnl", t(PHASE_DISCARD_ANALYSIS), total(PHASE_DISCARD_ANALYSIS));
cols[i++] = ColumnInfo("XPCnct", t(PHASE_XPCONNECT), total(PHASE_XPCONNECT));
cols[i++] = ColumnInfo("Destry", t(PHASE_DESTROY), total(PHASE_DESTROY));
cols[i++] = ColumnInfo("End", endDelay(PHASE_GC, PHASE_DESTROY));
cols[i++] = ColumnInfo("+Chu", counts[STAT_NEW_CHUNK]); const char *
cols[i++] = ColumnInfo("-Chu", counts[STAT_DESTROY_CHUNK]); Statistics::formatData()
{
buffer[0] = 0x00;
cols[i++] = ColumnInfo("Reason", ExplainReason(triggerReason)); int64_t total = 0, longest = 0;
JS_ASSERT(i == NUM_COLUMNS); for (SliceData *slice = slices.begin(); slice != slices.end(); slice++) {
total += slice->duration();
if (slice->duration() > longest)
longest = slice->duration();
}
double mmu20 = computeMMU(20 * PRMJ_USEC_PER_MSEC);
double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
fmt("TotalTime: %.1fms, Type: %s", t(total), compartment ? "compartment" : "global");
fmt(", MMU(20ms): %d%%, MMU(50ms): %d%%", int(mmu20 * 100), int(mmu50 * 100));
if (slices.length() > 1)
fmt(", MaxPause: %.1f", t(longest));
else
fmt(", Reason: %s", ExplainReason(slices[0].reason));
if (wasReset)
fmt(", ***RESET***");
fmt(", +chunks: %d, -chunks: %d\n", counts[STAT_NEW_CHUNK], counts[STAT_DESTROY_CHUNK]);
if (slices.length() > 1) {
for (size_t i = 0; i < slices.length(); i++) {
int64_t width = slices[i].duration();
if (i != 0 && i != slices.length() - 1 && width < SLICE_MIN_REPORT_TIME)
continue;
fmt(" Slice %d @ %.1fms (Pause: %.1f, Reason: %s): ",
i,
t(slices[i].end - slices[0].start),
t(width),
ExplainReason(slices[i].reason));
formatPhases(slices[i].phaseTimes);
fmt("\n");
}
fmt(" Totals: ");
}
formatPhases(phaseTimes);
fmt("\n");
return buffer;
} }
Statistics::Statistics(JSRuntime *rt) Statistics::Statistics(JSRuntime *rt)
: runtime(rt), : runtime(rt),
triggerReason(gcreason::NO_REASON) startupTime(PRMJ_Now()),
fp(NULL),
fullFormat(false),
compartment(NULL),
wasReset(false),
needComma(false)
{ {
PodArrayZero(phaseTotals);
PodArrayZero(counts); PodArrayZero(counts);
PodArrayZero(totals);
startupTime = PRMJ_Now();
char *env = getenv("MOZ_GCTIMER"); char *env = getenv("MOZ_GCTIMER");
if (!env || strcmp(env, "none") == 0) { if (!env || strcmp(env, "none") == 0) {
@ -159,14 +196,6 @@ Statistics::Statistics(JSRuntime *rt)
fp = fopen(env, "a"); fp = fopen(env, "a");
JS_ASSERT(fp); JS_ASSERT(fp);
fprintf(fp, " AppTime");
ColumnInfo cols[NUM_COLUMNS];
makeTable(cols);
for (int i = 0; i < NUM_COLUMNS; i++)
fprintf(fp, ", %*s", cols[i].width, cols[i].title);
fprintf(fp, "\n");
} }
} }
@ -174,13 +203,9 @@ Statistics::~Statistics()
{ {
if (fp) { if (fp) {
if (fullFormat) { if (fullFormat) {
fprintf(fp, "------>TOTAL"); buffer[0] = 0x00;
formatPhases(phaseTotals);
ColumnInfo cols[NUM_COLUMNS]; fprintf(fp, "TOTALS\n%s\n\n-------\n", buffer);
makeTable(cols);
for (int i = 0; i < NUM_COLUMNS && cols[i].totalStr[0]; i++)
fprintf(fp, ", %*s", cols[i].width, cols[i].totalStr);
fprintf(fp, "\n");
} }
if (fp != stdout && fp != stderr) if (fp != stdout && fp != stderr)
@ -188,120 +213,65 @@ Statistics::~Statistics()
} }
} }
struct GCCrashData
{
int isRegen;
int isCompartment;
};
void
Statistics::beginGC(JSCompartment *comp, gcreason::Reason reason)
{
compartment = comp;
PodArrayZero(phaseStarts);
PodArrayZero(phaseEnds);
PodArrayZero(phaseTimes);
triggerReason = reason;
beginPhase(PHASE_GC);
Probes::GCStart();
GCCrashData crashData;
crashData.isCompartment = !!compartment;
crash::SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
}
double double
Statistics::t(Phase phase) Statistics::t(int64_t t)
{ {
return double(phaseTimes[phase]) / PRMJ_USEC_PER_MSEC; return double(t) / PRMJ_USEC_PER_MSEC;
} }
double int64_t
Statistics::total(Phase phase) Statistics::gcDuration()
{ {
return double(totals[phase]) / PRMJ_USEC_PER_MSEC; return slices.back().end - slices[0].start;
}
double
Statistics::beginDelay(Phase phase1, Phase phase2)
{
return double(phaseStarts[phase1] - phaseStarts[phase2]) / PRMJ_USEC_PER_MSEC;
}
double
Statistics::endDelay(Phase phase1, Phase phase2)
{
return double(phaseEnds[phase1] - phaseEnds[phase2]) / PRMJ_USEC_PER_MSEC;
}
void
Statistics::statsToString(char *buffer, size_t size)
{
JS_ASSERT(size);
buffer[0] = 0x00;
ColumnInfo cols[NUM_COLUMNS];
makeTable(cols);
size_t pos = 0;
for (int i = 0; i < NUM_COLUMNS; i++) {
int len = strlen(cols[i].title) + 1 + strlen(cols[i].str);
if (i > 0)
len += 2;
if (pos + len >= size)
break;
if (i > 0)
strcat(buffer, ", ");
strcat(buffer, cols[i].title);
strcat(buffer, ":");
strcat(buffer, cols[i].str);
pos += len;
}
} }
void void
Statistics::printStats() Statistics::printStats()
{ {
if (fullFormat) { if (fullFormat) {
fprintf(fp, "%12.0f", double(phaseStarts[PHASE_GC] - startupTime) / PRMJ_USEC_PER_MSEC); fprintf(fp, "GC(T+%.3fs) %s\n",
t(slices[0].start - startupTime) / 1000.0,
ColumnInfo cols[NUM_COLUMNS]; formatData());
makeTable(cols);
for (int i = 0; i < NUM_COLUMNS; i++)
fprintf(fp, ", %*s", cols[i].width, cols[i].str);
fprintf(fp, "\n");
} else { } else {
fprintf(fp, "%f %f %f\n", fprintf(fp, "%f %f %f\n",
t(PHASE_GC), t(PHASE_MARK), t(PHASE_SWEEP)); t(gcDuration()),
t(phaseTimes[PHASE_MARK]),
t(phaseTimes[PHASE_SWEEP]));
} }
fflush(fp); fflush(fp);
} }
void
Statistics::beginGC()
{
PodArrayZero(phaseStarts);
PodArrayZero(phaseTimes);
slices.clearAndFree();
wasReset = false;
Probes::GCStart();
}
void void
Statistics::endGC() Statistics::endGC()
{ {
Probes::GCEnd(); Probes::GCEnd();
endPhase(PHASE_GC);
crash::SnapshotGCStack(); crash::SnapshotGCStack();
for (int i = 0; i < PHASE_LIMIT; i++) for (int i = 0; i < PHASE_LIMIT; i++)
totals[i] += phaseTimes[i]; phaseTotals[i] += phaseTimes[i];
if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) { if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback) {
(*cb)(JS_TELEMETRY_GC_REASON, triggerReason);
(*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0); (*cb)(JS_TELEMETRY_GC_IS_COMPARTMENTAL, compartment ? 1 : 0);
(*cb)(JS_TELEMETRY_GC_MS, t(PHASE_GC)); (*cb)(JS_TELEMETRY_GC_MS, t(gcDuration()));
(*cb)(JS_TELEMETRY_GC_MARK_MS, t(PHASE_MARK)); (*cb)(JS_TELEMETRY_GC_MARK_MS, t(phaseTimes[PHASE_MARK]));
(*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(PHASE_SWEEP)); (*cb)(JS_TELEMETRY_GC_SWEEP_MS, t(phaseTimes[PHASE_SWEEP]));
} (*cb)(JS_TELEMETRY_GC_RESET, wasReset);
(*cb)(JS_TELEMETRY_GC_INCREMENTAL_DISABLED, !runtime->gcIncrementalEnabled);
if (JSGCFinishedCallback cb = runtime->gcFinishedCallback) { double mmu50 = computeMMU(50 * PRMJ_USEC_PER_MSEC);
char buffer[1024]; (*cb)(JS_TELEMETRY_GC_MMU_50, mmu50 * 100);
statsToString(buffer, sizeof(buffer));
(*cb)(runtime, compartment, buffer);
} }
if (fp) if (fp)
@ -310,6 +280,47 @@ Statistics::endGC()
PodArrayZero(counts); PodArrayZero(counts);
} }
void
Statistics::beginSlice(JSCompartment *comp, gcreason::Reason reason)
{
compartment = comp;
bool first = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
if (first)
beginGC();
SliceData data(reason, PRMJ_Now());
(void) slices.append(data); /* Ignore any OOMs here. */
if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
(*cb)(JS_TELEMETRY_GC_REASON, reason);
if (GCSliceCallback cb = runtime->gcSliceCallback) {
GCDescription desc(NULL, !!compartment);
(*cb)(runtime, first ? GC_CYCLE_BEGIN : GC_SLICE_BEGIN, desc);
}
}
void
Statistics::endSlice()
{
slices.back().end = PRMJ_Now();
if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
(*cb)(JS_TELEMETRY_GC_SLICE_MS, t(slices.back().end - slices.back().start));
bool last = runtime->gcIncrementalState == gc::NO_INCREMENTAL;
if (last)
endGC();
if (GCSliceCallback cb = runtime->gcSliceCallback) {
if (last)
(*cb)(runtime, GC_CYCLE_END, GCDescription(formatData(), !!compartment));
else
(*cb)(runtime, GC_SLICE_END, GCDescription(NULL, !!compartment));
}
}
void void
Statistics::beginPhase(Phase phase) Statistics::beginPhase(Phase phase)
{ {
@ -324,8 +335,10 @@ Statistics::beginPhase(Phase phase)
void void
Statistics::endPhase(Phase phase) Statistics::endPhase(Phase phase)
{ {
phaseEnds[phase] = PRMJ_Now(); int64_t now = PRMJ_Now();
phaseTimes[phase] += phaseEnds[phase] - phaseStarts[phase]; int64_t t = now - phaseStarts[phase];
slices.back().phaseTimes[phase] += t;
phaseTimes[phase] += t;
if (phase == gcstats::PHASE_MARK) if (phase == gcstats::PHASE_MARK)
Probes::GCEndMarkPhase(); Probes::GCEndMarkPhase();
@ -333,5 +346,44 @@ Statistics::endPhase(Phase phase)
Probes::GCEndSweepPhase(); Probes::GCEndSweepPhase();
} }
/*
* MMU (minimum mutator utilization) is a measure of how much garbage collection
* is affecting the responsiveness of the system. MMU measurements are given
* with respect to a certain window size. If we report MMU(50ms) = 80%, then
* that means that, for any 50ms window of time, at least 80% of the window is
* devoted to the mutator. In other words, the GC is running for at most 20% of
* the window, or 10ms. The GC can run multiple slices during the 50ms window
* as long as the total time it spends is at most 10ms.
*/
double
Statistics::computeMMU(int64_t window)
{
JS_ASSERT(!slices.empty());
int64_t gc = slices[0].end - slices[0].start;
int64_t gcMax = gc;
if (gc >= window)
return 0.0;
int startIndex = 0;
for (size_t endIndex = 1; endIndex < slices.length(); endIndex++) {
gc += slices[endIndex].end - slices[endIndex].start;
while (slices[endIndex].end - slices[startIndex].end >= window) {
gc -= slices[startIndex].end - slices[startIndex].start;
startIndex++;
}
int64_t cur = gc;
if (slices[endIndex].end - slices[startIndex].start > window)
cur -= (slices[endIndex].end - slices[startIndex].start - window);
if (cur > gcMax)
gcMax = cur;
}
return double(window - gcMax) / window;
}
} /* namespace gcstats */ } /* namespace gcstats */
} /* namespace js */ } /* namespace js */

Просмотреть файл

@ -52,8 +52,10 @@ namespace js {
namespace gcstats { namespace gcstats {
enum Phase { enum Phase {
PHASE_GC,
PHASE_MARK, PHASE_MARK,
PHASE_MARK_ROOTS,
PHASE_MARK_DELAYED,
PHASE_MARK_OTHER,
PHASE_SWEEP, PHASE_SWEEP,
PHASE_SWEEP_OBJECT, PHASE_SWEEP_OBJECT,
PHASE_SWEEP_STRING, PHASE_SWEEP_STRING,
@ -74,16 +76,20 @@ enum Stat {
STAT_LIMIT STAT_LIMIT
}; };
static const size_t BUFFER_SIZE = 8192;
struct Statistics { struct Statistics {
Statistics(JSRuntime *rt); Statistics(JSRuntime *rt);
~Statistics(); ~Statistics();
void beginGC(JSCompartment *comp, gcreason::Reason reason);
void endGC();
void beginPhase(Phase phase); void beginPhase(Phase phase);
void endPhase(Phase phase); void endPhase(Phase phase);
void beginSlice(JSCompartment *comp, gcreason::Reason reason);
void endSlice();
void reset() { wasReset = true; }
void count(Stat s) { void count(Stat s) {
JS_ASSERT(s < STAT_LIMIT); JS_ASSERT(s < STAT_LIMIT);
counts[s]++; counts[s]++;
@ -92,48 +98,64 @@ struct Statistics {
private: private:
JSRuntime *runtime; JSRuntime *runtime;
uint64_t startupTime; int64_t startupTime;
FILE *fp; FILE *fp;
bool fullFormat; bool fullFormat;
gcreason::Reason triggerReason;
JSCompartment *compartment; JSCompartment *compartment;
bool wasReset;
uint64_t phaseStarts[PHASE_LIMIT]; struct SliceData {
uint64_t phaseEnds[PHASE_LIMIT]; SliceData(gcreason::Reason reason, int64_t start)
uint64_t phaseTimes[PHASE_LIMIT]; : reason(reason), start(start)
uint64_t totals[PHASE_LIMIT]; {
PodArrayZero(phaseTimes);
}
gcreason::Reason reason;
int64_t start, end;
int64_t phaseTimes[PHASE_LIMIT];
int64_t duration() const { return end - start; }
};
Vector<SliceData, 8, SystemAllocPolicy> slices;
/* Most recent time when the given phase started. */
int64_t phaseStarts[PHASE_LIMIT];
/* Total time in a given phase for this GC. */
int64_t phaseTimes[PHASE_LIMIT];
/* Total time in a given phase over all GCs. */
int64_t phaseTotals[PHASE_LIMIT];
/* Number of events of this type for this GC. */
unsigned int counts[STAT_LIMIT]; unsigned int counts[STAT_LIMIT];
double t(Phase phase); char buffer[BUFFER_SIZE];
double total(Phase phase); bool needComma;
double beginDelay(Phase phase1, Phase phase2);
double endDelay(Phase phase1, Phase phase2); void beginGC();
void endGC();
int64_t gcDuration();
double t(int64_t t);
void printStats(); void printStats();
void statsToString(char *buffer, size_t size); void fmt(const char *f, ...);
void fmtIfNonzero(const char *name, double t);
void formatPhases(int64_t *times);
const char *formatData();
struct ColumnInfo { double computeMMU(int64_t resolution);
const char *title;
char str[32];
char totalStr[32];
int width;
ColumnInfo() {}
ColumnInfo(const char *title, double t, double total);
ColumnInfo(const char *title, double t);
ColumnInfo(const char *title, unsigned int data);
ColumnInfo(const char *title, const char *data);
}; };
void makeTable(ColumnInfo *cols); struct AutoGCSlice {
}; AutoGCSlice(Statistics &stats, JSCompartment *comp, gcreason::Reason reason
struct AutoGC {
AutoGC(Statistics &stats, JSCompartment *comp, gcreason::Reason reason
JS_GUARD_OBJECT_NOTIFIER_PARAM) JS_GUARD_OBJECT_NOTIFIER_PARAM)
: stats(stats) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginGC(comp, reason); } : stats(stats) { JS_GUARD_OBJECT_NOTIFIER_INIT; stats.beginSlice(comp, reason); }
~AutoGC() { stats.endGC(); } ~AutoGCSlice() { stats.endSlice(); }
Statistics &stats; Statistics &stats;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER JS_DECL_USE_GUARD_OBJECT_NOTIFIER

Просмотреть файл

@ -723,8 +723,6 @@ JSRuntime::JSRuntime()
gcMaxBytes(0), gcMaxBytes(0),
gcMaxMallocBytes(0), gcMaxMallocBytes(0),
gcNumArenasFreeCommitted(0), gcNumArenasFreeCommitted(0),
gcNumber(0),
gcIncrementalTracer(NULL),
gcVerifyData(NULL), gcVerifyData(NULL),
gcChunkAllocationSinceLastGC(false), gcChunkAllocationSinceLastGC(false),
gcNextFullGCTime(0), gcNextFullGCTime(0),
@ -733,12 +731,20 @@ JSRuntime::JSRuntime()
gcIsNeeded(0), gcIsNeeded(0),
gcWeakMapList(NULL), gcWeakMapList(NULL),
gcStats(thisFromCtor()), gcStats(thisFromCtor()),
gcNumber(0),
gcStartNumber(0),
gcTriggerReason(gcreason::NO_REASON), gcTriggerReason(gcreason::NO_REASON),
gcTriggerCompartment(NULL), gcTriggerCompartment(NULL),
gcCurrentCompartment(NULL), gcCurrentCompartment(NULL),
gcCheckCompartment(NULL), gcCheckCompartment(NULL),
gcIncrementalState(gc::NO_INCREMENTAL),
gcCompartmentCreated(false),
gcLastMarkSlice(false),
gcInterFrameGC(0),
gcSliceBudget(SliceBudget::Unlimited),
gcIncrementalEnabled(true),
gcIncrementalCompartment(NULL),
gcPoke(false), gcPoke(false),
gcMarkAndSweep(false),
gcRunning(false), gcRunning(false),
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
gcZeal_(0), gcZeal_(0),
@ -747,7 +753,7 @@ JSRuntime::JSRuntime()
gcDebugCompartmentGC(false), gcDebugCompartmentGC(false),
#endif #endif
gcCallback(NULL), gcCallback(NULL),
gcFinishedCallback(NULL), gcSliceCallback(NULL),
gcMallocBytes(0), gcMallocBytes(0),
gcBlackRootsTraceOp(NULL), gcBlackRootsTraceOp(NULL),
gcBlackRootsData(NULL), gcBlackRootsData(NULL),
@ -814,6 +820,9 @@ JSRuntime::init(uint32_t maxbytes)
if (!js_InitGC(this, maxbytes)) if (!js_InitGC(this, maxbytes))
return false; return false;
if (!gcMarker.init())
return false;
if (!(atomsCompartment = this->new_<JSCompartment>(this)) || if (!(atomsCompartment = this->new_<JSCompartment>(this)) ||
!atomsCompartment->init(NULL) || !atomsCompartment->init(NULL) ||
!compartments.append(atomsCompartment)) { !compartments.append(atomsCompartment)) {
@ -2437,13 +2446,7 @@ JS_SetExtraGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data)
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
JS_TracerInit(JSTracer *trc, JSContext *cx, JSTraceCallback callback) JS_TracerInit(JSTracer *trc, JSContext *cx, JSTraceCallback callback)
{ {
trc->runtime = cx->runtime; InitTracer(trc, cx->runtime, cx, callback);
trc->context = cx;
trc->callback = callback;
trc->debugPrinter = NULL;
trc->debugPrintArg = NULL;
trc->debugPrintIndex = size_t(-1);
trc->eagerlyTraceWeakMaps = true;
} }
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
@ -2875,8 +2878,7 @@ JS_CompartmentGC(JSContext *cx, JSCompartment *comp)
/* We cannot GC the atoms compartment alone; use a full GC instead. */ /* We cannot GC the atoms compartment alone; use a full GC instead. */
JS_ASSERT(comp != cx->runtime->atomsCompartment); JS_ASSERT(comp != cx->runtime->atomsCompartment);
js::gc::VerifyBarriers(cx, true); GC(cx, comp, GC_NORMAL, gcreason::API);
js_GC(cx, comp, GC_NORMAL, gcreason::API);
} }
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
@ -2914,7 +2916,6 @@ JS_PUBLIC_API(JSBool)
JS_IsAboutToBeFinalized(void *thing) JS_IsAboutToBeFinalized(void *thing)
{ {
gc::Cell *t = static_cast<gc::Cell *>(thing); gc::Cell *t = static_cast<gc::Cell *>(thing);
JS_ASSERT(!t->compartment()->rt->gcIncrementalTracer);
return IsAboutToBeFinalized(t); return IsAboutToBeFinalized(t);
} }
@ -2931,11 +2932,15 @@ JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value)
case JSGC_MAX_MALLOC_BYTES: case JSGC_MAX_MALLOC_BYTES:
rt->setGCMaxMallocBytes(value); rt->setGCMaxMallocBytes(value);
break; break;
case JSGC_SLICE_TIME_BUDGET:
rt->gcSliceBudget = SliceBudget::TimeBudget(value);
break;
default: default:
JS_ASSERT(key == JSGC_MODE); JS_ASSERT(key == JSGC_MODE);
rt->gcMode = JSGCMode(value); rt->gcMode = JSGCMode(value);
JS_ASSERT(rt->gcMode == JSGC_MODE_GLOBAL || JS_ASSERT(rt->gcMode == JSGC_MODE_GLOBAL ||
rt->gcMode == JSGC_MODE_COMPARTMENT); rt->gcMode == JSGC_MODE_COMPARTMENT ||
rt->gcMode == JSGC_MODE_INCREMENTAL);
return; return;
} }
} }
@ -2956,9 +2961,11 @@ JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
return uint32_t(rt->gcChunkPool.getEmptyCount()); return uint32_t(rt->gcChunkPool.getEmptyCount());
case JSGC_TOTAL_CHUNKS: case JSGC_TOTAL_CHUNKS:
return uint32_t(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount()); return uint32_t(rt->gcChunkSet.count() + rt->gcChunkPool.getEmptyCount());
case JSGC_SLICE_TIME_BUDGET:
return uint32_t(rt->gcSliceBudget > 0 ? rt->gcSliceBudget / PRMJ_USEC_PER_MSEC : 0);
default: default:
JS_ASSERT(key == JSGC_NUMBER); JS_ASSERT(key == JSGC_NUMBER);
return rt->gcNumber; return uint32_t(rt->gcNumber);
} }
} }
@ -6609,7 +6616,16 @@ JS_AbortIfWrongThread(JSRuntime *rt)
JS_PUBLIC_API(void) JS_PUBLIC_API(void)
JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment) JS_SetGCZeal(JSContext *cx, uint8_t zeal, uint32_t frequency, JSBool compartment)
{ {
bool schedule = zeal >= js::gc::ZealAllocThreshold && zeal < js::gc::ZealVerifierThreshold; #ifdef JS_GC_ZEAL
const char *env = getenv("JS_GC_ZEAL");
if (env) {
zeal = atoi(env);
frequency = 1;
compartment = false;
}
#endif
bool schedule = zeal >= js::gc::ZealAllocValue;
cx->runtime->gcZeal_ = zeal; cx->runtime->gcZeal_ = zeal;
cx->runtime->gcZealFrequency = frequency; cx->runtime->gcZealFrequency = frequency;
cx->runtime->gcNextScheduled = schedule ? frequency : 0; cx->runtime->gcNextScheduled = schedule ? frequency : 0;

Просмотреть файл

@ -1029,7 +1029,7 @@ class AutoEnumStateRooter : private AutoGCRooter
protected: protected:
void trace(JSTracer *trc); void trace(JSTracer *trc);
JSObject * const obj; JSObject *obj;
private: private:
Value stateValue; Value stateValue;
@ -1428,8 +1428,11 @@ typedef JSBool
(* JSContextCallback)(JSContext *cx, uintN contextOp); (* JSContextCallback)(JSContext *cx, uintN contextOp);
typedef enum JSGCStatus { typedef enum JSGCStatus {
/* These callbacks happen outside the GC lock. */
JSGC_BEGIN, JSGC_BEGIN,
JSGC_END, JSGC_END,
/* These callbacks happen within the GC lock. */
JSGC_MARK_END, JSGC_MARK_END,
JSGC_FINALIZE_END JSGC_FINALIZE_END
} JSGCStatus; } JSGCStatus;
@ -3290,7 +3293,10 @@ typedef enum JSGCParamKey {
JSGC_UNUSED_CHUNKS = 7, JSGC_UNUSED_CHUNKS = 7,
/* Total number of allocated GC chunks. */ /* Total number of allocated GC chunks. */
JSGC_TOTAL_CHUNKS = 8 JSGC_TOTAL_CHUNKS = 8,
/* Max milliseconds to spend in an incremental GC slice. */
JSGC_SLICE_TIME_BUDGET = 9
} JSGCParamKey; } JSGCParamKey;
typedef enum JSGCMode { typedef enum JSGCMode {
@ -3298,7 +3304,13 @@ typedef enum JSGCMode {
JSGC_MODE_GLOBAL = 0, JSGC_MODE_GLOBAL = 0,
/* Perform per-compartment GCs until too much garbage has accumulated. */ /* Perform per-compartment GCs until too much garbage has accumulated. */
JSGC_MODE_COMPARTMENT = 1 JSGC_MODE_COMPARTMENT = 1,
/*
* Collect in short time slices rather than all at once. Implies
* JSGC_MODE_COMPARTMENT.
*/
JSGC_MODE_INCREMENTAL = 2
} JSGCMode; } JSGCMode;
extern JS_PUBLIC_API(void) extern JS_PUBLIC_API(void)
@ -3393,6 +3405,8 @@ struct JSClass {
object in prototype chain object in prototype chain
passed in via *objp in/out passed in via *objp in/out
parameter */ parameter */
#define JSCLASS_IMPLEMENTS_BARRIERS (1<<5) /* Correctly implements GC read
and write barriers */
#define JSCLASS_DOCUMENT_OBSERVER (1<<6) /* DOM document observer */ #define JSCLASS_DOCUMENT_OBSERVER (1<<6) /* DOM document observer */
/* /*

Просмотреть файл

@ -386,15 +386,20 @@ js_TraceAtomState(JSTracer *trc)
JSAtomState *state = &rt->atomState; JSAtomState *state = &rt->atomState;
if (rt->gcKeepAtoms) { if (rt->gcKeepAtoms) {
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
MarkStringRoot(trc, r.front().asPtr(), "locked_atom"); JSAtom *tmp = r.front().asPtr();
MarkStringRoot(trc, &tmp, "locked_atom");
JS_ASSERT(tmp == r.front().asPtr());
}
} else { } else {
for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) { for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
AtomStateEntry entry = r.front(); AtomStateEntry entry = r.front();
if (!entry.isTagged()) if (!entry.isTagged())
continue; continue;
MarkStringRoot(trc, entry.asPtr(), "interned_atom"); JSAtom *tmp = entry.asPtr();
MarkStringRoot(trc, &tmp, "interned_atom");
JS_ASSERT(tmp == entry.asPtr());
} }
} }
} }

Просмотреть файл

@ -282,10 +282,10 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
c->clearTraps(cx); c->clearTraps(cx);
JS_ClearAllWatchPoints(cx); JS_ClearAllWatchPoints(cx);
js_GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT); GC(cx, NULL, GC_NORMAL, gcreason::LAST_CONTEXT);
} else if (mode == JSDCM_FORCE_GC) { } else if (mode == JSDCM_FORCE_GC) {
js_GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT); GC(cx, NULL, GC_NORMAL, gcreason::DESTROY_CONTEXT);
} else if (mode == JSDCM_MAYBE_GC) { } else if (mode == JSDCM_MAYBE_GC) {
JS_MaybeGC(cx); JS_MaybeGC(cx);
} }
@ -875,7 +875,7 @@ js_InvokeOperationCallback(JSContext *cx)
JS_ATOMIC_SET(&rt->interrupt, 0); JS_ATOMIC_SET(&rt->interrupt, 0);
if (rt->gcIsNeeded) if (rt->gcIsNeeded)
js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason); GCSlice(cx, rt->gcTriggerCompartment, GC_NORMAL, rt->gcTriggerReason);
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
/* /*
@ -1278,7 +1278,7 @@ JSContext::mark(JSTracer *trc)
/* Mark other roots-by-definition in the JSContext. */ /* Mark other roots-by-definition in the JSContext. */
if (globalObject && !hasRunOption(JSOPTION_UNROOTED_GLOBAL)) if (globalObject && !hasRunOption(JSOPTION_UNROOTED_GLOBAL))
MarkObjectRoot(trc, globalObject, "global object"); MarkObjectRoot(trc, &globalObject, "global object");
if (isExceptionPending()) if (isExceptionPending())
MarkValueRoot(trc, &exception, "exception"); MarkValueRoot(trc, &exception, "exception");

Просмотреть файл

@ -304,24 +304,25 @@ struct JSRuntime : js::RuntimeFriendFields
* in MaybeGC. * in MaybeGC.
*/ */
volatile uint32_t gcNumArenasFreeCommitted; volatile uint32_t gcNumArenasFreeCommitted;
uint32_t gcNumber; js::FullGCMarker gcMarker;
js::GCMarker *gcIncrementalTracer;
void *gcVerifyData; void *gcVerifyData;
bool gcChunkAllocationSinceLastGC; bool gcChunkAllocationSinceLastGC;
int64_t gcNextFullGCTime; int64_t gcNextFullGCTime;
int64_t gcJitReleaseTime; int64_t gcJitReleaseTime;
JSGCMode gcMode; JSGCMode gcMode;
volatile uintptr_t gcBarrierFailed;
volatile uintptr_t gcIsNeeded; volatile uintptr_t gcIsNeeded;
js::WeakMapBase *gcWeakMapList; js::WeakMapBase *gcWeakMapList;
js::gcstats::Statistics gcStats; js::gcstats::Statistics gcStats;
/* Incremented on every GC slice. */
uint64_t gcNumber;
/* The gcNumber at the time of the most recent GC's first slice. */
uint64_t gcStartNumber;
/* The reason that an interrupt-triggered GC should be called. */ /* The reason that an interrupt-triggered GC should be called. */
js::gcreason::Reason gcTriggerReason; js::gcreason::Reason gcTriggerReason;
/* Pre-allocated space for the GC mark stack. */
uintptr_t gcMarkStackArray[js::MARK_STACK_LENGTH];
/* /*
* Compartment that triggered GC. If more than one Compatment need GC, * Compartment that triggered GC. If more than one Compatment need GC,
* gcTriggerCompartment is reset to NULL and a global GC is performed. * gcTriggerCompartment is reset to NULL and a global GC is performed.
@ -337,6 +338,53 @@ struct JSRuntime : js::RuntimeFriendFields
*/ */
JSCompartment *gcCheckCompartment; JSCompartment *gcCheckCompartment;
/*
* The current incremental GC phase. During non-incremental GC, this is
* always NO_INCREMENTAL.
*/
js::gc::State gcIncrementalState;
/* Indicates that a new compartment was created during incremental GC. */
bool gcCompartmentCreated;
/* Indicates that the last incremental slice exhausted the mark stack. */
bool gcLastMarkSlice;
/*
* Indicates that a GC slice has taken place in the middle of an animation
* frame, rather than at the beginning. In this case, the next slice will be
* delayed so that we don't get back-to-back slices.
*/
volatile uintptr_t gcInterFrameGC;
/* Default budget for incremental GC slice. See SliceBudget in jsgc.h. */
int64_t gcSliceBudget;
/*
* We disable incremental GC if we encounter a js::Class with a trace hook
* that does not implement write barriers.
*/
bool gcIncrementalEnabled;
/* Compartment that is undergoing an incremental GC. */
JSCompartment *gcIncrementalCompartment;
/*
* We save all conservative scanned roots in this vector so that
* conservative scanning can be "replayed" deterministically. In DEBUG mode,
* this allows us to run a non-incremental GC after every incremental GC to
* ensure that no objects were missed.
*/
#ifdef DEBUG
struct SavedGCRoot {
void *thing;
JSGCTraceKind kind;
SavedGCRoot(void *thing, JSGCTraceKind kind) : thing(thing), kind(kind) {}
};
js::Vector<SavedGCRoot, 0, js::SystemAllocPolicy> gcSavedRoots;
#endif
/* /*
* We can pack these flags as only the GC thread writes to them. Atomic * We can pack these flags as only the GC thread writes to them. Atomic
* updates to packed bytes are not guaranteed, so stores issued by one * updates to packed bytes are not guaranteed, so stores issued by one
@ -344,7 +392,6 @@ struct JSRuntime : js::RuntimeFriendFields
* other threads. * other threads.
*/ */
bool gcPoke; bool gcPoke;
bool gcMarkAndSweep;
bool gcRunning; bool gcRunning;
/* /*
@ -353,7 +400,7 @@ struct JSRuntime : js::RuntimeFriendFields
* gcNextScheduled is decremented. When it reaches zero, we do either a * gcNextScheduled is decremented. When it reaches zero, we do either a
* full or a compartmental GC, based on gcDebugCompartmentGC. * full or a compartmental GC, based on gcDebugCompartmentGC.
* *
* At this point, if gcZeal_ >= 2 then gcNextScheduled is reset to the * At this point, if gcZeal_ == 2 then gcNextScheduled is reset to the
* value of gcZealFrequency. Otherwise, no additional GCs take place. * value of gcZealFrequency. Otherwise, no additional GCs take place.
* *
* You can control these values in several ways: * You can control these values in several ways:
@ -361,9 +408,8 @@ struct JSRuntime : js::RuntimeFriendFields
* - Call gczeal() or schedulegc() from inside shell-executed JS code * - Call gczeal() or schedulegc() from inside shell-executed JS code
* (see the help for details) * (see the help for details)
* *
* Additionally, if gzZeal_ == 1 then we perform GCs in select places * If gzZeal_ == 1 then we perform GCs in select places (during MaybeGC and
* (during MaybeGC and whenever a GC poke happens). This option is mainly * whenever a GC poke happens). This option is mainly useful to embedders.
* useful to embedders.
* *
* We use gcZeal_ == 4 to enable write barrier verification. See the comment * We use gcZeal_ == 4 to enable write barrier verification. See the comment
* in jsgc.cpp for more information about this. * in jsgc.cpp for more information about this.
@ -378,7 +424,7 @@ struct JSRuntime : js::RuntimeFriendFields
bool needZealousGC() { bool needZealousGC() {
if (gcNextScheduled > 0 && --gcNextScheduled == 0) { if (gcNextScheduled > 0 && --gcNextScheduled == 0) {
if (gcZeal() >= js::gc::ZealAllocThreshold && gcZeal() < js::gc::ZealVerifierThreshold) if (gcZeal() == js::gc::ZealAllocValue)
gcNextScheduled = gcZealFrequency; gcNextScheduled = gcZealFrequency;
return true; return true;
} }
@ -390,7 +436,7 @@ struct JSRuntime : js::RuntimeFriendFields
#endif #endif
JSGCCallback gcCallback; JSGCCallback gcCallback;
JSGCFinishedCallback gcFinishedCallback; js::GCSliceCallback gcSliceCallback;
private: private:
/* /*

Просмотреть файл

@ -73,7 +73,6 @@ JSCompartment::JSCompartment(JSRuntime *rt)
: rt(rt), : rt(rt),
principals(NULL), principals(NULL),
needsBarrier_(false), needsBarrier_(false),
gcIncrementalTracer(NULL),
gcBytes(0), gcBytes(0),
gcTriggerBytes(0), gcTriggerBytes(0),
gcLastBytes(0), gcLastBytes(0),
@ -128,6 +127,9 @@ JSCompartment::init(JSContext *cx)
if (!scriptFilenameTable.init()) if (!scriptFilenameTable.init())
return false; return false;
if (!barrierMarker_.init())
return false;
return debuggees.init(); return debuggees.init();
} }
@ -435,7 +437,8 @@ JSCompartment::markTypes(JSTracer *trc)
for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) { for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
JSScript *script = i.get<JSScript>(); JSScript *script = i.get<JSScript>();
MarkScriptRoot(trc, script, "mark_types_script"); MarkScriptRoot(trc, &script, "mark_types_script");
JS_ASSERT(script == i.get<JSScript>());
} }
for (size_t thingKind = FINALIZE_OBJECT0; for (size_t thingKind = FINALIZE_OBJECT0;
@ -443,46 +446,23 @@ JSCompartment::markTypes(JSTracer *trc)
thingKind++) { thingKind++) {
for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) { for (CellIterUnderGC i(this, AllocKind(thingKind)); !i.done(); i.next()) {
JSObject *object = i.get<JSObject>(); JSObject *object = i.get<JSObject>();
if (object->hasSingletonType()) if (object->hasSingletonType()) {
MarkObjectRoot(trc, object, "mark_types_singleton"); MarkObjectRoot(trc, &object, "mark_types_singleton");
JS_ASSERT(object == i.get<JSObject>());
}
} }
} }
for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) for (CellIterUnderGC i(this, FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
MarkTypeObjectRoot(trc, i.get<types::TypeObject>(), "mark_types_scan"); types::TypeObject *type = i.get<types::TypeObject>();
MarkTypeObjectRoot(trc, &type, "mark_types_scan");
JS_ASSERT(type == i.get<types::TypeObject>());
}
} }
void void
JSCompartment::sweep(JSContext *cx, bool releaseTypes) JSCompartment::discardJitCode(JSContext *cx)
{ {
/* Remove dead wrappers from the table. */
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key) &&
!IsAboutToBeFinalized(e.front().value),
e.front().key.isString());
if (IsAboutToBeFinalized(e.front().key) ||
IsAboutToBeFinalized(e.front().value)) {
e.removeFront();
}
}
/* Remove dead references held weakly by the compartment. */
sweepBaseShapeTable(cx);
sweepInitialShapeTable(cx);
sweepNewTypeObjectTable(cx, newTypeObjects);
sweepNewTypeObjectTable(cx, lazyTypeObjects);
if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject))
emptyTypeObject = NULL;
newObjectCache.reset();
sweepBreakpoints(cx);
{
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
/* /*
* Kick all frames on the stack into the interpreter, and release all JIT * Kick all frames on the stack into the interpreter, and release all JIT
* code in the compartment. * code in the compartment.
@ -504,6 +484,41 @@ JSCompartment::sweep(JSContext *cx, bool releaseTypes)
#endif #endif
} }
void
JSCompartment::sweep(JSContext *cx, bool releaseTypes)
{
/* Remove dead wrappers from the table. */
for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
JS_ASSERT_IF(IsAboutToBeFinalized(e.front().key) &&
!IsAboutToBeFinalized(e.front().value),
e.front().key.isString());
if (IsAboutToBeFinalized(e.front().key) ||
IsAboutToBeFinalized(e.front().value)) {
e.removeFront();
}
}
/* Remove dead references held weakly by the compartment. */
regExps.sweep(rt);
sweepBaseShapeTable(cx);
sweepInitialShapeTable(cx);
sweepNewTypeObjectTable(cx, newTypeObjects);
sweepNewTypeObjectTable(cx, lazyTypeObjects);
if (emptyTypeObject && IsAboutToBeFinalized(emptyTypeObject))
emptyTypeObject = NULL;
newObjectCache.reset();
sweepBreakpoints(cx);
{
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_CODE);
discardJitCode(cx);
}
if (!activeAnalysis) { if (!activeAnalysis) {
gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS); gcstats::AutoPhase ap(rt->gcStats, gcstats::PHASE_DISCARD_ANALYSIS);
@ -555,8 +570,6 @@ JSCompartment::sweep(JSContext *cx, bool releaseTypes)
void void
JSCompartment::purge(JSContext *cx) JSCompartment::purge(JSContext *cx)
{ {
arenas.purge();
regExps.purge();
dtoaCache.purge(); dtoaCache.purge();
/* /*
@ -770,13 +783,6 @@ JSCompartment::sweepBreakpoints(JSContext *cx)
} }
} }
GCMarker *
JSCompartment::createBarrierTracer()
{
JS_ASSERT(!gcIncrementalTracer);
return NULL;
}
size_t size_t
JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf) JSCompartment::sizeOfShapeTable(JSMallocSizeOfFun mallocSizeOf)
{ {

Просмотреть файл

@ -46,7 +46,6 @@
#include "jscntxt.h" #include "jscntxt.h"
#include "jsfun.h" #include "jsfun.h"
#include "jsgc.h" #include "jsgc.h"
#include "jsgcstats.h"
#include "jsobj.h" #include "jsobj.h"
#include "jsscope.h" #include "jsscope.h"
#include "vm/GlobalObject.h" #include "vm/GlobalObject.h"
@ -163,6 +162,23 @@ typedef HashSet<ScriptFilenameEntry *,
ScriptFilenameHasher, ScriptFilenameHasher,
SystemAllocPolicy> ScriptFilenameTable; SystemAllocPolicy> ScriptFilenameTable;
/* If HashNumber grows, need to change WrapperHasher. */
JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
struct WrapperHasher
{
typedef Value Lookup;
static HashNumber hash(Value key) {
uint64_t bits = JSVAL_TO_IMPL(key).asBits;
return uint32_t(bits) ^ uint32_t(bits >> 32);
}
static bool match(const Value &l, const Value &k) { return l == k; }
};
typedef HashMap<Value, ReadBarrieredValue, WrapperHasher, SystemAllocPolicy> WrapperMap;
} /* namespace js */ } /* namespace js */
namespace JS { namespace JS {
@ -177,7 +193,7 @@ struct JSCompartment
js::gc::ArenaLists arenas; js::gc::ArenaLists arenas;
bool needsBarrier_; bool needsBarrier_;
js::GCMarker *gcIncrementalTracer; js::BarrierGCMarker barrierMarker_;
bool needsBarrier() { bool needsBarrier() {
return needsBarrier_; return needsBarrier_;
@ -185,9 +201,7 @@ struct JSCompartment
js::GCMarker *barrierTracer() { js::GCMarker *barrierTracer() {
JS_ASSERT(needsBarrier_); JS_ASSERT(needsBarrier_);
if (gcIncrementalTracer) return &barrierMarker_;
return gcIncrementalTracer;
return createBarrierTracer();
} }
size_t gcBytes; size_t gcBytes;
@ -325,10 +339,11 @@ struct JSCompartment
bool wrap(JSContext *cx, js::AutoIdVector &props); bool wrap(JSContext *cx, js::AutoIdVector &props);
void markTypes(JSTracer *trc); void markTypes(JSTracer *trc);
void discardJitCode(JSContext *cx);
void sweep(JSContext *cx, bool releaseTypes); void sweep(JSContext *cx, bool releaseTypes);
void purge(JSContext *cx); void purge(JSContext *cx);
void setGCLastBytes(size_t lastBytes, JSGCInvocationKind gckind); void setGCLastBytes(size_t lastBytes, js::JSGCInvocationKind gckind);
void reduceGCTriggerBytes(size_t amount); void reduceGCTriggerBytes(size_t amount);
void resetGCMallocBytes(); void resetGCMallocBytes();
@ -397,8 +412,6 @@ struct JSCompartment
private: private:
void sweepBreakpoints(JSContext *cx); void sweepBreakpoints(JSContext *cx);
js::GCMarker *createBarrierTracer();
public: public:
js::WatchpointMap *watchpointMap; js::WatchpointMap *watchpointMap;
}; };

Просмотреть файл

@ -94,7 +94,7 @@ exn_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
Class js::ErrorClass = { Class js::ErrorClass = {
js_Error_str, js_Error_str,
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_NEW_RESOLVE |
JSCLASS_HAS_CACHED_PROTO(JSProto_Error), JSCLASS_HAS_CACHED_PROTO(JSProto_Error),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
@ -419,14 +419,14 @@ exn_trace(JSTracer *trc, JSObject *obj)
priv = GetExnPrivate(obj); priv = GetExnPrivate(obj);
if (priv) { if (priv) {
if (priv->message) if (priv->message)
MarkString(trc, priv->message, "exception message"); MarkString(trc, &priv->message, "exception message");
if (priv->filename) if (priv->filename)
MarkString(trc, priv->filename, "exception filename"); MarkString(trc, &priv->filename, "exception filename");
elem = priv->stackElems; elem = priv->stackElems;
for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) { for (vcount = i = 0; i != priv->stackDepth; ++i, ++elem) {
if (elem->funName) if (elem->funName)
MarkString(trc, elem->funName, "stack trace function name"); MarkString(trc, &elem->funName, "stack trace function name");
if (IS_GC_MARKING_TRACER(trc) && elem->filename) if (IS_GC_MARKING_TRACER(trc) && elem->filename)
js_MarkScriptFilename(elem->filename); js_MarkScriptFilename(elem->filename);
vcount += elem->argc; vcount += elem->argc;

Просмотреть файл

@ -132,7 +132,7 @@ JS_NewObjectWithUniqueType(JSContext *cx, JSClass *clasp, JSObject *proto, JSObj
JS_FRIEND_API(void) JS_FRIEND_API(void)
js::GCForReason(JSContext *cx, gcreason::Reason reason) js::GCForReason(JSContext *cx, gcreason::Reason reason)
{ {
js_GC(cx, NULL, GC_NORMAL, reason); GC(cx, NULL, GC_NORMAL, reason);
} }
JS_FRIEND_API(void) JS_FRIEND_API(void)
@ -141,13 +141,19 @@ js::CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason
/* We cannot GC the atoms compartment alone; use a full GC instead. */ /* We cannot GC the atoms compartment alone; use a full GC instead. */
JS_ASSERT(comp != cx->runtime->atomsCompartment); JS_ASSERT(comp != cx->runtime->atomsCompartment);
js_GC(cx, comp, GC_NORMAL, reason); GC(cx, comp, GC_NORMAL, reason);
} }
JS_FRIEND_API(void) JS_FRIEND_API(void)
js::ShrinkingGC(JSContext *cx, gcreason::Reason reason) js::ShrinkingGC(JSContext *cx, gcreason::Reason reason)
{ {
js_GC(cx, NULL, GC_SHRINK, reason); GC(cx, NULL, GC_SHRINK, reason);
}
JS_FRIEND_API(void)
js::IncrementalGC(JSContext *cx, gcreason::Reason reason)
{
GCSlice(cx, NULL, GC_NORMAL, reason);
} }
JS_FRIEND_API(void) JS_FRIEND_API(void)
@ -171,7 +177,7 @@ JS_WrapPropertyDescriptor(JSContext *cx, js::PropertyDescriptor *desc)
JS_FRIEND_API(void) JS_FRIEND_API(void)
JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape) JS_TraceShapeCycleCollectorChildren(JSTracer *trc, void *shape)
{ {
MarkCycleCollectorChildren(trc, (const Shape *)shape); MarkCycleCollectorChildren(trc, (Shape *)shape);
} }
AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx AutoPreserveCompartment::AutoPreserveCompartment(JSContext *cx
@ -401,12 +407,6 @@ JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallba
rt->telemetryCallback = callback; rt->telemetryCallback = callback;
} }
JS_FRIEND_API(void)
JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback)
{
rt->gcFinishedCallback = callback;
}
#ifdef DEBUG #ifdef DEBUG
JS_FRIEND_API(void) JS_FRIEND_API(void)
js_DumpString(JSString *str) js_DumpString(JSString *str)
@ -551,39 +551,6 @@ js::DumpHeapComplete(JSContext *cx, FILE *fp)
namespace js { namespace js {
JS_FRIEND_API(bool)
IsIncrementalBarrierNeeded(JSRuntime *rt)
{
return !!rt->gcIncrementalTracer && !rt->gcRunning;
}
JS_FRIEND_API(bool)
IsIncrementalBarrierNeeded(JSContext *cx)
{
return IsIncrementalBarrierNeeded(cx->runtime);
}
extern JS_FRIEND_API(void)
IncrementalReferenceBarrier(void *ptr)
{
if (!ptr)
return;
JS_ASSERT(!static_cast<gc::Cell *>(ptr)->compartment()->rt->gcRunning);
uint32_t kind = gc::GetGCThingTraceKind(ptr);
if (kind == JSTRACE_OBJECT)
JSObject::writeBarrierPre((JSObject *) ptr);
else if (kind == JSTRACE_STRING)
JSString::writeBarrierPre((JSString *) ptr);
else
JS_NOT_REACHED("invalid trace kind");
}
extern JS_FRIEND_API(void)
IncrementalValueBarrier(const Value &v)
{
HeapValue::writeBarrierPre(v);
}
/* static */ void /* static */ void
AutoLockGC::LockGC(JSRuntime *rt) AutoLockGC::LockGC(JSRuntime *rt)
{ {
@ -719,4 +686,90 @@ SizeOfJSContext()
return sizeof(JSContext); return sizeof(JSContext);
} }
JS_FRIEND_API(GCSliceCallback)
SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback)
{
GCSliceCallback old = rt->gcSliceCallback;
rt->gcSliceCallback = callback;
return old;
}
JS_FRIEND_API(bool)
WantGCSlice(JSRuntime *rt)
{
if (rt->gcZeal() == gc::ZealFrameVerifierValue || rt->gcZeal() == gc::ZealFrameGCValue)
return true;
if (rt->gcIncrementalState != gc::NO_INCREMENTAL)
return true;
return false;
}
JS_FRIEND_API(void)
NotifyDidPaint(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
if (rt->gcZeal() == gc::ZealFrameVerifierValue) {
gc::VerifyBarriers(cx);
return;
}
if (rt->gcZeal() == gc::ZealFrameGCValue) {
GCSlice(cx, NULL, GC_NORMAL, gcreason::REFRESH_FRAME);
return;
}
if (rt->gcIncrementalState != gc::NO_INCREMENTAL && !rt->gcInterFrameGC)
GCSlice(cx, rt->gcIncrementalCompartment, GC_NORMAL, gcreason::REFRESH_FRAME);
rt->gcInterFrameGC = false;
}
extern JS_FRIEND_API(bool)
IsIncrementalGCEnabled(JSRuntime *rt)
{
return rt->gcIncrementalEnabled;
}
JS_FRIEND_API(bool)
IsIncrementalBarrierNeeded(JSRuntime *rt)
{
return (rt->gcIncrementalState == gc::MARK && !rt->gcRunning);
}
JS_FRIEND_API(bool)
IsIncrementalBarrierNeeded(JSContext *cx)
{
return IsIncrementalBarrierNeeded(cx->runtime);
}
JS_FRIEND_API(bool)
IsIncrementalBarrierNeededOnObject(JSObject *obj)
{
return obj->compartment()->needsBarrier();
}
extern JS_FRIEND_API(void)
IncrementalReferenceBarrier(void *ptr)
{
if (!ptr)
return;
JS_ASSERT(!static_cast<gc::Cell *>(ptr)->compartment()->rt->gcRunning);
uint32_t kind = gc::GetGCThingTraceKind(ptr);
if (kind == JSTRACE_OBJECT)
JSObject::writeBarrierPre((JSObject *) ptr);
else if (kind == JSTRACE_STRING)
JSString::writeBarrierPre((JSString *) ptr);
else
JS_NOT_REACHED("invalid trace kind");
}
extern JS_FRIEND_API(void)
IncrementalValueBarrier(const Value &v)
{
HeapValue::writeBarrierPre(v);
}
} // namespace js } // namespace js

Просмотреть файл

@ -100,7 +100,11 @@ enum {
JS_TELEMETRY_GC_IS_COMPARTMENTAL, JS_TELEMETRY_GC_IS_COMPARTMENTAL,
JS_TELEMETRY_GC_MS, JS_TELEMETRY_GC_MS,
JS_TELEMETRY_GC_MARK_MS, JS_TELEMETRY_GC_MARK_MS,
JS_TELEMETRY_GC_SWEEP_MS JS_TELEMETRY_GC_SWEEP_MS,
JS_TELEMETRY_GC_SLICE_MS,
JS_TELEMETRY_GC_MMU_50,
JS_TELEMETRY_GC_RESET,
JS_TELEMETRY_GC_INCREMENTAL_DISABLED
}; };
typedef void typedef void
@ -109,12 +113,6 @@ typedef void
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback); JS_SetAccumulateTelemetryCallback(JSRuntime *rt, JSAccumulateTelemetryDataCallback callback);
typedef void
(* JSGCFinishedCallback)(JSRuntime *rt, JSCompartment *comp, const char *description);
extern JS_FRIEND_API(void)
JS_SetGCFinishedCallback(JSRuntime *rt, JSGCFinishedCallback callback);
extern JS_FRIEND_API(JSPrincipals *) extern JS_FRIEND_API(JSPrincipals *)
JS_GetCompartmentPrincipals(JSCompartment *compartment); JS_GetCompartmentPrincipals(JSCompartment *compartment);
@ -703,12 +701,65 @@ CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason reas
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
ShrinkingGC(JSContext *cx, gcreason::Reason reason); ShrinkingGC(JSContext *cx, gcreason::Reason reason);
extern JS_FRIEND_API(void)
IncrementalGC(JSContext *cx, gcreason::Reason reason);
extern JS_FRIEND_API(void)
SetGCSliceTimeBudget(JSContext *cx, int64_t millis);
enum GCProgress {
/*
* During non-incremental GC, the GC is bracketed by JSGC_CYCLE_BEGIN/END
* callbacks. During an incremental GC, the sequence of callbacks is as
* follows:
* JSGC_CYCLE_BEGIN, JSGC_SLICE_END (first slice)
* JSGC_SLICE_BEGIN, JSGC_SLICE_END (second slice)
* ...
* JSGC_SLICE_BEGIN, JSGC_CYCLE_END (last slice)
*/
GC_CYCLE_BEGIN,
GC_SLICE_BEGIN,
GC_SLICE_END,
GC_CYCLE_END
};
struct GCDescription {
const char *logMessage;
bool isCompartment;
GCDescription(const char *msg, bool isCompartment)
: logMessage(msg), isCompartment(isCompartment) {}
};
typedef void
(* GCSliceCallback)(JSRuntime *rt, GCProgress progress, const GCDescription &desc);
extern JS_FRIEND_API(GCSliceCallback)
SetGCSliceCallback(JSRuntime *rt, GCSliceCallback callback);
extern JS_FRIEND_API(bool)
WantGCSlice(JSRuntime *rt);
/*
* Signals a good place to do an incremental slice, because the browser is
* drawing a frame.
*/
extern JS_FRIEND_API(void)
NotifyDidPaint(JSContext *cx);
extern JS_FRIEND_API(bool)
IsIncrementalGCEnabled(JSRuntime *rt);
extern JS_FRIEND_API(bool) extern JS_FRIEND_API(bool)
IsIncrementalBarrierNeeded(JSRuntime *rt); IsIncrementalBarrierNeeded(JSRuntime *rt);
extern JS_FRIEND_API(bool) extern JS_FRIEND_API(bool)
IsIncrementalBarrierNeeded(JSContext *cx); IsIncrementalBarrierNeeded(JSContext *cx);
extern JS_FRIEND_API(bool)
IsIncrementalBarrierNeededOnObject(JSObject *obj);
extern JS_FRIEND_API(void) extern JS_FRIEND_API(void)
IncrementalReferenceBarrier(void *ptr); IncrementalReferenceBarrier(void *ptr);

Просмотреть файл

@ -539,7 +539,7 @@ args_trace(JSTracer *trc, JSObject *obj)
#if JS_HAS_GENERATORS #if JS_HAS_GENERATORS
StackFrame *fp = argsobj.maybeStackFrame(); StackFrame *fp = argsobj.maybeStackFrame();
if (fp && fp->isFloatingGenerator()) if (fp && fp->isFloatingGenerator())
MarkObject(trc, js_FloatingFrameToGenerator(fp)->obj, "generator object"); MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
#endif #endif
} }
@ -551,7 +551,7 @@ args_trace(JSTracer *trc, JSObject *obj)
*/ */
Class js::NormalArgumentsObjectClass = { Class js::NormalArgumentsObjectClass = {
"Arguments", "Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) | JSCLASS_HAS_RESERVED_SLOTS(NormalArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION, JSCLASS_FOR_OF_ITERATION,
@ -587,7 +587,7 @@ Class js::NormalArgumentsObjectClass = {
*/ */
Class js::StrictArgumentsObjectClass = { Class js::StrictArgumentsObjectClass = {
"Arguments", "Arguments",
JSCLASS_NEW_RESOLVE | JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) | JSCLASS_HAS_RESERVED_SLOTS(StrictArgumentsObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object) | JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
JSCLASS_FOR_OF_ITERATION, JSCLASS_FOR_OF_ITERATION,
@ -936,13 +936,13 @@ call_trace(JSTracer *trc, JSObject *obj)
#if JS_HAS_GENERATORS #if JS_HAS_GENERATORS
StackFrame *fp = (StackFrame *) obj->getPrivate(); StackFrame *fp = (StackFrame *) obj->getPrivate();
if (fp && fp->isFloatingGenerator()) if (fp && fp->isFloatingGenerator())
MarkObject(trc, js_FloatingFrameToGenerator(fp)->obj, "generator object"); MarkObject(trc, &js_FloatingFrameToGenerator(fp)->obj, "generator object");
#endif #endif
} }
JS_PUBLIC_DATA(Class) js::CallClass = { JS_PUBLIC_DATA(Class) js::CallClass = {
"Call", "Call",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) | JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) |
JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS, JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
@ -1465,7 +1465,7 @@ JSFunction::trace(JSTracer *trc)
if (isInterpreted()) { if (isInterpreted()) {
if (script()) if (script())
MarkScript(trc, script(), "script"); MarkScript(trc, &script(), "script");
if (environment()) if (environment())
MarkObjectUnbarriered(trc, environment(), "fun_callscope"); MarkObjectUnbarriered(trc, environment(), "fun_callscope");
} }
@ -1499,7 +1499,7 @@ JSFunction::sizeOfMisc(JSMallocSizeOfFun mallocSizeOf) const
*/ */
JS_FRIEND_DATA(Class) js::FunctionClass = { JS_FRIEND_DATA(Class) js::FunctionClass = {
js_Function_str, js_Function_str,
JSCLASS_NEW_RESOLVE | JSCLASS_NEW_RESOLVE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Function), JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -55,7 +55,6 @@
#include "jslock.h" #include "jslock.h"
#include "jsutil.h" #include "jsutil.h"
#include "jsversion.h" #include "jsversion.h"
#include "jsgcstats.h"
#include "jscell.h" #include "jscell.h"
#include "ds/BitArray.h" #include "ds/BitArray.h"
@ -82,6 +81,14 @@ struct Shape;
namespace gc { namespace gc {
enum State {
NO_INCREMENTAL,
MARK_ROOTS,
MARK,
SWEEP,
INVALID
};
struct Arena; struct Arena;
/* /*
@ -419,6 +426,10 @@ struct ArenaHeader {
* not present in the stack we use an extra flag to tag arenas on the * not present in the stack we use an extra flag to tag arenas on the
* stack. * stack.
* *
* Delayed marking is also used for arenas that we allocate into during an
* incremental GC. In this case, we intend to mark all the objects in the
* arena, and it's faster to do this marking in bulk.
*
* To minimize the ArenaHeader size we record the next delayed marking * To minimize the ArenaHeader size we record the next delayed marking
* linkage as arenaAddress() >> ArenaShift and pack it with the allocKind * linkage as arenaAddress() >> ArenaShift and pack it with the allocKind
* field and hasDelayedMarking flag. We use 8 bits for the allocKind, not * field and hasDelayedMarking flag. We use 8 bits for the allocKind, not
@ -427,7 +438,9 @@ struct ArenaHeader {
*/ */
public: public:
size_t hasDelayedMarking : 1; size_t hasDelayedMarking : 1;
size_t nextDelayedMarking : JS_BITS_PER_WORD - 8 - 1; size_t allocatedDuringIncremental : 1;
size_t markOverflow : 1;
size_t nextDelayedMarking : JS_BITS_PER_WORD - 8 - 1 - 1 - 1;
static void staticAsserts() { static void staticAsserts() {
/* We must be able to fit the allockind into uint8_t. */ /* We must be able to fit the allockind into uint8_t. */
@ -437,7 +450,7 @@ struct ArenaHeader {
* nextDelayedMarkingpacking assumes that ArenaShift has enough bits * nextDelayedMarkingpacking assumes that ArenaShift has enough bits
* to cover allocKind and hasDelayedMarking. * to cover allocKind and hasDelayedMarking.
*/ */
JS_STATIC_ASSERT(ArenaShift >= 8 + 1); JS_STATIC_ASSERT(ArenaShift >= 8 + 1 + 1 + 1);
} }
inline uintptr_t address() const; inline uintptr_t address() const;
@ -450,6 +463,8 @@ struct ArenaHeader {
void init(JSCompartment *comp, AllocKind kind) { void init(JSCompartment *comp, AllocKind kind) {
JS_ASSERT(!allocated()); JS_ASSERT(!allocated());
JS_ASSERT(!markOverflow);
JS_ASSERT(!allocatedDuringIncremental);
JS_ASSERT(!hasDelayedMarking); JS_ASSERT(!hasDelayedMarking);
compartment = comp; compartment = comp;
@ -462,6 +477,8 @@ struct ArenaHeader {
void setAsNotAllocated() { void setAsNotAllocated() {
allocKind = size_t(FINALIZE_LIMIT); allocKind = size_t(FINALIZE_LIMIT);
markOverflow = 0;
allocatedDuringIncremental = 0;
hasDelayedMarking = 0; hasDelayedMarking = 0;
nextDelayedMarking = 0; nextDelayedMarking = 0;
} }
@ -507,8 +524,8 @@ struct ArenaHeader {
void checkSynchronizedWithFreeList() const; void checkSynchronizedWithFreeList() const;
#endif #endif
inline Arena *getNextDelayedMarking() const; inline ArenaHeader *getNextDelayedMarking() const;
inline void setNextDelayedMarking(Arena *arena); inline void setNextDelayedMarking(ArenaHeader *aheader);
}; };
struct Arena { struct Arena {
@ -908,25 +925,24 @@ ArenaHeader::getThingSize() const
return Arena::thingSize(getAllocKind()); return Arena::thingSize(getAllocKind());
} }
inline Arena * inline ArenaHeader *
ArenaHeader::getNextDelayedMarking() const ArenaHeader::getNextDelayedMarking() const
{ {
return reinterpret_cast<Arena *>(nextDelayedMarking << ArenaShift); return &reinterpret_cast<Arena *>(nextDelayedMarking << ArenaShift)->aheader;
} }
inline void inline void
ArenaHeader::setNextDelayedMarking(Arena *arena) ArenaHeader::setNextDelayedMarking(ArenaHeader *aheader)
{ {
JS_ASSERT(!hasDelayedMarking); JS_ASSERT(!(uintptr_t(aheader) & ArenaMask));
hasDelayedMarking = 1; hasDelayedMarking = 1;
nextDelayedMarking = arena->address() >> ArenaShift; nextDelayedMarking = aheader->arenaAddress() >> ArenaShift;
} }
JS_ALWAYS_INLINE void JS_ALWAYS_INLINE void
ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32_t color, ChunkBitmap::getMarkWordAndMask(const Cell *cell, uint32_t color,
uintptr_t **wordp, uintptr_t *maskp) uintptr_t **wordp, uintptr_t *maskp)
{ {
JS_ASSERT(cell->chunk() == Chunk::fromAddress(reinterpret_cast<uintptr_t>(this)));
size_t bit = (cell->address() & ChunkMask) / Cell::CellSize + color; size_t bit = (cell->address() & ChunkMask) / Cell::CellSize + color;
JS_ASSERT(bit < ArenaBitmapBits * ArenasPerChunk); JS_ASSERT(bit < ArenaBitmapBits * ArenasPerChunk);
*maskp = uintptr_t(1) << (bit % JS_BITS_PER_WORD); *maskp = uintptr_t(1) << (bit % JS_BITS_PER_WORD);
@ -970,21 +986,6 @@ Cell::compartment() const
return arenaHeader()->compartment; return arenaHeader()->compartment;
} }
/*
* Lower limit after which we limit the heap growth
*/
const size_t GC_ALLOCATION_THRESHOLD = 30 * 1024 * 1024;
/*
* A GC is triggered once the number of newly allocated arenas is
* GC_HEAP_GROWTH_FACTOR times the number of live arenas after the last GC
* starting after the lower limit of GC_ALLOCATION_THRESHOLD.
*/
const float GC_HEAP_GROWTH_FACTOR = 3.0f;
/* Perform a Full GC every 20 seconds if MaybeGC is called */
static const int64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
static inline JSGCTraceKind static inline JSGCTraceKind
MapAllocToTraceKind(AllocKind thingKind) MapAllocToTraceKind(AllocKind thingKind)
{ {
@ -1168,13 +1169,14 @@ struct ArenaLists {
FreeSpan *headSpan = &freeLists[i]; FreeSpan *headSpan = &freeLists[i];
if (!headSpan->isEmpty()) { if (!headSpan->isEmpty()) {
ArenaHeader *aheader = headSpan->arenaHeader(); ArenaHeader *aheader = headSpan->arenaHeader();
JS_ASSERT(!aheader->hasFreeThings());
aheader->setFirstFreeSpan(headSpan); aheader->setFirstFreeSpan(headSpan);
headSpan->initAsEmpty(); headSpan->initAsEmpty();
} }
} }
} }
inline void prepareForIncrementalGC(JSCompartment *comp);
/* /*
* Temporarily copy the free list heads to the arenas so the code can see * Temporarily copy the free list heads to the arenas so the code can see
* the proper value in ArenaHeader::freeList when accessing the latter * the proper value in ArenaHeader::freeList when accessing the latter
@ -1309,23 +1311,6 @@ typedef js::HashMap<void *,
js::DefaultHasher<void *>, js::DefaultHasher<void *>,
js::SystemAllocPolicy> RootedValueMap; js::SystemAllocPolicy> RootedValueMap;
/* If HashNumber grows, need to change WrapperHasher. */
JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
struct WrapperHasher
{
typedef Value Lookup;
static HashNumber hash(Value key) {
uint64_t bits = JSVAL_TO_IMPL(key).asBits;
return uint32_t(bits) ^ uint32_t(bits >> 32);
}
static bool match(const Value &l, const Value &k) { return l == k; }
};
typedef HashMap<Value, Value, WrapperHasher, SystemAllocPolicy> WrapperMap;
} /* namespace js */ } /* namespace js */
extern JS_FRIEND_API(JSGCTraceKind) extern JS_FRIEND_API(JSGCTraceKind)
@ -1376,6 +1361,9 @@ js_IsAddressableGCThing(JSRuntime *rt, uintptr_t w, js::gc::AllocKind *thingKind
namespace js { namespace js {
extern void
MarkCompartmentActive(js::StackFrame *fp);
extern void extern void
TraceRuntime(JSTracer *trc); TraceRuntime(JSTracer *trc);
@ -1396,8 +1384,6 @@ MaybeGC(JSContext *cx);
extern void extern void
ShrinkGCBuffers(JSRuntime *rt); ShrinkGCBuffers(JSRuntime *rt);
} /* namespace js */
/* /*
* Kinds of js_GC invocation. * Kinds of js_GC invocation.
*/ */
@ -1411,10 +1397,21 @@ typedef enum JSGCInvocationKind {
/* Pass NULL for |comp| to get a full GC. */ /* Pass NULL for |comp| to get a full GC. */
extern void extern void
js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason r); GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason reason);
extern void
GCSlice(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind, js::gcreason::Reason reason);
extern void
GCDebugSlice(JSContext *cx, int64_t objCount);
} /* namespace js */
namespace js { namespace js {
void
InitTracer(JSTracer *trc, JSRuntime *rt, JSContext *cx, JSTraceCallback callback);
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
class GCHelperThread { class GCHelperThread {
@ -1572,17 +1569,56 @@ struct MarkStack {
T *tos; T *tos;
T *limit; T *limit;
bool push(T item) { T *ballast;
if (tos == limit) T *ballastLimit;
MarkStack()
: stack(NULL),
tos(NULL),
limit(NULL),
ballast(NULL),
ballastLimit(NULL) { }
~MarkStack() {
if (stack != ballast)
js_free(stack);
js_free(ballast);
}
bool init(size_t ballastcap) {
JS_ASSERT(!stack);
if (ballastcap == 0)
return true;
ballast = (T *)js_malloc(sizeof(T) * ballastcap);
if (!ballast)
return false; return false;
ballastLimit = ballast + ballastcap;
stack = ballast;
limit = ballastLimit;
tos = stack;
return true;
}
bool push(T item) {
if (tos == limit) {
if (!enlarge())
return false;
}
JS_ASSERT(tos < limit);
*tos++ = item; *tos++ = item;
return true; return true;
} }
bool push(T item1, T item2, T item3) { bool push(T item1, T item2, T item3) {
T *nextTos = tos + 3; T *nextTos = tos + 3;
if (nextTos > limit) if (nextTos > limit) {
if (!enlarge())
return false; return false;
nextTos = tos + 3;
}
JS_ASSERT(nextTos <= limit);
tos[0] = item1; tos[0] = item1;
tos[1] = item2; tos[1] = item2;
tos[2] = item3; tos[2] = item3;
@ -1599,61 +1635,130 @@ struct MarkStack {
return *--tos; return *--tos;
} }
template<size_t N> ptrdiff_t position() const {
MarkStack(T (&buffer)[N]) return tos - stack;
: stack(buffer), }
tos(buffer),
limit(buffer + N) { } void reset() {
if (stack != ballast) {
js_free(stack);
stack = ballast;
limit = ballastLimit;
}
tos = stack;
JS_ASSERT(limit == ballastLimit);
}
bool enlarge() {
size_t tosIndex = tos - stack;
size_t cap = limit - stack;
size_t newcap = cap * 2;
if (newcap == 0)
newcap = 32;
T *newStack;
if (stack == ballast) {
newStack = (T *)js_malloc(sizeof(T) * newcap);
if (!newStack)
return false;
for (T *src = stack, *dst = newStack; src < tos; )
*dst++ = *src++;
} else {
newStack = (T *)js_realloc(stack, sizeof(T) * newcap);
if (!newStack)
return false;
}
stack = newStack;
tos = stack + tosIndex;
limit = newStack + newcap;
return true;
}
};
/*
* This class records how much work has been done in a given GC slice, so that
* we can return before pausing for too long. Some slices are allowed to run for
* unlimited time, and others are bounded. To reduce the number of gettimeofday
* calls, we only check the time every 1000 operations.
*/
struct SliceBudget {
int64_t deadline; /* in microseconds */
intptr_t counter;
static const intptr_t CounterReset = 1000;
static const int64_t Unlimited = 0;
static int64_t TimeBudget(int64_t millis);
static int64_t WorkBudget(int64_t work);
/* Equivalent to SliceBudget(UnlimitedBudget). */
SliceBudget();
/* Instantiate as SliceBudget(Time/WorkBudget(n)). */
SliceBudget(int64_t budget);
void reset() {
deadline = INT64_MAX;
counter = INTPTR_MAX;
}
void step() {
counter--;
}
bool checkOverBudget();
bool isOverBudget() {
if (counter > 0)
return false;
return checkOverBudget();
}
}; };
static const size_t MARK_STACK_LENGTH = 32768; static const size_t MARK_STACK_LENGTH = 32768;
struct GCMarker : public JSTracer { struct GCMarker : public JSTracer {
private:
/* /*
* We use a common mark stack to mark GC things of different types and use * We use a common mark stack to mark GC things of different types and use
* the explicit tags to distinguish them when it cannot be deduced from * the explicit tags to distinguish them when it cannot be deduced from
* the context of push or pop operation. * the context of push or pop operation.
*
* Currently we need only 4 tags. However that can be extended to 8 if
* necessary as we tag only GC things.
*/ */
enum StackTag { enum StackTag {
ValueArrayTag, ValueArrayTag,
ObjectTag, ObjectTag,
TypeTag, TypeTag,
XmlTag, XmlTag,
LastTag = XmlTag SavedValueArrayTag,
LastTag = SavedValueArrayTag
}; };
static const uintptr_t StackTagMask = 3; static const uintptr_t StackTagMask = 7;
static void staticAsserts() { static void staticAsserts() {
JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag)); JS_STATIC_ASSERT(StackTagMask >= uintptr_t(LastTag));
JS_STATIC_ASSERT(StackTagMask <= gc::Cell::CellMask); JS_STATIC_ASSERT(StackTagMask <= gc::Cell::CellMask);
} }
private:
/* The color is only applied to objects, functions and xml. */
uint32_t color;
public: public:
/* Pointer to the top of the stack of arenas we are delaying marking on. */ explicit GCMarker();
js::gc::Arena *unmarkedArenaStackTop; bool init(bool lazy);
/* Count of arenas that are currently in the stack. */
DebugOnly<size_t> markLaterArenas;
#ifdef JS_DUMP_CONSERVATIVE_GC_ROOTS void start(JSRuntime *rt, JSContext *cx);
js::gc::ConservativeGCStats conservativeStats; void stop();
Vector<void *, 0, SystemAllocPolicy> conservativeRoots; void reset();
const char *conservativeDumpFileName;
void dumpConservativeRoots();
#endif
MarkStack<uintptr_t> stack; void pushObject(JSObject *obj) {
pushTaggedPtr(ObjectTag, obj);
}
public: void pushType(types::TypeObject *type) {
explicit GCMarker(JSContext *cx); pushTaggedPtr(TypeTag, type);
~GCMarker(); }
void pushXML(JSXML *xml) {
pushTaggedPtr(XmlTag, xml);
}
uint32_t getMarkColor() const { uint32_t getMarkColor() const {
return color; return color;
@ -1668,43 +1773,123 @@ struct GCMarker : public JSTracer {
* objects that are still reachable. * objects that are still reachable.
*/ */
void setMarkColorGray() { void setMarkColorGray() {
JS_ASSERT(isDrained());
JS_ASSERT(color == gc::BLACK); JS_ASSERT(color == gc::BLACK);
color = gc::GRAY; color = gc::GRAY;
} }
inline void delayMarkingArena(gc::ArenaHeader *aheader);
void delayMarkingChildren(const void *thing); void delayMarkingChildren(const void *thing);
void markDelayedChildren(gc::ArenaHeader *aheader);
bool markDelayedChildren(SliceBudget &budget);
bool hasDelayedChildren() const { bool hasDelayedChildren() const {
return !!unmarkedArenaStackTop; return !!unmarkedArenaStackTop;
} }
void markDelayedChildren(); bool isDrained() {
return isMarkStackEmpty() && !unmarkedArenaStackTop;
}
bool drainMarkStack(SliceBudget &budget);
/*
* Gray marking must be done after all black marking is complete. However,
* we do not have write barriers on XPConnect roots. Therefore, XPConnect
* roots must be accumulated in the first slice of incremental GC. We
* accumulate these roots in the GrayRootMarker and then mark them later,
* after black marking is complete. This accumulation can fail, but in that
* case we switch to non-incremental GC.
*/
bool hasBufferedGrayRoots() const;
void startBufferingGrayRoots();
void endBufferingGrayRoots();
void markBufferedGrayRoots();
static void GrayCallback(JSTracer *trc, void **thing, JSGCTraceKind kind);
MarkStack<uintptr_t> stack;
private:
#ifdef DEBUG
void checkCompartment(void *p);
#else
void checkCompartment(void *p) {}
#endif
void pushTaggedPtr(StackTag tag, void *ptr) {
checkCompartment(ptr);
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
JS_ASSERT(!(addr & StackTagMask));
if (!stack.push(addr | uintptr_t(tag)))
delayMarkingChildren(ptr);
}
void pushValueArray(JSObject *obj, void *start, void *end) {
checkCompartment(obj);
if (start == end)
return;
JS_ASSERT(start <= end);
uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
/*
* Push in the reverse order so obj will be on top. If we cannot push
* the array, we trigger delay marking for the whole object.
*/
if (!stack.push(endAddr, startAddr, tagged))
delayMarkingChildren(obj);
}
bool isMarkStackEmpty() { bool isMarkStackEmpty() {
return stack.isEmpty(); return stack.isEmpty();
} }
void drainMarkStack(); bool restoreValueArray(JSObject *obj, void **vpp, void **endp);
void saveValueRanges();
inline void processMarkStackTop(SliceBudget &budget);
inline void processMarkStackTop(); void appendGrayRoot(void *thing, JSGCTraceKind kind);
void pushObject(JSObject *obj) { /* The color is only applied to objects, functions and xml. */
pushTaggedPtr(ObjectTag, obj); uint32_t color;
DebugOnly<bool> started;
/* Pointer to the top of the stack of arenas we are delaying marking on. */
js::gc::ArenaHeader *unmarkedArenaStackTop;
/* Count of arenas that are currently in the stack. */
DebugOnly<size_t> markLaterArenas;
struct GrayRoot {
void *thing;
JSGCTraceKind kind;
#ifdef DEBUG
JSTraceNamePrinter debugPrinter;
const void *debugPrintArg;
size_t debugPrintIndex;
#endif
GrayRoot(void *thing, JSGCTraceKind kind)
: thing(thing), kind(kind) {}
};
bool grayFailed;
Vector<GrayRoot, 0, SystemAllocPolicy> grayRoots;
};
struct BarrierGCMarker : public GCMarker {
bool init() {
return GCMarker::init(true);
} }
};
void pushType(types::TypeObject *type) {
pushTaggedPtr(TypeTag, type);
}
void pushXML(JSXML *xml) { struct FullGCMarker : public GCMarker {
pushTaggedPtr(XmlTag, xml); bool init() {
} return GCMarker::init(false);
void pushTaggedPtr(StackTag tag, void *ptr) {
uintptr_t addr = reinterpret_cast<uintptr_t>(ptr);
JS_ASSERT(!(addr & StackTagMask));
if (!stack.push(addr | uintptr_t(tag)))
delayMarkingChildren(ptr);
} }
}; };
@ -1757,7 +1942,8 @@ js_FinalizeStringRT(JSRuntime *rt, JSString *str);
/* /*
* Macro to test if a traversal is the marking phase of the GC. * Macro to test if a traversal is the marking phase of the GC.
*/ */
#define IS_GC_MARKING_TRACER(trc) ((trc)->callback == NULL) #define IS_GC_MARKING_TRACER(trc) \
((trc)->callback == NULL || (trc)->callback == GCMarker::GrayCallback)
namespace js { namespace js {
namespace gc { namespace gc {
@ -1778,20 +1964,30 @@ inline void MaybeCheckStackRoots(JSContext *cx) { CheckStackRoots(cx); }
inline void MaybeCheckStackRoots(JSContext *cx) {} inline void MaybeCheckStackRoots(JSContext *cx) {}
#endif #endif
const int ZealPokeThreshold = 1; const int ZealPokeValue = 1;
const int ZealAllocThreshold = 2; const int ZealAllocValue = 2;
const int ZealVerifierThreshold = 4; const int ZealFrameGCValue = 3;
const int ZealVerifierValue = 4;
const int ZealFrameVerifierValue = 5;
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
/* Check that write barriers have been used correctly. See jsgc.cpp. */ /* Check that write barriers have been used correctly. See jsgc.cpp. */
void void
VerifyBarriers(JSContext *cx, bool always = false); VerifyBarriers(JSContext *cx);
void
MaybeVerifyBarriers(JSContext *cx, bool always = false);
#else #else
static inline void static inline void
VerifyBarriers(JSContext *cx, bool always = false) VerifyBarriers(JSContext *cx)
{
}
static inline void
MaybeVerifyBarriers(JSContext *cx, bool always = false)
{ {
} }

Просмотреть файл

@ -210,7 +210,7 @@ GCPoke(JSRuntime *rt, Value oldval)
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
/* Schedule a GC to happen "soon" after a GC poke. */ /* Schedule a GC to happen "soon" after a GC poke. */
if (rt->gcZeal() >= js::gc::ZealPokeThreshold) if (rt->gcZeal() == js::gc::ZealPokeValue)
rt->gcNextScheduled = 1; rt->gcNextScheduled = 1;
#endif #endif
} }
@ -262,14 +262,25 @@ class CellIterImpl
CellIterImpl() { CellIterImpl() {
} }
void init(JSCompartment *comp, AllocKind kind) { void initSpan(JSCompartment *comp, AllocKind kind) {
JS_ASSERT(comp->arenas.isSynchronizedFreeList(kind)); JS_ASSERT(comp->arenas.isSynchronizedFreeList(kind));
firstThingOffset = Arena::firstThingOffset(kind); firstThingOffset = Arena::firstThingOffset(kind);
thingSize = Arena::thingSize(kind); thingSize = Arena::thingSize(kind);
aheader = comp->arenas.getFirstArena(kind);
firstSpan.initAsEmpty(); firstSpan.initAsEmpty();
span = &firstSpan; span = &firstSpan;
thing = span->first; thing = span->first;
}
void init(ArenaHeader *singleAheader) {
aheader = singleAheader;
initSpan(aheader->compartment, aheader->getAllocKind());
next();
aheader = NULL;
}
void init(JSCompartment *comp, AllocKind kind) {
initSpan(comp, kind);
aheader = comp->arenas.getFirstArena(kind);
next(); next();
} }
@ -311,13 +322,18 @@ class CellIterImpl
} }
}; };
class CellIterUnderGC : public CellIterImpl { class CellIterUnderGC : public CellIterImpl
{
public: public:
CellIterUnderGC(JSCompartment *comp, AllocKind kind) { CellIterUnderGC(JSCompartment *comp, AllocKind kind) {
JS_ASSERT(comp->rt->gcRunning); JS_ASSERT(comp->rt->gcRunning);
init(comp, kind); init(comp, kind);
} }
CellIterUnderGC(ArenaHeader *aheader) {
JS_ASSERT(aheader->compartment->rt->gcRunning);
init(aheader);
}
}; };
/* /*
@ -335,7 +351,8 @@ class CellIter: public CellIterImpl
public: public:
CellIter(JSContext *cx, JSCompartment *comp, AllocKind kind) CellIter(JSContext *cx, JSCompartment *comp, AllocKind kind)
: lists(&comp->arenas), : lists(&comp->arenas),
kind(kind) { kind(kind)
{
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
JS_ASSERT(comp->arenas.doneBackgroundFinalize(kind)); JS_ASSERT(comp->arenas.doneBackgroundFinalize(kind));
#endif #endif
@ -397,6 +414,9 @@ NewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
void *t = comp->arenas.allocateFromFreeList(kind, thingSize); void *t = comp->arenas.allocateFromFreeList(kind, thingSize);
if (!t) if (!t)
t = js::gc::ArenaLists::refillFreeList(cx, kind); t = js::gc::ArenaLists::refillFreeList(cx, kind);
JS_ASSERT_IF(t && comp->needsBarrier(),
static_cast<T *>(t)->arenaHeader()->allocatedDuringIncremental);
return static_cast<T *>(t); return static_cast<T *>(t);
} }
@ -419,6 +439,8 @@ TryNewGCThing(JSContext *cx, js::gc::AllocKind kind, size_t thingSize)
#endif #endif
void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize); void *t = cx->compartment->arenas.allocateFromFreeList(kind, thingSize);
JS_ASSERT_IF(t && cx->compartment->needsBarrier(),
static_cast<T *>(t)->arenaHeader()->allocatedDuringIncremental);
return static_cast<T *>(t); return static_cast<T *>(t);
} }

Просмотреть файл

@ -61,7 +61,7 @@ static inline void
PushMarkStack(GCMarker *gcmarker, JSScript *thing); PushMarkStack(GCMarker *gcmarker, JSScript *thing);
static inline void static inline void
PushMarkStack(GCMarker *gcmarker, const Shape *thing); PushMarkStack(GCMarker *gcmarker, Shape *thing);
static inline void static inline void
PushMarkStack(GCMarker *gcmarker, JSString *thing); PushMarkStack(GCMarker *gcmarker, JSString *thing);
@ -103,7 +103,7 @@ MarkInternal(JSTracer *trc, T *thing)
* GC. * GC.
*/ */
if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) { if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) {
if (IS_GC_MARKING_TRACER(trc)) { if (!trc->callback) {
PushMarkStack(static_cast<GCMarker *>(trc), thing); PushMarkStack(static_cast<GCMarker *>(trc), thing);
} else { } else {
void *tmp = (void *)thing; void *tmp = (void *)thing;
@ -118,6 +118,12 @@ MarkInternal(JSTracer *trc, T *thing)
#endif #endif
} }
#define JS_ROOT_MARKING_ASSERT(trc) \
JS_ASSERT_IF(IS_GC_MARKING_TRACER(trc), \
trc->runtime->gcIncrementalState == NO_INCREMENTAL || \
trc->runtime->gcIncrementalState == MARK_ROOTS);
template <typename T> template <typename T>
static void static void
MarkUnbarriered(JSTracer *trc, T *thing, const char *name) MarkUnbarriered(JSTracer *trc, T *thing, const char *name)
@ -128,18 +134,19 @@ MarkUnbarriered(JSTracer *trc, T *thing, const char *name)
template <typename T> template <typename T>
static void static void
Mark(JSTracer *trc, const HeapPtr<T> &thing, const char *name) Mark(JSTracer *trc, HeapPtr<T> *thing, const char *name)
{ {
JS_SET_TRACING_NAME(trc, name); JS_SET_TRACING_NAME(trc, name);
MarkInternal(trc, thing.get()); MarkInternal(trc, thing->get());
} }
template <typename T> template <typename T>
static void static void
MarkRoot(JSTracer *trc, T *thing, const char *name) MarkRoot(JSTracer *trc, T **thingp, const char *name)
{ {
JS_ROOT_MARKING_ASSERT(trc);
JS_SET_TRACING_NAME(trc, name); JS_SET_TRACING_NAME(trc, name);
MarkInternal(trc, thing); MarkInternal(trc, *thingp);
} }
template <typename T> template <typename T>
@ -158,6 +165,7 @@ template <typename T>
static void static void
MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name) MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name)
{ {
JS_ROOT_MARKING_ASSERT(trc);
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
JS_SET_TRACING_INDEX(trc, name, i); JS_SET_TRACING_INDEX(trc, name, i);
MarkInternal(trc, vec[i]); MarkInternal(trc, vec[i]);
@ -166,15 +174,15 @@ MarkRootRange(JSTracer *trc, size_t len, T **vec, const char *name)
#define DeclMarkerImpl(base, type) \ #define DeclMarkerImpl(base, type) \
void \ void \
Mark##base(JSTracer *trc, const HeapPtr<type> &thing, const char *name) \ Mark##base(JSTracer *trc, HeapPtr<type> *thing, const char *name) \
{ \ { \
Mark<type>(trc, thing, name); \ Mark<type>(trc, thing, name); \
} \ } \
\ \
void \ void \
Mark##base##Root(JSTracer *trc, type *thing, const char *name) \ Mark##base##Root(JSTracer *trc, type **thingp, const char *name) \
{ \ { \
MarkRoot<type>(trc, thing, name); \ MarkRoot<type>(trc, thingp, name); \
} \ } \
\ \
void \ void \
@ -246,6 +254,7 @@ MarkKind(JSTracer *trc, void *thing, JSGCTraceKind kind)
void void
MarkGCThingRoot(JSTracer *trc, void *thing, const char *name) MarkGCThingRoot(JSTracer *trc, void *thing, const char *name)
{ {
JS_ROOT_MARKING_ASSERT(trc);
JS_SET_TRACING_NAME(trc, name); JS_SET_TRACING_NAME(trc, name);
if (!thing) if (!thing)
return; return;
@ -255,24 +264,30 @@ MarkGCThingRoot(JSTracer *trc, void *thing, const char *name)
/*** ID Marking ***/ /*** ID Marking ***/
static inline void static inline void
MarkIdInternal(JSTracer *trc, const jsid &id) MarkIdInternal(JSTracer *trc, jsid *id)
{ {
if (JSID_IS_STRING(id)) if (JSID_IS_STRING(*id)) {
MarkInternal(trc, JSID_TO_STRING(id)); JSString *str = JSID_TO_STRING(*id);
else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) MarkInternal(trc, str);
MarkInternal(trc, JSID_TO_OBJECT(id)); *id = ATOM_TO_JSID(reinterpret_cast<JSAtom *>(str));
} else if (JS_UNLIKELY(JSID_IS_OBJECT(*id))) {
JSObject *obj = JSID_TO_OBJECT(*id);
MarkInternal(trc, obj);
*id = OBJECT_TO_JSID(obj);
}
} }
void void
MarkId(JSTracer *trc, const HeapId &id, const char *name) MarkId(JSTracer *trc, HeapId *id, const char *name)
{ {
JS_SET_TRACING_NAME(trc, name); JS_SET_TRACING_NAME(trc, name);
MarkIdInternal(trc, id); MarkIdInternal(trc, id->unsafeGet());
} }
void void
MarkIdRoot(JSTracer *trc, const jsid &id, const char *name) MarkIdRoot(JSTracer *trc, jsid *id, const char *name)
{ {
JS_ROOT_MARKING_ASSERT(trc);
JS_SET_TRACING_NAME(trc, name); JS_SET_TRACING_NAME(trc, name);
MarkIdInternal(trc, id); MarkIdInternal(trc, id);
} }
@ -282,16 +297,17 @@ MarkIdRange(JSTracer *trc, size_t len, HeapId *vec, const char *name)
{ {
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
JS_SET_TRACING_INDEX(trc, name, i); JS_SET_TRACING_INDEX(trc, name, i);
MarkIdInternal(trc, vec[i]); MarkIdInternal(trc, vec[i].unsafeGet());
} }
} }
void void
MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name) MarkIdRootRange(JSTracer *trc, size_t len, jsid *vec, const char *name)
{ {
JS_ROOT_MARKING_ASSERT(trc);
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
JS_SET_TRACING_INDEX(trc, name, i); JS_SET_TRACING_INDEX(trc, name, i);
MarkIdInternal(trc, vec[i]); MarkIdInternal(trc, &vec[i]);
} }
} }
@ -316,6 +332,7 @@ MarkValue(JSTracer *trc, HeapValue *v, const char *name)
void void
MarkValueRoot(JSTracer *trc, Value *v, const char *name) MarkValueRoot(JSTracer *trc, Value *v, const char *name)
{ {
JS_ROOT_MARKING_ASSERT(trc);
JS_SET_TRACING_NAME(trc, name); JS_SET_TRACING_NAME(trc, name);
MarkValueInternal(trc, v); MarkValueInternal(trc, v);
} }
@ -332,6 +349,7 @@ MarkValueRange(JSTracer *trc, size_t len, HeapValue *vec, const char *name)
void void
MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name) MarkValueRootRange(JSTracer *trc, size_t len, Value *vec, const char *name)
{ {
JS_ROOT_MARKING_ASSERT(trc);
for (size_t i = 0; i < len; ++i) { for (size_t i = 0; i < len; ++i) {
JS_SET_TRACING_INDEX(trc, name, i); JS_SET_TRACING_INDEX(trc, name, i);
MarkValueInternal(trc, &vec[i]); MarkValueInternal(trc, &vec[i]);
@ -351,13 +369,6 @@ MarkObject(JSTracer *trc, const HeapPtr<GlobalObject, JSScript *> &thing, const
MarkInternal(trc, thing.get()); MarkInternal(trc, thing.get());
} }
void
MarkShape(JSTracer *trc, const HeapPtr<const Shape> &thing, const char *name)
{
JS_SET_TRACING_NAME(trc, name);
MarkInternal(trc, const_cast<Shape *>(thing.get()));
}
void void
MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name) MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name)
{ {
@ -374,6 +385,10 @@ MarkCrossCompartmentValue(JSTracer *trc, HeapValue *v, const char *name)
if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment) if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment)
return; return;
/* In case we're called from a write barrier. */
if (rt->gcIncrementalCompartment && cell->compartment() != rt->gcIncrementalCompartment)
return;
MarkValue(trc, v, name); MarkValue(trc, v, name);
} }
} }
@ -443,10 +458,10 @@ PushMarkStack(GCMarker *gcmarker, JSScript *thing)
} }
static void static void
ScanShape(GCMarker *gcmarker, const Shape *shape); ScanShape(GCMarker *gcmarker, Shape *shape);
static void static void
PushMarkStack(GCMarker *gcmarker, const Shape *thing) PushMarkStack(GCMarker *gcmarker, Shape *thing)
{ {
JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing); JS_COMPARTMENT_ASSERT(gcmarker->runtime, thing);
@ -469,12 +484,12 @@ PushMarkStack(GCMarker *gcmarker, BaseShape *thing)
} }
static void static void
ScanShape(GCMarker *gcmarker, const Shape *shape) ScanShape(GCMarker *gcmarker, Shape *shape)
{ {
restart: restart:
PushMarkStack(gcmarker, shape->base()); PushMarkStack(gcmarker, shape->base());
jsid id = shape->maybePropid(); const HeapId &id = shape->propidRef();
if (JSID_IS_STRING(id)) if (JSID_IS_STRING(id))
PushMarkStack(gcmarker, JSID_TO_STRING(id)); PushMarkStack(gcmarker, JSID_TO_STRING(id));
else if (JS_UNLIKELY(JSID_IS_OBJECT(id))) else if (JS_UNLIKELY(JSID_IS_OBJECT(id)))
@ -543,7 +558,7 @@ ScanLinearString(GCMarker *gcmarker, JSLinearString *str)
static void static void
ScanRope(GCMarker *gcmarker, JSRope *rope) ScanRope(GCMarker *gcmarker, JSRope *rope)
{ {
uintptr_t *savedTos = gcmarker->stack.tos; ptrdiff_t savedPos = gcmarker->stack.position();
for (;;) { for (;;) {
JS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING); JS_ASSERT(GetGCThingTraceKind(rope) == JSTRACE_STRING);
JS_ASSERT(rope->JSString::isRope()); JS_ASSERT(rope->JSString::isRope());
@ -575,14 +590,14 @@ ScanRope(GCMarker *gcmarker, JSRope *rope)
} }
if (next) { if (next) {
rope = next; rope = next;
} else if (savedTos != gcmarker->stack.tos) { } else if (savedPos != gcmarker->stack.position()) {
JS_ASSERT(savedTos < gcmarker->stack.tos); JS_ASSERT(savedPos < gcmarker->stack.position());
rope = reinterpret_cast<JSRope *>(gcmarker->stack.pop()); rope = reinterpret_cast<JSRope *>(gcmarker->stack.pop());
} else { } else {
break; break;
} }
} }
JS_ASSERT(savedTos == gcmarker->stack.tos); JS_ASSERT(savedPos == gcmarker->stack.position());
} }
static inline void static inline void
@ -608,28 +623,10 @@ PushMarkStack(GCMarker *gcmarker, JSString *str)
ScanString(gcmarker, str); ScanString(gcmarker, str);
} }
static inline void
PushValueArray(GCMarker *gcmarker, JSObject* obj, HeapValue *start, HeapValue *end)
{
JS_ASSERT(start <= end);
uintptr_t tagged = reinterpret_cast<uintptr_t>(obj) | GCMarker::ValueArrayTag;
uintptr_t startAddr = reinterpret_cast<uintptr_t>(start);
uintptr_t endAddr = reinterpret_cast<uintptr_t>(end);
/* Push in the reverse order so obj will be on top. */
if (!gcmarker->stack.push(endAddr, startAddr, tagged)) {
/*
* If we cannot push the array, we trigger delay marking for the whole
* object.
*/
gcmarker->delayMarkingChildren(obj);
}
}
void void
MarkChildren(JSTracer *trc, JSObject *obj) MarkChildren(JSTracer *trc, JSObject *obj)
{ {
MarkTypeObject(trc, obj->typeFromGC(), "type"); MarkTypeObject(trc, &obj->typeFromGC(), "type");
Shape *shape = obj->lastProperty(); Shape *shape = obj->lastProperty();
MarkShapeUnbarriered(trc, shape, "shape"); MarkShapeUnbarriered(trc, shape, "shape");
@ -710,12 +707,12 @@ MarkChildren(JSTracer *trc, JSScript *script)
} }
static void static void
MarkChildren(JSTracer *trc, const Shape *shape) MarkChildren(JSTracer *trc, Shape *shape)
{ {
MarkBaseShapeUnbarriered(trc, shape->base(), "base"); MarkBaseShapeUnbarriered(trc, shape->base(), "base");
MarkId(trc, shape->maybePropid(), "propid"); MarkId(trc, &shape->propidRef(), "propid");
if (shape->previous()) if (shape->previous())
MarkShape(trc, shape->previous(), "parent"); MarkShape(trc, &shape->previousRef(), "parent");
} }
static inline void static inline void
@ -776,12 +773,12 @@ MarkCycleCollectorChildren(JSTracer *trc, BaseShape *base, JSObject **prevParent
* parent pointer will only be marked once. * parent pointer will only be marked once.
*/ */
void void
MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape) MarkCycleCollectorChildren(JSTracer *trc, Shape *shape)
{ {
JSObject *prevParent = NULL; JSObject *prevParent = NULL;
do { do {
MarkCycleCollectorChildren(trc, shape->base(), &prevParent); MarkCycleCollectorChildren(trc, shape->base(), &prevParent);
MarkId(trc, shape->maybePropid(), "propid"); MarkId(trc, &shape->propidRef(), "propid");
shape = shape->previous(); shape = shape->previous();
} while (shape); } while (shape);
} }
@ -824,23 +821,23 @@ MarkChildren(JSTracer *trc, types::TypeObject *type)
for (unsigned i = 0; i < count; i++) { for (unsigned i = 0; i < count; i++) {
types::Property *prop = type->getProperty(i); types::Property *prop = type->getProperty(i);
if (prop) if (prop)
MarkId(trc, prop->id, "type_prop"); MarkId(trc, &prop->id, "type_prop");
} }
} }
if (type->proto) if (type->proto)
MarkObject(trc, type->proto, "type_proto"); MarkObject(trc, &type->proto, "type_proto");
if (type->singleton && !type->lazy()) if (type->singleton && !type->lazy())
MarkObject(trc, type->singleton, "type_singleton"); MarkObject(trc, &type->singleton, "type_singleton");
if (type->newScript) { if (type->newScript) {
MarkObject(trc, type->newScript->fun, "type_new_function"); MarkObject(trc, &type->newScript->fun, "type_new_function");
MarkShape(trc, type->newScript->shape, "type_new_shape"); MarkShape(trc, &type->newScript->shape, "type_new_shape");
} }
if (type->interpretedFunction) if (type->interpretedFunction)
MarkObject(trc, type->interpretedFunction, "type_function"); MarkObject(trc, &type->interpretedFunction, "type_function");
} }
#ifdef JS_HAS_XML_SUPPORT #ifdef JS_HAS_XML_SUPPORT
@ -851,12 +848,163 @@ MarkChildren(JSTracer *trc, JSXML *xml)
} }
#endif #endif
template<typename T>
void
PushArenaTyped(GCMarker *gcmarker, ArenaHeader *aheader)
{
for (CellIterUnderGC i(aheader); !i.done(); i.next())
PushMarkStack(gcmarker, i.get<T>());
}
void
PushArena(GCMarker *gcmarker, ArenaHeader *aheader)
{
switch (MapAllocToTraceKind(aheader->getAllocKind())) {
case JSTRACE_OBJECT:
PushArenaTyped<JSObject>(gcmarker, aheader);
break;
case JSTRACE_STRING:
PushArenaTyped<JSString>(gcmarker, aheader);
break;
case JSTRACE_SCRIPT:
PushArenaTyped<JSScript>(gcmarker, aheader);
break;
case JSTRACE_SHAPE:
PushArenaTyped<js::Shape>(gcmarker, aheader);
break;
case JSTRACE_BASE_SHAPE:
PushArenaTyped<js::BaseShape>(gcmarker, aheader);
break;
case JSTRACE_TYPE_OBJECT:
PushArenaTyped<js::types::TypeObject>(gcmarker, aheader);
break;
#if JS_HAS_XML_SUPPORT
case JSTRACE_XML:
PushArenaTyped<JSXML>(gcmarker, aheader);
break;
#endif
}
}
} /* namespace gc */ } /* namespace gc */
using namespace js::gc; using namespace js::gc;
struct ValueArrayLayout
{
union {
HeapValue *end;
js::Class *clasp;
};
union {
HeapValue *start;
uintptr_t index;
};
JSObject *obj;
static void staticAsserts() {
/* This should have the same layout as three mark stack items. */
JS_STATIC_ASSERT(sizeof(ValueArrayLayout) == 3 * sizeof(uintptr_t));
}
};
/*
* During incremental GC, we return from drainMarkStack without having processed
* the entire stack. At that point, JS code can run and reallocate slot arrays
* that are stored on the stack. To prevent this from happening, we replace all
* ValueArrayTag stack items with SavedValueArrayTag. In the latter, slots
* pointers are replaced with slot indexes.
*
* We also replace the slot array end pointer (which can be derived from the obj
* pointer) with the object's class. During JS executation, array slowification
* can cause the layout of slots to change. We can observe that slowification
* happened if the class changed; in that case, we completely rescan the array.
*/
void
GCMarker::saveValueRanges()
{
for (uintptr_t *p = stack.tos; p > stack.stack; ) {
uintptr_t tag = *--p & StackTagMask;
if (tag == ValueArrayTag) {
p -= 2;
ValueArrayLayout *arr = reinterpret_cast<ValueArrayLayout *>(p);
JSObject *obj = arr->obj;
if (obj->getClass() == &ArrayClass) {
HeapValue *vp = obj->getDenseArrayElements();
JS_ASSERT(arr->start >= vp &&
arr->end == vp + obj->getDenseArrayInitializedLength());
arr->index = arr->start - vp;
} else {
HeapValue *vp = obj->fixedSlots();
unsigned nfixed = obj->numFixedSlots();
if (arr->start >= vp && arr->start < vp + nfixed) {
JS_ASSERT(arr->end == vp + Min(nfixed, obj->slotSpan()));
arr->index = arr->start - vp;
} else {
JS_ASSERT(arr->start >= obj->slots &&
arr->end == obj->slots + obj->slotSpan() - nfixed);
arr->index = (arr->start - obj->slots) + nfixed;
}
}
arr->clasp = obj->getClass();
p[2] |= SavedValueArrayTag;
} else if (tag == SavedValueArrayTag) {
p -= 2;
}
}
}
bool
GCMarker::restoreValueArray(JSObject *obj, void **vpp, void **endp)
{
uintptr_t start = stack.pop();
js::Class *clasp = reinterpret_cast<js::Class *>(stack.pop());
JS_ASSERT(obj->getClass() == clasp ||
(clasp == &ArrayClass && obj->getClass() == &SlowArrayClass));
if (clasp == &ArrayClass) {
if (obj->getClass() != &ArrayClass)
return false;
uint32_t initlen = obj->getDenseArrayInitializedLength();
HeapValue *vp = obj->getDenseArrayElements();
if (start < initlen) {
*vpp = vp + start;
*endp = vp + initlen;
} else {
/* The object shrunk, in which case no scanning is needed. */
*vpp = *endp = vp;
}
} else {
HeapValue *vp = obj->fixedSlots();
unsigned nfixed = obj->numFixedSlots();
unsigned nslots = obj->slotSpan();
if (start < nfixed) {
*vpp = vp + start;
*endp = vp + Min(nfixed, nslots);
} else if (start < nslots) {
*vpp = obj->slots + start - nfixed;
*endp = obj->slots + nslots - nfixed;
} else {
/* The object shrunk, in which case no scanning is needed. */
*vpp = *endp = obj->slots;
}
}
JS_ASSERT(*vpp <= *endp);
return true;
}
inline void inline void
GCMarker::processMarkStackTop() GCMarker::processMarkStackTop(SliceBudget &budget)
{ {
/* /*
* The function uses explicit goto and implements the scanning of the * The function uses explicit goto and implements the scanning of the
@ -885,29 +1033,46 @@ GCMarker::processMarkStackTop()
if (tag == ObjectTag) { if (tag == ObjectTag) {
obj = reinterpret_cast<JSObject *>(addr); obj = reinterpret_cast<JSObject *>(addr);
JS_COMPARTMENT_ASSERT(runtime, obj);
goto scan_obj; goto scan_obj;
} }
if (tag == TypeTag) { if (tag == TypeTag) {
ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr)); ScanTypeObject(this, reinterpret_cast<types::TypeObject *>(addr));
} else if (tag == SavedValueArrayTag) {
JS_ASSERT(!(addr & Cell::CellMask));
obj = reinterpret_cast<JSObject *>(addr);
if (restoreValueArray(obj, (void **)&vp, (void **)&end))
goto scan_value_array;
else
goto scan_obj;
} else { } else {
JS_ASSERT(tag == XmlTag); JS_ASSERT(tag == XmlTag);
MarkChildren(this, reinterpret_cast<JSXML *>(addr)); MarkChildren(this, reinterpret_cast<JSXML *>(addr));
} }
budget.step();
return; return;
scan_value_array: scan_value_array:
JS_ASSERT(vp <= end); JS_ASSERT(vp <= end);
while (vp != end) { while (vp != end) {
budget.step();
if (budget.isOverBudget()) {
pushValueArray(obj, vp, end);
return;
}
const Value &v = *vp++; const Value &v = *vp++;
if (v.isString()) { if (v.isString()) {
JSString *str = v.toString(); JSString *str = v.toString();
JS_COMPARTMENT_ASSERT_STR(runtime, str);
if (str->markIfUnmarked()) if (str->markIfUnmarked())
ScanString(this, str); ScanString(this, str);
} else if (v.isObject()) { } else if (v.isObject()) {
JSObject *obj2 = &v.toObject(); JSObject *obj2 = &v.toObject();
JS_COMPARTMENT_ASSERT(runtime, obj2);
if (obj2->markIfUnmarked(getMarkColor())) { if (obj2->markIfUnmarked(getMarkColor())) {
PushValueArray(this, obj, vp, end); pushValueArray(obj, vp, end);
obj = obj2; obj = obj2;
goto scan_obj; goto scan_obj;
} }
@ -917,6 +1082,14 @@ GCMarker::processMarkStackTop()
scan_obj: scan_obj:
{ {
JS_COMPARTMENT_ASSERT(runtime, obj);
budget.step();
if (budget.isOverBudget()) {
pushObject(obj);
return;
}
types::TypeObject *type = obj->typeFromGC(); types::TypeObject *type = obj->typeFromGC();
PushMarkStack(this, type); PushMarkStack(this, type);
@ -931,6 +1104,9 @@ GCMarker::processMarkStackTop()
vp = obj->getDenseArrayElements(); vp = obj->getDenseArrayElements();
end = vp + obj->getDenseArrayInitializedLength(); end = vp + obj->getDenseArrayInitializedLength();
goto scan_value_array; goto scan_value_array;
} else {
JS_ASSERT_IF(runtime->gcIncrementalState != NO_INCREMENTAL,
clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS);
} }
clasp->trace(this, obj); clasp->trace(this, obj);
} }
@ -943,7 +1119,7 @@ GCMarker::processMarkStackTop()
if (obj->slots) { if (obj->slots) {
unsigned nfixed = obj->numFixedSlots(); unsigned nfixed = obj->numFixedSlots();
if (nslots > nfixed) { if (nslots > nfixed) {
PushValueArray(this, obj, vp, vp + nfixed); pushValueArray(obj, vp, vp + nfixed);
vp = obj->slots; vp = obj->slots;
end = vp + (nslots - nfixed); end = vp + (nslots - nfixed);
goto scan_value_array; goto scan_value_array;
@ -955,15 +1131,33 @@ GCMarker::processMarkStackTop()
} }
} }
void bool
GCMarker::drainMarkStack() GCMarker::drainMarkStack(SliceBudget &budget)
{ {
#ifdef DEBUG
JSRuntime *rt = runtime; JSRuntime *rt = runtime;
rt->gcCheckCompartment = rt->gcCurrentCompartment;
struct AutoCheckCompartment {
JSRuntime *runtime;
AutoCheckCompartment(JSRuntime *rt) : runtime(rt) {
runtime->gcCheckCompartment = runtime->gcCurrentCompartment;
}
~AutoCheckCompartment() { runtime->gcCheckCompartment = NULL; }
} acc(rt);
#endif
if (budget.isOverBudget())
return false;
for (;;) { for (;;) {
while (!stack.isEmpty()) while (!stack.isEmpty()) {
processMarkStackTop(); processMarkStackTop(budget);
if (budget.isOverBudget()) {
saveValueRanges();
return false;
}
}
if (!hasDelayedChildren()) if (!hasDelayedChildren())
break; break;
@ -972,10 +1166,13 @@ GCMarker::drainMarkStack()
* above tracing. Don't do this until we're done with everything * above tracing. Don't do this until we're done with everything
* else. * else.
*/ */
markDelayedChildren(); if (!markDelayedChildren(budget)) {
saveValueRanges();
return false;
}
} }
rt->gcCheckCompartment = NULL; return true;
} }
void void

Просмотреть файл

@ -45,8 +45,8 @@ namespace gc {
* defined for marking arrays of object pointers. * defined for marking arrays of object pointers.
*/ */
#define DeclMarker(base, type) \ #define DeclMarker(base, type) \
void Mark##base(JSTracer *trc, const HeapPtr<type> &thing, const char *name); \ void Mark##base(JSTracer *trc, HeapPtr<type> *thing, const char *name); \
void Mark##base##Root(JSTracer *trc, type *thing, const char *name); \ void Mark##base##Root(JSTracer *trc, type **thingp, const char *name); \
void Mark##base##Unbarriered(JSTracer *trc, type *thing, const char *name); \ void Mark##base##Unbarriered(JSTracer *trc, type *thing, const char *name); \
void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type> *thing, const char *name); \ void Mark##base##Range(JSTracer *trc, size_t len, HeapPtr<type> *thing, const char *name); \
void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name); void Mark##base##RootRange(JSTracer *trc, size_t len, type **thing, const char *name);
@ -83,10 +83,10 @@ MarkGCThingRoot(JSTracer *trc, void *thing, const char *name);
/*** ID Marking ***/ /*** ID Marking ***/
void void
MarkId(JSTracer *trc, const HeapId &id, const char *name); MarkId(JSTracer *trc, HeapId *id, const char *name);
void void
MarkIdRoot(JSTracer *trc, const jsid &id, const char *name); MarkIdRoot(JSTracer *trc, jsid *id, const char *name);
void void
MarkIdRange(JSTracer *trc, size_t len, js::HeapId *vec, const char *name); MarkIdRange(JSTracer *trc, size_t len, js::HeapId *vec, const char *name);
@ -116,10 +116,6 @@ MarkValueRootRange(JSTracer *trc, Value *begin, Value *end, const char *name)
/*** Special Cases ***/ /*** Special Cases ***/
/* TypeNewObject contains a HeapPtr<const Shape> that needs a unique cast. */
void
MarkShape(JSTracer *trc, const HeapPtr<const Shape> &thing, const char *name);
/* Direct value access used by the write barriers and the methodjit */ /* Direct value access used by the write barriers and the methodjit */
void void
MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name); MarkValueUnbarriered(JSTracer *trc, Value *v, const char *name);
@ -144,9 +140,13 @@ MarkChildren(JSTracer *trc, JSObject *obj);
* JS_TraceShapeCycleCollectorChildren. * JS_TraceShapeCycleCollectorChildren.
*/ */
void void
MarkCycleCollectorChildren(JSTracer *trc, const Shape *shape); MarkCycleCollectorChildren(JSTracer *trc, Shape *shape);
void
PushArena(GCMarker *gcmarker, ArenaHeader *aheader);
/*** Generic ***/ /*** Generic ***/
/* /*
* The Mark() functions interface should only be used by code that must be * The Mark() functions interface should only be used by code that must be
* templated. Other uses should use the more specific, type-named functions. * templated. Other uses should use the more specific, type-named functions.
@ -159,13 +159,13 @@ Mark(JSTracer *trc, HeapValue *v, const char *name)
} }
inline void inline void
Mark(JSTracer *trc, const HeapPtr<JSObject> &o, const char *name) Mark(JSTracer *trc, HeapPtr<JSObject> *o, const char *name)
{ {
MarkObject(trc, o, name); MarkObject(trc, o, name);
} }
inline void inline void
Mark(JSTracer *trc, const HeapPtr<JSXML> &xml, const char *name) Mark(JSTracer *trc, HeapPtr<JSXML> *xml, const char *name)
{ {
MarkXML(trc, xml, name); MarkXML(trc, xml, name);
} }

Просмотреть файл

@ -2195,7 +2195,7 @@ TypeCompartment::nukeTypes(JSContext *cx)
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
AutoLockGC maybeLock; AutoLockGC maybeLock;
if (!cx->runtime->gcMarkAndSweep) if (!cx->runtime->gcRunning)
maybeLock.lock(cx->runtime); maybeLock.lock(cx->runtime);
#endif #endif
@ -2618,7 +2618,7 @@ struct types::ObjectTableKey
typedef JSObject * Lookup; typedef JSObject * Lookup;
static inline uint32_t hash(JSObject *obj) { static inline uint32_t hash(JSObject *obj) {
return (uint32_t) (JSID_BITS(obj->lastProperty()->propid()) ^ return (uint32_t) (JSID_BITS(obj->lastProperty()->propid().get()) ^
obj->slotSpan() ^ obj->numFixedSlots() ^ obj->slotSpan() ^ obj->numFixedSlots() ^
((uint32_t)(size_t)obj->getProto() >> 2)); ((uint32_t)(size_t)obj->getProto() >> 2));
} }

Просмотреть файл

@ -668,7 +668,7 @@ struct TypeNewScript
* Shape to use for newly constructed objects. Reflects all definite * Shape to use for newly constructed objects. Reflects all definite
* properties the object will have. * properties the object will have.
*/ */
HeapPtr<const Shape> shape; HeapPtrShape shape;
/* /*
* Order in which properties become initialized. We need this in case a * Order in which properties become initialized. We need this in case a

Просмотреть файл

@ -741,7 +741,7 @@ void
TypeScript::trace(JSTracer *trc) TypeScript::trace(JSTracer *trc)
{ {
if (hasScope() && global) if (hasScope() && global)
gc::MarkObject(trc, global, "script_global"); gc::MarkObject(trc, &global, "script_global");
/* Note: nesting does not keep anything alive. */ /* Note: nesting does not keep anything alive. */
} }
@ -1343,7 +1343,7 @@ TypeNewScript::writeBarrierPre(TypeNewScript *newScript)
JSCompartment *comp = newScript->fun->compartment(); JSCompartment *comp = newScript->fun->compartment();
if (comp->needsBarrier()) { if (comp->needsBarrier()) {
MarkObjectUnbarriered(comp->barrierTracer(), newScript->fun, "write barrier"); MarkObjectUnbarriered(comp->barrierTracer(), newScript->fun, "write barrier");
MarkShape(comp->barrierTracer(), newScript->shape, "write barrier"); MarkShape(comp->barrierTracer(), &newScript->shape, "write barrier");
} }
#endif #endif
} }

Просмотреть файл

@ -1147,7 +1147,7 @@ js::AssertValidPropertyCacheHit(JSContext *cx,
jsbytecode *pc; jsbytecode *pc;
cx->stack.currentScript(&pc); cx->stack.currentScript(&pc);
uint32_t sample = cx->runtime->gcNumber; uint64_t sample = cx->runtime->gcNumber;
PropertyCacheEntry savedEntry = *entry; PropertyCacheEntry savedEntry = *entry;
PropertyName *name = GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc]); PropertyName *name = GetNameFromBytecode(cx, pc, JSOp(*pc), js_CodeSpec[*pc]);
@ -1254,7 +1254,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
{ {
JSAutoResolveFlags rf(cx, RESOLVE_INFER); JSAutoResolveFlags rf(cx, RESOLVE_INFER);
gc::VerifyBarriers(cx, true); gc::MaybeVerifyBarriers(cx, true);
JS_ASSERT(!cx->compartment->activeAnalysis); JS_ASSERT(!cx->compartment->activeAnalysis);
@ -1289,7 +1289,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
# define DO_OP() JS_BEGIN_MACRO \ # define DO_OP() JS_BEGIN_MACRO \
CHECK_PCCOUNT_INTERRUPTS(); \ CHECK_PCCOUNT_INTERRUPTS(); \
js::gc::VerifyBarriers(cx); \ js::gc::MaybeVerifyBarriers(cx); \
JS_EXTENSION_(goto *jumpTable[op]); \ JS_EXTENSION_(goto *jumpTable[op]); \
JS_END_MACRO JS_END_MACRO
# define DO_NEXT_OP(n) JS_BEGIN_MACRO \ # define DO_NEXT_OP(n) JS_BEGIN_MACRO \
@ -1566,7 +1566,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
do_op: do_op:
CHECK_PCCOUNT_INTERRUPTS(); CHECK_PCCOUNT_INTERRUPTS();
js::gc::VerifyBarriers(cx); js::gc::MaybeVerifyBarriers(cx);
switchOp = intN(op) | switchMask; switchOp = intN(op) | switchMask;
do_switch: do_switch:
switch (switchOp) { switch (switchOp) {
@ -4424,6 +4424,6 @@ END_CASE(JSOP_ARRAYPUSH)
leave_on_safe_point: leave_on_safe_point:
#endif #endif
gc::VerifyBarriers(cx, true); gc::MaybeVerifyBarriers(cx, true);
return interpReturnOK; return interpReturnOK;
} }

Просмотреть файл

@ -89,7 +89,7 @@ static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly
Class js::IteratorClass = { Class js::IteratorClass = {
"Iterator", "Iterator",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator), JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
@ -148,9 +148,9 @@ void
NativeIterator::mark(JSTracer *trc) NativeIterator::mark(JSTracer *trc)
{ {
for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++) for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
MarkString(trc, *str, "prop"); MarkString(trc, str, "prop");
if (obj) if (obj)
MarkObject(trc, obj, "obj"); MarkObject(trc, &obj, "obj");
} }
static void static void
@ -1419,7 +1419,7 @@ generator_trace(JSTracer *trc, JSObject *obj)
Class js::GeneratorClass = { Class js::GeneratorClass = {
"Generator", "Generator",
JSCLASS_HAS_PRIVATE, JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */ JS_PropertyStub, /* getProperty */

Просмотреть файл

@ -414,8 +414,11 @@ js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
* with otherwise unreachable objects. But this is way too complex * with otherwise unreachable objects. But this is way too complex
* to justify spending efforts. * to justify spending efforts.
*/ */
for (JSSharpTable::Range r = map->table.all(); !r.empty(); r.popFront()) for (JSSharpTable::Range r = map->table.all(); !r.empty(); r.popFront()) {
MarkObjectRoot(trc, r.front().key, "sharp table entry"); JSObject *tmp = r.front().key;
MarkObjectRoot(trc, &tmp, "sharp table entry");
JS_ASSERT(tmp == r.front().key);
}
} }
#if JS_HAS_TOSOURCE #if JS_HAS_TOSOURCE
@ -2760,6 +2763,13 @@ NewObject(JSContext *cx, Class *clasp, types::TypeObject *type, JSObject *parent
if (!obj) if (!obj)
return NULL; return NULL;
/*
* This will cancel an already-running incremental GC from doing any more
* slices, and it will prevent any future incremental GCs.
*/
if (clasp->trace && !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
cx->runtime->gcIncrementalEnabled = false;
Probes::createObject(cx, obj); Probes::createObject(cx, obj);
return obj; return obj;
} }
@ -3472,7 +3482,7 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
a->slots = reserved.newaslots; a->slots = reserved.newaslots;
a->initSlotRange(0, reserved.bvals.begin(), bcap); a->initSlotRange(0, reserved.bvals.begin(), bcap);
if (a->hasPrivate()) if (a->hasPrivate())
a->setPrivate(bpriv); a->initPrivate(bpriv);
if (b->isNative()) if (b->isNative())
b->shape_->setNumFixedSlots(reserved.newbfixed); b->shape_->setNumFixedSlots(reserved.newbfixed);
@ -3482,7 +3492,7 @@ JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &
b->slots = reserved.newbslots; b->slots = reserved.newbslots;
b->initSlotRange(0, reserved.avals.begin(), acap); b->initSlotRange(0, reserved.avals.begin(), acap);
if (b->hasPrivate()) if (b->hasPrivate())
b->setPrivate(apriv); b->initPrivate(apriv);
/* Make sure the destructor for reserved doesn't free the slots. */ /* Make sure the destructor for reserved doesn't free the slots. */
reserved.newaslots = NULL; reserved.newaslots = NULL;

Просмотреть файл

@ -868,7 +868,7 @@ struct JSObject : js::gc::Cell
return type_; return type_;
} }
const js::HeapPtr<js::types::TypeObject> &typeFromGC() const { js::HeapPtr<js::types::TypeObject> &typeFromGC() {
/* Direct field access for use by GC. */ /* Direct field access for use by GC. */
return type_; return type_;
} }
@ -954,6 +954,7 @@ struct JSObject : js::gc::Cell
inline bool hasPrivate() const; inline bool hasPrivate() const;
inline void *getPrivate() const; inline void *getPrivate() const;
inline void setPrivate(void *data); inline void setPrivate(void *data);
inline void initPrivate(void *data);
/* Access private data for an object with a known number of fixed slots. */ /* Access private data for an object with a known number of fixed slots. */
inline void *getPrivate(size_t nfixed) const; inline void *getPrivate(size_t nfixed) const;
@ -1355,6 +1356,7 @@ struct JSObject : js::gc::Cell
static inline void writeBarrierPre(JSObject *obj); static inline void writeBarrierPre(JSObject *obj);
static inline void writeBarrierPost(JSObject *obj, void *addr); static inline void writeBarrierPost(JSObject *obj, void *addr);
static inline void readBarrier(JSObject *obj);
inline void privateWriteBarrierPre(void **oldval); inline void privateWriteBarrierPre(void **oldval);
inline void privateWriteBarrierPost(void **oldval); inline void privateWriteBarrierPost(void **oldval);

Просмотреть файл

@ -119,6 +119,12 @@ JSObject::setPrivate(void *data)
privateWriteBarrierPost(pprivate); privateWriteBarrierPost(pprivate);
} }
inline void
JSObject::initPrivate(void *data)
{
privateRef(numFixedSlots()) = data;
}
inline bool inline bool
JSObject::enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp) JSObject::enumerate(JSContext *cx, JSIterateOp iterop, js::Value *statep, jsid *idp)
{ {
@ -602,21 +608,33 @@ JSObject::moveDenseArrayElements(uintN dstStart, uintN srcStart, uintN count)
JS_ASSERT(srcStart + count <= getDenseArrayInitializedLength()); JS_ASSERT(srcStart + count <= getDenseArrayInitializedLength());
/* /*
* Use a custom write barrier here since it's performance sensitive. We * Using memmove here would skip write barriers. Also, we need to consider
* only want to barrier the elements that are being overwritten. * an array containing [A, B, C], in the following situation:
*
* 1. Incremental GC marks slot 0 of array (i.e., A), then returns to JS code.
* 2. JS code moves slots 1..2 into slots 0..1, so it contains [B, C, C].
* 3. Incremental GC finishes by marking slots 1 and 2 (i.e., C).
*
* Since normal marking never happens on B, it is very important that the
* write barrier is invoked here on B, despite the fact that it exists in
* the array before and after the move.
*/ */
uintN markStart, markEnd; if (compartment()->needsBarrier()) {
if (dstStart > srcStart) { if (dstStart < srcStart) {
markStart = js::Max(srcStart + count, dstStart); js::HeapValue *dst = elements + dstStart;
markEnd = dstStart + count; js::HeapValue *src = elements + srcStart;
for (unsigned i = 0; i < count; i++, dst++, src++)
*dst = *src;
} else { } else {
markStart = dstStart; js::HeapValue *dst = elements + dstStart + count - 1;
markEnd = js::Min(dstStart + count, srcStart); js::HeapValue *src = elements + srcStart + count - 1;
for (unsigned i = 0; i < count; i++, dst--, src--)
*dst = *src;
} }
prepareElementRangeForOverwrite(markStart, markEnd); } else {
memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value)); memmove(elements + dstStart, elements + srcStart, count * sizeof(js::Value));
} }
}
inline void inline void
JSObject::moveDenseArrayElementsUnbarriered(uintN dstStart, uintN srcStart, uintN count) JSObject::moveDenseArrayElementsUnbarriered(uintN dstStart, uintN srcStart, uintN count)
@ -2126,6 +2144,18 @@ JSObject::writeBarrierPre(JSObject *obj)
#endif #endif
} }
inline void
JSObject::readBarrier(JSObject *obj)
{
#ifdef JSGC_INCREMENTAL
JSCompartment *comp = obj->compartment();
if (comp->needsBarrier()) {
JS_ASSERT(!comp->rt->gcRunning);
MarkObjectUnbarriered(comp->barrierTracer(), obj, "read barrier");
}
#endif
}
inline void inline void
JSObject::writeBarrierPost(JSObject *obj, void *addr) JSObject::writeBarrierPost(JSObject *obj, void *addr)
{ {

Просмотреть файл

@ -282,7 +282,7 @@ PropertyCache::purge(JSContext *cx)
#ifdef JS_THREADSAFE #ifdef JS_THREADSAFE
fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id); fprintf(fp, "thread %lu, ", (unsigned long) cx->thread->id);
#endif #endif
fprintf(fp, "GC %u\n", cx->runtime->gcNumber); fprintf(fp, "GC %lu\n", (unsigned long)cx->runtime->gcNumber);
# define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem) # define P(mem) fprintf(fp, "%11s %10lu\n", #mem, (unsigned long)mem)
P(fills); P(fills);

Просмотреть файл

@ -1311,7 +1311,7 @@ proxy_TypeOf(JSContext *cx, JSObject *proxy)
JS_FRIEND_DATA(Class) js::ObjectProxyClass = { JS_FRIEND_DATA(Class) js::ObjectProxyClass = {
"Proxy", "Proxy",
Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(4), Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(4),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */ JS_PropertyStub, /* getProperty */
@ -1367,7 +1367,7 @@ JS_FRIEND_DATA(Class) js::ObjectProxyClass = {
JS_FRIEND_DATA(Class) js::OuterWindowProxyClass = { JS_FRIEND_DATA(Class) js::OuterWindowProxyClass = {
"Proxy", "Proxy",
Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(4), Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(4),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */ JS_PropertyStub, /* getProperty */
@ -1445,7 +1445,7 @@ proxy_Construct(JSContext *cx, uintN argc, Value *vp)
JS_FRIEND_DATA(Class) js::FunctionProxyClass = { JS_FRIEND_DATA(Class) js::FunctionProxyClass = {
"Proxy", "Proxy",
Class::NON_NATIVE | JSCLASS_HAS_RESERVED_SLOTS(6), Class::NON_NATIVE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_HAS_RESERVED_SLOTS(6),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */ JS_PropertyStub, /* getProperty */

Просмотреть файл

@ -1062,7 +1062,7 @@ JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *n
{ {
JS_ASSERT_IF(oldShape != lastProperty(), JS_ASSERT_IF(oldShape != lastProperty(),
inDictionaryMode() && inDictionaryMode() &&
nativeLookup(cx, oldShape->maybePropid()) == oldShape); nativeLookup(cx, oldShape->propidRef()) == oldShape);
JSObject *self = this; JSObject *self = this;
@ -1086,7 +1086,7 @@ JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *n
PropertyTable &table = self->lastProperty()->table(); PropertyTable &table = self->lastProperty()->table();
Shape **spp = oldShape->isEmptyShape() Shape **spp = oldShape->isEmptyShape()
? NULL ? NULL
: table.search(oldShape->maybePropid(), false); : table.search(oldShape->propidRef(), false);
/* /*
* Splice the new shape into the same position as the old shape, preserving * Splice the new shape into the same position as the old shape, preserving

Просмотреть файл

@ -562,6 +562,10 @@ struct Shape : public js::gc::Cell
return parent; return parent;
} }
HeapPtrShape &previousRef() {
return parent;
}
class Range { class Range {
protected: protected:
friend struct Shape; friend struct Shape;
@ -770,8 +774,12 @@ struct Shape : public js::gc::Cell
slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT); slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
} }
jsid propid() const { JS_ASSERT(!isEmptyShape()); return maybePropid(); } const HeapId &propid() const {
const HeapId &maybePropid() const { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; } JS_ASSERT(!isEmptyShape());
JS_ASSERT(!JSID_IS_VOID(propid_));
return propid_;
}
HeapId &propidRef() { JS_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); } int16_t shortid() const { JS_ASSERT(hasShortID()); return maybeShortid(); }
int16_t maybeShortid() const { return shortid_; } int16_t maybeShortid() const { return shortid_; }
@ -995,7 +1003,7 @@ struct StackShape
StackShape(const Shape *shape) StackShape(const Shape *shape)
: base(shape->base()->unowned()), : base(shape->base()->unowned()),
propid(shape->maybePropid()), propid(const_cast<Shape *>(shape)->propidRef()),
slot_(shape->slotInfo & Shape::SLOT_MASK), slot_(shape->slotInfo & Shape::SLOT_MASK),
attrs(shape->attrs), attrs(shape->attrs),
flags(shape->flags), flags(shape->flags),
@ -1091,7 +1099,7 @@ Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding)
} }
for (Shape *shape = start; shape; shape = shape->parent) { for (Shape *shape = start; shape; shape = shape->parent) {
if (shape->maybePropid() == id) if (shape->propidRef() == id)
return shape; return shape;
} }

Просмотреть файл

@ -319,7 +319,7 @@ void
Bindings::trace(JSTracer *trc) Bindings::trace(JSTracer *trc)
{ {
if (lastBinding) if (lastBinding)
MarkShape(trc, lastBinding, "shape"); MarkShape(trc, &lastBinding, "shape");
} }
#ifdef JS_CRASH_DIAGNOSTICS #ifdef JS_CRASH_DIAGNOSTICS

Просмотреть файл

@ -2180,6 +2180,7 @@ Class ArrayBuffer::slowClass = {
Class js::ArrayBufferClass = { Class js::ArrayBufferClass = {
"ArrayBuffer", "ArrayBuffer",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE |
JSCLASS_IMPLEMENTS_BARRIERS |
Class::NON_NATIVE | Class::NON_NATIVE |
JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) | JSCLASS_HAS_RESERVED_SLOTS(ARRAYBUFFER_RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer), JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
@ -2298,7 +2299,7 @@ JSFunctionSpec _typedArray::jsfuncs[] = { \
{ \ { \
#_typedArray, \ #_typedArray, \
JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \ JSCLASS_HAS_RESERVED_SLOTS(TypedArray::FIELD_MAX) | \
JSCLASS_HAS_PRIVATE | \ JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \
JSCLASS_FOR_OF_ITERATION | \ JSCLASS_FOR_OF_ITERATION | \
Class::NON_NATIVE, \ Class::NON_NATIVE, \
JS_PropertyStub, /* addProperty */ \ JS_PropertyStub, /* addProperty */ \

Просмотреть файл

@ -202,16 +202,20 @@ WatchpointMap::markIteratively(JSTracer *trc)
bool objectIsLive = !IsAboutToBeFinalized(e.key.object); bool objectIsLive = !IsAboutToBeFinalized(e.key.object);
if (objectIsLive || e.value.held) { if (objectIsLive || e.value.held) {
if (!objectIsLive) { if (!objectIsLive) {
MarkObject(trc, e.key.object, "held Watchpoint object"); HeapPtrObject tmp(e.key.object);
MarkObject(trc, &tmp, "held Watchpoint object");
JS_ASSERT(tmp == e.key.object);
marked = true; marked = true;
} }
const HeapId &id = e.key.id; const HeapId &id = e.key.id;
JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
MarkId(trc, id, "WatchKey::id"); HeapId tmp(id.get());
MarkId(trc, &tmp, "WatchKey::id");
JS_ASSERT(tmp.get() == id.get());
if (e.value.closure && IsAboutToBeFinalized(e.value.closure)) { if (e.value.closure && IsAboutToBeFinalized(e.value.closure)) {
MarkObject(trc, e.value.closure, "Watchpoint::closure"); MarkObject(trc, &e.value.closure, "Watchpoint::closure");
marked = true; marked = true;
} }
} }
@ -224,13 +228,17 @@ WatchpointMap::markAll(JSTracer *trc)
{ {
for (Map::Range r = map.all(); !r.empty(); r.popFront()) { for (Map::Range r = map.all(); !r.empty(); r.popFront()) {
Map::Entry &e = r.front(); Map::Entry &e = r.front();
MarkObject(trc, e.key.object, "held Watchpoint object"); HeapPtrObject tmpObj(e.key.object);
MarkObject(trc, &tmpObj, "held Watchpoint object");
JS_ASSERT(tmpObj == e.key.object);
const HeapId &id = e.key.id; const HeapId &id = e.key.id;
JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id)); JS_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
MarkId(trc, id, "WatchKey::id"); HeapId tmpId(id.get());
MarkId(trc, &tmpId, "WatchKey::id");
JS_ASSERT(tmpId.get() == id.get());
MarkObject(trc, e.value.closure, "Watchpoint::closure"); MarkObject(trc, &e.value.closure, "Watchpoint::closure");
} }
} }

Просмотреть файл

@ -62,7 +62,7 @@ bool
WeakMapBase::markAllIteratively(JSTracer *tracer) WeakMapBase::markAllIteratively(JSTracer *tracer)
{ {
bool markedAny = false; bool markedAny = false;
JSRuntime *rt = tracer->context->runtime; JSRuntime *rt = tracer->runtime;
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) { for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) {
if (m->markIteratively(tracer)) if (m->markIteratively(tracer))
markedAny = true; markedAny = true;
@ -73,7 +73,7 @@ WeakMapBase::markAllIteratively(JSTracer *tracer)
void void
WeakMapBase::sweepAll(JSTracer *tracer) WeakMapBase::sweepAll(JSTracer *tracer)
{ {
JSRuntime *rt = tracer->context->runtime; JSRuntime *rt = tracer->runtime;
for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next) for (WeakMapBase *m = rt->gcWeakMapList; m; m = m->next)
m->sweep(tracer); m->sweep(tracer);
} }
@ -314,8 +314,16 @@ WeakMap_mark(JSTracer *trc, JSObject *obj)
static void static void
WeakMap_finalize(JSContext *cx, JSObject *obj) WeakMap_finalize(JSContext *cx, JSObject *obj)
{ {
ObjectValueMap *map = GetObjectMap(obj); if (ObjectValueMap *map = GetObjectMap(obj)) {
map->check();
#ifdef DEBUG
map->~ObjectValueMap();
memset(map, 0xdc, sizeof(ObjectValueMap));
cx->free_(map);
#else
cx->delete_(map); cx->delete_(map);
#endif
}
} }
static JSBool static JSBool
@ -331,7 +339,7 @@ WeakMap_construct(JSContext *cx, uintN argc, Value *vp)
Class js::WeakMapClass = { Class js::WeakMapClass = {
"WeakMap", "WeakMap",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap), JSCLASS_HAS_CACHED_PROTO(JSProto_WeakMap),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */

Просмотреть файл

@ -127,7 +127,7 @@ class WeakMapBase {
// Add ourselves to the list if we are not already in the list. We can already // Add ourselves to the list if we are not already in the list. We can already
// be in the list if the weak map is marked more than once due delayed marking. // be in the list if the weak map is marked more than once due delayed marking.
if (next == WeakMapNotInList) { if (next == WeakMapNotInList) {
JSRuntime *rt = tracer->context->runtime; JSRuntime *rt = tracer->runtime;
next = rt->gcWeakMapList; next = rt->gcWeakMapList;
rt->gcWeakMapList = this; rt->gcWeakMapList = this;
} }
@ -156,6 +156,8 @@ class WeakMapBase {
// Trace all delayed weak map bindings. Used by the cycle collector. // Trace all delayed weak map bindings. Used by the cycle collector.
static void traceAllMappings(WeakMapTracer *tracer); static void traceAllMappings(WeakMapTracer *tracer);
void check() { JS_ASSERT(next == WeakMapNotInList); }
// Remove everything from the live weak map list. // Remove everything from the live weak map list.
static void resetWeakMapList(JSRuntime *rt); static void resetWeakMapList(JSRuntime *rt);
@ -204,7 +206,7 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
void nonMarkingTrace(JSTracer *trc) { void nonMarkingTrace(JSTracer *trc) {
ValueMarkPolicy vp(trc); ValueMarkPolicy vp(trc);
for (Range r = Base::all(); !r.empty(); r.popFront()) for (Range r = Base::all(); !r.empty(); r.popFront())
vp.mark(r.front().value); vp.mark(&r.front().value);
} }
bool markIteratively(JSTracer *trc) { bool markIteratively(JSTracer *trc) {
@ -216,7 +218,7 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, publ
Value &v = r.front().value; Value &v = r.front().value;
/* If the entry is live, ensure its key and value are marked. */ /* If the entry is live, ensure its key and value are marked. */
if (kp.isMarked(k)) { if (kp.isMarked(k)) {
markedAny |= vp.mark(v); markedAny |= vp.mark(&v);
} }
JS_ASSERT_IF(kp.isMarked(k), vp.isMarked(v)); JS_ASSERT_IF(kp.isMarked(k), vp.isMarked(v));
} }
@ -264,10 +266,10 @@ class DefaultMarkPolicy<HeapValue> {
return !IsAboutToBeFinalized(x); return !IsAboutToBeFinalized(x);
return true; return true;
} }
bool mark(HeapValue &x) { bool mark(HeapValue *x) {
if (isMarked(x)) if (isMarked(*x))
return false; return false;
js::gc::MarkValue(tracer, &x, "WeakMap entry"); js::gc::MarkValue(tracer, x, "WeakMap entry");
return true; return true;
} }
}; };
@ -281,8 +283,8 @@ class DefaultMarkPolicy<HeapPtrObject> {
bool isMarked(const HeapPtrObject &x) { bool isMarked(const HeapPtrObject &x) {
return !IsAboutToBeFinalized(x); return !IsAboutToBeFinalized(x);
} }
bool mark(HeapPtrObject &x) { bool mark(HeapPtrObject *x) {
if (isMarked(x)) if (isMarked(*x))
return false; return false;
js::gc::MarkObject(tracer, x, "WeakMap entry"); js::gc::MarkObject(tracer, x, "WeakMap entry");
return true; return true;
@ -298,8 +300,8 @@ class DefaultMarkPolicy<HeapPtrScript> {
bool isMarked(const HeapPtrScript &x) { bool isMarked(const HeapPtrScript &x) {
return !IsAboutToBeFinalized(x); return !IsAboutToBeFinalized(x);
} }
bool mark(HeapPtrScript &x) { bool mark(HeapPtrScript *x) {
if (isMarked(x)) if (isMarked(*x))
return false; return false;
js::gc::MarkScript(tracer, x, "WeakMap entry"); js::gc::MarkScript(tracer, x, "WeakMap entry");
return true; return true;

Просмотреть файл

@ -866,7 +866,7 @@ js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSXML> *cursor)
{ {
for (; cursor; cursor = cursor->next) { for (; cursor; cursor = cursor->next) {
if (cursor->root) if (cursor->root)
MarkXML(trc, (const HeapPtr<JSXML> &)cursor->root, "cursor_root"); MarkXML(trc, &(HeapPtr<JSXML> &)cursor->root, "cursor_root");
} }
} }
@ -875,7 +875,7 @@ js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSObject> *cursor)
{ {
for (; cursor; cursor = cursor->next) { for (; cursor; cursor = cursor->next) {
if (cursor->root) if (cursor->root)
MarkObject(trc, (const HeapPtr<JSObject> &)cursor->root, "cursor_root"); MarkObject(trc, &(HeapPtr<JSObject> &)cursor->root, "cursor_root");
} }
} }
@ -5369,7 +5369,7 @@ out:
JS_FRIEND_DATA(Class) js::XMLClass = { JS_FRIEND_DATA(Class) js::XMLClass = {
js_XML_str, js_XML_str,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_CACHED_PROTO(JSProto_XML), JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
@ -7328,15 +7328,15 @@ void
js_TraceXML(JSTracer *trc, JSXML *xml) js_TraceXML(JSTracer *trc, JSXML *xml)
{ {
if (xml->object) if (xml->object)
MarkObject(trc, xml->object, "object"); MarkObject(trc, &xml->object, "object");
if (xml->name) if (xml->name)
MarkObject(trc, xml->name, "name"); MarkObject(trc, &xml->name, "name");
if (xml->parent) if (xml->parent)
MarkXML(trc, xml->parent, "xml_parent"); MarkXML(trc, &xml->parent, "xml_parent");
if (JSXML_HAS_VALUE(xml)) { if (JSXML_HAS_VALUE(xml)) {
if (xml->xml_value) if (xml->xml_value)
MarkString(trc, xml->xml_value, "value"); MarkString(trc, &xml->xml_value, "value");
return; return;
} }
@ -7345,9 +7345,9 @@ js_TraceXML(JSTracer *trc, JSXML *xml)
if (xml->xml_class == JSXML_CLASS_LIST) { if (xml->xml_class == JSXML_CLASS_LIST) {
if (xml->xml_target) if (xml->xml_target)
MarkXML(trc, xml->xml_target, "target"); MarkXML(trc, &xml->xml_target, "target");
if (xml->xml_targetprop) if (xml->xml_targetprop)
MarkObject(trc, xml->xml_targetprop, "targetprop"); MarkObject(trc, &xml->xml_targetprop, "targetprop");
} else { } else {
MarkObjectRange(trc, xml->xml_namespaces.length, MarkObjectRange(trc, xml->xml_namespaces.length,
xml->xml_namespaces.vector, xml->xml_namespaces.vector,
@ -7898,11 +7898,11 @@ xmlfilter_trace(JSTracer *trc, JSObject *obj)
return; return;
JS_ASSERT(filter->list); JS_ASSERT(filter->list);
MarkXML(trc, filter->list, "list"); MarkXML(trc, &filter->list, "list");
if (filter->result) if (filter->result)
MarkXML(trc, filter->result, "result"); MarkXML(trc, &filter->result, "result");
if (filter->kid) if (filter->kid)
MarkXML(trc, filter->kid, "kid"); MarkXML(trc, &filter->kid, "kid");
/* /*
* We do not need to trace the cursor as that would be done when * We do not need to trace the cursor as that would be done when
@ -7922,7 +7922,7 @@ xmlfilter_finalize(JSContext *cx, JSObject *obj)
Class js_XMLFilterClass = { Class js_XMLFilterClass = {
"XMLFilter", "XMLFilter",
JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS, JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */ JS_PropertyStub, /* getProperty */

Просмотреть файл

@ -3924,7 +3924,7 @@ void
mjit::Compiler::interruptCheckHelper() mjit::Compiler::interruptCheckHelper()
{ {
Jump jump; Jump jump;
if (cx->runtime->gcZeal() >= js::gc::ZealVerifierThreshold) { if (cx->runtime->gcZeal() == js::gc::ZealVerifierValue) {
/* For barrier verification, always take the interrupt so we can verify. */ /* For barrier verification, always take the interrupt so we can verify. */
jump = masm.jump(); jump = masm.jump();
} else { } else {
@ -6892,7 +6892,9 @@ mjit::Compiler::jsop_regexp()
!cx->typeInferenceEnabled() || !cx->typeInferenceEnabled() ||
analysis->localsAliasStack() || analysis->localsAliasStack() ||
types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx), types::TypeSet::HasObjectFlags(cx, globalObj->getType(cx),
types::OBJECT_FLAG_REGEXP_FLAGS_SET)) { types::OBJECT_FLAG_REGEXP_FLAGS_SET) ||
cx->runtime->gcIncrementalState == gc::MARK)
{
prepareStubCall(Uses(0)); prepareStubCall(Uses(0));
masm.move(ImmPtr(obj), Registers::ArgReg1); masm.move(ImmPtr(obj), Registers::ArgReg1);
INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH); INLINE_STUBCALL(stubs::RegExp, REJOIN_FALLTHROUGH);
@ -6946,10 +6948,11 @@ mjit::Compiler::jsop_regexp()
} }
/* /*
* Force creation of the RegExpShared in the script's RegExpObject * Force creation of the RegExpShared in the script's RegExpObject so that
* so that we grab it in the getNewObject template copy. Note that * we grab it in the getNewObject template copy. Note that JIT code is
* JIT code is discarded on every GC, which permits us to burn in * discarded on every GC, which permits us to burn in the pointer to the
* the pointer to the RegExpShared. * RegExpShared. We don't do this during an incremental
* GC, since we don't discard JIT code after every marking slice.
*/ */
if (!reobj->getShared(cx)) if (!reobj->getShared(cx))
return false; return false;

Просмотреть файл

@ -484,7 +484,7 @@ private:
bool hasGlobalReallocation; bool hasGlobalReallocation;
bool oomInVector; // True if we have OOM'd appending to a vector. bool oomInVector; // True if we have OOM'd appending to a vector.
bool overflowICSpace; // True if we added a constant pool in a reserved space. bool overflowICSpace; // True if we added a constant pool in a reserved space.
uint32_t gcNumber; uint64_t gcNumber;
enum { NoApplyTricks, LazyArgsObj } applyTricks; enum { NoApplyTricks, LazyArgsObj } applyTricks;
PCLengthEntry *pcLengths; PCLengthEntry *pcLengths;

Просмотреть файл

@ -402,7 +402,7 @@ struct RecompilationMonitor
unsigned frameExpansions; unsigned frameExpansions;
/* If a GC occurs it may discard jit code on the stack. */ /* If a GC occurs it may discard jit code on the stack. */
unsigned gcNumber; uint64_t gcNumber;
RecompilationMonitor(JSContext *cx) RecompilationMonitor(JSContext *cx)
: cx(cx), : cx(cx),

Просмотреть файл

@ -102,7 +102,7 @@ class PICStubCompiler : public BaseCompiler
JSScript *script; JSScript *script;
ic::PICInfo &pic; ic::PICInfo &pic;
void *stub; void *stub;
uint32_t gcNumber; uint64_t gcNumber;
public: public:
bool canCallHook; bool canCallHook;

Просмотреть файл

@ -878,7 +878,7 @@ stubs::DebuggerStatement(VMFrame &f, jsbytecode *pc)
void JS_FASTCALL void JS_FASTCALL
stubs::Interrupt(VMFrame &f, jsbytecode *pc) stubs::Interrupt(VMFrame &f, jsbytecode *pc)
{ {
gc::VerifyBarriers(f.cx); gc::MaybeVerifyBarriers(f.cx);
if (!js_HandleExecutionInterrupt(f.cx)) if (!js_HandleExecutionInterrupt(f.cx))
THROW(); THROW();

Просмотреть файл

@ -1286,6 +1286,7 @@ static const struct ParamPair {
{"maxMallocBytes", JSGC_MAX_MALLOC_BYTES}, {"maxMallocBytes", JSGC_MAX_MALLOC_BYTES},
{"gcBytes", JSGC_BYTES}, {"gcBytes", JSGC_BYTES},
{"gcNumber", JSGC_NUMBER}, {"gcNumber", JSGC_NUMBER},
{"sliceTimeBudget", JSGC_SLICE_TIME_BUDGET}
}; };
static JSBool static JSBool
@ -1427,6 +1428,35 @@ ScheduleGC(JSContext *cx, uintN argc, jsval *vp)
*vp = JSVAL_VOID; *vp = JSVAL_VOID;
return JS_TRUE; return JS_TRUE;
} }
static JSBool
VerifyBarriers(JSContext *cx, uintN argc, jsval *vp)
{
gc::VerifyBarriers(cx);
*vp = JSVAL_VOID;
return JS_TRUE;
}
static JSBool
GCSlice(JSContext *cx, uintN argc, jsval *vp)
{
uint32_t budget;
if (argc != 1) {
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
(argc < 1)
? JSSMSG_NOT_ENOUGH_ARGS
: JSSMSG_TOO_MANY_ARGS,
"gcslice");
return JS_FALSE;
}
if (!JS_ValueToECMAUint32(cx, vp[2], &budget))
return JS_FALSE;
GCDebugSlice(cx, budget);
*vp = JSVAL_VOID;
return JS_TRUE;
}
#endif /* JS_GC_ZEAL */ #endif /* JS_GC_ZEAL */
typedef struct JSCountHeapNode JSCountHeapNode; typedef struct JSCountHeapNode JSCountHeapNode;
@ -1473,7 +1503,7 @@ CountHeapNotify(JSTracer *trc, void **thingp, JSGCTraceKind kind)
if (node) { if (node) {
countTracer->recycleList = node->next; countTracer->recycleList = node->next;
} else { } else {
node = (JSCountHeapNode *) JS_malloc(trc->context, sizeof *node); node = (JSCountHeapNode *) js_malloc(sizeof *node);
if (!node) { if (!node) {
countTracer->ok = JS_FALSE; countTracer->ok = JS_FALSE;
return; return;
@ -1575,7 +1605,7 @@ CountHeap(JSContext *cx, uintN argc, jsval *vp)
} }
while ((node = countTracer.recycleList) != NULL) { while ((node = countTracer.recycleList) != NULL) {
countTracer.recycleList = node->next; countTracer.recycleList = node->next;
JS_free(cx, node); js_free(node);
} }
JS_DHashTableFinish(&countTracer.visited); JS_DHashTableFinish(&countTracer.visited);
@ -4001,6 +4031,8 @@ static JSFunctionSpec shell_functions[] = {
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
JS_FN("gczeal", GCZeal, 2,0), JS_FN("gczeal", GCZeal, 2,0),
JS_FN("schedulegc", ScheduleGC, 1,0), JS_FN("schedulegc", ScheduleGC, 1,0),
JS_FN("verifybarriers", VerifyBarriers, 0,0),
JS_FN("gcslice", GCSlice, 1,0),
#endif #endif
JS_FN("internalConst", InternalConst, 1,0), JS_FN("internalConst", InternalConst, 1,0),
JS_FN("setDebug", SetDebug, 1,0), JS_FN("setDebug", SetDebug, 1,0),
@ -4114,6 +4146,8 @@ static const char *const shell_help_messages[] = {
" How zealous the garbage collector should be", " How zealous the garbage collector should be",
"schedulegc(num, [compartmentGC?])\n" "schedulegc(num, [compartmentGC?])\n"
" Schedule a GC to happen after num allocations", " Schedule a GC to happen after num allocations",
"verifybarriers() Start or end a run of the write barrier verifier",
"gcslice(n) Run an incremental GC slice that marks ~n objects",
#endif #endif
"internalConst(name)\n" "internalConst(name)\n"
" Query an internal constant for the engine. See InternalConst source for the\n" " Query an internal constant for the engine. See InternalConst source for the\n"
@ -5457,7 +5491,7 @@ main(int argc, char **argv, char **envp)
if (!cx) if (!cx)
return 1; return 1;
JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_COMPARTMENT); JS_SetGCParameter(rt, JSGC_MODE, JSGC_MODE_INCREMENTAL);
JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024); JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 16 * 1024 * 1024);
/* Must be done before creating the global object */ /* Must be done before creating the global object */

Просмотреть файл

@ -1063,15 +1063,21 @@ Debugger::markKeysInCompartment(JSTracer *tracer)
const ObjectMap &objStorage = objects; const ObjectMap &objStorage = objects;
for (ObjectMap::Range r = objStorage.all(); !r.empty(); r.popFront()) { for (ObjectMap::Range r = objStorage.all(); !r.empty(); r.popFront()) {
const HeapPtrObject &key = r.front().key; const HeapPtrObject &key = r.front().key;
if (key->compartment() == comp && IsAboutToBeFinalized(key)) if (key->compartment() == comp && IsAboutToBeFinalized(key)) {
gc::MarkObject(tracer, key, "cross-compartment WeakMap key"); HeapPtrObject tmp(key);
gc::MarkObject(tracer, &tmp, "cross-compartment WeakMap key");
JS_ASSERT(tmp == key);
}
} }
const ObjectMap &envStorage = environments; const ObjectMap &envStorage = environments;
for (ObjectMap::Range r = envStorage.all(); !r.empty(); r.popFront()) { for (ObjectMap::Range r = envStorage.all(); !r.empty(); r.popFront()) {
const HeapPtrObject &key = r.front().key; const HeapPtrObject &key = r.front().key;
if (key->compartment() == comp && IsAboutToBeFinalized(key)) if (key->compartment() == comp && IsAboutToBeFinalized(key)) {
js::gc::MarkObject(tracer, key, "cross-compartment WeakMap key"); HeapPtrObject tmp(key);
js::gc::MarkObject(tracer, &tmp, "cross-compartment WeakMap key");
JS_ASSERT(tmp == key);
}
} }
typedef HashMap<HeapPtrScript, HeapPtrObject, DefaultHasher<HeapPtrScript>, RuntimeAllocPolicy> typedef HashMap<HeapPtrScript, HeapPtrObject, DefaultHasher<HeapPtrScript>, RuntimeAllocPolicy>
@ -1079,8 +1085,11 @@ Debugger::markKeysInCompartment(JSTracer *tracer)
const ScriptMap &scriptStorage = scripts; const ScriptMap &scriptStorage = scripts;
for (ScriptMap::Range r = scriptStorage.all(); !r.empty(); r.popFront()) { for (ScriptMap::Range r = scriptStorage.all(); !r.empty(); r.popFront()) {
const HeapPtrScript &key = r.front().key; const HeapPtrScript &key = r.front().key;
if (key->compartment() == comp && IsAboutToBeFinalized(key)) if (key->compartment() == comp && IsAboutToBeFinalized(key)) {
gc::MarkScript(tracer, key, "cross-compartment WeakMap key"); HeapPtrScript tmp(key);
gc::MarkScript(tracer, &tmp, "cross-compartment WeakMap key");
JS_ASSERT(tmp == key);
}
} }
} }
@ -1176,7 +1185,7 @@ Debugger::markAllIteratively(GCMarker *trc)
* - it isn't already marked * - it isn't already marked
* - it actually has hooks that might be called * - it actually has hooks that might be called
*/ */
const HeapPtrObject &dbgobj = dbg->toJSObject(); HeapPtrObject &dbgobj = dbg->toJSObjectRef();
if (comp && comp != dbgobj->compartment()) if (comp && comp != dbgobj->compartment())
continue; continue;
@ -1186,7 +1195,7 @@ Debugger::markAllIteratively(GCMarker *trc)
* obj could be reachable only via its live, enabled * obj could be reachable only via its live, enabled
* debugger hooks, which may yet be called. * debugger hooks, which may yet be called.
*/ */
MarkObject(trc, dbgobj, "enabled Debugger"); MarkObject(trc, &dbgobj, "enabled Debugger");
markedAny = true; markedAny = true;
dbgMarked = true; dbgMarked = true;
} }
@ -1199,9 +1208,8 @@ Debugger::markAllIteratively(GCMarker *trc)
* The debugger and the script are both live. * The debugger and the script are both live.
* Therefore the breakpoint handler is live. * Therefore the breakpoint handler is live.
*/ */
const HeapPtrObject &handler = bp->getHandler(); if (IsAboutToBeFinalized(bp->getHandler())) {
if (IsAboutToBeFinalized(handler)) { MarkObject(trc, &bp->getHandlerRef(), "breakpoint handler");
MarkObject(trc, bp->getHandler(), "breakpoint handler");
markedAny = true; markedAny = true;
} }
} }
@ -1224,7 +1232,7 @@ void
Debugger::trace(JSTracer *trc) Debugger::trace(JSTracer *trc)
{ {
if (uncaughtExceptionHook) if (uncaughtExceptionHook)
MarkObject(trc, uncaughtExceptionHook, "hooks"); MarkObject(trc, &uncaughtExceptionHook, "hooks");
/* /*
* Mark Debugger.Frame objects. These are all reachable from JS, because the * Mark Debugger.Frame objects. These are all reachable from JS, because the
@ -1235,9 +1243,9 @@ Debugger::trace(JSTracer *trc)
* frames.) * frames.)
*/ */
for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) { for (FrameMap::Range r = frames.all(); !r.empty(); r.popFront()) {
const HeapPtrObject &frameobj = r.front().value; HeapPtrObject &frameobj = r.front().value;
JS_ASSERT(frameobj->getPrivate()); JS_ASSERT(frameobj->getPrivate());
MarkObject(trc, frameobj, "live Debugger.Frame"); MarkObject(trc, &frameobj, "live Debugger.Frame");
} }
/* Trace the weak map from JSScript instances to Debugger.Script objects. */ /* Trace the weak map from JSScript instances to Debugger.Script objects. */
@ -1315,7 +1323,9 @@ Debugger::finalize(JSContext *cx, JSObject *obj)
} }
Class Debugger::jsclass = { Class Debugger::jsclass = {
"Debugger", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT), "Debugger",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUG_COUNT),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Debugger::finalize, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Debugger::finalize,
NULL, /* reserved0 */ NULL, /* reserved0 */
@ -1846,7 +1856,9 @@ DebuggerScript_trace(JSTracer *trc, JSObject *obj)
} }
Class DebuggerScript_class = { Class DebuggerScript_class = {
"Script", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT), "Script",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGSCRIPT_COUNT),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
NULL, /* reserved0 */ NULL, /* reserved0 */
@ -2948,7 +2960,9 @@ DebuggerObject_trace(JSTracer *trc, JSObject *obj)
} }
Class DebuggerObject_class = { Class DebuggerObject_class = {
"Object", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT), "Object",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGOBJECT_COUNT),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
NULL, /* reserved0 */ NULL, /* reserved0 */
@ -3590,7 +3604,9 @@ DebuggerEnv_trace(JSTracer *trc, JSObject *obj)
} }
Class DebuggerEnv_class = { Class DebuggerEnv_class = {
"Environment", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT), "Environment",
JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGENV_COUNT),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
NULL, /* reserved0 */ NULL, /* reserved0 */

Просмотреть файл

@ -236,6 +236,7 @@ class Debugger {
bool init(JSContext *cx); bool init(JSContext *cx);
inline const js::HeapPtrObject &toJSObject() const; inline const js::HeapPtrObject &toJSObject() const;
inline js::HeapPtrObject &toJSObjectRef();
static inline Debugger *fromJSObject(JSObject *obj); static inline Debugger *fromJSObject(JSObject *obj);
static Debugger *fromChildJSObject(JSObject *obj); static Debugger *fromChildJSObject(JSObject *obj);
@ -431,6 +432,7 @@ class Breakpoint {
Breakpoint *nextInDebugger(); Breakpoint *nextInDebugger();
Breakpoint *nextInSite(); Breakpoint *nextInSite();
const HeapPtrObject &getHandler() const { return handler; } const HeapPtrObject &getHandler() const { return handler; }
HeapPtrObject &getHandlerRef() { return handler; }
}; };
Debugger * Debugger *
@ -455,6 +457,13 @@ Debugger::toJSObject() const
return object; return object;
} }
js::HeapPtrObject &
Debugger::toJSObjectRef()
{
JS_ASSERT(object);
return object;
}
Debugger * Debugger *
Debugger::fromJSObject(JSObject *obj) Debugger::fromJSObject(JSObject *obj)
{ {

Просмотреть файл

@ -80,6 +80,14 @@ RegExpObject::getShared(JSContext *cx)
return createShared(cx); return createShared(cx);
} }
inline void
RegExpObject::setShared(JSContext *cx, RegExpShared *shared)
{
if (shared)
shared->prepareForUse(cx);
JSObject::setPrivate(shared);
}
inline void inline void
RegExpObject::setLastIndex(const Value &v) RegExpObject::setLastIndex(const Value &v)
{ {
@ -148,6 +156,12 @@ RegExpToShared(JSContext *cx, JSObject &obj)
return Proxy::regexp_toShared(cx, &obj); return Proxy::regexp_toShared(cx, &obj);
} }
inline void
RegExpShared::prepareForUse(JSContext *cx)
{
gcNumberWhenUsed = cx->runtime->gcNumber;
}
} /* namespace js */ } /* namespace js */
#endif #endif

Просмотреть файл

@ -62,7 +62,7 @@ RegExpObjectBuilder::RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj)
: cx(cx), reobj_(reobj) : cx(cx), reobj_(reobj)
{ {
if (reobj_) if (reobj_)
reobj_->setPrivate(NULL); reobj_->setShared(cx, NULL);
} }
bool bool
@ -74,7 +74,7 @@ RegExpObjectBuilder::getOrCreate()
JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass); JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
if (!obj) if (!obj)
return false; return false;
obj->setPrivate(NULL); obj->initPrivate(NULL);
reobj_ = &obj->asRegExp(); reobj_ = &obj->asRegExp();
return true; return true;
@ -88,7 +88,7 @@ RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto)
JSObject *clone = NewObjectWithGivenProto(cx, &RegExpClass, proto, proto->getParent()); JSObject *clone = NewObjectWithGivenProto(cx, &RegExpClass, proto, proto->getParent());
if (!clone) if (!clone)
return false; return false;
clone->setPrivate(NULL); clone->initPrivate(NULL);
reobj_ = &clone->asRegExp(); reobj_ = &clone->asRegExp();
return true; return true;
@ -103,7 +103,7 @@ RegExpObjectBuilder::build(JSAtom *source, RegExpShared &shared)
if (!reobj_->init(cx, source, shared.getFlags())) if (!reobj_->init(cx, source, shared.getFlags()))
return NULL; return NULL;
reobj_->setPrivate(&shared); reobj_->setShared(cx, &shared);
return reobj_; return reobj_;
} }
@ -330,13 +330,18 @@ RegExpCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t st
static void static void
regexp_trace(JSTracer *trc, JSObject *obj) regexp_trace(JSTracer *trc, JSObject *obj)
{ {
if (trc->runtime->gcRunning) /*
* We have to check both conditions, since:
* 1. During TraceRuntime, gcRunning is set
* 2. When a write barrier executes, IS_GC_MARKING_TRACER is true.
*/
if (trc->runtime->gcRunning && IS_GC_MARKING_TRACER(trc))
obj->setPrivate(NULL); obj->setPrivate(NULL);
} }
Class js::RegExpClass = { Class js::RegExpClass = {
js_RegExp_str, js_RegExp_str,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) | JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp), JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
@ -360,8 +365,8 @@ Class js::RegExpClass = {
regexp_trace regexp_trace
}; };
RegExpShared::RegExpShared(RegExpFlag flags) RegExpShared::RegExpShared(JSRuntime *rt, RegExpFlag flags)
: parenCount(0), flags(flags), activeUseCount(0) : parenCount(0), flags(flags), activeUseCount(0), gcNumberWhenUsed(rt->gcNumber)
{} {}
RegExpObject * RegExpObject *
@ -402,7 +407,7 @@ RegExpObject::createShared(JSContext *cx)
if (!shared) if (!shared)
return NULL; return NULL;
setPrivate(shared); setShared(cx, shared);
return shared; return shared;
} }
@ -616,11 +621,12 @@ RegExpCompartment::init(JSContext *cx)
} }
void void
RegExpCompartment::purge() RegExpCompartment::sweep(JSRuntime *rt)
{ {
for (Map::Enum e(map_); !e.empty(); e.popFront()) { for (Map::Enum e(map_); !e.empty(); e.popFront()) {
/* See the comment on RegExpShared lifetime in RegExpObject.h. */
RegExpShared *shared = e.front().value; RegExpShared *shared = e.front().value;
if (shared->activeUseCount == 0) { if (shared->activeUseCount == 0 && shared->gcNumberWhenUsed < rt->gcStartNumber) {
Foreground::delete_(shared); Foreground::delete_(shared);
e.removeFront(); e.removeFront();
} }
@ -630,14 +636,14 @@ RegExpCompartment::purge()
inline RegExpShared * inline RegExpShared *
RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFlag flags, Type type) RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFlag flags, Type type)
{ {
DebugOnly<size_t> gcNumberBefore = cx->runtime->gcNumber; DebugOnly<uint64_t> gcNumberBefore = cx->runtime->gcNumber;
Key key(keyAtom, flags, type); Key key(keyAtom, flags, type);
Map::AddPtr p = map_.lookupForAdd(key); Map::AddPtr p = map_.lookupForAdd(key);
if (p) if (p)
return p->value; return p->value;
RegExpShared *shared = cx->runtime->new_<RegExpShared>(flags); RegExpShared *shared = cx->runtime->new_<RegExpShared>(cx->runtime, flags);
if (!shared || !shared->compile(cx, source)) if (!shared || !shared->compile(cx, source))
goto error; goto error;

Просмотреть файл

@ -169,6 +169,7 @@ class RegExpObject : public JSObject
inline RegExpShared &shared() const; inline RegExpShared &shared() const;
inline RegExpShared *maybeShared(); inline RegExpShared *maybeShared();
inline RegExpShared *getShared(JSContext *cx); inline RegExpShared *getShared(JSContext *cx);
inline void setShared(JSContext *cx, RegExpShared *shared);
private: private:
friend class RegExpObjectBuilder; friend class RegExpObjectBuilder;
@ -190,6 +191,9 @@ class RegExpObject : public JSObject
RegExpObject() MOZ_DELETE; RegExpObject() MOZ_DELETE;
RegExpObject &operator=(const RegExpObject &reo) MOZ_DELETE; RegExpObject &operator=(const RegExpObject &reo) MOZ_DELETE;
/* Call setShared in preference to setPrivate. */
void setPrivate(void *priv) MOZ_DELETE;
}; };
class RegExpObjectBuilder class RegExpObjectBuilder
@ -293,7 +297,26 @@ class RegExpCode
} /* namespace detail */ } /* namespace detail */
/* The compiled representation of a regexp. */ /*
* A RegExpShared is the compiled representation of a regexp. A RegExpShared is
* pointed to by potentially multiple RegExpObjects. Additionally, C++ code may
* have pointers to RegExpShareds on the stack. The RegExpShareds are tracked in
* a RegExpCompartment hashtable, and most are destroyed on every GC.
*
* During a GC, the trace hook for RegExpObject clears any pointers to
* RegExpShareds so that there will be no dangling pointers when they are
* deleted. However, some RegExpShareds are not deleted:
*
* 1. Any RegExpShared with pointers from the C++ stack is not deleted.
* 2. Any RegExpShared that was installed in a RegExpObject during an
* incremental GC is not deleted. This is because the RegExpObject may have
* been traced through before the new RegExpShared was installed, in which
* case deleting the RegExpShared would turn the RegExpObject's reference
* into a dangling pointer
*
* The activeUseCount and gcNumberWhenUsed fields are used to track these two
* conditions.
*/
class RegExpShared class RegExpShared
{ {
friend class RegExpCompartment; friend class RegExpCompartment;
@ -301,11 +324,12 @@ class RegExpShared
detail::RegExpCode code; detail::RegExpCode code;
uintN parenCount; uintN parenCount;
RegExpFlag flags; RegExpFlag flags;
size_t activeUseCount; size_t activeUseCount; /* See comment above. */
uint64_t gcNumberWhenUsed; /* See comment above. */
bool compile(JSContext *cx, JSAtom *source); bool compile(JSContext *cx, JSAtom *source);
RegExpShared(RegExpFlag flags); RegExpShared(JSRuntime *rt, RegExpFlag flags);
JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR; JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
public: public:
@ -338,6 +362,9 @@ class RegExpShared
RegExpShared &operator*() { JS_ASSERT(initialized()); return *re_; } RegExpShared &operator*() { JS_ASSERT(initialized()); return *re_; }
}; };
/* Called when a RegExpShared is installed into a RegExpObject. */
inline void prepareForUse(JSContext *cx);
/* Primary interface: run this regular expression on the given string. */ /* Primary interface: run this regular expression on the given string. */
RegExpRunStatus RegExpRunStatus
@ -388,7 +415,7 @@ class RegExpCompartment
~RegExpCompartment(); ~RegExpCompartment();
bool init(JSContext *cx); bool init(JSContext *cx);
void purge(); void sweep(JSRuntime *rt);
/* Return a regexp corresponding to the given (source, flags) pair. */ /* Return a regexp corresponding to the given (source, flags) pair. */
RegExpShared *get(JSContext *cx, JSAtom *source, RegExpFlag flags); RegExpShared *get(JSContext *cx, JSAtom *source, RegExpFlag flags);

Просмотреть файл

@ -71,7 +71,7 @@ resc_trace(JSTracer *trc, JSObject *obj)
Class js::RegExpStaticsClass = { Class js::RegExpStaticsClass = {
"RegExpStatics", "RegExpStatics",
JSCLASS_HAS_PRIVATE, JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
JS_PropertyStub, /* addProperty */ JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */ JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */ JS_PropertyStub, /* getProperty */

Просмотреть файл

@ -205,11 +205,11 @@ class RegExpStatics
return get(0, 1) - get(0, 0) > 0; return get(0, 1) - get(0, 0) > 0;
} }
void mark(JSTracer *trc) const { void mark(JSTracer *trc) {
if (pendingInput) if (pendingInput)
MarkString(trc, pendingInput, "res->pendingInput"); MarkString(trc, &pendingInput, "res->pendingInput");
if (matchPairsInput) if (matchPairsInput)
MarkString(trc, matchPairsInput, "res->matchPairsInput"); MarkString(trc, &matchPairsInput, "res->matchPairsInput");
} }
bool pairIsPresent(size_t pairNum) const { bool pairIsPresent(size_t pairNum) const {

Просмотреть файл

@ -532,6 +532,15 @@ StackSpace::mark(JSTracer *trc)
} }
} }
void
StackSpace::markActiveCompartments()
{
for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev())
MarkCompartmentActive(fp);
}
}
JS_FRIEND_API(bool) JS_FRIEND_API(bool)
StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals, StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
JSCompartment *dest) const JSCompartment *dest) const

Просмотреть файл

@ -1555,6 +1555,9 @@ class StackSpace
void mark(JSTracer *trc); void mark(JSTracer *trc);
void markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc); void markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc);
/* Called during GC: sets active flag on compartments with active frames. */
void markActiveCompartments();
/* We only report the committed size; uncommitted size is uninteresting. */ /* We only report the committed size; uncommitted size is uninteresting. */
JS_FRIEND_API(size_t) sizeOfCommitted(); JS_FRIEND_API(size_t) sizeOfCommitted();
}; };

Просмотреть файл

@ -398,7 +398,7 @@ enum nsGCType {
}; };
%} %}
[uuid(686bb1d0-4711-11e1-b86c-0800200c9a66)] [uuid(e92bf5e0-494c-11e1-b86c-0800200c9a66)]
interface nsIXPConnect : nsISupports interface nsIXPConnect : nsISupports
{ {
%{ C++ %{ C++
@ -734,6 +734,12 @@ interface nsIXPConnect : nsISupports
*/ */
void GarbageCollect(in PRUint32 reason, in PRUint32 kind); void GarbageCollect(in PRUint32 reason, in PRUint32 kind);
/**
* Signals a good place to do an incremental GC slice, because the
* browser is drawing a frame.
*/
void NotifyDidPaint();
/** /**
* Define quick stubs on the given object, @a proto. * Define quick stubs on the given object, @a proto.
* *

Просмотреть файл

@ -604,7 +604,8 @@ void XPCWrappedNativeTearOff::SetJSObject(JSObject* JSObj)
inline inline
XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff() XPCWrappedNativeTearOff::~XPCWrappedNativeTearOff()
{ {
NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObjectPreserveColor()), "tearoff not empty in dtor"); NS_ASSERTION(!(GetInterface()||GetNative()||GetJSObjectPreserveColor()),
"tearoff not empty in dtor");
} }
/***************************************************************************/ /***************************************************************************/

Просмотреть файл

@ -911,6 +911,8 @@ JSBool XPCJSRuntime::GCCallback(JSContext *cx, JSGCStatus status)
#ifdef XPC_TRACK_DEFERRED_RELEASES #ifdef XPC_TRACK_DEFERRED_RELEASES
printf("XPC - End deferred Releases\n"); printf("XPC - End deferred Releases\n");
#endif #endif
self->GetXPConnect()->ClearGCBeforeCC();
break; break;
} }
default: default:
@ -1890,6 +1892,18 @@ AccumulateTelemetryCallback(int id, uint32_t sample)
case JS_TELEMETRY_GC_SWEEP_MS: case JS_TELEMETRY_GC_SWEEP_MS:
Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample); Telemetry::Accumulate(Telemetry::GC_SWEEP_MS, sample);
break; break;
case JS_TELEMETRY_GC_SLICE_MS:
Telemetry::Accumulate(Telemetry::GC_SLICE_MS, sample);
break;
case JS_TELEMETRY_GC_MMU_50:
Telemetry::Accumulate(Telemetry::GC_MMU_50, sample);
break;
case JS_TELEMETRY_GC_RESET:
Telemetry::Accumulate(Telemetry::GC_RESET, sample);
break;
case JS_TELEMETRY_GC_INCREMENTAL_DISABLED:
Telemetry::Accumulate(Telemetry::GC_INCREMENTAL_DISABLED, sample);
break;
} }
} }

Просмотреть файл

@ -406,8 +406,6 @@ nsXPConnect::Collect(PRUint32 reason, PRUint32 kind)
// To improve debugging, if DEBUG_CC is defined all JS objects are // To improve debugging, if DEBUG_CC is defined all JS objects are
// traversed. // traversed.
mNeedGCBeforeCC = false;
XPCCallContext ccx(NATIVE_CALLER); XPCCallContext ccx(NATIVE_CALLER);
if (!ccx.IsValid()) if (!ccx.IsValid())
return; return;
@ -424,6 +422,8 @@ nsXPConnect::Collect(PRUint32 reason, PRUint32 kind)
js::gcreason::Reason gcreason = (js::gcreason::Reason)reason; js::gcreason::Reason gcreason = (js::gcreason::Reason)reason;
if (kind == nsGCShrinking) { if (kind == nsGCShrinking) {
js::ShrinkingGC(cx, gcreason); js::ShrinkingGC(cx, gcreason);
} else if (kind == nsGCIncremental) {
js::IncrementalGC(cx, gcreason);
} else { } else {
MOZ_ASSERT(kind == nsGCNormal); MOZ_ASSERT(kind == nsGCNormal);
js::GCForReason(cx, gcreason); js::GCForReason(cx, gcreason);
@ -2825,6 +2825,23 @@ nsXPConnect::GetTelemetryValue(JSContext *cx, jsval *rval)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsXPConnect::NotifyDidPaint()
{
JSRuntime *rt = mRuntime->GetJSRuntime();
if (!js::WantGCSlice(rt))
return NS_OK;
XPCCallContext ccx(NATIVE_CALLER);
if (!ccx.IsValid())
return UnexpectedFailure(NS_ERROR_FAILURE);
JSContext *cx = ccx.GetJSContext();
js::NotifyDidPaint(cx);
return NS_OK;
}
/* These are here to be callable from a debugger */ /* These are here to be callable from a debugger */
JS_BEGIN_EXTERN_C JS_BEGIN_EXTERN_C
JS_EXPORT_API(void) DumpJSStack() JS_EXPORT_API(void) DumpJSStack()

Просмотреть файл

@ -318,7 +318,8 @@ typedef nsDataHashtable<xpc::PtrAndPrincipalHashKey, JSCompartment *> XPCCompart
return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY return (result || !src) ? NS_OK : NS_ERROR_OUT_OF_MEMORY
#define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1)) #define WRAPPER_SLOTS (JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS | \
JSCLASS_HAS_RESERVED_SLOTS(1))
#define INVALID_OBJECT ((JSObject *)1) #define INVALID_OBJECT ((JSObject *)1)
@ -520,6 +521,7 @@ public:
JSBool IsShuttingDown() const {return mShuttingDown;} JSBool IsShuttingDown() const {return mShuttingDown;}
void EnsureGCBeforeCC() { mNeedGCBeforeCC = true; } void EnsureGCBeforeCC() { mNeedGCBeforeCC = true; }
void ClearGCBeforeCC() { mNeedGCBeforeCC = false; }
nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info); nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info); nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);

Просмотреть файл

@ -75,7 +75,8 @@ xpc_CreateMTGlobalObject(JSContext *cx, JSClass *clasp,
#define XPCONNECT_GLOBAL_FLAGS \ #define XPCONNECT_GLOBAL_FLAGS \
JSCLASS_XPCONNECT_GLOBAL | JSCLASS_HAS_PRIVATE | \ JSCLASS_XPCONNECT_GLOBAL | JSCLASS_HAS_PRIVATE | \
JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(1) JSCLASS_PRIVATE_IS_NSISUPPORTS | JSCLASS_IMPLEMENTS_BARRIERS | \
JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(1)
void void
TraceXPCGlobal(JSTracer *trc, JSObject *obj); TraceXPCGlobal(JSTracer *trc, JSObject *obj);
@ -182,8 +183,12 @@ xpc_UnmarkGrayObjectRecursive(JSObject* obj);
inline void inline void
xpc_UnmarkGrayObject(JSObject *obj) xpc_UnmarkGrayObject(JSObject *obj)
{ {
if (obj && xpc_IsGrayGCThing(obj)) if (obj) {
if (xpc_IsGrayGCThing(obj))
xpc_UnmarkGrayObjectRecursive(obj); xpc_UnmarkGrayObjectRecursive(obj);
else if (js::IsIncrementalBarrierNeededOnObject(obj))
js::IncrementalReferenceBarrier(obj);
}
} }
// If aVariant is an XPCVariant, this marks the object to be in aGeneration. // If aVariant is an XPCVariant, this marks the object to be in aGeneration.

Просмотреть файл

@ -5428,6 +5428,24 @@ PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
} }
} }
class nsAutoNotifyDidPaint
{
public:
nsAutoNotifyDidPaint(bool aWillSendDidPaint)
: mWillSendDidPaint(aWillSendDidPaint)
{
}
~nsAutoNotifyDidPaint()
{
if (!mWillSendDidPaint && nsContentUtils::XPConnect()) {
nsContentUtils::XPConnect()->NotifyDidPaint();
}
}
private:
bool mWillSendDidPaint;
};
void void
PresShell::Paint(nsIView* aViewToPaint, PresShell::Paint(nsIView* aViewToPaint,
nsIWidget* aWidgetToPaint, nsIWidget* aWidgetToPaint,
@ -5451,6 +5469,8 @@ PresShell::Paint(nsIView* aViewToPaint,
NS_ASSERTION(aViewToPaint, "null view"); NS_ASSERTION(aViewToPaint, "null view");
NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget"); NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget");
nsAutoNotifyDidPaint notifyDidPaint(aWillSendDidPaint);
nsPresContext* presContext = GetPresContext(); nsPresContext* presContext = GetPresContext();
AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint); AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
@ -5744,6 +5764,7 @@ PresShell::RecordMouseLocation(nsGUIEvent* aEvent)
} }
} }
#ifdef MOZ_TOUCH
static void static void
EvictTouchPoint(nsCOMPtr<nsIDOMTouch>& aTouch) EvictTouchPoint(nsCOMPtr<nsIDOMTouch>& aTouch)
{ {
@ -5791,6 +5812,7 @@ AppendToTouchList(const PRUint32& aKey, nsCOMPtr<nsIDOMTouch>& aData, void *aTou
touches->AppendElement(aData); touches->AppendElement(aData);
return PL_DHASH_NEXT; return PL_DHASH_NEXT;
} }
#endif // MOZ_TOUCH
nsresult nsresult
PresShell::HandleEvent(nsIFrame *aFrame, PresShell::HandleEvent(nsIFrame *aFrame,
@ -7221,6 +7243,10 @@ PresShell::DidPaint()
if (rootPresContext == mPresContext) { if (rootPresContext == mPresContext) {
rootPresContext->UpdatePluginGeometry(); rootPresContext->UpdatePluginGeometry();
} }
if (nsContentUtils::XPConnect()) {
nsContentUtils::XPConnect()->NotifyDidPaint();
}
} }
bool bool

Просмотреть файл

@ -1939,10 +1939,11 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
needToRecoverState = false; needToRecoverState = false;
// Update aState.mPrevChild as if we had reflowed all of the frames in // Update aState.mPrevChild as if we had reflowed all of the frames in
// this line. This is expensive in some cases, since it requires // this line.
// walking |GetNextSibling|.
if (line->IsDirty()) if (line->IsDirty())
aState.mPrevChild = line.prev()->LastChild(); NS_ASSERTION(line->mFirstChild->GetPrevSibling() ==
line.prev()->LastChild(), "unexpected line frames");
aState.mPrevChild = line->mFirstChild->GetPrevSibling();
} }
// Now repair the line and update |aState.mY| by calling // Now repair the line and update |aState.mY| by calling
@ -2132,9 +2133,11 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
aState.ReconstructMarginAbove(line); aState.ReconstructMarginAbove(line);
// Update aState.mPrevChild as if we had reflowed all of the frames in // Update aState.mPrevChild as if we had reflowed all of the frames in
// the last line. This is expensive in some cases, since it requires // the last line.
// walking |GetNextSibling|. NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() ==
aState.mPrevChild = line.prev()->LastChild(); line.prev()->LastChild(), "unexpected line frames");
aState.mPrevChild =
line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling();
} }
// Should we really have to do this? // Should we really have to do this?
@ -4396,7 +4399,12 @@ nsBlockFrame::PushLines(nsBlockReflowState& aState,
if (firstLine) { if (firstLine) {
mFrames.Clear(); mFrames.Clear();
} else { } else {
mFrames.RemoveFramesAfter(aLineBefore->LastChild()); nsIFrame* f = overBegin->mFirstChild;
nsIFrame* lineBeforeLastFrame =
f ? f->GetPrevSibling() : aLineBefore->LastChild();
NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(),
"unexpected line frames");
mFrames.RemoveFramesAfter(lineBeforeLastFrame);
} }
if (!overflowLines->empty()) { if (!overflowLines->empty()) {
// XXXbz If we switch overflow lines to nsFrameList, we should // XXXbz If we switch overflow lines to nsFrameList, we should
@ -4713,7 +4721,9 @@ nsBlockFrame::AppendFrames(ChildListID aListID,
} }
// Find the proper last-child for where the append should go // Find the proper last-child for where the append should go
nsIFrame* lastKid = mLines.empty() ? nsnull : mLines.back()->LastChild(); nsIFrame* lastKid = mFrames.LastChild();
NS_ASSERTION((mLines.empty() ? nsnull : mLines.back()->LastChild()) ==
lastKid, "out-of-sync mLines / mFrames");
// Add frames after the last child // Add frames after the last child
#ifdef NOISY_REFLOW_REASON #ifdef NOISY_REFLOW_REASON
@ -5394,8 +5404,16 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRUint32 aFlags)
// If the frame being deleted is the last one on the line then // If the frame being deleted is the last one on the line then
// optimize away the line->Contains(next-in-flow) call below. // optimize away the line->Contains(next-in-flow) call below.
bool isLastFrameOnLine = (1 == line->GetChildCount() || bool isLastFrameOnLine = 1 == line->GetChildCount();
line->LastChild() == aDeletedFrame); if (!isLastFrameOnLine) {
line_iterator next = line.next();
nsIFrame* lastFrame = next != line_end ?
next->mFirstChild->GetPrevSibling() :
(searchingOverflowList ? line->LastChild() : mFrames.LastChild());
NS_ASSERTION(next == line_end || lastFrame == line->LastChild(),
"unexpected line frames");
isLastFrameOnLine = lastFrame == aDeletedFrame;
}
// Remove aDeletedFrame from the line // Remove aDeletedFrame from the line
nsIFrame* nextFrame = aDeletedFrame->GetNextSibling(); nsIFrame* nextFrame = aDeletedFrame->GetNextSibling();

Просмотреть файл

@ -366,7 +366,7 @@ nsColumnSetFrame::ChooseColumnStrategy(const nsHTMLReflowState& aReflowState)
bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE; bool isBalancing = colStyle->mColumnFill == NS_STYLE_COLUMN_FILL_BALANCE;
if (isBalancing) { if (isBalancing) {
const PRUint32 MAX_NESTED_COLUMN_BALANCING = 5; const PRUint32 MAX_NESTED_COLUMN_BALANCING = 2;
PRUint32 cnt = 1; PRUint32 cnt = 1;
for (const nsHTMLReflowState* rs = aReflowState.parentReflowState; for (const nsHTMLReflowState* rs = aReflowState.parentReflowState;
rs && cnt < MAX_NESTED_COLUMN_BALANCING; rs && cnt < MAX_NESTED_COLUMN_BALANCING;

Просмотреть файл

@ -242,6 +242,7 @@ fails-if(Android) random-if(gtk2Widget) != text-language-01.xhtml text-language-
== text-layout-03.svg text-layout-03-ref.svg == text-layout-03.svg text-layout-03-ref.svg
== text-layout-04.svg text-layout-04-ref.svg == text-layout-04.svg text-layout-04-ref.svg
== text-layout-05.svg text-layout-05-ref.svg == text-layout-05.svg text-layout-05-ref.svg
== text-layout-06.svg text-layout-06-ref.svg
== text-scale-01.svg text-scale-01-ref.svg == text-scale-01.svg text-scale-01-ref.svg
== text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg
== stroke-dasharray-and-pathLength-01.svg pass.svg == stroke-dasharray-and-pathLength-01.svg pass.svg

Просмотреть файл

@ -0,0 +1,10 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<title>Reference to check fill and stroke handling</title>
<text x="50" y="80" font-size="80" fill="blue" stroke="none">A B</text>
<text x="50" y="80" font-size="80" fill="none" stroke="yellow" stroke-width="2">A B</text>
</svg>

После

Ширина:  |  Высота:  |  Размер: 395 B

Просмотреть файл

@ -0,0 +1,9 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<title>Testcase to check fill and stroke handling</title>
<text x="50" y="80" font-size="80" fill="blue" stroke="yellow" stroke-width="2">A B</text>
</svg>

После

Ширина:  |  Высота:  |  Размер: 320 B

Просмотреть файл

@ -374,7 +374,7 @@ nsSVGGlyphFrame::PaintSVG(nsSVGRenderState *aContext,
iter.SetInitialMatrix(gfx); iter.SetInitialMatrix(gfx);
nsRefPtr<gfxPattern> strokePattern; nsRefPtr<gfxPattern> strokePattern;
DrawMode drawMode = SetupCairoState(gfx, &strokePattern); DrawMode drawMode = SetupCairoState(gfx, getter_AddRefs(strokePattern));
if (drawMode) { if (drawMode) {
DrawCharacters(&iter, gfx, drawMode, strokePattern); DrawCharacters(&iter, gfx, drawMode, strokePattern);
@ -884,39 +884,45 @@ nsSVGGlyphFrame::GetBaselineOffset(float aMetricsScale)
} }
DrawMode DrawMode
nsSVGGlyphFrame::SetupCairoState(gfxContext *context, nsRefPtr<gfxPattern> *strokePattern) { nsSVGGlyphFrame::SetupCairoState(gfxContext *aContext, gfxPattern **aStrokePattern)
{
DrawMode toDraw = DrawMode(0); DrawMode toDraw = DrawMode(0);
const nsStyleSVG* style = GetStyleSVG(); const nsStyleSVG* style = GetStyleSVG();
if (HasStroke()) { if (HasStroke()) {
gfxContextMatrixAutoSaveRestore matrixRestore(context); gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
context->IdentityMatrix(); aContext->IdentityMatrix();
toDraw = DrawMode(toDraw | gfxFont::GLYPH_STROKE); toDraw = DrawMode(toDraw | gfxFont::GLYPH_STROKE);
SetupCairoStrokeHitGeometry(context); SetupCairoStrokeHitGeometry(aContext);
float opacity = style->mStrokeOpacity; float opacity = style->mStrokeOpacity;
nsSVGPaintServerFrame *ps = GetPaintServer(&style->mStroke, nsSVGPaintServerFrame *ps = GetPaintServer(&style->mStroke,
nsSVGEffects::StrokeProperty()); nsSVGEffects::StrokeProperty());
nsRefPtr<gfxPattern> strokePattern;
if (ps) { if (ps) {
// Gradient or Pattern: can get pattern directly from frame // Gradient or Pattern: can get pattern directly from frame
*strokePattern = ps->GetPaintServerPattern(this, opacity); strokePattern = ps->GetPaintServerPattern(this, opacity);
}
NS_ASSERTION(*strokePattern, "No pattern returned from paint server"); if (!strokePattern) {
} else {
nscolor color; nscolor color;
nsSVGUtils::GetFallbackOrPaintColor(context, GetStyleContext(), nsSVGUtils::GetFallbackOrPaintColor(aContext, GetStyleContext(),
&nsStyleSVG::mStroke, &opacity, &nsStyleSVG::mStroke, &opacity,
&color); &color);
*strokePattern = new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0, strokePattern = new gfxPattern(gfxRGBA(NS_GET_R(color) / 255.0,
NS_GET_G(color) / 255.0, NS_GET_G(color) / 255.0,
NS_GET_B(color) / 255.0, NS_GET_B(color) / 255.0,
NS_GET_A(color) / 255.0 * opacity)); NS_GET_A(color) / 255.0 * opacity));
} }
*aStrokePattern = nsnull;
strokePattern.swap(*aStrokePattern);
} }
if (SetupCairoFill(context)) { if (SetupCairoFill(aContext)) {
toDraw = DrawMode(toDraw | gfxFont::GLYPH_FILL); toDraw = DrawMode(toDraw | gfxFont::GLYPH_FILL);
} }

Просмотреть файл

@ -268,7 +268,8 @@ protected:
bool mTrimTrailingWhitespace; bool mTrimTrailingWhitespace;
private: private:
DrawMode SetupCairoState(gfxContext *context, nsRefPtr<gfxPattern> *strokePattern); DrawMode SetupCairoState(gfxContext *aContext,
gfxPattern **aStrokePattern);
}; };
#endif #endif

Просмотреть файл

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg">
<text stroke="url(#p)">t</text>
<pattern id="p"/>
</svg>

После

Ширина:  |  Высота:  |  Размер: 102 B

Просмотреть файл

@ -123,3 +123,4 @@ load 709920-1.svg
load 709920-2.svg load 709920-2.svg
load 713413-1.svg load 713413-1.svg
load 722003-1.svg load 722003-1.svg
load 725918-1.svg

Просмотреть файл

@ -1,5 +1,6 @@
function test() { function test() {
waitForExplicitFinish(); waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedTab = gBrowser.addTab();
SpecialPowers.setIntPref("ui.tooltipDelay", 0); SpecialPowers.setIntPref("ui.tooltipDelay", 0);
@ -60,8 +61,10 @@ function test() {
EventUtils.synthesizeMouseAtCenter(p1, { type: "mousemove" }, win); EventUtils.synthesizeMouseAtCenter(p1, { type: "mousemove" }, win);
} }
gBrowser.selectedBrowser.addEventListener("load", gBrowser.selectedBrowser.addEventListener("load", function loadListener() {
function () { setTimeout(onLoad, 0); }, true); gBrowser.selectedBrowser.removeEventListener("load", loadListener, true);
setTimeout(onLoad, 0);
}, true);
content.location = "data:text/html," + content.location = "data:text/html," +
"<p id=\"p1\" title=\"tooltip is here\">This paragraph has a tooltip.</p>" + "<p id=\"p1\" title=\"tooltip is here\">This paragraph has a tooltip.</p>" +

Просмотреть файл

@ -656,6 +656,8 @@ pref("javascript.options.typeinference", true);
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.gc_per_compartment", true); pref("javascript.options.mem.gc_per_compartment", true);
pref("javascript.options.mem.gc_incremental", true);
pref("javascript.options.mem.gc_incremental_slice_ms", 10);
pref("javascript.options.mem.log", false); pref("javascript.options.mem.log", false);
pref("javascript.options.gc_on_memory_pressure", true); pref("javascript.options.gc_on_memory_pressure", true);

Просмотреть файл

@ -4,6 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <dlfcn.h> #include <dlfcn.h>
#include "android/log.h"
#include "cutils/properties.h" #include "cutils/properties.h"
#include "base/basictypes.h" #include "base/basictypes.h"
@ -12,6 +13,7 @@
#include "nsStreamUtils.h" #include "nsStreamUtils.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "nsRawStructs.h" #include "nsRawStructs.h"
#include "prinit.h"
#define USE_GS2_LIBCAMERA #define USE_GS2_LIBCAMERA
#define CameraHardwareInterface CameraHardwareInterface_SGS2 #define CameraHardwareInterface CameraHardwareInterface_SGS2
@ -48,11 +50,11 @@ using namespace mozilla;
class CameraHardwareInterface { class CameraHardwareInterface {
public: public:
typedef enum { enum Type {
CAMERA_SGS2, CAMERA_SGS2,
CAMERA_MAGURO, CAMERA_MAGURO,
CAMERA_DEFAULT CAMERA_DEFAULT
} Type; };
static Type getType() { static Type getType() {
char propValue[PROPERTY_VALUE_MAX]; char propValue[PROPERTY_VALUE_MAX];
@ -60,7 +62,7 @@ class CameraHardwareInterface {
if (!strcmp(propValue, "GT-I9100")) if (!strcmp(propValue, "GT-I9100"))
return CAMERA_SGS2; return CAMERA_SGS2;
if (!strcmp(propValue, "MSM7627A_SKU1") || !strcmp(propValue, "MSM7627A_SKU3")) if (!strcmp(propValue, "msm7627a_sku1") || !strcmp(propValue, "MSM7627A_SKU3"))
return CAMERA_MAGURO; return CAMERA_MAGURO;
printf_stderr("CameraHardwareInterface : unsupported camera for device %s\n", propValue); printf_stderr("CameraHardwareInterface : unsupported camera for device %s\n", propValue);
@ -89,30 +91,25 @@ class CameraHardwareInterface {
CameraHardwareInterface(PRUint32 aCamera = 0) { }; CameraHardwareInterface(PRUint32 aCamera = 0) { };
}; };
class DlopenWrapper { // Intentionally not trying to dlclose() this handle. That's playing
public: // Russian roulette with security bugs.
DlopenWrapper() : mHandle(nsnull) { }; static void* sCameraLib;
static PRCallOnceType sInitCameraLib;
DlopenWrapper(const char* aLibrary) : mHandle(nsnull) { static PRStatus
mHandle = dlopen(aLibrary, RTLD_LAZY); InitCameraLib()
}; {
sCameraLib = dlopen("/system/lib/libcamera.so", RTLD_LAZY);
// We might fail to open the camera lib. That's OK.
return PR_SUCCESS;
}
~DlopenWrapper() { static void*
if (mHandle) GetCameraLibHandle()
dlclose(mHandle); {
}; PR_CallOnce(&sInitCameraLib, InitCameraLib);
return sCameraLib;
bool opened() { }
return mHandle != nsnull;
};
void* dlsym(const char* aFunction) {
return ::dlsym(mHandle, aFunction);
};
protected:
void* mHandle;
};
template<class T> class CameraImpl : public CameraHardwareInterface { template<class T> class CameraImpl : public CameraHardwareInterface {
public: public:
@ -121,14 +118,13 @@ template<class T> class CameraImpl : public CameraHardwareInterface {
typedef sp<T> (*HAL_openCameraHardware_MAGURO)(int, int); typedef sp<T> (*HAL_openCameraHardware_MAGURO)(int, int);
CameraImpl(PRUint32 aCamera = 0) : mOk(false), mCamera(nsnull) { CameraImpl(PRUint32 aCamera = 0) : mOk(false), mCamera(nsnull) {
DlopenWrapper wrapper("/system/lib/libcamera.so"); void* cameraLib = GetCameraLibHandle();
if (!cameraLib) {
if (!wrapper.opened()) printf_stderr("CameraImpl: Failed to dlopen() camera library.");
return; return;
}
mOk = true; void *hal = dlsym(cameraLib, "HAL_openCameraHardware");
void *hal = wrapper.dlsym("HAL_openCameraHardware");
HAL_openCameraHardware_DEFAULT funct0; HAL_openCameraHardware_DEFAULT funct0;
HAL_openCameraHardware_SGS2 funct1; HAL_openCameraHardware_SGS2 funct1;
HAL_openCameraHardware_MAGURO funct2; HAL_openCameraHardware_MAGURO funct2;
@ -146,6 +142,11 @@ template<class T> class CameraImpl : public CameraHardwareInterface {
mCamera = funct0(aCamera); mCamera = funct0(aCamera);
break; break;
} }
mOk = mCamera != nsnull;
if (!mOk) {
printf_stderr("CameraImpl: HAL_openCameraHardware() returned NULL (no camera interface).");
}
} }
bool ok() { bool ok() {
@ -251,12 +252,11 @@ GonkCameraInputStream::DataCallback(int32_t aMsgType, const sp<IMemory>& aDataPt
PRUint32 PRUint32
GonkCameraInputStream::getNumberOfCameras() { GonkCameraInputStream::getNumberOfCameras() {
typedef int (*HAL_getNumberOfCamerasFunct)(void); typedef int (*HAL_getNumberOfCamerasFunct)(void);
DlopenWrapper wrapper("/system/lib/libcamera.so"); void* cameraLib = GetCameraLibHandle();
if (!cameraLib)
if (!wrapper.opened())
return 0; return 0;
void *hal = wrapper.dlsym("HAL_getNumberOfCameras"); void *hal = dlsym(cameraLib, "HAL_getNumberOfCameras");
if (nsnull == hal) if (nsnull == hal)
return 0; return 0;

Просмотреть файл

@ -81,6 +81,10 @@ HISTOGRAM_BOOLEAN(GC_IS_COMPARTMENTAL, "Is it a compartmental GC?")
HISTOGRAM(GC_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC (ms)") HISTOGRAM(GC_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC (ms)")
HISTOGRAM(GC_MARK_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC mark phase (ms)") HISTOGRAM(GC_MARK_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC mark phase (ms)")
HISTOGRAM(GC_SWEEP_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC sweep phase (ms)") HISTOGRAM(GC_SWEEP_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running JS GC sweep phase (ms)")
HISTOGRAM(GC_SLICE_MS, 1, 10000, 50, EXPONENTIAL, "Time spent running a JS GC slice (ms)")
HISTOGRAM(GC_MMU_50, 1, 100, 20, LINEAR, "Minimum percentage of time spent outside GC over any 50ms window")
HISTOGRAM_BOOLEAN(GC_RESET, "Was an incremental GC canceled?")
HISTOGRAM_BOOLEAN(GC_INCREMENTAL_DISABLED, "Is incremental GC permanently disabled?")
HISTOGRAM(TELEMETRY_PING, 1, 3000, 10, EXPONENTIAL, "Time taken to submit telemetry info (ms)") HISTOGRAM(TELEMETRY_PING, 1, 3000, 10, EXPONENTIAL, "Time taken to submit telemetry info (ms)")
HISTOGRAM_BOOLEAN(TELEMETRY_SUCCESS, "Successful telemetry submission") HISTOGRAM_BOOLEAN(TELEMETRY_SUCCESS, "Successful telemetry submission")

Просмотреть файл

@ -116,6 +116,7 @@ window.onload = function () {
populatePreferencesSection(); populatePreferencesSection();
populateExtensionsSection(); populateExtensionsSection();
populateGraphicsSection(); populateGraphicsSection();
populateJavaScriptSection();
} }
function populateExtensionsSection() { function populateExtensionsSection() {
@ -382,6 +383,13 @@ function populateGraphicsSection() {
]); ]);
} }
function populateJavaScriptSection() {
let enabled = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils)
.isIncrementalGCEnabled();
document.getElementById("javascript-incremental-gc").textContent = enabled ? "1" : "0";
}
function getPrefValue(aName) { function getPrefValue(aName) {
let value = ""; let value = "";
let type = Services.prefs.getPrefType(aName); let type = Services.prefs.getPrefType(aName);

Просмотреть файл

@ -243,6 +243,24 @@
</tbody> </tbody>
</table> </table>
<!-- - - - - - - - - - - - - - - - - - - - - -->
<h2 class="major-section">
&aboutSupport.jsTitle;
</h2>
<table>
<tbody>
<tr>
<th class="column">
&aboutSupport.jsIncrementalGC;
</th>
<td id="javascript-incremental-gc">
</td>
</tr>
</tbody>
</table>
</div> </div>
</body> </body>

Просмотреть файл

@ -56,6 +56,7 @@ _BROWSER_TEST_FILES = \
browser_bug594509.js \ browser_bug594509.js \
browser_Geometry.js \ browser_Geometry.js \
browser_save_resend_postdata.js \ browser_save_resend_postdata.js \
browser_browserDrop.js \
browser_Services.js \ browser_Services.js \
$(NULL) $(NULL)

Просмотреть файл

@ -0,0 +1,61 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
waitForExplicitFinish();
let newTab = gBrowser.selectedTab = gBrowser.addTab();
registerCleanupFunction(function () {
gBrowser.removeTab(newTab);
});
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let chromeUtils = {};
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", chromeUtils);
let browser = gBrowser.selectedBrowser;
var linkHandlerActivated = 0;
// Don't worry about clobbering the droppedLinkHandler, since we're closing
// this tab after the test anyways
browser.droppedLinkHandler = function dlh(e, url, name) {
linkHandlerActivated++;
ok(!/(javascript|data)/i.test(url), "javascript link should not be dropped");
}
var receivedDropCount = 0;
function dropListener() {
receivedDropCount++;
if (receivedDropCount == triggeredDropCount) {
// Wait for the browser's system-phase event handler to run.
executeSoon(function () {
is(linkHandlerActivated, validDropCount,
"link handler was called correct number of times");
finish();
})
}
}
browser.addEventListener("drop", dropListener, false);
registerCleanupFunction(function () {
browser.removeEventListener("drop", dropListener, false);
});
var triggeredDropCount = 0;
var validDropCount = 0;
function drop(text, valid) {
triggeredDropCount++;
if (valid)
validDropCount++;
executeSoon(function () {
chromeUtils.synthesizeDrop(browser, browser, [[{type: "text/plain", data: text}]], "copy", window, EventUtils);
});
}
drop("mochi.test/first", true);
drop("javascript:'bad'");
drop("jAvascript:'also bad'");
drop("mochi.test/second", true);
drop("data:text/html,bad");
drop("mochi.test/third", true);
}

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше