JS_NondeterministicGetWeakMapKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret) { RootedObject obj(cx, objArg); obj = UncheckedUnwrap(obj); if (!obj || !obj->is<WeakMapObject>()) { ret.set(nullptr); return true; } RootedObject arr(cx, NewDenseEmptyArray(cx)); if (!arr) return false; ObjectValueMap* map = obj->as<WeakMapObject>().getMap(); if (map) { // Prevent GC from mutating the weakmap while iterating. AutoSuppressGC suppress(cx); for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) { JS::ExposeObjectToActiveJS(r.front().key()); RootedObject key(cx, r.front().key()); if (!cx->compartment()->wrap(cx, &key)) return false; if (!NewbornArrayPush(cx, arr, ObjectValue(*key))) return false; } } ret.set(arr); return true; }
bool WrapperFactory::WaiveXrayAndWrap(JSContext *cx, MutableHandleObject argObj) { MOZ_ASSERT(argObj); RootedObject obj(cx, js::UncheckedUnwrap(argObj)); MOZ_ASSERT(!js::IsInnerObject(obj)); if (js::IsObjectInContextCompartment(obj, cx)) { argObj.set(obj); return true; } // Even though waivers have no effect on access by scopes that don't subsume // the underlying object, good defense-in-depth dictates that we should avoid // handing out waivers to callers that can't use them. The transitive waiving // machinery unconditionally calls WaiveXrayAndWrap on return values from // waived functions, even though the return value might be not be same-origin // with the function. So if we find ourselves trying to create a waiver for // |cx|, we should check whether the caller has any business with waivers // to things in |obj|'s compartment. JSCompartment *target = js::GetContextCompartment(cx); JSCompartment *origin = js::GetObjectCompartment(obj); obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj; if (!obj) return false; if (!JS_WrapObject(cx, &obj)) return false; argObj.set(obj); return true; }
static bool strictargs_resolve(JSContext *cx, HandleObject obj, HandleId id, MutableHandleObject objp) { objp.set(nullptr); Rooted<StrictArgumentsObject*> argsobj(cx, &obj->as<StrictArgumentsObject>()); unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; PropertyOp getter = StrictArgGetter; StrictPropertyOp setter = StrictArgSetter; if (JSID_IS_INT(id)) { uint32_t arg = uint32_t(JSID_TO_INT(id)); if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) return true; attrs |= JSPROP_ENUMERATE; } else if (JSID_IS_ATOM(id, cx->names().length)) { if (argsobj->hasOverriddenLength()) return true; } else { if (!JSID_IS_ATOM(id, cx->names().callee) && !JSID_IS_ATOM(id, cx->names().caller)) return true; attrs = JSPROP_PERMANENT | JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED; getter = CastAsPropertyOp(argsobj->global().getThrowTypeError()); setter = CastAsStrictPropertyOp(argsobj->global().getThrowTypeError()); } if (!baseops::DefineGeneric(cx, argsobj, id, UndefinedHandleValue, getter, setter, attrs)) return false; objp.set(argsobj); return true; }
static JSBool args_resolve(JSContext *cx, HandleObject obj, HandleId id, unsigned flags, MutableHandleObject objp) { objp.set(NULL); Rooted<NormalArgumentsObject*> argsobj(cx, &obj->as<NormalArgumentsObject>()); unsigned attrs = JSPROP_SHARED | JSPROP_SHADOWABLE; if (JSID_IS_INT(id)) { uint32_t arg = uint32_t(JSID_TO_INT(id)); if (arg >= argsobj->initialLength() || argsobj->isElementDeleted(arg)) return true; attrs |= JSPROP_ENUMERATE; } else if (JSID_IS_ATOM(id, cx->names().length)) { if (argsobj->hasOverriddenLength()) return true; } else { if (!JSID_IS_ATOM(id, cx->names().callee)) return true; if (argsobj->callee().isMagic(JS_OVERWRITTEN_CALLEE)) return true; } RootedValue undef(cx, UndefinedValue()); if (!baseops::DefineGeneric(cx, argsobj, id, undef, ArgGetter, ArgSetter, attrs)) return JS_FALSE; objp.set(argsobj); return true; }
static bool WrapForSameCompartment(JSContext *cx, MutableHandleObject obj, const JSWrapObjectCallbacks *cb) { JS_ASSERT(cx->compartment() == obj->compartment()); if (!cb->sameCompartmentWrap) return true; RootedObject wrapped(cx, cb->sameCompartmentWrap(cx, obj)); if (!wrapped) return false; obj.set(wrapped); return true; }
bool JSCompartment::getNonWrapperObjectForCurrentCompartment(JSContext* cx, MutableHandleObject obj) { // Ensure that we have entered a compartment. MOZ_ASSERT(cx->global()); // 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(cx->global())); MOZ_ASSERT(!cx->runtime()->isSelfHostingGlobal(&obj->global())); // The object is already in the right compartment. Normally same- // compartment returns the object itself, however, windows are always // wrapped by a proxy, so we have to check for that case here manually. if (obj->compartment() == this) { obj.set(ToWindowProxyIfWindow(obj)); return true; } // Note that if the object is same-compartment, but has been wrapped into a // different compartment, we need to unwrap it and return the bare same- // compartment object. Note again that windows are always wrapped by a // WindowProxy even when same-compartment so take care not to strip this // particular wrapper. RootedObject objectPassedToWrap(cx, obj); obj.set(UncheckedUnwrap(obj, /* stopAtWindowProxy = */ true)); if (obj->compartment() == this) { MOZ_ASSERT(!IsWindow(obj)); return true; } // Invoke the prewrap callback. The prewrap callback is responsible for // doing similar reification as above, but can account for any additional // embedder requirements. // // We're a bit worried about infinite recursion here, so we do a check - // see bug 809295. auto preWrap = cx->runtime()->wrapObjectCallbacks->preWrap; if (!CheckSystemRecursionLimit(cx)) return false; if (preWrap) { preWrap(cx, cx->global(), obj, objectPassedToWrap, obj); if (!obj) return false; } MOZ_ASSERT(!IsWindow(obj)); return true; }
static bool WrapForSameCompartment(JSContext *cx, MutableHandleObject obj) { JS_ASSERT(cx->compartment() == obj->compartment()); if (!cx->runtime()->sameCompartmentWrapObjectCallback) return true; RootedObject wrapped(cx); wrapped = cx->runtime()->sameCompartmentWrapObjectCallback(cx, obj); if (!wrapped) return false; obj.set(wrapped); return true; }
static bool CreateExportObject(JSContext* cx, Handle<WasmModuleObject*> moduleObj, const ExportMap& exportMap, const ExportVector& exports, MutableHandleObject exportObj) { MOZ_ASSERT(exportMap.exportNames.length() == exports.length()); MOZ_ASSERT(exportMap.fieldNames.length() == exportMap.fieldsToExports.length()); for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) { const char* fieldName = exportMap.fieldNames[fieldIndex].get(); if (!*fieldName) { MOZ_ASSERT(!exportObj); uint32_t exportIndex = exportMap.fieldsToExports[fieldIndex]; exportObj.set(NewExportedFunction(cx, moduleObj, exportMap, exportIndex)); if (!exportObj) return false; break; } } Rooted<ValueVector> vals(cx, ValueVector(cx)); for (size_t exportIndex = 0; exportIndex < exports.length(); exportIndex++) { JSFunction* fun = NewExportedFunction(cx, moduleObj, exportMap, exportIndex); if (!fun || !vals.append(ObjectValue(*fun))) return false; } if (!exportObj) { exportObj.set(JS_NewPlainObject(cx)); if (!exportObj) return false; } for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) { const char* fieldName = exportMap.fieldNames[fieldIndex].get(); if (!*fieldName) continue; JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); HandleValue val = vals[exportMap.fieldsToExports[fieldIndex]]; if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE)) return false; } return true; }
bool ModuleNamespaceObject::ProxyHandler::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const { protop.set(nullptr); return true; }
bool OpaqueCrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject protop) const { protop.set(nullptr); return true; }
bool JSCompartment::wrap(JSContext* cx, MutableHandleObject obj) { MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(this)); MOZ_ASSERT(cx->compartment() == this); if (!obj) return true; AutoDisableProxyCheck adpc(cx->runtime()); // Anything we're wrapping has already escaped into script, so must have // been unmarked-gray at some point in the past. MOZ_ASSERT(!ObjectIsMarkedGray(obj)); // 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 did not result in a same-compartment object, // get or create a new wrapper object in this compartment for it. if (obj->compartment() != this) { if (!getOrCreateWrapper(cx, nullptr, obj)) return false; } // Ensure that the wrapper is also exposed. ExposeObjectToActiveJS(obj); return true; }
/* * HasInstance hooks need to find an appropriate reflector in order to function * properly. There are two complexities that we need to handle: * * 1 - Cross-compartment wrappers. Chrome uses over 100 compartments, all with * system principal. The success of an instanceof check should not depend * on which compartment an object comes from. At the same time, we want to * make sure we don't unwrap important security wrappers. * CheckedUnwrap does the right thing here. * * 2 - Prototype chains. Suppose someone creates a vanilla JS object |a| and * sets its __proto__ to some WN |b|. If |b instanceof nsIFoo| returns true, * one would expect |a instanceof nsIFoo| to return true as well, since * instanceof is transitive up the prototype chain in ECMAScript. Moreover, * there's chrome code that relies on this. * * This static method handles both complexities, returning either an XPCWN, a * DOM object, or null. The object may well be cross-compartment from |cx|. */ static nsresult FindObjectForHasInstance(JSContext* cx, HandleObject objArg, MutableHandleObject target) { RootedObject obj(cx, objArg), proto(cx); while (obj && !IS_WN_REFLECTOR(obj) && !IsDOMObject(obj) && !mozilla::jsipc::IsCPOW(obj)) { if (js::IsWrapper(obj)) { obj = js::CheckedUnwrap(obj, /* stopAtWindowProxy = */ false); continue; } { JSAutoCompartment ac(cx, obj); if (!js::GetObjectProto(cx, obj, &proto)) return NS_ERROR_FAILURE; } obj = proto; } target.set(obj); return NS_OK; }
static bool proxy_LookupProperty(JSContext* cx, HandleObject obj, HandleId id, MutableHandleObject objp, MutableHandle<JS::PropertyResult> propp) { bool found; if (!Proxy::has(cx, obj, id, &found)) return false; if (found) { propp.setNonNativeProperty(); objp.set(obj); } else { propp.setNotFound(); objp.set(nullptr); } return true; }
js::GetOriginalEval(JSContext *cx, HandleObject scope, MutableHandleObject eval) { assertSameCompartment(cx, scope); if (!scope->global().getOrCreateObjectPrototype(cx)) return false; eval.set(&scope->global().getOriginalEval().toObject()); return true; }
JS_NondeterministicGetWeakSetKeys(JSContext* cx, HandleObject objArg, MutableHandleObject ret) { RootedObject obj(cx, UncheckedUnwrap(objArg)); if (!obj || !obj->is<WeakSetObject>()) { ret.set(nullptr); return true; } return WeakCollectionObject::nondeterministicGetKeys(cx, obj.as<WeakCollectionObject>(), ret); }
/* static */ bool GlobalObject::getOrCreateEval(JSContext* cx, Handle<GlobalObject*> global, MutableHandleObject eval) { if (!global->getOrCreateObjectPrototype(cx)) return false; eval.set(&global->getSlot(EVAL).toObject()); return true; }
bool XDRState<mode>::codeFunction(MutableHandleObject objp) { if (mode == XDR_DECODE) objp.set(NULL); if (!VersionCheck(this)) return false; return XDRInterpretedFunction(this, NullPtr(), NullPtr(), objp); }
bool CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, MutableHandleObject protop) { if (!wrapper->getTaggedProto().isLazy()) { protop.set(wrapper->getTaggedProto().toObjectOrNull()); return true; } { RootedObject wrapped(cx, wrappedObject(wrapper)); AutoCompartment call(cx, wrapped); if (!JSObject::getProto(cx, wrapped, protop)) return false; if (protop) protop->setDelegate(cx); } return cx->compartment->wrap(cx, protop.address()); }
bool JSCompartment::getOrCreateWrapper(JSContext* cx, HandleObject existing, MutableHandleObject obj) { // 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>()); return true; } // Ensure that the wrappee is exposed in case we are creating a new wrapper // for a gray object. ExposeObjectToActiveJS(obj); // Create a new wrapper for the object. auto wrap = cx->runtime()->wrapObjectCallbacks->wrap; RootedObject wrapper(cx, wrap(cx, existing, obj)); if (!wrapper) 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(wrapper) == &key.get().toObject()); if (!putWrapper(cx, CrossCompartmentKey(key), ObjectValue(*wrapper))) { // Enforce the invariant that all cross-compartment wrapper object are // in the map by nuking the wrapper if we couldn't add it. // Unfortunately it's possible for the wrapper to still be marked if we // took this path, for example if the object metadata callback stashes a // reference to it. if (wrapper->is<CrossCompartmentWrapperObject>()) NukeCrossCompartmentWrapper(cx, wrapper); return false; } obj.set(wrapper); 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); }
bool CrossCompartmentWrapper::getPrototype(JSContext* cx, HandleObject wrapper, MutableHandleObject protop) const { { RootedObject wrapped(cx, wrappedObject(wrapper)); AutoCompartment call(cx, wrapped); if (!GetPrototype(cx, wrapped, protop)) return false; if (protop) protop->setDelegate(cx); } return cx->compartment()->wrap(cx, protop); }
js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScript scriptArg, MutableHandleObject scopeArg) { CHECK_REQUEST(cx); assertSameCompartment(cx, global); MOZ_ASSERT(global->is<GlobalObject>()); MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope()); RootedScript script(cx, scriptArg); Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>()); if (script->compartment() != cx->compartment()) { Rooted<StaticScope*> staticScope(cx, &globalRoot->lexicalScope().staticBlock()); staticScope = StaticNonSyntacticScope::create(cx, staticScope); if (!staticScope) return false; script = CloneGlobalScript(cx, staticScope, script); if (!script) return false; Debugger::onNewScript(cx, script); } Rooted<ClonedBlockObject*> globalLexical(cx, &globalRoot->lexicalScope()); Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalLexical)); if (!scope) return false; // Unlike the non-syntactic scope chain API used by the subscript loader, // this API creates a fresh block scope each time. Rooted<StaticNonSyntacticScope*> enclosingStaticScope(cx, &script->enclosingStaticScope()->as<StaticNonSyntacticScope>()); scope = ClonedBlockObject::createNonSyntactic(cx, enclosingStaticScope, scope); if (!scope) return false; RootedValue rval(cx); if (!ExecuteKernel(cx, script, *scope, UndefinedValue(), NullFramePtr() /* evalInFrame */, rval.address())) { return false; } scopeArg.set(scope); return true; }
static bool CreateInstance(JSContext* cx, HandleObject exportObj, MutableHandleObject instance) { instance.set(JS_NewPlainObject(cx)); if (!instance) return false; JSAtom* atom = Atomize(cx, ExportField, strlen(ExportField)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); RootedValue val(cx, ObjectValue(*exportObj)); if (!JS_DefinePropertyById(cx, instance, id, val, JSPROP_ENUMERATE)) return false; return true; }
// ES6 (14 October, 2014) 9.5.11 Proxy.[[Enumerate]] bool ScriptedDirectProxyHandler::enumerate(JSContext* cx, HandleObject proxy, MutableHandleObject objp) const { // step 1 RootedObject handler(cx, GetDirectProxyHandlerObject(proxy)); // step 2 if (!handler) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } // step 3: unnecessary assert // step 4 RootedObject target(cx, proxy->as<ProxyObject>().target()); // step 5-6 RootedValue trap(cx); if (!GetProperty(cx, handler, handler, cx->names().enumerate, &trap)) return false; // step 7 if (trap.isUndefined()) return GetIterator(cx, target, 0, objp); // step 8-9 Value argv[] = { ObjectOrNullValue(target) }; RootedValue trapResult(cx); if (!Invoke(cx, ObjectValue(*handler), trap, ArrayLength(argv), argv, &trapResult)) return false; // step 10 if (trapResult.isPrimitive()) { ReportInvalidTrapResult(cx, proxy, cx->names().enumerate); return false; } // step 11 objp.set(&trapResult.toObject()); return true; }
bool WrapperOwner::getPrototype(JSContext* cx, HandleObject proxy, MutableHandleObject objp) { ObjectId objId = idOf(proxy); ObjectOrNullVariant val; ReturnStatus status; if (!SendGetPrototype(objId, &status, &val)) return ipcfail(cx); LOG_STACK(); if (!ok(cx, status)) return false; objp.set(fromObjectOrNullVariant(cx, val)); return true; }
static bool FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj, MutableHandleObject result) { // Walk up the prototype chain until we find an error object instance or // prototype object. This allows code like: // Object.create(Error.prototype).stack // or // function NYI() { } // NYI.prototype = new Error; // (new NYI).stack // to continue returning stacks that are useless, but at least don't throw. RootedObject target(cx, CheckedUnwrap(obj)); if (!target) { ReportAccessDenied(cx); return false; } RootedObject proto(cx); while (!IsErrorProtoKey(StandardProtoKeyOrNull(target))) { if (!GetPrototype(cx, target, &proto)) return false; if (!proto) { // We walked the whole prototype chain and did not find an Error // object. JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, js_Error_str, "(get stack)", obj->getClass()->name); return false; } target = CheckedUnwrap(proto); if (!target) { ReportAccessDenied(cx); return false; } } result.set(target); return true; }
js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScript scriptArg, MutableHandleObject scopeArg) { CHECK_REQUEST(cx); assertSameCompartment(cx, global); MOZ_ASSERT(global->is<GlobalObject>()); RootedScript script(cx, scriptArg); if (script->compartment() != cx->compartment()) { script = CloneScript(cx, NullPtr(), NullPtr(), script); if (!script) return false; Debugger::onNewScript(cx, script); } RootedObject scope(cx, JS_NewPlainObject(cx)); if (!scope) return false; if (!scope->setQualifiedVarObj(cx)) return false; if (!scope->setUnqualifiedVarObj(cx)) return false; JSObject* thisobj = GetThisObject(cx, global); if (!thisobj) return false; RootedValue thisv(cx, ObjectValue(*thisobj)); RootedValue rval(cx); if (!ExecuteKernel(cx, script, *scope, thisv, EXECUTE_GLOBAL, NullFramePtr() /* evalInFrame */, rval.address())) { return false; } scopeArg.set(scope); return true; }
bool CrossCompartmentWrapper::getPrototypeIfOrdinary(JSContext* cx, HandleObject wrapper, bool* isOrdinary, MutableHandleObject protop) const { { RootedObject wrapped(cx, wrappedObject(wrapper)); AutoCompartment call(cx, wrapped); if (!GetPrototypeIfOrdinary(cx, wrapped, isOrdinary, protop)) return false; if (!*isOrdinary) return true; if (protop) { if (!protop->setDelegate(cx)) return false; } } return cx->compartment()->wrap(cx, protop); }
js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScript scriptArg, MutableHandleObject scopeArg) { CHECK_REQUEST(cx); assertSameCompartment(cx, global); MOZ_ASSERT(global->is<GlobalObject>()); MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope()); RootedScript script(cx, scriptArg); if (script->compartment() != cx->compartment()) { Rooted<ScopeObject*> staticScope(cx, StaticNonSyntacticScopeObjects::create(cx, nullptr)); if (!staticScope) return false; script = CloneGlobalScript(cx, staticScope, script); if (!script) return false; Debugger::onNewScript(cx, script); } Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>()); Rooted<ScopeObject*> scope(cx, NonSyntacticVariablesObject::create(cx, globalRoot)); if (!scope) return false; JSObject* thisobj = GetThisObject(cx, global); if (!thisobj) return false; RootedValue thisv(cx, ObjectValue(*thisobj)); RootedValue rval(cx); if (!ExecuteKernel(cx, script, *scope, thisv, UndefinedValue(), EXECUTE_GLOBAL, NullFramePtr() /* evalInFrame */, rval.address())) { return false; } scopeArg.set(scope); return true; }
bool WeakCollectionObject::nondeterministicGetKeys(JSContext* cx, Handle<WeakCollectionObject*> obj, MutableHandleObject ret) { RootedObject arr(cx, NewDenseEmptyArray(cx)); if (!arr) return false; if (ObjectValueMap* map = obj->getMap()) { // Prevent GC from mutating the weakmap while iterating. AutoSuppressGC suppress(cx); for (ObjectValueMap::Base::Range r = map->all(); !r.empty(); r.popFront()) { JS::ExposeObjectToActiveJS(r.front().key()); RootedObject key(cx, r.front().key()); if (!cx->compartment()->wrap(cx, &key)) return false; if (!NewbornArrayPush(cx, arr, ObjectValue(*key))) return false; } } ret.set(arr); return true; }