PageRenderTime 205ms CodeModel.GetById 22ms 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
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  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. #include "jsapi.h"
  38. #include "jsarena.h"
  39. #include "jsarray.h"
  40. #include "jsatom.h"
  41. #include "jsbool.h"
  42. #include "jscntxt.h"
  43. #include "jsdtoa.h"
  44. #include "jsinterp.h"
  45. #include "jsiter.h"
  46. #include "jsnum.h"
  47. #include "jsobj.h"
  48. #include "jsprf.h"
  49. #include "jsscan.h"
  50. #include "jsstr.h"
  51. #include "jstypes.h"
  52. #include "jsutil.h"
  53. #include "json.h"
  54. JSClass js_JSONClass = {
  55. js_JSON_str,
  56. JSCLASS_HAS_CACHED_PROTO(JSProto_JSON),
  57. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  58. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  59. JSCLASS_NO_OPTIONAL_MEMBERS
  60. };
  61. JSBool
  62. js_json_parse(JSContext *cx, uintN argc, jsval *vp)
  63. {
  64. JSString *s = NULL;
  65. jsval *argv = vp + 2;
  66. // Must throw an Error if there isn't a first arg
  67. if (!JS_ConvertArguments(cx, argc, argv, "S", &s))
  68. return JS_FALSE;
  69. JSONParser *jp = js_BeginJSONParse(cx, vp);
  70. JSBool ok = jp != NULL;
  71. if (ok) {
  72. ok = js_ConsumeJSONText(cx, jp, JS_GetStringChars(s), JS_GetStringLength(s));
  73. ok &= js_FinishJSONParse(cx, jp);
  74. }
  75. if (!ok)
  76. JS_ReportError(cx, "Error parsing JSON");
  77. return ok;
  78. }
  79. class StringifyClosure : JSAutoTempValueRooter
  80. {
  81. public:
  82. StringifyClosure(JSContext *cx, size_t len, jsval *vec)
  83. : JSAutoTempValueRooter(cx, len, vec), cx(cx), s(vec)
  84. {
  85. }
  86. JSContext *cx;
  87. jsval *s;
  88. };
  89. static JSBool
  90. WriteCallback(const jschar *buf, uint32 len, void *data)
  91. {
  92. StringifyClosure *sc = static_cast<StringifyClosure*>(data);
  93. JSString *s1 = JSVAL_TO_STRING(sc->s[0]);
  94. JSString *s2 = js_NewStringCopyN(sc->cx, buf, len);
  95. if (!s2)
  96. return JS_FALSE;
  97. sc->s[1] = STRING_TO_JSVAL(s2);
  98. s1 = js_ConcatStrings(sc->cx, s1, s2);
  99. if (!s1)
  100. return JS_FALSE;
  101. sc->s[0] = STRING_TO_JSVAL(s1);
  102. sc->s[1] = JSVAL_VOID;
  103. return JS_TRUE;
  104. }
  105. JSBool
  106. js_json_stringify(JSContext *cx, uintN argc, jsval *vp)
  107. {
  108. JSObject *obj;
  109. jsval *argv = vp + 2;
  110. // Must throw an Error if there isn't a first arg
  111. if (!JS_ConvertArguments(cx, argc, argv, "o", &obj))
  112. return JS_FALSE;
  113. // Only use objects and arrays as the root for now
  114. *vp = OBJECT_TO_JSVAL(obj);
  115. JSBool ok = js_TryJSON(cx, vp);
  116. JSType type;
  117. if (!ok ||
  118. JSVAL_IS_PRIMITIVE(*vp) ||
  119. ((type = JS_TypeOfValue(cx, *vp)) == JSTYPE_FUNCTION ||
  120. type == JSTYPE_XML)) {
  121. JS_ReportError(cx, "Invalid argument");
  122. return JS_FALSE;
  123. }
  124. JSString *s = JS_NewStringCopyN(cx, "", 0);
  125. if (!s)
  126. ok = JS_FALSE;
  127. if (ok) {
  128. jsval vec[2] = {STRING_TO_JSVAL(s), JSVAL_VOID};
  129. StringifyClosure sc(cx, 2, vec);
  130. JSAutoTempValueRooter resultTvr(cx, 1, sc.s);
  131. ok = js_Stringify(cx, vp, NULL, &WriteCallback, &sc, 0);
  132. *vp = *sc.s;
  133. }
  134. return ok;
  135. }
  136. JSBool
  137. js_TryJSON(JSContext *cx, jsval *vp)
  138. {
  139. // Checks whether the return value implements toJSON()
  140. JSBool ok = JS_TRUE;
  141. if (!JSVAL_IS_PRIMITIVE(*vp)) {
  142. JSObject *obj = JSVAL_TO_OBJECT(*vp);
  143. ok = js_TryMethod(cx, obj, cx->runtime->atomState.toJSONAtom, 0, NULL, vp);
  144. }
  145. return ok;
  146. }
  147. static const jschar quote = jschar('"');
  148. static const jschar backslash = jschar('\\');
  149. static const jschar unicodeEscape[] = {'\\', 'u', '0', '0'};
  150. static JSBool
  151. write_string(JSContext *cx, JSONWriteCallback callback, void *data, const jschar *buf, uint32 len)
  152. {
  153. if (!callback(&quote, 1, data))
  154. return JS_FALSE;
  155. uint32 mark = 0;
  156. uint32 i;
  157. for (i = 0; i < len; ++i) {
  158. if (buf[i] == quote || buf[i] == backslash) {
  159. if (!callback(&buf[mark], i - mark, data) || !callback(&backslash, 1, data) ||
  160. !callback(&buf[i], 1, data)) {
  161. return JS_FALSE;
  162. }
  163. mark = i + 1;
  164. } else if (buf[i] <= 31 || buf[i] == 127) {
  165. if (!callback(&buf[mark], i - mark, data) || !callback(unicodeEscape, 4, data))
  166. return JS_FALSE;
  167. char ubuf[3];
  168. size_t len = JS_snprintf(ubuf, sizeof(ubuf), "%.2x", buf[i]);
  169. JS_ASSERT(len == 2);
  170. jschar wbuf[3];
  171. size_t wbufSize = JS_ARRAY_LENGTH(wbuf);
  172. if (!js_InflateStringToBuffer(cx, ubuf, len, wbuf, &wbufSize) ||
  173. !callback(wbuf, wbufSize, data)) {
  174. return JS_FALSE;
  175. }
  176. mark = i + 1;
  177. }
  178. }
  179. if (mark < len && !callback(&buf[mark], len - mark, data))
  180. return JS_FALSE;
  181. if (!callback(&quote, 1, data))
  182. return JS_FALSE;
  183. return JS_TRUE;
  184. }
  185. JSBool
  186. js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer,
  187. JSONWriteCallback callback, void *data, uint32 depth)
  188. {
  189. if (depth > JSON_MAX_DEPTH)
  190. return JS_FALSE; /* encoding error */
  191. JSBool ok = JS_TRUE;
  192. JSObject *obj = JSVAL_TO_OBJECT(*vp);
  193. JSBool isArray = JS_IsArrayObject(cx, obj);
  194. jschar output = jschar(isArray ? '[' : '{');
  195. if (!callback(&output, 1, data))
  196. return JS_FALSE;
  197. JSObject *iterObj = NULL;
  198. jsint i = 0;
  199. jsuint length = 0;
  200. if (isArray) {
  201. if (!js_GetLengthProperty(cx, obj, &length))
  202. return JS_FALSE;
  203. } else {
  204. if (!js_ValueToIterator(cx, JSITER_ENUMERATE, vp))
  205. return JS_FALSE;
  206. iterObj = JSVAL_TO_OBJECT(*vp);
  207. }
  208. jsval outputValue = JSVAL_VOID;
  209. JSAutoTempValueRooter tvr(cx, 1, &outputValue);
  210. jsval key;
  211. JSBool memberWritten = JS_FALSE;
  212. do {
  213. outputValue = JSVAL_VOID;
  214. if (isArray) {
  215. if ((jsuint)i >= length)
  216. break;
  217. ok = OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(i), &outputValue);
  218. i++;
  219. } else {
  220. ok = js_CallIteratorNext(cx, iterObj, &key);
  221. if (!ok)
  222. break;
  223. if (key == JSVAL_HOLE)
  224. break;
  225. JSString *ks;
  226. if (JSVAL_IS_STRING(key)) {
  227. ks = JSVAL_TO_STRING(key);
  228. } else {
  229. ks = js_ValueToString(cx, key);
  230. if (!ks) {
  231. ok = JS_FALSE;
  232. break;
  233. }
  234. }
  235. ok = JS_GetUCProperty(cx, obj, JS_GetStringChars(ks),
  236. JS_GetStringLength(ks), &outputValue);
  237. }
  238. if (!ok)
  239. break;
  240. // if this is an array, holes are transmitted as null
  241. if (isArray && outputValue == JSVAL_VOID) {
  242. outputValue = JSVAL_NULL;
  243. } else if (JSVAL_IS_OBJECT(outputValue)) {
  244. ok = js_TryJSON(cx, &outputValue);
  245. if (!ok)
  246. break;
  247. }
  248. // elide undefined values
  249. if (outputValue == JSVAL_VOID)
  250. continue;
  251. // output a comma unless this is the first member to write
  252. if (memberWritten) {
  253. output = jschar(',');
  254. ok = callback(&output, 1, data);
  255. if (!ok)
  256. break;
  257. }
  258. memberWritten = JS_TRUE;
  259. JSType type = JS_TypeOfValue(cx, outputValue);
  260. // Can't encode these types, so drop them
  261. if (type == JSTYPE_FUNCTION || type == JSTYPE_XML)
  262. break;
  263. // Be careful below, this string is weakly rooted.
  264. JSString *s;
  265. // If this isn't an array, we need to output a key
  266. if (!isArray) {
  267. s = js_ValueToString(cx, key);
  268. if (!s) {
  269. ok = JS_FALSE;
  270. break;
  271. }
  272. ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s));
  273. if (!ok)
  274. break;
  275. output = jschar(':');
  276. ok = callback(&output, 1, data);
  277. if (!ok)
  278. break;
  279. }
  280. if (!JSVAL_IS_PRIMITIVE(outputValue)) {
  281. // recurse
  282. ok = js_Stringify(cx, &outputValue, replacer, callback, data, depth + 1);
  283. } else {
  284. JSString *outputString;
  285. s = js_ValueToString(cx, outputValue);
  286. if (!s) {
  287. ok = JS_FALSE;
  288. break;
  289. }
  290. if (type == JSTYPE_STRING) {
  291. ok = write_string(cx, callback, data, JS_GetStringChars(s), JS_GetStringLength(s));
  292. if (!ok)
  293. break;
  294. continue;
  295. }
  296. if (type == JSTYPE_NUMBER) {
  297. if (JSVAL_IS_DOUBLE(outputValue)) {
  298. jsdouble d = *JSVAL_TO_DOUBLE(outputValue);
  299. if (!JSDOUBLE_IS_FINITE(d))
  300. outputString = JS_NewStringCopyN(cx, "null", 4);
  301. else
  302. outputString = s;
  303. } else {
  304. outputString = s;
  305. }
  306. } else if (type == JSTYPE_BOOLEAN) {
  307. outputString = s;
  308. } else if (JSVAL_IS_NULL(outputValue)) {
  309. outputString = JS_NewStringCopyN(cx, "null", 4);
  310. } else {
  311. ok = JS_FALSE; // encoding error
  312. break;
  313. }
  314. if (!outputString) {
  315. ok = JS_FALSE;
  316. break;
  317. }
  318. ok = callback(JS_GetStringChars(outputString), JS_GetStringLength(outputString), data);
  319. }
  320. } while (ok);
  321. if (iterObj) {
  322. // Always close the iterator, but make sure not to stomp on OK
  323. ok &= js_CloseIterator(cx, *vp);
  324. // encoding error or propagate? FIXME: Bug 408838.
  325. }
  326. if (!ok) {
  327. JS_ReportError(cx, "Error during JSON encoding");
  328. return JS_FALSE;
  329. }
  330. output = jschar(isArray ? ']' : '}');
  331. ok = callback(&output, 1, data);
  332. return ok;
  333. }
  334. // helper to determine whether a character could be part of a number
  335. static JSBool IsNumChar(jschar c)
  336. {
  337. return ((c <= '9' && c >= '0') || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E');
  338. }
  339. JSONParser *
  340. js_BeginJSONParse(JSContext *cx, jsval *rootVal)
  341. {
  342. if (!cx)
  343. return NULL;
  344. JSObject *arr = js_NewArrayObject(cx, 0, NULL);
  345. if (!arr)
  346. return NULL;
  347. JSONParser *jp = (JSONParser*) JS_malloc(cx, sizeof(JSONParser));
  348. if (!jp)
  349. return NULL;
  350. jp->buffer = NULL;
  351. jp->objectStack = arr;
  352. if (!js_AddRoot(cx, &jp->objectStack, "JSON parse stack"))
  353. goto bad;
  354. jp->hexChar = 0;
  355. jp->numHex = 0;
  356. jp->statep = jp->stateStack;
  357. *jp->statep = JSON_PARSE_STATE_INIT;
  358. jp->rootVal = rootVal;
  359. jp->objectKey = (JSStringBuffer*) JS_malloc(cx, sizeof(JSStringBuffer));
  360. if (!jp->objectKey)
  361. goto bad;
  362. js_InitStringBuffer(jp->objectKey);
  363. jp->buffer = (JSStringBuffer*) JS_malloc(cx, sizeof(JSStringBuffer));
  364. if (!jp->buffer)
  365. goto bad;
  366. js_InitStringBuffer(jp->buffer);
  367. return jp;
  368. bad:
  369. JS_free(cx, jp->buffer);
  370. JS_free(cx, jp);
  371. return NULL;
  372. }
  373. JSBool
  374. js_FinishJSONParse(JSContext *cx, JSONParser *jp)
  375. {
  376. if (!jp)
  377. return JS_TRUE;
  378. if (jp->objectKey)
  379. js_FinishStringBuffer(jp->objectKey);
  380. JS_free(cx, jp->objectKey);
  381. if (jp->buffer)
  382. js_FinishStringBuffer(jp->buffer);
  383. JS_free(cx, jp->buffer);
  384. if (!js_RemoveRoot(cx->runtime, &jp->objectStack))
  385. return JS_FALSE;
  386. JSBool ok = *jp->statep == JSON_PARSE_STATE_FINISHED;
  387. JS_free(cx, jp);
  388. return ok;
  389. }
  390. static JSBool
  391. PushState(JSONParser *jp, JSONParserState state)
  392. {
  393. if (*jp->statep == JSON_PARSE_STATE_FINISHED)
  394. return JS_FALSE; // extra input
  395. jp->statep++;
  396. if ((uint32)(jp->statep - jp->stateStack) >= JS_ARRAY_LENGTH(jp->stateStack))
  397. return JS_FALSE; // too deep
  398. *jp->statep = state;
  399. return JS_TRUE;
  400. }
  401. static JSBool
  402. PopState(JSONParser *jp)
  403. {
  404. jp->statep--;
  405. if (jp->statep < jp->stateStack) {
  406. jp->statep = jp->stateStack;
  407. return JS_FALSE;
  408. }
  409. if (*jp->statep == JSON_PARSE_STATE_INIT)
  410. *jp->statep = JSON_PARSE_STATE_FINISHED;
  411. return JS_TRUE;
  412. }
  413. static JSBool
  414. PushValue(JSContext *cx, JSONParser *jp, JSObject *parent, jsval value)
  415. {
  416. JSAutoTempValueRooter tvr(cx, 1, &value);
  417. JSBool ok;
  418. if (OBJ_IS_ARRAY(cx, parent)) {
  419. jsuint len;
  420. ok = js_GetLengthProperty(cx, parent, &len);
  421. if (ok) {
  422. ok = OBJ_DEFINE_PROPERTY(cx, parent, INT_TO_JSID(len), value,
  423. NULL, NULL, JSPROP_ENUMERATE, NULL);
  424. }
  425. } else {
  426. ok = JS_DefineUCProperty(cx, parent, jp->objectKey->base,
  427. STRING_BUFFER_OFFSET(jp->objectKey), value,
  428. NULL, NULL, JSPROP_ENUMERATE);
  429. js_FinishStringBuffer(jp->objectKey);
  430. js_InitStringBuffer(jp->objectKey);
  431. }
  432. return ok;
  433. }
  434. static JSBool
  435. PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
  436. {
  437. jsuint len;
  438. if (!js_GetLengthProperty(cx, jp->objectStack, &len))
  439. return JS_FALSE;
  440. if (len >= JSON_MAX_DEPTH)
  441. return JS_FALSE; // decoding error
  442. jsval v = OBJECT_TO_JSVAL(obj);
  443. JSAutoTempValueRooter tvr(cx, v);
  444. // Check if this is the root object
  445. if (len == 0) {
  446. *jp->rootVal = v;
  447. // This property must be enumerable to keep the array dense
  448. if (!OBJ_DEFINE_PROPERTY(cx, jp->objectStack, INT_TO_JSID(0), *jp->rootVal,
  449. NULL, NULL, JSPROP_ENUMERATE, NULL)) {
  450. return JS_FALSE;
  451. }
  452. return JS_TRUE;
  453. }
  454. jsval p;
  455. if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len - 1), &p))
  456. return JS_FALSE;
  457. JS_ASSERT(JSVAL_IS_OBJECT(p));
  458. JSObject *parent = JSVAL_TO_OBJECT(p);
  459. if (!PushValue(cx, jp, parent, OBJECT_TO_JSVAL(obj)))
  460. return JS_FALSE;
  461. // This property must be enumerable to keep the array dense
  462. if (!OBJ_DEFINE_PROPERTY(cx, jp->objectStack, INT_TO_JSID(len), v,
  463. NULL, NULL, JSPROP_ENUMERATE, NULL)) {
  464. return JS_FALSE;
  465. }
  466. return JS_TRUE;
  467. }
  468. static JSObject *
  469. GetTopOfObjectStack(JSContext *cx, JSONParser *jp)
  470. {
  471. jsuint length;
  472. if (!js_GetLengthProperty(cx, jp->objectStack, &length))
  473. return NULL;
  474. jsval o;
  475. if (!OBJ_GET_PROPERTY(cx, jp->objectStack, INT_TO_JSID(length - 1), &o))
  476. return NULL;
  477. JS_ASSERT(!JSVAL_IS_PRIMITIVE(o));
  478. return JSVAL_TO_OBJECT(o);
  479. }
  480. static JSBool
  481. OpenObject(JSContext *cx, JSONParser *jp)
  482. {
  483. JSObject *obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
  484. if (!obj)
  485. return JS_FALSE;
  486. return PushObject(cx, jp, obj);
  487. }
  488. static JSBool
  489. OpenArray(JSContext *cx, JSONParser *jp)
  490. {
  491. // Add an array to an existing array or object
  492. JSObject *arr = js_NewArrayObject(cx, 0, NULL);
  493. if (!arr)
  494. return JS_FALSE;
  495. return PushObject(cx, jp, arr);
  496. }
  497. static JSBool
  498. CloseObject(JSContext *cx, JSONParser *jp)
  499. {
  500. jsuint len;
  501. if (!js_GetLengthProperty(cx, jp->objectStack, &len))
  502. return JS_FALSE;
  503. if (!js_SetLengthProperty(cx, jp->objectStack, len - 1))
  504. return JS_FALSE;
  505. return JS_TRUE;
  506. }
  507. static JSBool
  508. CloseArray(JSContext *cx, JSONParser *jp)
  509. {
  510. return CloseObject(cx, jp);
  511. }
  512. static JSBool
  513. HandleNumber(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
  514. {
  515. const jschar *ep;
  516. double val;
  517. if (!js_strtod(cx, buf, buf + len, &ep, &val) || ep != buf + len)
  518. return JS_FALSE;
  519. JSBool ok;
  520. jsval numVal;
  521. JSObject *obj = GetTopOfObjectStack(cx, jp);
  522. if (obj && JS_NewNumberValue(cx, val, &numVal))
  523. ok = PushValue(cx, jp, obj, numVal);
  524. else
  525. ok = JS_FALSE; // decode error
  526. return ok;
  527. }
  528. static JSBool
  529. HandleString(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
  530. {
  531. JSObject *obj = GetTopOfObjectStack(cx, jp);
  532. JSString *str = js_NewStringCopyN(cx, buf, len);
  533. if (!obj || !str)
  534. return JS_FALSE;
  535. return PushValue(cx, jp, obj, STRING_TO_JSVAL(str));
  536. }
  537. static JSBool
  538. HandleKeyword(JSContext *cx, JSONParser *jp, const jschar *buf, uint32 len)
  539. {
  540. jsval keyword;
  541. JSTokenType tt = js_CheckKeyword(buf, len);
  542. if (tt != TOK_PRIMARY)
  543. return JS_FALSE;
  544. if (buf[0] == 'n')
  545. keyword = JSVAL_NULL;
  546. else if (buf[0] == 't')
  547. keyword = JSVAL_TRUE;
  548. else if (buf[0] == 'f')
  549. keyword = JSVAL_FALSE;
  550. else
  551. return JS_FALSE;
  552. JSObject *obj = GetTopOfObjectStack(cx, jp);
  553. if (!obj)
  554. return JS_FALSE;
  555. return PushValue(cx, jp, obj, keyword);
  556. }
  557. static JSBool
  558. HandleData(JSContext *cx, JSONParser *jp, JSONDataType type)
  559. {
  560. JSBool ok = JS_FALSE;
  561. if (!STRING_BUFFER_OK(jp->buffer))
  562. return JS_FALSE;
  563. switch (type) {
  564. case JSON_DATA_STRING:
  565. ok = HandleString(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
  566. break;
  567. case JSON_DATA_KEYSTRING:
  568. js_AppendUCString(jp->objectKey, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
  569. ok = STRING_BUFFER_OK(jp->objectKey);
  570. break;
  571. case JSON_DATA_NUMBER:
  572. ok = HandleNumber(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
  573. break;
  574. case JSON_DATA_KEYWORD:
  575. ok = HandleKeyword(cx, jp, jp->buffer->base, STRING_BUFFER_OFFSET(jp->buffer));
  576. break;
  577. default:
  578. JS_NOT_REACHED("Should have a JSON data type");
  579. }
  580. js_FinishStringBuffer(jp->buffer);
  581. js_InitStringBuffer(jp->buffer);
  582. return ok;
  583. }
  584. JSBool
  585. js_ConsumeJSONText(JSContext *cx, JSONParser *jp, const jschar *data, uint32 len)
  586. {
  587. uint32 i;
  588. if (*jp->statep == JSON_PARSE_STATE_INIT) {
  589. PushState(jp, JSON_PARSE_STATE_OBJECT_VALUE);
  590. }
  591. for (i = 0; i < len; i++) {
  592. jschar c = data[i];
  593. switch (*jp->statep) {
  594. case JSON_PARSE_STATE_VALUE :
  595. if (c == ']') {
  596. // empty array
  597. if (!PopState(jp))
  598. return JS_FALSE;
  599. if (*jp->statep != JSON_PARSE_STATE_ARRAY)
  600. return JS_FALSE; // unexpected char
  601. if (!CloseArray(cx, jp) || !PopState(jp))
  602. return JS_FALSE;
  603. break;
  604. }
  605. if (c == '}') {
  606. // we should only find these in OBJECT_KEY state
  607. return JS_FALSE; // unexpected failure
  608. }
  609. if (c == '"') {
  610. *jp->statep = JSON_PARSE_STATE_STRING;
  611. break;
  612. }
  613. if (IsNumChar(c)) {
  614. *jp->statep = JSON_PARSE_STATE_NUMBER;
  615. js_AppendChar(jp->buffer, c);
  616. break;
  617. }
  618. if (JS7_ISLET(c)) {
  619. *jp->statep = JSON_PARSE_STATE_KEYWORD;
  620. js_AppendChar(jp->buffer, c);
  621. break;
  622. }
  623. // fall through in case the value is an object or array
  624. case JSON_PARSE_STATE_OBJECT_VALUE :
  625. if (c == '{') {
  626. *jp->statep = JSON_PARSE_STATE_OBJECT;
  627. if (!OpenObject(cx, jp) || !PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
  628. return JS_FALSE;
  629. } else if (c == '[') {
  630. *jp->statep = JSON_PARSE_STATE_ARRAY;
  631. if (!OpenArray(cx, jp) || !PushState(jp, JSON_PARSE_STATE_VALUE))
  632. return JS_FALSE;
  633. } else if (!JS_ISXMLSPACE(c)) {
  634. return JS_FALSE; // unexpected
  635. }
  636. break;
  637. case JSON_PARSE_STATE_OBJECT :
  638. if (c == '}') {
  639. if (!CloseObject(cx, jp) || !PopState(jp))
  640. return JS_FALSE;
  641. } else if (c == ',') {
  642. if (!PushState(jp, JSON_PARSE_STATE_OBJECT_PAIR))
  643. return JS_FALSE;
  644. } else if (c == ']' || !JS_ISXMLSPACE(c)) {
  645. return JS_FALSE; // unexpected
  646. }
  647. break;
  648. case JSON_PARSE_STATE_ARRAY :
  649. if (c == ']') {
  650. if (!CloseArray(cx, jp) || !PopState(jp))
  651. return JS_FALSE;
  652. } else if (c == ',') {
  653. if (!PushState(jp, JSON_PARSE_STATE_VALUE))
  654. return JS_FALSE;
  655. } else if (!JS_ISXMLSPACE(c)) {
  656. return JS_FALSE; // unexpected
  657. }
  658. break;
  659. case JSON_PARSE_STATE_OBJECT_PAIR :
  660. if (c == '"') {
  661. // we want to be waiting for a : when the string has been read
  662. *jp->statep = JSON_PARSE_STATE_OBJECT_IN_PAIR;
  663. if (!PushState(jp, JSON_PARSE_STATE_STRING))
  664. return JS_FALSE;
  665. } else if (c == '}') {
  666. // pop off the object pair state and the object state
  667. if (!CloseObject(cx, jp) || !PopState(jp) || !PopState(jp))
  668. return JS_FALSE;
  669. } else if (c == ']' || !JS_ISXMLSPACE(c)) {
  670. return JS_FALSE; // unexpected
  671. }
  672. break;
  673. case JSON_PARSE_STATE_OBJECT_IN_PAIR:
  674. if (c == ':') {
  675. *jp->statep = JSON_PARSE_STATE_VALUE;
  676. } else if (!JS_ISXMLSPACE(c)) {
  677. return JS_FALSE; // unexpected
  678. }
  679. break;
  680. case JSON_PARSE_STATE_STRING:
  681. if (c == '"') {
  682. if (!PopState(jp))
  683. return JS_FALSE;
  684. JSONDataType jdt;
  685. if (*jp->statep == JSON_PARSE_STATE_OBJECT_IN_PAIR) {
  686. jdt = JSON_DATA_KEYSTRING;
  687. } else {
  688. jdt = JSON_DATA_STRING;
  689. }
  690. if (!HandleData(cx, jp, jdt))
  691. return JS_FALSE;
  692. } else if (c == '\\') {
  693. *jp->statep = JSON_PARSE_STATE_STRING_ESCAPE;
  694. } else {
  695. js_AppendChar(jp->buffer, c);
  696. }
  697. break;
  698. case JSON_PARSE_STATE_STRING_ESCAPE:
  699. switch (c) {
  700. case '"':
  701. case '\\':
  702. case '/':
  703. break;
  704. case 'b' : c = '\b'; break;
  705. case 'f' : c = '\f'; break;
  706. case 'n' : c = '\n'; break;
  707. case 'r' : c = '\r'; break;
  708. case 't' : c = '\t'; break;
  709. default :
  710. if (c == 'u') {
  711. jp->numHex = 0;
  712. jp->hexChar = 0;
  713. *jp->statep = JSON_PARSE_STATE_STRING_HEX;
  714. continue;
  715. } else {
  716. return JS_FALSE; // unexpected
  717. }
  718. }
  719. js_AppendChar(jp->buffer, c);
  720. *jp->statep = JSON_PARSE_STATE_STRING;
  721. break;
  722. case JSON_PARSE_STATE_STRING_HEX:
  723. if (('0' <= c) && (c <= '9'))
  724. jp->hexChar = (jp->hexChar << 4) | (c - '0');
  725. else if (('a' <= c) && (c <= 'f'))
  726. jp->hexChar = (jp->hexChar << 4) | (c - 'a' + 0x0a);
  727. else if (('A' <= c) && (c <= 'F'))
  728. jp->hexChar = (jp->hexChar << 4) | (c - 'A' + 0x0a);
  729. else
  730. return JS_FALSE; // unexpected
  731. if (++(jp->numHex) == 4) {
  732. js_AppendChar(jp->buffer, jp->hexChar);
  733. jp->hexChar = 0;
  734. jp->numHex = 0;
  735. *jp->statep = JSON_PARSE_STATE_STRING;
  736. }
  737. break;
  738. case JSON_PARSE_STATE_KEYWORD:
  739. if (JS7_ISLET(c)) {
  740. js_AppendChar(jp->buffer, c);
  741. } else {
  742. // this character isn't part of the keyword, process it again
  743. i--;
  744. if (!PopState(jp))
  745. return JS_FALSE;
  746. if (!HandleData(cx, jp, JSON_DATA_KEYWORD))
  747. return JS_FALSE;
  748. }
  749. break;
  750. case JSON_PARSE_STATE_NUMBER:
  751. if (IsNumChar(c)) {
  752. js_AppendChar(jp->buffer, c);
  753. } else {
  754. // this character isn't part of the number, process it again
  755. i--;
  756. if (!PopState(jp))
  757. return JS_FALSE;
  758. if (!HandleData(cx, jp, JSON_DATA_NUMBER))
  759. return JS_FALSE;
  760. }
  761. break;
  762. case JSON_PARSE_STATE_FINISHED:
  763. if (!JS_ISXMLSPACE(c))
  764. return JS_FALSE; // extra input
  765. break;
  766. default:
  767. JS_NOT_REACHED("Invalid JSON parser state");
  768. }
  769. }
  770. return JS_TRUE;
  771. }
  772. #if JS_HAS_TOSOURCE
  773. static JSBool
  774. json_toSource(JSContext *cx, uintN argc, jsval *vp)
  775. {
  776. *vp = ATOM_KEY(CLASS_ATOM(cx, JSON));
  777. return JS_TRUE;
  778. }
  779. #endif
  780. static JSFunctionSpec json_static_methods[] = {
  781. #if JS_HAS_TOSOURCE
  782. JS_FN(js_toSource_str, json_toSource, 0, 0),
  783. #endif
  784. JS_FN("parse", js_json_parse, 0, 0),
  785. JS_FN("stringify", js_json_stringify, 0, 0),
  786. JS_FS_END
  787. };
  788. JSObject *
  789. js_InitJSONClass(JSContext *cx, JSObject *obj)
  790. {
  791. JSObject *JSON;
  792. JSON = JS_NewObject(cx, &js_JSONClass, NULL, obj);
  793. if (!JSON)
  794. return NULL;
  795. if (!JS_DefineProperty(cx, obj, js_JSON_str, OBJECT_TO_JSVAL(JSON),
  796. JS_PropertyStub, JS_PropertyStub, 0))
  797. return NULL;
  798. if (!JS_DefineFunctions(cx, JSON, json_static_methods))
  799. return NULL;
  800. return JSON;
  801. }