Example #1
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_GetArrayLength(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 = JS_GetElement(cx, obj, i++, &outputValue);
        } 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;
            }
 
            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
        js_CloseIteratorState(cx, iterObj);
        // 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;
}
Example #2
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('}');
}