mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1214961 - Sweep XPConnect incrementally; r=mccr8, r=jonco
This patch splits up the "wrapped-js" table by compartment in order to allow us to visit the miminal number of wrapped-js in each GC sweeping slice, instead of visiting the entire table repeatedly. This dramatically reduces our sweeping overhead, reducing the number of GC slices, and making them more likely to fall within budget.
This commit is contained in:
parent
330afb2752
commit
774fb050ef
@ -21,7 +21,7 @@ using namespace mozilla::jsipc;
|
||||
using mozilla::AutoSafeJSContext;
|
||||
|
||||
static void
|
||||
UpdateChildWeakPointersAfterGC(JSRuntime* rt, void* data)
|
||||
UpdateChildWeakPointersBeforeSweepingZoneGroup(JSRuntime* rt, void* data)
|
||||
{
|
||||
static_cast<JavaScriptChild*>(data)->updateWeakPointers();
|
||||
}
|
||||
@ -34,7 +34,7 @@ JavaScriptChild::JavaScriptChild(JSRuntime* rt)
|
||||
|
||||
JavaScriptChild::~JavaScriptChild()
|
||||
{
|
||||
JS_RemoveWeakPointerCallback(rt_, UpdateChildWeakPointersAfterGC);
|
||||
JS_RemoveWeakPointerZoneGroupCallback(rt_, UpdateChildWeakPointersBeforeSweepingZoneGroup);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -45,7 +45,7 @@ JavaScriptChild::init()
|
||||
if (!WrapperAnswer::init())
|
||||
return false;
|
||||
|
||||
JS_AddWeakPointerCallback(rt_, UpdateChildWeakPointersAfterGC, this);
|
||||
JS_AddWeakPointerZoneGroupCallback(rt_, UpdateChildWeakPointersBeforeSweepingZoneGroup, this);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -757,8 +757,10 @@ class GCRuntime
|
||||
void setGCCallback(JSGCCallback callback, void* data);
|
||||
bool addFinalizeCallback(JSFinalizeCallback callback, void* data);
|
||||
void removeFinalizeCallback(JSFinalizeCallback func);
|
||||
bool addWeakPointerCallback(JSWeakPointerCallback callback, void* data);
|
||||
void removeWeakPointerCallback(JSWeakPointerCallback func);
|
||||
bool addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback, void* data);
|
||||
void removeWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback);
|
||||
bool addWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback, void* data);
|
||||
void removeWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback);
|
||||
JS::GCSliceCallback setSliceCallback(JS::GCSliceCallback callback);
|
||||
|
||||
void setValidate(bool enable);
|
||||
@ -969,7 +971,8 @@ class GCRuntime
|
||||
#endif
|
||||
|
||||
void callFinalizeCallbacks(FreeOp* fop, JSFinalizeStatus status) const;
|
||||
void callWeakPointerCallbacks() const;
|
||||
void callWeakPointerZoneGroupCallbacks() const;
|
||||
void callWeakPointerCompartmentCallbacks(JSCompartment* comp) const;
|
||||
|
||||
public:
|
||||
JSRuntime* rt;
|
||||
@ -1269,7 +1272,8 @@ class GCRuntime
|
||||
|
||||
Callback<JSGCCallback> gcCallback;
|
||||
CallbackVector<JSFinalizeCallback> finalizeCallbacks;
|
||||
CallbackVector<JSWeakPointerCallback> updateWeakPointerCallbacks;
|
||||
CallbackVector<JSWeakPointerZoneGroupCallback> updateWeakPointerZoneGroupCallbacks;
|
||||
CallbackVector<JSWeakPointerCompartmentCallback> updateWeakPointerCompartmentCallbacks;
|
||||
|
||||
/*
|
||||
* Malloc counter to measure memory pressure for GC scheduling. It runs
|
||||
|
@ -138,7 +138,9 @@ static const PhaseInfo phases[] = {
|
||||
{ PHASE_SWEEP_MARK_INCOMING_GRAY, "Mark Incoming Gray Pointers", PHASE_SWEEP_MARK, 14 },
|
||||
{ PHASE_SWEEP_MARK_GRAY, "Mark Gray", PHASE_SWEEP_MARK, 15 },
|
||||
{ PHASE_SWEEP_MARK_GRAY_WEAK, "Mark Gray and Weak", PHASE_SWEEP_MARK, 16 },
|
||||
{ PHASE_FINALIZE_START, "Finalize Start Callback", PHASE_SWEEP, 17 },
|
||||
{ PHASE_FINALIZE_START, "Finalize Start Callbacks", PHASE_SWEEP, 17 },
|
||||
{ PHASE_WEAK_ZONEGROUP_CALLBACK, "Per-Slice Weak Callback", PHASE_FINALIZE_START, 56 },
|
||||
{ PHASE_WEAK_COMPARTMENT_CALLBACK, "Per-Compartment Weak Callback", PHASE_FINALIZE_START, 57 },
|
||||
{ PHASE_SWEEP_ATOMS, "Sweep Atoms", PHASE_SWEEP, 18 },
|
||||
{ PHASE_SWEEP_SYMBOL_REGISTRY, "Sweep Symbol Registry", PHASE_SWEEP, 19 },
|
||||
{ PHASE_SWEEP_COMPARTMENTS, "Sweep Compartments", PHASE_SWEEP, 20 },
|
||||
|
@ -44,6 +44,8 @@ enum Phase : uint8_t {
|
||||
PHASE_SWEEP_MARK_GRAY,
|
||||
PHASE_SWEEP_MARK_GRAY_WEAK,
|
||||
PHASE_FINALIZE_START,
|
||||
PHASE_WEAK_ZONEGROUP_CALLBACK,
|
||||
PHASE_WEAK_COMPARTMENT_CALLBACK,
|
||||
PHASE_SWEEP_ATOMS,
|
||||
PHASE_SWEEP_SYMBOL_REGISTRY,
|
||||
PHASE_SWEEP_COMPARTMENTS,
|
||||
|
@ -1539,18 +1539,33 @@ JS_RemoveFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb)
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_AddWeakPointerCallback(JSRuntime* rt, JSWeakPointerCallback cb, void* data)
|
||||
JS_AddWeakPointerZoneGroupCallback(JSRuntime* rt, JSWeakPointerZoneGroupCallback cb, void* data)
|
||||
{
|
||||
AssertHeapIsIdle(rt);
|
||||
return rt->gc.addWeakPointerCallback(cb, data);
|
||||
return rt->gc.addWeakPointerZoneGroupCallback(cb, data);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_RemoveWeakPointerCallback(JSRuntime* rt, JSWeakPointerCallback cb)
|
||||
JS_RemoveWeakPointerZoneGroupCallback(JSRuntime* rt, JSWeakPointerZoneGroupCallback cb)
|
||||
{
|
||||
rt->gc.removeWeakPointerCallback(cb);
|
||||
rt->gc.removeWeakPointerZoneGroupCallback(cb);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_AddWeakPointerCompartmentCallback(JSRuntime* rt, JSWeakPointerCompartmentCallback cb,
|
||||
void* data)
|
||||
{
|
||||
AssertHeapIsIdle(rt);
|
||||
return rt->gc.addWeakPointerCompartmentCallback(cb, data);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_RemoveWeakPointerCompartmentCallback(JSRuntime* rt, JSWeakPointerCompartmentCallback cb)
|
||||
{
|
||||
rt->gc.removeWeakPointerCompartmentCallback(cb);
|
||||
}
|
||||
|
||||
|
||||
JS_PUBLIC_API(void)
|
||||
JS_UpdateWeakPointerAfterGC(JS::Heap<JSObject*>* objp)
|
||||
{
|
||||
|
@ -601,7 +601,10 @@ typedef void
|
||||
(* JSFinalizeCallback)(JSFreeOp* fop, JSFinalizeStatus status, bool isCompartment, void* data);
|
||||
|
||||
typedef void
|
||||
(* JSWeakPointerCallback)(JSRuntime* rt, void* data);
|
||||
(* JSWeakPointerZoneGroupCallback)(JSRuntime* rt, void* data);
|
||||
|
||||
typedef void
|
||||
(* JSWeakPointerCompartmentCallback)(JSRuntime* rt, JSCompartment* comp, void* data);
|
||||
|
||||
typedef bool
|
||||
(* JSInterruptCallback)(JSContext* cx);
|
||||
@ -1721,11 +1724,20 @@ JS_RemoveFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb);
|
||||
*
|
||||
* To handle this, any part of the system that maintain weak pointers to
|
||||
* JavaScript GC things must register a callback with
|
||||
* JS_(Add,Remove)WeakPointerCallback(). This callback must then call
|
||||
* JS_UpdateWeakPointerAfterGC() on all weak pointers it knows about.
|
||||
* JS_(Add,Remove)WeakPointer{ZoneGroup,Compartment}Callback(). This callback
|
||||
* must then call JS_UpdateWeakPointerAfterGC() on all weak pointers it knows
|
||||
* about.
|
||||
*
|
||||
* The argument to JS_UpdateWeakPointerAfterGC() is an in-out param. If the
|
||||
* referent is about to be finalized the pointer will be set to null. If the
|
||||
* Since sweeping is incremental, we have several callbacks to avoid repeatedly
|
||||
* having to visit all embedder structures. The WeakPointerZoneGroupCallback is
|
||||
* called once for each strongly connected group of zones, whereas the
|
||||
* WeakPointerCompartmentCallback is called once for each compartment that is
|
||||
* visited while sweeping. Structures that cannot contain references in more
|
||||
* than one compartment should sweep the relevant per-compartment structures
|
||||
* using the latter callback to minimizer per-slice overhead.
|
||||
*
|
||||
* The argument to JS_UpdateWeakPointerAfterGC() is an in-out param. If the
|
||||
* referent is about to be finalized the pointer will be set to null. If the
|
||||
* referent has been moved then the pointer will be updated to point to the new
|
||||
* location.
|
||||
*
|
||||
@ -1736,10 +1748,17 @@ JS_RemoveFinalizeCallback(JSRuntime* rt, JSFinalizeCallback cb);
|
||||
*/
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_AddWeakPointerCallback(JSRuntime* rt, JSWeakPointerCallback cb, void* data);
|
||||
JS_AddWeakPointerZoneGroupCallback(JSRuntime* rt, JSWeakPointerZoneGroupCallback cb, void* data);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_RemoveWeakPointerCallback(JSRuntime* rt, JSWeakPointerCallback cb);
|
||||
JS_RemoveWeakPointerZoneGroupCallback(JSRuntime* rt, JSWeakPointerZoneGroupCallback cb);
|
||||
|
||||
extern JS_PUBLIC_API(bool)
|
||||
JS_AddWeakPointerCompartmentCallback(JSRuntime* rt, JSWeakPointerCompartmentCallback cb,
|
||||
void* data);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_RemoveWeakPointerCompartmentCallback(JSRuntime* rt, JSWeakPointerCompartmentCallback cb);
|
||||
|
||||
extern JS_PUBLIC_API(void)
|
||||
JS_UpdateWeakPointerAfterGC(JS::Heap<JSObject*>* objp);
|
||||
|
@ -1619,31 +1619,54 @@ GCRuntime::callFinalizeCallbacks(FreeOp* fop, JSFinalizeStatus status) const
|
||||
}
|
||||
|
||||
bool
|
||||
GCRuntime::addWeakPointerCallback(JSWeakPointerCallback callback, void* data)
|
||||
GCRuntime::addWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback, void* data)
|
||||
{
|
||||
return updateWeakPointerCallbacks.append(Callback<JSWeakPointerCallback>(callback, data));
|
||||
return updateWeakPointerZoneGroupCallbacks.append(
|
||||
Callback<JSWeakPointerZoneGroupCallback>(callback, data));
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::removeWeakPointerCallback(JSWeakPointerCallback callback)
|
||||
GCRuntime::removeWeakPointerZoneGroupCallback(JSWeakPointerZoneGroupCallback callback)
|
||||
{
|
||||
for (Callback<JSWeakPointerCallback>* p = updateWeakPointerCallbacks.begin();
|
||||
p < updateWeakPointerCallbacks.end(); p++)
|
||||
{
|
||||
if (p->op == callback) {
|
||||
updateWeakPointerCallbacks.erase(p);
|
||||
for (auto& p : updateWeakPointerZoneGroupCallbacks) {
|
||||
if (p.op == callback) {
|
||||
updateWeakPointerZoneGroupCallbacks.erase(&p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::callWeakPointerCallbacks() const
|
||||
GCRuntime::callWeakPointerZoneGroupCallbacks() const
|
||||
{
|
||||
for (const Callback<JSWeakPointerCallback>* p = updateWeakPointerCallbacks.begin();
|
||||
p < updateWeakPointerCallbacks.end(); p++)
|
||||
{
|
||||
p->op(rt, p->data);
|
||||
for (auto const& p : updateWeakPointerZoneGroupCallbacks) {
|
||||
p.op(rt, p.data);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GCRuntime::addWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback, void* data)
|
||||
{
|
||||
return updateWeakPointerCompartmentCallbacks.append(
|
||||
Callback<JSWeakPointerCompartmentCallback>(callback, data));
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::removeWeakPointerCompartmentCallback(JSWeakPointerCompartmentCallback callback)
|
||||
{
|
||||
for (auto& p : updateWeakPointerCompartmentCallbacks) {
|
||||
if (p.op == callback) {
|
||||
updateWeakPointerCompartmentCallbacks.erase(&p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GCRuntime::callWeakPointerCompartmentCallbacks(JSCompartment* comp) const
|
||||
{
|
||||
for (auto const& p : updateWeakPointerCompartmentCallbacks) {
|
||||
p.op(rt, comp, p.data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2665,7 +2688,9 @@ GCRuntime::updatePointersToRelocatedCells(Zone* zone)
|
||||
rt->nativeIterCache.purge();
|
||||
|
||||
// Call callbacks to get the rest of the system to fixup other untraced pointers.
|
||||
callWeakPointerCallbacks();
|
||||
callWeakPointerZoneGroupCallbacks();
|
||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
|
||||
callWeakPointerCompartmentCallbacks(comp);
|
||||
|
||||
// Finally, iterate through all cells that can contain JSObject pointers to
|
||||
// update them. Since updating each cell is independent we try to
|
||||
@ -5020,7 +5045,17 @@ GCRuntime::beginSweepingZoneGroup()
|
||||
{
|
||||
gcstats::AutoPhase ap(stats, gcstats::PHASE_FINALIZE_START);
|
||||
callFinalizeCallbacks(&fop, JSFINALIZE_GROUP_START);
|
||||
callWeakPointerCallbacks();
|
||||
{
|
||||
gcstats::AutoPhase ap2(stats, gcstats::PHASE_WEAK_ZONEGROUP_CALLBACK);
|
||||
callWeakPointerZoneGroupCallbacks();
|
||||
}
|
||||
{
|
||||
gcstats::AutoPhase ap2(stats, gcstats::PHASE_WEAK_COMPARTMENT_CALLBACK);
|
||||
for (GCZoneGroupIter zone(rt); !zone.done(); zone.next()) {
|
||||
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
|
||||
callWeakPointerCompartmentCallbacks(comp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sweepingAtoms) {
|
||||
|
@ -188,9 +188,27 @@ public:
|
||||
|
||||
namespace xpc {
|
||||
|
||||
CompartmentPrivate::CompartmentPrivate(JSCompartment* c)
|
||||
: wantXrays(false)
|
||||
, allowWaivers(true)
|
||||
, writeToGlobalPrototype(false)
|
||||
, skipWriteToGlobalPrototype(false)
|
||||
, isWebExtensionContentScript(false)
|
||||
, universalXPConnectEnabled(false)
|
||||
, forcePermissiveCOWs(false)
|
||||
, scriptability(c)
|
||||
, scope(nullptr)
|
||||
, mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_LENGTH))
|
||||
{
|
||||
MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
|
||||
mozilla::PodArrayZero(wrapperDenialWarnings);
|
||||
}
|
||||
|
||||
CompartmentPrivate::~CompartmentPrivate()
|
||||
{
|
||||
MOZ_COUNT_DTOR(xpc::CompartmentPrivate);
|
||||
mWrappedJSMap->ShutdownMarker();
|
||||
delete mWrappedJSMap;
|
||||
}
|
||||
|
||||
static bool
|
||||
@ -919,18 +937,36 @@ XPCJSRuntime::FinalizeCallback(JSFreeOp* fop,
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
XPCJSRuntime::WeakPointerCallback(JSRuntime* rt, void* data)
|
||||
XPCJSRuntime::WeakPointerZoneGroupCallback(JSRuntime* rt, void* data)
|
||||
{
|
||||
// Called to remove any weak pointers to GC things that are about to be
|
||||
// finalized and fixup any pointers that may have been moved.
|
||||
|
||||
// Called before each sweeping slice -- after processing any final marking
|
||||
// triggered by barriers -- to clear out any references to things that are
|
||||
// about to be finalized and update any pointers to moved GC things.
|
||||
XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
|
||||
|
||||
MOZ_ASSERT(self->WrappedJSToReleaseArray().IsEmpty());
|
||||
self->mWrappedJSMap->UpdateWeakPointersAfterGC(self);
|
||||
|
||||
XPCWrappedNativeScope::UpdateWeakPointersAfterGC(self);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
XPCJSRuntime::WeakPointerCompartmentCallback(JSRuntime* rt, JSCompartment* comp, void* data)
|
||||
{
|
||||
// Called immediately after the ZoneGroup weak pointer callback, but only
|
||||
// once for each compartment that is being swept.
|
||||
XPCJSRuntime* self = static_cast<XPCJSRuntime*>(data);
|
||||
CompartmentPrivate* xpcComp = CompartmentPrivate::Get(comp);
|
||||
if (xpcComp)
|
||||
xpcComp->UpdateWeakPointersAfterGC(self);
|
||||
}
|
||||
|
||||
void
|
||||
CompartmentPrivate::UpdateWeakPointersAfterGC(XPCJSRuntime* runtime)
|
||||
{
|
||||
mWrappedJSMap->UpdateWeakPointersAfterGC(runtime);
|
||||
}
|
||||
|
||||
static void WatchdogMain(void* arg);
|
||||
class Watchdog;
|
||||
class WatchdogManager;
|
||||
@ -1479,6 +1515,12 @@ XPCJSRuntime::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t
|
||||
CompartmentPrivate::SizeOfIncludingThis(MallocSizeOf mallocSizeOf)
|
||||
{
|
||||
return mallocSizeOf(this) + mWrappedJSMap->SizeOfWrappedJS(mallocSizeOf);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
void XPCJSRuntime::DestroyJSContextStack()
|
||||
@ -1560,7 +1602,8 @@ XPCJSRuntime::~XPCJSRuntime()
|
||||
// callback if we aren't careful. Null out the relevant callbacks.
|
||||
js::SetActivityCallback(Runtime(), nullptr, nullptr);
|
||||
JS_RemoveFinalizeCallback(Runtime(), FinalizeCallback);
|
||||
JS_RemoveWeakPointerCallback(Runtime(), WeakPointerCallback);
|
||||
JS_RemoveWeakPointerZoneGroupCallback(Runtime(), WeakPointerZoneGroupCallback);
|
||||
JS_RemoveWeakPointerCompartmentCallback(Runtime(), WeakPointerCompartmentCallback);
|
||||
|
||||
// Clear any pending exception. It might be an XPCWrappedJS, and if we try
|
||||
// to destroy it later we will crash.
|
||||
@ -2252,6 +2295,11 @@ ReportCompartmentStats(const JS::CompartmentStats& cStats,
|
||||
"Orphan DOM nodes, i.e. those that are only reachable from JavaScript "
|
||||
"objects.");
|
||||
|
||||
ZCREPORT_BYTES(cDOMPathPrefix + NS_LITERAL_CSTRING("private-data"),
|
||||
extras.sizeOfXPCPrivate,
|
||||
"Extra data attached to the compartment by XPConnect, including "\
|
||||
"wrapped-js that is local to a single compartment.");
|
||||
|
||||
ZCREPORT_GC_BYTES(cJSPathPrefix + NS_LITERAL_CSTRING("scripts/gc-heap"),
|
||||
cStats.scriptsGCHeap,
|
||||
"JSScript instances. There is one per user-defined function in a "
|
||||
@ -2758,14 +2806,16 @@ class XPCJSRuntimeStats : public JS::RuntimeStats
|
||||
xpc::CompartmentStatsExtras* extras = new xpc::CompartmentStatsExtras;
|
||||
nsCString cName;
|
||||
GetCompartmentName(c, cName, &mAnonymizeID, /* replaceSlashes = */ true);
|
||||
if (mGetLocations) {
|
||||
CompartmentPrivate* cp = CompartmentPrivate::Get(c);
|
||||
if (cp)
|
||||
cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
|
||||
getter_AddRefs(extras->location));
|
||||
CompartmentPrivate* cp = CompartmentPrivate::Get(c);
|
||||
if (cp) {
|
||||
if (mGetLocations) {
|
||||
cp->GetLocationURI(CompartmentPrivate::LocationHintAddon,
|
||||
getter_AddRefs(extras->location));
|
||||
}
|
||||
// Note: cannot use amIAddonManager implementation at this point,
|
||||
// as it is a JS service and the JS heap is currently not idle.
|
||||
// Otherwise, we could have computed the add-on id at this point.
|
||||
extras->sizeOfXPCPrivate = cp->SizeOfIncludingThis(mallocSizeOf_);
|
||||
}
|
||||
|
||||
// Get the compartment's global.
|
||||
@ -2856,7 +2906,7 @@ JSReporter::CollectReports(WindowPaths* windowPaths,
|
||||
|
||||
size_t xpcJSRuntimeSize = xpcrt->SizeOfIncludingThis(JSMallocSizeOf);
|
||||
|
||||
size_t wrappedJSSize = xpcrt->GetWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
|
||||
size_t wrappedJSSize = xpcrt->GetMultiCompartmentWrappedJSMap()->SizeOfWrappedJS(JSMallocSizeOf);
|
||||
|
||||
XPCWrappedNativeScope::ScopeSizeInfo sizeInfo(JSMallocSizeOf);
|
||||
XPCWrappedNativeScope::AddSizeOfAllScopesIncludingThis(&sizeInfo);
|
||||
@ -3398,7 +3448,8 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
JS_SetCompartmentNameCallback(runtime, CompartmentNameCallback);
|
||||
mPrevGCSliceCallback = JS::SetGCSliceCallback(runtime, GCSliceCallback);
|
||||
JS_AddFinalizeCallback(runtime, FinalizeCallback, nullptr);
|
||||
JS_AddWeakPointerCallback(runtime, WeakPointerCallback, this);
|
||||
JS_AddWeakPointerZoneGroupCallback(runtime, WeakPointerZoneGroupCallback, this);
|
||||
JS_AddWeakPointerCompartmentCallback(runtime, WeakPointerCompartmentCallback, this);
|
||||
JS_SetWrapObjectCallbacks(runtime, &WrapObjectCallbacks);
|
||||
js::SetPreserveWrapperCallback(runtime, PreserveWrapper);
|
||||
#ifdef MOZ_ENABLE_PROFILER_SPS
|
||||
@ -3459,7 +3510,7 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnect* aXPConnect)
|
||||
|
||||
if (self &&
|
||||
self->Runtime() &&
|
||||
self->GetWrappedJSMap() &&
|
||||
self->GetMultiCompartmentWrappedJSMap() &&
|
||||
self->GetWrappedJSClassMap() &&
|
||||
self->GetIID2NativeInterfaceMap() &&
|
||||
self->GetClassInfo2NativeSetMap() &&
|
||||
@ -3651,9 +3702,10 @@ XPCJSRuntime::DebugDump(int16_t depth)
|
||||
}
|
||||
XPC_LOG_OUTDENT();
|
||||
}
|
||||
|
||||
// iterate wrappers...
|
||||
XPC_LOG_ALWAYS(("mWrappedJSMap @ %x with %d wrappers(s)",
|
||||
mWrappedJSMap, mWrappedJSMap->Count()));
|
||||
// iterate wrappers...
|
||||
if (depth && mWrappedJSMap->Count()) {
|
||||
XPC_LOG_INDENT();
|
||||
mWrappedJSMap->Dump(depth);
|
||||
|
@ -95,7 +95,6 @@ JSObject2WrappedJSMap::UpdateWeakPointersAfterGC(XPCJSRuntime* runtime)
|
||||
// the posibility of doing any JS GCThing allocations during the gc cycle.
|
||||
|
||||
nsTArray<nsXPCWrappedJS*>& dying = runtime->WrappedJSToReleaseArray();
|
||||
MOZ_ASSERT(dying.IsEmpty());
|
||||
|
||||
for (Map::Enum e(mTable); !e.empty(); e.popFront()) {
|
||||
nsXPCWrappedJS* wrapper = e.front().value();
|
||||
|
@ -47,6 +47,16 @@ public:
|
||||
return p ? p->value() : nullptr;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
inline bool HasWrapper(nsXPCWrappedJS* wrapper) {
|
||||
for (auto r = mTable.all(); !r.empty(); r.popFront()) {
|
||||
if (r.front().value() == wrapper)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline nsXPCWrappedJS* Add(JSContext* cx, nsXPCWrappedJS* wrapper) {
|
||||
NS_PRECONDITION(wrapper,"bad param");
|
||||
JSObject* obj = wrapper->GetJSObjectPreserveColor();
|
||||
|
@ -323,16 +323,10 @@ nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj,
|
||||
MOZ_CRASH();
|
||||
|
||||
AutoJSContext cx;
|
||||
XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
|
||||
JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
|
||||
if (!map) {
|
||||
MOZ_ASSERT(map,"bad map");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
bool allowNonScriptable = mozilla::jsipc::IsWrappedCPOW(jsObj);
|
||||
RefPtr<nsXPCWrappedJSClass> clasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, aIID,
|
||||
allowNonScriptable);
|
||||
allowNonScriptable);
|
||||
if (!clasp)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
@ -340,8 +334,19 @@ nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj,
|
||||
if (!rootJSObj)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
xpc::CompartmentPrivate* rootComp = xpc::CompartmentPrivate::Get(rootJSObj);
|
||||
MOZ_ASSERT(rootComp);
|
||||
|
||||
// Find any existing wrapper.
|
||||
RefPtr<nsXPCWrappedJS> root = rootComp->GetWrappedJSMap()->Find(rootJSObj);
|
||||
MOZ_ASSERT_IF(root, !nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap()->
|
||||
Find(rootJSObj));
|
||||
if (!root) {
|
||||
root = nsXPConnect::GetRuntimeInstance()->GetMultiCompartmentWrappedJSMap()->
|
||||
Find(rootJSObj);
|
||||
}
|
||||
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
RefPtr<nsXPCWrappedJS> root = map->Find(rootJSObj);
|
||||
if (root) {
|
||||
RefPtr<nsXPCWrappedJS> wrapper = root->FindOrFindInherited(aIID);
|
||||
if (wrapper) {
|
||||
@ -353,7 +358,8 @@ nsXPCWrappedJS::GetNewOrUsed(JS::HandleObject jsObj,
|
||||
// Make a new root wrapper, because there is no existing
|
||||
// root wrapper, and the wrapper we are trying to make isn't
|
||||
// a root.
|
||||
RefPtr<nsXPCWrappedJSClass> rootClasp = nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports));
|
||||
RefPtr<nsXPCWrappedJSClass> rootClasp =
|
||||
nsXPCWrappedJSClass::GetNewOrUsed(cx, NS_GET_IID(nsISupports));
|
||||
if (!rootClasp)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
@ -391,11 +397,21 @@ nsXPCWrappedJS::nsXPCWrappedJS(JSContext* cx,
|
||||
NS_ADDREF_THIS();
|
||||
|
||||
if (IsRootWrapper()) {
|
||||
nsXPConnect::GetRuntimeInstance()->GetWrappedJSMap()->Add(cx, this);
|
||||
MOZ_ASSERT(!IsMultiCompartment(), "mNext is always nullptr here");
|
||||
xpc::CompartmentPrivate::Get(mJSObj)->GetWrappedJSMap()->Add(cx, this);
|
||||
} else {
|
||||
NS_ADDREF(mRoot);
|
||||
mNext = mRoot->mNext;
|
||||
mRoot->mNext = this;
|
||||
|
||||
// We always start wrappers in the per-compartment table. If adding
|
||||
// this wrapper to the chain causes it to cross compartments, we need
|
||||
// to migrate the chain to the global table on the XPCJSRuntime.
|
||||
if (mRoot->IsMultiCompartment()) {
|
||||
xpc::CompartmentPrivate::Get(mRoot->mJSObj)->GetWrappedJSMap()->Remove(mRoot);
|
||||
MOZ_RELEASE_ASSERT(nsXPConnect::GetRuntimeInstance()->
|
||||
GetMultiCompartmentWrappedJSMap()->Add(cx, mRoot));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -404,31 +420,71 @@ nsXPCWrappedJS::~nsXPCWrappedJS()
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void
|
||||
XPCJSRuntime::RemoveWrappedJS(nsXPCWrappedJS* wrapper)
|
||||
{
|
||||
AssertInvalidWrappedJSNotInTable(wrapper);
|
||||
if (!wrapper->IsValid())
|
||||
return;
|
||||
|
||||
// It is possible for the same JS XPCOM implementation object to be wrapped
|
||||
// with a different interface in multiple JSCompartments. In this case, the
|
||||
// wrapper chain will contain references to multiple compartments. While we
|
||||
// always store single-compartment chains in the per-compartment wrapped-js
|
||||
// table, chains in the multi-compartment wrapped-js table may contain
|
||||
// single-compartment chains, if they have ever contained a wrapper in a
|
||||
// different compartment. Since removal requires a lookup anyway, we just do
|
||||
// the remove on both tables unconditionally.
|
||||
MOZ_ASSERT_IF(wrapper->IsMultiCompartment(),
|
||||
!xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor())->
|
||||
GetWrappedJSMap()->HasWrapper(wrapper));
|
||||
GetMultiCompartmentWrappedJSMap()->Remove(wrapper);
|
||||
xpc::CompartmentPrivate::Get(wrapper->GetJSObjectPreserveColor())->GetWrappedJSMap()->
|
||||
Remove(wrapper);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
static void
|
||||
NotHasWrapperAssertionCallback(JSRuntime* rt, void* data, JSCompartment* comp)
|
||||
{
|
||||
auto wrapper = static_cast<nsXPCWrappedJS*>(data);
|
||||
auto xpcComp = xpc::CompartmentPrivate::Get(comp);
|
||||
MOZ_ASSERT_IF(xpcComp, !xpcComp->GetWrappedJSMap()->HasWrapper(wrapper));
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
XPCJSRuntime::AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (!wrapper->IsValid()) {
|
||||
MOZ_ASSERT(!GetMultiCompartmentWrappedJSMap()->HasWrapper(wrapper));
|
||||
if (!mGCIsRunning)
|
||||
JS_IterateCompartments(Runtime(), wrapper, NotHasWrapperAssertionCallback);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
nsXPCWrappedJS::Destroy()
|
||||
{
|
||||
MOZ_ASSERT(1 == int32_t(mRefCnt), "should be stabilized for deletion");
|
||||
|
||||
if (IsRootWrapper()) {
|
||||
XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
|
||||
JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
|
||||
if (map)
|
||||
map->Remove(this);
|
||||
}
|
||||
if (IsRootWrapper())
|
||||
nsXPConnect::GetRuntimeInstance()->RemoveWrappedJS(this);
|
||||
Unlink();
|
||||
}
|
||||
|
||||
void
|
||||
nsXPCWrappedJS::Unlink()
|
||||
{
|
||||
nsXPConnect::GetRuntimeInstance()->AssertInvalidWrappedJSNotInTable(this);
|
||||
|
||||
if (IsValid()) {
|
||||
XPCJSRuntime* rt = nsXPConnect::GetRuntimeInstance();
|
||||
if (rt) {
|
||||
if (IsRootWrapper()) {
|
||||
JSObject2WrappedJSMap* map = rt->GetWrappedJSMap();
|
||||
if (map)
|
||||
map->Remove(this);
|
||||
}
|
||||
if (IsRootWrapper())
|
||||
rt->RemoveWrappedJS(this);
|
||||
|
||||
if (mRefCnt > 1)
|
||||
RemoveFromRootSet();
|
||||
@ -450,6 +506,13 @@ nsXPCWrappedJS::Unlink()
|
||||
cur = cur->mNext;
|
||||
MOZ_ASSERT(cur, "failed to find wrapper in its own chain");
|
||||
}
|
||||
|
||||
// Note: unlinking this wrapper may have changed us from a multi-
|
||||
// compartment wrapper chain to a single-compartment wrapper chain. We
|
||||
// leave the wrapper in the multi-compartment table as it is likely to
|
||||
// need to be multi-compartment again in the future and, moreover, we
|
||||
// cannot get a JSContext here.
|
||||
|
||||
// let the root go
|
||||
NS_RELEASE(mRoot);
|
||||
}
|
||||
@ -465,6 +528,20 @@ nsXPCWrappedJS::Unlink()
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsXPCWrappedJS::IsMultiCompartment() const
|
||||
{
|
||||
MOZ_ASSERT(IsRootWrapper());
|
||||
JSCompartment* compartment = js::GetObjectCompartment(mJSObj);
|
||||
nsXPCWrappedJS* next = mNext;
|
||||
while (next) {
|
||||
if (js::GetObjectCompartment(next->mJSObj) != compartment)
|
||||
return true;
|
||||
next = next->mNext;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsXPCWrappedJS*
|
||||
nsXPCWrappedJS::Find(REFNSIID aIID)
|
||||
{
|
||||
|
@ -430,6 +430,9 @@ public:
|
||||
XPCJSContextStack* GetJSContextStack() {return mJSContextStack;}
|
||||
void DestroyJSContextStack();
|
||||
|
||||
void RemoveWrappedJS(nsXPCWrappedJS* wrapper);
|
||||
void AssertInvalidWrappedJSNotInTable(nsXPCWrappedJS* wrapper) const;
|
||||
|
||||
XPCCallContext* GetCallContext() const {return mCallContext;}
|
||||
XPCCallContext* SetCallContext(XPCCallContext* ccx)
|
||||
{XPCCallContext* old = mCallContext; mCallContext = ccx; return old;}
|
||||
@ -443,10 +446,10 @@ public:
|
||||
{XPCWrappedNative* old = mResolvingWrapper;
|
||||
mResolvingWrapper = w; return old;}
|
||||
|
||||
JSObject2WrappedJSMap* GetWrappedJSMap() const
|
||||
JSObject2WrappedJSMap* GetMultiCompartmentWrappedJSMap() const
|
||||
{return mWrappedJSMap;}
|
||||
|
||||
IID2WrappedJSClassMap* GetWrappedJSClassMap() const
|
||||
IID2WrappedJSClassMap* GetWrappedJSClassMap() const
|
||||
{return mWrappedJSClassMap;}
|
||||
|
||||
IID2NativeInterfaceMap* GetIID2NativeInterfaceMap() const
|
||||
@ -567,7 +570,8 @@ public:
|
||||
JSFinalizeStatus status,
|
||||
bool isCompartmentGC,
|
||||
void* data);
|
||||
static void WeakPointerCallback(JSRuntime* rt, void* data);
|
||||
static void WeakPointerZoneGroupCallback(JSRuntime* rt, void* data);
|
||||
static void WeakPointerCompartmentCallback(JSRuntime* rt, JSCompartment* comp, void* data);
|
||||
|
||||
inline void AddVariantRoot(XPCTraceableVariant* variant);
|
||||
inline void AddWrappedJSRoot(nsXPCWrappedJS* wrappedJS);
|
||||
@ -611,7 +615,7 @@ public:
|
||||
nsTArray<nsXPCWrappedJS*>& WrappedJSToReleaseArray() { return mWrappedJSToReleaseArray; }
|
||||
|
||||
private:
|
||||
XPCJSRuntime(); // no implementation
|
||||
XPCJSRuntime() = delete;
|
||||
explicit XPCJSRuntime(nsXPConnect* aXPConnect);
|
||||
|
||||
void ReleaseIncrementally(nsTArray<nsISupports*>& array);
|
||||
@ -2449,6 +2453,14 @@ public:
|
||||
*/
|
||||
JSObject* GetJSObjectPreserveColor() const {return mJSObj;}
|
||||
|
||||
// Returns true if the wrapper chain contains references to multiple
|
||||
// compartments. If the wrapper chain contains references to multiple
|
||||
// compartments, then it must be registered on the XPCJSRuntime. Otherwise,
|
||||
// it should be registered in the CompartmentPrivate for the compartment of
|
||||
// the root's JS object. This will only return correct results when called
|
||||
// on the root wrapper and will assert if not called on a root wrapper.
|
||||
bool IsMultiCompartment() const;
|
||||
|
||||
nsXPCWrappedJSClass* GetClass() const {return mClass;}
|
||||
REFNSIID GetIID() const {return GetClass()->GetIID();}
|
||||
nsXPCWrappedJS* GetRootWrapper() const {return mRoot;}
|
||||
@ -2491,7 +2503,7 @@ public:
|
||||
|
||||
virtual ~nsXPCWrappedJS();
|
||||
protected:
|
||||
nsXPCWrappedJS(); // not implemented
|
||||
nsXPCWrappedJS() = delete;
|
||||
nsXPCWrappedJS(JSContext* cx,
|
||||
JSObject* aJSObj,
|
||||
nsXPCWrappedJSClass* aClass,
|
||||
@ -3677,20 +3689,7 @@ public:
|
||||
LocationHintAddon
|
||||
};
|
||||
|
||||
explicit CompartmentPrivate(JSCompartment* c)
|
||||
: wantXrays(false)
|
||||
, allowWaivers(true)
|
||||
, writeToGlobalPrototype(false)
|
||||
, skipWriteToGlobalPrototype(false)
|
||||
, isWebExtensionContentScript(false)
|
||||
, universalXPConnectEnabled(false)
|
||||
, forcePermissiveCOWs(false)
|
||||
, scriptability(c)
|
||||
, scope(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(xpc::CompartmentPrivate);
|
||||
mozilla::PodArrayZero(wrapperDenialWarnings);
|
||||
}
|
||||
explicit CompartmentPrivate(JSCompartment* c);
|
||||
|
||||
~CompartmentPrivate();
|
||||
|
||||
@ -3802,9 +3801,15 @@ public:
|
||||
locationURI = aLocationURI;
|
||||
}
|
||||
|
||||
JSObject2WrappedJSMap* GetWrappedJSMap() const { return mWrappedJSMap; }
|
||||
void UpdateWeakPointersAfterGC(XPCJSRuntime* runtime);
|
||||
|
||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf);
|
||||
|
||||
private:
|
||||
nsCString location;
|
||||
nsCOMPtr<nsIURI> locationURI;
|
||||
JSObject2WrappedJSMap* mWrappedJSMap;
|
||||
|
||||
bool TryParseLocationURI(LocationHint aType, nsIURI** aURI);
|
||||
};
|
||||
|
@ -383,11 +383,13 @@ private:
|
||||
class CompartmentStatsExtras {
|
||||
public:
|
||||
CompartmentStatsExtras()
|
||||
: sizeOfXPCPrivate(0)
|
||||
{}
|
||||
|
||||
nsAutoCString jsPathPrefix;
|
||||
nsAutoCString domPathPrefix;
|
||||
nsCOMPtr<nsIURI> location;
|
||||
size_t sizeOfXPCPrivate;
|
||||
|
||||
private:
|
||||
CompartmentStatsExtras(const CompartmentStatsExtras& other) = delete;
|
||||
|
Loading…
Reference in New Issue
Block a user