/js/src/jsclone.cpp

http://github.com/zpao/v8monkey · C++ · 886 lines · 713 code · 98 blank · 75 comment · 164 complexity · f4cc6bd3ff4097b132914664952ba51f MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * The Original Code is JavaScript structured data serialization.
  16. *
  17. * The Initial Developer of the Original Code is
  18. * the Mozilla Foundation.
  19. * Portions created by the Initial Developer are Copyright (C) 2010
  20. * the Initial Developer. All Rights Reserved.
  21. *
  22. * Contributor(s):
  23. * Jason Orendorff <jorendorff@mozilla.com>
  24. *
  25. * Alternatively, the contents of this file may be used under the terms of
  26. * either the GNU General Public License Version 2 or later (the "GPL"), or
  27. * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  28. * in which case the provisions of the GPL or the LGPL are applicable instead
  29. * of those above. If you wish to allow use of your version of this file only
  30. * under the terms of either the GPL or the LGPL, and not to allow others to
  31. * use your version of this file under the terms of the MPL, indicate your
  32. * decision by deleting the provisions above and replace them with the notice
  33. * and other provisions required by the GPL or the LGPL. If you do not delete
  34. * the provisions above, a recipient may use your version of this file under
  35. * the terms of any one of the MPL, the GPL or the LGPL.
  36. *
  37. * ***** END LICENSE BLOCK ***** */
  38. #include "jsclone.h"
  39. #include "jsdate.h"
  40. #include "jstypedarray.h"
  41. #include "jstypedarrayinlines.h"
  42. #include "vm/RegExpObject-inl.h"
  43. using namespace js;
  44. JS_FRIEND_API(uint64_t)
  45. js_GetSCOffset(JSStructuredCloneWriter* writer)
  46. {
  47. JS_ASSERT(writer);
  48. return writer->output().count() * sizeof(uint64_t);
  49. }
  50. namespace js {
  51. bool
  52. WriteStructuredClone(JSContext *cx, const Value &v, uint64_t **bufp, size_t *nbytesp,
  53. const JSStructuredCloneCallbacks *cb, void *cbClosure)
  54. {
  55. SCOutput out(cx);
  56. JSStructuredCloneWriter w(out, cb, cbClosure);
  57. return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp);
  58. }
  59. bool
  60. ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, Value *vp,
  61. const JSStructuredCloneCallbacks *cb, void *cbClosure)
  62. {
  63. SCInput in(cx, data, nbytes);
  64. JSStructuredCloneReader r(in, cb, cbClosure);
  65. return r.read(vp);
  66. }
  67. } /* namespace js */
  68. enum StructuredDataType {
  69. /* Structured data types provided by the engine */
  70. SCTAG_FLOAT_MAX = 0xFFF00000,
  71. SCTAG_NULL = 0xFFFF0000,
  72. SCTAG_UNDEFINED,
  73. SCTAG_BOOLEAN,
  74. SCTAG_INDEX,
  75. SCTAG_STRING,
  76. SCTAG_DATE_OBJECT,
  77. SCTAG_REGEXP_OBJECT,
  78. SCTAG_ARRAY_OBJECT,
  79. SCTAG_OBJECT_OBJECT,
  80. SCTAG_ARRAY_BUFFER_OBJECT,
  81. SCTAG_BOOLEAN_OBJECT,
  82. SCTAG_STRING_OBJECT,
  83. SCTAG_NUMBER_OBJECT,
  84. SCTAG_BACK_REFERENCE_OBJECT,
  85. SCTAG_TYPED_ARRAY_MIN = 0xFFFF0100,
  86. SCTAG_TYPED_ARRAY_MAX = SCTAG_TYPED_ARRAY_MIN + TypedArray::TYPE_MAX - 1,
  87. SCTAG_END_OF_BUILTIN_TYPES
  88. };
  89. JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES <= JS_SCTAG_USER_MIN);
  90. JS_STATIC_ASSERT(JS_SCTAG_USER_MIN <= JS_SCTAG_USER_MAX);
  91. static uint8_t
  92. SwapBytes(uint8_t u)
  93. {
  94. return u;
  95. }
  96. static uint16_t
  97. SwapBytes(uint16_t u)
  98. {
  99. #ifdef IS_BIG_ENDIAN
  100. return ((u & 0x00ff) << 8) | ((u & 0xff00) >> 8);
  101. #else
  102. return u;
  103. #endif
  104. }
  105. static uint32_t
  106. SwapBytes(uint32_t u)
  107. {
  108. #ifdef IS_BIG_ENDIAN
  109. return ((u & 0x000000ffU) << 24) |
  110. ((u & 0x0000ff00U) << 8) |
  111. ((u & 0x00ff0000U) >> 8) |
  112. ((u & 0xff000000U) >> 24);
  113. #else
  114. return u;
  115. #endif
  116. }
  117. static uint64_t
  118. SwapBytes(uint64_t u)
  119. {
  120. #ifdef IS_BIG_ENDIAN
  121. return ((u & 0x00000000000000ffLLU) << 56) |
  122. ((u & 0x000000000000ff00LLU) << 40) |
  123. ((u & 0x0000000000ff0000LLU) << 24) |
  124. ((u & 0x00000000ff000000LLU) << 8) |
  125. ((u & 0x000000ff00000000LLU) >> 8) |
  126. ((u & 0x0000ff0000000000LLU) >> 24) |
  127. ((u & 0x00ff000000000000LLU) >> 40) |
  128. ((u & 0xff00000000000000LLU) >> 56);
  129. #else
  130. return u;
  131. #endif
  132. }
  133. bool
  134. SCInput::eof()
  135. {
  136. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "truncated");
  137. return false;
  138. }
  139. SCInput::SCInput(JSContext *cx, const uint64_t *data, size_t nbytes)
  140. : cx(cx), point(data), end(data + nbytes / 8)
  141. {
  142. JS_ASSERT((uintptr_t(data) & 7) == 0);
  143. JS_ASSERT((nbytes & 7) == 0);
  144. }
  145. bool
  146. SCInput::read(uint64_t *p)
  147. {
  148. if (point == end)
  149. return eof();
  150. *p = SwapBytes(*point++);
  151. return true;
  152. }
  153. bool
  154. SCInput::readPair(uint32_t *tagp, uint32_t *datap)
  155. {
  156. uint64_t u = 0; /* initialize to shut GCC up */
  157. bool ok = read(&u);
  158. if (ok) {
  159. *tagp = uint32_t(u >> 32);
  160. *datap = uint32_t(u);
  161. }
  162. return ok;
  163. }
  164. /*
  165. * The purpose of this never-inlined function is to avoid a strange g++ build
  166. * error on OS X 10.5 (see bug 624080). :-(
  167. */
  168. static JS_NEVER_INLINE double
  169. CanonicalizeNan(double d)
  170. {
  171. return JS_CANONICALIZE_NAN(d);
  172. }
  173. bool
  174. SCInput::readDouble(jsdouble *p)
  175. {
  176. union {
  177. uint64_t u;
  178. jsdouble d;
  179. } pun;
  180. if (!read(&pun.u))
  181. return false;
  182. *p = CanonicalizeNan(pun.d);
  183. return true;
  184. }
  185. template <class T>
  186. bool
  187. SCInput::readArray(T *p, size_t nelems)
  188. {
  189. JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
  190. /*
  191. * Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is
  192. * larger than the remaining data.
  193. */
  194. size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
  195. if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems || nwords > size_t(end - point))
  196. return eof();
  197. if (sizeof(T) == 1) {
  198. js_memcpy(p, point, nelems);
  199. } else {
  200. const T *q = (const T *) point;
  201. const T *qend = q + nelems;
  202. while (q != qend)
  203. *p++ = ::SwapBytes(*q++);
  204. }
  205. point += nwords;
  206. return true;
  207. }
  208. bool
  209. SCInput::readBytes(void *p, size_t nbytes)
  210. {
  211. return readArray((uint8_t *) p, nbytes);
  212. }
  213. bool
  214. SCInput::readChars(jschar *p, size_t nchars)
  215. {
  216. JS_ASSERT(sizeof(jschar) == sizeof(uint16_t));
  217. return readArray((uint16_t *) p, nchars);
  218. }
  219. SCOutput::SCOutput(JSContext *cx) : cx(cx), buf(cx) {}
  220. bool
  221. SCOutput::write(uint64_t u)
  222. {
  223. return buf.append(SwapBytes(u));
  224. }
  225. static inline uint64_t
  226. PairToUInt64(uint32_t tag, uint32_t data)
  227. {
  228. return uint64_t(data) | (uint64_t(tag) << 32);
  229. }
  230. bool
  231. SCOutput::writePair(uint32_t tag, uint32_t data)
  232. {
  233. /*
  234. * As it happens, the tag word appears after the data word in the output.
  235. * This is because exponents occupy the last 2 bytes of jsdoubles on the
  236. * little-endian platforms we care most about.
  237. *
  238. * For example, JSVAL_TRUE is written using writePair(SCTAG_BOOLEAN, 1).
  239. * PairToUInt64 produces the number 0xFFFF000200000001.
  240. * That is written out as the bytes 01 00 00 00 02 00 FF FF.
  241. */
  242. return write(PairToUInt64(tag, data));
  243. }
  244. static inline uint64_t
  245. ReinterpretDoubleAsUInt64(jsdouble d)
  246. {
  247. union {
  248. jsdouble d;
  249. uint64_t u;
  250. } pun;
  251. pun.d = d;
  252. return pun.u;
  253. }
  254. static inline jsdouble
  255. ReinterpretUInt64AsDouble(uint64_t u)
  256. {
  257. union {
  258. uint64_t u;
  259. jsdouble d;
  260. } pun;
  261. pun.u = u;
  262. return pun.d;
  263. }
  264. static inline jsdouble
  265. ReinterpretPairAsDouble(uint32_t tag, uint32_t data)
  266. {
  267. return ReinterpretUInt64AsDouble(PairToUInt64(tag, data));
  268. }
  269. bool
  270. SCOutput::writeDouble(jsdouble d)
  271. {
  272. return write(ReinterpretDoubleAsUInt64(CanonicalizeNan(d)));
  273. }
  274. template <class T>
  275. bool
  276. SCOutput::writeArray(const T *p, size_t nelems)
  277. {
  278. JS_ASSERT(8 % sizeof(T) == 0);
  279. JS_ASSERT(sizeof(uint64_t) % sizeof(T) == 0);
  280. if (nelems == 0)
  281. return true;
  282. if (nelems + sizeof(uint64_t) / sizeof(T) - 1 < nelems) {
  283. js_ReportAllocationOverflow(context());
  284. return false;
  285. }
  286. size_t nwords = JS_HOWMANY(nelems, sizeof(uint64_t) / sizeof(T));
  287. size_t start = buf.length();
  288. if (!buf.growByUninitialized(nwords))
  289. return false;
  290. buf.back() = 0; /* zero-pad to an 8-byte boundary */
  291. T *q = (T *) &buf[start];
  292. if (sizeof(T) == 1) {
  293. js_memcpy(q, p, nelems);
  294. } else {
  295. const T *pend = p + nelems;
  296. while (p != pend)
  297. *q++ = ::SwapBytes(*p++);
  298. }
  299. return true;
  300. }
  301. bool
  302. SCOutput::writeBytes(const void *p, size_t nbytes)
  303. {
  304. return writeArray((const uint8_t *) p, nbytes);
  305. }
  306. bool
  307. SCOutput::writeChars(const jschar *p, size_t nchars)
  308. {
  309. JS_ASSERT(sizeof(jschar) == sizeof(uint16_t));
  310. return writeArray((const uint16_t *) p, nchars);
  311. }
  312. bool
  313. SCOutput::extractBuffer(uint64_t **datap, size_t *sizep)
  314. {
  315. *sizep = buf.length() * sizeof(uint64_t);
  316. return (*datap = buf.extractRawBuffer()) != NULL;
  317. }
  318. JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX);
  319. bool
  320. JSStructuredCloneWriter::writeString(uint32_t tag, JSString *str)
  321. {
  322. size_t length = str->length();
  323. const jschar *chars = str->getChars(context());
  324. if (!chars)
  325. return false;
  326. return out.writePair(tag, uint32_t(length)) && out.writeChars(chars, length);
  327. }
  328. bool
  329. JSStructuredCloneWriter::writeId(jsid id)
  330. {
  331. if (JSID_IS_INT(id))
  332. return out.writePair(SCTAG_INDEX, uint32_t(JSID_TO_INT(id)));
  333. JS_ASSERT(JSID_IS_STRING(id));
  334. return writeString(SCTAG_STRING, JSID_TO_STRING(id));
  335. }
  336. inline void
  337. JSStructuredCloneWriter::checkStack()
  338. {
  339. #ifdef DEBUG
  340. /* To avoid making serialization O(n^2), limit stack-checking at 10. */
  341. const size_t MAX = 10;
  342. size_t limit = JS_MIN(counts.length(), MAX);
  343. JS_ASSERT(objs.length() == counts.length());
  344. size_t total = 0;
  345. for (size_t i = 0; i < limit; i++) {
  346. JS_ASSERT(total + counts[i] >= total);
  347. total += counts[i];
  348. }
  349. if (counts.length() <= MAX)
  350. JS_ASSERT(total == ids.length());
  351. else
  352. JS_ASSERT(total <= ids.length());
  353. size_t j = objs.length();
  354. for (size_t i = 0; i < limit; i++)
  355. JS_ASSERT(memory.has(&objs[--j].toObject()));
  356. #endif
  357. }
  358. static inline uint32_t
  359. ArrayTypeToTag(uint32_t type)
  360. {
  361. /*
  362. * As long as these are all true, we can just add. Note that for backward
  363. * compatibility, the tags cannot change. So if the ArrayType type codes
  364. * change, this function and TagToArrayType will have to do more work.
  365. */
  366. JS_STATIC_ASSERT(TypedArray::TYPE_INT8 == 0);
  367. JS_STATIC_ASSERT(TypedArray::TYPE_UINT8 == 1);
  368. JS_STATIC_ASSERT(TypedArray::TYPE_INT16 == 2);
  369. JS_STATIC_ASSERT(TypedArray::TYPE_UINT16 == 3);
  370. JS_STATIC_ASSERT(TypedArray::TYPE_INT32 == 4);
  371. JS_STATIC_ASSERT(TypedArray::TYPE_UINT32 == 5);
  372. JS_STATIC_ASSERT(TypedArray::TYPE_FLOAT32 == 6);
  373. JS_STATIC_ASSERT(TypedArray::TYPE_FLOAT64 == 7);
  374. JS_STATIC_ASSERT(TypedArray::TYPE_UINT8_CLAMPED == 8);
  375. JS_STATIC_ASSERT(TypedArray::TYPE_MAX == TypedArray::TYPE_UINT8_CLAMPED + 1);
  376. JS_ASSERT(type < TypedArray::TYPE_MAX);
  377. return SCTAG_TYPED_ARRAY_MIN + type;
  378. }
  379. static inline uint32_t
  380. TagToArrayType(uint32_t tag)
  381. {
  382. JS_ASSERT(SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX);
  383. return tag - SCTAG_TYPED_ARRAY_MIN;
  384. }
  385. bool
  386. JSStructuredCloneWriter::writeTypedArray(JSObject *obj)
  387. {
  388. JSObject *arr = TypedArray::getTypedArray(obj);
  389. if (!out.writePair(ArrayTypeToTag(TypedArray::getType(arr)), TypedArray::getLength(arr)))
  390. return false;
  391. switch (TypedArray::getType(arr)) {
  392. case TypedArray::TYPE_INT8:
  393. case TypedArray::TYPE_UINT8:
  394. case TypedArray::TYPE_UINT8_CLAMPED:
  395. return out.writeArray((const uint8_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr));
  396. case TypedArray::TYPE_INT16:
  397. case TypedArray::TYPE_UINT16:
  398. return out.writeArray((const uint16_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr));
  399. case TypedArray::TYPE_INT32:
  400. case TypedArray::TYPE_UINT32:
  401. case TypedArray::TYPE_FLOAT32:
  402. return out.writeArray((const uint32_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr));
  403. case TypedArray::TYPE_FLOAT64:
  404. return out.writeArray((const uint64_t *) TypedArray::getDataOffset(arr), TypedArray::getLength(arr));
  405. default:
  406. JS_NOT_REACHED("unknown TypedArray type");
  407. return false;
  408. }
  409. }
  410. bool
  411. JSStructuredCloneWriter::writeArrayBuffer(JSObject *obj)
  412. {
  413. obj = ArrayBuffer::getArrayBuffer(obj);
  414. return out.writePair(SCTAG_ARRAY_BUFFER_OBJECT, obj->arrayBufferByteLength()) &&
  415. out.writeBytes(obj->arrayBufferDataOffset(), obj->arrayBufferByteLength());
  416. }
  417. bool
  418. JSStructuredCloneWriter::startObject(JSObject *obj)
  419. {
  420. JS_ASSERT(obj->isArray() || obj->isObject());
  421. /* Handle cycles in the object graph. */
  422. CloneMemory::AddPtr p = memory.lookupForAdd(obj);
  423. if (p)
  424. return out.writePair(SCTAG_BACK_REFERENCE_OBJECT, p->value);
  425. if (!memory.add(p, obj, memory.count()))
  426. return false;
  427. if (memory.count() == UINT32_MAX) {
  428. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
  429. JSMSG_NEED_DIET, "object graph to serialize");
  430. return false;
  431. }
  432. /*
  433. * Get enumerable property ids and put them in reverse order so that they
  434. * will come off the stack in forward order.
  435. */
  436. size_t initialLength = ids.length();
  437. if (!GetPropertyNames(context(), obj, JSITER_OWNONLY, &ids))
  438. return false;
  439. jsid *begin = ids.begin() + initialLength, *end = ids.end();
  440. size_t count = size_t(end - begin);
  441. Reverse(begin, end);
  442. /* Push obj and count to the stack. */
  443. if (!objs.append(ObjectValue(*obj)) || !counts.append(count))
  444. return false;
  445. checkStack();
  446. /* Write the header for obj. */
  447. return out.writePair(obj->isArray() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
  448. }
  449. bool
  450. JSStructuredCloneWriter::startWrite(const js::Value &v)
  451. {
  452. if (v.isString()) {
  453. return writeString(SCTAG_STRING, v.toString());
  454. } else if (v.isNumber()) {
  455. return out.writeDouble(v.toNumber());
  456. } else if (v.isBoolean()) {
  457. return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
  458. } else if (v.isNull()) {
  459. return out.writePair(SCTAG_NULL, 0);
  460. } else if (v.isUndefined()) {
  461. return out.writePair(SCTAG_UNDEFINED, 0);
  462. } else if (v.isObject()) {
  463. JSObject *obj = &v.toObject();
  464. if (obj->isRegExp()) {
  465. RegExpObject &reobj = obj->asRegExp();
  466. return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
  467. writeString(SCTAG_STRING, reobj.getSource());
  468. } else if (obj->isDate()) {
  469. jsdouble d = js_DateGetMsecSinceEpoch(context(), obj);
  470. return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
  471. } else if (obj->isObject() || obj->isArray()) {
  472. return startObject(obj);
  473. } else if (js_IsTypedArray(obj)) {
  474. return writeTypedArray(obj);
  475. } else if (js_IsArrayBuffer(obj)) {
  476. return writeArrayBuffer(obj);
  477. } else if (obj->isBoolean()) {
  478. return out.writePair(SCTAG_BOOLEAN_OBJECT, obj->getPrimitiveThis().toBoolean());
  479. } else if (obj->isNumber()) {
  480. return out.writePair(SCTAG_NUMBER_OBJECT, 0) &&
  481. out.writeDouble(obj->getPrimitiveThis().toNumber());
  482. } else if (obj->isString()) {
  483. return writeString(SCTAG_STRING_OBJECT, obj->getPrimitiveThis().toString());
  484. }
  485. if (callbacks && callbacks->write)
  486. return callbacks->write(context(), this, obj, closure);
  487. /* else fall through */
  488. }
  489. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_UNSUPPORTED_TYPE);
  490. return false;
  491. }
  492. bool
  493. JSStructuredCloneWriter::write(const Value &v)
  494. {
  495. if (!startWrite(v))
  496. return false;
  497. while (!counts.empty()) {
  498. JSObject *obj = &objs.back().toObject();
  499. if (counts.back()) {
  500. counts.back()--;
  501. jsid id = ids.back();
  502. ids.popBack();
  503. checkStack();
  504. if (JSID_IS_STRING(id) || JSID_IS_INT(id)) {
  505. /*
  506. * If obj still has an own property named id, write it out.
  507. * The cost of re-checking could be avoided by using
  508. * NativeIterators.
  509. */
  510. JSObject *obj2;
  511. JSProperty *prop;
  512. if (!js_HasOwnProperty(context(), obj->getOps()->lookupGeneric, obj, id,
  513. &obj2, &prop)) {
  514. return false;
  515. }
  516. if (prop) {
  517. Value val;
  518. if (!writeId(id) ||
  519. !obj->getGeneric(context(), id, &val) ||
  520. !startWrite(val))
  521. return false;
  522. }
  523. }
  524. } else {
  525. out.writePair(SCTAG_NULL, 0);
  526. objs.popBack();
  527. counts.popBack();
  528. }
  529. }
  530. memory.clear();
  531. return true;
  532. }
  533. bool
  534. JSStructuredCloneReader::checkDouble(jsdouble d)
  535. {
  536. jsval_layout l;
  537. l.asDouble = d;
  538. if (!JSVAL_IS_DOUBLE_IMPL(l)) {
  539. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
  540. JSMSG_SC_BAD_SERIALIZED_DATA, "unrecognized NaN");
  541. return false;
  542. }
  543. return true;
  544. }
  545. class Chars {
  546. JSContext *cx;
  547. jschar *p;
  548. public:
  549. Chars(JSContext *cx) : cx(cx), p(NULL) {}
  550. ~Chars() { if (p) cx->free_(p); }
  551. bool allocate(size_t len) {
  552. JS_ASSERT(!p);
  553. // We're going to null-terminate!
  554. p = (jschar *) cx->malloc_((len + 1) * sizeof(jschar));
  555. if (p) {
  556. p[len] = jschar(0);
  557. return true;
  558. }
  559. return false;
  560. }
  561. jschar *get() { return p; }
  562. void forget() { p = NULL; }
  563. };
  564. JSString *
  565. JSStructuredCloneReader::readString(uint32_t nchars)
  566. {
  567. if (nchars > JSString::MAX_LENGTH) {
  568. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
  569. "string length");
  570. return NULL;
  571. }
  572. Chars chars(context());
  573. if (!chars.allocate(nchars) || !in.readChars(chars.get(), nchars))
  574. return NULL;
  575. JSString *str = js_NewString(context(), chars.get(), nchars);
  576. if (str)
  577. chars.forget();
  578. return str;
  579. }
  580. bool
  581. JSStructuredCloneReader::readTypedArray(uint32_t tag, uint32_t nelems, Value *vp)
  582. {
  583. uint32_t atype = TagToArrayType(tag);
  584. JSObject *obj = js_CreateTypedArray(context(), atype, nelems);
  585. if (!obj)
  586. return false;
  587. vp->setObject(*obj);
  588. JSObject *arr = TypedArray::getTypedArray(obj);
  589. JS_ASSERT(TypedArray::getLength(arr) == nelems);
  590. JS_ASSERT(TypedArray::getType(arr) == atype);
  591. switch (atype) {
  592. case TypedArray::TYPE_INT8:
  593. case TypedArray::TYPE_UINT8:
  594. case TypedArray::TYPE_UINT8_CLAMPED:
  595. return in.readArray((uint8_t *) TypedArray::getDataOffset(arr), nelems);
  596. case TypedArray::TYPE_INT16:
  597. case TypedArray::TYPE_UINT16:
  598. return in.readArray((uint16_t *) TypedArray::getDataOffset(arr), nelems);
  599. case TypedArray::TYPE_INT32:
  600. case TypedArray::TYPE_UINT32:
  601. case TypedArray::TYPE_FLOAT32:
  602. return in.readArray((uint32_t *) TypedArray::getDataOffset(arr), nelems);
  603. case TypedArray::TYPE_FLOAT64:
  604. return in.readArray((uint64_t *) TypedArray::getDataOffset(arr), nelems);
  605. default:
  606. JS_NOT_REACHED("unknown TypedArray type");
  607. return false;
  608. }
  609. }
  610. bool
  611. JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes, Value *vp)
  612. {
  613. JSObject *obj = js_CreateArrayBuffer(context(), nbytes);
  614. if (!obj)
  615. return false;
  616. vp->setObject(*obj);
  617. JS_ASSERT(obj->arrayBufferByteLength() == nbytes);
  618. return in.readArray(obj->arrayBufferDataOffset(), nbytes);
  619. }
  620. bool
  621. JSStructuredCloneReader::startRead(Value *vp)
  622. {
  623. uint32_t tag, data;
  624. if (!in.readPair(&tag, &data))
  625. return false;
  626. switch (tag) {
  627. case SCTAG_NULL:
  628. vp->setNull();
  629. break;
  630. case SCTAG_UNDEFINED:
  631. vp->setUndefined();
  632. break;
  633. case SCTAG_BOOLEAN:
  634. case SCTAG_BOOLEAN_OBJECT:
  635. vp->setBoolean(!!data);
  636. if (tag == SCTAG_BOOLEAN_OBJECT && !js_PrimitiveToObject(context(), vp))
  637. return false;
  638. break;
  639. case SCTAG_STRING:
  640. case SCTAG_STRING_OBJECT: {
  641. JSString *str = readString(data);
  642. if (!str)
  643. return false;
  644. vp->setString(str);
  645. if (tag == SCTAG_STRING_OBJECT && !js_PrimitiveToObject(context(), vp))
  646. return false;
  647. break;
  648. }
  649. case SCTAG_NUMBER_OBJECT: {
  650. jsdouble d;
  651. if (!in.readDouble(&d) || !checkDouble(d))
  652. return false;
  653. vp->setDouble(d);
  654. if (!js_PrimitiveToObject(context(), vp))
  655. return false;
  656. break;
  657. }
  658. case SCTAG_DATE_OBJECT: {
  659. jsdouble d;
  660. if (!in.readDouble(&d) || !checkDouble(d))
  661. return false;
  662. if (d == d && d != TIMECLIP(d)) {
  663. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
  664. "date");
  665. return false;
  666. }
  667. JSObject *obj = js_NewDateObjectMsec(context(), d);
  668. if (!obj)
  669. return false;
  670. vp->setObject(*obj);
  671. break;
  672. }
  673. case SCTAG_REGEXP_OBJECT: {
  674. RegExpFlag flags = RegExpFlag(data);
  675. uint32_t tag2, nchars;
  676. if (!in.readPair(&tag2, &nchars))
  677. return false;
  678. if (tag2 != SCTAG_STRING) {
  679. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
  680. "regexp");
  681. return false;
  682. }
  683. JSString *str = readString(nchars);
  684. if (!str)
  685. return false;
  686. size_t length = str->length();
  687. const jschar *chars = str->getChars(context());
  688. if (!chars)
  689. return false;
  690. RegExpObject *reobj = RegExpObject::createNoStatics(context(), chars, length, flags, NULL);
  691. if (!reobj)
  692. return false;
  693. vp->setObject(*reobj);
  694. break;
  695. }
  696. case SCTAG_ARRAY_OBJECT:
  697. case SCTAG_OBJECT_OBJECT: {
  698. JSObject *obj = (tag == SCTAG_ARRAY_OBJECT)
  699. ? NewDenseEmptyArray(context())
  700. : NewBuiltinClassInstance(context(), &ObjectClass);
  701. if (!obj || !objs.append(ObjectValue(*obj)) ||
  702. !allObjs.append(ObjectValue(*obj)))
  703. return false;
  704. vp->setObject(*obj);
  705. break;
  706. }
  707. case SCTAG_BACK_REFERENCE_OBJECT: {
  708. if (data >= allObjs.length()) {
  709. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL,
  710. JSMSG_SC_BAD_SERIALIZED_DATA,
  711. "invalid input");
  712. }
  713. *vp = allObjs[data];
  714. break;
  715. }
  716. case SCTAG_ARRAY_BUFFER_OBJECT:
  717. return readArrayBuffer(data, vp);
  718. default: {
  719. if (tag <= SCTAG_FLOAT_MAX) {
  720. jsdouble d = ReinterpretPairAsDouble(tag, data);
  721. if (!checkDouble(d))
  722. return false;
  723. vp->setNumber(d);
  724. break;
  725. }
  726. if (SCTAG_TYPED_ARRAY_MIN <= tag && tag <= SCTAG_TYPED_ARRAY_MAX)
  727. return readTypedArray(tag, data, vp);
  728. if (!callbacks || !callbacks->read) {
  729. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA,
  730. "unsupported type");
  731. return false;
  732. }
  733. JSObject *obj = callbacks->read(context(), this, tag, data, closure);
  734. if (!obj)
  735. return false;
  736. vp->setObject(*obj);
  737. }
  738. }
  739. return true;
  740. }
  741. bool
  742. JSStructuredCloneReader::readId(jsid *idp)
  743. {
  744. uint32_t tag, data;
  745. if (!in.readPair(&tag, &data))
  746. return false;
  747. if (tag == SCTAG_INDEX) {
  748. *idp = INT_TO_JSID(int32_t(data));
  749. return true;
  750. }
  751. if (tag == SCTAG_STRING) {
  752. JSString *str = readString(data);
  753. if (!str)
  754. return false;
  755. JSAtom *atom = js_AtomizeString(context(), str);
  756. if (!atom)
  757. return false;
  758. *idp = ATOM_TO_JSID(atom);
  759. return true;
  760. }
  761. if (tag == SCTAG_NULL) {
  762. *idp = JSID_VOID;
  763. return true;
  764. }
  765. JS_ReportErrorNumber(context(), js_GetErrorMessage, NULL, JSMSG_SC_BAD_SERIALIZED_DATA, "id");
  766. return false;
  767. }
  768. bool
  769. JSStructuredCloneReader::read(Value *vp)
  770. {
  771. if (!startRead(vp))
  772. return false;
  773. while (objs.length() != 0) {
  774. JSObject *obj = &objs.back().toObject();
  775. jsid id;
  776. if (!readId(&id))
  777. return false;
  778. if (JSID_IS_VOID(id)) {
  779. objs.popBack();
  780. } else {
  781. Value v;
  782. if (!startRead(&v) || !obj->defineGeneric(context(), id, v))
  783. return false;
  784. }
  785. }
  786. allObjs.clear();
  787. return true;
  788. }