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 InitExceptionObject(JSContext *cx, JSObject *obj, JSString *message, JSString *filename, uintN lineno) { JSCheckAccessOp checkAccess; JSErrorReporter older; JSExceptionState *state; jschar *stackbuf; size_t stacklen, stackmax; JSStackFrame *fp; jsval callerid, v; JSBool ok; JSString *argsrc, *stack; uintN i, ulineno; const char *cp; char ulnbuf[11]; if (!JS_DefineProperty(cx, obj, js_message_str, STRING_TO_JSVAL(message), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } if (!JS_DefineProperty(cx, obj, js_filename_str, STRING_TO_JSVAL(filename), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } if (!JS_DefineProperty(cx, obj, js_lineno_str, INT_TO_JSVAL(lineno), NULL, NULL, JSPROP_ENUMERATE)) { return JS_FALSE; } /* * Set the 'stack' property. * * First, set aside any error reporter for cx and save its exception state * so we can suppress any checkAccess failures. Such failures should stop * the backtrace procedure, not result in a failure of this constructor. */ checkAccess = cx->runtime->checkObjectAccess; if (checkAccess) { older = JS_SetErrorReporter(cx, NULL); state = JS_SaveExceptionState(cx); } #ifdef __GNUC__ /* suppress bogus gcc warnings */ else { older = NULL; state = NULL; } #endif callerid = ATOM_KEY(cx->runtime->atomState.callerAtom); /* * Prepare to allocate a jschar buffer at stackbuf, where stacklen indexes * the next free jschar slot, and with room for at most stackmax non-null * jschars. If stackbuf is non-null, it always contains an extra slot for * the null terminator we'll store at the end, as a backstop. * * All early returns must goto done after this point, till the after-loop * cleanup code has run! */ stackbuf = NULL; stacklen = stackmax = 0; ok = JS_TRUE; #define APPEND_CHAR_TO_STACK(c) \ JS_BEGIN_MACRO \ if (stacklen == stackmax) { \ void *ptr_; \ stackmax = stackmax ? 2 * stackmax : 64; \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) { \ ok = JS_FALSE; \ goto done; \ } \ stackbuf = ptr_; \ } \ stackbuf[stacklen++] = (c); \ JS_END_MACRO #define APPEND_STRING_TO_STACK(str) \ JS_BEGIN_MACRO \ JSString *str_ = str; \ size_t length_ = JSSTRING_LENGTH(str_); \ if (stacklen + length_ > stackmax) { \ void *ptr_; \ stackmax = JS_BIT(JS_CeilingLog2(stacklen + length_)); \ ptr_ = JS_realloc(cx, stackbuf, (stackmax+1) * sizeof(jschar)); \ if (!ptr_) { \ ok = JS_FALSE; \ goto done; \ } \ stackbuf = ptr_; \ } \ js_strncpy(stackbuf + stacklen, JSSTRING_CHARS(str_), length_); \ stacklen += length_; \ JS_END_MACRO for (fp = cx->fp; fp; fp = fp->down) { if (checkAccess) { v = (fp->fun && fp->argv) ? fp->argv[-2] : JSVAL_NULL; if (!JSVAL_IS_PRIMITIVE(v)) { ok = checkAccess(cx, fp->fun->object, callerid, JSACC_READ, &v); if (!ok) { ok = JS_TRUE; break; } } } if (fp->fun) { if (fp->fun->atom) APPEND_STRING_TO_STACK(ATOM_TO_STRING(fp->fun->atom)); APPEND_CHAR_TO_STACK('('); for (i = 0; i < fp->argc; i++) { /* Avoid toSource bloat and fallibility for object types. */ v = fp->argv[i]; if (JSVAL_IS_PRIMITIVE(v)) { argsrc = js_ValueToSource(cx, v); } else if (JSVAL_IS_FUNCTION(cx, v)) { /* XXX Avoid function decompilation bloat for now. */ argsrc = JS_GetFunctionId(JS_ValueToFunction(cx, v)); if (!argsrc) argsrc = js_ValueToSource(cx, v); } else { /* XXX Avoid toString on objects, it takes too long and uses too much memory, for too many classes (see Mozilla bug 166743). */ char buf[100]; JS_snprintf(buf, sizeof buf, "[object %s]", OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v))->name); argsrc = JS_NewStringCopyZ(cx, buf); } if (!argsrc) { ok = JS_FALSE; goto done; } if (i > 0) APPEND_CHAR_TO_STACK(','); APPEND_STRING_TO_STACK(argsrc); } APPEND_CHAR_TO_STACK(')'); } APPEND_CHAR_TO_STACK('@'); if (fp->script && fp->script->filename) { for (cp = fp->script->filename; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); } APPEND_CHAR_TO_STACK(':'); if (fp->script && fp->pc) { ulineno = js_PCToLineNumber(fp->script, fp->pc); JS_snprintf(ulnbuf, sizeof ulnbuf, "%u", ulineno); for (cp = ulnbuf; *cp; cp++) APPEND_CHAR_TO_STACK(*cp); } else { APPEND_CHAR_TO_STACK('0'); } APPEND_CHAR_TO_STACK('\n'); } #undef APPEND_CHAR_TO_STACK #undef APPEND_STRING_TO_STACK done: if (checkAccess) { if (ok) JS_RestoreExceptionState(cx, state); else JS_DropExceptionState(cx, state); JS_SetErrorReporter(cx, older); } if (!ok) { JS_free(cx, stackbuf); return JS_FALSE; } if (!stackbuf) { stack = cx->runtime->emptyString; } else { /* NB: if stackbuf was allocated, it has room for the terminator. */ JS_ASSERT(stacklen <= stackmax); if (stacklen < stackmax) { /* * Realloc can fail when shrinking on some FreeBSD versions, so * don't use JS_realloc here; simply let the oversized allocation * be owned by the string in that rare case. */ void *shrunk = realloc(stackbuf, (stacklen+1) * sizeof(jschar)); if (shrunk) stackbuf = shrunk; } stackbuf[stacklen] = 0; stack = js_NewString(cx, stackbuf, stacklen, 0); if (!stack) { JS_free(cx, stackbuf); return JS_FALSE; } } return JS_DefineProperty(cx, obj, js_stack_str, STRING_TO_JSVAL(stack), NULL, NULL, JSPROP_ENUMERATE); }
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; }