PageRenderTime 197ms CodeModel.GetById 76ms app.highlight 110ms RepoModel.GetById 1ms app.codeStats 0ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/json.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 954 lines | 876 code | 37 blank | 41 comment | 60 complexity | 8cb1e4393f751fd3b101049d329443ee MD5 | raw file
  1/* ***** BEGIN LICENSE BLOCK *****
  2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3 *
  4 * The contents of this file are subject to the Mozilla Public License Version
  5 * 1.1 (the "License"); you may not use this file except in compliance with
  6 * the License. You may obtain a copy of the License at
  7 * http://www.mozilla.org/MPL/
  8 *
  9 * Software distributed under the License is distributed on an "AS IS" basis,
 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11 * for the specific language governing rights and limitations under the
 12 * License.
 13 *
 14 * The Original Code is SpiderMonkey JSON.
 15 *
 16 * The Initial Developer of the Original Code is
 17 * Mozilla Corporation.
 18 * Portions created by the Initial Developer are Copyright (C) 1998-1999
 19 * the Initial Developer. All Rights Reserved.
 20 *
 21 * Contributor(s):
 22 *   Robert Sayre <sayrer@gmail.com>
 23 *
 24 * Alternatively, the contents of this file may be used under the terms of
 25 * either of the GNU General Public License Version 2 or later (the "GPL"),
 26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 27 * in which case the provisions of the GPL or the LGPL are applicable instead
 28 * of those above. If you wish to allow use of your version of this file only
 29 * under the terms of either the GPL or the LGPL, and not to allow others to
 30 * use your version of this file under the terms of the MPL, indicate your
 31 * decision by deleting the provisions above and replace them with the notice
 32 * and other provisions required by the GPL or the LGPL. If you do not delete
 33 * the provisions above, a recipient may use your version of this file under
 34 * the terms of any one of the MPL, the GPL or the LGPL.
 35 *
 36 * ***** END LICENSE BLOCK ***** */
 37
 38
 39#include "jsapi.h"
 40#include "jsarena.h"
 41#include "jsarray.h"
 42#include "jsatom.h"
 43#include "jsbool.h"
 44#include "jscntxt.h"
 45#include "jsdtoa.h"
 46#include "jsinterp.h"
 47#include "jsiter.h"
 48#include "jsnum.h"
 49#include "jsobj.h"
 50#include "jsprf.h"
 51#include "jsscan.h"
 52#include "jsstr.h"
 53#include "jstypes.h"
 54#include "jsutil.h"
 55
 56#include "json.h"
 57
 58JSClass 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
 66JSBool
 67js_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
 91class StringifyClosure : JSAutoTempValueRooter
 92{
 93public:
 94    StringifyClosure(JSContext *cx, size_t len, jsval *vec)
 95        : JSAutoTempValueRooter(cx, len, vec), cx(cx), s(vec)
 96    {
 97    }
 98
 99    JSContext *cx;
100    jsval *s;
101};
102
103static JSBool
104WriteCallback(const jschar *buf, uint32 len, void *data)
105{
106    StringifyClosure *sc = static_cast<StringifyClosure*>(data);
107    JSString *s1 = JSVAL_TO_STRING(sc->s[0]);
108    JSString *s2 = js_NewStringCopyN(sc->cx, buf, len);
109    if (!s2)
110        return JS_FALSE;
111
112    sc->s[1] = STRING_TO_JSVAL(s2);
113
114    s1 = js_ConcatStrings(sc->cx, s1, s2);
115    if (!s1)
116        return JS_FALSE;
117
118    sc->s[0] = STRING_TO_JSVAL(s1);
119    sc->s[1] = JSVAL_VOID;
120
121    return JS_TRUE;
122}
123
124JSBool
125js_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    *vp = OBJECT_TO_JSVAL(obj);
136    
137    JSBool ok = js_TryJSON(cx, vp);
138    JSType type;
139    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        return JS_FALSE;
145    }
146    
147    JSString *s = JS_NewStringCopyN(cx, "", 0);
148    if (!s)
149        ok = JS_FALSE;
150
151    if (ok) {
152        jsval vec[2] = {STRING_TO_JSVAL(s), JSVAL_VOID};
153        StringifyClosure sc(cx, 2, vec);
154        JSAutoTempValueRooter resultTvr(cx, 1, sc.s);
155        ok = js_Stringify(cx, vp, NULL, &WriteCallback, &sc, 0);
156        *vp = *sc.s;
157    }
158
159    return ok;
160}
161
162JSBool
163js_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
177static const jschar quote = jschar('"');
178static const jschar backslash = jschar('\\');
179static const jschar unicodeEscape[] = {'\\', 'u', '0', '0'};
180
181static JSBool
182write_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            char ubuf[3];
200            size_t len = JS_snprintf(ubuf, sizeof(ubuf), "%.2x", buf[i]);
201            JS_ASSERT(len == 2);
202            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                return JS_FALSE;
207            }
208            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
221JSBool
222js_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
235    JSObject *iterObj = NULL;
236    jsint i = 0;
237    jsuint length = 0;
238
239    if (isArray) {
240        if (!js_GetLengthProperty(cx, obj, &length))
241            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            ok = OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(i), &outputValue);
260            i++;
261        } 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                ks = js_ValueToString(cx, key);
273                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            s = js_ValueToString(cx, key);
320            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            ok = js_Stringify(cx, &outputValue, replacer, callback, data, depth + 1);
338        } else {
339            JSString *outputString;
340            s = js_ValueToString(cx, outputValue);
341            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
351                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            if (!outputString) {
374                ok = JS_FALSE;
375                break;
376            }
377
378            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        JS_ReportError(cx, "Error during JSON encoding");
390        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
400static JSBool IsNumChar(jschar c)
401{
402    return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E');
403}
404
405JSONParser *
406js_BeginJSONParse(JSContext *cx, jsval *rootVal)
407{
408    if (!cx)
409        return NULL;
410
411    JSObject *arr = js_NewArrayObject(cx, 0, NULL);
412    if (!arr)
413        return NULL;
414
415    JSONParser *jp = (JSONParser*) JS_malloc(cx, sizeof(JSONParser));
416    if (!jp)
417        return NULL;
418    jp->buffer = NULL;
419
420    jp->objectStack = arr;
421    if (!js_AddRoot(cx, &jp->objectStack, "JSON parse stack"))
422        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
430    jp->objectKey = (JSStringBuffer*) JS_malloc(cx, sizeof(JSStringBuffer));
431    if (!jp->objectKey)
432        goto bad;
433    js_InitStringBuffer(jp->objectKey);
434    
435    jp->buffer = (JSStringBuffer*) JS_malloc(cx, sizeof(JSStringBuffer));
436    if (!jp->buffer)
437        goto bad;
438    js_InitStringBuffer(jp->buffer);
439
440    return jp;
441bad:
442    JS_free(cx, jp->buffer);
443    JS_free(cx, jp);
444    return NULL;
445}
446
447JSBool
448js_FinishJSONParse(JSContext *cx, JSONParser *jp)
449{
450    if (!jp)
451        return JS_TRUE;
452
453    if (jp->objectKey)
454        js_FinishStringBuffer(jp->objectKey);
455    JS_free(cx, jp->objectKey);
456
457    if (jp->buffer)
458        js_FinishStringBuffer(jp->buffer);
459    JS_free(cx, jp->buffer);
460    
461    if (!js_RemoveRoot(cx->runtime, &jp->objectStack))
462        return JS_FALSE;
463    JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
464    JS_free(cx, jp);
465
466    return ok;
467}
468
469static JSBool
470PushState(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
484static JSBool
485PopState(JSONParser *jp)
486{
487    jp->statep--;
488    if (jp->statep < jp->stateStack) {
489        jp->statep = jp->stateStack;
490        return JS_FALSE;
491    }
492
493    if (*jp->statep == JSON_PARSE_STATE_INIT)
494        *jp->statep = JSON_PARSE_STATE_FINISHED;
495
496    return JS_TRUE;
497}
498
499static JSBool
500PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
501{
502    JSAutoTempValueRooter tvr(cx, 1, &value);
503 
504    JSBool ok;
505    if (OBJ_IS_ARRAY(cx, parent)) {
506        jsuint len;
507        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    } else {
513        ok = JS_DefineUCProperty(cx, parent, jp->objectKey->base,
514                                 STRING_BUFFER_OFFSET(jp->objectKey), value,
515                                 NULL, NULL, JSPROP_ENUMERATE);
516        js_FinishStringBuffer(jp->objectKey);
517        js_InitStringBuffer(jp->objectKey);
518    }
519
520    return ok;
521}
522
523static JSBool
524PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
525{
526    jsuint len;
527    if (!js_GetLengthProperty(cx, jp->objectStack, &len))
528        return JS_FALSE;
529    if (len >= JSON_MAX_DEPTH)
530        return JS_FALSE; // decoding error
531
532    jsval v = OBJECT_TO_JSVAL(obj);
533    JSAutoTempValueRooter tvr(cx, v);
534
535    // Check if this is the root object
536    if (len == 0) {
537        *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            return JS_FALSE;
542        }
543        return JS_TRUE;
544    }
545
546    jsval p;
547    if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &p))
548        return JS_FALSE;
549
550    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    // 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        return JS_FALSE;
559    }
560
561    return JS_TRUE;
562}
563
564static JSObject *
565GetTopOfObjectStack(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
579static JSBool
580OpenObject(JSContext *cx, JSONParser *jp)
581{
582    JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
583    if (!obj)
584        return JS_FALSE;
585
586    return PushObject(cx, jp, obj);
587}
588
589static JSBool
590OpenArray(JSContext *cx, JSONParser *jp)
591{
592    // Add an array to an existing array or object
593    JSObject *arr = js_NewArrayObject(cx, 0, NULL);
594    if (!arr)
595        return JS_FALSE;
596
597    return PushObject(cx, jp, arr);
598}
599
600static JSBool
601CloseObject(JSContext *cx, JSONParser *jp)
602{
603    jsuint len;
604    if (!js_GetLengthProperty(cx, jp->objectStack, &len))
605        return JS_FALSE;
606    if (!js_SetLengthProperty(cx, jp->objectStack, len - 1))
607        return JS_FALSE;
608
609    return JS_TRUE;
610}
611
612static JSBool
613CloseArray(JSContext *cx, JSONParser *jp)
614{
615    return CloseObject(cx, jp);
616}
617
618static JSBool
619HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
620{
621    const jschar *ep;
622    double val;
623    if (!js_strtod(cx, buf, buf + len, &ep, &val) || ep != buf + len)
624        return JS_FALSE;
625
626    JSBool ok;
627    jsval numVal;
628    JSObject *obj = GetTopOfObjectStack(cx, jp);
629    if (obj && JS_NewNumberValue(cx, val, &numVal))
630        ok = PushValue(cx, jp, obj, numVal);
631    else
632        ok = JS_FALSE; // decode error
633
634    return ok;
635}
636
637static JSBool
638HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
639{
640    JSObject *obj = GetTopOfObjectStack(cx, jp);
641    JSString *str = js_NewStringCopyN(cx, buf, len);
642    if (!obj || !str)
643        return JS_FALSE;
644
645    return PushValue(cx, jp, obj, STRING_TO_JSVAL(str));
646}
647
648static JSBool
649HandleKeyword(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    JSObject *obj = GetTopOfObjectStack(cx, jp);
666    if (!obj)
667        return JS_FALSE;
668
669    return PushValue(cx, jp, obj, keyword);
670}
671
672static JSBool
673HandleData(JSContext *cx, JSONParser *jp, JSONDataType type)
674{
675  JSBool ok = JS_FALSE;
676
677  if (!STRING_BUFFER_OK(jp->buffer))
678      return JS_FALSE;
679
680  switch (type) {
681    case JSON_DATA_STRING:
682      ok = HandleString(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
683      break;
684
685    case JSON_DATA_KEYSTRING:
686      js_AppendUCString(jp->objectKey, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
687      ok = STRING_BUFFER_OK(jp->objectKey);
688      break;
689
690    case JSON_DATA_NUMBER:
691      ok = HandleNumber(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
692      break;
693
694    case JSON_DATA_KEYWORD:
695      ok = HandleKeyword(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
696      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
708JSBool
709js_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    for (i = 0; i < len; i++) {
718        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                    if (*jp->statep != JSON_PARSE_STATE_ARRAY)
726                        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                }
741
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                if (c == '}') {
771                    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
793            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
808            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
816            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                    if (!HandleData(cx, jp, jdt))
827                        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
835            case JSON_PARSE_STATE_STRING_ESCAPE:
836                switch (c) {
837                    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
861            case JSON_PARSE_STATE_STRING_HEX:
862                if (('0' <= c) && (c <= '9'))
863                  jp->hexChar = (jp->hexChar << 4) | (c - '0');
864                else if (('a' <= c) && (c <= 'f'))
865                  jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
866                else if (('A' <= c) && (c <= 'F'))
867                  jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
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
879            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                    if (!PopState(jp))
886                        return JS_FALSE;
887
888                    if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
889                        return JS_FALSE;
890                }
891                break;
892
893            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                    if (!PopState(jp))
900                        return JS_FALSE;
901                    if (!HandleData(cx, jp, JSON_DATA_NUMBER))
902                        return JS_FALSE;
903                }
904                break;
905
906            case JSON_PARSE_STATE_FINISHED:
907                if (!JS_ISXMLSPACE(c))
908                  return JS_FALSE; // extra input
909
910                break;
911
912            default:
913                JS_NOT_REACHED("Invalid JSON parser state");
914      }
915    }
916
917    return JS_TRUE;
918}
919
920#if JS_HAS_TOSOURCE
921static JSBool
922json_toSource(JSContext *cx, uintN argc, jsval *vp)
923{
924    *vp = ATOM_KEY(CLASS_ATOM(cx, JSON));
925    return JS_TRUE;
926}
927#endif
928
929static 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
938JSObject *
939js_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                           JS_PropertyStub, JS_PropertyStub, 0))
948        return NULL;
949
950    if (!JS_DefineFunctions(cx, JSON, json_static_methods))
951        return NULL;
952
953    return JSON;
954}