JSContext * js_ContextIterator(JSRuntime *rt, JSContext **iterp) { JSContext *cx = *iterp; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); if (!cx) cx = (JSContext *)rt->contextList.next; if ((void *)cx == &rt->contextList) return NULL; *iterp = (JSContext *)cx->links.next; return cx; }
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; }
void js_DestroyContext(JSContext *cx) { JSRuntime *rt; JSBool rtempty; rt = cx->runtime; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); /* Remove cx from context list first. */ PR_REMOVE_LINK(&cx->links); rtempty = (rt->contextList.next == (PRCList *)&rt->contextList); if (js_InterpreterHooks && js_InterpreterHooks->destroyContext) { /* This is a stub, but in case it removes roots, call it now. */ js_InterpreterHooks->destroyContext(cx); } if (rtempty) { /* No more contexts: clear debugging state to remove GC roots. */ JS_ClearAllTraps(cx); JS_ClearAllWatchPoints(cx); } /* Remove more GC roots in regExpStatics, then collect garbage. */ #if JS_HAS_REGEXPS js_FreeRegExpStatics(cx, &cx->regExpStatics); #endif js_ForceGC(cx); if (rtempty) { /* Free atom state now that we've run the GC. */ js_FreeAtomState(cx, &rt->atomState); } /* Free the stuff hanging off of cx. */ PR_FinishArenaPool(&cx->stackPool); PR_FinishArenaPool(&cx->codePool); PR_FinishArenaPool(&cx->tempPool); if (cx->lastMessage) free(cx->lastMessage); free(cx); }
JSProperty * js_DefineProperty(JSContext *cx, JSObject *obj, jsval id, jsval value, JSPropertyOp getter, JSPropertyOp setter, uintN flags) { JSRuntime *rt; JSScope *scope; JSProperty *prop; rt = cx->runtime; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); /* Handle old bug that treated empty string as zero index. */ CHECK_FOR_FUNNY_INDEX(id); /* Use the object's class getter and setter by default. */ if (!getter) getter = obj->map->clasp->getProperty; if (!setter) setter = obj->map->clasp->setProperty; /* Find a sharable scope, or get a new one for obj. */ scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop); if (!scope) return NULL; /* Add the property only if MutateScope didn't find a shared scope. */ if (!prop) { prop = js_NewProperty(cx, scope, id, getter, setter, flags); if (!prop) return NULL; if (!obj->map->clasp->addProperty(cx, obj, prop->id, &value) || !scope->ops->add(cx, scope, id, prop)) { js_DestroyProperty(cx, prop); return NULL; } PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); } PR_ASSERT(prop->slot < obj->map->freeslot); obj->slots[prop->slot] = value; return prop; }
JSBool js_FindVariable(JSContext *cx, jsval id, JSObject **objp, JSProperty **propp) { JSRuntime *rt; JSObject *obj; JSProperty *prop; rt = cx->runtime; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); /* * First look for id's property along the "with" statement and the * statically-linked scope chains. */ if (!js_FindProperty(cx, id, objp, propp)) return JS_FALSE; if (*propp) return JS_TRUE; /* * Use the top-level scope from the scope chain, which won't end in the * same scope as cx->globalObject's for cross-context function calls. */ obj = *objp; PR_ASSERT(obj); /* * Make a top-level variable. */ prop = js_DefineProperty(cx, obj, id, JSVAL_VOID, NULL, NULL, JSPROP_ENUMERATE); if (!prop) return JS_FALSE; PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); *propp = prop; return JS_TRUE; }
JSBool js_DeleteProperty2(JSContext *cx, JSObject *obj, JSProperty *prop, jsval id, jsval *rval) { #if JS_HAS_PROP_DELETE JSRuntime *rt; JSString *str; JSScope *scope; JSObject *proto; PRHashNumber hash; JSSymbol *sym; rt = cx->runtime; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID; if (prop->flags & JSPROP_PERMANENT) { if (JSVERSION_IS_ECMA(cx->version)) { *rval = JSVAL_FALSE; return JS_TRUE; } str = js_ValueToSource(cx, js_IdToValue(id)); if (str) JS_ReportError(cx, "%s is permanent", JS_GetStringBytes(str)); return JS_FALSE; } if (!obj->map->clasp->delProperty(cx, obj, prop->id, &prop->object->slots[prop->slot])) { return JS_FALSE; } /* Handle old bug that treated empty string as zero index. */ CHECK_FOR_FUNNY_INDEX(id); GC_POKE(cx, prop->object->slots[prop->slot]); scope = (JSScope *)obj->map; proto = scope->object; if (proto == obj) { /* The object has its own scope, so remove id if it was found here. */ if (prop->object == obj) { /* Purge cache only if prop is not about to be destroyed. */ if (prop->nrefs != 1) { PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, PROP_NOT_FOUND); } #if JS_HAS_OBJ_WATCHPOINT if (prop->setter == js_watch_set) { /* * Keep the symbol around with null value in case of re-set. * The watchpoint will hold the "deleted" property until it * is removed by obj_unwatch or a native JS_ClearWatchPoint. * See js_SetProperty for the re-set logic. */ for (sym = prop->symbols; sym; sym = sym->next) { if (sym_id(sym) == id) { sym->entry.value = NULL; prop = js_DropProperty(cx, prop); PR_ASSERT(prop); return JS_TRUE; } } } #endif scope->ops->remove(cx, scope, id); } proto = OBJ_GET_PROTO(obj); if (!proto) return JS_TRUE; } /* Search shared prototype scopes for an inherited property to hide. */ hash = js_HashValue(id); do { scope = (JSScope *)proto->map; sym = scope->ops->lookup(cx, scope, id, hash); if (sym) { /* Add a null-valued symbol to hide the prototype property. */ scope = js_GetMutableScope(cx, obj); if (!scope) return JS_FALSE; if (!scope->ops->add(cx, scope, id, NULL)) return JS_FALSE; PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, PROP_NOT_FOUND); return JS_TRUE; } proto = OBJ_GET_PROTO(proto); } while (proto); return JS_TRUE; #else jsval null = JSVAL_NULL; return (js_SetProperty(cx, obj, id, &null) != NULL); #endif }
JSProperty * js_SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSRuntime *rt; JSScope *scope, *protoScope; JSProperty *prop, *protoProp; PRHashNumber hash; JSSymbol *sym, *protoSym; JSObject *proto, *assignobj; jsval pval, aval, rval; jsint slot; JSErrorReporter older; JSString *str; rt = cx->runtime; PR_ASSERT(JS_IS_RUNTIME_LOCKED(rt)); #ifdef SCOPE_TABLE_NOTYET /* XXX hash recompute */ /* XXX protoProp->getter, protoProp->setter, protoProp->flags */ scope = js_MutateScope(cx, obj, id, getter, setter, flags, &prop); #endif scope = js_GetMutableScope(cx, obj); if (!scope) return NULL; /* Handle old bug that treated empty string as zero index. */ CHECK_FOR_FUNNY_INDEX(id); hash = js_HashValue(id); sym = scope->ops->lookup(cx, scope, id, hash); if (sym) { prop = sym_property(sym); #if JS_HAS_OBJ_WATCHPOINT if (!prop) { /* * Deleted property place-holder, could have a watchpoint that * holds the deleted-but-watched property. */ prop = js_FindWatchPoint(rt, obj, js_IdToValue(id)); } #endif } else { prop = NULL; } if (!prop) { /* Find a prototype property with the same id. */ proto = OBJ_GET_PROTO(obj); protoProp = NULL; while (proto) { protoScope = (JSScope *)proto->map; protoSym = protoScope->ops->lookup(cx, protoScope, id, hash); if (protoSym) { protoProp = sym_property(protoSym); if (protoProp) break; } proto = OBJ_GET_PROTO(proto); } /* Make a new property descriptor with the right heritage. */ if (protoProp) { prop = js_NewProperty(cx, scope, id, protoProp->getter, protoProp->setter, protoProp->flags | JSPROP_ENUMERATE); prop->id = protoProp->id; } else { prop = js_NewProperty(cx, scope, id, scope->map.clasp->getProperty, scope->map.clasp->setProperty, JSPROP_ENUMERATE); } if (!prop) return NULL; if (!obj->map->clasp->addProperty(cx, obj, prop->id, vp)) { js_DestroyProperty(cx, prop); return NULL; } /* Initialize new properties to undefined. */ obj->slots[prop->slot] = JSVAL_VOID; if (sym) { /* Null-valued symbol left behind from a delete operation. */ sym->entry.value = js_HoldProperty(cx, prop); } } if (!sym) { /* Need a new symbol as well as a new property. */ sym = scope->ops->add(cx, scope, id, prop); if (!sym) return NULL; #if JS_BUG_AUTO_INDEX_PROPS { jsval id2 = INT_TO_JSVAL(prop->slot - JSSLOT_START); if (!scope->ops->add(cx, scope, id2, prop)) { scope->ops->remove(cx, scope, id); return NULL; } PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id2, prop); } #endif PROPERTY_CACHE_FILL(cx, &rt->propertyCache, obj, id, prop); } /* Get the current property value from its slot. */ PR_ASSERT(prop->slot < obj->map->freeslot); slot = prop->slot; pval = obj->slots[slot]; /* Evil overloaded operator assign() hack. */ if (JSVAL_IS_OBJECT(pval)) { assignobj = JSVAL_TO_OBJECT(pval); if (assignobj) { older = JS_SetErrorReporter(cx, NULL); if (js_GetProperty(cx, assignobj, (jsval)rt->atomState.assignAtom, &aval) && JSVAL_IS_FUNCTION(aval) && js_Call(cx, assignobj, aval, 1, vp, &rval)) { *vp = rval; JS_SetErrorReporter(cx, older); prop->flags |= JSPROP_ASSIGNHACK; return prop; } JS_SetErrorReporter(cx, older); } } /* Check for readonly *after* the assign() hack. */ if (prop->flags & JSPROP_READONLY) { if (!JSVERSION_IS_ECMA(cx->version)) { str = js_ValueToSource(cx, js_IdToValue(id)); if (str) { JS_ReportError(cx, "%s is read-only", JS_GetStringBytes(str)); } } return NULL; } /* Let the setter modify vp before copying from it to obj->slots[slot]. */ if (!prop->setter(cx, obj, prop->id, vp)) return NULL; GC_POKE(cx, pval); obj->slots[slot] = *vp; /* Setting a property makes it enumerable. */ prop->flags |= JSPROP_ENUMERATE; return prop; }