js/src/vm/ArrayBufferObject.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "vm/ArrayBufferObject.h"
     9 #include "mozilla/Alignment.h"
    10 #include "mozilla/FloatingPoint.h"
    11 #include "mozilla/PodOperations.h"
    13 #include <string.h>
    14 #ifndef XP_WIN
    15 # include <sys/mman.h>
    16 #endif
    18 #ifdef MOZ_VALGRIND
    19 # include <valgrind/memcheck.h>
    20 #endif
    22 #include "jsapi.h"
    23 #include "jsarray.h"
    24 #include "jscntxt.h"
    25 #include "jscpucfg.h"
    26 #include "jsnum.h"
    27 #include "jsobj.h"
    28 #include "jstypes.h"
    29 #include "jsutil.h"
    30 #ifdef XP_WIN
    31 # include "jswin.h"
    32 #endif
    33 #include "jswrapper.h"
    35 #include "gc/Barrier.h"
    36 #include "gc/Marking.h"
    37 #include "gc/Memory.h"
    38 #include "jit/AsmJS.h"
    39 #include "jit/AsmJSModule.h"
    40 #include "js/MemoryMetrics.h"
    41 #include "vm/GlobalObject.h"
    42 #include "vm/Interpreter.h"
    43 #include "vm/NumericConversions.h"
    44 #include "vm/SharedArrayObject.h"
    45 #include "vm/WrapperObject.h"
    47 #include "jsatominlines.h"
    48 #include "jsinferinlines.h"
    49 #include "jsobjinlines.h"
    51 #include "vm/Shape-inl.h"
    53 using mozilla::DebugOnly;
    55 using namespace js;
    56 using namespace js::gc;
    57 using namespace js::types;
    59 /*
    60  * Convert |v| to an array index for an array of length |length| per
    61  * the Typed Array Specification section 7.0, |subarray|. If successful,
    62  * the output value is in the range [0, length].
    63  */
    64 bool
    65 js::ToClampedIndex(JSContext *cx, HandleValue v, uint32_t length, uint32_t *out)
    66 {
    67     int32_t result;
    68     if (!ToInt32(cx, v, &result))
    69         return false;
    70     if (result < 0) {
    71         result += length;
    72         if (result < 0)
    73             result = 0;
    74     } else if (uint32_t(result) > length) {
    75         result = length;
    76     }
    77     *out = uint32_t(result);
    78     return true;
    79 }
    81 /*
    82  * ArrayBufferObject
    83  *
    84  * This class holds the underlying raw buffer that the TypedArrayObject classes
    85  * access.  It can be created explicitly and passed to a TypedArrayObject, or
    86  * can be created implicitly by constructing a TypedArrayObject with a size.
    87  */
    89 /*
    90  * ArrayBufferObject (base)
    91  */
    93 const Class ArrayBufferObject::protoClass = {
    94     "ArrayBufferPrototype",
    95     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
    96     JS_PropertyStub,         /* addProperty */
    97     JS_DeletePropertyStub,   /* delProperty */
    98     JS_PropertyStub,         /* getProperty */
    99     JS_StrictPropertyStub,   /* setProperty */
   100     JS_EnumerateStub,
   101     JS_ResolveStub,
   102     JS_ConvertStub
   103 };
   105 const Class ArrayBufferObject::class_ = {
   106     "ArrayBuffer",
   107     JSCLASS_IMPLEMENTS_BARRIERS |
   108     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
   109     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
   110     JSCLASS_BACKGROUND_FINALIZE,
   111     JS_PropertyStub,         /* addProperty */
   112     JS_DeletePropertyStub,   /* delProperty */
   113     JS_PropertyStub,         /* getProperty */
   114     JS_StrictPropertyStub,   /* setProperty */
   115     JS_EnumerateStub,
   116     JS_ResolveStub,
   117     JS_ConvertStub,
   118     ArrayBufferObject::finalize,
   119     nullptr,        /* call        */
   120     nullptr,        /* hasInstance */
   121     nullptr,        /* construct   */
   122     ArrayBufferObject::obj_trace,
   123     JS_NULL_CLASS_SPEC,
   124     JS_NULL_CLASS_EXT
   125 };
   127 const JSFunctionSpec ArrayBufferObject::jsfuncs[] = {
   128     JS_FN("slice", ArrayBufferObject::fun_slice, 2, JSFUN_GENERIC_NATIVE),
   129     JS_FS_END
   130 };
   132 const JSFunctionSpec ArrayBufferObject::jsstaticfuncs[] = {
   133     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
   134     JS_FS_END
   135 };
   137 bool
   138 js::IsArrayBuffer(HandleValue v)
   139 {
   140     return v.isObject() &&
   141            (v.toObject().is<ArrayBufferObject>() ||
   142             v.toObject().is<SharedArrayBufferObject>());
   143 }
   145 bool
   146 js::IsArrayBuffer(HandleObject obj)
   147 {
   148     return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
   149 }
   151 bool
   152 js::IsArrayBuffer(JSObject *obj)
   153 {
   154     return obj->is<ArrayBufferObject>() || obj->is<SharedArrayBufferObject>();
   155 }
   157 ArrayBufferObject &
   158 js::AsArrayBuffer(HandleObject obj)
   159 {
   160     JS_ASSERT(IsArrayBuffer(obj));
   161     if (obj->is<SharedArrayBufferObject>())
   162         return obj->as<SharedArrayBufferObject>();
   163     return obj->as<ArrayBufferObject>();
   164 }
   166 ArrayBufferObject &
   167 js::AsArrayBuffer(JSObject *obj)
   168 {
   169     JS_ASSERT(IsArrayBuffer(obj));
   170     if (obj->is<SharedArrayBufferObject>())
   171         return obj->as<SharedArrayBufferObject>();
   172     return obj->as<ArrayBufferObject>();
   173 }
   175 MOZ_ALWAYS_INLINE bool
   176 ArrayBufferObject::byteLengthGetterImpl(JSContext *cx, CallArgs args)
   177 {
   178     JS_ASSERT(IsArrayBuffer(args.thisv()));
   179     args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
   180     return true;
   181 }
   183 bool
   184 ArrayBufferObject::byteLengthGetter(JSContext *cx, unsigned argc, Value *vp)
   185 {
   186     CallArgs args = CallArgsFromVp(argc, vp);
   187     return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
   188 }
   190 bool
   191 ArrayBufferObject::fun_slice_impl(JSContext *cx, CallArgs args)
   192 {
   193     JS_ASSERT(IsArrayBuffer(args.thisv()));
   195     Rooted<ArrayBufferObject*> thisObj(cx, &args.thisv().toObject().as<ArrayBufferObject>());
   197     // these are the default values
   198     uint32_t length = thisObj->byteLength();
   199     uint32_t begin = 0, end = length;
   201     if (args.length() > 0) {
   202         if (!ToClampedIndex(cx, args[0], length, &begin))
   203             return false;
   205         if (args.length() > 1) {
   206             if (!ToClampedIndex(cx, args[1], length, &end))
   207                 return false;
   208         }
   209     }
   211     if (begin > end)
   212         begin = end;
   214     JSObject *nobj = createSlice(cx, thisObj, begin, end);
   215     if (!nobj)
   216         return false;
   217     args.rval().setObject(*nobj);
   218     return true;
   219 }
   221 bool
   222 ArrayBufferObject::fun_slice(JSContext *cx, unsigned argc, Value *vp)
   223 {
   224     CallArgs args = CallArgsFromVp(argc, vp);
   225     return CallNonGenericMethod<IsArrayBuffer, fun_slice_impl>(cx, args);
   226 }
   228 /*
   229  * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
   230  */
   231 bool
   232 ArrayBufferObject::fun_isView(JSContext *cx, unsigned argc, Value *vp)
   233 {
   234     CallArgs args = CallArgsFromVp(argc, vp);
   235     args.rval().setBoolean(args.get(0).isObject() &&
   236                            JS_IsArrayBufferViewObject(&args.get(0).toObject()));
   237     return true;
   238 }
   240 /*
   241  * new ArrayBuffer(byteLength)
   242  */
   243 bool
   244 ArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp)
   245 {
   246     int32_t nbytes = 0;
   247     CallArgs args = CallArgsFromVp(argc, vp);
   248     if (argc > 0 && !ToInt32(cx, args[0], &nbytes))
   249         return false;
   251     if (nbytes < 0) {
   252         /*
   253          * We're just not going to support arrays that are bigger than what will fit
   254          * as an integer value; if someone actually ever complains (validly), then we
   255          * can fix.
   256          */
   257         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
   258         return false;
   259     }
   261     JSObject *bufobj = create(cx, uint32_t(nbytes));
   262     if (!bufobj)
   263         return false;
   264     args.rval().setObject(*bufobj);
   265     return true;
   266 }
   268 /*
   269  * Note that some callers are allowed to pass in a nullptr cx, so we allocate
   270  * with the cx if available and fall back to the runtime.  If oldptr is given,
   271  * it's expected to be a previously-allocated contents pointer that we then
   272  * realloc.
   273  */
   274 static void *
   275 AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldptr = nullptr, size_t oldnbytes = 0)
   276 {
   277     void *p;
   279     // if oldptr is given, then we need to do a realloc
   280     if (oldptr) {
   281         p = maybecx ? maybecx->runtime()->reallocCanGC(oldptr, nbytes) : js_realloc(oldptr, nbytes);
   283         // if we grew the array, we need to set the new bytes to 0
   284         if (p && nbytes > oldnbytes)
   285             memset(reinterpret_cast<uint8_t *>(p) + oldnbytes, 0, nbytes - oldnbytes);
   286     } else {
   287         p = maybecx ? maybecx->runtime()->callocCanGC(nbytes) : js_calloc(nbytes);
   288     }
   290     if (!p && maybecx)
   291         js_ReportOutOfMemory(maybecx);
   293     return p;
   294 }
   296 ArrayBufferViewObject *
   297 ArrayBufferObject::viewList() const
   298 {
   299     return reinterpret_cast<ArrayBufferViewObject *>(getSlot(VIEW_LIST_SLOT).toPrivate());
   300 }
   302 void
   303 ArrayBufferObject::setViewListNoBarrier(ArrayBufferViewObject *viewsHead)
   304 {
   305     setSlot(VIEW_LIST_SLOT, PrivateValue(viewsHead));
   306 }
   308 void
   309 ArrayBufferObject::setViewList(ArrayBufferViewObject *viewsHead)
   310 {
   311     if (ArrayBufferViewObject *oldHead = viewList())
   312         ArrayBufferViewObject::writeBarrierPre(oldHead);
   313     setViewListNoBarrier(viewsHead);
   314     PostBarrierTypedArrayObject(this);
   315 }
   317 bool
   318 ArrayBufferObject::canNeuter(JSContext *cx)
   319 {
   320     if (isSharedArrayBuffer())
   321         return false;
   323     if (isAsmJSArrayBuffer()) {
   324         if (!ArrayBufferObject::canNeuterAsmJSArrayBuffer(cx, *this))
   325             return false;
   326     }
   328     return true;
   329 }
   331 /* static */ void
   332 ArrayBufferObject::neuter(JSContext *cx, Handle<ArrayBufferObject*> buffer, void *newData)
   333 {
   334     JS_ASSERT(buffer->canNeuter(cx));
   336     // Neuter all views on the buffer, clear out the list of views and the
   337     // buffer's data.
   339     for (ArrayBufferViewObject *view = buffer->viewList(); view; view = view->nextView()) {
   340         view->neuter(newData);
   342         // Notify compiled jit code that the base pointer has moved.
   343         MarkObjectStateChange(cx, view);
   344     }
   346     if (newData != buffer->dataPointer())
   347         buffer->setNewOwnedData(cx->runtime()->defaultFreeOp(), newData);
   349     buffer->setByteLength(0);
   350     buffer->setViewList(nullptr);
   351     buffer->setIsNeutered();
   353     // If this is happening during an incremental GC, remove the buffer from
   354     // the list of live buffers with multiple views if necessary.
   355     if (buffer->inLiveList()) {
   356         ArrayBufferVector &gcLiveArrayBuffers = cx->compartment()->gcLiveArrayBuffers;
   357         DebugOnly<bool> found = false;
   358         for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
   359             if (buffer == gcLiveArrayBuffers[i]) {
   360                 found = true;
   361                 gcLiveArrayBuffers[i] = gcLiveArrayBuffers.back();
   362                 gcLiveArrayBuffers.popBack();
   363                 break;
   364             }
   365         }
   366         JS_ASSERT(found);
   367         buffer->setInLiveList(false);
   368     }
   369 }
   371 void
   372 ArrayBufferObject::setNewOwnedData(FreeOp* fop, void *newData)
   373 {
   374     JS_ASSERT(!isAsmJSArrayBuffer());
   375     JS_ASSERT(!isSharedArrayBuffer());
   377     if (ownsData()) {
   378         JS_ASSERT(newData != dataPointer());
   379         releaseData(fop);
   380     }
   382     setDataPointer(static_cast<uint8_t *>(newData), OwnsData);
   383 }
   385 void
   386 ArrayBufferObject::changeContents(JSContext *cx, void *newData)
   387 {
   388     // Change buffer contents.
   389     uint8_t* oldDataPointer = dataPointer();
   390     setNewOwnedData(cx->runtime()->defaultFreeOp(), newData);
   392     // Update all views.
   393     ArrayBufferViewObject *viewListHead = viewList();
   394     for (ArrayBufferViewObject *view = viewListHead; view; view = view->nextView()) {
   395         // Watch out for NULL data pointers in views. This means that the view
   396         // is not fully initialized (in which case it'll be initialized later
   397         // with the correct pointer).
   398         uint8_t *viewDataPointer = view->dataPointer();
   399         if (viewDataPointer) {
   400             JS_ASSERT(newData);
   401             ptrdiff_t offset = viewDataPointer - oldDataPointer;
   402             viewDataPointer = static_cast<uint8_t *>(newData) + offset;
   403             view->setPrivate(viewDataPointer);
   404         }
   406         // Notify compiled jit code that the base pointer has moved.
   407         MarkObjectStateChange(cx, view);
   408     }
   409 }
   411 #if defined(JS_CPU_X64)
   412 // Refer to comment above AsmJSMappedSize in AsmJS.h.
   413 JS_STATIC_ASSERT(AsmJSAllocationGranularity == AsmJSPageSize);
   414 #endif
   416 #if defined(JS_ION) && defined(JS_CPU_X64)
   417 /* static */ bool
   418 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
   419 {
   420     if (buffer->isAsmJSArrayBuffer())
   421         return true;
   423     // SharedArrayBuffers are already created with AsmJS support in mind.
   424     if (buffer->isSharedArrayBuffer())
   425         return true;
   427     // Get the entire reserved region (with all pages inaccessible).
   428     void *data;
   429 # ifdef XP_WIN
   430     data = VirtualAlloc(nullptr, AsmJSMappedSize, MEM_RESERVE, PAGE_NOACCESS);
   431     if (!data)
   432         return false;
   433 # else
   434     data = mmap(nullptr, AsmJSMappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
   435     if (data == MAP_FAILED)
   436         return false;
   437 # endif
   439     // Enable access to the valid region.
   440     JS_ASSERT(buffer->byteLength() % AsmJSAllocationGranularity == 0);
   441 # ifdef XP_WIN
   442     if (!VirtualAlloc(data, buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) {
   443         VirtualFree(data, 0, MEM_RELEASE);
   444         return false;
   445     }
   446 # else
   447     size_t validLength = buffer->byteLength();
   448     if (mprotect(data, validLength, PROT_READ | PROT_WRITE)) {
   449         munmap(data, AsmJSMappedSize);
   450         return false;
   451     }
   452 #   if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
   453     // Tell Valgrind/Memcheck to not report accesses in the inaccessible region.
   454     VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + validLength,
   455                                                    AsmJSMappedSize-validLength);
   456 #   endif
   457 # endif
   459     // Copy over the current contents of the typed array.
   460     memcpy(data, buffer->dataPointer(), buffer->byteLength());
   462     // Swap the new elements into the ArrayBufferObject.
   463     buffer->changeContents(cx, data);
   464     JS_ASSERT(data == buffer->dataPointer());
   466     // Mark the ArrayBufferObject so (1) we don't do this again, (2) we know not
   467     // to js_free the data in the normal way.
   468     buffer->setIsAsmJSArrayBuffer();
   470     return true;
   471 }
   473 void
   474 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
   475 {
   476     void *data = dataPointer();
   478     JS_ASSERT(uintptr_t(data) % AsmJSPageSize == 0);
   479 # ifdef XP_WIN
   480     VirtualFree(data, 0, MEM_RELEASE);
   481 # else
   482     munmap(data, AsmJSMappedSize);
   483 #   if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
   484     // Tell Valgrind/Memcheck to recommence reporting accesses in the
   485     // previously-inaccessible region.
   486     if (AsmJSMappedSize > 0) {
   487         VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(data, AsmJSMappedSize);
   488     }
   489 #   endif
   490 # endif
   491 }
   492 #else  /* defined(JS_ION) && defined(JS_CPU_X64) */
   493 bool
   494 ArrayBufferObject::prepareForAsmJS(JSContext *cx, Handle<ArrayBufferObject*> buffer)
   495 {
   496     if (buffer->isAsmJSArrayBuffer())
   497         return true;
   499     if (buffer->isSharedArrayBuffer())
   500         return true;
   502     if (!ensureNonInline(cx, buffer))
   503         return false;
   505     buffer->setIsAsmJSArrayBuffer();
   506     return true;
   507 }
   509 void
   510 ArrayBufferObject::releaseAsmJSArray(FreeOp *fop)
   511 {
   512     fop->free_(dataPointer());
   513 }
   514 #endif
   516 bool
   517 ArrayBufferObject::canNeuterAsmJSArrayBuffer(JSContext *cx, ArrayBufferObject &buffer)
   518 {
   519     JS_ASSERT(!buffer.isSharedArrayBuffer());
   520 #ifdef JS_ION
   521     AsmJSActivation *act = cx->mainThread().asmJSActivationStackFromOwnerThread();
   522     for (; act; act = act->prevAsmJS()) {
   523         if (act->module().maybeHeapBufferObject() == &buffer)
   524             break;
   525     }
   526     if (!act)
   527         return true;
   529     return false;
   530 #else
   531     return true;
   532 #endif
   533 }
   535 void *
   536 ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length)
   537 {
   538     return AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT);
   539 }
   541 void
   542 ArrayBufferObject::releaseMappedArray()
   543 {
   544     if(!isMappedArrayBuffer() || isNeutered())
   545         return;
   547     DeallocateMappedContent(dataPointer(), byteLength());
   548 }
   550 void
   551 ArrayBufferObject::addView(ArrayBufferViewObject *view)
   552 {
   553     // Note that pre-barriers are not needed here because either the list was
   554     // previously empty, in which case no pointer is being overwritten, or the
   555     // list was nonempty and will be made weak during this call (and weak
   556     // pointers cannot violate the snapshot-at-the-beginning invariant.)
   558     ArrayBufferViewObject *viewsHead = viewList();
   559     if (viewsHead == nullptr) {
   560         // This ArrayBufferObject will have a single view at this point, so it
   561         // is a strong pointer (it will be marked during tracing.)
   562         JS_ASSERT(view->nextView() == nullptr);
   563     } else {
   564         view->setNextView(viewsHead);
   565     }
   567     setViewList(view);
   568 }
   570 uint8_t *
   571 ArrayBufferObject::dataPointer() const
   572 {
   573     if (isSharedArrayBuffer())
   574         return (uint8_t *)this->as<SharedArrayBufferObject>().dataPointer();
   575     return static_cast<uint8_t *>(getSlot(DATA_SLOT).toPrivate());
   576 }
   578 void
   579 ArrayBufferObject::releaseData(FreeOp *fop)
   580 {
   581     JS_ASSERT(ownsData());
   583     if (isAsmJSArrayBuffer())
   584         releaseAsmJSArray(fop);
   585     else if (isMappedArrayBuffer())
   586         releaseMappedArray();
   587     else
   588         fop->free_(dataPointer());
   589 }
   591 void
   592 ArrayBufferObject::setDataPointer(void *data, OwnsState ownsData)
   593 {
   594     MOZ_ASSERT_IF(!is<SharedArrayBufferObject>() && !isMappedArrayBuffer(), data != nullptr);
   595     setSlot(DATA_SLOT, PrivateValue(data));
   596     setOwnsData(ownsData);
   597 }
   599 size_t
   600 ArrayBufferObject::byteLength() const
   601 {
   602     return size_t(getSlot(BYTE_LENGTH_SLOT).toDouble());
   603 }
   605 void
   606 ArrayBufferObject::setByteLength(size_t length)
   607 {
   608     setSlot(BYTE_LENGTH_SLOT, DoubleValue(length));
   609 }
   611 uint32_t
   612 ArrayBufferObject::flags() const
   613 {
   614     return uint32_t(getSlot(FLAGS_SLOT).toInt32());
   615 }
   617 void
   618 ArrayBufferObject::setFlags(uint32_t flags)
   619 {
   620     setSlot(FLAGS_SLOT, Int32Value(flags));
   621 }
   623 ArrayBufferObject *
   624 ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, void *data /* = nullptr */,
   625                           NewObjectKind newKind /* = GenericObject */,
   626                           bool mapped /* = false */)
   627 {
   628     JS_ASSERT_IF(mapped, data);
   630     // If we need to allocate data, try to use a larger object size class so
   631     // that the array buffer's data can be allocated inline with the object.
   632     // The extra space will be left unused by the object's fixed slots and
   633     // available for the buffer's data, see NewObject().
   634     size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
   636     size_t nslots = reservedSlots;
   637     if (!data) {
   638         size_t usableSlots = JSObject::MAX_FIXED_SLOTS - reservedSlots;
   639         if (nbytes <= usableSlots * sizeof(Value)) {
   640             int newSlots = (nbytes - 1) / sizeof(Value) + 1;
   641             JS_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
   642             nslots = reservedSlots + newSlots;
   643         } else {
   644             data = AllocateArrayBufferContents(cx, nbytes);
   645             if (!data)
   646                 return nullptr;
   647         }
   648     }
   650     JS_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
   651     gc::AllocKind allocKind = GetGCObjectKind(nslots);
   653     Rooted<ArrayBufferObject*> obj(cx, NewBuiltinClassInstance<ArrayBufferObject>(cx, allocKind, newKind));
   654     if (!obj)
   655         return nullptr;
   657     JS_ASSERT(obj->getClass() == &class_);
   659     JS_ASSERT(!gc::IsInsideNursery(cx->runtime(), obj));
   661     if (data) {
   662         obj->initialize(nbytes, data, OwnsData);
   663         if (mapped)
   664             obj->setIsMappedArrayBuffer();
   665     } else {
   666         void *data = obj->fixedData(reservedSlots);
   667         memset(data, 0, nbytes);
   668         obj->initialize(nbytes, data, DoesntOwnData);
   669     }
   671     return obj;
   672 }
   674 JSObject *
   675 ArrayBufferObject::createSlice(JSContext *cx, Handle<ArrayBufferObject*> arrayBuffer,
   676                                uint32_t begin, uint32_t end)
   677 {
   678     uint32_t bufLength = arrayBuffer->byteLength();
   679     if (begin > bufLength || end > bufLength || begin > end) {
   680         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPE_ERR_BAD_ARGS);
   681         return nullptr;
   682     }
   684     uint32_t length = end - begin;
   686     if (!arrayBuffer->hasData())
   687         return create(cx, 0);
   689     ArrayBufferObject *slice = create(cx, length);
   690     if (!slice)
   691         return nullptr;
   692     memcpy(slice->dataPointer(), arrayBuffer->dataPointer() + begin, length);
   693     return slice;
   694 }
   696 bool
   697 ArrayBufferObject::createDataViewForThisImpl(JSContext *cx, CallArgs args)
   698 {
   699     JS_ASSERT(IsArrayBuffer(args.thisv()));
   701     /*
   702      * This method is only called for |DataView(alienBuf, ...)| which calls
   703      * this as |createDataViewForThis.call(alienBuf, ..., DataView.prototype)|,
   704      * ergo there must be at least two arguments.
   705      */
   706     JS_ASSERT(args.length() >= 2);
   708     Rooted<JSObject*> proto(cx, &args[args.length() - 1].toObject());
   710     Rooted<JSObject*> buffer(cx, &args.thisv().toObject());
   712     /*
   713      * Pop off the passed-along prototype and delegate to normal DataViewObject
   714      * construction.
   715      */
   716     CallArgs frobbedArgs = CallArgsFromVp(args.length() - 1, args.base());
   717     return DataViewObject::construct(cx, buffer, frobbedArgs, proto);
   718 }
   720 bool
   721 ArrayBufferObject::createDataViewForThis(JSContext *cx, unsigned argc, Value *vp)
   722 {
   723     CallArgs args = CallArgsFromVp(argc, vp);
   724     return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
   725 }
   727 /* static */ bool
   728 ArrayBufferObject::ensureNonInline(JSContext *cx, Handle<ArrayBufferObject*> buffer)
   729 {
   730     if (!buffer->ownsData()) {
   731         void *data = AllocateArrayBufferContents(cx, buffer->byteLength());
   732         if (!data)
   733             return false;
   734         memcpy(data, buffer->dataPointer(), buffer->byteLength());
   735         buffer->changeContents(cx, data);
   736     }
   738     return true;
   739 }
   741 /* static */ void *
   742 ArrayBufferObject::stealContents(JSContext *cx, Handle<ArrayBufferObject*> buffer)
   743 {
   744     if (!buffer->canNeuter(cx)) {
   745         js_ReportOverRecursed(cx);
   746         return nullptr;
   747     }
   749     void *oldData = buffer->dataPointer();
   750     void *newData = AllocateArrayBufferContents(cx, buffer->byteLength());
   751     if (!newData)
   752         return nullptr;
   754     if (buffer->hasStealableContents()) {
   755         buffer->setOwnsData(DoesntOwnData);
   756         ArrayBufferObject::neuter(cx, buffer, newData);
   757         return oldData;
   758     } else {
   759         memcpy(newData, oldData, buffer->byteLength());
   760         ArrayBufferObject::neuter(cx, buffer, oldData);
   761         return newData;
   762     }
   764     return oldData;
   765 }
   767 /* static */ void
   768 ArrayBufferObject::addSizeOfExcludingThis(JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, JS::ObjectsExtraSizes *sizes)
   769 {
   770     ArrayBufferObject &buffer = AsArrayBuffer(obj);
   772     if (!buffer.ownsData())
   773         return;
   775     if (MOZ_UNLIKELY(buffer.isAsmJSArrayBuffer())) {
   776 #if defined (JS_CPU_X64)
   777         // On x64, ArrayBufferObject::prepareForAsmJS switches the
   778         // ArrayBufferObject to use mmap'd storage.
   779         sizes->nonHeapElementsAsmJS += buffer.byteLength();
   780 #else
   781         sizes->mallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
   782 #endif
   783     } else if (MOZ_UNLIKELY(buffer.isMappedArrayBuffer())) {
   784         sizes->nonHeapElementsMapped += buffer.byteLength();
   785     } else if (buffer.dataPointer()) {
   786         sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(buffer.dataPointer());
   787     }
   788 }
   790 /* static */ void
   791 ArrayBufferObject::finalize(FreeOp *fop, JSObject *obj)
   792 {
   793     ArrayBufferObject &buffer = obj->as<ArrayBufferObject>();
   795     if (buffer.ownsData())
   796         buffer.releaseData(fop);
   797 }
   799 /* static */ void
   800 ArrayBufferObject::obj_trace(JSTracer *trc, JSObject *obj)
   801 {
   802     if (!IS_GC_MARKING_TRACER(trc) && !trc->runtime()->isHeapMinorCollecting())
   803         return;
   805     // ArrayBufferObjects need to maintain a list of possibly-weak pointers to
   806     // their views. The straightforward way to update the weak pointers would
   807     // be in the views' finalizers, but giving views finalizers means they
   808     // cannot be swept in the background. This results in a very high
   809     // performance cost.  Instead, ArrayBufferObjects with a single view hold a
   810     // strong pointer to the view. This can entrain garbage when the single
   811     // view becomes otherwise unreachable while the buffer is still live, but
   812     // this is expected to be rare. ArrayBufferObjects with 0-1 views are
   813     // expected to be by far the most common cases. ArrayBufferObjects with
   814     // multiple views are collected into a linked list during collection, and
   815     // then swept to prune out their dead views.
   817     ArrayBufferObject &buffer = AsArrayBuffer(obj);
   818     ArrayBufferViewObject *viewsHead = buffer.viewList();
   819     if (!viewsHead)
   820         return;
   822     buffer.setViewList(UpdateObjectIfRelocated(trc->runtime(), &viewsHead));
   824     if (viewsHead->nextView() == nullptr) {
   825         // Single view: mark it, but only if we're actually doing a GC pass
   826         // right now. Otherwise, the tracing pass for barrier verification will
   827         // fail if we add another view and the pointer becomes weak.
   828         MarkObjectUnbarriered(trc, &viewsHead, "arraybuffer.singleview");
   829         buffer.setViewListNoBarrier(viewsHead);
   830     } else {
   831         // Multiple views: do not mark, but append buffer to list.
   832         ArrayBufferVector &gcLiveArrayBuffers = buffer.compartment()->gcLiveArrayBuffers;
   834         // obj_trace may be called multiple times before sweep(), so avoid
   835         // adding this buffer to the list multiple times.
   836         if (buffer.inLiveList()) {
   837 #ifdef DEBUG
   838             bool found = false;
   839             for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++)
   840                 found |= gcLiveArrayBuffers[i] == &buffer;
   841             JS_ASSERT(found);
   842 #endif
   843         } else if (gcLiveArrayBuffers.append(&buffer)) {
   844             buffer.setInLiveList(true);
   845         } else {
   846             CrashAtUnhandlableOOM("OOM while updating live array buffers");
   847         }
   848     }
   849 }
   851 /* static */ void
   852 ArrayBufferObject::sweep(JSCompartment *compartment)
   853 {
   854     JSRuntime *rt = compartment->runtimeFromMainThread();
   855     ArrayBufferVector &gcLiveArrayBuffers = compartment->gcLiveArrayBuffers;
   857     for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
   858         ArrayBufferObject *buffer = gcLiveArrayBuffers[i];
   860         JS_ASSERT(buffer->inLiveList());
   861         buffer->setInLiveList(false);
   863         ArrayBufferViewObject *viewsHead = buffer->viewList();
   864         JS_ASSERT(viewsHead);
   865         buffer->setViewList(UpdateObjectIfRelocated(rt, &viewsHead));
   867         // Rebuild the list of views of the ArrayBufferObject, discarding dead
   868         // views.  If there is only one view, it will have already been marked.
   869         ArrayBufferViewObject *prevLiveView = nullptr;
   870         ArrayBufferViewObject *view = viewsHead;
   871         while (view) {
   872             JS_ASSERT(buffer->compartment() == view->compartment());
   873             ArrayBufferViewObject *nextView = view->nextView();
   874             if (!IsObjectAboutToBeFinalized(&view)) {
   875                 view->setNextView(prevLiveView);
   876                 prevLiveView = view;
   877             }
   878             view = UpdateObjectIfRelocated(rt, &nextView);
   879         }
   881         buffer->setViewList(prevLiveView);
   882     }
   884     gcLiveArrayBuffers.clear();
   885 }
   887 void
   888 ArrayBufferObject::resetArrayBufferList(JSCompartment *comp)
   889 {
   890     ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers;
   892     for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
   893         ArrayBufferObject *buffer = gcLiveArrayBuffers[i];
   895         JS_ASSERT(buffer->inLiveList());
   896         buffer->setInLiveList(false);
   897     }
   899     gcLiveArrayBuffers.clear();
   900 }
   902 /* static */ bool
   903 ArrayBufferObject::saveArrayBufferList(JSCompartment *comp, ArrayBufferVector &vector)
   904 {
   905     const ArrayBufferVector &gcLiveArrayBuffers = comp->gcLiveArrayBuffers;
   907     for (size_t i = 0; i < gcLiveArrayBuffers.length(); i++) {
   908         if (!vector.append(gcLiveArrayBuffers[i]))
   909             return false;
   910     }
   912     return true;
   913 }
   915 /* static */ void
   916 ArrayBufferObject::restoreArrayBufferLists(ArrayBufferVector &vector)
   917 {
   918     for (size_t i = 0; i < vector.length(); i++) {
   919         ArrayBufferObject *buffer = vector[i];
   921         JS_ASSERT(!buffer->inLiveList());
   922         buffer->setInLiveList(true);
   924         buffer->compartment()->gcLiveArrayBuffers.infallibleAppend(buffer);
   925     }
   926 }
   928 /*
   929  * ArrayBufferViewObject
   930  */
   932 /*
   933  * This method is used to trace TypedArrayObjects and DataViewObjects. We need
   934  * a custom tracer because some of an ArrayBufferViewObject's reserved slots
   935  * are weak references, and some need to be updated specially during moving
   936  * GCs.
   937  */
   938 /* static */ void
   939 ArrayBufferViewObject::trace(JSTracer *trc, JSObject *obj)
   940 {
   941     HeapSlot &bufSlot = obj->getReservedSlotRef(BUFFER_SLOT);
   942     MarkSlot(trc, &bufSlot, "typedarray.buffer");
   944     // Update obj's data pointer if the array buffer moved. Note that during
   945     // initialization, bufSlot may still contain |undefined|.
   946     if (bufSlot.isObject()) {
   947         ArrayBufferObject &buf = AsArrayBuffer(&bufSlot.toObject());
   948         int32_t offset = obj->getReservedSlot(BYTEOFFSET_SLOT).toInt32();
   949         MOZ_ASSERT(buf.dataPointer() != nullptr);
   950         obj->initPrivate(buf.dataPointer() + offset);
   951     }
   953     /* Update NEXT_VIEW_SLOT, if the view moved. */
   954     IsSlotMarked(&obj->getReservedSlotRef(NEXT_VIEW_SLOT));
   955 }
   957 void
   958 ArrayBufferViewObject::neuter(void *newData)
   959 {
   960     MOZ_ASSERT(newData != nullptr);
   961     if (is<DataViewObject>())
   962         as<DataViewObject>().neuter(newData);
   963     else if (is<TypedArrayObject>())
   964         as<TypedArrayObject>().neuter(newData);
   965     else
   966         as<TypedObject>().neuter(newData);
   967 }
   969 /* static */ ArrayBufferObject *
   970 ArrayBufferViewObject::bufferObject(JSContext *cx, Handle<ArrayBufferViewObject *> thisObject)
   971 {
   972     if (thisObject->is<TypedArrayObject>()) {
   973         Rooted<TypedArrayObject *> typedArray(cx, &thisObject->as<TypedArrayObject>());
   974         if (!TypedArrayObject::ensureHasBuffer(cx, typedArray))
   975             return nullptr;
   976     }
   977     return &thisObject->getFixedSlot(BUFFER_SLOT).toObject().as<ArrayBufferObject>();
   978 }
   980 /* JS Friend API */
   982 JS_FRIEND_API(bool)
   983 JS_IsArrayBufferViewObject(JSObject *obj)
   984 {
   985     obj = CheckedUnwrap(obj);
   986     return obj ? obj->is<ArrayBufferViewObject>() : false;
   987 }
   989 JS_FRIEND_API(JSObject *)
   990 js::UnwrapArrayBufferView(JSObject *obj)
   991 {
   992     if (JSObject *unwrapped = CheckedUnwrap(obj))
   993         return unwrapped->is<ArrayBufferViewObject>() ? unwrapped : nullptr;
   994     return nullptr;
   995 }
   997 JS_FRIEND_API(uint32_t)
   998 JS_GetArrayBufferByteLength(JSObject *obj)
   999 {
  1000     obj = CheckedUnwrap(obj);
  1001     return obj ? AsArrayBuffer(obj).byteLength() : 0;
  1004 JS_FRIEND_API(uint8_t *)
  1005 JS_GetArrayBufferData(JSObject *obj)
  1007     obj = CheckedUnwrap(obj);
  1008     if (!obj)
  1009         return nullptr;
  1010     return AsArrayBuffer(obj).dataPointer();
  1013 JS_FRIEND_API(uint8_t *)
  1014 JS_GetStableArrayBufferData(JSContext *cx, HandleObject objArg)
  1016     JSObject *obj = CheckedUnwrap(objArg);
  1017     if (!obj)
  1018         return nullptr;
  1020     Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(obj));
  1021     if (!ArrayBufferObject::ensureNonInline(cx, buffer))
  1022         return nullptr;
  1024     return buffer->dataPointer();
  1027 JS_FRIEND_API(bool)
  1028 JS_NeuterArrayBuffer(JSContext *cx, HandleObject obj,
  1029                      NeuterDataDisposition changeData)
  1031     if (!obj->is<ArrayBufferObject>()) {
  1032         JS_ReportError(cx, "ArrayBuffer object required");
  1033         return false;
  1036     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
  1038     if (!buffer->canNeuter(cx)) {
  1039         js_ReportOverRecursed(cx);
  1040         return false;
  1043     void *newData;
  1044     if (changeData == ChangeData && buffer->hasStealableContents()) {
  1045         newData = AllocateArrayBufferContents(cx, buffer->byteLength());
  1046         if (!newData)
  1047             return false;
  1048     } else {
  1049         newData = buffer->dataPointer();
  1052     ArrayBufferObject::neuter(cx, buffer, newData);
  1053     return true;
  1056 JS_FRIEND_API(JSObject *)
  1057 JS_NewArrayBuffer(JSContext *cx, uint32_t nbytes)
  1059     JS_ASSERT(nbytes <= INT32_MAX);
  1060     return ArrayBufferObject::create(cx, nbytes);
  1063 JS_PUBLIC_API(JSObject *)
  1064 JS_NewArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents)
  1066     JS_ASSERT(contents);
  1067     return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, false);
  1070 JS_PUBLIC_API(void *)
  1071 JS_AllocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes)
  1073     return AllocateArrayBufferContents(maybecx, nbytes);
  1076 JS_PUBLIC_API(void *)
  1077 JS_ReallocateArrayBufferContents(JSContext *maybecx, uint32_t nbytes, void *oldContents, uint32_t oldNbytes)
  1079     return AllocateArrayBufferContents(maybecx, nbytes, oldContents, oldNbytes);
  1082 JS_FRIEND_API(bool)
  1083 JS_IsArrayBufferObject(JSObject *obj)
  1085     obj = CheckedUnwrap(obj);
  1086     return obj ? obj->is<ArrayBufferObject>() : false;
  1089 JS_FRIEND_API(JSObject *)
  1090 js::UnwrapArrayBuffer(JSObject *obj)
  1092     if (JSObject *unwrapped = CheckedUnwrap(obj))
  1093         return unwrapped->is<ArrayBufferObject>() ? unwrapped : nullptr;
  1094     return nullptr;
  1097 JS_PUBLIC_API(void *)
  1098 JS_StealArrayBufferContents(JSContext *cx, HandleObject objArg)
  1100     JSObject *obj = CheckedUnwrap(objArg);
  1101     if (!obj)
  1102         return nullptr;
  1104     if (!obj->is<ArrayBufferObject>()) {
  1105         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
  1106         return nullptr;
  1109     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
  1110     return ArrayBufferObject::stealContents(cx, buffer);
  1113 JS_PUBLIC_API(JSObject *)
  1114 JS_NewMappedArrayBufferWithContents(JSContext *cx, size_t nbytes, void *contents)
  1116     JS_ASSERT(contents);
  1117     return ArrayBufferObject::create(cx, nbytes, contents, TenuredObject, true);
  1120 JS_PUBLIC_API(void *)
  1121 JS_CreateMappedArrayBufferContents(int fd, size_t offset, size_t length)
  1123     return ArrayBufferObject::createMappedContents(fd, offset, length);
  1126 JS_PUBLIC_API(void)
  1127 JS_ReleaseMappedArrayBufferContents(void *contents, size_t length)
  1129     DeallocateMappedContent(contents, length);
  1132 JS_FRIEND_API(bool)
  1133 JS_IsMappedArrayBufferObject(JSObject *obj)
  1135     obj = CheckedUnwrap(obj);
  1136     if (!obj)
  1137         return false;
  1139     return obj->is<ArrayBufferObject>()
  1140            ? obj->as<ArrayBufferObject>().isMappedArrayBuffer()
  1141            : false;
  1144 JS_FRIEND_API(void *)
  1145 JS_GetArrayBufferViewData(JSObject *obj)
  1147     obj = CheckedUnwrap(obj);
  1148     if (!obj)
  1149         return nullptr;
  1150     return obj->is<DataViewObject>() ? obj->as<DataViewObject>().dataPointer()
  1151                                      : obj->as<TypedArrayObject>().viewData();
  1154 JS_FRIEND_API(JSObject *)
  1155 JS_GetArrayBufferViewBuffer(JSContext *cx, JSObject *obj)
  1157     obj = CheckedUnwrap(obj);
  1158     if (!obj)
  1159         return nullptr;
  1160     Rooted<ArrayBufferViewObject *> viewObject(cx, &obj->as<ArrayBufferViewObject>());
  1161     return ArrayBufferViewObject::bufferObject(cx, viewObject);
  1164 JS_FRIEND_API(uint32_t)
  1165 JS_GetArrayBufferViewByteLength(JSObject *obj)
  1167     obj = CheckedUnwrap(obj);
  1168     if (!obj)
  1169         return 0;
  1170     return obj->is<DataViewObject>()
  1171            ? obj->as<DataViewObject>().byteLength()
  1172            : obj->as<TypedArrayObject>().byteLength();
  1175 JS_FRIEND_API(JSObject *)
  1176 JS_GetObjectAsArrayBufferView(JSObject *obj, uint32_t *length, uint8_t **data)
  1178     if (!(obj = CheckedUnwrap(obj)))
  1179         return nullptr;
  1180     if (!(obj->is<ArrayBufferViewObject>()))
  1181         return nullptr;
  1183     *length = obj->is<DataViewObject>()
  1184               ? obj->as<DataViewObject>().byteLength()
  1185               : obj->as<TypedArrayObject>().byteLength();
  1187     *data = static_cast<uint8_t*>(obj->is<DataViewObject>()
  1188                                   ? obj->as<DataViewObject>().dataPointer()
  1189                                   : obj->as<TypedArrayObject>().viewData());
  1190     return obj;
  1193 JS_FRIEND_API(void)
  1194 js::GetArrayBufferViewLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
  1196     MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
  1198     *length = obj->is<DataViewObject>()
  1199               ? obj->as<DataViewObject>().byteLength()
  1200               : obj->as<TypedArrayObject>().byteLength();
  1202     *data = static_cast<uint8_t*>(obj->is<DataViewObject>()
  1203                                   ? obj->as<DataViewObject>().dataPointer()
  1204                                   : obj->as<TypedArrayObject>().viewData());
  1207 JS_FRIEND_API(JSObject *)
  1208 JS_GetObjectAsArrayBuffer(JSObject *obj, uint32_t *length, uint8_t **data)
  1210     if (!(obj = CheckedUnwrap(obj)))
  1211         return nullptr;
  1212     if (!IsArrayBuffer(obj))
  1213         return nullptr;
  1215     *length = AsArrayBuffer(obj).byteLength();
  1216     *data = AsArrayBuffer(obj).dataPointer();
  1218     return obj;
  1221 JS_FRIEND_API(void)
  1222 js::GetArrayBufferLengthAndData(JSObject *obj, uint32_t *length, uint8_t **data)
  1224     MOZ_ASSERT(IsArrayBuffer(obj));
  1225     *length = AsArrayBuffer(obj).byteLength();
  1226     *data = AsArrayBuffer(obj).dataPointer();

mercurial