static bool CloneValue(JSContext *cx, MutableHandleValue vp, CloneMemory &clonedObjects) { if (vp.isObject()) { RootedObject obj(cx, &vp.toObject()); RootedObject clone(cx, CloneObject(cx, obj, clonedObjects)); if (!clone) return false; vp.setObject(*clone); } else if (vp.isBoolean() || vp.isNumber() || vp.isNullOrUndefined()) { // Nothing to do here: these are represented inline in the value } else if (vp.isString()) { Rooted<JSStableString*> str(cx, vp.toString()->ensureStable(cx)); if (!str) return false; RootedString clone(cx, js_NewStringCopyN<CanGC>(cx, str->chars().get(), str->length())); if (!clone) return false; vp.setString(clone); } else { MOZ_ASSUME_UNREACHABLE("Self-hosting CloneValue can't clone given value."); } 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); }