Bug 1363214 - Remove JS::Realm/JSCompartment inheritance. r=jonco

--HG--
extra : rebase_source : ad63de7c6676532640659d1fccced7f83798e06d
This commit is contained in:
Jan de Mooij 2018-06-06 14:30:52 +02:00
parent 9f253dcf5c
commit 156284156a
15 changed files with 164 additions and 92 deletions

View File

@ -711,7 +711,10 @@ struct ZoneStats
macro(Other, MallocHeap, baselineStubsOptimized) \
macro(Other, MallocHeap, cachedCFG) \
macro(Other, MallocHeap, uniqueIdMap) \
macro(Other, MallocHeap, shapeTables)
macro(Other, MallocHeap, shapeTables) \
macro(Other, MallocHeap, compartmentObjects) \
macro(Other, MallocHeap, crossCompartmentWrappersTables) \
macro(Other, MallocHeap, compartmentsPrivateData)
ZoneStats()
: FOR_EACH_SIZE(ZERO_SIZE)
@ -827,12 +830,10 @@ struct RealmStats
macro(Other, MallocHeap, innerViewsTable) \
macro(Other, MallocHeap, lazyArrayBuffersTable) \
macro(Other, MallocHeap, objectMetadataTable) \
macro(Other, MallocHeap, crossCompartmentWrappersTable) \
macro(Other, MallocHeap, savedStacksSet) \
macro(Other, MallocHeap, varNamesSet) \
macro(Other, MallocHeap, nonSyntacticLexicalScopesTable) \
macro(Other, MallocHeap, jitRealm) \
macro(Other, MallocHeap, privateData) \
macro(Other, MallocHeap, scriptCountsMap)
RealmStats()

View File

@ -44,21 +44,33 @@ struct GCPolicy<Realm*> : public NonGCPointerPolicy<Realm*>
extern JS_PUBLIC_API(Realm*)
GetCurrentRealmOrNull(JSContext* cx);
namespace shadow {
class Realm
{
protected:
JSCompartment* compartment_;
explicit Realm(JSCompartment* comp)
: compartment_(comp)
{}
public:
JSCompartment* compartment() {
return compartment_;
}
static shadow::Realm* get(JS::Realm* realm) {
return reinterpret_cast<shadow::Realm*>(realm);
}
};
}; // namespace shadow
// Return the compartment that contains a given realm.
inline JSCompartment*
GetCompartmentForRealm(Realm* realm) {
// Implementation note: For now, realms are a fiction; we treat realms and
// compartments as being one-to-one, but they are actually identical.
return reinterpret_cast<JSCompartment*>(realm);
}
// Return the realm in a given compartment.
//
// Deprecated. There is currently exactly one realm per compartment, but this
// will change.
inline Realm*
GetRealmForCompartment(JSCompartment* compartment) {
return reinterpret_cast<Realm*>(compartment);
GetCompartmentForRealm(Realm* realm)
{
return shadow::Realm::get(realm)->compartment();
}
// Return an object's realm. All objects except cross-compartment wrappers are

View File

@ -1063,7 +1063,9 @@ GetContextRealm(const JSContext* cx)
inline JSCompartment*
GetContextCompartment(const JSContext* cx)
{
return GetCompartmentForRealm(GetContextRealm(cx));
if (JS::Realm* realm = GetContextRealm(cx))
return GetCompartmentForRealm(realm);
return nullptr;
}
inline JS::Zone*

View File

@ -236,6 +236,7 @@ const WHITELIST_TYPES: &'static [&'static str] = &[
"JS::TransferableOwnership",
"JS::Value",
"JS::WarningReporter",
"JS::shadow::Realm",
"JS::shadow::Zone",
"JS::Zone",
];

View File

@ -1055,7 +1055,8 @@ pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
#[inline]
pub unsafe fn get_object_compartment(obj: *mut JSObject) -> *mut JSCompartment {
(*get_object_group(obj)).realm as *mut JSCompartment
let realm = (*get_object_group(obj)).realm as *const JS::shadow::Realm;
(*realm).compartment_
}
#[inline]

View File

@ -1338,8 +1338,12 @@ GCRuntime::finish()
if (rt->gcInitialized) {
AutoSetThreadIsSweeping threadIsSweeping;
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) {
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next())
js_delete(JS::GetRealmForCompartment(comp.get()));
for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) {
for (RealmsInCompartmentIter realm(comp); !realm.done(); realm.next())
js_delete(realm.get());
comp->realms().clear();
js_delete(comp.get());
}
zone->compartments().clear();
js_delete(zone.get());
}
@ -3820,11 +3824,18 @@ Realm::destroy(FreeOp* fop)
JSRuntime* rt = fop->runtime();
if (auto callback = rt->destroyRealmCallback)
callback(fop, this);
if (auto callback = rt->destroyCompartmentCallback)
callback(fop, this);
if (principals())
JS_DropPrincipals(rt->mainContextFromOwnThread(), principals());
fop->delete_(this);
}
void
JSCompartment::destroy(FreeOp* fop)
{
JSRuntime* rt = fop->runtime();
if (auto callback = rt->destroyCompartmentCallback)
callback(fop, this);
fop->delete_(this);
rt->gc.stats().sweptCompartment();
}
@ -3856,22 +3867,53 @@ Zone::sweepCompartments(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime
JSCompartment** write = read;
while (read < end) {
JSCompartment* comp = *read++;
Realm* realm = JS::GetRealmForCompartment(comp);
/*
* Don't delete the last compartment and realm if keepAtleastOne is
* still true, meaning all the other compartments were deleted.
*/
bool keepAtleastOneRealm = read == end && keepAtleastOne;
comp->sweepRealms(fop, keepAtleastOneRealm, destroyingRuntime);
if (!comp->realms().empty()) {
*write++ = comp;
keepAtleastOne = false;
} else {
comp->destroy(fop);
}
}
compartments().shrinkTo(write - compartments().begin());
MOZ_ASSERT_IF(keepAtleastOne, !compartments().empty());
MOZ_ASSERT_IF(destroyingRuntime, compartments().empty());
}
void
JSCompartment::sweepRealms(FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime)
{
MOZ_ASSERT(!realms().empty());
MOZ_ASSERT_IF(destroyingRuntime, !keepAtleastOne);
Realm** read = realms().begin();
Realm** end = realms().end();
Realm** write = read;
while (read < end) {
Realm* realm = *read++;
/*
* Don't delete the last realm if keepAtleastOne is still true, meaning
* all the other realms were deleted.
*/
bool dontDelete = read == end && keepAtleastOne;
if ((realm->marked() || dontDelete) && !destroyingRuntime) {
*write++ = comp;
*write++ = realm;
keepAtleastOne = false;
} else {
realm->destroy(fop);
}
}
compartments().shrinkTo(write - compartments().begin());
MOZ_ASSERT_IF(keepAtleastOne, !compartments().empty());
realms().shrinkTo(write - realms().begin());
MOZ_ASSERT_IF(keepAtleastOne, !realms().empty());
MOZ_ASSERT_IF(destroyingRuntime, realms().empty());
}
void
@ -7952,6 +7994,7 @@ js::NewRealm(JSContext* cx, JSPrincipals* principals, const JS::RealmOptions& op
JS_AbortIfWrongThread(cx);
UniquePtr<Zone> zoneHolder;
UniquePtr<JSCompartment> compHolder;
Zone* zone = nullptr;
JS::ZoneSpecifier zoneSpec = options.creationOptions().zoneSpecifier();
@ -7984,14 +8027,18 @@ js::NewRealm(JSContext* cx, JSPrincipals* principals, const JS::RealmOptions& op
zone = zoneHolder.get();
}
UniquePtr<Realm> realm = cx->make_unique<Realm>(zone, options);
compHolder = cx->make_unique<JSCompartment>(zone);
if (!compHolder || !compHolder->init(cx))
return nullptr;
JSCompartment* comp = compHolder.get();
UniquePtr<Realm> realm(cx->new_<Realm>(comp, options));
if (!realm || !realm->init(cx))
return nullptr;
// Set up the principals.
JS::SetRealmPrincipals(realm.get(), principals);
JSCompartment* comp = realm->compartment();
if (!comp->realms().append(realm.get())) {
ReportOutOfMemory(cx);
return nullptr;
@ -8018,6 +8065,7 @@ js::NewRealm(JSContext* cx, JSPrincipals* principals, const JS::RealmOptions& op
}
}
mozilla::Unused << compHolder.release();
mozilla::Unused << zoneHolder.release();
return realm.release();
}

View File

@ -402,7 +402,9 @@ Zone::deleteEmptyCompartment(JSCompartment* comp)
MOZ_ASSERT(comp->realms().length() == 1);
Realm* realm = comp->realms()[0];
realm->destroy(runtimeFromMainThread()->defaultFreeOp());
FreeOp* fop = runtimeFromMainThread()->defaultFreeOp();
realm->destroy(fop);
comp->destroy(fop);
compartments().clear();
}

View File

@ -201,7 +201,10 @@ struct Zone : public JS::shadow::Zone,
size_t* cachedCFG,
size_t* uniqueIdMap,
size_t* shapeTables,
size_t* atomsMarkBitmaps);
size_t* atomsMarkBitmaps,
size_t* compartmentObjects,
size_t* crossCompartmentWrappersTables,
size_t* compartmentsPrivateData);
// Iterate over all cells in the zone. See the definition of ZoneCellIter
// in gc/GC-inl.h for the possible arguments and documentation.

View File

@ -447,7 +447,7 @@ ErrorCopier::~ErrorCopier()
// The provenance of Debugger.DebuggeeWouldRun is the topmost locking
// debugger compartment; it should not be copied around.
if (JS::GetCompartmentForRealm(ar->origin()) != cx->compartment() &&
if (ar->origin()->compartment() != cx->compartment() &&
cx->isExceptionPending() &&
!cx->isThrowingDebuggeeWouldRun())
{

View File

@ -55,14 +55,16 @@ ObjectRealm::~ObjectRealm()
MOZ_ASSERT(enumerators == iteratorSentinel_.get());
}
Realm::Realm(JS::Zone* zone, const JS::RealmOptions& options)
: JSCompartment(zone),
Realm::Realm(JSCompartment* comp, const JS::RealmOptions& options)
: JS::shadow::Realm(comp),
zone_(comp->zone()),
runtime_(comp->runtimeFromMainThread()),
creationOptions_(options.creationOptions()),
behaviors_(options.behaviors()),
global_(nullptr),
objects_(zone),
objects_(zone_),
randomKeyGenerator_(runtime_->forkRandomKeyGenerator()),
wasm(zone->runtimeFromMainThread()),
wasm(runtime_),
performanceMonitoring(runtime_)
{
MOZ_ASSERT_IF(creationOptions_.mergeable(),
@ -120,11 +122,6 @@ ObjectRealm::init(JSContext* cx)
bool
Realm::init(JSContext* cx)
{
// Initialize JSCompartment. This is temporary until Realm and
// JSCompartment are completely separated.
if (!JSCompartment::init(cx))
return false;
/*
* As a hack, we clear our timezone cache every time we create a new realm.
* This ensures that the cache is always relatively fresh, but shouldn't
@ -1037,7 +1034,7 @@ Realm::clearTables()
// No scripts should have run in this realm. This is used when merging
// a realm that has been used off thread into another realm and zone.
JS::GetCompartmentForRealm(this)->assertNoCrossCompartmentWrappers();
compartment()->assertNoCrossCompartmentWrappers();
MOZ_ASSERT(!jitRealm_);
MOZ_ASSERT(!debugEnvs_);
MOZ_ASSERT(objects_.enumerators->next() == objects_.enumerators);
@ -1076,7 +1073,7 @@ void
Realm::setNewObjectMetadata(JSContext* cx, HandleObject obj)
{
MOZ_ASSERT(obj->realm() == this);
assertSameCompartment(cx, JS::GetCompartmentForRealm(this), obj);
assertSameCompartment(cx, compartment(), obj);
AutoEnterOOMUnsafeRegion oomUnsafe;
if (JSObject* metadata = allocationMetadataBuilder_->build(cx, obj, oomUnsafe)) {
@ -1311,13 +1308,16 @@ Realm::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, HandleObject handler)
}
void
JSCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* crossCompartmentWrappersArg)
JSCompartment::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* compartmentObjects,
size_t* crossCompartmentWrappersTables,
size_t* compartmentsPrivateData)
{
// Note that Realm inherits from JSCompartment (for now) so sizeof(*this) is
// included in that.
*compartmentObjects += mallocSizeOf(this);
*crossCompartmentWrappersTables += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
*crossCompartmentWrappersArg += crossCompartmentWrappers.sizeOfExcludingThis(mallocSizeOf);
if (auto callback = runtime_->sizeOfIncludingThisCompartmentCallback)
*compartmentsPrivateData += callback(mallocSizeOf, this);
}
void
@ -1349,17 +1349,12 @@ Realm::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* innerViewsArg,
size_t* lazyArrayBuffersArg,
size_t* objectMetadataTablesArg,
size_t* crossCompartmentWrappersArg,
size_t* savedStacksSet,
size_t* varNamesSet,
size_t* nonSyntacticLexicalEnvironmentsArg,
size_t* jitRealm,
size_t* privateData,
size_t* scriptCountsMapArg)
{
// This is temporary until Realm and JSCompartment are completely separated.
JSCompartment::addSizeOfExcludingThis(mallocSizeOf, crossCompartmentWrappersArg);
*realmObject += mallocSizeOf(this);
objectGroups_.addSizeOfExcludingThis(mallocSizeOf, tiAllocationSiteTables,
tiArrayTypeTables, tiObjectTypeTables,
@ -1378,15 +1373,10 @@ Realm::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
if (jitRealm_)
*jitRealm += jitRealm_->sizeOfIncludingThis(mallocSizeOf);
auto callback = runtime_->sizeOfIncludingThisCompartmentCallback;
if (callback)
*privateData += callback(mallocSizeOf, this);
if (scriptCountsMap) {
*scriptCountsMapArg += scriptCountsMap->sizeOfIncludingThis(mallocSizeOf);
for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront()) {
for (auto r = scriptCountsMap->all(); !r.empty(); r.popFront())
*scriptCountsMapArg += r.front().value()->sizeOfIncludingThis(mallocSizeOf);
}
}
}

View File

@ -552,11 +552,10 @@ class WeakMapBase;
struct JSCompartment
{
protected:
private:
JS::Zone* zone_;
JSRuntime* runtime_;
private:
js::WrapperMap crossCompartmentWrappers;
using RealmVector = js::Vector<JS::Realm*, 1, js::SystemAllocPolicy>;
@ -603,11 +602,11 @@ struct JSCompartment
MOZ_ASSERT(crossCompartmentWrappers.empty());
}
protected:
void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* crossCompartmentWrappersArg);
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* compartmentObjects,
size_t* crossCompartmentWrappersTables,
size_t* compartmentsPrivateData);
public:
#ifdef JSGC_HASH_TABLE_CHECKS
void checkWrapperMapAfterMovingGC();
#endif
@ -616,12 +615,12 @@ struct JSCompartment
bool getNonWrapperObjectForCurrentCompartment(JSContext* cx, js::MutableHandleObject obj);
bool getOrCreateWrapper(JSContext* cx, js::HandleObject existing, js::MutableHandleObject obj);
protected:
public:
explicit JSCompartment(JS::Zone* zone);
MOZ_MUST_USE bool init(JSContext* cx);
void destroy(js::FreeOp* fop);
public:
MOZ_MUST_USE inline bool wrap(JSContext* cx, JS::MutableHandleValue vp);
MOZ_MUST_USE bool wrap(JSContext* cx, js::MutableHandleString strp);
@ -675,8 +674,8 @@ struct JSCompartment
void traceOutgoingCrossCompartmentWrappers(JSTracer* trc);
static void traceIncomingCrossCompartmentEdgesForZoneGC(JSTracer* trc);
void sweepRealms(js::FreeOp* fop, bool keepAtleastOne, bool destroyingRuntime);
void sweepAfterMinorGC(JSTracer* trc);
void sweepCrossCompartmentWrappers();
static void fixupCrossCompartmentWrappersAfterMovingGC(JSTracer* trc);
@ -752,8 +751,11 @@ class ObjectRealm
} // namespace js
class JS::Realm : private JSCompartment
class JS::Realm : public JS::shadow::Realm
{
JS::Zone* zone_;
JSRuntime* runtime_;
const JS::RealmCreationOptions creationOptions_;
JS::RealmBehaviors behaviors_;
@ -885,7 +887,7 @@ class JS::Realm : private JSCompartment
void operator=(const Realm&) = delete;
public:
Realm(JS::Zone* zone, const JS::RealmOptions& options);
Realm(JSCompartment* comp, const JS::RealmOptions& options);
~Realm();
MOZ_MUST_USE bool init(JSContext* cx);
@ -901,18 +903,12 @@ class JS::Realm : private JSCompartment
size_t* innerViews,
size_t* lazyArrayBuffers,
size_t* objectMetadataTables,
size_t* crossCompartmentWrappers,
size_t* savedStacksSet,
size_t* varNamesSet,
size_t* nonSyntacticLexicalScopes,
size_t* jitRealm,
size_t* privateData,
size_t* scriptCountsMapArg);
JSCompartment* compartment() {
return this;
}
JS::Zone* zone() {
return zone_;
}

View File

@ -145,7 +145,7 @@ struct JSContext : public JS::RootingContext,
template <typename T>
inline bool isInsideCurrentCompartment(T thing) const {
return thing->compartment() == GetCompartmentForRealm(realm_);
return thing->compartment() == compartment();
}
void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes, void* reallocPtr = nullptr) {
@ -242,10 +242,11 @@ struct JSContext : public JS::RootingContext,
return nurserySuppressions_;
}
// Threads may freely access any data in their compartment and zone.
// Threads may freely access any data in their realm, compartment and zone.
JSCompartment* compartment() const {
return JS::GetCompartmentForRealm(realm_);
return realm_ ? JS::GetCompartmentForRealm(realm_) : nullptr;
}
JS::Realm* realm() const {
return realm_;
}

View File

@ -327,7 +327,10 @@ StatsZoneCallback(JSRuntime* rt, void* data, Zone* zone)
&zStats.cachedCFG,
&zStats.uniqueIdMap,
&zStats.shapeTables,
&rtStats->runtime.atomsMarkBitmaps);
&rtStats->runtime.atomsMarkBitmaps,
&zStats.compartmentObjects,
&zStats.crossCompartmentWrappersTables,
&zStats.compartmentsPrivateData);
}
static void
@ -355,12 +358,10 @@ StatsRealmCallback(JSContext* cx, void* data, Handle<Realm*> realm)
&realmStats.innerViewsTable,
&realmStats.lazyArrayBuffersTable,
&realmStats.objectMetadataTable,
&realmStats.crossCompartmentWrappersTable,
&realmStats.savedStacksSet,
&realmStats.varNamesSet,
&realmStats.nonSyntacticLexicalScopesTable,
&realmStats.jitRealm,
&realmStats.privateData,
&realmStats.scriptCountsMap);
}

View File

@ -4595,7 +4595,10 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
size_t* cachedCFG,
size_t* uniqueIdMap,
size_t* shapeTables,
size_t* atomsMarkBitmaps)
size_t* atomsMarkBitmaps,
size_t* compartmentObjects,
size_t* crossCompartmentWrappersTables,
size_t* compartmentsPrivateData)
{
*typePool += types.typeLifoAlloc().sizeOfExcludingThis(mallocSizeOf);
*regexpZone += regExps.sizeOfExcludingThis(mallocSizeOf);
@ -4605,6 +4608,13 @@ Zone::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
*shapeTables += baseShapes().sizeOfExcludingThis(mallocSizeOf)
+ initialShapes().sizeOfExcludingThis(mallocSizeOf);
*atomsMarkBitmaps += markedAtoms().sizeOfExcludingThis(mallocSizeOf);
for (CompartmentsInZoneIter comp(this); !comp.done(); comp.next()) {
comp->addSizeOfIncludingThis(mallocSizeOf,
compartmentObjects,
crossCompartmentWrappersTables,
compartmentsPrivateData);
}
}
TypeZone::TypeZone(Zone* zone)

View File

@ -1350,6 +1350,19 @@ ReportZoneStats(const JS::ZoneStats& zStats,
zStats.shapeTables,
"Tables storing shape information.");
ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/compartment-objects"),
zStats.compartmentObjects,
"The JSCompartment objects in this zone.");
ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/cross-compartment-wrapper-tables"),
zStats.crossCompartmentWrappersTables,
"The cross-compartment wrapper tables.");
ZRREPORT_BYTES(pathPrefix + NS_LITERAL_CSTRING("compartments/private-data"),
zStats.compartmentsPrivateData,
"Extra data attached to each compartment by XPConnect, including "
"its wrapped-js.");
ZRREPORT_GC_BYTES(pathPrefix + NS_LITERAL_CSTRING("lazy-scripts/gc-heap"),
zStats.lazyScriptsGCHeap,
"Scripts that haven't executed yet.");
@ -1750,7 +1763,7 @@ ReportRealmStats(const JS::RealmStats& realmStats,
ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("realm-object"),
realmStats.realmObject,
"The JSCompartment object itself.");
"The JS::Realm object itself.");
ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("realm-tables"),
realmStats.realmTables,
@ -1768,10 +1781,6 @@ ReportRealmStats(const JS::RealmStats& realmStats,
realmStats.objectMetadataTable,
"The table used by debugging tools for tracking object metadata");
ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("cross-compartment-wrapper-table"),
realmStats.crossCompartmentWrappersTable,
"The cross-compartment wrapper table.");
ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("saved-stacks-set"),
realmStats.savedStacksSet,
"The saved stacks set.");
@ -1784,11 +1793,6 @@ ReportRealmStats(const JS::RealmStats& realmStats,
realmStats.jitRealm,
"The JIT realm.");
ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("private-data"),
realmStats.privateData,
"Extra data attached to the realm by XPConnect, including "
"its wrapped-js.");
ZRREPORT_BYTES(realmJSPathPrefix + NS_LITERAL_CSTRING("script-counts-map"),
realmStats.scriptCountsMap,
"Profiling-related information for scripts.");