JSBool js_GetClassPrototype(JSContext *cx, JSClass *clasp, JSObject **protop) { JSBool ok; JSObject *ctor; jsval pval; JSProperty *prop; *protop = NULL; JS_LOCK(cx); ok = FindConstructor(cx, clasp, &pval); if (!ok || !JSVAL_IS_FUNCTION(pval)) goto out; ctor = JSVAL_TO_OBJECT(pval); if (!js_LookupProperty(cx, ctor, (jsval)cx->runtime->atomState.classPrototypeAtom, NULL, &prop)) { ok = JS_FALSE; goto out; } if (prop) { pval = prop->object->slots[prop->slot]; if (JSVAL_IS_OBJECT(pval)) *protop = JSVAL_TO_OBJECT(pval); } out: JS_UNLOCK(cx); return ok; }
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; }
JSProperty * js_GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSProperty *prop; JSObject *obj2; jsint slot; PR_ASSERT(JS_IS_LOCKED(cx)); if (!js_LookupProperty(cx, obj, id, &obj, &prop)) return NULL; if (!prop) { prop = js_DefineProperty(cx, obj, id, #if JS_BUG_NULL_INDEX_PROPS (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0) ? JSVAL_NULL : JSVAL_VOID, #else JSVAL_VOID, #endif NULL, NULL, 0); if (!prop) return NULL; } obj2 = prop->object; slot = prop->slot; *vp = obj2->slots[slot]; if (!prop->getter(cx, obj, prop->id, vp)) return NULL; obj2->slots[slot] = *vp; return prop; }
JSBool js_FindProperty(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp) { JSRuntime *rt; JSObject *obj, *parent, *lastobj; JSProperty *prop; rt = cx->runtime; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); for (obj = cx->fp->scopeChain; obj; obj = parent) { /* Try the property cache and return immediately on cache hit. */ PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, prop); if (PROP_FOUND(prop)) { *objp = obj; *propp = prop; return JS_TRUE; } /* * Set parent here, after cache hit to minimize cycles in that case, * but before js_LookupProperty, which might change obj. */ parent = OBJ_GET_PARENT(obj); /* If cache miss (not cached-as-not-found), take the slow path. */ if (!prop) { if (!js_LookupProperty(cx, obj, id, &obj, &prop)) return JS_FALSE; if (prop) { PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); *objp = obj; *propp = prop; return JS_TRUE; } /* No such property -- cache obj[id] as not-found. */ PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, PROP_NOT_FOUND); } lastobj = obj; } *objp = lastobj; *propp = NULL; return JS_TRUE; }
JSBool js_DeleteProperty(JSContext *cx, JSObject *obj, jsval id, jsval *rval) { #if JS_HAS_PROP_DELETE JSProperty *prop; PR_ASSERT(JS_IS_LOCKED(cx)); if (!js_LookupProperty(cx, obj, id, NULL, &prop)) return JS_FALSE; if (!prop) return JS_TRUE; return js_DeleteProperty2(cx, obj, prop, id, rval); #else jsval null = JSVAL_NULL; *rval = JSVAL_VOID; return (js_SetProperty(cx, obj, id, &null) != NULL); #endif }
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 obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSFunction *fun; jsval userid, symid, propid, value; JSAtom *atom; JSRuntime *rt; JSBool ok; JSProperty *prop; JSPropertyOp getter, setter; JSClass *clasp; fun = JS_ValueToFunction(cx, argv[1]); if (!fun) return JS_FALSE; argv[1] = OBJECT_TO_JSVAL(fun->object); /* * Lock the world while we look in obj. Be sure to return via goto out * on error, so we unlock. */ rt = cx->runtime; JS_LOCK_RUNTIME(rt); /* Compute the unique int/atom symbol id needed by js_LookupProperty. */ userid = argv[0]; if (JSVAL_IS_INT(userid)) { symid = userid; atom = NULL; } else { atom = js_ValueToStringAtom(cx, userid); if (!atom) { ok = JS_FALSE; goto out; } symid = (jsval)atom; } ok = js_LookupProperty(cx, obj, symid, &obj, &prop); if (atom) js_DropAtom(cx, atom); if (!ok) goto out; /* Set propid from the property, in case it has a tinyid. */ if (prop) { propid = prop->id; getter = prop->getter; setter = prop->setter; value = prop->object->slots[prop->slot]; } else { propid = userid; clasp = obj->map->clasp; getter = clasp->getProperty; setter = clasp->setProperty; value = JSVAL_VOID; } /* * Security policy is implemented in getters and setters, so we have to * call get and set here to let the JS API client check for a watchpoint * that crosses a trust boundary. * XXX assumes get and set are idempotent, should use a clasp->watch hook */ ok = getter(cx, obj, propid, &value) && setter(cx, obj, propid, &value); if (!ok) goto out; /* Finally, call into jsdbgapi.c to set the watchpoint on userid. */ ok = JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, fun->object); out: JS_UNLOCK_RUNTIME(rt); return ok; }