Example #1
0
/*
 * 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;
}
Example #2
0
JSAtom *
js_ValueToStringAtom(JSContext *cx, jsval v)
{
    JSString *str;

    str = js_ValueToString(cx, v);
    if (!str)
        return NULL;
    return js_AtomizeString(cx, str, 0);
}
Example #3
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;
}
Example #4
0
/*
 * 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);
}
Example #5
0
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;
}
Example #6
0
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;
}
Example #7
0
/*
 * 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;
}
Example #8
0
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;
}
Example #9
0
/*
 * 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;
}
Example #10
0
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);
}
Example #11
0
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;
}
Example #12
0
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;
}
Example #13
0
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('}');
}
Example #14
0
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;
}
Example #15
0
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;
}
Example #16
0
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;
}
Example #17
0
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;
}