PageRenderTime 79ms CodeModel.GetById 13ms app.highlight 59ms RepoModel.GetById 2ms app.codeStats 0ms

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