XPC_NW_AddProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSProperty *prop; JSObject *pobj; jsid idAsId; if (!::JS_ValueToId(cx, id, &idAsId) || !OBJ_LOOKUP_PROPERTY(cx, obj, idAsId, &pobj, &prop)) { return JS_FALSE; } // Do not allow scripted getters or setters on XPCNativeWrappers. NS_ASSERTION(prop && pobj == obj, "Wasn't this property just added?"); JSScopeProperty *sprop = (JSScopeProperty *) prop; uint8 attrs = sprop->attrs; OBJ_DROP_PROPERTY(cx, pobj, prop); if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) { return ThrowException(NS_ERROR_ILLEGAL_VALUE, cx); } jsval flags; ::JS_GetReservedSlot(cx, obj, 0, &flags); if (!HAS_FLAGS(flags, FLAG_RESOLVING)) { return JS_TRUE; } // Note: no need to protect *vp from GC here, since it's already in the slot // on |obj|. return EnsureLegalActivity(cx, obj) && XPC_NW_RewrapIfDeepWrapper(cx, obj, *vp, vp); }
static JSBool array_unshift(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint length, last; uintN i; jsid id, id2; jsval v; #if JS_HAS_SPARSE_ARRAYS JSObject *obj2; JSProperty *prop; #endif if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; if (argc > 0) { /* Slide up the array to make room for argc at the bottom. */ if (length > 0) { last = length; while (last--) { if (!IndexToId(cx, last, &id)) return JS_FALSE; if (!IndexToId(cx, last + argc, &id2)) return JS_FALSE; #if JS_HAS_SPARSE_ARRAYS if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */ continue; } OBJ_DROP_PROPERTY(cx, obj2, prop); #endif if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) return JS_FALSE; if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) return JS_FALSE; } } /* Copy from argv to the bottom of the array. */ for (i = 0; i < argc; i++) { if (!IndexToId(cx, i, &id)) return JS_FALSE; if (!OBJ_SET_PROPERTY(cx, obj, id, &argv[i])) return JS_FALSE; } /* Follow Perl by returning the new array length. */ length += argc; if (!js_SetLengthProperty(cx, obj, length)) return JS_FALSE; } return IndexToValue(cx, length, rval); }
static JSBool array_reverse(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsuint len, half, i; jsid id, id2; jsval v, v2; if (!js_GetLengthProperty(cx, obj, &len)) return JS_FALSE; half = len / 2; for (i = 0; i < half; i++) { if (!IndexToId(cx, i, &id)) return JS_FALSE; if (!IndexToId(cx, len - i - 1, &id2)) return JS_FALSE; if (!OBJ_GET_PROPERTY(cx, obj, id, &v)) return JS_FALSE; if (!OBJ_GET_PROPERTY(cx, obj, id2, &v2)) return JS_FALSE; #if JS_HAS_SPARSE_ARRAYS /* This part isn't done yet. */ if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop)) return JS_FALSE; if (!prop) { OBJ_DELETE_PROPERTY(cx, obj, id2, &v); /* v is junk. */ continue; } OBJ_DROP_PROPERTY(cx, obj2, prop); #endif if (!OBJ_SET_PROPERTY(cx, obj, id, &v2)) return JS_FALSE; if (!OBJ_SET_PROPERTY(cx, obj, id2, &v)) return JS_FALSE; } *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; }
/* XXXmccabe do the sort helper functions need to take int? (Or can we claim * that 2^32 * 32 is too large to worry about?) Something dumps when I change * to unsigned int; is qsort using -1 as a fencepost? */ static JSBool array_sort(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval fval; CompareArgs ca; jsuint len, newlen, i; jsval *vec; jsid id; size_t nbytes; /* * Optimize the default compare function case if all of obj's elements * have values of type string. */ JSBool all_strings; if (argc > 0) { if (JSVAL_IS_PRIMITIVE(argv[0])) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SORT_ARG); return JS_FALSE; } fval = argv[0]; all_strings = JS_FALSE; /* non-default compare function */ } else { fval = JSVAL_NULL; all_strings = JS_TRUE; /* check for all string values */ } if (!js_GetLengthProperty(cx, obj, &len)) return JS_FALSE; if (len == 0) { *rval = OBJECT_TO_JSVAL(obj); return JS_TRUE; } /* * Test for size_t overflow, which could lead to indexing beyond the end * of the malloc'd vector. */ nbytes = len * sizeof(jsval); if (nbytes != (double) len * sizeof(jsval)) { JS_ReportOutOfMemory(cx); return JS_FALSE; } vec = (jsval *) JS_malloc(cx, nbytes); if (!vec) return JS_FALSE; #if JS_HAS_SPARSE_ARRAYS newlen = 0; #else newlen = len; #endif for (i = 0; i < len; i++) { ca.status = IndexToId(cx, i, &id); if (!ca.status) goto out; #if JS_HAS_SPARSE_ARRAYS { JSObject *obj2; JSProperty *prop; ca.status = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ca.status) goto out; if (!prop) { vec[i] = JSVAL_VOID; continue; } OBJ_DROP_PROPERTY(cx, obj2, prop); newlen++; } #endif ca.status = OBJ_GET_PROPERTY(cx, obj, id, &vec[i]); if (!ca.status) goto out; /* We know JSVAL_IS_STRING yields 0 or 1, so avoid a branch via &=. */ all_strings &= JSVAL_IS_STRING(vec[i]); } ca.context = cx; ca.fval = fval; ca.status = JS_TRUE; if (!js_HeapSort(vec, (size_t) len, sizeof(jsval), all_strings ? sort_compare_strings : sort_compare, &ca)) { JS_ReportOutOfMemory(cx); ca.status = JS_FALSE; } if (ca.status) { ca.status = InitArrayElements(cx, obj, newlen, vec); if (ca.status) *rval = OBJECT_TO_JSVAL(obj); #if JS_HAS_SPARSE_ARRAYS /* set length of newly-created array object to old length. */ if (ca.status && newlen < len) { ca.status = js_SetLengthProperty(cx, obj, len); /* Delete any leftover properties greater than newlen. */ while (ca.status && newlen < len) { jsval junk; ca.status = !IndexToId(cx, newlen, &id) || !OBJ_DELETE_PROPERTY(cx, obj, id, &junk); newlen++; } } #endif } out: if (vec) JS_free(cx, vec); return ca.status; }
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(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass); obj = STOBJ_GET_PARENT(iterobj); origobj = STOBJ_GET_PROTO(iterobj); state = STOBJ_GET_SLOT(iterobj, 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) { JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, &id, rval)) { return JS_FALSE; } } else { if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; } STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (JSVAL_IS_NULL(state)) goto stop; } else #endif { restart: if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, 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_GET_PROTO(cx, obj); if (obj) { STOBJ_SET_PARENT(iterobj, obj); if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) return JS_FALSE; STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state); if (!JSVAL_IS_NULL(state)) goto restart; } } goto stop; } /* Skip properties not in obj when looking from origobj. */ if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) return JS_FALSE; if (!prop) goto restart; OBJ_DROP_PROPERTY(cx, obj2, 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 OBJ_LOOKUP_PROPERTY 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 (!OBJ_GET_PROPERTY(cx, origobj, 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(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL); *rval = JSVAL_HOLE; return JS_TRUE; }