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

Annotation of /trunk/js/json.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (hide annotations)
Tue Dec 9 03:37:47 2008 UTC (11 years, 10 months ago) by siliconforks
File size: 27869 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

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

  ViewVC Help
Powered by ViewVC 1.1.24