Bug 816054 - Assert wrappers are sensible when added to compartment map r=billm

--HG--
extra : rebase_source : 1089ac35de6ac4394e0ac9578257647e9bf25b92
This commit is contained in:
Jon Coppeard 2012-11-30 17:41:30 +00:00
parent 96c58ac7d3
commit 98b772bfc1
8 changed files with 57 additions and 34 deletions

View File

@ -1571,7 +1571,6 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
AutoMaybeTouchDeadCompartments agc(cx);
JSCompartment *destination = target->compartment();
WrapperMap &map = destination->crossCompartmentWrappers;
Value origv = ObjectValue(*origobj);
JSObject *newIdentity;
@ -1583,7 +1582,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
if (!origobj->swap(cx, target))
MOZ_CRASH();
newIdentity = origobj;
} else if (WrapperMap::Ptr p = map.lookup(origv)) {
} 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|.
@ -1591,7 +1590,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
// When we remove origv from the wrapper map, its wrapper, newIdentity,
// must immediately cease to be a cross-compartment wrapper. Neuter it.
map.remove(p);
destination->removeWrapper(p);
NukeCrossCompartmentWrapper(cx, newIdentity);
if (!newIdentity->swap(cx, target))
@ -1615,7 +1614,7 @@ JS_TransplantObject(JSContext *cx, JSObject *origobjArg, JSObject *targetArg)
JS_ASSERT(Wrapper::wrappedObject(newIdentityWrapper) == newIdentity);
if (!origobj->swap(cx, newIdentityWrapper))
MOZ_CRASH();
origobj->compartment()->crossCompartmentWrappers.put(ObjectValue(*newIdentity), origv);
origobj->compartment()->putWrapper(ObjectValue(*newIdentity), origv);
}
// The new identity object might be one of several things. Return it to avoid
@ -1652,7 +1651,6 @@ js_TransplantObjectWithWrapper(JSContext *cx,
JSObject *newWrapper;
JSCompartment *destination = targetobj->compartment();
WrapperMap &map = destination->crossCompartmentWrappers;
// |origv| is the map entry we're looking up. The map entries are going to
// be for |origobj|, not |origwrapper|.
@ -1660,14 +1658,14 @@ js_TransplantObjectWithWrapper(JSContext *cx,
// There might already be a wrapper for the original object in the new
// compartment.
if (WrapperMap::Ptr p = map.lookup(origv)) {
if (WrapperMap::Ptr p = destination->lookupWrapper(origv)) {
// There is. Make the existing cross-compartment wrapper a same-
// compartment wrapper.
newWrapper = &p->value.toObject();
// When we remove origv from the wrapper map, its wrapper, newWrapper,
// must immediately cease to be a cross-compartment wrapper. Neuter it.
map.remove(p);
destination->removeWrapper(p);
NukeCrossCompartmentWrapper(cx, newWrapper);
if (!newWrapper->swap(cx, targetwrapper))
@ -1707,8 +1705,8 @@ js_TransplantObjectWithWrapper(JSContext *cx,
JS_ASSERT(Wrapper::wrappedObject(wrapperGuts) == targetobj);
if (!origwrapper->swap(cx, wrapperGuts))
MOZ_CRASH();
origwrapper->compartment()->crossCompartmentWrappers.put(ObjectValue(*targetobj),
ObjectValue(*origwrapper));
origwrapper->compartment()->putWrapper(ObjectValue(*targetobj),
ObjectValue(*origwrapper));
}
return newWrapper;

View File

@ -227,6 +227,17 @@ WrapForSameCompartment(JSContext *cx, HandleObject obj, Value *vp)
return true;
}
bool
JSCompartment::putWrapper(const CrossCompartmentKey &wrapped, const js::Value &wrapper)
{
JS_ASSERT(wrapped.wrapped);
JS_ASSERT_IF(wrapped.kind == CrossCompartmentKey::StringWrapper, wrapper.isString());
JS_ASSERT_IF(wrapped.kind != CrossCompartmentKey::StringWrapper, wrapper.isObject());
// todo: uncomment when bug 815999 is fixed:
// JS_ASSERT(!wrapped.wrapped->isMarked(gc::GRAY));
return crossCompartmentWrappers.put(wrapped, wrapper);
}
bool
JSCompartment::wrap(JSContext *cx, Value *vp, JSObject *existing)
{
@ -337,7 +348,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp, JSObject *existing)
if (!wrapped)
return false;
vp->setString(wrapped);
if (!crossCompartmentWrappers.put(orig, *vp))
if (!putWrapper(orig, *vp))
return false;
if (str->compartment()->isGCMarking()) {
@ -385,7 +396,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp, JSObject *existing)
vp->setObject(*wrapper);
if (!crossCompartmentWrappers.put(key, *vp))
if (!putWrapper(key, *vp))
return false;
return true;

View File

@ -273,7 +273,6 @@ struct JSCompartment : public js::gc::GraphNodeBase
return gcState == Finished;
}
size_t gcBytes;
size_t gcTriggerBytes;
size_t gcMaxMallocBytes;
@ -297,8 +296,11 @@ struct JSCompartment : public js::gc::GraphNodeBase
void *data;
bool active; // GC flag, whether there are active frames
private:
js::WrapperMap crossCompartmentWrappers;
public:
/*
* These flags help us to discover if a compartment that shouldn't be alive
* manages to outlive a GC.
@ -401,6 +403,20 @@ struct JSCompartment : public js::gc::GraphNodeBase
bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
bool wrap(JSContext *cx, js::AutoIdVector &props);
bool putWrapper(const js::CrossCompartmentKey& wrapped, const js::Value& wrapper);
js::WrapperMap::Ptr lookupWrapper(const js::Value& wrapped) {
return crossCompartmentWrappers.lookup(wrapped);
}
void removeWrapper(js::WrapperMap::Ptr p) {
crossCompartmentWrappers.remove(p);
}
struct WrapperEnum : public js::WrapperMap::Enum {
WrapperEnum(JSCompartment *c) : js::WrapperMap::Enum(c->crossCompartmentWrappers) {}
};
void mark(JSTracer *trc);
void markTypes(JSTracer *trc);
void discardJitCode(js::FreeOp *fop, bool discardConstraints);

View File

@ -580,7 +580,7 @@ js::UnmarkGrayGCThing(void *thing)
JS_FRIEND_API(void)
js::VisitGrayWrapperTargets(JSCompartment *comp, GCThingCallback *callback, void *closure)
{
for (WrapperMap::Enum e(comp->crossCompartmentWrappers); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(comp); !e.empty(); e.popFront()) {
gc::Cell *thing = e.front().key.wrapped;
if (thing->isMarked(gc::GRAY))
callback(closure, thing);

View File

@ -2489,7 +2489,7 @@ InCrossCompartmentMap(JSObject *src, Cell *dst, JSGCTraceKind dstKind)
if (dstKind == JSTRACE_OBJECT) {
Value key = ObjectValue(*static_cast<JSObject *>(dst));
if (WrapperMap::Ptr p = srccomp->crossCompartmentWrappers.lookup(key)) {
if (WrapperMap::Ptr p = srccomp->lookupWrapper(key)) {
if (*p->value.unsafeGet() == ObjectValue(*src))
return true;
}
@ -2499,7 +2499,7 @@ InCrossCompartmentMap(JSObject *src, Cell *dst, JSGCTraceKind dstKind)
* If the cross-compartment edge is caused by the debugger, then we don't
* know the right hashtable key, so we have to iterate.
*/
for (WrapperMap::Enum e(srccomp->crossCompartmentWrappers); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(srccomp); !e.empty(); e.popFront()) {
if (e.front().key.wrapped == dst && ToMarkable(e.front().value) == src)
return true;
}
@ -2676,7 +2676,7 @@ BeginMarkPhase(JSRuntime *rt)
/* Set the maybeAlive flag based on cross-compartment edges. */
for (CompartmentsIter c(rt); !c.done(); c.next()) {
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
Cell *dst = e.front().key.wrapped;
dst->compartment()->maybeAlive = true;
}
@ -2876,7 +2876,7 @@ DropStringWrappers(JSRuntime *rt)
* compartment group.
*/
for (CompartmentsIter c(rt); !c.done(); c.next()) {
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
if (e.front().key.kind == CrossCompartmentKey::StringWrapper)
e.removeFront();
}
@ -3381,7 +3381,7 @@ BeginSweepPhase(JSRuntime *rt)
JS_ASSERT(!rt->gcCompartmentGroup);
for (CompartmentsIter c(rt); !c.done(); c.next()) {
JS_ASSERT(!c->gcIncomingGrayPointers);
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
if (e.front().key.kind != CrossCompartmentKey::StringWrapper)
AssertNotOnGrayList(&e.front().value.get().toObject());
}
@ -3550,7 +3550,7 @@ EndSweepPhase(JSRuntime *rt, JSGCInvocationKind gckind, bool lastGC)
JS_ASSERT(!c->gcIncomingGrayPointers);
JS_ASSERT(!c->gcLiveArrayBuffers);
for (WrapperMap::Enum e(c->crossCompartmentWrappers); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
if (e.front().key.kind != CrossCompartmentKey::StringWrapper)
AssertNotOnGrayList(&e.front().value.get().toObject());
}

View File

@ -2834,7 +2834,7 @@ proxy_TraceObject(JSTracer *trc, RawObject obj)
* the invariant that the wrapped object is the key in the wrapper map.
*/
Value key = ObjectValue(*referent);
WrapperMap::Ptr p = obj->compartment()->crossCompartmentWrappers.lookup(key);
WrapperMap::Ptr p = obj->compartment()->lookupWrapper(key);
JS_ASSERT(*p->value.unsafeGet() == ObjectValue(*obj));
}
}

View File

@ -1037,8 +1037,7 @@ js::NukeCrossCompartmentWrappers(JSContext* cx,
continue;
// Iterate the wrappers looking for anything interesting.
WrapperMap &pmap = c->crossCompartmentWrappers;
for (WrapperMap::Enum e(pmap); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
// Some cross-compartment wrappers are for strings. We're not
// interested in those.
const CrossCompartmentKey &k = e.front().key;
@ -1075,17 +1074,18 @@ js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget)
JS_ASSERT(origTarget);
Value origv = ObjectValue(*origTarget);
JSCompartment *wcompartment = wobj->compartment();
WrapperMap &pmap = wcompartment->crossCompartmentWrappers;
// If we're mapping to a different target (as opposed to just recomputing
// for the same target), we must not have an existing wrapper for the new
// target, otherwise this will break.
JS_ASSERT_IF(origTarget != newTarget, !pmap.has(ObjectValue(*newTarget)));
JS_ASSERT_IF(origTarget != newTarget,
!wcompartment->lookupWrapper(ObjectValue(*newTarget)));
// The old value should still be in the cross-compartment wrapper map, and
// the lookup should return wobj.
JS_ASSERT(&pmap.lookup(origv)->value.unsafeGet()->toObject() == wobj);
pmap.remove(origv);
WrapperMap::Ptr p = wcompartment->lookupWrapper(origv);
JS_ASSERT(&p->value.unsafeGet()->toObject() == wobj);
wcompartment->removeWrapper(p);
// When we remove origv from the wrapper map, its wrapper, wobj, must
// immediately cease to be a cross-compartment wrapper. Neuter it.
@ -1117,7 +1117,7 @@ js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget)
// Update the entry in the compartment's wrapper map to point to the old
// wrapper, which has now been updated (via reuse or swap).
pmap.put(ObjectValue(*newTarget), ObjectValue(*wobj));
wcompartment->putWrapper(ObjectValue(*newTarget), ObjectValue(*wobj));
return true;
}
@ -1134,8 +1134,7 @@ js::RemapAllWrappersForObject(JSContext *cx, JSObject *oldTarget,
return false;
for (CompartmentsIter c(cx->runtime); !c.done(); c.next()) {
WrapperMap &pmap = c->crossCompartmentWrappers;
if (WrapperMap::Ptr wp = pmap.lookup(origv)) {
if (WrapperMap::Ptr wp = c->lookupWrapper(origv)) {
// We found a wrapper. Remember and root it.
toTransplant.infallibleAppend(WrapperValue(wp));
}
@ -1165,8 +1164,7 @@ js::RecomputeWrappers(JSContext *cx, const CompartmentFilter &sourceFilter,
continue;
// Iterate over the wrappers, filtering appropriately.
WrapperMap &pmap = c->crossCompartmentWrappers;
for (WrapperMap::Enum e(pmap); !e.empty(); e.popFront()) {
for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
// Filter out non-objects.
const CrossCompartmentKey &k = e.front().key;
if (k.kind != CrossCompartmentKey::ObjectWrapper)

View File

@ -651,7 +651,7 @@ Debugger::wrapEnvironment(JSContext *cx, Handle<Env*> env, Value *rval)
}
CrossCompartmentKey key(CrossCompartmentKey::DebuggerEnvironment, object, env);
if (!object->compartment()->crossCompartmentWrappers.put(key, ObjectValue(*envobj))) {
if (!object->compartment()->putWrapper(key, ObjectValue(*envobj))) {
environments.remove(env);
js_ReportOutOfMemory(cx);
return false;
@ -689,7 +689,7 @@ Debugger::wrapDebuggeeValue(JSContext *cx, Value *vp)
if (obj->compartment() != object->compartment()) {
CrossCompartmentKey key(CrossCompartmentKey::DebuggerObject, object, obj);
if (!object->compartment()->crossCompartmentWrappers.put(key, ObjectValue(*dobj))) {
if (!object->compartment()->putWrapper(key, ObjectValue(*dobj))) {
objects.remove(obj);
js_ReportOutOfMemory(cx);
return false;
@ -2612,7 +2612,7 @@ Debugger::wrapScript(JSContext *cx, HandleScript script)
}
CrossCompartmentKey key(CrossCompartmentKey::DebuggerScript, object, script);
if (!object->compartment()->crossCompartmentWrappers.put(key, ObjectValue(*scriptobj))) {
if (!object->compartment()->putWrapper(key, ObjectValue(*scriptobj))) {
scripts.remove(script);
js_ReportOutOfMemory(cx);
return NULL;