/* * Convert to string. * * This method only uses JavaScript-modifiable properties name, message. It * is left to the host to check for private data and report filename and line * number information along with this message. */ static JSBool exn_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSString *name, *message, *result; jschar *chars, *cp; size_t name_length, message_length, length; if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v)) return JS_FALSE; name = js_ValueToString(cx, v); if (!name) return JS_FALSE; if (!JS_GetProperty(cx, obj, js_message_str, &v) || !(message = js_ValueToString(cx, v))) { return JS_FALSE; } if (JSSTRING_LENGTH(message) != 0) { name_length = JSSTRING_LENGTH(name); message_length = JSSTRING_LENGTH(message); length = name_length + message_length + 2; cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; js_strncpy(cp, JSSTRING_CHARS(name), name_length); cp += name_length; *cp++ = ':'; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(message), message_length); cp += message_length; *cp = 0; result = js_NewString(cx, chars, length, 0); if (!result) { JS_free(cx, chars); return JS_FALSE; } } else { result = name; } *rval = STRING_TO_JSVAL(result); return JS_TRUE; }
JSAtom * js_ValueToStringAtom(JSContext *cx, jsval v) { JSString *str; str = js_ValueToString(cx, v); if (!str) return NULL; return js_AtomizeString(cx, str, 0); }
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; }
/* * Perl-inspired join, reverse, and sort. */ static JSBool array_join(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSString *str; if (JSVAL_IS_VOID(argv[0])) return array_join_sub(cx, obj, &comma, JS_FALSE, rval, JS_FALSE); str = js_ValueToString(cx, argv[0]); if (!str) return JS_FALSE; argv[0] = STRING_TO_JSVAL(str); return array_join_sub(cx, obj, str, JS_FALSE, rval, JS_FALSE); }
static JSBool navigator_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { FILE *fOut; JSString *str; size_t n, i; jschar *s; if (JSVAL_IS_INT(id)) { switch (JSVAL_TO_INT(id)) { case navigator_appversion: fOut = fopen(SETTERLOG, "a"); if (fOut == NULL) return JS_FALSE; if (JSVAL_IS_INT(*vp)) { fprintf(fOut, "navigator set property read-only %s = %d\n", navigator_property_str[JSVAL_TO_INT(id)], JSVAL_TO_INT(*vp)); } else if (JSVAL_IS_STRING(*vp)) { str = js_ValueToString(cx, *vp); if (!str) return JS_FALSE; if (JSSTRING_IS_DEPENDENT(str)) { n = JSSTRDEP_LENGTH(str); s = JSSTRDEP_CHARS(str); } else { n = str->length; s = str->chars; } fprintf(fOut, "navigator set property read-only %s = \"", navigator_property_str[JSVAL_TO_INT(id)]); if (n != 0) { for (i = 0; i < n; i++) fputc(s[i], fOut); } fprintf(fOut, "\"\n"); } else { fprintf(fOut, "navigator set property read-only %s to value of unknown type %x\n", navigator_property_str[JSVAL_TO_INT(id)], JSVAL_TAG(*vp)); } fclose (fOut); break; } } return JS_TRUE; }
static JSBool screen_captureEvents (JSContext * cx, JSObject * obj, uintN argc, jsval * argv, jsval * rval) { JSString *str; size_t n, i; jschar *s; FILE *fOut, *fOutUC; str = js_ValueToString (cx, argv[0]); if (!str) return JS_FALSE; if (JSSTRING_IS_DEPENDENT (str)) { n = JSSTRDEP_LENGTH (str); s = JSSTRDEP_CHARS (str); } else { n = str->length; s = str->chars; } fOut = fopen (METHODLOG, "a"); if (fOut == NULL) return JS_FALSE; fprintf (fOut, "screen.captureEvents(\""); if (n != 0) { for (i = 0; i < n; i++) fputc (s[i], fOut); } fprintf (fOut, "\")\n"); fclose (fOut); return JS_TRUE; }
/* * Return a string that may eval to something similar to the original object. */ static JSBool exn_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jsval v; JSString *name, *message, *filename, *lineno_as_str, *result; int32 lineno; size_t lineno_length, name_length, message_length, filename_length, length; jschar *chars, *cp; if (!OBJ_GET_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.nameAtom, &v)) return JS_FALSE; name = js_ValueToString(cx, v); if (!name) return JS_FALSE; if (!JS_GetProperty(cx, obj, js_message_str, &v) || !(message = js_ValueToSource(cx, v))) { return JS_FALSE; } if (!JS_GetProperty(cx, obj, js_filename_str, &v) || !(filename = js_ValueToSource(cx, v))) { return JS_FALSE; } if (!JS_GetProperty(cx, obj, js_lineno_str, &v) || !js_ValueToInt32 (cx, v, &lineno)) { return JS_FALSE; } if (lineno != 0) { if (!(lineno_as_str = js_ValueToString(cx, v))) { return JS_FALSE; } lineno_length = JSSTRING_LENGTH(lineno_as_str); } else { lineno_as_str = NULL; lineno_length = 0; } /* Magic 8, for the characters in ``(new ())''. */ name_length = JSSTRING_LENGTH(name); message_length = JSSTRING_LENGTH(message); length = 8 + name_length + message_length; filename_length = JSSTRING_LENGTH(filename); if (filename_length != 0) { /* append filename as ``, {filename}'' */ length += 2 + filename_length; if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ length += 2 + lineno_length; } } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ length += 6 + lineno_length; } } cp = chars = (jschar*) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) return JS_FALSE; *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(name), name_length); cp += name_length; *cp++ = '('; if (message_length != 0) { js_strncpy(cp, JSSTRING_CHARS(message), message_length); cp += message_length; } if (filename_length != 0) { /* append filename as ``, {filename}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(filename), filename_length); cp += filename_length; } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; } } if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length); cp += lineno_length; } *cp++ = ')'; *cp++ = ')'; *cp = 0; result = js_NewString(cx, chars, length, 0); if (!result) { JS_free(cx, chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(result); return JS_TRUE; }
static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { JSBool ok; jsval pval; int32 lineno; JSString *message, *filename; if (cx->creatingException) return JS_FALSE; cx->creatingException = JS_TRUE; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, whose .name member is used by * js_NewObject to find the class prototype, we must get the class * prototype ourselves. */ ok = OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), (jsid)cx->runtime->atomState.classPrototypeAtom, &pval); if (!ok) goto out; obj = js_NewObject(cx, &ExceptionClass, JSVAL_TO_OBJECT(pval), NULL); if (!obj) { ok = JS_FALSE; goto out; } *rval = OBJECT_TO_JSVAL(obj); } /* * If it's a new object of class Exception, then null out the private * data so that the finalizer doesn't attempt to free it. */ if (OBJ_GET_CLASS(cx, obj) == &ExceptionClass) OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, JSVAL_VOID); /* Set the 'message' property. */ if (argc != 0) { message = js_ValueToString(cx, argv[0]); if (!message) { ok = JS_FALSE; goto out; } } else { message = cx->runtime->emptyString; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) { ok = JS_FALSE; goto out; } } else { filename = cx->runtime->emptyString; } /* Set the 'lineNumber' property. */ if (argc > 2) { ok = js_ValueToInt32(cx, argv[2], &lineno); if (!ok) goto out; } else { lineno = 0; } ok = InitExceptionObject(cx, obj, message, filename, lineno); out: cx->creatingException = JS_FALSE; return ok; }
/* * Return a string that may eval to something similar to the original object. */ static JSBool exn_toSource(JSContext *cx, uintN argc, jsval *vp) { JSObject *obj; JSString *name, *message, *filename, *lineno_as_str, *result; jsval localroots[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; JSTempValueRooter tvr; JSBool ok; uint32 lineno; size_t lineno_length, name_length, message_length, filename_length, length; jschar *chars, *cp; obj = JS_THIS_OBJECT(cx, vp); if (!obj || !OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.nameAtom), vp)) { return JS_FALSE; } name = js_ValueToString(cx, *vp); if (!name) return JS_FALSE; *vp = STRING_TO_JSVAL(name); /* After this, control must flow through label out: to exit. */ JS_PUSH_TEMP_ROOT(cx, 3, localroots, &tvr); #ifdef __GNUC__ message = filename = NULL; #endif ok = JS_GetProperty(cx, obj, js_message_str, &localroots[0]) && (message = js_ValueToSource(cx, localroots[0])); if (!ok) goto out; localroots[0] = STRING_TO_JSVAL(message); ok = JS_GetProperty(cx, obj, js_fileName_str, &localroots[1]) && (filename = js_ValueToSource(cx, localroots[1])); if (!ok) goto out; localroots[1] = STRING_TO_JSVAL(filename); ok = JS_GetProperty(cx, obj, js_lineNumber_str, &localroots[2]); if (!ok) goto out; lineno = js_ValueToECMAUint32 (cx, &localroots[2]); ok = !JSVAL_IS_NULL(localroots[2]); if (!ok) goto out; if (lineno != 0) { lineno_as_str = js_ValueToString(cx, localroots[2]); if (!lineno_as_str) { ok = JS_FALSE; goto out; } lineno_length = JSSTRING_LENGTH(lineno_as_str); } else { lineno_as_str = NULL; lineno_length = 0; } /* Magic 8, for the characters in ``(new ())''. */ name_length = JSSTRING_LENGTH(name); message_length = JSSTRING_LENGTH(message); length = 8 + name_length + message_length; filename_length = JSSTRING_LENGTH(filename); if (filename_length != 0) { /* append filename as ``, {filename}'' */ length += 2 + filename_length; if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ length += 2 + lineno_length; } } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ length += 6 + lineno_length; } } cp = chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar)); if (!chars) { ok = JS_FALSE; goto out; } *cp++ = '('; *cp++ = 'n'; *cp++ = 'e'; *cp++ = 'w'; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(name), name_length); cp += name_length; *cp++ = '('; if (message_length != 0) { js_strncpy(cp, JSSTRING_CHARS(message), message_length); cp += message_length; } if (filename_length != 0) { /* append filename as ``, {filename}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(filename), filename_length); cp += filename_length; } else { if (lineno_as_str) { /* * no filename, but have line number, * need to append ``, "", {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; *cp++ = '"'; *cp++ = '"'; } } if (lineno_as_str) { /* append lineno as ``, {lineno_as_str}'' */ *cp++ = ','; *cp++ = ' '; js_strncpy(cp, JSSTRING_CHARS(lineno_as_str), lineno_length); cp += lineno_length; } *cp++ = ')'; *cp++ = ')'; *cp = 0; result = js_NewString(cx, chars, length); if (!result) { JS_free(cx, chars); ok = JS_FALSE; goto out; } *vp = STRING_TO_JSVAL(result); ok = JS_TRUE; out: JS_POP_TEMP_ROOT(cx, &tvr); return ok; }
static JSBool Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { uint32 lineno; JSString *message, *filename; JSStackFrame *fp; if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) { /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, whose .name member is used by * js_NewObject to find the class prototype, we must get the class * prototype ourselves. */ if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(argv[-2]), ATOM_TO_JSID(cx->runtime->atomState .classPrototypeAtom), rval)) return JS_FALSE; obj = js_NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL, 0); if (!obj) return JS_FALSE; *rval = OBJECT_TO_JSVAL(obj); } /* * If it's a new object of class Exception, then null out the private * data so that the finalizer doesn't attempt to free it. */ if (OBJ_GET_CLASS(cx, obj) == &js_ErrorClass) STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, JSVAL_VOID); /* Set the 'message' property. */ if (argc != 0) { message = js_ValueToString(cx, argv[0]); if (!message) return JS_FALSE; argv[0] = STRING_TO_JSVAL(message); } else { message = cx->runtime->emptyString; } /* Set the 'fileName' property. */ if (argc > 1) { filename = js_ValueToString(cx, argv[1]); if (!filename) return JS_FALSE; argv[1] = STRING_TO_JSVAL(filename); fp = NULL; } else { fp = JS_GetScriptedCaller(cx, NULL); if (fp) { filename = FilenameToString(cx, fp->script->filename); if (!filename) return JS_FALSE; } else { filename = cx->runtime->emptyString; } } /* Set the 'lineNumber' property. */ if (argc > 2) { lineno = js_ValueToECMAUint32(cx, &argv[2]); if (JSVAL_IS_NULL(argv[2])) return JS_FALSE; } else { if (!fp) fp = JS_GetScriptedCaller(cx, NULL); lineno = (fp && fp->regs) ? js_PCToLineNumber(cx, fp->script, fp->regs->pc) : 0; } return (OBJ_GET_CLASS(cx, obj) != &js_ErrorClass) || InitExnPrivate(cx, obj, message, filename, lineno, NULL); }
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; }
static JSBool array_join_sub(JSContext *cx, JSObject *obj, JSString *sep, JSBool literalize, jsval *rval, JSBool localeString) { JSBool ok; jsval v; jsuint length, index; jschar *chars, *ochars; size_t nchars, growth, seplen, tmplen; const jschar *sepstr; JSString *str; JSHashEntry *he; JSObject *obj2; ok = js_GetLengthProperty(cx, obj, &length); if (!ok) return JS_FALSE; he = js_EnterSharpObject(cx, obj, NULL, &chars); if (!he) return JS_FALSE; if (literalize) { if (IS_SHARP(he)) { #if JS_HAS_SHARP_VARS nchars = js_strlen(chars); #else chars[0] = '['; chars[1] = ']'; chars[2] = 0; nchars = 2; #endif goto make_string; } /* * Allocate 1 + 3 + 1 for "[", the worst-case closing ", ]", and the * terminating 0. */ growth = (1 + 3 + 1) * sizeof(jschar); if (!chars) { nchars = 0; chars = (jschar *) malloc(growth); if (!chars) goto done; } else { MAKE_SHARP(he); nchars = js_strlen(chars); chars = (jschar *) realloc((ochars = chars), nchars * sizeof(jschar) + growth); if (!chars) { free(ochars); goto done; } } chars[nchars++] = '['; } else { /* * Free any sharp variable definition in chars. Normally, we would * MAKE_SHARP(he) so that only the first sharp variable annotation is * a definition, and all the rest are references, but in the current * case of (!literalize), we don't need chars at all. */ if (chars) JS_free(cx, chars); chars = NULL; nchars = 0; /* Return the empty string on a cycle as well as on empty join. */ if (IS_BUSY(he) || length == 0) { js_LeaveSharpObject(cx, NULL); *rval = JS_GetEmptyStringValue(cx); return ok; } /* Flag he as BUSY so we can distinguish a cycle from a join-point. */ MAKE_BUSY(he); } sepstr = NULL; seplen = JSSTRING_LENGTH(sep); v = JSVAL_NULL; for (index = 0; index < length; index++) { ok = JS_GetElement(cx, obj, index, &v); if (!ok) goto done; if (!literalize && (JSVAL_IS_VOID(v) || JSVAL_IS_NULL(v))) { str = cx->runtime->emptyString; } else { if (localeString) { if (!js_ValueToObject(cx, v, &obj2) || !js_TryMethod(cx, obj2, cx->runtime->atomState.toLocaleStringAtom, 0, NULL, &v)) { str = NULL; } else { str = js_ValueToString(cx, v); } } else { str = (literalize ? js_ValueToSource : js_ValueToString)(cx, v); } if (!str) { ok = JS_FALSE; goto done; } } /* Allocate 3 + 1 at end for ", ", closing bracket, and zero. */ growth = (nchars + (sepstr ? seplen : 0) + JSSTRING_LENGTH(str) + 3 + 1) * sizeof(jschar); if (!chars) { chars = (jschar *) malloc(growth); if (!chars) goto done; } else { chars = (jschar *) realloc((ochars = chars), growth); if (!chars) { free(ochars); goto done; } } if (sepstr) { js_strncpy(&chars[nchars], sepstr, seplen); nchars += seplen; } sepstr = JSSTRING_CHARS(sep); tmplen = JSSTRING_LENGTH(str); js_strncpy(&chars[nchars], JSSTRING_CHARS(str), tmplen); nchars += tmplen; } done: if (literalize) { if (chars) { if (JSVAL_IS_VOID(v)) { chars[nchars++] = ','; chars[nchars++] = ' '; } chars[nchars++] = ']'; } } else { CLEAR_BUSY(he); } js_LeaveSharpObject(cx, NULL); if (!ok) { if (chars) free(chars); return ok; } make_string: if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; } chars[nchars] = 0; str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; }
static JSBool JO(JSContext *cx, jsval *vp, StringifyContext *scx) { JSObject *obj = JSVAL_TO_OBJECT(*vp); if (!scx->cb.append('{')) return JS_FALSE; jsval vec[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL}; JSAutoTempValueRooter tvr(cx, 3, vec); jsval& key = vec[0]; jsval& outputValue = vec[1]; JSObject *iterObj = NULL; jsval *keySource = vp; bool usingWhitelist = false; // if the replacer is an array, we use the keys from it if (scx->replacer && JS_IsArrayObject(cx, scx->replacer)) { usingWhitelist = true; vec[2] = OBJECT_TO_JSVAL(scx->replacer); keySource = &vec[2]; } if (!js_ValueToIterator(cx, JSITER_ENUMERATE, keySource)) return JS_FALSE; iterObj = JSVAL_TO_OBJECT(*keySource); JSBool memberWritten = JS_FALSE; bool ok = false; while (true) { outputValue = JSVAL_VOID; if (!js_CallIteratorNext(cx, iterObj, &key)) goto error_break; if (key == JSVAL_HOLE) break; jsuint index = 0; if (usingWhitelist) { // skip non-index properties if (!js_IdIsIndex(key, &index)) continue; jsval newKey; if (!scx->replacer->getProperty(cx, key, &newKey)) goto error_break; key = newKey; } JSString *ks; if (JSVAL_IS_STRING(key)) { ks = JSVAL_TO_STRING(key); } else { ks = js_ValueToString(cx, key); if (!ks) goto error_break; } JSAutoTempValueRooter keyStringRoot(cx, ks); // Don't include prototype properties, since this operation is // supposed to be implemented as if by ES3.1 Object.keys() jsid id; jsval v = JS_FALSE; if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) || !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) { goto error_break; } if (v != JSVAL_TRUE) continue; if (!JS_GetPropertyById(cx, obj, id, &outputValue)) goto error_break; if (JSVAL_IS_OBJECT(outputValue) && !js_TryJSON(cx, &outputValue)) goto error_break; // call this here, so we don't write out keys if the replacer function // wants to elide the value. if (!CallReplacerFunction(cx, id, obj, scx, &outputValue)) goto error_break; JSType type = JS_TypeOfValue(cx, outputValue); // elide undefined values and functions and XML if (outputValue == JSVAL_VOID || type == JSTYPE_FUNCTION || type == JSTYPE_XML) continue; // output a comma unless this is the first member to write if (memberWritten && !scx->cb.append(',')) goto error_break; memberWritten = JS_TRUE; if (!WriteIndent(cx, scx, scx->depth)) goto error_break; // Be careful below, this string is weakly rooted JSString *s = js_ValueToString(cx, key); if (!s) goto error_break; const jschar *chars; size_t length; s->getCharsAndLength(chars, length); if (!write_string(cx, scx->cb, chars, length) || !scx->cb.append(':') || !Str(cx, id, obj, scx, &outputValue, false)) { goto error_break; } } ok = true; error_break: if (iterObj) { // Always close the iterator, but make sure not to stomp on OK JS_ASSERT(OBJECT_TO_JSVAL(iterObj) == *keySource); ok &= js_CloseIterator(cx, *keySource); } if (!ok) return JS_FALSE; if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1)) return JS_FALSE; return scx->cb.append('}'); }
JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn, *vp; JSObject *exnObject; void *mark; JSErrorReport *reportp, report; JSString *str; const char *bytes; JSBool ok; if (!JS_IsExceptionPending(cx)) return JS_TRUE; if (!JS_GetPendingException(cx, &exn)) return JS_FALSE; /* * Because js_ValueToString below could error and an exception object * could become unrooted, we must root exnObject. Later, if exnObject is * non-null, we need to root other intermediates, so allocate an operand * stack segment to protect all of these values. */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; vp = NULL; #ifdef __GNUC__ /* suppress bogus gcc warnings */ mark = NULL; #endif } else { exnObject = JSVAL_TO_OBJECT(exn); vp = js_AllocStack(cx, 5, &mark); if (!vp) { ok = JS_FALSE; goto out; } vp[0] = exn; } #if JS_HAS_ERROR_EXCEPTIONS reportp = js_ErrorFromException(cx, exn); #else reportp = NULL; #endif /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, exn); if (!str) { bytes = "unknown (can't convert to string)"; } else { if (vp) vp[1] = STRING_TO_JSVAL(str); bytes = js_GetStringBytes(str); } ok = JS_TRUE; if (!reportp && exnObject && OBJ_GET_CLASS(cx, exnObject) == &ExceptionClass) { const char *filename; uint32 lineno; ok = JS_GetProperty(cx, exnObject, js_message_str, &vp[2]); if (!ok) goto out; if (JSVAL_IS_STRING(vp[2])) bytes = JS_GetStringBytes(JSVAL_TO_STRING(vp[2])); ok = JS_GetProperty(cx, exnObject, js_filename_str, &vp[3]); if (!ok) goto out; str = js_ValueToString(cx, vp[3]); if (!str) { ok = JS_FALSE; goto out; } filename = StringToFilename(cx, str); ok = JS_GetProperty(cx, exnObject, js_lineno_str, &vp[4]); if (!ok) goto out; ok = js_ValueToECMAUint32 (cx, vp[4], &lineno); if (!ok) goto out; reportp = &report; memset(&report, 0, sizeof report); report.filename = filename; report.lineno = (uintN) lineno; } if (!reportp) { 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); } JS_ClearPendingException(cx); out: if (exnObject) js_FreeStack(cx, mark); return ok; }
JSBool js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { jschar *chars; size_t nchars; char *clazz, *prefix; JSString *str; #if JS_HAS_OBJECT_LITERAL if (cx->version >= JSVERSION_1_2) { JSProperty *list, *prop; JSBool ok; char *comma, *idquote, *valquote; jsval id, val; JSString *idstr, *valstr; #if JS_HAS_SHARP_VARS PRHashEntry *he; #endif /* Early returns after this must unlock, or goto done with ok set. */ JS_LOCK(cx); list = obj->map->props; #if JS_HAS_SHARP_VARS he = js_EnterSharpObject(cx, obj, &chars); if (!he) { JS_UNLOCK(cx); return JS_FALSE; } if (IS_SHARP(he)) { /* we didn't enter -- obj is already sharp */ JS_UNLOCK(cx); nchars = js_strlen(chars); goto make_string; } MAKE_SHARP(he); #else /* Shut off recursion by temporarily clearing obj's property list. */ obj->map->props = NULL; chars = NULL; #endif ok = JS_TRUE; /* Allocate 2 + 1 for "{}" and the terminator. */ if (!chars) { chars = malloc((2 + 1) * sizeof(jschar)); nchars = 0; } else { nchars = js_strlen(chars); chars = realloc(chars, (nchars + 2 + 1) * sizeof(jschar)); } if (!chars) goto done; chars[nchars++] = '{'; comma = NULL; for (prop = list; prop; prop = prop->next) { if (!prop->symbols || !(prop->flags & JSPROP_ENUMERATE)) continue; /* Get strings for id and val and GC-root them via argv. */ id = js_IdToValue(sym_id(prop->symbols)); idstr = js_ValueToString(cx, id); if (idstr) argv[0] = STRING_TO_JSVAL(idstr); val = prop->object->slots[prop->slot]; valstr = js_ValueToString(cx, val); if (!idstr || !valstr) { ok = JS_FALSE; goto done; } argv[1] = STRING_TO_JSVAL(valstr); /* If id is a non-identifier string, it needs to be quoted. */ if (JSVAL_IS_STRING(id) && !js_IsIdentifier(idstr)) { idquote = "'"; idstr = js_EscapeString(cx, idstr, *idquote); if (!idstr) { ok = JS_FALSE; goto done; } argv[0] = STRING_TO_JSVAL(idstr); } else { idquote = NULL; } /* Same for val, except it can't be an identifier. */ if (JSVAL_IS_STRING(val)) { valquote = "\""; valstr = js_EscapeString(cx, valstr, *valquote); if (!valstr) { ok = JS_FALSE; goto done; } argv[1] = STRING_TO_JSVAL(valstr); } else { valquote = NULL; } /* Allocate 1 + 1 at end for closing brace and terminating 0. */ chars = realloc(chars, (nchars + (comma ? 2 : 0) + (idquote ? 2 : 0) + idstr->length + 1 + (valquote ? 2 : 0) + valstr->length + 1 + 1) * sizeof(jschar)); if (!chars) goto done; if (comma) { chars[nchars++] = comma[0]; chars[nchars++] = comma[1]; } comma = ", "; if (idquote) chars[nchars++] = *idquote; js_strncpy(&chars[nchars], idstr->chars, idstr->length); nchars += idstr->length; if (idquote) chars[nchars++] = *idquote; chars[nchars++] = ':'; if (valquote) chars[nchars++] = *valquote; js_strncpy(&chars[nchars], valstr->chars, valstr->length); nchars += valstr->length; if (valquote) chars[nchars++] = *valquote; } done: if (chars) { chars[nchars++] = '}'; chars[nchars] = 0; } #if JS_HAS_SHARP_VARS js_LeaveSharpObject(cx); #else /* Restore obj's property list before bailing on error. */ obj->map->props = list; #endif JS_UNLOCK(cx); if (!ok) { if (chars) free(chars); return ok; } } else #endif /* JS_HAS_OBJECT_LITERAL */ { clazz = obj->map->clasp->name; nchars = 9 + strlen(clazz); /* 9 for "[object ]" */ chars = malloc((nchars + 1) * sizeof(jschar)); if (chars) { prefix = "[object "; nchars = 0; while ((chars[nchars] = (jschar)*prefix) != 0) nchars++, prefix++; while ((chars[nchars] = (jschar)*clazz) != 0) nchars++, clazz++; chars[nchars++] = ']'; chars[nchars] = 0; } } #if JS_HAS_SHARP_VARS make_string: #endif if (!chars) { JS_ReportOutOfMemory(cx); return JS_FALSE; } str = js_NewString(cx, chars, nchars, 0); if (!str) { free(chars); return JS_FALSE; } *rval = STRING_TO_JSVAL(str); return JS_TRUE; }
JSBool js_ReportUncaughtException(JSContext *cx) { jsval exn; JSObject *exnObject; jsval roots[5]; JSTempValueRooter tvr; JSErrorReport *reportp, report; JSString *str; const char *bytes; JSBool ok; if (!JS_IsExceptionPending(cx)) return JS_TRUE; if (!JS_GetPendingException(cx, &exn)) return JS_FALSE; memset(roots, 0, sizeof roots); JS_PUSH_TEMP_ROOT(cx, JS_ARRAY_LENGTH(roots), roots, &tvr); /* * Because js_ValueToString below could error and an exception object * could become unrooted, we must root exnObject. Later, if exnObject is * non-null, we need to root other intermediates, so allocate an operand * stack segment to protect all of these values. */ if (JSVAL_IS_PRIMITIVE(exn)) { exnObject = NULL; } else { exnObject = JSVAL_TO_OBJECT(exn); roots[0] = exn; } JS_ClearPendingException(cx); reportp = js_ErrorFromException(cx, exn); /* XXX L10N angels cry once again (see also jsemit.c, /L10N gaffes/) */ str = js_ValueToString(cx, exn); if (!str) { bytes = "unknown (can't convert to string)"; } else { roots[1] = STRING_TO_JSVAL(str); bytes = js_GetStringBytes(cx, str); if (!bytes) { ok = JS_FALSE; goto out; } } ok = JS_TRUE; if (!reportp && exnObject && OBJ_GET_CLASS(cx, exnObject) == &js_ErrorClass) { const char *filename; uint32 lineno; ok = JS_GetProperty(cx, exnObject, js_message_str, &roots[2]); if (!ok) goto out; if (JSVAL_IS_STRING(roots[2])) { bytes = js_GetStringBytes(cx, JSVAL_TO_STRING(roots[2])); if (!bytes) { ok = JS_FALSE; goto out; } } ok = JS_GetProperty(cx, exnObject, js_fileName_str, &roots[3]); if (!ok) goto out; str = js_ValueToString(cx, roots[3]); if (!str) { ok = JS_FALSE; goto out; } filename = StringToFilename(cx, str); if (!filename) { ok = JS_FALSE; goto out; } ok = JS_GetProperty(cx, exnObject, js_lineNumber_str, &roots[4]); if (!ok) goto out; lineno = js_ValueToECMAUint32 (cx, &roots[4]); ok = !JSVAL_IS_NULL(roots[4]); if (!ok) goto out; reportp = &report; memset(&report, 0, sizeof report); report.filename = filename; report.lineno = (uintN) lineno; } if (!reportp) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNCAUGHT_EXCEPTION, bytes); } else { /* Flag the error as an exception. */ reportp->flags |= JSREPORT_EXCEPTION; /* Pass the exception object. */ JS_SetPendingException(cx, exn); js_ReportErrorAgain(cx, bytes, reportp); JS_ClearPendingException(cx); } out: JS_POP_TEMP_ROOT(cx, &tvr); return ok; }
JSBool js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, JSONWriteCallback callback, void *data, uint32 depth) { if (depth > JSON_MAX_DEPTH) return JS_FALSE; /* encoding error */ JSBool ok = JS_TRUE; JSObject *obj = JSVAL_TO_OBJECT(*vp); JSBool isArray = JS_IsArrayObject(cx, obj); jschar output = jschar(isArray ? '[' : '{'); if (!callback(&output, 1, data)) return JS_FALSE; JSObject *iterObj = NULL; jsint i = 0; jsuint length = 0; if (isArray) { if (!js_GetLengthProperty(cx, obj, &length)) return JS_FALSE; } else { if (!js_ValueToIterator(cx, JSITER_ENUMERATE, vp)) return JS_FALSE; iterObj = JSVAL_TO_OBJECT(*vp); } jsval outputValue = JSVAL_VOID; JSAutoTempValueRooter tvr(cx, 1, &outputValue); jsval key; JSBool memberWritten = JS_FALSE; do { outputValue = JSVAL_VOID; if (isArray) { if ((jsuint)i >= length) break; ok = OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(i), &outputValue); i++; } else { ok = js_CallIteratorNext(cx, iterObj, &key); if (!ok) break; if (key == JSVAL_HOLE) break; JSString *ks; if (JSVAL_IS_STRING(key)) { ks = JSVAL_TO_STRING(key); } else { ks = js_ValueToString(cx, key); if (!ks) { ok = JS_FALSE; break; } } ok = JS_GetUCProperty(cx, obj, JS_GetStringChars(ks), JS_GetStringLength(ks), &outputValue); } if (!ok) break; // if this is an array, holes are transmitted as null if (isArray && outputValue == JSVAL_VOID) { outputValue = JSVAL_NULL; } else if (JSVAL_IS_OBJECT(outputValue)) { ok = js_TryJSON(cx, &outputValue); if (!ok) break; } // elide undefined values if (outputValue == JSVAL_VOID) continue; // output a comma unless this is the first member to write if (memberWritten) { output = jschar(','); ok = callback(&output, 1, data); if (!ok) break; } memberWritten = JS_TRUE; JSType type = JS_TypeOfValue(cx, outputValue); // Can't encode these types, so drop them if (type == JSTYPE_FUNCTION || type == JSTYPE_XML) break; // Be careful below, this string is weakly rooted. JSString *s; // If this isn't an array, we need to output a key if (!isArray) { s = js_ValueToString(cx, key); if (!s) { ok = JS_FALSE; break; } ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s)); if (!ok) break; output = jschar(':'); ok = callback(&output, 1, data); if (!ok) break; } if (!JSVAL_IS_PRIMITIVE(outputValue)) { // recurse ok = js_Stringify(cx, &outputValue, replacer, callback, data, depth + 1); } else { JSString *outputString; s = js_ValueToString(cx, outputValue); if (!s) { ok = JS_FALSE; break; } if (type == JSTYPE_STRING) { ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s)); if (!ok) break; continue; } if (type == JSTYPE_NUMBER) { if (JSVAL_IS_DOUBLE(outputValue)) { jsdouble d = *JSVAL_TO_DOUBLE(outputValue); if (!JSDOUBLE_IS_FINITE(d)) outputString = JS_NewStringCopyN(cx, "null", 4); else outputString = s; } else { outputString = s; } } else if (type == JSTYPE_BOOLEAN) { outputString = s; } else if (JSVAL_IS_NULL(outputValue)) { outputString = JS_NewStringCopyN(cx, "null", 4); } else { ok = JS_FALSE; // encoding error break; } if (!outputString) { ok = JS_FALSE; break; } ok = callback(JS_GetStringChars(outputString), JS_GetStringLength(outputString), data); } } while (ok); if (iterObj) { // Always close the iterator, but make sure not to stomp on OK ok &= js_CloseIterator(cx, *vp); // encoding error or propagate? FIXME: Bug 408838. } if (!ok) { JS_ReportError(cx, "Error during JSON encoding"); return JS_FALSE; } output = jschar(isArray ? ']' : '}'); ok = callback(&output, 1, data); return ok; }