/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/json.cpp
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("e, 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("e, 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}