/[jscoverage]/trunk/js/json.cpp
ViewVC logotype

Contents of /trunk/js/json.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 26997 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3 *
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/
8 *
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
12 * License.
13 *
14 * The Original Code is SpiderMonkey JSON.
15 *
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.
20 *
21 * Contributor(s):
22 * Robert Sayre <sayrer@gmail.com>
23 *
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.
35 *
36 * ***** END LICENSE BLOCK ***** */
37
38
39 #include "jsapi.h"
40 #include "jsarena.h"
41 #include "jsarray.h"
42 #include "jsatom.h"
43 #include "jsbool.h"
44 #include "jscntxt.h"
45 #include "jsdtoa.h"
46 #include "jsinterp.h"
47 #include "jsiter.h"
48 #include "jsnum.h"
49 #include "jsobj.h"
50 #include "jsprf.h"
51 #include "jsscan.h"
52 #include "jsstr.h"
53 #include "jstypes.h"
54 #include "jsutil.h"
55
56 #include "json.h"
57
58 JSClass js_JSONClass = {
59 js_JSON_str,
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
64 };
65
66 JSBool
67 js_json_parse(JSContext *cx, uintN argc, jsval *vp)
68 {
69 JSString *s = NULL;
70 jsval *argv = vp + 2;
71
72 // Must throw an Error if there isn't a first arg
73 if (!JS_ConvertArguments(cx, argc, argv, "S", &s))
74 return JS_FALSE;
75
76
77 JSONParser *jp = js_BeginJSONParse(cx, vp);
78 JSBool ok = jp != NULL;
79
80 if (ok) {
81 ok = js_ConsumeJSONText(cx, jp, JS_GetStringChars(s), JS_GetStringLength(s));
82 ok &= js_FinishJSONParse(cx, jp);
83 }
84
85 if (!ok)
86 JS_ReportError(cx, "Error parsing JSON.");
87
88 return ok;
89 }
90
91 struct StringifyClosure
92 {
93 StringifyClosure(JSContext *aCx, jsval *str) : cx(aCx), s(str)
94 {
95 }
96
97 JSContext *cx;
98 jsval *s;
99 };
100
101 static
102 JSBool WriteCallback(const jschar *buf, uint32 len, void *data)
103 {
104 StringifyClosure *sc = static_cast<StringifyClosure*>(data);
105 JSString *s1 = JSVAL_TO_STRING(*sc->s);
106 JSString *s2 = JS_NewUCStringCopyN(sc->cx, buf, len);
107 if (!s2)
108 return JS_FALSE;
109
110 s1 = js_ConcatStrings(sc->cx, s1, s2);
111 if (!s1)
112 return JS_FALSE;
113
114 *sc->s = STRING_TO_JSVAL(s1);
115 return JS_TRUE;
116 }
117
118 JSBool
119 js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
120 {
121 JSObject *obj;
122 jsval *argv = vp + 2;
123
124 // Must throw an Error if there isn't a first arg
125 if (!JS_ConvertArguments(cx, argc, argv, "o", &obj))
126 return JS_FALSE;
127
128 // Only use objects and arrays as the root for now
129 jsval v = OBJECT_TO_JSVAL(obj);
130 JSBool ok = js_TryJSON(cx, &v);
131 JSType type;
132 if (!(ok && !JSVAL_IS_PRIMITIVE(v) &&
133 (type = JS_TypeOfValue(cx, v)) != JSTYPE_FUNCTION &&
134 type != JSTYPE_XML)) {
135 JS_ReportError(cx, "Invalid argument.");
136 return JS_FALSE;
137 }
138
139 JSString *s = JS_NewStringCopyN(cx, "", 0);
140 if (!s)
141 ok = JS_FALSE;
142
143 if (ok) {
144 jsval sv = STRING_TO_JSVAL(s);
145 StringifyClosure sc(cx, &sv);
146 JSAutoTempValueRooter tvr(cx, 1, sc.s);
147 ok = js_Stringify(cx, &v, NULL, &WriteCallback, &sc, 0);
148 *vp = *sc.s;
149 }
150
151 return ok;
152 }
153
154 JSBool
155 js_TryJSON(JSContext *cx, jsval *vp)
156 {
157 // Checks whether the return value implements toJSON()
158 JSBool ok = JS_TRUE;
159
160 if (!JSVAL_IS_PRIMITIVE(*vp)) {
161 JSObject *obj = JSVAL_TO_OBJECT(*vp);
162 ok = js_TryMethod(cx, obj, cx->runtime->atomState.toJSONAtom, 0, NULL, vp);
163 }
164
165 return ok;
166 }
167
168
169 static const jschar quote = jschar('"');
170 static const jschar backslash = jschar('\\');
171 static const jschar unicodeEscape[] = {'\\', 'u', '0', '0'};
172
173 static JSBool
174 write_string(JSContext *cx, JSONWriteCallback callback, void *data, const jschar *buf, uint32 len)
175 {
176 if (!callback(&quote, 1, data))
177 return JS_FALSE;
178
179 uint32 mark = 0;
180 uint32 i;
181 for (i = 0; i < len; ++i) {
182 if (buf[i] == quote || buf[i] == backslash) {
183 if (!callback(&buf[mark], i - mark, data) || !callback(&backslash, 1, data) ||
184 !callback(&buf[i], 1, data)) {
185 return JS_FALSE;
186 }
187 mark = i + 1;
188 } else if (buf[i] <= 31 || buf[i] == 127) {
189 if (!callback(&buf[mark], i - mark, data) || !callback(unicodeEscape, 4, data))
190 return JS_FALSE;
191 char ubuf[10];
192 unsigned int len = JS_snprintf(ubuf, sizeof(ubuf), "%.2x", buf[i]);
193 JS_ASSERT(len == 2);
194 // TODO: don't allocate a JSString just to inflate (js_InflateStringToBuffer on static?)
195 JSString *us = JS_NewStringCopyN(cx, ubuf, len);
196 if (!callback(JS_GetStringChars(us), len, data))
197 return JS_FALSE;
198 mark = i + 1;
199 }
200 }
201
202 if (mark < len && !callback(&buf[mark], len - mark, data))
203 return JS_FALSE;
204
205 if (!callback(&quote, 1, data))
206 return JS_FALSE;
207
208 return JS_TRUE;
209 }
210
211 JSBool
212 js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer,
213 JSONWriteCallback callback, void *data, uint32 depth)
214 {
215 if (depth > JSON_MAX_DEPTH)
216 return JS_FALSE; /* encoding error */
217
218 JSBool ok = JS_TRUE;
219 JSObject *obj = JSVAL_TO_OBJECT(*vp);
220 JSBool isArray = JS_IsArrayObject(cx, obj);
221 jschar output = jschar(isArray ? '[' : '{');
222 if (!callback(&output, 1, data))
223 return JS_FALSE;
224
225 JSObject *iterObj = NULL;
226 jsint i = 0;
227 jsuint length = 0;
228
229 if (isArray) {
230 if (!JS_GetArrayLength(cx, obj, &length))
231 return JS_FALSE;
232 } else {
233 if (!js_ValueToIterator(cx, JSITER_ENUMERATE, vp))
234 return JS_FALSE;
235 iterObj = JSVAL_TO_OBJECT(*vp);
236 }
237
238 jsval outputValue = JSVAL_VOID;
239 JSAutoTempValueRooter tvr(cx, 1, &outputValue);
240
241 jsval key;
242 JSBool memberWritten = JS_FALSE;
243 do {
244 outputValue = JSVAL_VOID;
245
246 if (isArray) {
247 if ((jsuint)i >= length)
248 break;
249 ok = JS_GetElement(cx, obj, i++, &outputValue);
250 } else {
251 ok = js_CallIteratorNext(cx, iterObj, &key);
252 if (!ok)
253 break;
254 if (key == JSVAL_HOLE)
255 break;
256
257 JSString *ks;
258 if (JSVAL_IS_STRING(key)) {
259 ks = JSVAL_TO_STRING(key);
260 } else {
261 ks = JS_ValueToString(cx, key);
262 if (!ks) {
263 ok = JS_FALSE;
264 break;
265 }
266 }
267
268 ok = JS_GetUCProperty(cx, obj, JS_GetStringChars(ks),
269 JS_GetStringLength(ks), &outputValue);
270 }
271
272 if (!ok)
273 break;
274
275 // if this is an array, holes are transmitted as null
276 if (isArray && outputValue == JSVAL_VOID) {
277 outputValue = JSVAL_NULL;
278 } else if (JSVAL_IS_OBJECT(outputValue)) {
279 ok = js_TryJSON(cx, &outputValue);
280 if (!ok)
281 break;
282 }
283
284 // elide undefined values
285 if (outputValue == JSVAL_VOID)
286 continue;
287
288 // output a comma unless this is the first member to write
289 if (memberWritten) {
290 output = jschar(',');
291 ok = callback(&output, 1, data);
292 if (!ok)
293 break;
294 }
295 memberWritten = JS_TRUE;
296
297 JSType type = JS_TypeOfValue(cx, outputValue);
298
299 // Can't encode these types, so drop them
300 if (type == JSTYPE_FUNCTION || type == JSTYPE_XML)
301 break;
302
303 // Be careful below, this string is weakly rooted.
304 JSString *s;
305
306 // If this isn't an array, we need to output a key
307 if (!isArray) {
308 s = JS_ValueToString(cx, key);
309 if (!s) {
310 ok = JS_FALSE;
311 break;
312 }
313
314 ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s));
315 if (!ok)
316 break;
317
318 output = jschar(':');
319 ok = callback(&output, 1, data);
320 if (!ok)
321 break;
322 }
323
324 if (!JSVAL_IS_PRIMITIVE(outputValue)) {
325 // recurse
326 ok = js_Stringify(cx, &outputValue, replacer, callback, data, depth + 1);
327 } else {
328 JSString *outputString;
329 s = JS_ValueToString(cx, outputValue);
330 if (!s) {
331 ok = JS_FALSE;
332 break;
333 }
334
335 if (type == JSTYPE_STRING) {
336 ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s));
337 if (!ok)
338 break;
339
340 continue;
341 }
342
343 if (type == JSTYPE_NUMBER) {
344 if (JSVAL_IS_DOUBLE(outputValue)) {
345 jsdouble d = *JSVAL_TO_DOUBLE(outputValue);
346 if (!JSDOUBLE_IS_FINITE(d))
347 outputString = JS_NewStringCopyN(cx, "null", 4);
348 else
349 outputString = s;
350 } else {
351 outputString = s;
352 }
353 } else if (type == JSTYPE_BOOLEAN) {
354 outputString = s;
355 } else if (JSVAL_IS_NULL(outputValue)) {
356 outputString = JS_NewStringCopyN(cx, "null", 4);
357 } else {
358 ok = JS_FALSE; // encoding error
359 break;
360 }
361
362 ok = callback(JS_GetStringChars(outputString), JS_GetStringLength(outputString), data);
363 }
364 } while (ok);
365
366 if (iterObj) {
367 // Always close the iterator, but make sure not to stomp on OK
368 ok &= js_CloseIterator(cx, *vp);
369 // encoding error or propagate? FIXME: Bug 408838.
370 }
371
372 if (!ok) {
373 JS_ReportError(cx, "Error during JSON encoding.");
374 return JS_FALSE;
375 }
376
377 output = jschar(isArray ? ']' : '}');
378 ok = callback(&output, 1, data);
379
380 return ok;
381 }
382
383 // helper to determine whether a character could be part of a number
384 static JSBool IsNumChar(jschar c)
385 {
386 return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E');
387 }
388
389 JSONParser *
390 js_BeginJSONParse(JSContext *cx, jsval *rootVal)
391 {
392 if (!cx)
393 return NULL;
394
395 JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
396 if (!arr)
397 return NULL;
398
399 JSONParser *jp = (JSONParser*) JS_malloc(cx, sizeof(JSONParser));
400 if (!jp)
401 return NULL;
402 jp->buffer = NULL;
403
404 jp->objectStack = arr;
405 if (!JS_AddRoot(cx, jp->objectStack))
406 goto bad;
407
408 jp->hexChar = 0;
409 jp->numHex = 0;
410 jp->statep = jp->stateStack;
411 *jp->statep = JSON_PARSE_STATE_INIT;
412 jp->rootVal = rootVal;
413 jp->objectKey = NULL;
414 jp->buffer = (JSStringBuffer*) JS_malloc(cx, sizeof(JSStringBuffer));
415 if (!jp->buffer)
416 goto bad;
417 js_InitStringBuffer(jp->buffer);
418
419 return jp;
420 bad:
421 JS_free(cx, jp->buffer);
422 JS_free(cx, jp);
423 return NULL;
424 }
425
426 JSBool
427 js_FinishJSONParse(JSContext *cx, JSONParser *jp)
428 {
429 if (!jp)
430 return JS_TRUE;
431
432 if (jp->buffer)
433 js_FinishStringBuffer(jp->buffer);
434
435 JS_free(cx, jp->buffer);
436 if (!JS_RemoveRoot(cx, jp->objectStack))
437 return JS_FALSE;
438 JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
439 JS_free(cx, jp);
440
441 return ok;
442 }
443
444
445 static JSBool
446 PushState(JSONParser *jp, JSONParserState state)
447 {
448 if (*jp->statep == JSON_PARSE_STATE_FINISHED)
449 return JS_FALSE; // extra input
450
451 jp->statep++;
452 if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack))
453 return JS_FALSE; // too deep
454
455 *jp->statep = state;
456
457 return JS_TRUE;
458 }
459
460 static JSBool
461 PopState(JSONParser *jp)
462 {
463 jp->statep--;
464 if (jp->statep < jp->stateStack) {
465 jp->statep = jp->stateStack;
466 return JS_FALSE;
467 }
468
469 if (*jp->statep == JSON_PARSE_STATE_INIT)
470 *jp->statep = JSON_PARSE_STATE_FINISHED;
471
472 return JS_TRUE;
473 }
474
475 static JSBool
476 PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
477 {
478 JSAutoTempValueRooter tvr(cx, 1, &value);
479
480 JSBool ok;
481 if (OBJ_IS_ARRAY(cx, parent)) {
482 jsuint len;
483 ok = JS_GetArrayLength(cx, parent, &len);
484 if (ok)
485 ok = JS_SetElement(cx, parent, len, &value);
486 } else {
487 ok = JS_DefineUCProperty(cx, parent, JS_GetStringChars(jp->objectKey),
488 JS_GetStringLength(jp->objectKey), value,
489 NULL, NULL, JSPROP_ENUMERATE);
490 }
491
492 return ok;
493 }
494
495 static JSBool
496 PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
497 {
498 jsuint len;
499 if (!JS_GetArrayLength(cx, jp->objectStack, &len))
500 return JS_FALSE;
501 if (len >= JSON_MAX_DEPTH)
502 return JS_FALSE; // decoding error
503
504 jsval v = OBJECT_TO_JSVAL(obj);
505
506 // Check if this is the root object
507 if (len == 0) {
508 *jp->rootVal = v;
509 if (!JS_SetElement(cx, jp->objectStack, 0, jp->rootVal))
510 return JS_FALSE;
511 return JS_TRUE;
512 }
513
514 jsval p;
515 if (!JS_GetElement(cx, jp->objectStack, len - 1, &p))
516 return JS_FALSE;
517 JS_ASSERT(JSVAL_IS_OBJECT(p));
518 JSObject *parent = JSVAL_TO_OBJECT(p);
519 if (!PushValue(cx, jp, parent, OBJECT_TO_JSVAL(obj)))
520 return JS_FALSE;
521
522 if (!JS_SetElement(cx, jp->objectStack, len, &v))
523 return JS_FALSE;
524
525 return JS_TRUE;
526 }
527
528 static JSBool
529 OpenObject(JSContext *cx, JSONParser *jp)
530 {
531 JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
532 if (!obj)
533 return JS_FALSE;
534
535 return PushObject(cx, jp, obj);
536 }
537
538 static JSBool
539 OpenArray(JSContext *cx, JSONParser *jp)
540 {
541 // Add an array to an existing array or object
542 JSObject *arr = JS_NewArrayObject(cx, 0, NULL);
543 if (!arr)
544 return JS_FALSE;
545
546 return PushObject(cx, jp, arr);
547 }
548
549 static JSBool
550 CloseObject(JSContext *cx, JSONParser *jp)
551 {
552 jsuint len;
553 if (!JS_GetArrayLength(cx, jp->objectStack, &len))
554 return JS_FALSE;
555 if (!JS_SetArrayLength(cx, jp->objectStack, len - 1))
556 return JS_FALSE;
557
558 return JS_TRUE;
559 }
560
561 static JSBool
562 CloseArray(JSContext *cx, JSONParser *jp)
563 {
564 return CloseObject(cx, jp);
565 }
566
567 static JSBool
568 HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
569 {
570 JSBool ok;
571 jsuint length;
572 if (!JS_GetArrayLength(cx, jp->objectStack, &length))
573 return JS_FALSE;
574
575 jsval o;
576 if (!JS_GetElement(cx, jp->objectStack, length - 1, &o))
577 return JS_FALSE;
578 JS_ASSERT(JSVAL_IS_OBJECT(o));
579 JSObject *obj = JSVAL_TO_OBJECT(o);
580
581 const jschar *ep;
582 double val;
583 if (!js_strtod(cx, buf, buf + len, &ep, &val) || ep != buf + len)
584 return JS_FALSE;
585
586 jsval numVal;
587 if (JS_NewNumberValue(cx, val, &numVal))
588 ok = PushValue(cx, jp, obj, numVal);
589 else
590 ok = JS_FALSE; // decode error
591
592 return ok;
593 }
594
595 static JSBool
596 HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
597 {
598 jsuint length;
599 if (!JS_GetArrayLength(cx, jp->objectStack, &length))
600 return JS_FALSE;
601
602 jsval o;
603 if (!JS_GetElement(cx, jp->objectStack, length - 1, &o))
604 return JS_FALSE;
605 JS_ASSERT(JSVAL_IS_OBJECT(o));
606 JSObject *obj = JSVAL_TO_OBJECT(o);
607
608 JSString *str = JS_NewUCStringCopyN(cx, buf, len);
609 if (!str)
610 return JS_FALSE;
611
612 return PushValue(cx, jp, obj, STRING_TO_JSVAL(str));
613 }
614
615 static JSBool
616 HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
617 {
618 jsval keyword;
619 JSTokenType tt = js_CheckKeyword(buf, len);
620 if (tt != TOK_PRIMARY)
621 return JS_FALSE;
622
623 if (buf[0] == 'n')
624 keyword = JSVAL_NULL;
625 else if (buf[0] == 't')
626 keyword = JSVAL_TRUE;
627 else if (buf[0] == 'f')
628 keyword = JSVAL_FALSE;
629 else
630 return JS_FALSE;
631
632 jsuint length;
633 if (!JS_GetArrayLength(cx, jp->objectStack, &length))
634 return JS_FALSE;
635
636 jsval o;
637 if (!JS_GetElement(cx, jp->objectStack, length - 1, &o))
638 return JS_FALSE;
639 JS_ASSERT(JSVAL_IS_OBJECT(o));
640 JSObject *obj = JSVAL_TO_OBJECT(o);
641
642 return PushValue(cx, jp, obj, keyword);
643 }
644
645 static JSBool
646 HandleData(JSContext *cx, JSONParser *jp, JSONDataType type, const jschar *buf, uint32 len)
647 {
648 JSBool ok = JS_FALSE;
649
650 switch (type) {
651 case JSON_DATA_STRING:
652 ok = HandleString(cx, jp, buf, len);
653 break;
654
655 case JSON_DATA_KEYSTRING:
656 jp->objectKey = JS_NewUCStringCopyN(cx, buf, len);
657 ok = JS_TRUE;
658 break;
659
660 case JSON_DATA_NUMBER:
661 ok = HandleNumber(cx, jp, buf, len);
662 break;
663
664 case JSON_DATA_KEYWORD:
665 ok = HandleKeyword(cx, jp, buf, len);
666 break;
667
668 default:
669 JS_NOT_REACHED("Should have a JSON data type");
670 }
671
672 js_FinishStringBuffer(jp->buffer);
673 js_InitStringBuffer(jp->buffer);
674
675 return ok;
676 }
677
678 JSBool
679 js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len)
680 {
681 uint32 i;
682
683 if (*jp->statep == JSON_PARSE_STATE_INIT) {
684 PushState(jp, JSON_PARSE_STATE_OBJECT_VALUE);
685 }
686
687 for (i = 0; i < len; i++) {
688 jschar c = data[i];
689 switch (*jp->statep) {
690 case JSON_PARSE_STATE_VALUE :
691 if (c == ']') {
692 // empty array
693 if (!PopState(jp))
694 return JS_FALSE;
695 if (*jp->statep != JSON_PARSE_STATE_ARRAY)
696 return JS_FALSE; // unexpected char
697 if (!CloseArray(cx, jp) || !PopState(jp))
698 return JS_FALSE;
699 break;
700 }
701
702 if (c == '}') {
703 // we should only find these in OBJECT_KEY state
704 return JS_FALSE; // unexpected failure
705 }
706
707 if (c == '"') {
708 *jp->statep = JSON_PARSE_STATE_STRING;
709 break;
710 }
711
712 if (IsNumChar(c)) {
713 *jp->statep = JSON_PARSE_STATE_NUMBER;
714 js_AppendChar(jp->buffer, c);
715 break;
716 }
717
718 if (JS7_ISLET(c)) {
719 *jp->statep = JSON_PARSE_STATE_KEYWORD;
720 js_AppendChar(jp->buffer, c);
721 break;
722 }
723
724 // fall through in case the value is an object or array
725 case JSON_PARSE_STATE_OBJECT_VALUE :
726 if (c == '{') {
727 *jp->statep = JSON_PARSE_STATE_OBJECT;
728 if (!OpenObject(cx, jp) || !PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
729 return JS_FALSE;
730 } else if (c == '[') {
731 *jp->statep = JSON_PARSE_STATE_ARRAY;
732 if (!OpenArray(cx, jp) || !PushState(jp, JSON_PARSE_STATE_VALUE))
733 return JS_FALSE;
734 } else if (!JS_ISXMLSPACE(c)) {
735 return JS_FALSE; // unexpected
736 }
737 break;
738
739 case JSON_PARSE_STATE_OBJECT :
740 if (c == '}') {
741 if (!CloseObject(cx, jp) || !PopState(jp))
742 return JS_FALSE;
743 } else if (c == ',') {
744 if (!PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
745 return JS_FALSE;
746 } else if (c == ']' || !JS_ISXMLSPACE(c)) {
747 return JS_FALSE; // unexpected
748 }
749 break;
750
751 case JSON_PARSE_STATE_ARRAY :
752 if (c == ']') {
753 if (!CloseArray(cx, jp) || !PopState(jp))
754 return JS_FALSE;
755 } else if (c == ',') {
756 if (!PushState(jp, JSON_PARSE_STATE_VALUE))
757 return JS_FALSE;
758 } else if (!JS_ISXMLSPACE(c)) {
759 return JS_FALSE; // unexpected
760 }
761 break;
762 case JSON_PARSE_STATE_OBJECT_PAIR :
763 if (c == '"') {
764 // we want to be waiting for a : when the string has been read
765 *jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
766 if (!PushState(jp, JSON_PARSE_STATE_STRING))
767 return JS_FALSE;
768 } else if (c == '}') {
769 // pop off the object pair state and the object state
770 if (!CloseObject(cx, jp) || !PopState(jp) || !PopState(jp))
771 return JS_FALSE;
772 } else if (c == ']' || !JS_ISXMLSPACE(c)) {
773 return JS_FALSE; // unexpected
774 }
775 break;
776 case JSON_PARSE_STATE_OBJECT_IN_PAIR:
777 if (c == ':') {
778 *jp->statep = JSON_PARSE_STATE_VALUE;
779 } else if (!JS_ISXMLSPACE(c)) {
780 return JS_FALSE; // unexpected
781 }
782 break;
783 case JSON_PARSE_STATE_STRING:
784 if (c == '"') {
785 if (!PopState(jp))
786 return JS_FALSE;
787 JSONDataType jdt;
788 if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
789 jdt = JSON_DATA_KEYSTRING;
790 } else {
791 jdt = JSON_DATA_STRING;
792 }
793 if (!HandleData(cx, jp, jdt, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)))
794 return JS_FALSE;
795 } else if (c == '\\') {
796 *jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
797 } else {
798 js_AppendChar(jp->buffer, c);
799 }
800 break;
801
802 case JSON_PARSE_STATE_STRING_ESCAPE:
803 switch(c) {
804 case '"':
805 case '\\':
806 case '/':
807 break;
808 case 'b' : c = '\b'; break;
809 case 'f' : c = '\f'; break;
810 case 'n' : c = '\n'; break;
811 case 'r' : c = '\r'; break;
812 case 't' : c = '\t'; break;
813 default :
814 if (c == 'u') {
815 jp->numHex = 0;
816 jp->hexChar = 0;
817 *jp->statep = JSON_PARSE_STATE_STRING_HEX;
818 continue;
819 } else {
820 return JS_FALSE; // unexpected
821 }
822 }
823
824 js_AppendChar(jp->buffer, c);
825 *jp->statep = JSON_PARSE_STATE_STRING;
826 break;
827 case JSON_PARSE_STATE_STRING_HEX:
828 if (('0' <= c) && (c <= '9'))
829 jp->hexChar = (jp->hexChar << 4) | (c - '0');
830 else if (('a' <= c) && (c <= 'f'))
831 jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
832 else if (('A' <= c) && (c <= 'F'))
833 jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
834 else
835 return JS_FALSE; // unexpected
836
837 if (++(jp->numHex) == 4) {
838 js_AppendChar(jp->buffer, jp->hexChar);
839 jp->hexChar = 0;
840 jp->numHex = 0;
841 *jp->statep = JSON_PARSE_STATE_STRING;
842 }
843 break;
844 case JSON_PARSE_STATE_KEYWORD:
845 if (JS7_ISLET(c)) {
846 js_AppendChar(jp->buffer, c);
847 } else {
848 // this character isn't part of the keyword, process it again
849 i--;
850 if(!PopState(jp))
851 return JS_FALSE;
852
853 if (!HandleData(cx, jp, JSON_DATA_KEYWORD, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)))
854 return JS_FALSE;
855 }
856 break;
857 case JSON_PARSE_STATE_NUMBER:
858 if (IsNumChar(c)) {
859 js_AppendChar(jp->buffer, c);
860 } else {
861 // this character isn't part of the number, process it again
862 i--;
863 if(!PopState(jp))
864 return JS_FALSE;
865 if (!HandleData(cx, jp, JSON_DATA_NUMBER, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer)))
866 return JS_FALSE;
867 }
868 break;
869 case JSON_PARSE_STATE_FINISHED:
870 if (!JS_ISXMLSPACE(c))
871 return JS_FALSE; // extra input
872
873 break;
874 default:
875 JS_NOT_REACHED("Invalid JSON parser state");
876 }
877 }
878
879 return JS_TRUE;
880 }
881
882 #if JS_HAS_TOSOURCE
883 static JSBool
884 json_toSource(JSContext *cx, uintN argc, jsval *vp)
885 {
886 *vp = ATOM_KEY(CLASS_ATOM(cx, JSON));
887 return JS_TRUE;
888 }
889 #endif
890
891 static JSFunctionSpec json_static_methods[] = {
892 #if JS_HAS_TOSOURCE
893 JS_FN(js_toSource_str, json_toSource, 0, 0),
894 #endif
895 JS_FN("parse", js_json_parse, 0, 0),
896 JS_FN("stringify", js_json_stringify, 0, 0),
897 JS_FS_END
898 };
899
900 JSObject *
901 js_InitJSONClass(JSContext *cx, JSObject *obj)
902 {
903 JSObject *JSON;
904
905 JSON = JS_NewObject(cx, &js_JSONClass, NULL, obj);
906 if (!JSON)
907 return NULL;
908 if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON),
909 JS_PropertyStub, JS_PropertyStub, JSPROP_ENUMERATE))
910 return NULL;
911
912 if (!JS_DefineFunctions(cx, JSON, json_static_methods))
913 return NULL;
914
915 return JSON;
916 }

  ViewVC Help
Powered by ViewVC 1.1.24