1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is SpiderMonkey JSON.
16 * The Initial Developer of the Original Code is
17 * Mozilla Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1998-1999
19 * the Initial Developer. All Rights Reserved.
22 * Robert Sayre <sayrer@gmail.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
58 JSClass js_JSONClass
= {
60 JSCLASS_HAS_CACHED_PROTO(JSProto_JSON
),
61 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
62 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, JS_FinalizeStub
,
63 JSCLASS_NO_OPTIONAL_MEMBERS
67 js_json_parse(JSContext
*cx
, uintN argc
, jsval
*vp
)
72 // Must throw an Error if there isn't a first arg
73 if (!JS_ConvertArguments(cx
, argc
, argv
, "S", &s
))
77 JSONParser
*jp
= js_BeginJSONParse(cx
, vp
);
78 JSBool ok
= jp
!= NULL
;
81 ok
= js_ConsumeJSONText(cx
, jp
, JS_GetStringChars(s
), JS_GetStringLength(s
));
82 ok
&= js_FinishJSONParse(cx
, jp
);
86 JS_ReportError(cx
, "Error parsing JSON");
91 class StringifyClosure
: JSAutoTempValueRooter
94 StringifyClosure(JSContext
*cx
, size_t len
, jsval
*vec
)
95 : JSAutoTempValueRooter(cx
, len
, vec
), cx(cx
), s(vec
)
104 WriteCallback(const jschar
*buf
, uint32 len
, void *data
)
106 StringifyClosure
*sc
= static_cast<StringifyClosure
*>(data
);
107 JSString
*s1
= JSVAL_TO_STRING(sc
->s
[0]);
108 JSString
*s2
= js_NewStringCopyN(sc
->cx
, buf
, len
);
112 sc
->s
[1] = STRING_TO_JSVAL(s2
);
114 s1
= js_ConcatStrings(sc
->cx
, s1
, s2
);
118 sc
->s
[0] = STRING_TO_JSVAL(s1
);
119 sc
->s
[1] = JSVAL_VOID
;
125 js_json_stringify(JSContext
*cx
, uintN argc
, jsval
*vp
)
128 jsval
*argv
= vp
+ 2;
130 // Must throw an Error if there isn't a first arg
131 if (!JS_ConvertArguments(cx
, argc
, argv
, "o", &obj
))
134 // Only use objects and arrays as the root for now
135 *vp
= OBJECT_TO_JSVAL(obj
);
137 JSBool ok
= js_TryJSON(cx
, vp
);
140 JSVAL_IS_PRIMITIVE(*vp
) ||
141 ((type
= JS_TypeOfValue(cx
, *vp
)) == JSTYPE_FUNCTION
||
142 type
== JSTYPE_XML
)) {
143 JS_ReportError(cx
, "Invalid argument");
147 JSString
*s
= JS_NewStringCopyN(cx
, "", 0);
152 jsval vec
[2] = {STRING_TO_JSVAL(s
), JSVAL_VOID
};
153 StringifyClosure
sc(cx
, 2, vec
);
154 JSAutoTempValueRooter
resultTvr(cx
, 1, sc
.s
);
155 ok
= js_Stringify(cx
, vp
, NULL
, &WriteCallback
, &sc
, 0);
163 js_TryJSON(JSContext
*cx
, jsval
*vp
)
165 // Checks whether the return value implements toJSON()
168 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
169 JSObject
*obj
= JSVAL_TO_OBJECT(*vp
);
170 ok
= js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toJSONAtom
, 0, NULL
, vp
);
177 static const jschar quote
= jschar('"');
178 static const jschar backslash
= jschar('\\');
179 static const jschar unicodeEscape
[] = {'\\', 'u', '0', '0'};
182 write_string(JSContext
*cx
, JSONWriteCallback callback
, void *data
, const jschar
*buf
, uint32 len
)
184 if (!callback("e
, 1, data
))
189 for (i
= 0; i
< len
; ++i
) {
190 if (buf
[i
] == quote
|| buf
[i
] == backslash
) {
191 if (!callback(&buf
[mark
], i
- mark
, data
) || !callback(&backslash
, 1, data
) ||
192 !callback(&buf
[i
], 1, data
)) {
196 } else if (buf
[i
] <= 31 || buf
[i
] == 127) {
197 if (!callback(&buf
[mark
], i
- mark
, data
) || !callback(unicodeEscape
, 4, data
))
200 size_t len
= JS_snprintf(ubuf
, sizeof(ubuf
), "%.2x", buf
[i
]);
203 size_t wbufSize
= JS_ARRAY_LENGTH(wbuf
);
204 if (!js_InflateStringToBuffer(cx
, ubuf
, len
, wbuf
, &wbufSize
) ||
205 !callback(wbuf
, wbufSize
, data
)) {
212 if (mark
< len
&& !callback(&buf
[mark
], len
- mark
, data
))
215 if (!callback("e
, 1, data
))
222 js_Stringify(JSContext
*cx
, jsval
*vp
, JSObject
*replacer
,
223 JSONWriteCallback callback
, void *data
, uint32 depth
)
225 if (depth
> JSON_MAX_DEPTH
)
226 return JS_FALSE
; /* encoding error */
229 JSObject
*obj
= JSVAL_TO_OBJECT(*vp
);
230 JSBool isArray
= JS_IsArrayObject(cx
, obj
);
231 jschar output
= jschar(isArray
? '[' : '{');
232 if (!callback(&output
, 1, data
))
235 JSObject
*iterObj
= NULL
;
240 if (!js_GetLengthProperty(cx
, obj
, &length
))
243 if (!js_ValueToIterator(cx
, JSITER_ENUMERATE
, vp
))
245 iterObj
= JSVAL_TO_OBJECT(*vp
);
248 jsval outputValue
= JSVAL_VOID
;
249 JSAutoTempValueRooter
tvr(cx
, 1, &outputValue
);
252 JSBool memberWritten
= JS_FALSE
;
254 outputValue
= JSVAL_VOID
;
257 if ((jsuint
)i
>= length
)
259 ok
= OBJ_GET_PROPERTY(cx
, obj
, INT_TO_JSID(i
), &outputValue
);
262 ok
= js_CallIteratorNext(cx
, iterObj
, &key
);
265 if (key
== JSVAL_HOLE
)
269 if (JSVAL_IS_STRING(key
)) {
270 ks
= JSVAL_TO_STRING(key
);
272 ks
= js_ValueToString(cx
, key
);
279 ok
= JS_GetUCProperty(cx
, obj
, JS_GetStringChars(ks
),
280 JS_GetStringLength(ks
), &outputValue
);
286 // if this is an array, holes are transmitted as null
287 if (isArray
&& outputValue
== JSVAL_VOID
) {
288 outputValue
= JSVAL_NULL
;
289 } else if (JSVAL_IS_OBJECT(outputValue
)) {
290 ok
= js_TryJSON(cx
, &outputValue
);
295 // elide undefined values
296 if (outputValue
== JSVAL_VOID
)
299 // output a comma unless this is the first member to write
301 output
= jschar(',');
302 ok
= callback(&output
, 1, data
);
306 memberWritten
= JS_TRUE
;
308 JSType type
= JS_TypeOfValue(cx
, outputValue
);
310 // Can't encode these types, so drop them
311 if (type
== JSTYPE_FUNCTION
|| type
== JSTYPE_XML
)
314 // Be careful below, this string is weakly rooted.
317 // If this isn't an array, we need to output a key
319 s
= js_ValueToString(cx
, key
);
325 ok
= write_string(cx
, callback
, data
, JS_GetStringChars(s
), JS_GetStringLength(s
));
329 output
= jschar(':');
330 ok
= callback(&output
, 1, data
);
335 if (!JSVAL_IS_PRIMITIVE(outputValue
)) {
337 ok
= js_Stringify(cx
, &outputValue
, replacer
, callback
, data
, depth
+ 1);
339 JSString
*outputString
;
340 s
= js_ValueToString(cx
, outputValue
);
346 if (type
== JSTYPE_STRING
) {
347 ok
= write_string(cx
, callback
, data
, JS_GetStringChars(s
), JS_GetStringLength(s
));
354 if (type
== JSTYPE_NUMBER
) {
355 if (JSVAL_IS_DOUBLE(outputValue
)) {
356 jsdouble d
= *JSVAL_TO_DOUBLE(outputValue
);
357 if (!JSDOUBLE_IS_FINITE(d
))
358 outputString
= JS_NewStringCopyN(cx
, "null", 4);
364 } else if (type
== JSTYPE_BOOLEAN
) {
366 } else if (JSVAL_IS_NULL(outputValue
)) {
367 outputString
= JS_NewStringCopyN(cx
, "null", 4);
369 ok
= JS_FALSE
; // encoding error
378 ok
= callback(JS_GetStringChars(outputString
), JS_GetStringLength(outputString
), data
);
383 // Always close the iterator, but make sure not to stomp on OK
384 ok
&= js_CloseIterator(cx
, *vp
);
385 // encoding error or propagate? FIXME: Bug 408838.
389 JS_ReportError(cx
, "Error during JSON encoding");
393 output
= jschar(isArray
? ']' : '}');
394 ok
= callback(&output
, 1, data
);
399 // helper to determine whether a character could be part of a number
400 static JSBool
IsNumChar(jschar c
)
402 return ((c
<= '9' && c
>= '0') || c
== '.' || c
== '-' || c
== '+' || c
== 'e' || c
== 'E');
406 js_BeginJSONParse(JSContext
*cx
, jsval
*rootVal
)
411 JSObject
*arr
= js_NewArrayObject(cx
, 0, NULL
);
415 JSONParser
*jp
= (JSONParser
*) JS_malloc(cx
, sizeof(JSONParser
));
420 jp
->objectStack
= arr
;
421 if (!js_AddRoot(cx
, &jp
->objectStack
, "JSON parse stack"))
426 jp
->statep
= jp
->stateStack
;
427 *jp
->statep
= JSON_PARSE_STATE_INIT
;
428 jp
->rootVal
= rootVal
;
430 jp
->objectKey
= (JSStringBuffer
*) JS_malloc(cx
, sizeof(JSStringBuffer
));
433 js_InitStringBuffer(jp
->objectKey
);
435 jp
->buffer
= (JSStringBuffer
*) JS_malloc(cx
, sizeof(JSStringBuffer
));
438 js_InitStringBuffer(jp
->buffer
);
442 JS_free(cx
, jp
->buffer
);
448 js_FinishJSONParse(JSContext
*cx
, JSONParser
*jp
)
454 js_FinishStringBuffer(jp
->objectKey
);
455 JS_free(cx
, jp
->objectKey
);
458 js_FinishStringBuffer(jp
->buffer
);
459 JS_free(cx
, jp
->buffer
);
461 if (!js_RemoveRoot(cx
->runtime
, &jp
->objectStack
))
463 JSBool ok
= *jp
->statep
== JSON_PARSE_STATE_FINISHED
;
470 PushState(JSONParser
*jp
, JSONParserState state
)
472 if (*jp
->statep
== JSON_PARSE_STATE_FINISHED
)
473 return JS_FALSE
; // extra input
476 if ((uint32
)(jp
->statep
- jp
->stateStack
) >= JS_ARRAY_LENGTH(jp
->stateStack
))
477 return JS_FALSE
; // too deep
485 PopState(JSONParser
*jp
)
488 if (jp
->statep
< jp
->stateStack
) {
489 jp
->statep
= jp
->stateStack
;
493 if (*jp
->statep
== JSON_PARSE_STATE_INIT
)
494 *jp
->statep
= JSON_PARSE_STATE_FINISHED
;
500 PushValue(JSContext
*cx
, JSONParser
*jp
, JSObject
*parent
, jsval value
)
502 JSAutoTempValueRooter
tvr(cx
, 1, &value
);
505 if (OBJ_IS_ARRAY(cx
, parent
)) {
507 ok
= js_GetLengthProperty(cx
, parent
, &len
);
509 ok
= OBJ_DEFINE_PROPERTY(cx
, parent
, INT_TO_JSID(len
), value
,
510 NULL
, NULL
, JSPROP_ENUMERATE
, NULL
);
513 ok
= JS_DefineUCProperty(cx
, parent
, jp
->objectKey
->base
,
514 STRING_BUFFER_OFFSET(jp
->objectKey
), value
,
515 NULL
, NULL
, JSPROP_ENUMERATE
);
516 js_FinishStringBuffer(jp
->objectKey
);
517 js_InitStringBuffer(jp
->objectKey
);
524 PushObject(JSContext
*cx
, JSONParser
*jp
, JSObject
*obj
)
527 if (!js_GetLengthProperty(cx
, jp
->objectStack
, &len
))
529 if (len
>= JSON_MAX_DEPTH
)
530 return JS_FALSE
; // decoding error
532 jsval v
= OBJECT_TO_JSVAL(obj
);
533 JSAutoTempValueRooter
tvr(cx
, v
);
535 // Check if this is the root object
538 // This property must be enumerable to keep the array dense
539 if (!OBJ_DEFINE_PROPERTY(cx
, jp
->objectStack
, INT_TO_JSID(0), *jp
->rootVal
,
540 NULL
, NULL
, JSPROP_ENUMERATE
, NULL
)) {
547 if (!OBJ_GET_PROPERTY(cx
, jp
->objectStack
, INT_TO_JSID(len
- 1), &p
))
550 JS_ASSERT(JSVAL_IS_OBJECT(p
));
551 JSObject
*parent
= JSVAL_TO_OBJECT(p
);
552 if (!PushValue(cx
, jp
, parent
, OBJECT_TO_JSVAL(obj
)))
555 // This property must be enumerable to keep the array dense
556 if (!OBJ_DEFINE_PROPERTY(cx
, jp
->objectStack
, INT_TO_JSID(len
), v
,
557 NULL
, NULL
, JSPROP_ENUMERATE
, NULL
)) {
565 GetTopOfObjectStack(JSContext
*cx
, JSONParser
*jp
)
568 if (!js_GetLengthProperty(cx
, jp
->objectStack
, &length
))
572 if (!OBJ_GET_PROPERTY(cx
, jp
->objectStack
, INT_TO_JSID(length
- 1), &o
))
575 JS_ASSERT(!JSVAL_IS_PRIMITIVE(o
));
576 return JSVAL_TO_OBJECT(o
);
580 OpenObject(JSContext
*cx
, JSONParser
*jp
)
582 JSObject
*obj
= js_NewObject(cx
, &js_ObjectClass
, NULL
, NULL
, 0);
586 return PushObject(cx
, jp
, obj
);
590 OpenArray(JSContext
*cx
, JSONParser
*jp
)
592 // Add an array to an existing array or object
593 JSObject
*arr
= js_NewArrayObject(cx
, 0, NULL
);
597 return PushObject(cx
, jp
, arr
);
601 CloseObject(JSContext
*cx
, JSONParser
*jp
)
604 if (!js_GetLengthProperty(cx
, jp
->objectStack
, &len
))
606 if (!js_SetLengthProperty(cx
, jp
->objectStack
, len
- 1))
613 CloseArray(JSContext
*cx
, JSONParser
*jp
)
615 return CloseObject(cx
, jp
);
619 HandleNumber(JSContext
*cx
, JSONParser
*jp
, const jschar
*buf
, uint32 len
)
623 if (!js_strtod(cx
, buf
, buf
+ len
, &ep
, &val
) || ep
!= buf
+ len
)
628 JSObject
*obj
= GetTopOfObjectStack(cx
, jp
);
629 if (obj
&& JS_NewNumberValue(cx
, val
, &numVal
))
630 ok
= PushValue(cx
, jp
, obj
, numVal
);
632 ok
= JS_FALSE
; // decode error
638 HandleString(JSContext
*cx
, JSONParser
*jp
, const jschar
*buf
, uint32 len
)
640 JSObject
*obj
= GetTopOfObjectStack(cx
, jp
);
641 JSString
*str
= js_NewStringCopyN(cx
, buf
, len
);
645 return PushValue(cx
, jp
, obj
, STRING_TO_JSVAL(str
));
649 HandleKeyword(JSContext
*cx
, JSONParser
*jp
, const jschar
*buf
, uint32 len
)
652 JSTokenType tt
= js_CheckKeyword(buf
, len
);
653 if (tt
!= TOK_PRIMARY
)
657 keyword
= JSVAL_NULL
;
658 else if (buf
[0] == 't')
659 keyword
= JSVAL_TRUE
;
660 else if (buf
[0] == 'f')
661 keyword
= JSVAL_FALSE
;
665 JSObject
*obj
= GetTopOfObjectStack(cx
, jp
);
669 return PushValue(cx
, jp
, obj
, keyword
);
673 HandleData(JSContext
*cx
, JSONParser
*jp
, JSONDataType type
)
675 JSBool ok
= JS_FALSE
;
677 if (!STRING_BUFFER_OK(jp
->buffer
))
681 case JSON_DATA_STRING
:
682 ok
= HandleString(cx
, jp
, jp
->buffer
->base
, STRING_BUFFER_OFFSET(jp
->buffer
));
685 case JSON_DATA_KEYSTRING
:
686 js_AppendUCString(jp
->objectKey
, jp
->buffer
->base
, STRING_BUFFER_OFFSET(jp
->buffer
));
687 ok
= STRING_BUFFER_OK(jp
->objectKey
);
690 case JSON_DATA_NUMBER
:
691 ok
= HandleNumber(cx
, jp
, jp
->buffer
->base
, STRING_BUFFER_OFFSET(jp
->buffer
));
694 case JSON_DATA_KEYWORD
:
695 ok
= HandleKeyword(cx
, jp
, jp
->buffer
->base
, STRING_BUFFER_OFFSET(jp
->buffer
));
699 JS_NOT_REACHED("Should have a JSON data type");
702 js_FinishStringBuffer(jp
->buffer
);
703 js_InitStringBuffer(jp
->buffer
);
709 js_ConsumeJSONText(JSContext
*cx
, JSONParser
*jp
, const jschar
*data
, uint32 len
)
713 if (*jp
->statep
== JSON_PARSE_STATE_INIT
) {
714 PushState(jp
, JSON_PARSE_STATE_OBJECT_VALUE
);
717 for (i
= 0; i
< len
; i
++) {
719 switch (*jp
->statep
) {
720 case JSON_PARSE_STATE_VALUE
:
725 if (*jp
->statep
!= JSON_PARSE_STATE_ARRAY
)
726 return JS_FALSE
; // unexpected char
727 if (!CloseArray(cx
, jp
) || !PopState(jp
))
733 // we should only find these in OBJECT_KEY state
734 return JS_FALSE
; // unexpected failure
738 *jp
->statep
= JSON_PARSE_STATE_STRING
;
743 *jp
->statep
= JSON_PARSE_STATE_NUMBER
;
744 js_AppendChar(jp
->buffer
, c
);
749 *jp
->statep
= JSON_PARSE_STATE_KEYWORD
;
750 js_AppendChar(jp
->buffer
, c
);
754 // fall through in case the value is an object or array
755 case JSON_PARSE_STATE_OBJECT_VALUE
:
757 *jp
->statep
= JSON_PARSE_STATE_OBJECT
;
758 if (!OpenObject(cx
, jp
) || !PushState(jp
, JSON_PARSE_STATE_OBJECT_PAIR
))
760 } else if (c
== '[') {
761 *jp
->statep
= JSON_PARSE_STATE_ARRAY
;
762 if (!OpenArray(cx
, jp
) || !PushState(jp
, JSON_PARSE_STATE_VALUE
))
764 } else if (!JS_ISXMLSPACE(c
)) {
765 return JS_FALSE
; // unexpected
769 case JSON_PARSE_STATE_OBJECT
:
771 if (!CloseObject(cx
, jp
) || !PopState(jp
))
773 } else if (c
== ',') {
774 if (!PushState(jp
, JSON_PARSE_STATE_OBJECT_PAIR
))
776 } else if (c
== ']' || !JS_ISXMLSPACE(c
)) {
777 return JS_FALSE
; // unexpected
781 case JSON_PARSE_STATE_ARRAY
:
783 if (!CloseArray(cx
, jp
) || !PopState(jp
))
785 } else if (c
== ',') {
786 if (!PushState(jp
, JSON_PARSE_STATE_VALUE
))
788 } else if (!JS_ISXMLSPACE(c
)) {
789 return JS_FALSE
; // unexpected
793 case JSON_PARSE_STATE_OBJECT_PAIR
:
795 // we want to be waiting for a : when the string has been read
796 *jp
->statep
= JSON_PARSE_STATE_OBJECT_IN_PAIR
;
797 if (!PushState(jp
, JSON_PARSE_STATE_STRING
))
799 } else if (c
== '}') {
800 // pop off the object pair state and the object state
801 if (!CloseObject(cx
, jp
) || !PopState(jp
) || !PopState(jp
))
803 } else if (c
== ']' || !JS_ISXMLSPACE(c
)) {
804 return JS_FALSE
; // unexpected
808 case JSON_PARSE_STATE_OBJECT_IN_PAIR
:
810 *jp
->statep
= JSON_PARSE_STATE_VALUE
;
811 } else if (!JS_ISXMLSPACE(c
)) {
812 return JS_FALSE
; // unexpected
816 case JSON_PARSE_STATE_STRING
:
821 if (*jp
->statep
== JSON_PARSE_STATE_OBJECT_IN_PAIR
) {
822 jdt
= JSON_DATA_KEYSTRING
;
824 jdt
= JSON_DATA_STRING
;
826 if (!HandleData(cx
, jp
, jdt
))
828 } else if (c
== '\\') {
829 *jp
->statep
= JSON_PARSE_STATE_STRING_ESCAPE
;
831 js_AppendChar(jp
->buffer
, c
);
835 case JSON_PARSE_STATE_STRING_ESCAPE
:
841 case 'b' : c
= '\b'; break;
842 case 'f' : c
= '\f'; break;
843 case 'n' : c
= '\n'; break;
844 case 'r' : c
= '\r'; break;
845 case 't' : c
= '\t'; break;
850 *jp
->statep
= JSON_PARSE_STATE_STRING_HEX
;
853 return JS_FALSE
; // unexpected
857 js_AppendChar(jp
->buffer
, c
);
858 *jp
->statep
= JSON_PARSE_STATE_STRING
;
861 case JSON_PARSE_STATE_STRING_HEX
:
862 if (('0' <= c
) && (c
<= '9'))
863 jp
->hexChar
= (jp
->hexChar
<< 4) | (c
- '0');
864 else if (('a' <= c
) && (c
<= 'f'))
865 jp
->hexChar
= (jp
->hexChar
<< 4) | (c
- 'a' + 0x0a);
866 else if (('A' <= c
) && (c
<= 'F'))
867 jp
->hexChar
= (jp
->hexChar
<< 4) | (c
- 'A' + 0x0a);
869 return JS_FALSE
; // unexpected
871 if (++(jp
->numHex
) == 4) {
872 js_AppendChar(jp
->buffer
, jp
->hexChar
);
875 *jp
->statep
= JSON_PARSE_STATE_STRING
;
879 case JSON_PARSE_STATE_KEYWORD
:
881 js_AppendChar(jp
->buffer
, c
);
883 // this character isn't part of the keyword, process it again
888 if (!HandleData(cx
, jp
, JSON_DATA_KEYWORD
))
893 case JSON_PARSE_STATE_NUMBER
:
895 js_AppendChar(jp
->buffer
, c
);
897 // this character isn't part of the number, process it again
901 if (!HandleData(cx
, jp
, JSON_DATA_NUMBER
))
906 case JSON_PARSE_STATE_FINISHED
:
907 if (!JS_ISXMLSPACE(c
))
908 return JS_FALSE
; // extra input
913 JS_NOT_REACHED("Invalid JSON parser state");
922 json_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
924 *vp
= ATOM_KEY(CLASS_ATOM(cx
, JSON
));
929 static JSFunctionSpec json_static_methods
[] = {
931 JS_FN(js_toSource_str
, json_toSource
, 0, 0),
933 JS_FN("parse", js_json_parse
, 0, 0),
934 JS_FN("stringify", js_json_stringify
, 0, 0),
939 js_InitJSONClass(JSContext
*cx
, JSObject
*obj
)
943 JSON
= JS_NewObject(cx
, &js_JSONClass
, NULL
, obj
);
946 if (!JS_DefineProperty(cx
, obj
, js_JSON_str
, OBJECT_TO_JSVAL(JSON
),
947 JS_PropertyStub
, JS_PropertyStub
, 0))
950 if (!JS_DefineFunctions(cx
, JSON
, json_static_methods
))