JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda) { JSClass *clasp; JSScope *scope; uint32 i, n; JSPropertyDesc *pd; JSScopeProperty *sprop; clasp = OBJ_GET_CLASS(cx, obj); if (!OBJ_IS_NATIVE(obj) || (clasp->flags & JSCLASS_NEW_ENUMERATE)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DESCRIBE_PROPS, clasp->name); return JS_FALSE; } if (!clasp->enumerate(cx, obj)) return JS_FALSE; /* have no props, or object's scope has not mutated from that of proto */ scope = OBJ_SCOPE(obj); if (scope->object != obj || scope->entryCount == 0) { pda->length = 0; pda->array = NULL; return JS_TRUE; } n = scope->entryCount; if (n > scope->map.nslots) n = scope->map.nslots; pd = (JSPropertyDesc *) JS_malloc(cx, (size_t)n * sizeof(JSPropertyDesc)); if (!pd) return JS_FALSE; i = 0; for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) { if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop)) continue; if (!js_AddRoot(cx, &pd[i].id, NULL)) goto bad; if (!js_AddRoot(cx, &pd[i].value, NULL)) goto bad; if (!JS_GetPropertyDesc(cx, obj, sprop, &pd[i])) goto bad; if ((pd[i].flags & JSPD_ALIAS) && !js_AddRoot(cx, &pd[i].alias, NULL)) goto bad; if (++i == n) break; } pda->length = i; pda->array = pd; return JS_TRUE; bad: pda->length = i + 1; pda->array = pd; JS_PutPropertyDescArray(cx, pda); return JS_FALSE; }
JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, void *closure) { JSRuntime *rt; JSTrap *trap; rt = cx->runtime; trap = FindTrap(rt, script, pc); if (trap) { /* Restore opcode at pc so it can be saved again. */ *pc = (jsbytecode)trap->op; } else { trap = (JSTrap *) JS_malloc(cx, sizeof *trap); if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) { if (trap) JS_free(cx, trap); return JS_FALSE; } } JS_APPEND_LINK(&trap->links, &rt->trapList); trap->script = script; trap->pc = pc; trap->op = (JSOp)*pc; trap->handler = handler; trap->closure = closure; *pc = JSOP_TRAP; return JS_TRUE; }
JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, void *closure) { JSRuntime *rt; JSTrap *trap; rt = cx->runtime; trap = FindTrap(rt, script, pc); if (trap) { JS_ASSERT(trap->script == script && trap->pc == pc); JS_ASSERT(*pc == JSOP_TRAP); } else { trap = (JSTrap *) JS_malloc(cx, sizeof *trap); if (!trap || !js_AddRoot(cx, &trap->closure, "trap->closure")) { if (trap) JS_free(cx, trap); return JS_FALSE; } JS_APPEND_LINK(&trap->links, &rt->trapList); trap->script = script; trap->pc = pc; trap->op = (JSOp)*pc; *pc = JSOP_TRAP; } trap->handler = handler; trap->closure = closure; return JS_TRUE; }
JSBool js_ReportUncaughtException(JSContext *cx) { JSObject *exnObject; JSString *str; jsval exn; JSErrorReport *reportp; const char *bytes; if (!JS_IsExceptionPending(cx)) return JS_FALSE; if (!JS_GetPendingException(cx, &exn)) return JS_FALSE; /* * Because js_ValueToString below could error and an exception object * could become unrooted, we root it here. */ if (JSVAL_IS_OBJECT(exn) && exn != JSVAL_NULL) { exnObject = JSVAL_TO_OBJECT(exn); if (!js_AddRoot(cx, &exnObject, "exn.report.root")) return JS_FALSE; } else { exnObject = NULL; } #if JS_HAS_ERROR_EXCEPTIONS reportp = js_ErrorFromException(cx, exn); #else reportp = NULL; #endif str = js_ValueToString(cx, exn); bytes = str ? js_GetStringBytes(str) : "null"; if (reportp == NULL) { /* * XXXmccabe todo: Instead of doing this, synthesize an error report * struct that includes the filename, lineno where the exception was * originally thrown. */ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNCAUGHT_EXCEPTION, bytes); } else { /* Flag the error as an exception. */ reportp->flags |= JSREPORT_EXCEPTION; js_ReportErrorAgain(cx, bytes, reportp); } if (exnObject != NULL) js_RemoveRoot(cx->runtime, &exnObject); return JS_TRUE; }
JSONParser * js_BeginJSONParse(JSContext *cx, jsval *rootVal) { if (!cx) return NULL; JSObject *arr = js_NewArrayObject(cx, 0, NULL); if (!arr) return NULL; JSONParser *jp = (JSONParser*) JS_malloc(cx, sizeof(JSONParser)); if (!jp) return NULL; jp->buffer = NULL; jp->objectStack = arr; if (!js_AddRoot(cx, &jp->objectStack, "JSON parse stack")) goto bad; jp->hexChar = 0; jp->numHex = 0; jp->statep = jp->stateStack; *jp->statep = JSON_PARSE_STATE_INIT; jp->rootVal = rootVal; jp->objectKey = (JSStringBuffer*) JS_malloc(cx, sizeof(JSStringBuffer)); if (!jp->objectKey) goto bad; js_InitStringBuffer(jp->objectKey); jp->buffer = (JSStringBuffer*) JS_malloc(cx, sizeof(JSStringBuffer)); if (!jp->buffer) goto bad; js_InitStringBuffer(jp->buffer); return jp; bad: JS_free(cx, jp->buffer); JS_free(cx, jp); return NULL; }
JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, JSPropertyDesc *pd) { JSPropertyOp getter; JSScope *scope; JSScopeProperty *aprop; jsval lastException; JSBool wasThrowing; pd->id = ID_TO_VALUE(sprop->id); wasThrowing = cx->throwing; if (wasThrowing) { lastException = cx->exception; if (JSVAL_IS_GCTHING(lastException) && !js_AddRoot(cx, &lastException, "lastException")) { return JS_FALSE; } cx->throwing = JS_FALSE; } if (!js_GetProperty(cx, obj, sprop->id, &pd->value)) { if (!cx->throwing) { pd->flags = JSPD_ERROR; pd->value = JSVAL_VOID; } else { pd->flags = JSPD_EXCEPTION; pd->value = cx->exception; } } else { pd->flags = 0; } cx->throwing = wasThrowing; if (wasThrowing) { cx->exception = lastException; if (JSVAL_IS_GCTHING(lastException)) js_RemoveRoot(cx->runtime, &lastException); } getter = sprop->getter; pd->flags |= ((sprop->attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0) | ((sprop->attrs & JSPROP_READONLY) ? JSPD_READONLY : 0) | ((sprop->attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0) #if JS_HAS_CALL_OBJECT | ((getter == js_GetCallVariable) ? JSPD_VARIABLE : 0) #endif /* JS_HAS_CALL_OBJECT */ | ((getter == js_GetArgument) ? JSPD_ARGUMENT : 0) | ((getter == js_GetLocalVariable) ? JSPD_VARIABLE : 0); #if JS_HAS_CALL_OBJECT /* for Call Object 'real' getter isn't passed in to us */ if (OBJ_GET_CLASS(cx, obj) == &js_CallClass && getter == js_CallClass.getProperty) { /* * Property of a heavyweight function's variable object having the * class-default getter. It's either an argument if permanent, or a * nested function if impermanent. Local variables have a special * getter (js_GetCallVariable, tested above) and setter, and not the * class default. */ pd->flags |= (sprop->attrs & JSPROP_PERMANENT) ? JSPD_ARGUMENT : JSPD_VARIABLE; } #endif /* JS_HAS_CALL_OBJECT */ pd->spare = 0; pd->slot = (pd->flags & (JSPD_ARGUMENT | JSPD_VARIABLE)) ? sprop->shortid : 0; pd->alias = JSVAL_VOID; scope = OBJ_SCOPE(obj); if (SPROP_HAS_VALID_SLOT(sprop, scope)) { for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) { if (aprop != sprop && aprop->slot == sprop->slot) { pd->alias = ID_TO_VALUE(aprop->id); break; } } } return JS_TRUE; }
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; }
JSBool js_ReportCompileErrorNumber(JSContext *cx, JSTokenStream *ts, JSCodeGenerator *cg, uintN flags, const uintN errorNumber, ...) { va_list ap; JSErrorReporter onError; JSErrorReport report; jschar *tokenptr; JSString *linestr = NULL; char *message; JSBool warning; if ((flags & JSREPORT_STRICT) && !JS_HAS_STRICT_OPTION(cx)) return JS_TRUE; memset(&report, 0, sizeof (struct JSErrorReport)); report.flags = flags; report.errorNumber = errorNumber; message = NULL; va_start(ap, errorNumber); if (!js_ExpandErrorArguments(cx, js_GetErrorMessage, NULL, errorNumber, &message, &report, &warning, JS_TRUE, ap)) { return JS_FALSE; } va_end(ap); js_AddRoot(cx, &linestr, "error line buffer"); JS_ASSERT(!ts || ts->linebuf.limit < ts->linebuf.base + JS_LINE_LIMIT); onError = cx->errorReporter; if (onError) { /* * We are typically called with non-null ts and null cg from jsparse.c. * We can be called with null ts from the regexp compilation functions. * The code generator (jsemit.c) may pass null ts and non-null cg. */ if (ts) { report.filename = ts->filename; report.lineno = ts->lineno; linestr = js_NewStringCopyN(cx, ts->linebuf.base, ts->linebuf.limit - ts->linebuf.base, 0); report.linebuf = linestr ? JS_GetStringBytes(linestr) : NULL; tokenptr = ts->tokens[(ts->cursor + ts->lookahead) & NTOKENS_MASK].ptr; report.tokenptr = linestr ? report.linebuf + (tokenptr - ts->linebuf.base) : NULL; report.uclinebuf = linestr ? JS_GetStringChars(linestr) : NULL; report.uctokenptr = linestr ? report.uclinebuf + (tokenptr - ts->linebuf.base) : NULL; } else if (cg) { report.filename = cg->filename; report.lineno = cg->currentLine; } #if JS_HAS_ERROR_EXCEPTIONS /* * If there's a runtime exception type associated with this error * number, set that as the pending exception. For errors occuring at * compile time, this is very likely to be a JSEXN_SYNTAXERR. * * If an exception is thrown but not caught, the JSREPORT_EXCEPTION * flag will be set in report.flags. Proper behavior for an error * reporter is to ignore a report with this flag for all but top-level * compilation errors. The exception will remain pending, and so long * as the non-top-level "load", "eval", or "compile" native function * returns false, the top-level reporter will eventually receive the * uncaught exception report. * * XXX it'd probably be best if there was only one call to this * function, but there seem to be two error reporter call points. */ /* * Only try to raise an exception if there isn't one already set - * otherwise the exception will describe only the last compile error, * which is likely spurious. */ if (!(ts && (ts->flags & TSF_ERROR))) if (js_ErrorToException(cx, message, &report)) onError = NULL; /* * Suppress any compiletime errors that don't occur at the top level. * This may still fail, as interplevel may be zero in contexts where we * don't really want to call the error reporter, as when js is called * by other code which could catch the error. */ if (cx->interpLevel != 0) onError = NULL; #endif if (cx->runtime->debugErrorHook && onError) { JSDebugErrorHook hook = cx->runtime->debugErrorHook; /* test local in case debugErrorHook changed on another thread */ if (hook && !hook(cx, message, &report, cx->runtime->debugErrorHookData)) { onError = NULL; } } if (onError) (*onError)(cx, message, &report); } if (message) JS_free(cx, message); if (report.messageArgs) { int i = 0; while (report.messageArgs[i]) JS_free(cx, (void *)report.messageArgs[i++]); JS_free(cx, (void *)report.messageArgs); } if (report.ucmessage) JS_free(cx, (void *)report.ucmessage); js_RemoveRoot(cx->runtime, &linestr); if (ts && !JSREPORT_IS_WARNING(flags)) { /* Set the error flag to suppress spurious reports. */ ts->flags |= TSF_ERROR; } return warning; }