mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-05 23:07:31 +09:00
Remove remaining conditional GCZeal code.
Creates stubs: jsgc.h/cpp VerifyBarriers() stub MaybeVerifyBarriers() stub GCRuntime::computeNonIncrementalMarkingForValidation() stub GCRuntime::validateIncrementalMarking() stub GCRuntime::finishMarkingValidation() stub GCRuntime::pushZealSelectedObjects() stub bool useZeal? GCRuntime::runDebugGC() stub XPConnect: nsXPCComponents_Utils::SetGCZeal() stub (always NS_OK)
This commit is contained in:
parent
97a0673483
commit
b5de790f2a
20 changed files with 4 additions and 832 deletions
|
|
@ -231,21 +231,6 @@ GC(JSContext *cx,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
static bool
|
|
||||||
GCZeal(JSContext *cx, unsigned argc, JS::Value *vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
|
|
||||||
uint32_t zeal;
|
|
||||||
if (!ToUint32(cx, args.get(0), &zeal))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
JS_SetGCZeal(cx, uint8_t(zeal), JS_DEFAULT_ZEAL_FREQ);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const JSFunctionSpec gGlobalFunctions[] =
|
const JSFunctionSpec gGlobalFunctions[] =
|
||||||
{
|
{
|
||||||
JS_FS("print", Print, 0,0),
|
JS_FS("print", Print, 0,0),
|
||||||
|
|
@ -255,9 +240,6 @@ const JSFunctionSpec gGlobalFunctions[] =
|
||||||
JS_FS("dumpXPC", DumpXPC, 1,0),
|
JS_FS("dumpXPC", DumpXPC, 1,0),
|
||||||
JS_FS("dump", Dump, 1,0),
|
JS_FS("dump", Dump, 1,0),
|
||||||
JS_FS("gc", GC, 0,0),
|
JS_FS("gc", GC, 0,0),
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
JS_FS("gczeal", GCZeal, 1,0),
|
|
||||||
#endif
|
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,10 +51,6 @@ class CompileRuntime
|
||||||
// &runtime()->activation_
|
// &runtime()->activation_
|
||||||
const void* addressOfActivation();
|
const void* addressOfActivation();
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
const void* addressOfGCZealModeBits();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
const void* addressOfInterruptUint32();
|
const void* addressOfInterruptUint32();
|
||||||
|
|
||||||
// We have to bake JSContext* into JIT code, but this pointer shouldn't be
|
// We have to bake JSContext* into JIT code, but this pointer shouldn't be
|
||||||
|
|
|
||||||
|
|
@ -627,11 +627,7 @@ PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index)
|
||||||
if (obj->is<NativeObject>() &&
|
if (obj->is<NativeObject>() &&
|
||||||
!obj->as<NativeObject>().isInWholeCellBuffer() &&
|
!obj->as<NativeObject>().isInWholeCellBuffer() &&
|
||||||
uint32_t(index) < obj->as<NativeObject>().getDenseInitializedLength() &&
|
uint32_t(index) < obj->as<NativeObject>().getDenseInitializedLength() &&
|
||||||
(obj->as<NativeObject>().getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
|
(obj->as<NativeObject>().getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE))
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
|| rt->hasZealMode(gc::ZealMode::ElementsBarrier)
|
|
||||||
#endif
|
|
||||||
))
|
|
||||||
{
|
{
|
||||||
rt->gc.storeBuffer.putSlot(&obj->as<NativeObject>(), HeapSlot::Element, index, 1);
|
rt->gc.storeBuffer.putSlot(&obj->as<NativeObject>(), HeapSlot::Element, index, 1);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,6 @@
|
||||||
/* Define to 1 if SpiderMonkey should include ctypes support. */
|
/* Define to 1 if SpiderMonkey should include ctypes support. */
|
||||||
#undef JS_HAS_CTYPES
|
#undef JS_HAS_CTYPES
|
||||||
|
|
||||||
/* Define to 1 if SpiderMonkey should support the ability to perform
|
|
||||||
entirely too much GC. */
|
|
||||||
#undef JS_GC_ZEAL
|
|
||||||
|
|
||||||
/* Define to 1 if SpiderMonkey should use small chunks. */
|
/* Define to 1 if SpiderMonkey should use small chunks. */
|
||||||
#undef JS_GC_SMALL_CHUNK_SIZE
|
#undef JS_GC_SMALL_CHUNK_SIZE
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,36 +94,6 @@ BEGIN_TEST(testGCFinalizeCallback)
|
||||||
CHECK(checkFinalizeStatus());
|
CHECK(checkFinalizeStatus());
|
||||||
CHECK(checkFinalizeIsZoneGC(true));
|
CHECK(checkFinalizeIsZoneGC(true));
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
|
|
||||||
/* Full GC with reset due to new zone, becoming zone GC. */
|
|
||||||
|
|
||||||
FinalizeCalls = 0;
|
|
||||||
JS_SetGCZeal(cx, 9, 1000000);
|
|
||||||
JS::PrepareForFullGC(cx);
|
|
||||||
js::SliceBudget budget(js::WorkBudget(1));
|
|
||||||
cx->gc.startDebugGC(GC_NORMAL, budget);
|
|
||||||
CHECK(cx->gc.state() == js::gc::State::Mark);
|
|
||||||
CHECK(cx->gc.isFullGc());
|
|
||||||
|
|
||||||
JS::RootedObject global4(cx, createTestGlobal());
|
|
||||||
budget = js::SliceBudget(js::WorkBudget(1));
|
|
||||||
cx->gc.debugGCSlice(budget);
|
|
||||||
while (cx->gc.isIncrementalGCInProgress())
|
|
||||||
cx->gc.debugGCSlice(budget);
|
|
||||||
CHECK(!cx->gc.isIncrementalGCInProgress());
|
|
||||||
CHECK(!cx->gc.isFullGc());
|
|
||||||
CHECK(checkMultipleGroups());
|
|
||||||
CHECK(checkFinalizeStatus());
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < FinalizeCalls - 1; ++i)
|
|
||||||
CHECK(!IsZoneGCBuffer[i]);
|
|
||||||
CHECK(IsZoneGCBuffer[FinalizeCalls - 1]);
|
|
||||||
|
|
||||||
JS_SetGCZeal(cx, 0, 0);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make some use of the globals here to ensure the compiler doesn't optimize
|
* Make some use of the globals here to ensure the compiler doesn't optimize
|
||||||
* them away in release builds, causing the zones to be collected and
|
* them away in release builds, causing the zones to be collected and
|
||||||
|
|
|
||||||
|
|
@ -40,10 +40,6 @@ JSFunction* CreateGCThing(JSContext* cx)
|
||||||
|
|
||||||
BEGIN_TEST(testGCHeapPostBarriers)
|
BEGIN_TEST(testGCHeapPostBarriers)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
AutoLeaveZeal nozeal(cx);
|
|
||||||
#endif /* JS_GC_ZEAL */
|
|
||||||
|
|
||||||
/* Sanity check - objects start in the nursery and then become tenured. */
|
/* Sanity check - objects start in the nursery and then become tenured. */
|
||||||
JS_GC(cx);
|
JS_GC(cx);
|
||||||
JS::RootedObject obj(cx, CreateGCThing<JSObject>(cx));
|
JS::RootedObject obj(cx, CreateGCThing<JSObject>(cx));
|
||||||
|
|
|
||||||
|
|
@ -101,10 +101,6 @@ class CCWTestTracer : public JS::CallbackTracer {
|
||||||
|
|
||||||
BEGIN_TEST(testTracingIncomingCCWs)
|
BEGIN_TEST(testTracingIncomingCCWs)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
|
||||||
JS_SetGCZeal(cx, 0, 100);
|
|
||||||
#endif
|
|
||||||
JS_GC(cx);
|
JS_GC(cx);
|
||||||
|
|
||||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||||
|
|
@ -146,10 +142,6 @@ countWrappers(JSCompartment* comp)
|
||||||
|
|
||||||
BEGIN_TEST(testDeadNurseryCCW)
|
BEGIN_TEST(testDeadNurseryCCW)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
|
||||||
JS_SetGCZeal(cx, 0, 100);
|
|
||||||
#endif
|
|
||||||
JS_GC(cx);
|
JS_GC(cx);
|
||||||
|
|
||||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||||
|
|
@ -177,10 +169,6 @@ END_TEST(testDeadNurseryCCW)
|
||||||
|
|
||||||
BEGIN_TEST(testLiveNurseryCCW)
|
BEGIN_TEST(testLiveNurseryCCW)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
|
||||||
JS_SetGCZeal(cx, 0, 100);
|
|
||||||
#endif
|
|
||||||
JS_GC(cx);
|
JS_GC(cx);
|
||||||
|
|
||||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||||
|
|
@ -208,10 +196,6 @@ END_TEST(testLiveNurseryCCW)
|
||||||
|
|
||||||
BEGIN_TEST(testLiveNurseryWrapperCCW)
|
BEGIN_TEST(testLiveNurseryWrapperCCW)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
|
||||||
JS_SetGCZeal(cx, 0, 100);
|
|
||||||
#endif
|
|
||||||
JS_GC(cx);
|
JS_GC(cx);
|
||||||
|
|
||||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||||
|
|
@ -244,10 +228,6 @@ END_TEST(testLiveNurseryWrapperCCW)
|
||||||
|
|
||||||
BEGIN_TEST(testLiveNurseryWrappeeCCW)
|
BEGIN_TEST(testLiveNurseryWrappeeCCW)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
|
||||||
JS_SetGCZeal(cx, 0, 100);
|
|
||||||
#endif
|
|
||||||
JS_GC(cx);
|
JS_GC(cx);
|
||||||
|
|
||||||
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
JS::RootedObject global1(cx, JS::CurrentGlobalOrNull(cx));
|
||||||
|
|
@ -280,11 +260,6 @@ BEGIN_TEST(testIncrementalRoots)
|
||||||
{
|
{
|
||||||
JSRuntime* rt = cx->runtime();
|
JSRuntime* rt = cx->runtime();
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
// Disable zeal modes because this test needs to control exactly when the GC happens.
|
|
||||||
JS_SetGCZeal(cx, 0, 100);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Construct a big object graph to mark. In JS, the resulting object graph
|
// Construct a big object graph to mark. In JS, the resulting object graph
|
||||||
// is equivalent to:
|
// is equivalent to:
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,6 @@ MinimizeHeap(JSContext* cx)
|
||||||
|
|
||||||
BEGIN_TEST(testGCUID)
|
BEGIN_TEST(testGCUID)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
AutoLeaveZeal nozeal(cx);
|
|
||||||
#endif /* JS_GC_ZEAL */
|
|
||||||
|
|
||||||
uint64_t uid = 0;
|
uint64_t uid = 0;
|
||||||
uint64_t tmp = 0;
|
uint64_t tmp = 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -418,39 +418,4 @@ class TestJSPrincipals : public JSPrincipals
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
/*
|
|
||||||
* Temporarily disable the GC zeal setting. This is only useful in tests that
|
|
||||||
* need very explicit GC behavior and should not be used elsewhere.
|
|
||||||
*/
|
|
||||||
class AutoLeaveZeal
|
|
||||||
{
|
|
||||||
JSContext* cx_;
|
|
||||||
uint32_t zealBits_;
|
|
||||||
uint32_t frequency_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit AutoLeaveZeal(JSContext* cx) : cx_(cx) {
|
|
||||||
uint32_t dummy;
|
|
||||||
JS_GetGCZealBits(cx_, &zealBits_, &frequency_, &dummy);
|
|
||||||
JS_SetGCZeal(cx_, 0, 0);
|
|
||||||
JS::PrepareForFullGC(cx_);
|
|
||||||
JS::GCForReason(cx_, GC_SHRINK, JS::gcreason::DEBUG_GC);
|
|
||||||
}
|
|
||||||
~AutoLeaveZeal() {
|
|
||||||
for (size_t i = 0; i < sizeof(zealBits_) * 8; i++) {
|
|
||||||
if (zealBits_ & (1 << i))
|
|
||||||
JS_SetGCZeal(cx_, i, frequency_);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
uint32_t zealBitsAfter, frequencyAfter, dummy;
|
|
||||||
JS_GetGCZealBits(cx_, &zealBitsAfter, &frequencyAfter, &dummy);
|
|
||||||
MOZ_ASSERT(zealBitsAfter == zealBits_);
|
|
||||||
MOZ_ASSERT(frequencyAfter == frequency_);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif /* JS_GC_ZEAL */
|
|
||||||
|
|
||||||
#endif /* jsapi_tests_tests_h */
|
#endif /* jsapi_tests_tests_h */
|
||||||
|
|
|
||||||
|
|
@ -6263,26 +6263,6 @@ JS_AbortIfWrongThread(JSContext* cx)
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
JS_PUBLIC_API(void)
|
|
||||||
JS_GetGCZealBits(JSContext* cx, uint32_t* zealBits, uint32_t* frequency, uint32_t* nextScheduled)
|
|
||||||
{
|
|
||||||
cx->runtime()->gc.getZealBits(zealBits, frequency, nextScheduled);
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
|
||||||
JS_SetGCZeal(JSContext* cx, uint8_t zeal, uint32_t frequency)
|
|
||||||
{
|
|
||||||
cx->gc.setZeal(zeal, frequency);
|
|
||||||
}
|
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
|
||||||
JS_ScheduleGC(JSContext* cx, uint32_t count)
|
|
||||||
{
|
|
||||||
cx->runtime()->gc.setNextScheduled(count);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JS_PUBLIC_API(void)
|
JS_PUBLIC_API(void)
|
||||||
JS_SetParallelParsingEnabled(JSContext* cx, bool enabled)
|
JS_SetParallelParsingEnabled(JSContext* cx, bool enabled)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
595
js/src/jsgc.cpp
595
js/src/jsgc.cpp
|
|
@ -844,9 +844,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
||||||
arenasAllocatedDuringSweep(nullptr),
|
arenasAllocatedDuringSweep(nullptr),
|
||||||
startedCompacting(false),
|
startedCompacting(false),
|
||||||
relocatedArenasToRelease(nullptr),
|
relocatedArenasToRelease(nullptr),
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
markingValidator(nullptr),
|
|
||||||
#endif
|
|
||||||
interFrameGC(false),
|
interFrameGC(false),
|
||||||
defaultTimeBudget_(SliceBudget::UnlimitedTimeBudget),
|
defaultTimeBudget_(SliceBudget::UnlimitedTimeBudget),
|
||||||
incrementalAllowed(true),
|
incrementalAllowed(true),
|
||||||
|
|
@ -856,13 +853,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
||||||
manipulatingDeadZones(false),
|
manipulatingDeadZones(false),
|
||||||
objectsMarkedInDeadZones(0),
|
objectsMarkedInDeadZones(0),
|
||||||
poked(false),
|
poked(false),
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
zealModeBits(0),
|
|
||||||
zealFrequency(0),
|
|
||||||
nextScheduled(0),
|
|
||||||
deterministicOnly(false),
|
|
||||||
incrementalLimit(0),
|
|
||||||
#endif
|
|
||||||
fullCompartmentChecks(false),
|
fullCompartmentChecks(false),
|
||||||
mallocBytesUntilGC(0),
|
mallocBytesUntilGC(0),
|
||||||
mallocGCTriggered(false),
|
mallocGCTriggered(false),
|
||||||
|
|
@ -881,147 +871,6 @@ GCRuntime::GCRuntime(JSRuntime* rt) :
|
||||||
setGCMode(JSGC_MODE_GLOBAL);
|
setGCMode(JSGC_MODE_GLOBAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
|
|
||||||
void
|
|
||||||
GCRuntime::getZealBits(uint32_t* zealBits, uint32_t* frequency, uint32_t* scheduled)
|
|
||||||
{
|
|
||||||
*zealBits = zealModeBits;
|
|
||||||
*frequency = zealFrequency;
|
|
||||||
*scheduled = nextScheduled;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* gc::ZealModeHelpText =
|
|
||||||
" Specifies how zealous the garbage collector should be. Some of these modes can\n"
|
|
||||||
" be set simultaneously, by passing multiple level options, e.g. \"2;4\" will activate\n"
|
|
||||||
" both modes 2 and 4. Modes can be specified by name or number.\n"
|
|
||||||
" \n"
|
|
||||||
" Values:\n"
|
|
||||||
" 0: (None) Normal amount of collection (resets all modes)\n"
|
|
||||||
" 1: (Poke) Collect when roots are added or removed\n"
|
|
||||||
" 2: (Alloc) Collect when every N allocations (default: 100)\n"
|
|
||||||
" 3: (FrameGC) Collect when the window paints (browser only)\n"
|
|
||||||
" 4: (VerifierPre) Verify pre write barriers between instructions\n"
|
|
||||||
" 5: (FrameVerifierPre) Verify pre write barriers between paints\n"
|
|
||||||
" 6: (StackRooting) Verify stack rooting\n"
|
|
||||||
" 7: (GenerationalGC) Collect the nursery every N nursery allocations\n"
|
|
||||||
" 8: (IncrementalRootsThenFinish) Incremental GC in two slices: 1) mark roots 2) finish collection\n"
|
|
||||||
" 9: (IncrementalMarkAllThenFinish) Incremental GC in two slices: 1) mark all 2) new marking and finish\n"
|
|
||||||
" 10: (IncrementalMultipleSlices) Incremental GC in multiple slices\n"
|
|
||||||
" 11: (IncrementalMarkingValidator) Verify incremental marking\n"
|
|
||||||
" 12: (ElementsBarrier) Always use the individual element post-write barrier, regardless of elements size\n"
|
|
||||||
" 13: (CheckHashTablesOnMinorGC) Check internal hashtables on minor GC\n"
|
|
||||||
" 14: (Compact) Perform a shrinking collection every N allocations\n"
|
|
||||||
" 15: (CheckHeapAfterGC) Walk the heap to check its integrity after every GC\n"
|
|
||||||
" 16: (CheckNursery) Check nursery integrity on minor GC\n";
|
|
||||||
|
|
||||||
void
|
|
||||||
GCRuntime::setZeal(uint8_t zeal, uint32_t frequency)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(zeal <= unsigned(ZealMode::Limit));
|
|
||||||
|
|
||||||
if (verifyPreData)
|
|
||||||
VerifyBarriers(rt, PreBarrierVerifier);
|
|
||||||
|
|
||||||
if (zeal == 0 && hasZealMode(ZealMode::GenerationalGC)) {
|
|
||||||
evictNursery(JS::gcreason::DEBUG_GC);
|
|
||||||
nursery.leaveZealMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
ZealMode zealMode = ZealMode(zeal);
|
|
||||||
if (zealMode == ZealMode::GenerationalGC)
|
|
||||||
nursery.enterZealMode();
|
|
||||||
|
|
||||||
// Zeal modes 8-10 are mutually exclusive. If we're setting one of those,
|
|
||||||
// we first reset all of them.
|
|
||||||
if (zealMode >= ZealMode::IncrementalRootsThenFinish &&
|
|
||||||
zealMode <= ZealMode::IncrementalMultipleSlices)
|
|
||||||
{
|
|
||||||
clearZealMode(ZealMode::IncrementalRootsThenFinish);
|
|
||||||
clearZealMode(ZealMode::IncrementalMarkAllThenFinish);
|
|
||||||
clearZealMode(ZealMode::IncrementalMultipleSlices);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool schedule = zealMode >= ZealMode::Alloc;
|
|
||||||
if (zeal != 0)
|
|
||||||
zealModeBits |= 1 << unsigned(zeal);
|
|
||||||
else
|
|
||||||
zealModeBits = 0;
|
|
||||||
zealFrequency = frequency;
|
|
||||||
nextScheduled = schedule ? frequency : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GCRuntime::setNextScheduled(uint32_t count)
|
|
||||||
{
|
|
||||||
nextScheduled = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
|
||||||
GCRuntime::parseAndSetZeal(const char* str)
|
|
||||||
{
|
|
||||||
int frequency = -1;
|
|
||||||
bool foundFrequency = false;
|
|
||||||
mozilla::Vector<int, 0, SystemAllocPolicy> zeals;
|
|
||||||
|
|
||||||
static const struct {
|
|
||||||
const char* const zealMode;
|
|
||||||
size_t length;
|
|
||||||
uint32_t zeal;
|
|
||||||
} zealModes[] = {
|
|
||||||
#define ZEAL_MODE(name, value) {#name, sizeof(#name) - 1, value},
|
|
||||||
JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
|
|
||||||
#undef ZEAL_MODE
|
|
||||||
{"None", 4, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
do {
|
|
||||||
int zeal = -1;
|
|
||||||
|
|
||||||
const char* p = nullptr;
|
|
||||||
if (isdigit(str[0])) {
|
|
||||||
zeal = atoi(str);
|
|
||||||
|
|
||||||
size_t offset = strspn(str, "0123456789");
|
|
||||||
p = str + offset;
|
|
||||||
} else {
|
|
||||||
for (auto z : zealModes) {
|
|
||||||
if (!strncmp(str, z.zealMode, z.length)) {
|
|
||||||
zeal = z.zeal;
|
|
||||||
p = str + z.length;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (p) {
|
|
||||||
if (!*p || *p == ';') {
|
|
||||||
frequency = JS_DEFAULT_ZEAL_FREQ;
|
|
||||||
} else if (*p == ',') {
|
|
||||||
frequency = atoi(p + 1);
|
|
||||||
foundFrequency = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zeal < 0 || zeal > int(ZealMode::Limit) || frequency <= 0) {
|
|
||||||
fprintf(stderr, "Format: JS_GC_ZEAL=level(;level)*[,N]\n");
|
|
||||||
fputs(ZealModeHelpText, stderr);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!zeals.emplaceBack(zeal)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} while (!foundFrequency &&
|
|
||||||
(str = strchr(str, ';')) != nullptr &&
|
|
||||||
str++);
|
|
||||||
|
|
||||||
for (auto z : zeals)
|
|
||||||
setZeal(z, frequency);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Lifetime in number of major GCs for type sets attached to scripts containing
|
* Lifetime in number of major GCs for type sets attached to scripts containing
|
||||||
* observed types.
|
* observed types.
|
||||||
|
|
@ -1063,12 +912,6 @@ GCRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
const char* zealSpec = getenv("JS_GC_ZEAL");
|
|
||||||
if (zealSpec && zealSpec[0] && !parseAndSetZeal(zealSpec))
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!InitTrace(*this))
|
if (!InitTrace(*this))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
@ -1094,11 +937,6 @@ GCRuntime::finish()
|
||||||
allocTask.cancel(GCParallelTask::CancelAndWait);
|
allocTask.cancel(GCParallelTask::CancelAndWait);
|
||||||
decommitTask.cancel(GCParallelTask::CancelAndWait);
|
decommitTask.cancel(GCParallelTask::CancelAndWait);
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
/* Free memory associated with GC verification. */
|
|
||||||
finishVerifier();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Delete all remaining zones. */
|
/* Delete all remaining zones. */
|
||||||
if (rt->gcInitialized) {
|
if (rt->gcInitialized) {
|
||||||
AutoSetThreadIsSweeping threadIsSweeping;
|
AutoSetThreadIsSweeping threadIsSweeping;
|
||||||
|
|
@ -2635,7 +2473,7 @@ GCRuntime::releaseRelocatedArenasWithoutUnlocking(Arena* arenaList, const AutoLo
|
||||||
// Mark arena as empty
|
// Mark arena as empty
|
||||||
arena->setAsFullyUnused();
|
arena->setAsFullyUnused();
|
||||||
|
|
||||||
#if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
|
#if defined(JS_CRASH_DIAGNOSTICS)
|
||||||
JS_POISON(reinterpret_cast<void*>(arena->thingsStart()),
|
JS_POISON(reinterpret_cast<void*>(arena->thingsStart()),
|
||||||
JS_MOVED_TENURED_PATTERN, arena->getThingsSpan());
|
JS_MOVED_TENURED_PATTERN, arena->getThingsSpan());
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -3048,13 +2886,6 @@ GCRuntime::triggerZoneGC(Zone* zone, JS::gcreason::Reason reason)
|
||||||
if (rt->isHeapCollecting())
|
if (rt->isHeapCollecting())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (hasZealMode(ZealMode::Alloc)) {
|
|
||||||
MOZ_RELEASE_ASSERT(triggerGC(reason));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (zone->isAtomsZone()) {
|
if (zone->isAtomsZone()) {
|
||||||
/* We can't do a zone GC of the atoms compartment. */
|
/* We can't do a zone GC of the atoms compartment. */
|
||||||
if (rt->keepAtoms()) {
|
if (rt->keepAtoms()) {
|
||||||
|
|
@ -3077,14 +2908,6 @@ GCRuntime::maybeGC(Zone* zone)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (hasZealMode(ZealMode::Alloc) || hasZealMode(ZealMode::Poke)) {
|
|
||||||
JS::PrepareForFullGC(rt->contextFromMainThread());
|
|
||||||
gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (gcIfRequested())
|
if (gcIfRequested())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -3418,11 +3241,6 @@ GCRuntime::shouldReleaseObservedTypes()
|
||||||
{
|
{
|
||||||
bool releaseTypes = false;
|
bool releaseTypes = false;
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (zealModeBits != 0)
|
|
||||||
releaseTypes = true;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* We may miss the exact target GC due to resets. */
|
/* We may miss the exact target GC due to resets. */
|
||||||
if (majorGCNumber >= jitReleaseNumber)
|
if (majorGCNumber >= jitReleaseNumber)
|
||||||
releaseTypes = true;
|
releaseTypes = true;
|
||||||
|
|
@ -4068,290 +3886,19 @@ GCRuntime::markAllGrayReferences(gcstats::Phase phase)
|
||||||
markGrayReferences<GCZonesIter, GCCompartmentsIter>(phase);
|
markGrayReferences<GCZonesIter, GCCompartmentsIter>(phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
|
|
||||||
struct GCChunkHasher {
|
|
||||||
typedef gc::Chunk* Lookup;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Strip zeros for better distribution after multiplying by the golden
|
|
||||||
* ratio.
|
|
||||||
*/
|
|
||||||
static HashNumber hash(gc::Chunk* chunk) {
|
|
||||||
MOZ_ASSERT(!(uintptr_t(chunk) & gc::ChunkMask));
|
|
||||||
return HashNumber(uintptr_t(chunk) >> gc::ChunkShift);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool match(gc::Chunk* k, gc::Chunk* l) {
|
|
||||||
MOZ_ASSERT(!(uintptr_t(k) & gc::ChunkMask));
|
|
||||||
MOZ_ASSERT(!(uintptr_t(l) & gc::ChunkMask));
|
|
||||||
return k == l;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class js::gc::MarkingValidator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit MarkingValidator(GCRuntime* gc);
|
|
||||||
~MarkingValidator();
|
|
||||||
void nonIncrementalMark(AutoLockForExclusiveAccess& lock);
|
|
||||||
void validate();
|
|
||||||
|
|
||||||
private:
|
|
||||||
GCRuntime* gc;
|
|
||||||
bool initialized;
|
|
||||||
|
|
||||||
typedef HashMap<Chunk*, ChunkBitmap*, GCChunkHasher, SystemAllocPolicy> BitmapMap;
|
|
||||||
BitmapMap map;
|
|
||||||
};
|
|
||||||
|
|
||||||
js::gc::MarkingValidator::MarkingValidator(GCRuntime* gc)
|
|
||||||
: gc(gc),
|
|
||||||
initialized(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
js::gc::MarkingValidator::~MarkingValidator()
|
|
||||||
{
|
|
||||||
if (!map.initialized())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (BitmapMap::Range r(map.all()); !r.empty(); r.popFront())
|
|
||||||
js_delete(r.front().value());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
js::gc::MarkingValidator::nonIncrementalMark(AutoLockForExclusiveAccess& lock)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Perform a non-incremental mark for all collecting zones and record
|
|
||||||
* the results for later comparison.
|
|
||||||
*
|
|
||||||
* Currently this does not validate gray marking.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!map.init())
|
|
||||||
return;
|
|
||||||
|
|
||||||
JSRuntime* runtime = gc->rt;
|
|
||||||
GCMarker* gcmarker = &gc->marker;
|
|
||||||
|
|
||||||
gc->waitBackgroundSweepEnd();
|
|
||||||
|
|
||||||
/* Save existing mark bits. */
|
|
||||||
for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
|
|
||||||
ChunkBitmap* bitmap = &chunk->bitmap;
|
|
||||||
ChunkBitmap* entry = js_new<ChunkBitmap>();
|
|
||||||
if (!entry)
|
|
||||||
return;
|
|
||||||
|
|
||||||
memcpy((void*)entry->bitmap, (void*)bitmap->bitmap, sizeof(bitmap->bitmap));
|
|
||||||
if (!map.putNew(chunk, entry))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Temporarily clear the weakmaps' mark flags for the compartments we are
|
|
||||||
* collecting.
|
|
||||||
*/
|
|
||||||
|
|
||||||
WeakMapSet markedWeakMaps;
|
|
||||||
if (!markedWeakMaps.init())
|
|
||||||
return;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For saving, smush all of the keys into one big table and split them back
|
|
||||||
* up into per-zone tables when restoring.
|
|
||||||
*/
|
|
||||||
gc::WeakKeyTable savedWeakKeys(SystemAllocPolicy(), runtime->randomHashCodeScrambler());
|
|
||||||
if (!savedWeakKeys.init())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
|
|
||||||
if (!WeakMapBase::saveZoneMarkedWeakMaps(zone, markedWeakMaps))
|
|
||||||
return;
|
|
||||||
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
||||||
for (gc::WeakKeyTable::Range r = zone->gcWeakKeys.all(); !r.empty(); r.popFront()) {
|
|
||||||
if (!savedWeakKeys.put(Move(r.front().key), Move(r.front().value)))
|
|
||||||
oomUnsafe.crash("saving weak keys table for validator");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!zone->gcWeakKeys.clear())
|
|
||||||
oomUnsafe.crash("clearing weak keys table for validator");
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* After this point, the function should run to completion, so we shouldn't
|
|
||||||
* do anything fallible.
|
|
||||||
*/
|
|
||||||
initialized = true;
|
|
||||||
|
|
||||||
/* Re-do all the marking, but non-incrementally. */
|
|
||||||
js::gc::State state = gc->incrementalState;
|
|
||||||
gc->incrementalState = State::MarkRoots;
|
|
||||||
|
|
||||||
{
|
|
||||||
gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_MARK);
|
|
||||||
|
|
||||||
{
|
|
||||||
gcstats::AutoPhase ap(gc->stats, gcstats::PHASE_UNMARK);
|
|
||||||
|
|
||||||
for (GCZonesIter zone(runtime); !zone.done(); zone.next())
|
|
||||||
WeakMapBase::unmarkZone(zone);
|
|
||||||
|
|
||||||
MOZ_ASSERT(gcmarker->isDrained());
|
|
||||||
gcmarker->reset();
|
|
||||||
|
|
||||||
for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next())
|
|
||||||
chunk->bitmap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
gc->traceRuntimeForMajorGC(gcmarker, lock);
|
|
||||||
|
|
||||||
gc->incrementalState = State::Mark;
|
|
||||||
auto unlimited = SliceBudget::unlimited();
|
|
||||||
MOZ_RELEASE_ASSERT(gc->marker.drainMarkStack(unlimited));
|
|
||||||
}
|
|
||||||
|
|
||||||
gc->incrementalState = State::Sweep;
|
|
||||||
{
|
|
||||||
gcstats::AutoPhase ap1(gc->stats, gcstats::PHASE_SWEEP);
|
|
||||||
gcstats::AutoPhase ap2(gc->stats, gcstats::PHASE_SWEEP_MARK);
|
|
||||||
|
|
||||||
gc->markAllWeakReferences(gcstats::PHASE_SWEEP_MARK_WEAK);
|
|
||||||
|
|
||||||
/* Update zone state for gray marking. */
|
|
||||||
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
|
|
||||||
MOZ_ASSERT(zone->isGCMarkingBlack());
|
|
||||||
zone->setGCState(Zone::MarkGray);
|
|
||||||
}
|
|
||||||
gc->marker.setMarkColorGray();
|
|
||||||
|
|
||||||
gc->markAllGrayReferences(gcstats::PHASE_SWEEP_MARK_GRAY);
|
|
||||||
gc->markAllWeakReferences(gcstats::PHASE_SWEEP_MARK_GRAY_WEAK);
|
|
||||||
|
|
||||||
/* Restore zone state. */
|
|
||||||
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
|
|
||||||
MOZ_ASSERT(zone->isGCMarkingGray());
|
|
||||||
zone->setGCState(Zone::Mark);
|
|
||||||
}
|
|
||||||
MOZ_ASSERT(gc->marker.isDrained());
|
|
||||||
gc->marker.setMarkColorBlack();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Take a copy of the non-incremental mark state and restore the original. */
|
|
||||||
for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
|
|
||||||
ChunkBitmap* bitmap = &chunk->bitmap;
|
|
||||||
ChunkBitmap* entry = map.lookup(chunk)->value();
|
|
||||||
Swap(*entry, *bitmap);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (GCZonesIter zone(runtime); !zone.done(); zone.next()) {
|
|
||||||
WeakMapBase::unmarkZone(zone);
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
||||||
if (!zone->gcWeakKeys.clear())
|
|
||||||
oomUnsafe.crash("clearing weak keys table for validator");
|
|
||||||
}
|
|
||||||
|
|
||||||
WeakMapBase::restoreMarkedWeakMaps(markedWeakMaps);
|
|
||||||
|
|
||||||
for (gc::WeakKeyTable::Range r = savedWeakKeys.all(); !r.empty(); r.popFront()) {
|
|
||||||
AutoEnterOOMUnsafeRegion oomUnsafe;
|
|
||||||
Zone* zone = gc::TenuredCell::fromPointer(r.front().key.asCell())->zone();
|
|
||||||
if (!zone->gcWeakKeys.put(Move(r.front().key), Move(r.front().value)))
|
|
||||||
oomUnsafe.crash("restoring weak keys table for validator");
|
|
||||||
}
|
|
||||||
|
|
||||||
gc->incrementalState = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
js::gc::MarkingValidator::validate()
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Validates the incremental marking for a single compartment by comparing
|
|
||||||
* the mark bits to those previously recorded for a non-incremental mark.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (!initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
gc->waitBackgroundSweepEnd();
|
|
||||||
|
|
||||||
for (auto chunk = gc->allNonEmptyChunks(); !chunk.done(); chunk.next()) {
|
|
||||||
BitmapMap::Ptr ptr = map.lookup(chunk);
|
|
||||||
if (!ptr)
|
|
||||||
continue; /* Allocated after we did the non-incremental mark. */
|
|
||||||
|
|
||||||
ChunkBitmap* bitmap = ptr->value();
|
|
||||||
ChunkBitmap* incBitmap = &chunk->bitmap;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < ArenasPerChunk; i++) {
|
|
||||||
if (chunk->decommittedArenas.get(i))
|
|
||||||
continue;
|
|
||||||
Arena* arena = &chunk->arenas[i];
|
|
||||||
if (!arena->allocated())
|
|
||||||
continue;
|
|
||||||
if (!arena->zone->isGCSweeping())
|
|
||||||
continue;
|
|
||||||
if (arena->allocatedDuringIncremental)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
AllocKind kind = arena->getAllocKind();
|
|
||||||
uintptr_t thing = arena->thingsStart();
|
|
||||||
uintptr_t end = arena->thingsEnd();
|
|
||||||
while (thing < end) {
|
|
||||||
Cell* cell = (Cell*)thing;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If a non-incremental GC wouldn't have collected a cell, then
|
|
||||||
* an incremental GC won't collect it.
|
|
||||||
*/
|
|
||||||
MOZ_ASSERT_IF(bitmap->isMarked(cell, BLACK), incBitmap->isMarked(cell, BLACK));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the cycle collector isn't allowed to collect an object
|
|
||||||
* after a non-incremental GC has run, then it isn't allowed to
|
|
||||||
* collected it after an incremental GC.
|
|
||||||
*/
|
|
||||||
MOZ_ASSERT_IF(!bitmap->isMarked(cell, GRAY), !incBitmap->isMarked(cell, GRAY));
|
|
||||||
|
|
||||||
thing += Arena::thingSize(kind);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // JS_GC_ZEAL
|
|
||||||
|
|
||||||
void
|
void
|
||||||
GCRuntime::computeNonIncrementalMarkingForValidation(AutoLockForExclusiveAccess& lock)
|
GCRuntime::computeNonIncrementalMarkingForValidation(AutoLockForExclusiveAccess& lock)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
MOZ_ASSERT(!markingValidator);
|
|
||||||
if (isIncremental && hasZealMode(ZealMode::IncrementalMarkingValidator))
|
|
||||||
markingValidator = js_new<MarkingValidator>(this);
|
|
||||||
if (markingValidator)
|
|
||||||
markingValidator->nonIncrementalMark(lock);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCRuntime::validateIncrementalMarking()
|
GCRuntime::validateIncrementalMarking()
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (markingValidator)
|
|
||||||
markingValidator->validate();
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GCRuntime::finishMarkingValidation()
|
GCRuntime::finishMarkingValidation()
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
js_delete(markingValidator);
|
|
||||||
markingValidator = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -5785,11 +5332,6 @@ AutoGCSlice::~AutoGCSlice()
|
||||||
void
|
void
|
||||||
GCRuntime::pushZealSelectedObjects()
|
GCRuntime::pushZealSelectedObjects()
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
/* Push selected objects onto the mark stack and clear the list. */
|
|
||||||
for (JSObject** obj = selectedForMarking.begin(); obj != selectedForMarking.end(); obj++)
|
|
||||||
TraceManuallyBarrieredEdge(&marker, obj, "selected obj");
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -5818,16 +5360,6 @@ GCRuntime::incrementalCollectSlice(SliceBudget& budget, JS::gcreason::Reason rea
|
||||||
gc::State initialState = incrementalState;
|
gc::State initialState = incrementalState;
|
||||||
|
|
||||||
bool useZeal = false;
|
bool useZeal = false;
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (reason == JS::gcreason::DEBUG_GC && !budget.isUnlimited()) {
|
|
||||||
/*
|
|
||||||
* Do the incremental collection type specified by zeal mode if the
|
|
||||||
* collection was triggered by runDebugGC() and incremental GC has not
|
|
||||||
* been cancelled by resetIncrementalGC().
|
|
||||||
*/
|
|
||||||
useZeal = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
|
MOZ_ASSERT_IF(isIncrementalGCInProgress(), isIncremental);
|
||||||
isIncremental = !budget.isUnlimited();
|
isIncremental = !budget.isUnlimited();
|
||||||
|
|
@ -6202,11 +5734,6 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
|
||||||
|
|
||||||
chunkAllocationSinceLastGC = false;
|
chunkAllocationSinceLastGC = false;
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
/* Keeping these around after a GC is dangerous. */
|
|
||||||
clearSelectedForMarking();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Clear gcMallocBytes for all zones. */
|
/* Clear gcMallocBytes for all zones. */
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
|
||||||
zone->resetGCMallocBytes();
|
zone->resetGCMallocBytes();
|
||||||
|
|
@ -6218,23 +5745,6 @@ GCRuntime::gcCycle(bool nonincrementalByAPI, SliceBudget& budget, JS::gcreason::
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
static bool
|
|
||||||
IsDeterministicGCReason(JS::gcreason::Reason reason)
|
|
||||||
{
|
|
||||||
if (reason > JS::gcreason::DEBUG_GC &&
|
|
||||||
reason != JS::gcreason::CC_FORCED && reason != JS::gcreason::SHUTDOWN_CC)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (reason == JS::gcreason::EAGER_ALLOC_TRIGGER)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
gcstats::ZoneGCStats
|
gcstats::ZoneGCStats
|
||||||
GCRuntime::scanZonesBeforeGC()
|
GCRuntime::scanZonesBeforeGC()
|
||||||
{
|
{
|
||||||
|
|
@ -6300,11 +5810,6 @@ GCRuntime::checkIfGCAllowedInCurrentState(JS::gcreason::Reason reason)
|
||||||
if (rt->isBeingDestroyed() && !IsShutdownGC(reason))
|
if (rt->isBeingDestroyed() && !IsShutdownGC(reason))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (deterministicOnly && !IsDeterministicGCReason(reason))
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -6360,13 +5865,6 @@ GCRuntime::collect(bool nonincrementalByAPI, SliceBudget budget, JS::gcreason::R
|
||||||
|
|
||||||
if (reason == JS::gcreason::COMPARTMENT_REVIVED)
|
if (reason == JS::gcreason::COMPARTMENT_REVIVED)
|
||||||
maybeDoCycleCollection();
|
maybeDoCycleCollection();
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (rt->hasZealMode(ZealMode::CheckHeapAfterGC)) {
|
|
||||||
gcstats::AutoPhase ap(rt->gc.stats, gcstats::PHASE_TRACE_HEAP);
|
|
||||||
CheckHeapAfterGC(rt);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
js::AutoEnqueuePendingParseTasksAfterGC::~AutoEnqueuePendingParseTasksAfterGC()
|
js::AutoEnqueuePendingParseTasksAfterGC::~AutoEnqueuePendingParseTasksAfterGC()
|
||||||
|
|
@ -6460,17 +5958,6 @@ GCRuntime::notifyDidPaint()
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (hasZealMode(ZealMode::FrameVerifierPre))
|
|
||||||
verifyPreBarriers();
|
|
||||||
|
|
||||||
if (hasZealMode(ZealMode::FrameGC)) {
|
|
||||||
JS::PrepareForFullGC(rt->contextFromMainThread());
|
|
||||||
gc(GC_NORMAL, JS::gcreason::REFRESH_FRAME);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (isIncrementalGCInProgress() && !interFrameGC && tunables.areRefreshFrameSlicesEnabled()) {
|
if (isIncrementalGCInProgress() && !interFrameGC && tunables.areRefreshFrameSlicesEnabled()) {
|
||||||
JS::PrepareForIncrementalGC(rt->contextFromMainThread());
|
JS::PrepareForIncrementalGC(rt->contextFromMainThread());
|
||||||
gcSlice(JS::gcreason::REFRESH_FRAME);
|
gcSlice(JS::gcreason::REFRESH_FRAME);
|
||||||
|
|
@ -6566,11 +6053,6 @@ GCRuntime::minorGC(JS::gcreason::Reason reason, gcstats::Phase phase)
|
||||||
|
|
||||||
blocksToFreeAfterMinorGC.freeAll();
|
blocksToFreeAfterMinorGC.freeAll();
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (rt->hasZealMode(ZealMode::CheckHeapAfterGC))
|
|
||||||
CheckHeapAfterGC(rt);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{
|
{
|
||||||
AutoLockGC lock(rt);
|
AutoLockGC lock(rt);
|
||||||
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
|
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next())
|
||||||
|
|
@ -6756,59 +6238,6 @@ gc::MergeCompartments(JSCompartment* source, JSCompartment* target)
|
||||||
void
|
void
|
||||||
GCRuntime::runDebugGC()
|
GCRuntime::runDebugGC()
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (rt->mainThread.suppressGC)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (hasZealMode(ZealMode::GenerationalGC))
|
|
||||||
return minorGC(JS::gcreason::DEBUG_GC);
|
|
||||||
|
|
||||||
PrepareForDebugGC(rt);
|
|
||||||
|
|
||||||
auto budget = SliceBudget::unlimited();
|
|
||||||
if (hasZealMode(ZealMode::IncrementalRootsThenFinish) ||
|
|
||||||
hasZealMode(ZealMode::IncrementalMarkAllThenFinish) ||
|
|
||||||
hasZealMode(ZealMode::IncrementalMultipleSlices))
|
|
||||||
{
|
|
||||||
js::gc::State initialState = incrementalState;
|
|
||||||
if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
|
|
||||||
/*
|
|
||||||
* Start with a small slice limit and double it every slice. This
|
|
||||||
* ensure that we get multiple slices, and collection runs to
|
|
||||||
* completion.
|
|
||||||
*/
|
|
||||||
if (!isIncrementalGCInProgress())
|
|
||||||
incrementalLimit = zealFrequency / 2;
|
|
||||||
else
|
|
||||||
incrementalLimit *= 2;
|
|
||||||
budget = SliceBudget(WorkBudget(incrementalLimit));
|
|
||||||
} else {
|
|
||||||
// This triggers incremental GC but is actually ignored by IncrementalMarkSlice.
|
|
||||||
budget = SliceBudget(WorkBudget(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isIncrementalGCInProgress())
|
|
||||||
invocationKind = GC_SHRINK;
|
|
||||||
collect(false, budget, JS::gcreason::DEBUG_GC);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For multi-slice zeal, reset the slice size when we get to the sweep
|
|
||||||
* or compact phases.
|
|
||||||
*/
|
|
||||||
if (hasZealMode(ZealMode::IncrementalMultipleSlices)) {
|
|
||||||
if ((initialState == State::Mark && incrementalState == State::Sweep) ||
|
|
||||||
(initialState == State::Sweep && incrementalState == State::Compact))
|
|
||||||
{
|
|
||||||
incrementalLimit = zealFrequency / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (hasZealMode(ZealMode::Compact)) {
|
|
||||||
gc(GC_SHRINK, JS::gcreason::DEBUG_GC);
|
|
||||||
} else {
|
|
||||||
gc(GC_NORMAL, JS::gcreason::DEBUG_GC);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -6818,28 +6247,6 @@ GCRuntime::setFullCompartmentChecks(bool enabled)
|
||||||
fullCompartmentChecks = enabled;
|
fullCompartmentChecks = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
bool
|
|
||||||
GCRuntime::selectForMarking(JSObject* object)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!rt->isHeapMajorCollecting());
|
|
||||||
return selectedForMarking.append(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GCRuntime::clearSelectedForMarking()
|
|
||||||
{
|
|
||||||
selectedForMarking.clearAndFree();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
GCRuntime::setDeterministic(bool enabled)
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(!rt->isHeapMajorCollecting());
|
|
||||||
deterministicOnly = enabled;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
||||||
/* Should only be called manually under gdb */
|
/* Should only be called manually under gdb */
|
||||||
|
|
|
||||||
|
|
@ -1264,19 +1264,6 @@ enum VerifierType {
|
||||||
PreBarrierVerifier
|
PreBarrierVerifier
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
|
|
||||||
extern const char* ZealModeHelpText;
|
|
||||||
|
|
||||||
/* Check that write barriers have been used correctly. See jsgc.cpp. */
|
|
||||||
void
|
|
||||||
VerifyBarriers(JSRuntime* rt, VerifierType type);
|
|
||||||
|
|
||||||
void
|
|
||||||
MaybeVerifyBarriers(JSContext* cx, bool always = false);
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
static inline void
|
static inline void
|
||||||
VerifyBarriers(JSRuntime* rt, VerifierType type)
|
VerifyBarriers(JSRuntime* rt, VerifierType type)
|
||||||
{
|
{
|
||||||
|
|
@ -1287,8 +1274,6 @@ MaybeVerifyBarriers(JSContext* cx, bool always = false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Instances of this class set the |JSRuntime::suppressGC| flag for the duration
|
* Instances of this class set the |JSRuntime::suppressGC| flag for the duration
|
||||||
* that they are live. Use of this class is highly discouraged. Please carefully
|
* that they are live. Use of this class is highly discouraged. Please carefully
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
#include "js/TraceKind.h"
|
#include "js/TraceKind.h"
|
||||||
#include "js/TypeDecls.h"
|
#include "js/TypeDecls.h"
|
||||||
|
|
||||||
#if defined(JS_GC_ZEAL) || defined(DEBUG)
|
#if defined(DEBUG)
|
||||||
# define JSGC_HASH_TABLE_CHECKS
|
# define JSGC_HASH_TABLE_CHECKS
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -368,7 +368,7 @@ Poison(void* ptr, uint8_t value, size_t num)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Enable poisoning in crash-diagnostics and zeal builds. */
|
/* Enable poisoning in crash-diagnostics and zeal builds. */
|
||||||
#if defined(JS_CRASH_DIAGNOSTICS) || defined(JS_GC_ZEAL)
|
#if defined(JS_CRASH_DIAGNOSTICS)
|
||||||
# define JS_POISON(p, val, size) Poison(p, val, size)
|
# define JS_POISON(p, val, size) Poison(p, val, size)
|
||||||
#else
|
#else
|
||||||
# define JS_POISON(p, val, size) ((void) 0)
|
# define JS_POISON(p, val, size) ((void) 0)
|
||||||
|
|
|
||||||
|
|
@ -1805,17 +1805,6 @@ if test -n "$MOZ_DEBUG"; then
|
||||||
AC_DEFINE(JS_DEBUG)
|
AC_DEFINE(JS_DEBUG)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
dnl ========================================================
|
|
||||||
dnl Zealous JavaScript GC
|
|
||||||
dnl ========================================================
|
|
||||||
MOZ_ARG_ENABLE_BOOL(gczeal,
|
|
||||||
[ --enable-gczeal Enable zealous GCing],
|
|
||||||
JS_GC_ZEAL=1,
|
|
||||||
JS_GC_ZEAL= )
|
|
||||||
if test -n "$JS_GC_ZEAL" -o -n "$MOZ_DEBUG"; then
|
|
||||||
AC_DEFINE(JS_GC_ZEAL)
|
|
||||||
fi
|
|
||||||
|
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
dnl Enable breakpoint for artificial OOMs
|
dnl Enable breakpoint for artificial OOMs
|
||||||
dnl ========================================================
|
dnl ========================================================
|
||||||
|
|
|
||||||
|
|
@ -323,10 +323,6 @@ static bool enableNativeRegExp = false;
|
||||||
static bool enableUnboxedArrays = false;
|
static bool enableUnboxedArrays = false;
|
||||||
static bool enableSharedMemory = SHARED_MEMORY_DEFAULT;
|
static bool enableSharedMemory = SHARED_MEMORY_DEFAULT;
|
||||||
static bool enableWasmAlwaysBaseline = false;
|
static bool enableWasmAlwaysBaseline = false;
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
static uint32_t gZealBits = 0;
|
|
||||||
static uint32_t gZealFrequency = 0;
|
|
||||||
#endif
|
|
||||||
static bool printTiming = false;
|
static bool printTiming = false;
|
||||||
static const char* jsCacheDir = nullptr;
|
static const char* jsCacheDir = nullptr;
|
||||||
static const char* jsCacheAsmJSPath = nullptr;
|
static const char* jsCacheAsmJSPath = nullptr;
|
||||||
|
|
@ -7517,16 +7513,6 @@ SetContextOptions(JSContext* cx, const OptionParser& op)
|
||||||
dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
|
dumpEntrainedVariables = op.getBoolOption("dump-entrained-variables");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
const char* zealStr = op.getStringOption("gc-zeal");
|
|
||||||
if (zealStr) {
|
|
||||||
if (!cx->gc.parseAndSetZeal(zealStr))
|
|
||||||
return false;
|
|
||||||
uint32_t nextScheduled;
|
|
||||||
cx->gc.getZealBits(&gZealBits, &gZealFrequency, &nextScheduled);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7544,16 +7530,6 @@ SetWorkerContextOptions(JSContext* cx)
|
||||||
cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
|
cx->setOffthreadIonCompilationEnabled(offthreadCompilation);
|
||||||
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
|
cx->profilingScripts = enableCodeCoverage || enableDisassemblyDumps;
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
if (gZealBits && gZealFrequency) {
|
|
||||||
#define ZEAL_MODE(_, value) \
|
|
||||||
if (gZealBits & (1 << value)) \
|
|
||||||
cx->gc.setZeal(value, gZealFrequency);
|
|
||||||
JS_FOR_EACH_ZEAL_MODE(ZEAL_MODE)
|
|
||||||
#undef ZEAL_MODE
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
JS_SetNativeStackQuota(cx, gMaxStackSize);
|
JS_SetNativeStackQuota(cx, gMaxStackSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -7838,9 +7814,6 @@ main(int argc, char** argv, char** envp)
|
||||||
"NUMBER of instructions.", -1)
|
"NUMBER of instructions.", -1)
|
||||||
#endif
|
#endif
|
||||||
|| !op.addIntOption('\0', "nursery-size", "SIZE-MB", "Set the maximum nursery size in MB", 16)
|
|| !op.addIntOption('\0', "nursery-size", "SIZE-MB", "Set the maximum nursery size in MB", 16)
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
|| !op.addStringOption('z', "gc-zeal", "LEVEL(;LEVEL)*[,N]", gc::ZealModeHelpText)
|
|
||||||
#endif
|
|
||||||
|| !op.addStringOption('\0', "module-load-path", "DIR", "Set directory to load modules from")
|
|| !op.addStringOption('\0', "module-load-path", "DIR", "Set directory to load modules from")
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -930,9 +930,6 @@ class DebugEnvironments
|
||||||
void mark(JSTracer* trc);
|
void mark(JSTracer* trc);
|
||||||
void sweep(JSRuntime* rt);
|
void sweep(JSRuntime* rt);
|
||||||
void finish();
|
void finish();
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
void checkHashTablesAfterMovingGC(JSRuntime* rt);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// If a live frame has a synthesized entry in missingEnvs, make sure it's not
|
// If a live frame has a synthesized entry in missingEnvs, make sure it's not
|
||||||
// collected.
|
// collected.
|
||||||
|
|
|
||||||
|
|
@ -3007,9 +3007,6 @@ GENERATE_JSCONTEXTOPTION_GETTER_SETTER(Ion, ion, setIon)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsXPCComponents_Utils::SetGCZeal(int32_t aValue, JSContext* cx)
|
nsXPCComponents_Utils::SetGCZeal(int32_t aValue, JSContext* cx)
|
||||||
{
|
{
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
JS_SetGCZeal(cx, uint8_t(aValue), JS_DEFAULT_ZEAL_FREQ);
|
|
||||||
#endif
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1473,16 +1473,6 @@ ReloadPrefsCallback(const char* pref, void* data)
|
||||||
sExtraWarningsForSystemJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict.debug");
|
sExtraWarningsForSystemJS = Preferences::GetBool(JS_OPTIONS_DOT_STR "strict.debug");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
int32_t zeal = Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal", -1);
|
|
||||||
int32_t zeal_frequency =
|
|
||||||
Preferences::GetInt(JS_OPTIONS_DOT_STR "gczeal.frequency",
|
|
||||||
JS_DEFAULT_ZEAL_FREQ);
|
|
||||||
if (zeal >= 0) {
|
|
||||||
JS_SetGCZeal(cx, (uint8_t)zeal, zeal_frequency);
|
|
||||||
}
|
|
||||||
#endif // JS_GC_ZEAL
|
|
||||||
|
|
||||||
JS::ContextOptionsRef(cx).setBaseline(useBaseline)
|
JS::ContextOptionsRef(cx).setBaseline(useBaseline)
|
||||||
.setIon(useIon)
|
.setIon(useIon)
|
||||||
.setAsmJS(useAsmJS)
|
.setAsmJS(useAsmJS)
|
||||||
|
|
|
||||||
|
|
@ -440,21 +440,6 @@ GC(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
static bool
|
|
||||||
GCZeal(JSContext* cx, unsigned argc, Value* vp)
|
|
||||||
{
|
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
|
||||||
uint32_t zeal;
|
|
||||||
if (!ToUint32(cx, args.get(0), &zeal))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
JS_SetGCZeal(cx, uint8_t(zeal), JS_DEFAULT_ZEAL_FREQ);
|
|
||||||
args.rval().setUndefined();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
SendCommand(JSContext* cx, unsigned argc, Value* vp)
|
SendCommand(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
@ -652,9 +637,6 @@ static const JSFunctionSpec glob_functions[] = {
|
||||||
JS_FS("dumpXPC", DumpXPC, 1,0),
|
JS_FS("dumpXPC", DumpXPC, 1,0),
|
||||||
JS_FS("dump", Dump, 1,0),
|
JS_FS("dump", Dump, 1,0),
|
||||||
JS_FS("gc", GC, 0,0),
|
JS_FS("gc", GC, 0,0),
|
||||||
#ifdef JS_GC_ZEAL
|
|
||||||
JS_FS("gczeal", GCZeal, 1,0),
|
|
||||||
#endif
|
|
||||||
JS_FS("options", Options, 0,0),
|
JS_FS("options", Options, 0,0),
|
||||||
JS_FS("sendCommand", SendCommand, 1,0),
|
JS_FS("sendCommand", SendCommand, 1,0),
|
||||||
JS_FS("atob", xpc::Atob, 1,0),
|
JS_FS("atob", xpc::Atob, 1,0),
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue