static MOZ_ALWAYS_INLINE bool SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj, HandleObject key, HandleValue value) { ObjectValueMap* map = mapObj->getMap(); if (!map) { auto newMap = cx->make_unique<ObjectValueMap>(cx, mapObj.get()); if (!newMap) return false; if (!newMap->init()) { JS_ReportOutOfMemory(cx); return false; } map = newMap.release(); mapObj->setPrivate(map); } // Preserve wrapped native keys to prevent wrapper optimization. if (!TryPreserveReflector(cx, key)) return false; if (JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp()) { RootedObject delegate(cx, op(key)); if (delegate && !TryPreserveReflector(cx, delegate)) return false; } MOZ_ASSERT(key->compartment() == mapObj->compartment()); MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment()); if (!map->put(key, value)) { JS_ReportOutOfMemory(cx); return false; } return true; }
static bool WrapForSameCompartment(JSContext *cx, HandleObject obj, MutableHandleValue vp) { JS_ASSERT(cx->compartment == obj->compartment()); if (!cx->runtime->sameCompartmentWrapObjectCallback) { vp.setObject(*obj); return true; } JSObject *wrapped = cx->runtime->sameCompartmentWrapObjectCallback(cx, obj); if (!wrapped) return false; vp.setObject(*wrapped); return true; }
bool JSCompartment::rewrap(JSContext* cx, MutableHandleObject obj, HandleObject existingArg) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); MOZ_ASSERT(obj); MOZ_ASSERT(existingArg); MOZ_ASSERT(existingArg->compartment() == cx->compartment()); MOZ_ASSERT(IsDeadProxyObject(existingArg)); AutoDisableProxyCheck adpc(cx->runtime()); // It may not be possible to re-use existing; if so, clear it so that we // are forced to create a new wrapper. Note that this cannot call out to // |wrap| because of the different gray unmarking semantics. RootedObject existing(cx, existingArg); if (existing->hasStaticPrototype() || // Note: Class asserted above, so all that's left to check is callability existing->isCallable() || obj->isCallable()) { existing.set(nullptr); } // The passed object may already be wrapped, or may fit a number of special // cases that we need to check for and manually correct. if (!getNonWrapperObjectForCurrentCompartment(cx, obj)) return false; // If the reification above resulted in a same-compartment object, we do // not need to create or return an existing wrapper. if (obj->compartment() == this) return true; return getOrCreateWrapper(cx, existing, obj); }
MOZ_ALWAYS_INLINE bool SetWeakMapEntryInternal(JSContext *cx, Handle<WeakMapObject*> mapObj, HandleObject key, HandleValue value) { ObjectValueMap *map = mapObj->getMap(); if (!map) { map = cx->new_<ObjectValueMap>(cx, mapObj.get()); if (!map) return false; if (!map->init()) { js_delete(map); JS_ReportOutOfMemory(cx); return false; } mapObj->setPrivate(map); } // Preserve wrapped native keys to prevent wrapper optimization. if (!TryPreserveReflector(cx, key)) return false; if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) { RootedObject delegate(cx, op(key)); if (delegate && !TryPreserveReflector(cx, delegate)) return false; } JS_ASSERT(key->compartment() == mapObj->compartment()); JS_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment()); if (!map->put(key, value)) { JS_ReportOutOfMemory(cx); return false; } WeakMapPostWriteBarrier(cx->runtime(), map, key.get()); return true; }
bool JSCompartment::wrap(JSContext *cx, MutableHandleValue vp, HandleObject existingArg) { JS_ASSERT(cx->compartment == this); JS_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment); JS_ASSERT_IF(existingArg, vp.isObject()); JS_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); unsigned flags = 0; JS_CHECK_CHROME_RECURSION(cx, return false); #ifdef DEBUG struct AutoDisableProxyCheck { JSRuntime *runtime; AutoDisableProxyCheck(JSRuntime *rt) : runtime(rt) { runtime->gcDisableStrictProxyCheckingCount++; } ~AutoDisableProxyCheck() { runtime->gcDisableStrictProxyCheckingCount--; } } adpc(rt); #endif /* Only GC things have to be wrapped or copied. */ if (!vp.isMarkable()) return true; if (vp.isString()) { RawString str = vp.toString(); /* If the string is already in this compartment, we are done. */ if (str->zone() == zone()) return true; /* If the string is an atom, we don't have to copy. */ if (str->isAtom()) { JS_ASSERT(str->zone() == cx->runtime->atomsCompartment->zone()); return true; } } /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a NULL * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ HandleObject global = cx->global(); /* Unwrap incoming objects. */ if (vp.isObject()) { RootedObject obj(cx, &vp.toObject()); if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, vp); /* Translate StopIteration singleton. */ if (obj->isStopIteration()) return js_FindClassObject(cx, JSProto_StopIteration, vp); /* Unwrap the object, but don't unwrap outer windows. */ obj = UnwrapObject(obj, /* stopAtOuter = */ true, &flags); if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, vp); if (cx->runtime->preWrapObjectCallback) { obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags); if (!obj) return false; } if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, vp); vp.setObject(*obj); #ifdef DEBUG { JSObject *outer = GetOuterObject(cx, obj); JS_ASSERT(outer && outer == obj); } #endif } RootedValue key(cx, vp); /* If we already have a wrapper for this value, use it. */ if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) { vp.set(p->value); if (vp.isObject()) { RawObject obj = &vp.toObject(); JS_ASSERT(obj->isCrossCompartmentWrapper()); JS_ASSERT(obj->getParent() == global); } return true; } if (vp.isString()) { Rooted<JSLinearString *> str(cx, vp.toString()->ensureLinear(cx)); if (!str) return false; UnrootedString wrapped = js_NewStringCopyN<CanGC>(cx, str->chars(), str->length()); if (!wrapped) return false; vp.setString(wrapped); if (!putWrapper(key, vp)) return false; if (str->zone()->isGCMarking()) { /* * All string wrappers are dropped when collection starts, but we * just created a new one. Mark the wrapped string to stop it being * finalized, because if it was then the pointer in this * compartment's wrapper map would be left dangling. */ JSString *tmp = str; MarkStringUnbarriered(&rt->gcMarker, &tmp, "wrapped string"); JS_ASSERT(tmp == str); } return true; } RootedObject proto(cx, Proxy::LazyProto); RootedObject obj(cx, &vp.toObject()); RootedObject existing(cx, existingArg); if (existing) { /* Is it possible to reuse |existing|? */ if (!existing->getTaggedProto().isLazy() || existing->getClass() != &ObjectProxyClass || existing->getParent() != global || obj->isCallable()) { existing = NULL; } } /* * We hand in the original wrapped object into the wrap hook to allow * the wrap hook to reason over what wrappers are currently applied * to the object. */ RootedObject wrapper(cx); wrapper = cx->runtime->wrapObjectCallback(cx, existing, obj, proto, global, flags); if (!wrapper) return false; // We maintain the invariant that the key in the cross-compartment wrapper // map is always directly wrapped by the value. JS_ASSERT(Wrapper::wrappedObject(wrapper) == &key.get().toObject()); vp.setObject(*wrapper); return putWrapper(key, vp); }
bool JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existingArg) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); MOZ_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment()); MOZ_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); if (!obj) return true; AutoDisableProxyCheck adpc(cx->runtime()); // Wrappers should really be parented to the wrapped parent of the wrapped // object, but in that case a wrapped global object would have a nullptr // parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, // we parent all wrappers to the global object in their home compartment. // This loses us some transparency, and is generally very cheesy. HandleObject global = cx->global(); RootedObject objGlobal(cx, &obj->global()); MOZ_ASSERT(global); MOZ_ASSERT(objGlobal); const JSWrapObjectCallbacks *cb = cx->runtime()->wrapObjectCallbacks; if (obj->compartment() == this) { obj.set(GetOuterObject(cx, obj)); return true; } // If we have a cross-compartment wrapper, make sure that the cx isn't // associated with the self-hosting global. We don't want to create // wrappers for objects in other runtimes, which may be the case for the // self-hosting global. MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(global) && !cx->runtime()->isSelfHostingGlobal(objGlobal)); // Unwrap the object, but don't unwrap outer windows. RootedObject objectPassedToWrap(cx, obj); obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true)); if (obj->compartment() == this) { MOZ_ASSERT(obj == GetOuterObject(cx, obj)); return true; } // Translate StopIteration singleton. if (obj->is<StopIterationObject>()) { // StopIteration isn't a constructor, but it's stored in GlobalObject // as one, out of laziness. Hence the GetBuiltinConstructor call here. RootedObject stopIteration(cx); if (!GetBuiltinConstructor(cx, JSProto_StopIteration, &stopIteration)) return false; obj.set(stopIteration); return true; } // Invoke the prewrap callback. We're a bit worried about infinite // recursion here, so we do a check - see bug 809295. JS_CHECK_SYSTEM_RECURSION(cx, return false); if (cb->preWrap) { obj.set(cb->preWrap(cx, global, obj, objectPassedToWrap)); if (!obj) return false; } MOZ_ASSERT(obj == GetOuterObject(cx, obj)); if (obj->compartment() == this) return true; // If we already have a wrapper for this value, use it. RootedValue key(cx, ObjectValue(*obj)); if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(CrossCompartmentKey(key))) { obj.set(&p->value().get().toObject()); MOZ_ASSERT(obj->is<CrossCompartmentWrapperObject>()); MOZ_ASSERT(obj->getParent() == global); return true; } RootedObject existing(cx, existingArg); if (existing) { // Is it possible to reuse |existing|? if (!existing->getTaggedProto().isLazy() || // Note: Class asserted above, so all that's left to check is callability existing->isCallable() || existing->getParent() != global || obj->isCallable()) { existing = nullptr; } } obj.set(cb->wrap(cx, existing, obj, global)); if (!obj) return false; // We maintain the invariant that the key in the cross-compartment wrapper // map is always directly wrapped by the value. MOZ_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject()); return putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*obj)); }
bool JSCompartment::wrap(JSContext *cx, MutableHandleObject obj, HandleObject existingArg) { JS_ASSERT(!cx->runtime()->isAtomsCompartment(this)); JS_ASSERT(cx->compartment() == this); JS_ASSERT_IF(existingArg, existingArg->compartment() == cx->compartment()); JS_ASSERT_IF(existingArg, IsDeadProxyObject(existingArg)); if (!obj) return true; AutoDisableProxyCheck adpc(cx->runtime()); /* * Wrappers should really be parented to the wrapped parent of the wrapped * object, but in that case a wrapped global object would have a nullptr * parent without being a proper global object (JSCLASS_IS_GLOBAL). Instead, * we parent all wrappers to the global object in their home compartment. * This loses us some transparency, and is generally very cheesy. */ HandleObject global = cx->global(); RootedObject objGlobal(cx, &obj->global()); JS_ASSERT(global); JS_ASSERT(objGlobal); const JSWrapObjectCallbacks *cb; if (cx->runtime()->isSelfHostingGlobal(global) || cx->runtime()->isSelfHostingGlobal(objGlobal)) cb = &SelfHostingWrapObjectCallbacks; else cb = cx->runtime()->wrapObjectCallbacks; if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, cb); /* Unwrap the object, but don't unwrap outer windows. */ unsigned flags = 0; obj.set(UncheckedUnwrap(obj, /* stopAtOuter = */ true, &flags)); if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, cb); /* Translate StopIteration singleton. */ if (obj->is<StopIterationObject>()) { RootedValue v(cx); if (!js_FindClassObject(cx, JSProto_StopIteration, &v)) return false; obj.set(&v.toObject()); return true; } /* Invoke the prewrap callback. We're a bit worried about infinite * recursion here, so we do a check - see bug 809295. */ JS_CHECK_CHROME_RECURSION(cx, return false); if (cb->preWrap) { obj.set(cb->preWrap(cx, global, obj, flags)); if (!obj) return false; } if (obj->compartment() == this) return WrapForSameCompartment(cx, obj, cb); #ifdef DEBUG { JSObject *outer = GetOuterObject(cx, obj); JS_ASSERT(outer && outer == obj); } #endif /* If we already have a wrapper for this value, use it. */ RootedValue key(cx, ObjectValue(*obj)); if (WrapperMap::Ptr p = crossCompartmentWrappers.lookup(key)) { obj.set(&p->value().get().toObject()); JS_ASSERT(obj->is<CrossCompartmentWrapperObject>()); JS_ASSERT(obj->getParent() == global); return true; } RootedObject proto(cx, TaggedProto::LazyProto); RootedObject existing(cx, existingArg); if (existing) { /* Is it possible to reuse |existing|? */ if (!existing->getTaggedProto().isLazy() || // Note: don't use is<ObjectProxyObject>() here -- it also matches subclasses! existing->getClass() != &ProxyObject::uncallableClass_ || existing->getParent() != global || obj->isCallable()) { existing = nullptr; } } obj.set(cb->wrap(cx, existing, obj, proto, global, flags)); if (!obj) return false; /* * We maintain the invariant that the key in the cross-compartment wrapper * map is always directly wrapped by the value. */ JS_ASSERT(Wrapper::wrappedObject(obj) == &key.get().toObject()); return putWrapper(key, ObjectValue(*obj)); }