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

Annotation of /trunk/js/json.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (12 years 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 siliconforks 332 /* ***** 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