Bug 459065. JSON space patrol. r=gal
authorRobert Sayre <sayrer@gmail.com>
Wed, 08 Oct 2008 13:31:07 -0400
changeset 20181 dd18104921a94fe82e6d2638980038c7330c86b1
parent 20093 6ecedbcf4d050047b78a04cf778f0c190858320c
child 20182 e18564ab198d0dc150cf904c23d6bcf9235fde5a
push idunknown
push userunknown
push dateunknown
reviewersgal
bugs459065
milestone1.9.1b1pre
Bug 459065. JSON space patrol. r=gal
js/src/jsapi.cpp
js/src/jsapi.h
js/src/json.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5522,17 +5522,17 @@ JS_DecodeBytes(JSContext *cx, const char
 
 JS_PUBLIC_API(char *)
 JS_EncodeString(JSContext *cx, JSString *str)
 {
     return js_DeflateString(cx, JSSTRING_CHARS(str), JSSTRING_LENGTH(str));
 }
 
 JS_PUBLIC_API(JSBool)
-JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, 
+JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer,
              JSONWriteCallback callback, void *data)
 {
     CHECK_REQUEST(cx);
     return js_Stringify(cx, vp, replacer, callback, data, 0);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_TryJSON(JSContext *cx, jsval *vp)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2400,17 +2400,17 @@ JS_EncodeString(JSContext *cx, JSString 
  * JSON functions
  */
 typedef JSBool (* JSONWriteCallback)(const jschar *buf, uint32 len, void *data);
 
 /*
  * JSON.stringify as specificed by ES3.1 (draft)
  */
 JS_PUBLIC_API(JSBool)
-JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, 
+JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer,
              JSONWriteCallback callback, void *data);
 
 /*
  * Retrieve a toJSON function. If found, set vp to its result.
  */
 JS_PUBLIC_API(JSBool)
 JS_TryJSON(JSContext *cx, jsval *vp);
 
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -138,17 +138,17 @@ js_json_stringify(JSContext *cx, uintN a
     
     JSString *s = JS_NewStringCopyN(cx, "", 0);
     if (!s)
         ok = JS_FALSE;
 
     if (ok) {
         jsval sv = STRING_TO_JSVAL(s);
         StringifyClosure sc(cx, &sv);
-	JSAutoTempValueRooter tvr(cx, 1, sc.s); 
+        JSAutoTempValueRooter tvr(cx, 1, sc.s);
         ok = js_Stringify(cx, &v, NULL, &WriteCallback, &sc, 0);
         *vp = *sc.s;
     }
 
     return ok;
 }
 
 JSBool
@@ -216,17 +216,17 @@ js_Stringify(JSContext *cx, jsval *vp, J
         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 {
@@ -318,30 +318,30 @@ js_Stringify(JSContext *cx, jsval *vp, J
             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);
+            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);
@@ -376,34 +376,34 @@ js_Stringify(JSContext *cx, jsval *vp, J
 
     output = jschar(isArray ? ']' : '}');
     ok = callback(&output, 1, data);
 
     return ok;
 }
 
 // helper to determine whether a character could be part of a number
-static JSBool IsNumChar(jschar c) 
+static JSBool IsNumChar(jschar c)
 {
     return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E');
 }
 
 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;        
+        return NULL;
     jp->buffer = NULL;
 
     jp->objectStack = arr;
     if (!JS_AddRoot(cx, jp->objectStack))
         goto bad;
 
     jp->hexChar = 0;
     jp->numHex = 0;
@@ -426,27 +426,26 @@ bad:
 JSBool
 js_FinishJSONParse(JSContext *cx, JSONParser *jp)
 {
     if (!jp)
         return JS_TRUE;
 
     if (jp->buffer)
         js_FinishStringBuffer(jp->buffer);
-    
+
     JS_free(cx, jp->buffer);
     if (!JS_RemoveRoot(cx, jp->objectStack))
         return JS_FALSE;
     JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
     JS_free(cx, jp);
 
     return ok;
 }
 
-
 static JSBool
 PushState(JSONParser *jp, JSONParserState state)
 {
     if (*jp->statep == JSON_PARSE_STATE_FINISHED)
         return JS_FALSE; // extra input
 
     jp->statep++;
     if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack))
@@ -459,29 +458,29 @@ PushState(JSONParser *jp, JSONParserStat
 
 static JSBool
 PopState(JSONParser *jp)
 {
     jp->statep--;
     if (jp->statep < jp->stateStack) {
         jp->statep = jp->stateStack;
         return JS_FALSE;
-    } 
+    }
 
     if (*jp->statep == JSON_PARSE_STATE_INIT)
         *jp->statep = JSON_PARSE_STATE_FINISHED;
 
     return JS_TRUE;
 }
 
 static JSBool
 PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
 {
     JSAutoTempValueRooter tvr(cx, 1, &value);
-  
+ 
     JSBool ok;
     if (OBJ_IS_ARRAY(cx, parent)) {
         jsuint len;
         ok = JS_GetArrayLength(cx, parent, &len);
         if (ok)
             ok = JS_SetElement(cx, parent, len, &value);
     } else {
         ok = JS_DefineUCProperty(cx, parent, JS_GetStringChars(jp->objectKey),
@@ -500,17 +499,17 @@ PushObject(JSContext *cx, JSONParser *jp
         return JS_FALSE;
     if (len >= JSON_MAX_DEPTH)
         return JS_FALSE; // decoding error
 
     jsval v = OBJECT_TO_JSVAL(obj);
 
     // Check if this is the root object
     if (len == 0) {
-        *jp->rootVal = v;        
+        *jp->rootVal = v;
         if (!JS_SetElement(cx, jp->objectStack, 0, jp->rootVal))
             return JS_FALSE;
         return JS_TRUE;
     }
 
     jsval p;
     if (!JS_GetElement(cx, jp->objectStack, len - 1, &p))
         return JS_FALSE;
@@ -574,17 +573,17 @@ HandleNumber(JSContext *cx, JSONParser *
 
     jsval o;
     if (!JS_GetElement(cx, jp->objectStack, length - 1, &o))
         return JS_FALSE;
     JS_ASSERT(JSVAL_IS_OBJECT(o));
     JSObject *obj = JSVAL_TO_OBJECT(o);
 
     const jschar *ep;
-    double val;    
+    double val;
     if (!js_strtod(cx, buf, buf + len, &ep, &val) || ep != buf + len)
         return JS_FALSE;
 
     jsval numVal;
     if (JS_NewNumberValue(cx, val, &numVal))
         ok = PushValue(cx, jp, obj, numVal);
     else
         ok = JS_FALSE; // decode error
@@ -679,40 +678,40 @@ JSBool
 js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len)
 {
     uint32 i;
 
     if (*jp->statep == JSON_PARSE_STATE_INIT) {
         PushState(jp, JSON_PARSE_STATE_OBJECT_VALUE);
     }
 
-    for (i = 0; i < len; i++) {        
+    for (i = 0; i < len; i++) {
         jschar c = data[i];
         switch (*jp->statep) {
             case JSON_PARSE_STATE_VALUE :
                 if (c == ']') {
                     // empty array
                     if (!PopState(jp))
                         return JS_FALSE;
-                    if (*jp->statep != JSON_PARSE_STATE_ARRAY) 
+                    if (*jp->statep != JSON_PARSE_STATE_ARRAY)
                         return JS_FALSE; // unexpected char
                     if (!CloseArray(cx, jp) || !PopState(jp))
                         return JS_FALSE;
                     break;
                 }
 
                 if (c == '}') {
                     // we should only find these in OBJECT_KEY state
                     return JS_FALSE; // unexpected failure
                 }
 
                 if (c == '"') {
                     *jp->statep = JSON_PARSE_STATE_STRING;
                     break;
-                } 
+                }
 
                 if (IsNumChar(c)) {
                     *jp->statep = JSON_PARSE_STATE_NUMBER;
                     js_AppendChar(jp->buffer, c);
                     break;
                 }
 
                 if (JS7_ISLET(c)) {
@@ -732,17 +731,17 @@ js_ConsumeJSONText(JSContext *cx, JSONPa
                   if (!OpenArray(cx, jp) || !PushState(jp, JSON_PARSE_STATE_VALUE))
                       return JS_FALSE;
                 } else if (!JS_ISXMLSPACE(c)) {
                   return JS_FALSE; // unexpected
                 }
                 break;
 
             case JSON_PARSE_STATE_OBJECT :
-                if (c == '}') {                    
+                if (c == '}') {
                     if (!CloseObject(cx, jp) || !PopState(jp))
                         return JS_FALSE;
                 } else if (c == ',') {
                     if (!PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
                         return JS_FALSE;
                 } else if (c == ']' || !JS_ISXMLSPACE(c)) {
                     return JS_FALSE; // unexpected
                 }
@@ -754,37 +753,40 @@ js_ConsumeJSONText(JSContext *cx, JSONPa
                         return JS_FALSE;
                 } else if (c == ',') {
                     if (!PushState(jp, JSON_PARSE_STATE_VALUE))
                         return JS_FALSE;
                 } else if (!JS_ISXMLSPACE(c)) {
                     return JS_FALSE; // unexpected
                 }
                 break;
+
             case JSON_PARSE_STATE_OBJECT_PAIR :
                 if (c == '"') {
                     // we want to be waiting for a : when the string has been read
                     *jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
                     if (!PushState(jp, JSON_PARSE_STATE_STRING))
                         return JS_FALSE;
                 } else if (c == '}') {
                     // pop off the object pair state and the object state
                     if (!CloseObject(cx, jp) || !PopState(jp) || !PopState(jp))
                         return JS_FALSE;
                 } else if (c == ']' || !JS_ISXMLSPACE(c)) {
                   return JS_FALSE; // unexpected
                 }
                 break;
+
             case JSON_PARSE_STATE_OBJECT_IN_PAIR:
                 if (c == ':') {
                     *jp->statep = JSON_PARSE_STATE_VALUE;
                 } else if (!JS_ISXMLSPACE(c)) {
                     return JS_FALSE; // unexpected
                 }
                 break;
+
             case JSON_PARSE_STATE_STRING:
                 if (c == '"') {
                     if (!PopState(jp))
                         return JS_FALSE;
                     JSONDataType jdt;
                     if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
                         jdt = JSON_DATA_KEYSTRING;
                     } else {
@@ -793,19 +795,19 @@ js_ConsumeJSONText(JSContext *cx, JSONPa
                     if (!HandleData(cx, jp, jdt, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)))
                         return JS_FALSE;
                 } else if (c == '\\') {
                     *jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
                 } else {
                     js_AppendChar(jp->buffer, c);
                 }
                 break;
-        
+
             case JSON_PARSE_STATE_STRING_ESCAPE:
-                switch(c) {
+                switch (c) {
                     case '"':
                     case '\\':
                     case '/':
                         break;
                     case 'b' : c = '\b'; break;
                     case 'f' : c = '\f'; break;
                     case 'n' : c = '\n'; break;
                     case 'r' : c = '\r'; break;
@@ -819,16 +821,17 @@ js_ConsumeJSONText(JSContext *cx, JSONPa
                         } else {
                             return JS_FALSE; // unexpected
                         }
                 }
 
                 js_AppendChar(jp->buffer, c);
                 *jp->statep = JSON_PARSE_STATE_STRING;
                 break;
+
             case JSON_PARSE_STATE_STRING_HEX:
                 if (('0' <= c) && (c <= '9'))
                   jp->hexChar = (jp->hexChar << 4) | (c - '0');
                 else if (('a' <= c) && (c <= 'f'))
                   jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
                 else if (('A' <= c) && (c <= 'F'))
                   jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
                 else
@@ -836,46 +839,50 @@ js_ConsumeJSONText(JSContext *cx, JSONPa
 
                 if (++(jp->numHex) == 4) {
                     js_AppendChar(jp->buffer, jp->hexChar);
                     jp->hexChar = 0;
                     jp->numHex = 0;
                     *jp->statep = JSON_PARSE_STATE_STRING;
                 }
                 break;
+
             case JSON_PARSE_STATE_KEYWORD:
                 if (JS7_ISLET(c)) {
                     js_AppendChar(jp->buffer, c);
                 } else {
                     // this character isn't part of the keyword, process it again
                     i--;
-                    if(!PopState(jp))
+                    if (!PopState(jp))
                         return JS_FALSE;
-                
+
                     if (!HandleData(cx, jp, JSON_DATA_KEYWORD, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)))
                         return JS_FALSE;
                 }
                 break;
+
             case JSON_PARSE_STATE_NUMBER:
                 if (IsNumChar(c)) {
                     js_AppendChar(jp->buffer, c);
                 } else {
                     // this character isn't part of the number, process it again
                     i--;
-                    if(!PopState(jp))
+                    if (!PopState(jp))
                         return JS_FALSE;
                     if (!HandleData(cx, jp, JSON_DATA_NUMBER, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)))
                         return JS_FALSE;
                 }
                 break;
+
             case JSON_PARSE_STATE_FINISHED:
                 if (!JS_ISXMLSPACE(c))
                   return JS_FALSE; // extra input
 
                 break;
+
             default:
                 JS_NOT_REACHED("Invalid JSON parser state");
       }
     }
 
     return JS_TRUE;
 }