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 exn_enumerate(JSContext *cx, JSObject *obj) { JSAtomState *atomState; uintN i; JSAtom *atom; JSObject *pobj; JSProperty *prop; JS_STATIC_ASSERT(sizeof(JSAtomState) <= (size_t)(uint16)-1); static const uint16 offsets[] = { (uint16)offsetof(JSAtomState, messageAtom), (uint16)offsetof(JSAtomState, fileNameAtom), (uint16)offsetof(JSAtomState, lineNumberAtom), (uint16)offsetof(JSAtomState, stackAtom), }; atomState = &cx->runtime->atomState; for (i = 0; i != JS_ARRAY_LENGTH(offsets); ++i) { atom = *(JSAtom **)((uint8 *)atomState + offsets[i]); if (!js_LookupProperty(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop)) return JS_FALSE; if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); } return JS_TRUE; }
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; }
JS_SetWatchPoint(JSContext *cx, JSObject *obj, jsval id, JSWatchPointHandler handler, void *closure) { JSAtom *atom; jsid propid; JSObject *pobj; JSScopeProperty *sprop; JSRuntime *rt; JSWatchPoint *wp; JSPropertyOp watcher; if (!OBJ_IS_NATIVE(obj)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_WATCH, OBJ_GET_CLASS(cx, obj)->name); return JS_FALSE; } if (JSVAL_IS_INT(id)) { propid = (jsid)id; atom = NULL; } else { atom = js_ValueToStringAtom(cx, id); if (!atom) return JS_FALSE; propid = (jsid)atom; } if (!js_LookupProperty(cx, obj, propid, &pobj, (JSProperty **)&sprop)) return JS_FALSE; rt = cx->runtime; if (!sprop) { /* Check for a deleted symbol watchpoint, which holds its property. */ sprop = js_FindWatchPoint(rt, OBJ_SCOPE(obj), propid); if (!sprop) { /* Make a new property in obj so we can watch for the first set. */ if (!js_DefineProperty(cx, obj, propid, JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE, (JSProperty **)&sprop)) { sprop = NULL; } } } else if (pobj != obj) { /* Clone the prototype property so we can watch the right object. */ jsval value; JSPropertyOp getter, setter; uintN attrs; if (OBJ_IS_NATIVE(pobj)) { value = SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)) ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot) : JSVAL_VOID; getter = sprop->getter; setter = sprop->setter; attrs = sprop->attrs; } else { if (!OBJ_GET_PROPERTY(cx, pobj, id, &value)) { OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); return JS_FALSE; } getter = setter = JS_PropertyStub; attrs = JSPROP_ENUMERATE; } OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop); if (!js_DefineProperty(cx, obj, propid, value, getter, setter, attrs, (JSProperty **)&sprop)) { sprop = NULL; } } if (!sprop) return JS_FALSE; wp = FindWatchPoint(rt, OBJ_SCOPE(obj), propid); if (!wp) { watcher = js_WrapWatchedSetter(cx, propid, sprop->attrs, sprop->setter); if (!watcher) return JS_FALSE; wp = (JSWatchPoint *) JS_malloc(cx, sizeof *wp); if (!wp) return JS_FALSE; wp->handler = NULL; wp->closure = NULL; if (!js_AddRoot(cx, &wp->closure, "wp->closure")) { JS_free(cx, wp); return JS_FALSE; } JS_APPEND_LINK(&wp->links, &rt->watchPointList); wp->object = obj; wp->sprop = sprop; JS_ASSERT(sprop->setter != js_watch_set); wp->setter = sprop->setter; wp->nrefs = 1; sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, 0, sprop->attrs, sprop->getter, watcher); if (!sprop) return DropWatchPoint(cx, wp); } wp->handler = handler; wp->closure = closure; OBJ_DROP_PROPERTY(cx, obj, (JSProperty *)sprop); 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(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; }