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
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/
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
15 * The Original Code is JavaScript structured data serialization.
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.
23 * Jason Orendorff <jorendorff@mozilla.com>
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.
37 * ***** END LICENSE BLOCK ***** */
42 #include "jstypedarray.h"
44 #include "jsregexpinlines.h"
52 WriteStructuredClone(JSContext
*cx
, const Value
&v
, uint64
**bufp
, size_t *nbytesp
,
53 const JSStructuredCloneCallbacks
*cb
, void *cbClosure
)
56 JSStructuredCloneWriter
w(out
, cb
, cbClosure
);
57 return w
.init() && w
.write(v
) && out
.extractBuffer(bufp
, nbytesp
);
61 ReadStructuredClone(JSContext
*cx
, const uint64_t *data
, size_t nbytes
, Value
*vp
,
62 const JSStructuredCloneCallbacks
*cb
, void *cbClosure
)
64 SCInput
in(cx
, data
, nbytes
);
65 JSStructuredCloneReader
r(in
, cb
, cbClosure
);
71 enum StructuredDataType
{
72 /* Structured data types provided by the engine */
73 SCTAG_FLOAT_MAX
= 0xFFF00000,
74 SCTAG_NULL
= 0xFFFF0000,
83 SCTAG_ARRAY_BUFFER_OBJECT
,
87 SCTAG_TYPED_ARRAY_MIN
= 0xFFFF0100,
88 SCTAG_TYPED_ARRAY_MAX
= SCTAG_TYPED_ARRAY_MIN
+ TypedArray::TYPE_MAX
- 1,
89 SCTAG_END_OF_BUILTIN_TYPES
92 JS_STATIC_ASSERT(SCTAG_END_OF_BUILTIN_TYPES
<= JS_SCTAG_USER_MIN
);
93 JS_STATIC_ASSERT(JS_SCTAG_USER_MIN
<= JS_SCTAG_USER_MAX
);
102 SwapBytes(uint16_t u
)
105 return ((u
& 0x00ff) << 8) | ((u
& 0xff00) >> 8);
112 SwapBytes(uint32_t u
)
115 return ((u
& 0x000000ffU
) << 24) |
116 ((u
& 0x0000ff00U
) << 8) |
117 ((u
& 0x00ff0000U
) >> 8) |
118 ((u
& 0xff000000U
) >> 24);
125 SwapBytes(uint64_t u
)
128 return ((u
& 0x00000000000000ffLLU
) << 56) |
129 ((u
& 0x000000000000ff00LLU
) << 40) |
130 ((u
& 0x0000000000ff0000LLU
) << 24) |
131 ((u
& 0x00000000ff000000LLU
) << 8) |
132 ((u
& 0x000000ff00000000LLU
) >> 8) |
133 ((u
& 0x0000ff0000000000LLU
) >> 24) |
134 ((u
& 0x00ff000000000000LLU
) >> 40) |
135 ((u
& 0xff00000000000000LLU
) >> 56);
144 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_SC_BAD_SERIALIZED_DATA
, "truncated");
148 SCInput::SCInput(JSContext
*cx
, const uint64_t *data
, size_t nbytes
)
149 : cx(cx
), point(data
), end(data
+ nbytes
/ 8)
151 JS_ASSERT((uintptr_t(data
) & 7) == 0);
152 JS_ASSERT((nbytes
& 7) == 0);
156 SCInput::read(uint64_t *p
)
160 *p
= SwapBytes(*point
++);
165 SCInput::readPair(uint32_t *tagp
, uint32_t *datap
)
167 uint64_t u
= 0; /* initialize to shut GCC up */
170 *tagp
= uint32_t(u
>> 32);
171 *datap
= uint32_t(u
);
177 * The purpose of this never-inlined function is to avoid a strange g++ build
178 * error on OS X 10.5 (see bug 624080). :-(
180 static JS_NEVER_INLINE
double
181 CanonicalizeNan(double d
)
183 return JS_CANONICALIZE_NAN(d
);
187 SCInput::readDouble(jsdouble
*p
)
195 *p
= CanonicalizeNan(pun
.d
);
201 SCInput::readArray(T
*p
, size_t nelems
)
203 JS_STATIC_ASSERT(sizeof(uint64_t) % sizeof(T
) == 0);
206 * Fail if nelems is so huge as to make JS_HOWMANY overflow or if nwords is
207 * larger than the remaining data.
209 size_t nwords
= JS_HOWMANY(nelems
, sizeof(uint64_t) / sizeof(T
));
210 if (nelems
+ sizeof(uint64_t) / sizeof(T
) - 1 < nelems
|| nwords
> size_t(end
- point
))
213 if (sizeof(T
) == 1) {
214 memcpy(p
, point
, nelems
);
216 const T
*q
= (const T
*) point
;
217 const T
*qend
= q
+ nelems
;
219 *p
++ = SwapBytes(*q
++);
226 SCInput::readBytes(void *p
, size_t nbytes
)
228 return readArray((uint8_t *) p
, nbytes
);
232 SCInput::readChars(jschar
*p
, size_t nchars
)
234 JS_ASSERT(sizeof(jschar
) == sizeof(uint16_t));
235 return readArray((uint16_t *) p
, nchars
);
238 SCOutput::SCOutput(JSContext
*cx
) : cx(cx
), buf(cx
) {}
241 SCOutput::write(uint64_t u
)
243 return buf
.append(SwapBytes(u
));
246 static inline uint64_t
247 PairToUInt64(uint32_t tag
, uint32_t data
)
249 return uint64_t(data
) | (uint64_t(tag
) << 32);
253 SCOutput::writePair(uint32_t tag
, uint32_t data
)
256 * As it happens, the tag word appears after the data word in the output.
257 * This is because exponents occupy the last 2 bytes of jsdoubles on the
258 * little-endian platforms we care most about.
260 * For example, JSVAL_TRUE is written using writePair(SCTAG_BOOLEAN, 1).
261 * PairToUInt64 produces the number 0xFFFF000200000001.
262 * That is written out as the bytes 01 00 00 00 02 00 FF FF.
264 return write(PairToUInt64(tag
, data
));
267 static inline uint64_t
268 ReinterpretDoubleAsUInt64(jsdouble d
)
278 static inline jsdouble
279 ReinterpretUInt64AsDouble(uint64_t u
)
289 static inline jsdouble
290 ReinterpretPairAsDouble(uint32_t tag
, uint32_t data
)
292 return ReinterpretUInt64AsDouble(PairToUInt64(tag
, data
));
296 SCOutput::writeDouble(jsdouble d
)
298 return write(ReinterpretDoubleAsUInt64(CanonicalizeNan(d
)));
303 SCOutput::writeArray(const T
*p
, size_t nelems
)
305 JS_ASSERT(8 % sizeof(T
) == 0);
306 JS_ASSERT(sizeof(uint64_t) % sizeof(T
) == 0);
311 if (nelems
+ sizeof(uint64_t) / sizeof(T
) - 1 < nelems
) {
312 js_ReportAllocationOverflow(context());
315 size_t nwords
= JS_HOWMANY(nelems
, sizeof(uint64_t) / sizeof(T
));
316 size_t start
= buf
.length();
317 if (!buf
.growByUninitialized(nwords
))
320 buf
.back() = 0; /* zero-pad to an 8-byte boundary */
322 T
*q
= (T
*) &buf
[start
];
323 if (sizeof(T
) == 1) {
324 memcpy(q
, p
, nelems
);
326 const T
*pend
= p
+ nelems
;
328 *q
++ = SwapBytes(*p
++);
334 SCOutput::writeBytes(const void *p
, size_t nbytes
)
336 return writeArray((const uint8_t *) p
, nbytes
);
340 SCOutput::writeChars(const jschar
*p
, size_t nchars
)
342 JS_ASSERT(sizeof(jschar
) == sizeof(uint16_t));
343 return writeArray((const uint16_t *) p
, nchars
);
347 SCOutput::extractBuffer(uint64_t **datap
, size_t *sizep
)
349 *sizep
= buf
.length() * sizeof(uint64_t);
350 return (*datap
= buf
.extractRawBuffer()) != NULL
;
353 JS_STATIC_ASSERT(JSString::MAX_LENGTH
< UINT32_MAX
);
356 JSStructuredCloneWriter::writeString(uint32_t tag
, JSString
*str
)
358 size_t length
= str
->length();
359 const jschar
*chars
= str
->getChars(context());
362 return out
.writePair(tag
, uint32_t(length
)) && out
.writeChars(chars
, length
);
366 JSStructuredCloneWriter::writeId(jsid id
)
369 return out
.writePair(SCTAG_INDEX
, uint32_t(JSID_TO_INT(id
)));
370 JS_ASSERT(JSID_IS_STRING(id
));
371 return writeString(SCTAG_STRING
, JSID_TO_STRING(id
));
375 JSStructuredCloneWriter::checkStack()
378 /* To avoid making serialization O(n^2), limit stack-checking at 10. */
379 const size_t MAX
= 10;
381 size_t limit
= JS_MIN(counts
.length(), MAX
);
382 JS_ASSERT(objs
.length() == counts
.length());
384 for (size_t i
= 0; i
< limit
; i
++) {
385 JS_ASSERT(total
+ counts
[i
] >= total
);
388 if (counts
.length() <= MAX
)
389 JS_ASSERT(total
== ids
.length());
391 JS_ASSERT(total
<= ids
.length());
393 JS_ASSERT(memory
.count() == objs
.length());
394 size_t j
= objs
.length();
395 for (size_t i
= 0; i
< limit
; i
++)
396 JS_ASSERT(memory
.has(&objs
[--j
].toObject()));
400 static inline uint32_t
401 ArrayTypeToTag(uint32_t type
)
404 * As long as these are all true, we can just add. Note that for backward
405 * compatibility, the tags cannot change. So if the ArrayType type codes
406 * change, this function and TagToArrayType will have to do more work.
408 JS_STATIC_ASSERT(TypedArray::TYPE_INT8
== 0);
409 JS_STATIC_ASSERT(TypedArray::TYPE_UINT8
== 1);
410 JS_STATIC_ASSERT(TypedArray::TYPE_INT16
== 2);
411 JS_STATIC_ASSERT(TypedArray::TYPE_UINT16
== 3);
412 JS_STATIC_ASSERT(TypedArray::TYPE_INT32
== 4);
413 JS_STATIC_ASSERT(TypedArray::TYPE_UINT32
== 5);
414 JS_STATIC_ASSERT(TypedArray::TYPE_FLOAT32
== 6);
415 JS_STATIC_ASSERT(TypedArray::TYPE_FLOAT64
== 7);
416 JS_STATIC_ASSERT(TypedArray::TYPE_UINT8_CLAMPED
== 8);
417 JS_STATIC_ASSERT(TypedArray::TYPE_MAX
== TypedArray::TYPE_UINT8_CLAMPED
+ 1);
419 JS_ASSERT(type
< TypedArray::TYPE_MAX
);
420 return SCTAG_TYPED_ARRAY_MIN
+ type
;
423 static inline uint32_t
424 TagToArrayType(uint32_t tag
)
426 JS_ASSERT(SCTAG_TYPED_ARRAY_MIN
<= tag
&& tag
<= SCTAG_TYPED_ARRAY_MAX
);
427 return tag
- SCTAG_TYPED_ARRAY_MIN
;
431 JSStructuredCloneWriter::writeTypedArray(JSObject
*obj
)
433 TypedArray
*arr
= TypedArray::fromJSObject(obj
);
434 if (!out
.writePair(ArrayTypeToTag(arr
->type
), arr
->length
))
438 case TypedArray::TYPE_INT8
:
439 case TypedArray::TYPE_UINT8
:
440 case TypedArray::TYPE_UINT8_CLAMPED
:
441 return out
.writeArray((const uint8_t *) arr
->data
, arr
->length
);
442 case TypedArray::TYPE_INT16
:
443 case TypedArray::TYPE_UINT16
:
444 return out
.writeArray((const uint16_t *) arr
->data
, arr
->length
);
445 case TypedArray::TYPE_INT32
:
446 case TypedArray::TYPE_UINT32
:
447 case TypedArray::TYPE_FLOAT32
:
448 return out
.writeArray((const uint32_t *) arr
->data
, arr
->length
);
449 case TypedArray::TYPE_FLOAT64
:
450 return out
.writeArray((const uint64_t *) arr
->data
, arr
->length
);
452 JS_NOT_REACHED("unknown TypedArray type");
458 JSStructuredCloneWriter::writeArrayBuffer(JSObject
*obj
)
460 ArrayBuffer
*abuf
= ArrayBuffer::fromJSObject(obj
);
461 return out
.writePair(SCTAG_ARRAY_BUFFER_OBJECT
, abuf
->byteLength
) &&
462 out
.writeBytes(abuf
->data
, abuf
->byteLength
);
466 JSStructuredCloneWriter::startObject(JSObject
*obj
)
468 JS_ASSERT(obj
->isArray() || obj
->isObject());
470 /* Fail if obj is already on the stack. */
471 HashSet
<JSObject
*>::AddPtr p
= memory
.lookupForAdd(obj
);
473 JSContext
*cx
= context();
474 if (callbacks
&& callbacks
->reportError
)
475 callbacks
->reportError(cx
, JS_SCERR_RECURSION
);
477 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_SC_RECURSION
);
480 if (!memory
.add(p
, obj
))
484 * Get enumerable property ids and put them in reverse order so that they
485 * will come off the stack in forward order.
487 size_t initialLength
= ids
.length();
488 if (!GetPropertyNames(context(), obj
, JSITER_OWNONLY
, &ids
))
490 jsid
*begin
= ids
.begin() + initialLength
, *end
= ids
.end();
491 size_t count
= size_t(end
- begin
);
494 /* Push obj and count to the stack. */
495 if (!objs
.append(ObjectValue(*obj
)) || !counts
.append(count
))
499 /* Write the header for obj. */
500 return out
.writePair(obj
->isArray() ? SCTAG_ARRAY_OBJECT
: SCTAG_OBJECT_OBJECT
, 0);
504 JSStructuredCloneWriter::startWrite(const js::Value
&v
)
507 return writeString(SCTAG_STRING
, v
.toString());
508 } else if (v
.isNumber()) {
509 return out
.writeDouble(v
.toNumber());
510 } else if (v
.isBoolean()) {
511 return out
.writePair(SCTAG_BOOLEAN
, v
.toBoolean());
512 } else if (v
.isNull()) {
513 return out
.writePair(SCTAG_NULL
, 0);
514 } else if (v
.isUndefined()) {
515 return out
.writePair(SCTAG_UNDEFINED
, 0);
516 } else if (v
.isObject()) {
517 JSObject
*obj
= &v
.toObject();
518 if (obj
->isRegExp()) {
519 RegExp
*re
= RegExp::extractFrom(obj
);
520 return out
.writePair(SCTAG_REGEXP_OBJECT
, re
->getFlags()) &&
521 writeString(SCTAG_STRING
, re
->getSource());
522 } else if (obj
->isDate()) {
523 jsdouble d
= js_DateGetMsecSinceEpoch(context(), obj
);
524 return out
.writePair(SCTAG_DATE_OBJECT
, 0) && out
.writeDouble(d
);
525 } else if (obj
->isObject() || obj
->isArray()) {
526 return startObject(obj
);
527 } else if (js_IsTypedArray(obj
)) {
528 return writeTypedArray(obj
);
529 } else if (js_IsArrayBuffer(obj
) && ArrayBuffer::fromJSObject(obj
)) {
530 return writeArrayBuffer(obj
);
531 } else if (obj
->isBoolean()) {
532 return out
.writePair(SCTAG_BOOLEAN_OBJECT
, obj
->getPrimitiveThis().toBoolean());
533 } else if (obj
->isNumber()) {
534 return out
.writePair(SCTAG_NUMBER_OBJECT
, 0) &&
535 out
.writeDouble(obj
->getPrimitiveThis().toNumber());
536 } else if (obj
->isString()) {
537 return writeString(SCTAG_STRING_OBJECT
, obj
->getPrimitiveThis().toString());
540 if (callbacks
&& callbacks
->write
)
541 return callbacks
->write(context(), this, obj
, closure
);
542 /* else fall through */
545 JS_ReportErrorNumber(context(), js_GetErrorMessage
, NULL
, JSMSG_SC_UNSUPPORTED_TYPE
);
550 JSStructuredCloneWriter::write(const Value
&v
)
555 while (!counts
.empty()) {
556 JSObject
*obj
= &objs
.back().toObject();
559 jsid id
= ids
.back();
562 if (JSID_IS_STRING(id
) || JSID_IS_INT(id
)) {
564 * If obj still has an own property named id, write it out.
565 * The cost of re-checking could be avoided by using
570 if (!js_HasOwnProperty(context(), obj
->getOps()->lookupProperty
, obj
, id
,
577 if (!writeId(id
) || !obj
->getProperty(context(), id
, &val
) || !startWrite(val
))
582 out
.writePair(SCTAG_NULL
, 0);
592 JSStructuredCloneReader::checkDouble(jsdouble d
)
596 if (!JSVAL_IS_DOUBLE(JSVAL_FROM_LAYOUT(l
))) {
597 JS_ReportErrorNumber(context(), js_GetErrorMessage
, NULL
,
598 JSMSG_SC_BAD_SERIALIZED_DATA
, "unrecognized NaN");
609 ~Chars() { if (p
) cx
->free(p
); }
611 bool allocate(JSContext
*cx
, size_t len
) {
613 // We're going to null-terminate!
614 p
= (jschar
*) cx
->malloc((len
+ 1) * sizeof(jschar
));
622 jschar
*get() { return p
; }
623 void forget() { p
= NULL
; }
627 JSStructuredCloneReader::readString(uint32_t nchars
)
629 if (nchars
> JSString::MAX_LENGTH
) {
630 JS_ReportErrorNumber(context(), js_GetErrorMessage
, NULL
, JSMSG_SC_BAD_SERIALIZED_DATA
,
635 if (!chars
.allocate(context(), nchars
) || !in
.readChars(chars
.get(), nchars
))
637 JSString
*str
= js_NewString(context(), chars
.get(), nchars
);
644 JSStructuredCloneReader::readTypedArray(uint32_t tag
, uint32_t nelems
, Value
*vp
)
646 uint32_t atype
= TagToArrayType(tag
);
647 JSObject
*obj
= js_CreateTypedArray(context(), atype
, nelems
);
652 TypedArray
*arr
= TypedArray::fromJSObject(obj
);
653 JS_ASSERT(arr
->length
== nelems
);
654 JS_ASSERT(arr
->type
== atype
);
656 case TypedArray::TYPE_INT8
:
657 case TypedArray::TYPE_UINT8
:
658 case TypedArray::TYPE_UINT8_CLAMPED
:
659 return in
.readArray((uint8_t *) arr
->data
, nelems
);
660 case TypedArray::TYPE_INT16
:
661 case TypedArray::TYPE_UINT16
:
662 return in
.readArray((uint16_t *) arr
->data
, nelems
);
663 case TypedArray::TYPE_INT32
:
664 case TypedArray::TYPE_UINT32
:
665 case TypedArray::TYPE_FLOAT32
:
666 return in
.readArray((uint32_t *) arr
->data
, nelems
);
667 case TypedArray::TYPE_FLOAT64
:
668 return in
.readArray((uint64_t *) arr
->data
, nelems
);
670 JS_NOT_REACHED("unknown TypedArray type");
676 JSStructuredCloneReader::readArrayBuffer(uint32_t nbytes
, Value
*vp
)
678 JSObject
*obj
= js_CreateArrayBuffer(context(), nbytes
);
682 ArrayBuffer
*abuf
= ArrayBuffer::fromJSObject(obj
);
683 JS_ASSERT(abuf
->byteLength
== nbytes
);
684 return in
.readArray((uint8_t *) abuf
->data
, nbytes
);
688 JSStructuredCloneReader::startRead(Value
*vp
)
692 if (!in
.readPair(&tag
, &data
))
699 case SCTAG_UNDEFINED
:
704 case SCTAG_BOOLEAN_OBJECT
:
705 vp
->setBoolean(!!data
);
706 if (tag
== SCTAG_BOOLEAN_OBJECT
&& !js_PrimitiveToObject(context(), vp
))
711 case SCTAG_STRING_OBJECT
: {
712 JSString
*str
= readString(data
);
716 if (tag
== SCTAG_STRING_OBJECT
&& !js_PrimitiveToObject(context(), vp
))
721 case SCTAG_NUMBER_OBJECT
: {
723 if (!in
.readDouble(&d
) || !checkDouble(d
))
726 if (!js_PrimitiveToObject(context(), vp
))
731 case SCTAG_DATE_OBJECT
: {
733 if (!in
.readDouble(&d
) || !checkDouble(d
))
735 if (d
== d
&& d
!= TIMECLIP(d
)) {
736 JS_ReportErrorNumber(context(), js_GetErrorMessage
, NULL
, JSMSG_SC_BAD_SERIALIZED_DATA
,
740 JSObject
*obj
= js_NewDateObjectMsec(context(), d
);
747 case SCTAG_REGEXP_OBJECT
: {
748 uint32_t tag2
, nchars
;
749 if (!in
.readPair(&tag2
, &nchars
))
751 if (tag2
!= SCTAG_STRING
) {
752 JS_ReportErrorNumber(context(), js_GetErrorMessage
, NULL
, JSMSG_SC_BAD_SERIALIZED_DATA
,
756 JSString
*str
= readString(nchars
);
759 size_t length
= str
->length();
760 const jschar
*chars
= str
->getChars(context());
763 JSObject
*obj
= RegExp::createObjectNoStatics(context(), chars
, length
, data
);
770 case SCTAG_ARRAY_OBJECT
:
771 case SCTAG_OBJECT_OBJECT
: {
772 JSObject
*obj
= (tag
== SCTAG_ARRAY_OBJECT
)
773 ? NewDenseEmptyArray(context())
774 : NewBuiltinClassInstance(context(), &js_ObjectClass
);
775 if (!obj
|| !objs
.append(ObjectValue(*obj
)))
781 case SCTAG_ARRAY_BUFFER_OBJECT
:
782 return readArrayBuffer(data
, vp
);
785 if (tag
<= SCTAG_FLOAT_MAX
) {
786 jsdouble d
= ReinterpretPairAsDouble(tag
, data
);
793 if (SCTAG_TYPED_ARRAY_MIN
<= tag
&& tag
<= SCTAG_TYPED_ARRAY_MAX
)
794 return readTypedArray(tag
, data
, vp
);
796 if (!callbacks
|| !callbacks
->read
) {
797 JS_ReportErrorNumber(context(), js_GetErrorMessage
, NULL
, JSMSG_SC_BAD_SERIALIZED_DATA
,
801 JSObject
*obj
= callbacks
->read(context(), this, tag
, data
, closure
);
811 JSStructuredCloneReader::readId(jsid
*idp
)
814 if (!in
.readPair(&tag
, &data
))
817 if (tag
== SCTAG_INDEX
) {
818 *idp
= INT_TO_JSID(int32_t(data
));
821 if (tag
== SCTAG_STRING
) {
822 JSString
*str
= readString(data
);
825 JSAtom
*atom
= js_AtomizeString(context(), str
, 0);
828 *idp
= ATOM_TO_JSID(atom
);
831 if (tag
== SCTAG_NULL
) {
835 JS_ReportErrorNumber(context(), js_GetErrorMessage
, NULL
, JSMSG_SC_BAD_SERIALIZED_DATA
, "id");
840 JSStructuredCloneReader::read(Value
*vp
)
845 while (objs
.length() != 0) {
846 JSObject
*obj
= &objs
.back().toObject();
852 if (JSID_IS_VOID(id
)) {
856 if (!startRead(&v
) || !obj
->defineProperty(context(), id
, v
))