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

Annotation of /trunk/js/json.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (hide annotations)
Sat Sep 26 23:15:22 2009 UTC (11 years ago) by siliconforks
File size: 39350 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

1 siliconforks 460 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2     * vim: set ts=8 sw=4 et tw=99:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5 siliconforks 332 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
7     * The contents of this file are subject to the Mozilla Public License Version
8     * 1.1 (the "License"); you may not use this file except in compliance with
9     * the License. You may obtain a copy of the License at
10     * http://www.mozilla.org/MPL/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is SpiderMonkey JSON.
18     *
19     * The Initial Developer of the Original Code is
20     * Mozilla Corporation.
21     * Portions created by the Initial Developer are Copyright (C) 1998-1999
22     * the Initial Developer. All Rights Reserved.
23     *
24     * Contributor(s):
25     * Robert Sayre <sayrer@gmail.com>
26     *
27     * Alternatively, the contents of this file may be used under the terms of
28     * either of the GNU General Public License Version 2 or later (the "GPL"),
29     * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30     * in which case the provisions of the GPL or the LGPL are applicable instead
31     * of those above. If you wish to allow use of your version of this file only
32     * under the terms of either the GPL or the LGPL, and not to allow others to
33     * use your version of this file under the terms of the MPL, indicate your
34     * decision by deleting the provisions above and replace them with the notice
35     * and other provisions required by the GPL or the LGPL. If you do not delete
36     * the provisions above, a recipient may use your version of this file under
37     * the terms of any one of the MPL, the GPL or the LGPL.
38     *
39     * ***** END LICENSE BLOCK ***** */
40    
41 siliconforks 460 #include <string.h> /* memset */
42 siliconforks 332 #include "jsapi.h"
43     #include "jsarena.h"
44     #include "jsarray.h"
45     #include "jsatom.h"
46     #include "jsbool.h"
47     #include "jscntxt.h"
48     #include "jsdtoa.h"
49 siliconforks 460 #include "jsfun.h"
50 siliconforks 332 #include "jsinterp.h"
51     #include "jsiter.h"
52     #include "jsnum.h"
53     #include "jsobj.h"
54     #include "jsprf.h"
55     #include "jsscan.h"
56     #include "jsstr.h"
57     #include "jstypes.h"
58     #include "jsutil.h"
59 siliconforks 460 #include "jsxml.h"
60 siliconforks 332
61     #include "json.h"
62    
63     JSClass js_JSONClass = {
64     js_JSON_str,
65     JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
66     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
67     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
68     JSCLASS_NO_OPTIONAL_MEMBERS
69     };
70    
71     JSBool
72     js_json_parse(JSContext *cx, uintN argc, jsval *vp)
73     {
74     JSString *s = NULL;
75     jsval *argv = vp + 2;
76 siliconforks 460 jsval reviver = JSVAL_NULL;
77     JSAutoTempValueRooter(cx, 1, &reviver);
78    
79     if (!JS_ConvertArguments(cx, argc, argv, "S / v", &s, &reviver))
80 siliconforks 332 return JS_FALSE;
81    
82     JSONParser *jp = js_BeginJSONParse(cx, vp);
83     JSBool ok = jp != NULL;
84     if (ok) {
85     ok = js_ConsumeJSONText(cx, jp, JS_GetStringChars(s), JS_GetStringLength(s));
86 siliconforks 460 ok &= js_FinishJSONParse(cx, jp, reviver);
87 siliconforks 332 }
88    
89     return ok;
90     }
91    
92 siliconforks 460 class WriterContext
93 siliconforks 332 {
94 siliconforks 399 public:
95 siliconforks 460 WriterContext(JSContext *cx) : cx(cx), didWrite(JS_FALSE)
96 siliconforks 332 {
97 siliconforks 460 js_InitStringBuffer(&sb);
98 siliconforks 332 }
99    
100 siliconforks 460 ~WriterContext()
101     {
102     js_FinishStringBuffer(&sb);
103     }
104    
105     JSStringBuffer sb;
106 siliconforks 332 JSContext *cx;
107 siliconforks 460 JSBool didWrite;
108 siliconforks 332 };
109    
110 siliconforks 399 static JSBool
111     WriteCallback(const jschar *buf, uint32 len, void *data)
112 siliconforks 332 {
113 siliconforks 460 WriterContext *wc = static_cast<WriterContext*>(data);
114     wc->didWrite = JS_TRUE;
115 siliconforks 332
116 siliconforks 460 js_AppendUCString(&wc->sb, buf, len);
117     if (!STRING_BUFFER_OK(&wc->sb)) {
118     JS_ReportOutOfMemory(wc->cx);
119 siliconforks 332 return JS_FALSE;
120 siliconforks 460 }
121 siliconforks 332
122     return JS_TRUE;
123     }
124    
125     JSBool
126     js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
127     {
128     jsval *argv = vp + 2;
129 siliconforks 460 JSObject *replacer = NULL;
130     jsval space = JSVAL_NULL;
131     JSAutoTempValueRooter(cx, replacer);
132     JSAutoTempValueRooter(cx, 1, &space);
133    
134 siliconforks 332 // Must throw an Error if there isn't a first arg
135 siliconforks 460 if (!JS_ConvertArguments(cx, argc, argv, "v / o v", vp, &replacer, &space))
136 siliconforks 332 return JS_FALSE;
137    
138 siliconforks 460 WriterContext wc(cx);
139    
140     if (!js_Stringify(cx, vp, replacer, space, &WriteCallback, &wc))
141 siliconforks 332 return JS_FALSE;
142    
143 siliconforks 460 // XXX This can never happen to nsJSON.cpp, but the JSON object
144     // needs to support returning undefined. So this is a little awkward
145     // for the API, because we want to support streaming writers.
146     if (wc.didWrite) {
147     JSStringBuffer *sb = &wc.sb;
148     JSString *s = JS_NewUCStringCopyN(cx, sb->base, STRING_BUFFER_OFFSET(sb));
149     if (!s)
150     return JS_FALSE;
151     *vp = STRING_TO_JSVAL(s);
152     } else {
153     *vp = JSVAL_VOID;
154 siliconforks 332 }
155    
156 siliconforks 460 return JS_TRUE;
157 siliconforks 332 }
158    
159     JSBool
160     js_TryJSON(JSContext *cx, jsval *vp)
161     {
162     // Checks whether the return value implements toJSON()
163     JSBool ok = JS_TRUE;
164 siliconforks 460
165 siliconforks 332 if (!JSVAL_IS_PRIMITIVE(*vp)) {
166     JSObject *obj = JSVAL_TO_OBJECT(*vp);
167     ok = js_TryMethod(cx, obj, cx->runtime->atomState.toJSONAtom, 0, NULL, vp);
168     }
169 siliconforks 460
170 siliconforks 332 return ok;
171     }
172    
173    
174     static const jschar quote = jschar('"');
175     static const jschar backslash = jschar('\\');
176     static const jschar unicodeEscape[] = {'\\', 'u', '0', '0'};
177 siliconforks 460 static const jschar null_ucstr[] = {'n', 'u', 'l', 'l'};
178     static const jschar true_ucstr[] = {'t', 'r', 'u', 'e'};
179     static const jschar false_ucstr[] = {'f', 'a', 'l', 's', 'e'};
180 siliconforks 332
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 siliconforks 460 return callback(&quote, 1, data);
216     }
217 siliconforks 332
218 siliconforks 460 class StringifyContext
219     {
220     public:
221     StringifyContext(JSONWriteCallback callback, JSObject *replacer, void *data)
222     : callback(callback), replacer(replacer), data(data), depth(0)
223     {
224     js_InitStringBuffer(&gap);
225     }
226    
227     ~StringifyContext()
228     {
229     if (STRING_BUFFER_OK(&gap))
230     js_FinishStringBuffer(&gap);
231     }
232    
233     JSONWriteCallback callback;
234     JSStringBuffer gap;
235     JSObject *replacer;
236     void *data;
237     uint32 depth;
238     };
239    
240     static JSBool CallReplacerFunction(JSContext *cx, jsid id, JSObject *holder,
241     StringifyContext *scx, jsval *vp);
242     static JSBool Str(JSContext *cx, jsid id, JSObject *holder,
243     StringifyContext *scx, jsval *vp, bool callReplacer = true);
244    
245     static JSBool
246     WriteIndent(JSContext *cx, StringifyContext *scx, uint32 limit)
247     {
248     if (STRING_BUFFER_OFFSET(&scx->gap) > 0) {
249     jschar c = jschar('\n');
250     if (!scx->callback(&c, 1, scx->data))
251     return JS_FALSE;
252     for (uint32 i = 0; i < limit; i++) {
253     if (!scx->callback(scx->gap.base, STRING_BUFFER_OFFSET(&scx->gap), scx->data))
254     return JS_FALSE;
255     }
256     }
257    
258 siliconforks 332 return JS_TRUE;
259     }
260    
261 siliconforks 460 static JSBool
262     JO(JSContext *cx, jsval *vp, StringifyContext *scx)
263 siliconforks 332 {
264 siliconforks 460 JSObject *obj = JSVAL_TO_OBJECT(*vp);
265 siliconforks 332
266 siliconforks 460 jschar c = jschar('{');
267     if (!scx->callback(&c, 1, scx->data))
268 siliconforks 332 return JS_FALSE;
269 siliconforks 399
270 siliconforks 460 jsval vec[3] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
271     JSAutoTempValueRooter tvr(cx, 3, vec);
272     jsval& key = vec[0];
273     jsval& outputValue = vec[1];
274    
275 siliconforks 332 JSObject *iterObj = NULL;
276 siliconforks 460 jsval *keySource = vp;
277     bool usingWhitelist = false;
278 siliconforks 332
279 siliconforks 460 // if the replacer is an array, we use the keys from it
280     if (scx->replacer && JS_IsArrayObject(cx, scx->replacer)) {
281     usingWhitelist = true;
282     vec[2] = OBJECT_TO_JSVAL(scx->replacer);
283     keySource = &vec[2];
284 siliconforks 332 }
285    
286 siliconforks 460 if (!js_ValueToIterator(cx, JSITER_ENUMERATE, keySource))
287     return JS_FALSE;
288     iterObj = JSVAL_TO_OBJECT(*keySource);
289 siliconforks 332
290     JSBool memberWritten = JS_FALSE;
291 siliconforks 460 JSBool ok;
292    
293 siliconforks 332 do {
294     outputValue = JSVAL_VOID;
295 siliconforks 460 ok = js_CallIteratorNext(cx, iterObj, &key);
296     if (!ok)
297     break;
298     if (key == JSVAL_HOLE)
299     break;
300 siliconforks 332
301 siliconforks 460 jsuint index = 0;
302     if (usingWhitelist) {
303     // skip non-index properties
304     if (!js_IdIsIndex(key, &index))
305     continue;
306    
307     jsval newKey;
308     if (!OBJ_GET_PROPERTY(cx, scx->replacer, key, &newKey))
309     return JS_FALSE;
310     key = newKey;
311     }
312    
313     JSString *ks;
314     if (JSVAL_IS_STRING(key)) {
315     ks = JSVAL_TO_STRING(key);
316 siliconforks 332 } else {
317 siliconforks 460 ks = js_ValueToString(cx, key);
318     if (!ks) {
319     ok = JS_FALSE;
320 siliconforks 332 break;
321     }
322 siliconforks 460 }
323     JSAutoTempValueRooter keyStringRoot(cx, ks);
324 siliconforks 332
325 siliconforks 460 // Don't include prototype properties, since this operation is
326     // supposed to be implemented as if by ES3.1 Object.keys()
327     jsid id;
328     jsval v = JS_FALSE;
329     if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) ||
330     !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) {
331     ok = JS_FALSE;
332     break;
333 siliconforks 332 }
334    
335 siliconforks 460 if (v != JSVAL_TRUE)
336     continue;
337    
338     ok = JS_GetPropertyById(cx, obj, id, &outputValue);
339 siliconforks 332 if (!ok)
340     break;
341    
342 siliconforks 460 if (JSVAL_IS_OBJECT(outputValue)) {
343 siliconforks 332 ok = js_TryJSON(cx, &outputValue);
344     if (!ok)
345     break;
346     }
347    
348 siliconforks 460 // call this here, so we don't write out keys if the replacer function
349     // wants to elide the value.
350     if (!CallReplacerFunction(cx, id, obj, scx, &outputValue))
351     return JS_FALSE;
352    
353     JSType type = JS_TypeOfValue(cx, outputValue);
354    
355     // elide undefined values and functions and XML
356     if (outputValue == JSVAL_VOID || type == JSTYPE_FUNCTION || type == JSTYPE_XML)
357 siliconforks 332 continue;
358    
359     // output a comma unless this is the first member to write
360     if (memberWritten) {
361 siliconforks 460 c = jschar(',');
362     ok = scx->callback(&c, 1, scx->data);
363     if (!ok)
364 siliconforks 332 break;
365     }
366     memberWritten = JS_TRUE;
367    
368 siliconforks 460 if (!WriteIndent(cx, scx, scx->depth))
369     return JS_FALSE;
370 siliconforks 332
371 siliconforks 460 // Be careful below, this string is weakly rooted
372     JSString *s = js_ValueToString(cx, key);
373     if (!s) {
374     ok = JS_FALSE;
375 siliconforks 332 break;
376 siliconforks 460 }
377 siliconforks 332
378 siliconforks 460 ok = write_string(cx, scx->callback, scx->data, JS_GetStringChars(s), JS_GetStringLength(s));
379     if (!ok)
380     break;
381 siliconforks 332
382 siliconforks 460 c = jschar(':');
383     ok = scx->callback(&c, 1, scx->data);
384     if (!ok)
385     break;
386 siliconforks 332
387 siliconforks 460 ok = Str(cx, id, obj, scx, &outputValue, false);
388     if (!ok)
389     break;
390 siliconforks 332
391 siliconforks 460 } while (ok);
392    
393     if (iterObj) {
394     // Always close the iterator, but make sure not to stomp on OK
395     JS_ASSERT(OBJECT_TO_JSVAL(iterObj) == *keySource);
396     ok &= js_CloseIterator(cx, *keySource);
397     }
398    
399     if (!ok)
400     return JS_FALSE;
401    
402     if (memberWritten && !WriteIndent(cx, scx, scx->depth - 1))
403     return JS_FALSE;
404    
405     c = jschar('}');
406    
407     return scx->callback(&c, 1, scx->data);
408     }
409    
410     static JSBool
411     JA(JSContext *cx, jsval *vp, StringifyContext *scx)
412     {
413     JSObject *obj = JSVAL_TO_OBJECT(*vp);
414    
415     jschar c = jschar('[');
416     if (!scx->callback(&c, 1, scx->data))
417     return JS_FALSE;
418    
419     jsuint length;
420     if (!js_GetLengthProperty(cx, obj, &length))
421     return JS_FALSE;
422    
423     jsval outputValue = JSVAL_NULL;
424     JSAutoTempValueRooter tvr(cx, 1, &outputValue);
425    
426     jsid id;
427     jsuint i;
428     for (i = 0; i < length; i++) {
429     id = INT_TO_JSID(i);
430    
431     if (!OBJ_GET_PROPERTY(cx, obj, id, &outputValue))
432     return JS_FALSE;
433    
434     if (!Str(cx, id, obj, scx, &outputValue))
435     return JS_FALSE;
436    
437     if (outputValue == JSVAL_VOID) {
438     if (!scx->callback(null_ucstr, JS_ARRAY_LENGTH(null_ucstr), scx->data))
439     return JS_FALSE;
440 siliconforks 332 }
441    
442 siliconforks 460 if (i < length - 1) {
443     c = jschar(',');
444     if (!scx->callback(&c, 1, scx->data))
445     return JS_FALSE;
446     if (!WriteIndent(cx, scx, scx->depth))
447     return JS_FALSE;
448     }
449     }
450 siliconforks 332
451 siliconforks 460 if (length != 0 && !WriteIndent(cx, scx, scx->depth - 1))
452     return JS_FALSE;
453 siliconforks 399
454 siliconforks 460 c = jschar(']');
455 siliconforks 332
456 siliconforks 460 return scx->callback(&c, 1, scx->data);
457     }
458 siliconforks 332
459 siliconforks 460 static JSBool
460     CallReplacerFunction(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp)
461     {
462     if (scx->replacer && js_IsCallable(scx->replacer, cx)) {
463     jsval vec[2] = {ID_TO_VALUE(id), *vp};
464     if (!JS_CallFunctionValue(cx, holder, OBJECT_TO_JSVAL(scx->replacer), 2, vec, vp))
465     return JS_FALSE;
466     }
467 siliconforks 399
468 siliconforks 460 return JS_TRUE;
469     }
470    
471     static JSBool
472     Str(JSContext *cx, jsid id, JSObject *holder, StringifyContext *scx, jsval *vp, bool callReplacer)
473     {
474     JS_CHECK_RECURSION(cx, return JS_FALSE);
475    
476     if (!OBJ_GET_PROPERTY(cx, holder, id, vp))
477     return JS_FALSE;
478    
479     if (!JSVAL_IS_PRIMITIVE(*vp) && !js_TryJSON(cx, vp))
480     return JS_FALSE;
481    
482     if (callReplacer && !CallReplacerFunction(cx, id, holder, scx, vp))
483     return JS_FALSE;
484    
485     // catches string and number objects with no toJSON
486     if (!JSVAL_IS_PRIMITIVE(*vp)) {
487     JSClass *clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(*vp));
488     if (clasp == &js_StringClass || clasp == &js_NumberClass)
489     *vp = JSVAL_TO_OBJECT(*vp)->fslots[JSSLOT_PRIVATE];
490     }
491    
492     if (JSVAL_IS_STRING(*vp)) {
493     JSString *s = JSVAL_TO_STRING(*vp);
494     return write_string(cx, scx->callback, scx->data, JS_GetStringChars(s), JS_GetStringLength(s));
495     }
496    
497     if (JSVAL_IS_NULL(*vp)) {
498     return scx->callback(null_ucstr, JS_ARRAY_LENGTH(null_ucstr), scx->data);
499     }
500    
501     if (JSVAL_IS_BOOLEAN(*vp)) {
502     uint32 len = JS_ARRAY_LENGTH(true_ucstr);
503     const jschar *chars = true_ucstr;
504     JSBool b = JSVAL_TO_BOOLEAN(*vp);
505    
506     if (!b) {
507     chars = false_ucstr;
508     len = JS_ARRAY_LENGTH(false_ucstr);
509 siliconforks 332 }
510    
511 siliconforks 460 return scx->callback(chars, len, scx->data);
512 siliconforks 332 }
513    
514 siliconforks 460 if (JSVAL_IS_NUMBER(*vp)) {
515     if (JSVAL_IS_DOUBLE(*vp)) {
516     jsdouble d = *JSVAL_TO_DOUBLE(*vp);
517     if (!JSDOUBLE_IS_FINITE(d))
518     return scx->callback(null_ucstr, JS_ARRAY_LENGTH(null_ucstr), scx->data);
519     }
520    
521     char numBuf[DTOSTR_STANDARD_BUFFER_SIZE], *numStr;
522     jsdouble d = JSVAL_IS_INT(*vp) ? jsdouble(JSVAL_TO_INT(*vp)) : *JSVAL_TO_DOUBLE(*vp);
523     numStr = JS_dtostr(numBuf, sizeof numBuf, DTOSTR_STANDARD, 0, d);
524     if (!numStr) {
525     JS_ReportOutOfMemory(cx);
526     return JS_FALSE;
527     }
528    
529     jschar dstr[DTOSTR_STANDARD_BUFFER_SIZE];
530     size_t dbufSize = DTOSTR_STANDARD_BUFFER_SIZE;
531     if (!js_InflateStringToBuffer(cx, numStr, strlen(numStr), dstr, &dbufSize))
532     return JS_FALSE;
533    
534     return scx->callback(dstr, dbufSize, scx->data);
535     }
536    
537     if (JSVAL_IS_OBJECT(*vp) && !VALUE_IS_FUNCTION(cx, *vp) && !VALUE_IS_XML(cx, *vp)) {
538     JSBool ok;
539    
540     scx->depth++;
541     ok = (JS_IsArrayObject(cx, JSVAL_TO_OBJECT(*vp)) ? JA : JO)(cx, vp, scx);
542     scx->depth--;
543    
544     return ok;
545     }
546    
547     *vp = JSVAL_VOID;
548     return JS_TRUE;
549     }
550    
551     static JSBool
552     WriteStringGap(JSContext *cx, jsval space, JSStringBuffer *sb)
553     {
554     JSString *s = js_ValueToString(cx, space);
555     if (!s)
556 siliconforks 332 return JS_FALSE;
557 siliconforks 460
558     js_AppendUCString(sb, JS_GetStringChars(s), JS_GetStringLength(s));
559     if (!STRING_BUFFER_OK(sb)) {
560     JS_ReportOutOfMemory(cx);
561     return JS_FALSE;
562 siliconforks 332 }
563    
564 siliconforks 460 return JS_TRUE;
565     }
566 siliconforks 332
567 siliconforks 460 static JSBool
568     InitializeGap(JSContext *cx, jsval space, JSStringBuffer *sb)
569     {
570     if (!JSVAL_IS_PRIMITIVE(space)) {
571     JSClass *clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(space));
572     if (clasp == &js_StringClass || clasp == &js_NumberClass)
573     return WriteStringGap(cx, space, sb);
574     }
575    
576     if (JSVAL_IS_STRING(space))
577     return WriteStringGap(cx, space, sb);
578    
579     if (JSVAL_IS_NUMBER(space)) {
580     uint32 i;
581     if (!JS_ValueToECMAUint32(cx, space, &i))
582     return JS_FALSE;
583    
584     js_RepeatChar(sb, jschar(' '), i);
585    
586     if (!STRING_BUFFER_OK(sb)) {
587     JS_ReportOutOfMemory(cx);
588     return JS_FALSE;
589     }
590     }
591    
592     return JS_TRUE;
593 siliconforks 332 }
594    
595 siliconforks 460 JSBool
596     js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space,
597     JSONWriteCallback callback, void *data)
598     {
599     // XXX stack
600     JSObject *stack = JS_NewArrayObject(cx, 0, NULL);
601     if (!stack)
602     return JS_FALSE;
603    
604     StringifyContext scx(callback, replacer, data);
605     if (!InitializeGap(cx, space, &scx.gap))
606     return JS_FALSE;
607    
608     JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
609     if (!obj)
610     return JS_FALSE;
611    
612     if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
613     *vp, NULL, NULL, JSPROP_ENUMERATE, NULL)) {
614     return JS_FALSE;
615     }
616    
617     return Str(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, &scx, vp);
618     }
619    
620 siliconforks 332 // helper to determine whether a character could be part of a number
621 siliconforks 399 static JSBool IsNumChar(jschar c)
622 siliconforks 332 {
623     return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E');
624     }
625    
626 siliconforks 460 static JSBool HandleData(JSContext *cx, JSONParser *jp, JSONDataType type);
627     static JSBool PopState(JSContext *cx, JSONParser *jp);
628    
629     static JSBool
630     DestroyIdArrayOnError(JSContext *cx, JSIdArray *ida) {
631     JS_DestroyIdArray(cx, ida);
632     return JS_FALSE;
633     }
634    
635     static JSBool
636     Walk(JSContext *cx, jsid id, JSObject *holder, jsval reviver, jsval *vp)
637     {
638     JS_CHECK_RECURSION(cx, return JS_FALSE);
639    
640     if (!OBJ_GET_PROPERTY(cx, holder, id, vp))
641     return JS_FALSE;
642    
643     JSObject *obj;
644    
645     if (!JSVAL_IS_PRIMITIVE(*vp) && !js_IsCallable(obj = JSVAL_TO_OBJECT(*vp), cx)) {
646     jsval propValue = JSVAL_NULL;
647     JSAutoTempValueRooter tvr(cx, 1, &propValue);
648    
649     if(OBJ_IS_ARRAY(cx, obj)) {
650     jsuint length = 0;
651     if (!js_GetLengthProperty(cx, obj, &length))
652     return JS_FALSE;
653    
654     for (jsuint i = 0; i < length; i++) {
655     jsid index;
656     if (!js_IndexToId(cx, i, &index))
657     return JS_FALSE;
658    
659     if (!Walk(cx, index, obj, reviver, &propValue))
660     return JS_FALSE;
661    
662     if (!OBJ_DEFINE_PROPERTY(cx, obj, index, propValue,
663     NULL, NULL, JSPROP_ENUMERATE, NULL)) {
664     return JS_FALSE;
665     }
666     }
667     } else {
668     JSIdArray *ida = JS_Enumerate(cx, obj);
669     if (!ida)
670     return JS_FALSE;
671    
672     JSAutoTempValueRooter idaroot(cx, JS_ARRAY_LENGTH(ida), (jsval*)ida);
673    
674     for(jsint i = 0; i < ida->length; i++) {
675     jsid idName = ida->vector[i];
676     if (!Walk(cx, idName, obj, reviver, &propValue))
677     return DestroyIdArrayOnError(cx, ida);
678     if (propValue == JSVAL_VOID) {
679     if (!js_DeleteProperty(cx, obj, idName, &propValue))
680     return DestroyIdArrayOnError(cx, ida);
681     } else {
682     if (!OBJ_DEFINE_PROPERTY(cx, obj, idName, propValue,
683     NULL, NULL, JSPROP_ENUMERATE, NULL)) {
684     return DestroyIdArrayOnError(cx, ida);
685     }
686     }
687     }
688    
689     JS_DestroyIdArray(cx, ida);
690     }
691     }
692    
693     // return reviver.call(holder, key, value);
694     jsval value = *vp;
695     JSString *key = js_ValueToString(cx, ID_TO_VALUE(id));
696     if (!key)
697     return JS_FALSE;
698    
699     jsval vec[2] = {STRING_TO_JSVAL(key), value};
700     jsval reviverResult;
701     if (!JS_CallFunctionValue(cx, holder, reviver, 2, vec, &reviverResult))
702     return JS_FALSE;
703    
704     *vp = reviverResult;
705    
706     return JS_TRUE;
707     }
708    
709     static JSBool
710     Revive(JSContext *cx, jsval reviver, jsval *vp)
711     {
712    
713     JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
714     if (!obj)
715     return JS_FALSE;
716    
717     jsval v = OBJECT_TO_JSVAL(obj);
718     JSAutoTempValueRooter tvr(cx, 1, &v);
719     if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom),
720     *vp, NULL, NULL, JSPROP_ENUMERATE, NULL)) {
721     return JS_FALSE;
722     }
723    
724     return Walk(cx, ATOM_TO_JSID(cx->runtime->atomState.emptyAtom), obj, reviver, vp);
725     }
726    
727     #define JSON_INITIAL_BUFSIZE 256
728    
729 siliconforks 332 JSONParser *
730     js_BeginJSONParse(JSContext *cx, jsval *rootVal)
731     {
732     if (!cx)
733     return NULL;
734    
735 siliconforks 399 JSObject *arr = js_NewArrayObject(cx, 0, NULL);
736 siliconforks 332 if (!arr)
737     return NULL;
738    
739     JSONParser *jp = (JSONParser*) JS_malloc(cx, sizeof(JSONParser));
740     if (!jp)
741 siliconforks 399 return NULL;
742 siliconforks 460 memset(jp, 0, sizeof *jp);
743 siliconforks 332
744     jp->objectStack = arr;
745 siliconforks 399 if (!js_AddRoot(cx, &jp->objectStack, "JSON parse stack"))
746 siliconforks 332 goto bad;
747    
748     jp->statep = jp->stateStack;
749     *jp->statep = JSON_PARSE_STATE_INIT;
750     jp->rootVal = rootVal;
751 siliconforks 399
752 siliconforks 460 js_InitStringBuffer(&jp->objectKey);
753     js_InitStringBuffer(&jp->buffer);
754    
755     if (!jp->buffer.grow(&jp->buffer, JSON_INITIAL_BUFSIZE)) {
756     JS_ReportOutOfMemory(cx);
757 siliconforks 399 goto bad;
758 siliconforks 460 }
759     return jp;
760 siliconforks 332
761     bad:
762 siliconforks 460 js_FinishJSONParse(cx, jp, JSVAL_NULL);
763 siliconforks 332 return NULL;
764     }
765    
766     JSBool
767 siliconforks 460 js_FinishJSONParse(JSContext *cx, JSONParser *jp, jsval reviver)
768 siliconforks 332 {
769     if (!jp)
770     return JS_TRUE;
771    
772 siliconforks 460 JSBool early_ok = JS_TRUE;
773 siliconforks 399
774 siliconforks 460 // Check for unprocessed primitives at the root. This doesn't happen for
775     // strings because a closing quote triggers value processing.
776     if ((jp->statep - jp->stateStack) == 1) {
777     if (*jp->statep == JSON_PARSE_STATE_KEYWORD) {
778     early_ok = HandleData(cx, jp, JSON_DATA_KEYWORD);
779     if (early_ok)
780     PopState(cx, jp);
781     } else if (*jp->statep == JSON_PARSE_STATE_NUMBER) {
782     early_ok = HandleData(cx, jp, JSON_DATA_NUMBER);
783     if (early_ok)
784     PopState(cx, jp);
785     }
786     }
787    
788     js_FinishStringBuffer(&jp->objectKey);
789     js_FinishStringBuffer(&jp->buffer);
790    
791     /* This internal API is infallible, in spite of its JSBool return type. */
792     js_RemoveRoot(cx->runtime, &jp->objectStack);
793    
794 siliconforks 332 JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
795 siliconforks 460 jsval *vp = jp->rootVal;
796 siliconforks 332 JS_free(cx, jp);
797    
798 siliconforks 460 if (!early_ok)
799     return JS_FALSE;
800    
801     if (!ok) {
802     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
803     return JS_FALSE;
804     }
805    
806     if (!JSVAL_IS_PRIMITIVE(reviver) && js_IsCallable(JSVAL_TO_OBJECT(reviver), cx))
807     ok = Revive(cx, reviver, vp);
808    
809 siliconforks 332 return ok;
810     }
811    
812     static JSBool
813 siliconforks 460 PushState(JSContext *cx, JSONParser *jp, JSONParserState state)
814 siliconforks 332 {
815 siliconforks 460 if (*jp->statep == JSON_PARSE_STATE_FINISHED) {
816     // extra input
817     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
818     return JS_FALSE;
819     }
820 siliconforks 332
821     jp->statep++;
822 siliconforks 460 if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack)) {
823     // too deep
824     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
825     return JS_FALSE;
826     }
827 siliconforks 332
828     *jp->statep = state;
829    
830     return JS_TRUE;
831     }
832    
833     static JSBool
834 siliconforks 460 PopState(JSContext *cx, JSONParser *jp)
835 siliconforks 332 {
836     jp->statep--;
837     if (jp->statep < jp->stateStack) {
838     jp->statep = jp->stateStack;
839 siliconforks 460 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
840 siliconforks 332 return JS_FALSE;
841 siliconforks 399 }
842 siliconforks 332
843     if (*jp->statep == JSON_PARSE_STATE_INIT)
844     *jp->statep = JSON_PARSE_STATE_FINISHED;
845    
846     return JS_TRUE;
847     }
848    
849     static JSBool
850     PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
851     {
852     JSBool ok;
853     if (OBJ_IS_ARRAY(cx, parent)) {
854     jsuint len;
855 siliconforks 399 ok = js_GetLengthProperty(cx, parent, &len);
856     if (ok) {
857 siliconforks 460 jsid index;
858     if (!js_IndexToId(cx, len, &index))
859     return JS_FALSE;
860     ok = OBJ_DEFINE_PROPERTY(cx, parent, index, value,
861 siliconforks 399 NULL, NULL, JSPROP_ENUMERATE, NULL);
862     }
863 siliconforks 332 } else {
864 siliconforks 460 ok = JS_DefineUCProperty(cx, parent, jp->objectKey.base,
865     STRING_BUFFER_OFFSET(&jp->objectKey), value,
866 siliconforks 332 NULL, NULL, JSPROP_ENUMERATE);
867 siliconforks 460 js_RewindStringBuffer(&jp->objectKey);
868 siliconforks 332 }
869    
870     return ok;
871     }
872    
873     static JSBool
874     PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
875     {
876     jsuint len;
877 siliconforks 399 if (!js_GetLengthProperty(cx, jp->objectStack, &len))
878 siliconforks 332 return JS_FALSE;
879 siliconforks 460 if (len >= JSON_MAX_DEPTH) {
880     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
881     return JS_FALSE;
882     }
883 siliconforks 332
884     jsval v = OBJECT_TO_JSVAL(obj);
885 siliconforks 399 JSAutoTempValueRooter tvr(cx, v);
886 siliconforks 332
887     // Check if this is the root object
888     if (len == 0) {
889 siliconforks 399 *jp->rootVal = v;
890     // This property must be enumerable to keep the array dense
891     if (!OBJ_DEFINE_PROPERTY(cx, jp->objectStack, INT_TO_JSID(0), *jp->rootVal,
892     NULL, NULL, JSPROP_ENUMERATE, NULL)) {
893 siliconforks 332 return JS_FALSE;
894 siliconforks 399 }
895 siliconforks 332 return JS_TRUE;
896     }
897    
898     jsval p;
899 siliconforks 399 if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &p))
900 siliconforks 332 return JS_FALSE;
901 siliconforks 399
902 siliconforks 332 JS_ASSERT(JSVAL_IS_OBJECT(p));
903     JSObject *parent = JSVAL_TO_OBJECT(p);
904 siliconforks 460 if (!PushValue(cx, jp, parent, v))
905 siliconforks 332 return JS_FALSE;
906    
907 siliconforks 399 // This property must be enumerable to keep the array dense
908     if (!OBJ_DEFINE_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len), v,
909     NULL, NULL, JSPROP_ENUMERATE, NULL)) {
910 siliconforks 332 return JS_FALSE;
911 siliconforks 399 }
912 siliconforks 332
913     return JS_TRUE;
914     }
915    
916     static JSBool
917     OpenObject(JSContext *cx, JSONParser *jp)
918     {
919 siliconforks 399 JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
920 siliconforks 332 if (!obj)
921     return JS_FALSE;
922    
923     return PushObject(cx, jp, obj);
924     }
925    
926     static JSBool
927     OpenArray(JSContext *cx, JSONParser *jp)
928     {
929     // Add an array to an existing array or object
930 siliconforks 399 JSObject *arr = js_NewArrayObject(cx, 0, NULL);
931 siliconforks 332 if (!arr)
932     return JS_FALSE;
933    
934     return PushObject(cx, jp, arr);
935     }
936    
937     static JSBool
938     CloseObject(JSContext *cx, JSONParser *jp)
939     {
940     jsuint len;
941 siliconforks 399 if (!js_GetLengthProperty(cx, jp->objectStack, &len))
942 siliconforks 332 return JS_FALSE;
943 siliconforks 399 if (!js_SetLengthProperty(cx, jp->objectStack, len - 1))
944 siliconforks 332 return JS_FALSE;
945    
946     return JS_TRUE;
947     }
948    
949     static JSBool
950     CloseArray(JSContext *cx, JSONParser *jp)
951     {
952 siliconforks 399 return CloseObject(cx, jp);
953 siliconforks 332 }
954    
955     static JSBool
956 siliconforks 460 PushPrimitive(JSContext *cx, JSONParser *jp, jsval value)
957     {
958     JSAutoTempValueRooter tvr(cx, 1, &value);
959    
960     jsuint len;
961     if (!js_GetLengthProperty(cx, jp->objectStack, &len))
962     return JS_FALSE;
963    
964     if (len > 0) {
965     jsval o;
966     if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &o))
967     return JS_FALSE;
968    
969     JS_ASSERT(!JSVAL_IS_PRIMITIVE(o));
970     return PushValue(cx, jp, JSVAL_TO_OBJECT(o), value);
971     }
972    
973     // root value must be primitive
974     *jp->rootVal = value;
975     return JS_TRUE;
976     }
977    
978     static JSBool
979 siliconforks 332 HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
980     {
981     const jschar *ep;
982 siliconforks 399 double val;
983 siliconforks 460 if (!js_strtod(cx, buf, buf + len, &ep, &val))
984 siliconforks 332 return JS_FALSE;
985 siliconforks 460 if (ep != buf + len) {
986     // bad number input
987     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
988     return JS_FALSE;
989     }
990 siliconforks 332
991 siliconforks 460 jsval numVal;
992     if (!JS_NewNumberValue(cx, val, &numVal))
993     return JS_FALSE;
994    
995     return PushPrimitive(cx, jp, numVal);
996 siliconforks 332 }
997    
998     static JSBool
999     HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
1000     {
1001 siliconforks 399 JSString *str = js_NewStringCopyN(cx, buf, len);
1002 siliconforks 460 if (!str)
1003 siliconforks 332 return JS_FALSE;
1004    
1005 siliconforks 460 return PushPrimitive(cx, jp, STRING_TO_JSVAL(str));
1006 siliconforks 332 }
1007    
1008     static JSBool
1009     HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
1010     {
1011     jsval keyword;
1012     JSTokenType tt = js_CheckKeyword(buf, len);
1013 siliconforks 460 if (tt != TOK_PRIMARY) {
1014     // bad keyword
1015     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1016 siliconforks 332 return JS_FALSE;
1017 siliconforks 460 }
1018 siliconforks 332
1019 siliconforks 460 if (buf[0] == 'n') {
1020 siliconforks 332 keyword = JSVAL_NULL;
1021 siliconforks 460 } else if (buf[0] == 't') {
1022 siliconforks 332 keyword = JSVAL_TRUE;
1023 siliconforks 460 } else if (buf[0] == 'f') {
1024 siliconforks 332 keyword = JSVAL_FALSE;
1025 siliconforks 460 } else {
1026     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1027 siliconforks 332 return JS_FALSE;
1028 siliconforks 460 }
1029 siliconforks 332
1030 siliconforks 460 return PushPrimitive(cx, jp, keyword);
1031 siliconforks 332 }
1032    
1033     static JSBool
1034 siliconforks 399 HandleData(JSContext *cx, JSONParser *jp, JSONDataType type)
1035 siliconforks 332 {
1036 siliconforks 460 JSBool ok;
1037 siliconforks 332
1038 siliconforks 460 switch (type) {
1039     case JSON_DATA_STRING:
1040     ok = HandleString(cx, jp, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
1041     break;
1042 siliconforks 399
1043 siliconforks 460 case JSON_DATA_KEYSTRING:
1044     js_AppendUCString(&jp->objectKey, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
1045     ok = STRING_BUFFER_OK(&jp->objectKey);
1046     if (!ok)
1047     JS_ReportOutOfMemory(cx);
1048     break;
1049 siliconforks 332
1050 siliconforks 460 case JSON_DATA_NUMBER:
1051     ok = HandleNumber(cx, jp, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
1052     break;
1053 siliconforks 332
1054 siliconforks 460 default:
1055     JS_ASSERT(type == JSON_DATA_KEYWORD);
1056     ok = HandleKeyword(cx, jp, jp->buffer.base, STRING_BUFFER_OFFSET(&jp->buffer));
1057     break;
1058     }
1059 siliconforks 332
1060 siliconforks 460 if (ok) {
1061     ok = STRING_BUFFER_OK(&jp->buffer);
1062     if (ok)
1063     js_RewindStringBuffer(&jp->buffer);
1064     else
1065     JS_ReportOutOfMemory(cx);
1066     }
1067     return ok;
1068 siliconforks 332 }
1069    
1070     JSBool
1071     js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len)
1072     {
1073     uint32 i;
1074    
1075     if (*jp->statep == JSON_PARSE_STATE_INIT) {
1076 siliconforks 460 PushState(cx, jp, JSON_PARSE_STATE_VALUE);
1077 siliconforks 332 }
1078    
1079 siliconforks 399 for (i = 0; i < len; i++) {
1080 siliconforks 332 jschar c = data[i];
1081     switch (*jp->statep) {
1082 siliconforks 460 case JSON_PARSE_STATE_VALUE:
1083     if (c == ']') {
1084     // empty array
1085     if (!PopState(cx, jp))
1086     return JS_FALSE;
1087 siliconforks 332
1088 siliconforks 460 if (*jp->statep != JSON_PARSE_STATE_ARRAY) {
1089     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1090     return JS_FALSE;
1091 siliconforks 332 }
1092    
1093 siliconforks 460 if (!CloseArray(cx, jp) || !PopState(cx, jp))
1094     return JS_FALSE;
1095 siliconforks 332
1096     break;
1097 siliconforks 460 }
1098 siliconforks 332
1099 siliconforks 460 if (c == '}') {
1100     // we should only find these in OBJECT_KEY state
1101     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1102     return JS_FALSE;
1103     }
1104 siliconforks 332
1105 siliconforks 460 if (c == '"') {
1106     *jp->statep = JSON_PARSE_STATE_STRING;
1107 siliconforks 332 break;
1108 siliconforks 460 }
1109 siliconforks 399
1110 siliconforks 460 if (IsNumChar(c)) {
1111     *jp->statep = JSON_PARSE_STATE_NUMBER;
1112     js_FastAppendChar(&jp->buffer, c);
1113 siliconforks 332 break;
1114 siliconforks 460 }
1115 siliconforks 399
1116 siliconforks 460 if (JS7_ISLET(c)) {
1117     *jp->statep = JSON_PARSE_STATE_KEYWORD;
1118     js_FastAppendChar(&jp->buffer, c);
1119 siliconforks 332 break;
1120 siliconforks 460 }
1121 siliconforks 399
1122 siliconforks 460 // fall through in case the value is an object or array
1123     case JSON_PARSE_STATE_OBJECT_VALUE:
1124     if (c == '{') {
1125     *jp->statep = JSON_PARSE_STATE_OBJECT;
1126     if (!OpenObject(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
1127     return JS_FALSE;
1128     } else if (c == '[') {
1129     *jp->statep = JSON_PARSE_STATE_ARRAY;
1130     if (!OpenArray(cx, jp) || !PushState(cx, jp, JSON_PARSE_STATE_VALUE))
1131     return JS_FALSE;
1132     } else if (!JS_ISXMLSPACE(c)) {
1133     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1134     return JS_FALSE;
1135     }
1136     break;
1137 siliconforks 399
1138 siliconforks 460 case JSON_PARSE_STATE_OBJECT:
1139     if (c == '}') {
1140     if (!CloseObject(cx, jp) || !PopState(cx, jp))
1141     return JS_FALSE;
1142     } else if (c == ',') {
1143     if (!PushState(cx, jp, JSON_PARSE_STATE_OBJECT_PAIR))
1144     return JS_FALSE;
1145     } else if (c == ']' || !JS_ISXMLSPACE(c)) {
1146     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1147     return JS_FALSE;
1148     }
1149     break;
1150 siliconforks 332
1151 siliconforks 460 case JSON_PARSE_STATE_ARRAY :
1152     if (c == ']') {
1153     if (!CloseArray(cx, jp) || !PopState(cx, jp))
1154     return JS_FALSE;
1155     } else if (c == ',') {
1156     if (!PushState(cx, jp, JSON_PARSE_STATE_VALUE))
1157     return JS_FALSE;
1158     } else if (!JS_ISXMLSPACE(c)) {
1159     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1160     return JS_FALSE;
1161     }
1162     break;
1163 siliconforks 399
1164 siliconforks 460 case JSON_PARSE_STATE_OBJECT_PAIR :
1165     if (c == '"') {
1166     // we want to be waiting for a : when the string has been read
1167     *jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
1168     if (!PushState(cx, jp, JSON_PARSE_STATE_STRING))
1169     return JS_FALSE;
1170     } else if (c == '}') {
1171     // pop off the object pair state and the object state
1172     if (!CloseObject(cx, jp) || !PopState(cx, jp) || !PopState(cx, jp))
1173     return JS_FALSE;
1174     } else if (c == ']' || !JS_ISXMLSPACE(c)) {
1175     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1176     return JS_FALSE;
1177     }
1178     break;
1179 siliconforks 332
1180 siliconforks 460 case JSON_PARSE_STATE_OBJECT_IN_PAIR:
1181     if (c == ':') {
1182     *jp->statep = JSON_PARSE_STATE_VALUE;
1183     } else if (!JS_ISXMLSPACE(c)) {
1184     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1185     return JS_FALSE;
1186     }
1187     break;
1188 siliconforks 399
1189 siliconforks 460 case JSON_PARSE_STATE_STRING:
1190     if (c == '"') {
1191     if (!PopState(cx, jp))
1192     return JS_FALSE;
1193     JSONDataType jdt;
1194     if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
1195     jdt = JSON_DATA_KEYSTRING;
1196 siliconforks 332 } else {
1197 siliconforks 460 jdt = JSON_DATA_STRING;
1198     }
1199     if (!HandleData(cx, jp, jdt))
1200     return JS_FALSE;
1201     } else if (c == '\\') {
1202     *jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
1203     } else {
1204     js_FastAppendChar(&jp->buffer, c);
1205     }
1206     break;
1207 siliconforks 399
1208 siliconforks 460 case JSON_PARSE_STATE_STRING_ESCAPE:
1209     switch (c) {
1210     case '"':
1211     case '\\':
1212     case '/':
1213 siliconforks 332 break;
1214 siliconforks 460 case 'b' : c = '\b'; break;
1215     case 'f' : c = '\f'; break;
1216     case 'n' : c = '\n'; break;
1217     case 'r' : c = '\r'; break;
1218     case 't' : c = '\t'; break;
1219     default :
1220     if (c == 'u') {
1221     jp->numHex = 0;
1222     jp->hexChar = 0;
1223     *jp->statep = JSON_PARSE_STATE_STRING_HEX;
1224     continue;
1225 siliconforks 332 } else {
1226 siliconforks 460 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1227     return JS_FALSE;
1228 siliconforks 332 }
1229 siliconforks 460 }
1230 siliconforks 399
1231 siliconforks 460 js_FastAppendChar(&jp->buffer, c);
1232     *jp->statep = JSON_PARSE_STATE_STRING;
1233     break;
1234 siliconforks 332
1235 siliconforks 460 case JSON_PARSE_STATE_STRING_HEX:
1236     if (('0' <= c) && (c <= '9')) {
1237     jp->hexChar = (jp->hexChar << 4) | (c - '0');
1238     } else if (('a' <= c) && (c <= 'f')) {
1239     jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
1240     } else if (('A' <= c) && (c <= 'F')) {
1241     jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
1242     } else {
1243     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1244     return JS_FALSE;
1245     }
1246    
1247     if (++(jp->numHex) == 4) {
1248     js_FastAppendChar(&jp->buffer, jp->hexChar);
1249     jp->hexChar = 0;
1250     jp->numHex = 0;
1251     *jp->statep = JSON_PARSE_STATE_STRING;
1252     }
1253     break;
1254 siliconforks 399
1255 siliconforks 460 case JSON_PARSE_STATE_KEYWORD:
1256     if (JS7_ISLET(c)) {
1257     js_FastAppendChar(&jp->buffer, c);
1258     } else {
1259     // this character isn't part of the keyword, process it again
1260     i--;
1261     if (!PopState(cx, jp))
1262     return JS_FALSE;
1263    
1264     if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
1265     return JS_FALSE;
1266     }
1267     break;
1268    
1269     case JSON_PARSE_STATE_NUMBER:
1270     if (IsNumChar(c)) {
1271     js_FastAppendChar(&jp->buffer, c);
1272     } else {
1273     // this character isn't part of the number, process it again
1274     i--;
1275     if (!PopState(cx, jp))
1276     return JS_FALSE;
1277     if (!HandleData(cx, jp, JSON_DATA_NUMBER))
1278     return JS_FALSE;
1279     }
1280     break;
1281    
1282     case JSON_PARSE_STATE_FINISHED:
1283     if (!JS_ISXMLSPACE(c)) {
1284     // extra input
1285     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_JSON_BAD_PARSE);
1286     return JS_FALSE;
1287     }
1288     break;
1289    
1290     default:
1291     JS_NOT_REACHED("Invalid JSON parser state");
1292     }
1293    
1294     if (!STRING_BUFFER_OK(&jp->buffer)) {
1295     JS_ReportOutOfMemory(cx);
1296     return JS_FALSE;
1297     }
1298 siliconforks 332 }
1299    
1300 siliconforks 460 *jp->buffer.ptr = 0;
1301 siliconforks 332 return JS_TRUE;
1302     }
1303    
1304     #if JS_HAS_TOSOURCE
1305     static JSBool
1306     json_toSource(JSContext *cx, uintN argc, jsval *vp)
1307     {
1308     *vp = ATOM_KEY(CLASS_ATOM(cx, JSON));
1309     return JS_TRUE;
1310     }
1311     #endif
1312    
1313     static JSFunctionSpec json_static_methods[] = {
1314     #if JS_HAS_TOSOURCE
1315     JS_FN(js_toSource_str, json_toSource, 0, 0),
1316     #endif
1317 siliconforks 460 JS_FN("parse", js_json_parse, 1, 0),
1318     JS_FN("stringify", js_json_stringify, 1, 0),
1319 siliconforks 332 JS_FS_END
1320     };
1321    
1322     JSObject *
1323     js_InitJSONClass(JSContext *cx, JSObject *obj)
1324     {
1325     JSObject *JSON;
1326    
1327     JSON = JS_NewObject(cx, &js_JSONClass, NULL, obj);
1328     if (!JSON)
1329     return NULL;
1330     if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON),
1331 siliconforks 399 JS_PropertyStub, JS_PropertyStub, 0))
1332 siliconforks 332 return NULL;
1333    
1334     if (!JS_DefineFunctions(cx, JSON, json_static_methods))
1335     return NULL;
1336    
1337     return JSON;
1338     }

  ViewVC Help
Powered by ViewVC 1.1.24