LCOV - app.info - js/src/jsclone.cpp

LCOV - code coverage report
| Current view: | directory - js/src - jsclone.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 417 | 97 | 23.3 %
| Date: | 2012-04-07 | Functions: | 59 | 24 | 40.7 %

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

Generated by: LCOV version 1.7