Bug 1188364 - Supress GC while transplanting to prevent compacting GC observing intermediate state r=terrence

This commit is contained in:
Jon Coppeard 2016-02-15 10:37:31 +00:00
parent 616689b68c
commit fd636314d9
5 changed files with 55 additions and 52 deletions

View File

@ -793,6 +793,7 @@ class GCRuntime
bool isIncrementalGc() const { return isIncremental; }
bool isFullGc() const { return isFull; }
bool isCompactingGc() const { return isCompacting; }
bool shouldCleanUpEverything() { return cleanUpEverything; }

View File

@ -916,53 +916,54 @@ JS_TransplantObject(JSContext* cx, HandleObject origobj, HandleObject target)
RootedValue origv(cx, ObjectValue(*origobj));
RootedObject newIdentity(cx);
{
AutoDisableProxyCheck adpc(cx->runtime());
// Don't allow a compacting GC to observe any intermediate state.
AutoDisableCompactingGC nocgc(cx->runtime());
JSCompartment* destination = target->compartment();
AutoDisableProxyCheck adpc(cx->runtime());
if (origobj->compartment() == destination) {
// If the original object is in the same compartment as the
// destination, then we know that we won't find a wrapper in the
// destination's cross compartment map and that the same
// object will continue to work.
if (!JSObject::swap(cx, origobj, target))
MOZ_CRASH();
newIdentity = origobj;
} else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
// There might already be a wrapper for the original object in
// the new compartment. If there is, we use its identity and swap
// in the contents of |target|.
newIdentity = &p->value().get().toObject();
JSCompartment* destination = target->compartment();
// When we remove origv from the wrapper map, its wrapper, newIdentity,
// must immediately cease to be a cross-compartment wrapper. Nuke it.
destination->removeWrapper(p);
NukeCrossCompartmentWrapper(cx, newIdentity);
if (!JSObject::swap(cx, newIdentity, target))
MOZ_CRASH();
} else {
// Otherwise, we use |target| for the new identity object.
newIdentity = target;
}
// Now, iterate through other scopes looking for references to the
// old object, and update the relevant cross-compartment wrappers.
if (!RemapAllWrappersForObject(cx, origobj, newIdentity))
if (origobj->compartment() == destination) {
// If the original object is in the same compartment as the
// destination, then we know that we won't find a wrapper in the
// destination's cross compartment map and that the same
// object will continue to work.
if (!JSObject::swap(cx, origobj, target))
MOZ_CRASH();
newIdentity = origobj;
} else if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
// There might already be a wrapper for the original object in
// the new compartment. If there is, we use its identity and swap
// in the contents of |target|.
newIdentity = &p->value().get().toObject();
// Lastly, update the original object to point to the new one.
if (origobj->compartment() != destination) {
RootedObject newIdentityWrapper(cx, newIdentity);
AutoCompartment ac(cx, origobj);
if (!JS_WrapObject(cx, &newIdentityWrapper))
MOZ_CRASH();
MOZ_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
if (!JSObject::swap(cx, origobj, newIdentityWrapper))
MOZ_CRASH();
origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv);
}
// When we remove origv from the wrapper map, its wrapper, newIdentity,
// must immediately cease to be a cross-compartment wrapper. Nuke it.
destination->removeWrapper(p);
NukeCrossCompartmentWrapper(cx, newIdentity);
if (!JSObject::swap(cx, newIdentity, target))
MOZ_CRASH();
} else {
// Otherwise, we use |target| for the new identity object.
newIdentity = target;
}
// Now, iterate through other scopes looking for references to the
// old object, and update the relevant cross-compartment wrappers.
if (!RemapAllWrappersForObject(cx, origobj, newIdentity))
MOZ_CRASH();
// Lastly, update the original object to point to the new one.
if (origobj->compartment() != destination) {
RootedObject newIdentityWrapper(cx, newIdentity);
AutoCompartment ac(cx, origobj);
if (!JS_WrapObject(cx, &newIdentityWrapper))
MOZ_CRASH();
MOZ_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
if (!JSObject::swap(cx, origobj, newIdentityWrapper))
MOZ_CRASH();
origobj->compartment()->putWrapper(cx, CrossCompartmentKey(newIdentity), origv);
}
// The new identity object might be one of several things. Return it to avoid

View File

@ -2005,6 +2005,8 @@ AutoDisableCompactingGC::AutoDisableCompactingGC(JSRuntime* rt)
: gc(rt->gc)
{
gc.disableCompactingGC();
if (gc.isIncrementalGCInProgress() && gc.isCompactingGc())
AutoFinishGC finishGC(rt);
}
AutoDisableCompactingGC::~AutoDisableCompactingGC()

View File

@ -300,7 +300,7 @@ IsCrossCompartmentWrapper(JSObject* obj);
void
NukeCrossCompartmentWrapper(JSContext* cx, JSObject* wrapper);
bool
void
RemapWrapper(JSContext* cx, JSObject* wobj, JSObject* newTarget);
JS_FRIEND_API(bool)

View File

@ -494,7 +494,9 @@ js::NukeCrossCompartmentWrappers(JSContext* cx,
// Given a cross-compartment wrapper |wobj|, update it to point to
// |newTarget|. This recomputes the wrapper with JS_WrapValue, and thus can be
// useful even if wrapper already points to newTarget.
bool
// This operation crashes on failure rather than leaving the heap in an
// inconsistent state.
void
js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
{
RootedObject wobj(cx, wobjArg);
@ -551,8 +553,8 @@ js::RemapWrapper(JSContext* cx, JSObject* wobjArg, JSObject* newTargetArg)
// Update the entry in the compartment's wrapper map to point to the old
// wrapper, which has now been updated (via reuse or swap).
MOZ_ASSERT(wobj->is<WrapperObject>());
wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj));
return true;
if (!wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj)))
MOZ_CRASH();
}
// Remap all cross-compartment wrappers pointing to |oldTarget| to point to
@ -575,10 +577,8 @@ js::RemapAllWrappersForObject(JSContext* cx, JSObject* oldTargetArg,
}
}
for (const Value& v : toTransplant) {
if (!RemapWrapper(cx, &v.toObject(), newTarget))
MOZ_CRASH();
}
for (const Value& v : toTransplant)
RemapWrapper(cx, &v.toObject(), newTarget);
return true;
}
@ -615,8 +615,7 @@ js::RecomputeWrappers(JSContext* cx, const CompartmentFilter& sourceFilter,
for (const Value& v : toRecompute) {
JSObject* wrapper = &v.toObject();
JSObject* wrapped = Wrapper::wrappedObject(wrapper);
if (!RemapWrapper(cx, wrapper, wrapped))
MOZ_CRASH();
RemapWrapper(cx, wrapper, wrapped);
}
return true;