bool JSCompartment::wrap(JSContext *cx, MutableHandle<PropDesc> desc) { if (desc.isUndefined()) return true; JSCompartment *comp = cx->compartment(); if (desc.hasValue()) { RootedValue value(cx, desc.value()); if (!comp->wrap(cx, &value)) return false; desc.setValue(value); } if (desc.hasGet()) { RootedValue get(cx, desc.getterValue()); if (!comp->wrap(cx, &get)) return false; desc.setGetter(get); } if (desc.hasSet()) { RootedValue set(cx, desc.setterValue()); if (!comp->wrap(cx, &set)) return false; desc.setSetter(set); } return true; }
/* * Rewrap *idp and the fields of *desc for the current compartment. Also: * defining a property on a proxy requires pd_ to contain a descriptor object, * so reconstitute desc->pd_ if needed. */ bool PropDesc::wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId, PropDesc *desc) const { MOZ_ASSERT(!isUndefined()); JSCompartment *comp = cx->compartment; *wrappedId = id; if (!comp->wrapId(cx, wrappedId)) return false; *desc = *this; RootedValue value(cx, desc->value_); RootedValue get(cx, desc->get_); RootedValue set(cx, desc->set_); if (!comp->wrap(cx, &value) || !comp->wrap(cx, &get) || !comp->wrap(cx, &set)) return false; desc->value_ = value; desc->get_ = get; desc->set_ = set; return !obj->isProxy() || desc->makeObject(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 js::RemapWrapper(JSContext *cx, JSObject *wobjArg, JSObject *newTargetArg) { RootedObject wobj(cx, wobjArg); RootedObject newTarget(cx, newTargetArg); JS_ASSERT(IsCrossCompartmentWrapper(wobj)); JS_ASSERT(!IsCrossCompartmentWrapper(newTarget)); JSObject *origTarget = Wrapper::wrappedObject(wobj); JS_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. 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. 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. 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.address(), 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. JS_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). JS_ASSERT(wobj->isWrapper()); wcompartment->putWrapper(ObjectValue(*newTarget), ObjectValue(*wobj)); return true; }
/* * Rewrap *idp and the fields of *desc for the current compartment. Also: * defining a property on a proxy requires pd_ to contain a descriptor object, * so reconstitute desc->pd_ if needed. */ bool PropDesc::wrapInto(JSContext *cx, JSObject *obj, const jsid &id, jsid *wrappedId, PropDesc *desc) const { MOZ_ASSERT(!isUndefined()); JSCompartment *comp = cx->compartment; *wrappedId = id; if (!comp->wrapId(cx, wrappedId)) return false; *desc = *this; if (!comp->wrap(cx, &desc->value_)) return false; if (!comp->wrap(cx, &desc->get_)) return false; if (!comp->wrap(cx, &desc->set_)) return false; return !obj->isProxy() || desc->makeObject(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 js::RemapWrapper(JSContext *cx, JSObject *wobj, JSObject *newTarget) { JS_ASSERT(IsCrossCompartmentWrapper(wobj)); JS_ASSERT(!IsCrossCompartmentWrapper(newTarget)); JSObject *origTarget = Wrapper::wrappedObject(wobj); 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))); // The old value should still be in the cross-compartment wrapper map, and // the lookup should return wobj. JS_ASSERT(&pmap.lookup(origv)->value.toObject() == wobj); pmap.remove(origv); // When we remove origv from the wrapper map, its wrapper, wobj, must // immediately cease to be a cross-compartment wrapper. Neuter it. NukeCrossCompartmentWrapper(wobj); // First, we wrap it in the new compartment. This will return // a new wrapper. AutoCompartment ac(cx, wobj); JSObject *tobj = newTarget; if (!ac.enter() || !wcompartment->wrap(cx, &tobj)) return false; // Now, because we need to maintain object identity, we do a // brain transplant on the old object. At the same time, we // update the entry in the compartment's wrapper map to point // to the old wrapper. JS_ASSERT(tobj != wobj); if (!wobj->swap(cx, tobj)) return false; // Before swapping, this wrapper came out of wrap(), which enforces the // invariant that the wrapper in the map points directly to the key. JS_ASSERT(Wrapper::wrappedObject(wobj) == newTarget); pmap.put(ObjectValue(*newTarget), ObjectValue(*wobj)); return true; }