// 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. // 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); RootedObject newTarget(cx, newTargetArg); MOZ_ASSERT(wobj->is<CrossCompartmentWrapperObject>()); MOZ_ASSERT(!newTarget->is<CrossCompartmentWrapperObject>()); JSObject* origTarget = Wrapper::wrappedObject(wobj); MOZ_ASSERT(origTarget); Value origv = ObjectValue(*origTarget); JSCompartment* wcompartment = wobj->compartment(); AutoDisableProxyCheck adpc(cx->runtime()); // 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. MOZ_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. WrapperMap::Ptr p = wcompartment->lookupWrapper(origv); MOZ_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. Nuke it. NukeCrossCompartmentWrapper(cx, wobj); // First, we wrap it in the new compartment. We try to use the existing // wrapper, |wobj|, since it's been nuked anyway. The wrap() function has // the choice to reuse |wobj| or not. RootedObject tobj(cx, newTarget); AutoCompartment ac(cx, wobj); if (!wcompartment->wrap(cx, &tobj, wobj)) MOZ_CRASH(); // If wrap() reused |wobj|, it will have overwritten it and returned with // |tobj == wobj|. Otherwise, |tobj| will point to a new wrapper and |wobj| // will still be nuked. In the latter case, we replace |wobj| with the // contents of the new wrapper in |tobj|. if (tobj != wobj) { // Now, because we need to maintain object identity, we do a brain // transplant on the old object so that it contains the contents of the // new one. if (!JSObject::swap(cx, wobj, tobj)) MOZ_CRASH(); } // Before swapping, this wrapper came out of wrap(), which enforces the // invariant that the wrapper in the map points directly to the key. MOZ_ASSERT(Wrapper::wrappedObject(wobj) == 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). MOZ_ASSERT(wobj->is<WrapperObject>()); if (!wcompartment->putWrapper(cx, CrossCompartmentKey(newTarget), ObjectValue(*wobj))) MOZ_CRASH(); }
/* static */ void ProxyObject::trace(JSTracer* trc, JSObject* obj) { ProxyObject* proxy = &obj->as<ProxyObject>(); TraceEdge(trc, proxy->shapePtr(), "ProxyObject_shape"); #ifdef DEBUG if (TlsContext.get()->isStrictProxyCheckingEnabled() && proxy->is<WrapperObject>()) { JSObject* referent = MaybeForwarded(proxy->target()); if (referent->compartment() != proxy->compartment()) { /* * Assert that this proxy is tracked in the wrapper map. We maintain * the invariant that the wrapped object is the key in the wrapper map. */ Value key = ObjectValue(*referent); WrapperMap::Ptr p = proxy->compartment()->lookupWrapper(key); MOZ_ASSERT(p); MOZ_ASSERT(*p->value().unsafeGet() == ObjectValue(*proxy)); } } #endif // Note: If you add new slots here, make sure to change // nuke() to cope. traceEdgeToTarget(trc, proxy); size_t nreserved = proxy->numReservedSlots(); for (size_t i = 0; i < nreserved; i++) { /* * The GC can use the second reserved slot to link the cross compartment * wrappers into a linked list, in which case we don't want to trace it. */ if (proxy->is<CrossCompartmentWrapperObject>() && i == CrossCompartmentWrapperObject::GrayLinkReservedSlot) { continue; } TraceEdge(trc, proxy->reservedSlotPtr(i), "proxy_reserved"); } Proxy::trace(trc, obj); }