// Like GetWrappedObject, but works on other types of wrappers, too. // See also js_GetWrappedObject in jsobj.h. // TODO Move to XPCWrapper? static inline JSObject * GetWrappedJSObject(JSContext *cx, JSObject *obj) { JSClass *clasp = STOBJ_GET_CLASS(obj); if (!(clasp->flags & JSCLASS_IS_EXTENDED)) { return obj; } JSExtendedClass *xclasp = (JSExtendedClass *)clasp; if (!xclasp->wrappedObject) { if (XPCNativeWrapper::IsNativeWrapper(obj)) { XPCWrappedNative *wn = XPCNativeWrapper::SafeGetWrappedNative(obj); return wn ? wn->GetFlatJSObject() : nsnull; } return obj; } return xclasp->wrappedObject(cx, obj); }
JSBool unwrapAnyObject(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSObject *wrapped; JSObject *wrappee = NULL; if (!JS_ConvertArguments(cx, argc, argv, "o", &wrapped)) return JS_FALSE; JSClass *klass = JS_GetClass(cx, wrapped); if (klass && (klass->flags & JSCLASS_IS_EXTENDED)) { JSExtendedClass *eClass = (JSExtendedClass *) klass; if (eClass->wrappedObject != NULL) wrappee = eClass->wrappedObject(cx, wrapped); } if (wrappee) *rval = OBJECT_TO_JSVAL(wrappee); else *rval = JSVAL_NULL; return JS_TRUE; }
static JSBool CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) { JSObject *obj, *origobj; jsval state; JSBool foreach; jsid id; JSObject *obj2; JSBool cond; JSClass *clasp; JSExtendedClass *xclasp; JSProperty *prop; JSString *str; JS_ASSERT(flags & JSITER_ENUMERATE); JS_ASSERT(iterobj->getClass() == &js_IteratorClass); obj = iterobj->getParent(); origobj = iterobj->getProto(); state = iterobj->getSlot(JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) goto stop; foreach = (flags & JSITER_FOREACH) != 0; #if JS_HAS_XML_SUPPORT /* * Treat an XML object specially only when it starts the prototype chain. * Otherwise we need to do the usual deleted and shadowed property checks. */ if (obj == origobj && OBJECT_IS_XML(cx, obj)) { if (foreach) { if (!js_EnumerateXMLValues(cx, obj, JSENUMERATE_NEXT, &state, &id, rval)) { return JS_FALSE; } } else { if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; } iterobj->setSlot(JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) goto stop; } else #endif { restart: if (!obj->enumerate(cx, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; iterobj->setSlot(JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) { #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { /* * We just finished enumerating an XML obj that is present on * the prototype chain of a non-XML origobj. Stop further * prototype chain searches because XML objects don't * enumerate prototypes. */ JS_ASSERT(origobj != obj); JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); } else #endif { obj = obj->getProto(); if (obj) { iterobj->setParent(obj); if (!obj->enumerate(cx, JSENUMERATE_INIT, &state, NULL)) return JS_FALSE; iterobj->setSlot(JSSLOT_ITER_STATE, state); if (!JSVAL_IS_NULL(state)) goto restart; } } goto stop; } /* Skip properties not in obj when looking from origobj. */ if (!origobj->lookupProperty(cx, id, &obj2, &prop)) return JS_FALSE; if (!prop) goto restart; obj2->dropProperty(cx, prop); /* * If the id was found in a prototype object or an unrelated object * (specifically, not in an inner object for obj), skip it. This step * means that all lookupProperty implementations must return an * object further along on the prototype chain, or else possibly an * object returned by the JSExtendedClass.outerObject optional hook. */ if (obj != obj2) { cond = JS_FALSE; clasp = OBJ_GET_CLASS(cx, obj2); if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; cond = xclasp->outerObject && xclasp->outerObject(cx, obj2) == obj; } if (!cond) goto restart; } if (foreach) { /* Get property querying the original object. */ if (!origobj->getProperty(cx, id, rval)) return JS_FALSE; } } if (foreach) { if (flags & JSITER_KEYVALUE) { if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } } else { /* Make rval a string for uniformity and compatibility. */ str = js_ValueToString(cx, ID_TO_VALUE(id)); if (!str) return JS_FALSE; *rval = STRING_TO_JSVAL(str); } return JS_TRUE; stop: JS_ASSERT(iterobj->getSlot(JSSLOT_ITER_STATE) == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; }
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) { JSObject *obj; JSAtom *atom; JSClass *clasp; JSExtendedClass *xclasp; JSObject *iterobj; jsval arg; JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE))); /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); AutoValueRooter tvr(cx); /* XXX work around old valueOf call hidden beneath js_ValueToObject */ if (!JSVAL_IS_PRIMITIVE(*vp)) { obj = JSVAL_TO_OBJECT(*vp); } else { /* * Enumerating over null and undefined gives an empty enumerator. * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of * the first production in 12.6.4 and step 4 of the second production, * but it's "web JS" compatible. */ if ((flags & JSITER_ENUMERATE)) { if (!js_ValueToObject(cx, *vp, &obj)) return false; if (!obj) goto default_iter; } else { obj = js_ValueToNonNullObject(cx, *vp); if (!obj) return false; } } tvr.setObject(obj); clasp = OBJ_GET_CLASS(cx, obj); if ((clasp->flags & JSCLASS_IS_EXTENDED) && (xclasp = (JSExtendedClass *) clasp)->iteratorObject) { iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH)); if (!iterobj) return false; *vp = OBJECT_TO_JSVAL(iterobj); } else { atom = cx->runtime->atomState.iteratorAtom; if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp)) return false; if (JSVAL_IS_VOID(*vp)) { default_iter: /* * Fail over to the default enumerating native iterator. * * Create iterobj with a NULL parent to ensure that we use the * correct scope chain to lookup the iterator's constructor. Since * we use the parent slot to keep track of the iterable, we must * fix it up after. */ iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); if (!iterobj) return false; /* Store in *vp to protect it from GC (callers must root vp). */ *vp = OBJECT_TO_JSVAL(iterobj); if (!InitNativeIterator(cx, iterobj, obj, flags)) return false; } else { LeaveTrace(cx); arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) return false; if (JSVAL_IS_PRIMITIVE(*vp)) { js_ReportValueError(cx, JSMSG_BAD_ITERATOR_RETURN, JSDVG_SEARCH_STACK, *vp, NULL); return false; } } } return true; }
JSBool XPC_XOW_WrapObject(JSContext *cx, JSObject *parent, jsval *vp, XPCWrappedNative* wn) { NS_ASSERTION(XPCPerThreadData::IsMainThread(cx), "Can't do this off the main thread!"); // Our argument should be a wrapped native object, but the caller may have // passed it in as an optimization. JSObject *wrappedObj; if (!JSVAL_IS_OBJECT(*vp) || !(wrappedObj = JSVAL_TO_OBJECT(*vp)) || STOBJ_GET_CLASS(wrappedObj) == &sXPC_XOW_JSClass.base) { return JS_TRUE; } if (!wn && !(wn = XPCWrappedNative::GetAndMorphWrappedNativeOfJSObject(cx, wrappedObj))) { return JS_TRUE; } XPCJSRuntime *rt = nsXPConnect::GetRuntimeInstance(); // The parent must be the inner global object for its scope. parent = JS_GetGlobalForObject(cx, parent); JSClass *clasp = STOBJ_GET_CLASS(parent); if (clasp->flags & JSCLASS_IS_EXTENDED) { JSExtendedClass *xclasp = reinterpret_cast<JSExtendedClass *>(clasp); if (xclasp->innerObject) { parent = xclasp->innerObject(cx, parent); if (!parent) { return JS_FALSE; } } } XPCWrappedNativeScope *parentScope = XPCWrappedNativeScope::FindInJSObjectScope(cx, parent, nsnull, rt); #ifdef DEBUG_mrbkap_off printf("Wrapping object at %p (%s) [%p]\n", (void *)wrappedObj, STOBJ_GET_CLASS(wrappedObj)->name, (void *)parentScope); #endif JSObject *outerObj = nsnull; WrappedNative2WrapperMap *map = parentScope->GetWrapperMap(); outerObj = map->Find(wrappedObj); if (outerObj) { NS_ASSERTION(STOBJ_GET_CLASS(outerObj) == &sXPC_XOW_JSClass.base, "What crazy object are we getting here?"); #ifdef DEBUG_mrbkap_off printf("But found a wrapper in the map %p!\n", (void *)outerObj); #endif *vp = OBJECT_TO_JSVAL(outerObj); return JS_TRUE; } outerObj = JS_NewObjectWithGivenProto(cx, &sXPC_XOW_JSClass.base, nsnull, parent); if (!outerObj) { return JS_FALSE; } if (!JS_SetReservedSlot(cx, outerObj, XPCWrapper::sWrappedObjSlot, *vp) || !JS_SetReservedSlot(cx, outerObj, XPCWrapper::sFlagsSlot, JSVAL_ZERO) || !JS_SetReservedSlot(cx, outerObj, XPC_XOW_ScopeSlot, PRIVATE_TO_JSVAL(parentScope))) { return JS_FALSE; } *vp = OBJECT_TO_JSVAL(outerObj); map->Add(wn->GetScope()->GetWrapperMap(), wrappedObj, outerObj); return JS_TRUE; }
js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) { JSObject *obj; JSTempValueRooter tvr; JSAtom *atom; JSClass *clasp; JSExtendedClass *xclasp; JSBool ok; JSObject *iterobj; jsval arg; JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE))); /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); /* XXX work around old valueOf call hidden beneath js_ValueToObject */ if (!JSVAL_IS_PRIMITIVE(*vp)) { obj = JSVAL_TO_OBJECT(*vp); } else { /* * Enumerating over null and undefined gives an empty enumerator. * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of * the first production in 12.6.4 and step 4 of the second production, * but it's "web JS" compatible. */ if ((flags & JSITER_ENUMERATE)) { if (!js_ValueToObject(cx, *vp, &obj)) return JS_FALSE; if (!obj) goto default_iter; } else { obj = js_ValueToNonNullObject(cx, *vp); if (!obj) return JS_FALSE; } } JS_ASSERT(obj); JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); clasp = OBJ_GET_CLASS(cx, obj); if ((clasp->flags & JSCLASS_IS_EXTENDED) && (xclasp = (JSExtendedClass *) clasp)->iteratorObject) { iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH)); if (!iterobj) goto bad; *vp = OBJECT_TO_JSVAL(iterobj); } else { atom = cx->runtime->atomState.iteratorAtom; #if JS_HAS_XML_SUPPORT if (OBJECT_IS_XML(cx, obj)) { if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp)) goto bad; } else #endif { if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp)) goto bad; } if (JSVAL_IS_VOID(*vp)) { default_iter: /* * Fail over to the default enumerating native iterator. * * Create iterobj with a NULL parent to ensure that we use the * correct scope chain to lookup the iterator's constructor. Since * we use the parent slot to keep track of the iterable, we must * fix it up after. */ iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL, 0); if (!iterobj) goto bad; /* Store in *vp to protect it from GC (callers must root vp). */ *vp = OBJECT_TO_JSVAL(iterobj); if (!InitNativeIterator(cx, iterobj, obj, flags)) goto bad; } else { arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) { goto bad; } if (JSVAL_IS_PRIMITIVE(*vp)) { const char *printable = js_AtomToPrintableString(cx, atom); if (printable) { js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN, JSDVG_SEARCH_STACK, *vp, NULL, printable); } goto bad; } } } ok = JS_TRUE; out: if (obj) JS_POP_TEMP_ROOT(cx, &tvr); return ok; bad: ok = JS_FALSE; goto out; }