LCOV - code coverage report
Current view: top level - js/src/vm - ArrayBufferObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 5 783 0.6 %
Date: 2017-07-14 16:53:18 Functions: 2 103 1.9 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : #include "vm/ArrayBufferObject-inl.h"
       8             : #include "vm/ArrayBufferObject.h"
       9             : 
      10             : #include "mozilla/Alignment.h"
      11             : #include "mozilla/CheckedInt.h"
      12             : #include "mozilla/FloatingPoint.h"
      13             : #include "mozilla/Maybe.h"
      14             : #include "mozilla/PodOperations.h"
      15             : #include "mozilla/TaggedAnonymousMemory.h"
      16             : 
      17             : #include <string.h>
      18             : #ifndef XP_WIN
      19             : # include <sys/mman.h>
      20             : #endif
      21             : 
      22             : #ifdef MOZ_VALGRIND
      23             : # include <valgrind/memcheck.h>
      24             : #endif
      25             : 
      26             : #include "jsapi.h"
      27             : #include "jsarray.h"
      28             : #include "jscntxt.h"
      29             : #include "jscpucfg.h"
      30             : #include "jsfriendapi.h"
      31             : #include "jsnum.h"
      32             : #include "jsobj.h"
      33             : #include "jstypes.h"
      34             : #include "jsutil.h"
      35             : #ifdef XP_WIN
      36             : # include "jswin.h"
      37             : #endif
      38             : #include "jswrapper.h"
      39             : 
      40             : #include "builtin/DataViewObject.h"
      41             : #include "gc/Barrier.h"
      42             : #include "gc/Marking.h"
      43             : #include "gc/Memory.h"
      44             : #include "js/Conversions.h"
      45             : #include "js/MemoryMetrics.h"
      46             : #include "vm/GlobalObject.h"
      47             : #include "vm/Interpreter.h"
      48             : #include "vm/SharedArrayObject.h"
      49             : #include "vm/WrapperObject.h"
      50             : #include "wasm/WasmSignalHandlers.h"
      51             : #include "wasm/WasmTypes.h"
      52             : 
      53             : #include "jsatominlines.h"
      54             : 
      55             : #include "vm/NativeObject-inl.h"
      56             : #include "vm/Shape-inl.h"
      57             : 
      58             : using JS::ToInt32;
      59             : 
      60             : using mozilla::DebugOnly;
      61             : using mozilla::CheckedInt;
      62             : using mozilla::Some;
      63             : using mozilla::Maybe;
      64             : using mozilla::Nothing;
      65             : 
      66             : using namespace js;
      67             : using namespace js::gc;
      68             : 
      69             : /*
      70             :  * Convert |v| to an array index for an array of length |length| per
      71             :  * the Typed Array Specification section 7.0, |subarray|. If successful,
      72             :  * the output value is in the range [0, length].
      73             :  */
      74             : bool
      75           0 : js::ToClampedIndex(JSContext* cx, HandleValue v, uint32_t length, uint32_t* out)
      76             : {
      77             :     int32_t result;
      78           0 :     if (!ToInt32(cx, v, &result))
      79           0 :         return false;
      80           0 :     if (result < 0) {
      81           0 :         result += length;
      82           0 :         if (result < 0)
      83           0 :             result = 0;
      84           0 :     } else if (uint32_t(result) > length) {
      85           0 :         result = length;
      86             :     }
      87           0 :     *out = uint32_t(result);
      88           0 :     return true;
      89             : }
      90             : 
      91             : /*
      92             :  * ArrayBufferObject
      93             :  *
      94             :  * This class holds the underlying raw buffer that the TypedArrayObject classes
      95             :  * access.  It can be created explicitly and passed to a TypedArrayObject, or
      96             :  * can be created implicitly by constructing a TypedArrayObject with a size.
      97             :  */
      98             : 
      99             : /*
     100             :  * ArrayBufferObject (base)
     101             :  */
     102             : 
     103             : static JSObject*
     104           6 : CreateArrayBufferPrototype(JSContext* cx, JSProtoKey key)
     105             : {
     106           6 :     return GlobalObject::createBlankPrototype(cx, cx->global(), &ArrayBufferObject::protoClass_);
     107             : }
     108             : 
     109             : static const ClassOps ArrayBufferObjectClassOps = {
     110             :     nullptr,        /* addProperty */
     111             :     nullptr,        /* delProperty */
     112             :     nullptr,        /* getProperty */
     113             :     nullptr,        /* setProperty */
     114             :     nullptr,        /* enumerate */
     115             :     nullptr,        /* newEnumerate */
     116             :     nullptr,        /* resolve */
     117             :     nullptr,        /* mayResolve */
     118             :     ArrayBufferObject::finalize,
     119             :     nullptr,        /* call        */
     120             :     nullptr,        /* hasInstance */
     121             :     nullptr,        /* construct   */
     122             :     ArrayBufferObject::trace,
     123             : };
     124             : 
     125             : static const JSFunctionSpec static_functions[] = {
     126             :     JS_FN("isView", ArrayBufferObject::fun_isView, 1, 0),
     127             :     JS_FS_END
     128             : };
     129             : 
     130             : static const JSPropertySpec static_properties[] = {
     131             :     JS_SELF_HOSTED_SYM_GET(species, "ArrayBufferSpecies", 0),
     132             :     JS_PS_END
     133             : };
     134             : 
     135             : 
     136             : static const JSFunctionSpec prototype_functions[] = {
     137             :     JS_SELF_HOSTED_FN("slice", "ArrayBufferSlice", 2, 0),
     138             :     JS_FS_END
     139             : };
     140             : 
     141             : static const JSPropertySpec prototype_properties[] = {
     142             :     JS_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0),
     143             :     JS_STRING_SYM_PS(toStringTag, "ArrayBuffer", JSPROP_READONLY),
     144             :     JS_PS_END
     145             : };
     146             : 
     147             : static const ClassSpec ArrayBufferObjectClassSpec = {
     148             :     GenericCreateConstructor<ArrayBufferObject::class_constructor, 1, gc::AllocKind::FUNCTION>,
     149             :     CreateArrayBufferPrototype,
     150             :     static_functions,
     151             :     static_properties,
     152             :     prototype_functions,
     153             :     prototype_properties
     154             : };
     155             : 
     156             : static const ClassExtension ArrayBufferObjectClassExtension = {
     157             :     nullptr,    /* weakmapKeyDelegateOp */
     158             :     ArrayBufferObject::objectMoved
     159             : };
     160             : 
     161             : const Class ArrayBufferObject::class_ = {
     162             :     "ArrayBuffer",
     163             :     JSCLASS_DELAY_METADATA_BUILDER |
     164             :     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
     165             :     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer) |
     166             :     JSCLASS_BACKGROUND_FINALIZE,
     167             :     &ArrayBufferObjectClassOps,
     168             :     &ArrayBufferObjectClassSpec,
     169             :     &ArrayBufferObjectClassExtension
     170             : };
     171             : 
     172             : const Class ArrayBufferObject::protoClass_ = {
     173             :     "ArrayBufferPrototype",
     174             :     JSCLASS_HAS_CACHED_PROTO(JSProto_ArrayBuffer),
     175             :     JS_NULL_CLASS_OPS,
     176             :     &ArrayBufferObjectClassSpec
     177             : };
     178             : 
     179             : bool
     180           0 : js::IsArrayBuffer(HandleValue v)
     181             : {
     182           0 :     return v.isObject() && v.toObject().is<ArrayBufferObject>();
     183             : }
     184             : 
     185             : bool
     186           0 : js::IsArrayBuffer(HandleObject obj)
     187             : {
     188           0 :     return obj->is<ArrayBufferObject>();
     189             : }
     190             : 
     191             : bool
     192           0 : js::IsArrayBuffer(JSObject* obj)
     193             : {
     194           0 :     return obj->is<ArrayBufferObject>();
     195             : }
     196             : 
     197             : ArrayBufferObject&
     198           0 : js::AsArrayBuffer(HandleObject obj)
     199             : {
     200           0 :     MOZ_ASSERT(IsArrayBuffer(obj));
     201           0 :     return obj->as<ArrayBufferObject>();
     202             : }
     203             : 
     204             : ArrayBufferObject&
     205           0 : js::AsArrayBuffer(JSObject* obj)
     206             : {
     207           0 :     MOZ_ASSERT(IsArrayBuffer(obj));
     208           0 :     return obj->as<ArrayBufferObject>();
     209             : }
     210             : 
     211             : bool
     212           0 : js::IsArrayBufferMaybeShared(HandleValue v)
     213             : {
     214           0 :     return v.isObject() && v.toObject().is<ArrayBufferObjectMaybeShared>();
     215             : }
     216             : 
     217             : bool
     218           0 : js::IsArrayBufferMaybeShared(HandleObject obj)
     219             : {
     220           0 :     return obj->is<ArrayBufferObjectMaybeShared>();
     221             : }
     222             : 
     223             : bool
     224           0 : js::IsArrayBufferMaybeShared(JSObject* obj)
     225             : {
     226           0 :     return obj->is<ArrayBufferObjectMaybeShared>();
     227             : }
     228             : 
     229             : ArrayBufferObjectMaybeShared&
     230           0 : js::AsArrayBufferMaybeShared(HandleObject obj)
     231             : {
     232           0 :     MOZ_ASSERT(IsArrayBufferMaybeShared(obj));
     233           0 :     return obj->as<ArrayBufferObjectMaybeShared>();
     234             : }
     235             : 
     236             : ArrayBufferObjectMaybeShared&
     237           0 : js::AsArrayBufferMaybeShared(JSObject* obj)
     238             : {
     239           0 :     MOZ_ASSERT(IsArrayBufferMaybeShared(obj));
     240           0 :     return obj->as<ArrayBufferObjectMaybeShared>();
     241             : }
     242             : 
     243             : MOZ_ALWAYS_INLINE bool
     244           0 : ArrayBufferObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
     245             : {
     246           0 :     MOZ_ASSERT(IsArrayBuffer(args.thisv()));
     247           0 :     args.rval().setInt32(args.thisv().toObject().as<ArrayBufferObject>().byteLength());
     248           0 :     return true;
     249             : }
     250             : 
     251             : bool
     252           0 : ArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
     253             : {
     254           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     255           0 :     return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
     256             : }
     257             : 
     258             : /*
     259             :  * ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
     260             :  */
     261             : bool
     262           0 : ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
     263             : {
     264           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     265           0 :     args.rval().setBoolean(args.get(0).isObject() &&
     266           0 :                            JS_IsArrayBufferViewObject(&args.get(0).toObject()));
     267           0 :     return true;
     268             : }
     269             : 
     270             : // ES2017 draft 24.1.2.1
     271             : bool
     272           0 : ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
     273             : {
     274           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     275             : 
     276             :     // Step 1.
     277           0 :     if (!ThrowIfNotConstructing(cx, args, "ArrayBuffer"))
     278           0 :         return false;
     279             : 
     280             :     // Step 2.
     281             :     uint64_t byteLength;
     282           0 :     if (!ToIndex(cx, args.get(0), &byteLength))
     283           0 :         return false;
     284             : 
     285             :     // Step 3 (Inlined 24.1.1.1 AllocateArrayBuffer).
     286             :     // 24.1.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
     287           0 :     RootedObject proto(cx);
     288           0 :     if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
     289           0 :         return false;
     290             : 
     291             :     // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
     292             :     // Refuse to allocate too large buffers, currently limited to ~2 GiB.
     293           0 :     if (byteLength > INT32_MAX) {
     294           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
     295           0 :         return false;
     296             :     }
     297             : 
     298             :     // 24.1.1.1, steps 1 and 4-6.
     299           0 :     JSObject* bufobj = create(cx, uint32_t(byteLength), proto);
     300           0 :     if (!bufobj)
     301           0 :         return false;
     302           0 :     args.rval().setObject(*bufobj);
     303           0 :     return true;
     304             : }
     305             : 
     306             : static ArrayBufferObject::BufferContents
     307           0 : AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
     308             : {
     309           0 :     uint8_t* p = cx->runtime()->pod_callocCanGC<uint8_t>(nbytes);
     310           0 :     if (!p)
     311           0 :         ReportOutOfMemory(cx);
     312             : 
     313           0 :     return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
     314             : }
     315             : 
     316             : static void
     317           0 : NoteViewBufferWasDetached(ArrayBufferViewObject* view,
     318             :                           ArrayBufferObject::BufferContents newContents,
     319             :                           JSContext* cx)
     320             : {
     321           0 :     view->notifyBufferDetached(cx, newContents.data());
     322             : 
     323             :     // Notify compiled jit code that the base pointer has moved.
     324           0 :     MarkObjectStateChange(cx, view);
     325           0 : }
     326             : 
     327             : /* static */ void
     328           0 : ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer,
     329             :                           BufferContents newContents)
     330             : {
     331           0 :     assertSameCompartment(cx, buffer);
     332           0 :     MOZ_ASSERT(!buffer->isPreparedForAsmJS());
     333             : 
     334             :     // When detaching buffers where we don't know all views, the new data must
     335             :     // match the old data. All missing views are typed objects, which do not
     336             :     // expect their data to ever change.
     337           0 :     MOZ_ASSERT_IF(buffer->forInlineTypedObject(),
     338             :                   newContents.data() == buffer->dataPointer());
     339             : 
     340             :     // When detaching a buffer with typed object views, any jitcode accessing
     341             :     // such views must be deoptimized so that detachment checks are performed.
     342             :     // This is done by setting a compartment-wide flag indicating that buffers
     343             :     // with typed object views have been detached.
     344           0 :     if (buffer->hasTypedObjectViews()) {
     345             :         // Make sure the global object's group has been instantiated, so the
     346             :         // flag change will be observed.
     347           0 :         AutoEnterOOMUnsafeRegion oomUnsafe;
     348           0 :         if (!JSObject::getGroup(cx, cx->global()))
     349           0 :             oomUnsafe.crash("ArrayBufferObject::detach");
     350           0 :         MarkObjectGroupFlags(cx, cx->global(), OBJECT_FLAG_TYPED_OBJECT_HAS_DETACHED_BUFFER);
     351           0 :         cx->compartment()->detachedTypedObjects = 1;
     352             :     }
     353             : 
     354             :     // Update all views of the buffer to account for the buffer having been
     355             :     // detached, and clear the buffer's data and list of views.
     356             : 
     357           0 :     auto& innerViews = cx->compartment()->innerViews.get();
     358           0 :     if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(buffer)) {
     359           0 :         for (size_t i = 0; i < views->length(); i++)
     360           0 :             NoteViewBufferWasDetached((*views)[i], newContents, cx);
     361           0 :         innerViews.removeViews(buffer);
     362             :     }
     363           0 :     if (buffer->firstView()) {
     364           0 :         if (buffer->forInlineTypedObject()) {
     365             :             // The buffer points to inline data in its first view, so to keep
     366             :             // this pointer alive we don't clear out the first view.
     367           0 :             MOZ_ASSERT(buffer->firstView()->is<InlineTransparentTypedObject>());
     368             :         } else {
     369           0 :             NoteViewBufferWasDetached(buffer->firstView(), newContents, cx);
     370           0 :             buffer->setFirstView(nullptr);
     371             :         }
     372             :     }
     373             : 
     374           0 :     if (newContents.data() != buffer->dataPointer())
     375           0 :         buffer->setNewData(cx->runtime()->defaultFreeOp(), newContents, OwnsData);
     376             : 
     377           0 :     buffer->setByteLength(0);
     378           0 :     buffer->setIsDetached();
     379           0 : }
     380             : 
     381             : void
     382           0 : ArrayBufferObject::setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState)
     383             : {
     384           0 :     if (ownsData()) {
     385           0 :         MOZ_ASSERT(newContents.data() != dataPointer());
     386           0 :         releaseData(fop);
     387             :     }
     388             : 
     389           0 :     setDataPointer(newContents, ownsState);
     390           0 : }
     391             : 
     392             : // This is called *only* from changeContents(), below.
     393             : // By construction, every view parameter will be mapping unshared memory (an ArrayBuffer).
     394             : // Hence no reason to worry about shared memory here.
     395             : 
     396             : void
     397           0 : ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
     398             :                                       uint8_t* oldDataPointer, BufferContents newContents)
     399             : {
     400           0 :     MOZ_ASSERT(!view->isSharedMemory());
     401             : 
     402             :     // Watch out for NULL data pointers in views. This means that the view
     403             :     // is not fully initialized (in which case it'll be initialized later
     404             :     // with the correct pointer).
     405           0 :     JS::AutoCheckCannotGC nogc;
     406           0 :     uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
     407           0 :     if (viewDataPointer) {
     408           0 :         MOZ_ASSERT(newContents);
     409           0 :         ptrdiff_t offset = viewDataPointer - oldDataPointer;
     410           0 :         viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset;
     411           0 :         view->setDataPointerUnshared(viewDataPointer);
     412             :     }
     413             : 
     414             :     // Notify compiled jit code that the base pointer has moved.
     415           0 :     MarkObjectStateChange(cx, view);
     416           0 : }
     417             : 
     418             : // BufferContents is specific to ArrayBuffer, hence it will not represent shared memory.
     419             : 
     420             : void
     421           0 : ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents,
     422             :                                   OwnsState ownsState)
     423             : {
     424           0 :     MOZ_RELEASE_ASSERT(!isWasm());
     425           0 :     MOZ_ASSERT(!forInlineTypedObject());
     426             : 
     427             :     // Change buffer contents.
     428           0 :     uint8_t* oldDataPointer = dataPointer();
     429           0 :     setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
     430             : 
     431             :     // Update all views.
     432           0 :     auto& innerViews = cx->compartment()->innerViews.get();
     433           0 :     if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
     434           0 :         for (size_t i = 0; i < views->length(); i++)
     435           0 :             changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
     436             :     }
     437           0 :     if (firstView())
     438           0 :         changeViewContents(cx, firstView(), oldDataPointer, newContents);
     439           0 : }
     440             : 
     441             : /*
     442             :  * Wasm Raw Buf Linear Memory Structure
     443             :  *
     444             :  * The linear heap in Wasm is an mmaped array buffer. Several
     445             :  * constants manage its lifetime:
     446             :  *
     447             :  *  - length - the wasm-visible current length of the buffer. Accesses in the
     448             :  *    range [0, length] succeed. May only increase.
     449             :  *
     450             :  *  - boundsCheckLimit - when !WASM_HUGE_MEMORY, the size against which we
     451             :  *    perform bounds checks. It is always a constant offset smaller than
     452             :  *    mappedSize. Currently that constant offset is 64k (wasm::GuardSize).
     453             :  *
     454             :  *  - max - the optional declared limit on how much length can grow.
     455             :  *
     456             :  *  - mappedSize - the actual mmaped size. Access in the range
     457             :  *    [0, mappedSize] will either succeed, or be handled by the wasm signal
     458             :  *    handlers.
     459             :  *
     460             :  * The below diagram shows the layout of the wasm heap. The wasm-visible
     461             :  * portion of the heap starts at 0. There is one extra page prior to the
     462             :  * start of the wasm heap which contains the WasmArrayRawBuffer struct at
     463             :  * its end (i.e. right before the start of the WASM heap).
     464             :  *
     465             :  *  WasmArrayRawBuffer
     466             :  *      \    ArrayBufferObject::dataPointer()
     467             :  *       \  /
     468             :  *        \ |
     469             :  *  ______|_|____________________________________________________________
     470             :  * |______|_|______________|___________________|____________|____________|
     471             :  *          0          length              maxSize  boundsCheckLimit  mappedSize
     472             :  *
     473             :  * \_______________________/
     474             :  *          COMMITED
     475             :  *                          \____________________________________________/
     476             :  *                                           SLOP
     477             :  * \_____________________________________________________________________/
     478             :  *                         MAPPED
     479             :  *
     480             :  * Invariants:
     481             :  *  - length only increases
     482             :  *  - 0 <= length <= maxSize (if present) <= boundsCheckLimit <= mappedSize
     483             :  *  - on ARM boundsCheckLimit must be a valid ARM immediate.
     484             :  *  - if maxSize is not specified, boundsCheckLimit/mappedSize may grow. They are
     485             :  *    otherwise constant.
     486             :  *
     487             :  * NOTE: For asm.js on non-x64 we guarantee that
     488             :  *
     489             :  * length == maxSize == boundsCheckLimit == mappedSize
     490             :  *
     491             :  * That is, signal handlers will not be invoked, since they cannot emulate
     492             :  * asm.js accesses on non-x64 architectures.
     493             :  *
     494             :  * The region between length and mappedSize is the SLOP - an area where we use
     495             :  * signal handlers to catch things that slip by bounds checks. Logically it has
     496             :  * two parts:
     497             :  *
     498             :  *  - from length to boundsCheckLimit - this part of the SLOP serves to catch
     499             :  *  accesses to memory we have reserved but not yet grown into. This allows us
     500             :  *  to grow memory up to max (when present) without having to patch/update the
     501             :  *  bounds checks.
     502             :  *
     503             :  *  - from boundsCheckLimit to mappedSize - (Note: In current patch 0) - this
     504             :  *  part of the SLOP allows us to bounds check against base pointers and fold
     505             :  *  some constant offsets inside loads. This enables better Bounds
     506             :  *  Check Elimination.
     507             :  *
     508             :  */
     509             : 
     510             : class js::WasmArrayRawBuffer
     511             : {
     512             :     Maybe<uint32_t> maxSize_;
     513             :     size_t mappedSize_;
     514             : 
     515             :   protected:
     516           0 :     WasmArrayRawBuffer(uint8_t* buffer, uint32_t length, const Maybe<uint32_t>& maxSize,
     517             :                        size_t mappedSize)
     518           0 :       : maxSize_(maxSize), mappedSize_(mappedSize)
     519             :     {
     520           0 :         MOZ_ASSERT(buffer == dataPointer());
     521           0 :     }
     522             : 
     523             :   public:
     524             :     static WasmArrayRawBuffer* Allocate(uint32_t numBytes, const Maybe<uint32_t>& maxSize);
     525             :     static void Release(void* mem);
     526             : 
     527           0 :     uint8_t* dataPointer() {
     528           0 :         uint8_t* ptr = reinterpret_cast<uint8_t*>(this);
     529           0 :         return ptr + sizeof(WasmArrayRawBuffer);
     530             :     }
     531             : 
     532           0 :     uint8_t* basePointer() {
     533           0 :         return dataPointer() - gc::SystemPageSize();
     534             :     }
     535             : 
     536           0 :     size_t mappedSize() const {
     537           0 :         return mappedSize_;
     538             :     }
     539             : 
     540           0 :     Maybe<uint32_t> maxSize() const {
     541           0 :         return maxSize_;
     542             :     }
     543             : 
     544           0 :     size_t allocatedBytes() const {
     545           0 :         return mappedSize_ + gc::SystemPageSize();
     546             :     }
     547             : 
     548             : #ifndef WASM_HUGE_MEMORY
     549             :     uint32_t boundsCheckLimit() const {
     550             :         MOZ_ASSERT(mappedSize_ <= UINT32_MAX);
     551             :         MOZ_ASSERT(mappedSize_ >= wasm::GuardSize);
     552             :         MOZ_ASSERT(wasm::IsValidBoundsCheckImmediate(mappedSize_ - wasm::GuardSize));
     553             :         return mappedSize_ - wasm::GuardSize;
     554             :     }
     555             : #endif
     556             : 
     557           0 :     MOZ_MUST_USE bool growToSizeInPlace(uint32_t oldSize, uint32_t newSize) {
     558           0 :         MOZ_ASSERT(newSize >= oldSize);
     559           0 :         MOZ_ASSERT_IF(maxSize(), newSize <= maxSize().value());
     560           0 :         MOZ_ASSERT(newSize <= mappedSize());
     561             : 
     562           0 :         uint32_t delta = newSize - oldSize;
     563           0 :         MOZ_ASSERT(delta % wasm::PageSize == 0);
     564             : 
     565           0 :         uint8_t* dataEnd = dataPointer() + oldSize;
     566           0 :         MOZ_ASSERT(uintptr_t(dataEnd) % gc::SystemPageSize() == 0);
     567             : # ifdef XP_WIN
     568             :         if (delta && !VirtualAlloc(dataEnd, delta, MEM_COMMIT, PAGE_READWRITE))
     569             :             return false;
     570             : # else  // XP_WIN
     571           0 :         if (delta && mprotect(dataEnd, delta, PROT_READ | PROT_WRITE))
     572           0 :             return false;
     573             : # endif  // !XP_WIN
     574             : 
     575             : #  if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
     576             :         VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)dataEnd, delta);
     577             : #  endif
     578             : 
     579           0 :         MemProfiler::SampleNative(dataEnd, delta);
     580           0 :         return true;
     581             :     }
     582             : 
     583             : #ifndef WASM_HUGE_MEMORY
     584             :     bool extendMappedSize(uint32_t maxSize) {
     585             :         size_t newMappedSize = wasm::ComputeMappedSize(maxSize);
     586             :         MOZ_ASSERT(mappedSize_ <= newMappedSize);
     587             :         if (mappedSize_ == newMappedSize)
     588             :             return true;
     589             : 
     590             : # ifdef XP_WIN
     591             :         uint8_t* mappedEnd = dataPointer() + mappedSize_;
     592             :         uint32_t delta = newMappedSize - mappedSize_;
     593             :         if (!VirtualAlloc(mappedEnd, delta, MEM_RESERVE, PAGE_NOACCESS))
     594             :             return false;
     595             : # elif defined(XP_LINUX)
     596             :         // Note this will not move memory (no MREMAP_MAYMOVE specified)
     597             :         if (MAP_FAILED == mremap(dataPointer(), mappedSize_, newMappedSize, 0))
     598             :             return false;
     599             : # else
     600             :         // No mechanism for remapping on MacOS and other Unices. Luckily
     601             :         // shouldn't need it here as most of these are 64-bit.
     602             :         return false;
     603             : # endif
     604             : 
     605             :         mappedSize_ = newMappedSize;
     606             :         return true;
     607             :     }
     608             : 
     609             :     // Try and grow the mapped region of memory. Does not changes current size.
     610             :     // Does not move memory if no space to grow.
     611             :     void tryGrowMaxSizeInPlace(uint32_t deltaMaxSize) {
     612             :         CheckedInt<uint32_t> newMaxSize = maxSize_.value();
     613             :         newMaxSize += deltaMaxSize;
     614             :         MOZ_ASSERT(newMaxSize.isValid());
     615             :         MOZ_ASSERT(newMaxSize.value() % wasm::PageSize == 0);
     616             : 
     617             :         if (!extendMappedSize(newMaxSize.value()))
     618             :             return;
     619             : 
     620             :         maxSize_ = Some(newMaxSize.value());
     621             :     }
     622             : #endif // WASM_HUGE_MEMORY
     623             : };
     624             : 
     625             : /* static */ WasmArrayRawBuffer*
     626           0 : WasmArrayRawBuffer::Allocate(uint32_t numBytes, const Maybe<uint32_t>& maxSize)
     627             : {
     628             :     size_t mappedSize;
     629             : #ifdef WASM_HUGE_MEMORY
     630           0 :     mappedSize = wasm::HugeMappedSize;
     631             : #else
     632             :     mappedSize = wasm::ComputeMappedSize(maxSize.valueOr(numBytes));
     633             : #endif
     634             : 
     635           0 :     MOZ_RELEASE_ASSERT(mappedSize <= SIZE_MAX - gc::SystemPageSize());
     636           0 :     MOZ_RELEASE_ASSERT(numBytes <= maxSize.valueOr(UINT32_MAX));
     637           0 :     MOZ_ASSERT(numBytes % gc::SystemPageSize() == 0);
     638           0 :     MOZ_ASSERT(mappedSize % gc::SystemPageSize() == 0);
     639             : 
     640           0 :     uint64_t mappedSizeWithHeader = mappedSize + gc::SystemPageSize();
     641           0 :     uint64_t numBytesWithHeader = numBytes + gc::SystemPageSize();
     642             : 
     643             : # ifdef XP_WIN
     644             :     void* data = VirtualAlloc(nullptr, (size_t) mappedSizeWithHeader, MEM_RESERVE, PAGE_NOACCESS);
     645             :     if (!data)
     646             :         return nullptr;
     647             : 
     648             :     if (!VirtualAlloc(data, numBytesWithHeader, MEM_COMMIT, PAGE_READWRITE)) {
     649             :         VirtualFree(data, 0, MEM_RELEASE);
     650             :         return nullptr;
     651             :     }
     652             : # else  // XP_WIN
     653             :     void* data = MozTaggedAnonymousMmap(nullptr, (size_t) mappedSizeWithHeader, PROT_NONE,
     654           0 :                                         MAP_PRIVATE | MAP_ANON, -1, 0, "wasm-reserved");
     655           0 :     if (data == MAP_FAILED)
     656           0 :         return nullptr;
     657             : 
     658             :     // Note we will waste a page on zero-sized memories here
     659           0 :     if (mprotect(data, numBytesWithHeader, PROT_READ | PROT_WRITE)) {
     660           0 :         munmap(data, mappedSizeWithHeader);
     661           0 :         return nullptr;
     662             :     }
     663             : # endif  // !XP_WIN
     664           0 :     MemProfiler::SampleNative(data, numBytesWithHeader);
     665             : 
     666             : #  if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
     667             :     VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + numBytesWithHeader,
     668             :                                                    mappedSizeWithHeader - numBytesWithHeader);
     669             : #  endif
     670             : 
     671           0 :     uint8_t* base = reinterpret_cast<uint8_t*>(data) + gc::SystemPageSize();
     672           0 :     uint8_t* header = base - sizeof(WasmArrayRawBuffer);
     673             : 
     674           0 :     auto rawBuf = new (header) WasmArrayRawBuffer(base, numBytes, maxSize, mappedSize);
     675           0 :     return rawBuf;
     676             : }
     677             : 
     678             : /* static */ void
     679           0 : WasmArrayRawBuffer::Release(void* mem)
     680             : {
     681           0 :     WasmArrayRawBuffer* header = (WasmArrayRawBuffer*)((uint8_t*)mem - sizeof(WasmArrayRawBuffer));
     682           0 :     uint8_t* base = header->basePointer();
     683           0 :     MOZ_RELEASE_ASSERT(header->mappedSize() <= SIZE_MAX - gc::SystemPageSize());
     684           0 :     size_t mappedSizeWithHeader = header->mappedSize() + gc::SystemPageSize();
     685             : 
     686           0 :     MemProfiler::RemoveNative(base);
     687             : # ifdef XP_WIN
     688             :     VirtualFree(base, 0, MEM_RELEASE);
     689             : # else  // XP_WIN
     690           0 :     munmap(base, mappedSizeWithHeader);
     691             : # endif  // !XP_WIN
     692             : 
     693             : #  if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
     694             :     VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, mappedSizeWithHeader);
     695             : #  endif
     696           0 : }
     697             : 
     698             : WasmArrayRawBuffer*
     699           0 : ArrayBufferObject::BufferContents::wasmBuffer() const
     700             : {
     701           0 :     MOZ_RELEASE_ASSERT(kind_ == WASM);
     702           0 :     return (WasmArrayRawBuffer*)(data_ - sizeof(WasmArrayRawBuffer));
     703             : }
     704             : 
     705             : #define ROUND_UP(v, a) ((v) % (a) == 0 ? (v) : v + a - ((v) % (a)))
     706             : 
     707             : /* static */ ArrayBufferObject*
     708           0 : ArrayBufferObject::createForWasm(JSContext* cx, uint32_t initialSize,
     709             :                                  const Maybe<uint32_t>& maybeMaxSize)
     710             : {
     711           0 :     MOZ_ASSERT(initialSize % wasm::PageSize == 0);
     712           0 :     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
     713           0 :     MOZ_RELEASE_ASSERT((initialSize / wasm::PageSize) <= wasm::MaxMemoryInitialPages);
     714             : 
     715             :     // Prevent applications specifying a large max (like UINT32_MAX) from
     716             :     // unintentially OOMing the browser on 32-bit: they just want "a lot of
     717             :     // memory". Maintain the invariant that initialSize <= maxSize.
     718           0 :     Maybe<uint32_t> maxSize = maybeMaxSize;
     719           0 :     if (sizeof(void*) == 4 && maybeMaxSize) {
     720             :         static const uint32_t OneGiB = 1 << 30;
     721           0 :         uint32_t clamp = Max(OneGiB, initialSize);
     722           0 :         maxSize = Some(Min(clamp, maybeMaxSize.value()));
     723             :     }
     724             : 
     725           0 :     RootedArrayBufferObject buffer(cx, ArrayBufferObject::createEmpty(cx));
     726           0 :     if (!buffer)
     727           0 :         return nullptr;
     728             : 
     729             :     // Try to reserve the maximum requested memory
     730           0 :     WasmArrayRawBuffer* wasmBuf = WasmArrayRawBuffer::Allocate(initialSize, maxSize);
     731           0 :     if (!wasmBuf) {
     732             : #ifdef  WASM_HUGE_MEMORY
     733           0 :         ReportOutOfMemory(cx);
     734           0 :         return nullptr;
     735             : #else
     736             :         // If we fail, and have a maxSize, try to reserve the biggest chunk in
     737             :         // the range [initialSize, maxSize) using log backoff.
     738             :         if (!maxSize) {
     739             :             ReportOutOfMemory(cx);
     740             :             return nullptr;
     741             :         }
     742             : 
     743             :         uint32_t cur = maxSize.value() / 2;
     744             : 
     745             :         for (; cur > initialSize; cur /= 2) {
     746             :             wasmBuf = WasmArrayRawBuffer::Allocate(initialSize, Some(ROUND_UP(cur, wasm::PageSize)));
     747             :             if (wasmBuf)
     748             :                 break;
     749             :         }
     750             : 
     751             :         if (!wasmBuf) {
     752             :             ReportOutOfMemory(cx);
     753             :             return nullptr;
     754             :         }
     755             : 
     756             :         // Try to grow our chunk as much as possible.
     757             :         for (size_t d = cur / 2; d >= wasm::PageSize; d /= 2)
     758             :             wasmBuf->tryGrowMaxSizeInPlace(ROUND_UP(d, wasm::PageSize));
     759             : #endif
     760             :     }
     761             : 
     762           0 :     auto contents = BufferContents::create<WASM>(wasmBuf->dataPointer());
     763           0 :     buffer->initialize(initialSize, contents, OwnsData);
     764           0 :     cx->zone()->updateMallocCounter(wasmBuf->mappedSize());
     765           0 :     return buffer;
     766             : }
     767             : 
     768             : // Note this function can return false with or without an exception pending. The
     769             : // asm.js caller checks cx->isExceptionPending before propagating failure.
     770             : // Returning false without throwing means that asm.js linking will fail which
     771             : // will recompile as non-asm.js.
     772             : /* static */ bool
     773           0 : ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer, bool needGuard)
     774             : {
     775             : #ifdef WASM_HUGE_MEMORY
     776           0 :     MOZ_ASSERT(needGuard);
     777             : #endif
     778           0 :     MOZ_ASSERT(buffer->byteLength() % wasm::PageSize == 0);
     779           0 :     MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
     780             : 
     781           0 :     if (buffer->forInlineTypedObject())
     782           0 :         return false;
     783             : 
     784           0 :     if (needGuard) {
     785           0 :         if (buffer->isWasm() && buffer->isPreparedForAsmJS())
     786           0 :             return true;
     787             : 
     788             :         // Non-prepared-for-asm.js wasm buffers can be detached at any time.
     789             :         // This error can only be triggered for SIMD.js (which isn't shipping)
     790             :         // on !WASM_HUGE_MEMORY so this error is only visible in testing.
     791           0 :         if (buffer->isWasm() || buffer->isPreparedForAsmJS())
     792           0 :             return false;
     793             : 
     794           0 :         uint32_t length = buffer->byteLength();
     795           0 :         WasmArrayRawBuffer* wasmBuf = WasmArrayRawBuffer::Allocate(length, Some(length));
     796           0 :         if (!wasmBuf) {
     797           0 :             ReportOutOfMemory(cx);
     798           0 :             return false;
     799             :         }
     800             : 
     801           0 :         void* data = wasmBuf->dataPointer();
     802           0 :         memcpy(data, buffer->dataPointer(), length);
     803             : 
     804             :         // Swap the new elements into the ArrayBufferObject. Mark the
     805             :         // ArrayBufferObject so we don't do this again.
     806           0 :         buffer->changeContents(cx, BufferContents::create<WASM>(data), OwnsData);
     807           0 :         buffer->setIsPreparedForAsmJS();
     808           0 :         MOZ_ASSERT(data == buffer->dataPointer());
     809           0 :         cx->zone()->updateMallocCounter(wasmBuf->mappedSize());
     810           0 :         return true;
     811             :     }
     812             : 
     813           0 :     if (!buffer->isWasm() && buffer->isPreparedForAsmJS())
     814           0 :         return true;
     815             : 
     816             :     // Non-prepared-for-asm.js wasm buffers can be detached at any time.
     817           0 :     if (buffer->isWasm())
     818           0 :         return false;
     819             : 
     820           0 :     if (!buffer->ownsData()) {
     821           0 :         BufferContents contents = AllocateArrayBufferContents(cx, buffer->byteLength());
     822           0 :         if (!contents)
     823           0 :             return false;
     824           0 :         memcpy(contents.data(), buffer->dataPointer(), buffer->byteLength());
     825           0 :         buffer->changeContents(cx, contents, OwnsData);
     826             :     }
     827             : 
     828           0 :     buffer->setIsPreparedForAsmJS();
     829           0 :     return true;
     830             : }
     831             : 
     832             : ArrayBufferObject::BufferContents
     833           0 : ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length)
     834             : {
     835           0 :     void* data = AllocateMappedContent(fd, offset, length, ARRAY_BUFFER_ALIGNMENT);
     836           0 :     MemProfiler::SampleNative(data, length);
     837           0 :     return BufferContents::create<MAPPED>(data);
     838             : }
     839             : 
     840             : uint8_t*
     841           0 : ArrayBufferObject::inlineDataPointer() const
     842             : {
     843           0 :     return static_cast<uint8_t*>(fixedData(JSCLASS_RESERVED_SLOTS(&class_)));
     844             : }
     845             : 
     846             : uint8_t*
     847           0 : ArrayBufferObject::dataPointer() const
     848             : {
     849           0 :     return static_cast<uint8_t*>(getSlot(DATA_SLOT).toPrivate());
     850             : }
     851             : 
     852             : SharedMem<uint8_t*>
     853           0 : ArrayBufferObject::dataPointerShared() const
     854             : {
     855           0 :     return SharedMem<uint8_t*>::unshared(getSlot(DATA_SLOT).toPrivate());
     856             : }
     857             : 
     858             : void
     859           0 : ArrayBufferObject::releaseData(FreeOp* fop)
     860             : {
     861           0 :     MOZ_ASSERT(ownsData());
     862             : 
     863           0 :     switch (bufferKind()) {
     864             :       case PLAIN:
     865           0 :         fop->free_(dataPointer());
     866           0 :         break;
     867             :       case MAPPED:
     868           0 :         MemProfiler::RemoveNative(dataPointer());
     869           0 :         DeallocateMappedContent(dataPointer(), byteLength());
     870           0 :         break;
     871             :       case WASM:
     872           0 :         WasmArrayRawBuffer::Release(dataPointer());
     873           0 :         break;
     874             :       case KIND_MASK:
     875           0 :         MOZ_CRASH("bad bufferKind()");
     876             :     }
     877           0 : }
     878             : 
     879             : void
     880           0 : ArrayBufferObject::setDataPointer(BufferContents contents, OwnsState ownsData)
     881             : {
     882           0 :     setSlot(DATA_SLOT, PrivateValue(contents.data()));
     883           0 :     setOwnsData(ownsData);
     884           0 :     setFlags((flags() & ~KIND_MASK) | contents.kind());
     885           0 : }
     886             : 
     887             : uint32_t
     888           0 : ArrayBufferObject::byteLength() const
     889             : {
     890           0 :     return getSlot(BYTE_LENGTH_SLOT).toInt32();
     891             : }
     892             : 
     893             : void
     894           0 : ArrayBufferObject::setByteLength(uint32_t length)
     895             : {
     896           0 :     MOZ_ASSERT(length <= INT32_MAX);
     897           0 :     setSlot(BYTE_LENGTH_SLOT, Int32Value(length));
     898           0 : }
     899             : 
     900             : size_t
     901           0 : ArrayBufferObject::wasmMappedSize() const
     902             : {
     903           0 :     if (isWasm())
     904           0 :         return contents().wasmBuffer()->mappedSize();
     905           0 :     return byteLength();
     906             : }
     907             : 
     908             : size_t
     909           0 : js::WasmArrayBufferMappedSize(const ArrayBufferObjectMaybeShared* buf)
     910             : {
     911           0 :     if (buf->is<ArrayBufferObject>())
     912           0 :         return buf->as<ArrayBufferObject>().wasmMappedSize();
     913             : #ifdef WASM_HUGE_MEMORY
     914           0 :     return wasm::HugeMappedSize;
     915             : #else
     916             :     return buf->as<SharedArrayBufferObject>().byteLength();
     917             : #endif
     918             : }
     919             : 
     920             : Maybe<uint32_t>
     921           0 : ArrayBufferObject::wasmMaxSize() const
     922             : {
     923           0 :     if (isWasm())
     924           0 :         return contents().wasmBuffer()->maxSize();
     925             :     else
     926           0 :         return Some<uint32_t>(byteLength());
     927             : }
     928             : 
     929             : Maybe<uint32_t>
     930           0 : js::WasmArrayBufferMaxSize(const ArrayBufferObjectMaybeShared* buf)
     931             : {
     932           0 :     if (buf->is<ArrayBufferObject>())
     933           0 :         return buf->as<ArrayBufferObject>().wasmMaxSize();
     934             : 
     935           0 :     return Some(buf->as<SharedArrayBufferObject>().byteLength());
     936             : }
     937             : 
     938             : /* static */ bool
     939           0 : ArrayBufferObject::wasmGrowToSizeInPlace(uint32_t newSize,
     940             :                                          HandleArrayBufferObject oldBuf,
     941             :                                          MutableHandleArrayBufferObject newBuf,
     942             :                                          JSContext* cx)
     943             : {
     944             :     // On failure, do not throw and ensure that the original buffer is
     945             :     // unmodified and valid. After WasmArrayRawBuffer::growToSizeInPlace(), the
     946             :     // wasm-visible length of the buffer has been increased so it must be the
     947             :     // last fallible operation.
     948             : 
     949             :     // byteLength can be at most INT32_MAX. Note: if this hard limit changes,
     950             :     // update the clamping behavior in wasm::DecodeMemoryLimits and remove this
     951             :     // comment as well as the one in wasmMovingGrowToSize.
     952           0 :     if (newSize > INT32_MAX)
     953           0 :         return false;
     954             : 
     955           0 :     newBuf.set(ArrayBufferObject::createEmpty(cx));
     956           0 :     if (!newBuf) {
     957           0 :         cx->clearPendingException();
     958           0 :         return false;
     959             :     }
     960             : 
     961           0 :     if (!oldBuf->contents().wasmBuffer()->growToSizeInPlace(oldBuf->byteLength(), newSize))
     962           0 :         return false;
     963             : 
     964           0 :     bool hasStealableContents = true;
     965           0 :     BufferContents contents = ArrayBufferObject::stealContents(cx, oldBuf, hasStealableContents);
     966           0 :     MOZ_ASSERT(contents);
     967           0 :     newBuf->initialize(newSize, contents, OwnsData);
     968           0 :     return true;
     969             : }
     970             : 
     971             : #ifndef WASM_HUGE_MEMORY
     972             : /* static */ bool
     973             : ArrayBufferObject::wasmMovingGrowToSize(uint32_t newSize,
     974             :                                         HandleArrayBufferObject oldBuf,
     975             :                                         MutableHandleArrayBufferObject newBuf,
     976             :                                         JSContext* cx)
     977             : {
     978             :     // On failure, do not throw and ensure that the original buffer is
     979             :     // unmodified and valid.
     980             : 
     981             :     // byteLength can be at most INT32_MAX.
     982             :     // See comment in wasmGrowToSizeInPlace about wasm::DecodeMemoryLimits.
     983             :     if (newSize > INT32_MAX)
     984             :         return false;
     985             : 
     986             :     if (newSize <= oldBuf->wasmBoundsCheckLimit() ||
     987             :         oldBuf->contents().wasmBuffer()->extendMappedSize(newSize))
     988             :     {
     989             :         return wasmGrowToSizeInPlace(newSize, oldBuf, newBuf, cx);
     990             :     }
     991             : 
     992             :     newBuf.set(ArrayBufferObject::createEmpty(cx));
     993             :     if (!newBuf) {
     994             :         cx->clearPendingException();
     995             :         return false;
     996             :     }
     997             : 
     998             :     WasmArrayRawBuffer* newRawBuf = WasmArrayRawBuffer::Allocate(newSize, Nothing());
     999             :     if (!newRawBuf)
    1000             :         return false;
    1001             :     BufferContents contents = BufferContents::create<WASM>(newRawBuf->dataPointer());
    1002             :     newBuf->initialize(newSize, contents, OwnsData);
    1003             : 
    1004             :     memcpy(newBuf->dataPointer(), oldBuf->dataPointer(), oldBuf->byteLength());
    1005             :     ArrayBufferObject::detach(cx, oldBuf, BufferContents::createPlain(nullptr));
    1006             :     return true;
    1007             : }
    1008             : 
    1009             : uint32_t
    1010             : ArrayBufferObject::wasmBoundsCheckLimit() const
    1011             : {
    1012             :     if (isWasm())
    1013             :         return contents().wasmBuffer()->boundsCheckLimit();
    1014             :     else
    1015             :         return byteLength();
    1016             : }
    1017             : 
    1018             : uint32_t
    1019             : ArrayBufferObjectMaybeShared::wasmBoundsCheckLimit() const
    1020             : {
    1021             :     if (is<ArrayBufferObject>())
    1022             :         return as<ArrayBufferObject>().wasmBoundsCheckLimit();
    1023             : 
    1024             :     return as<SharedArrayBufferObject>().byteLength();
    1025             : }
    1026             : #endif
    1027             : 
    1028             : uint32_t
    1029           0 : ArrayBufferObject::flags() const
    1030             : {
    1031           0 :     return uint32_t(getSlot(FLAGS_SLOT).toInt32());
    1032             : }
    1033             : 
    1034             : void
    1035           0 : ArrayBufferObject::setFlags(uint32_t flags)
    1036             : {
    1037           0 :     setSlot(FLAGS_SLOT, Int32Value(flags));
    1038           0 : }
    1039             : 
    1040             : ArrayBufferObject*
    1041           0 : ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents,
    1042             :                           OwnsState ownsState /* = OwnsData */,
    1043             :                           HandleObject proto /* = nullptr */,
    1044             :                           NewObjectKind newKind /* = GenericObject */)
    1045             : {
    1046           0 :     MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
    1047             : 
    1048             :     // 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
    1049             :     // Refuse to allocate too large buffers, currently limited to ~2 GiB.
    1050           0 :     if (nbytes > INT32_MAX) {
    1051           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
    1052           0 :         return nullptr;
    1053             :     }
    1054             : 
    1055             :     // If we need to allocate data, try to use a larger object size class so
    1056             :     // that the array buffer's data can be allocated inline with the object.
    1057             :     // The extra space will be left unused by the object's fixed slots and
    1058             :     // available for the buffer's data, see NewObject().
    1059           0 :     size_t reservedSlots = JSCLASS_RESERVED_SLOTS(&class_);
    1060             : 
    1061           0 :     size_t nslots = reservedSlots;
    1062           0 :     bool allocated = false;
    1063           0 :     if (contents) {
    1064           0 :         if (ownsState == OwnsData) {
    1065             :             // The ABO is taking ownership, so account the bytes against the zone.
    1066           0 :             size_t nAllocated = nbytes;
    1067           0 :             if (contents.kind() == MAPPED)
    1068           0 :                 nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
    1069           0 :             else if (contents.kind() == WASM)
    1070           0 :                 nAllocated = contents.wasmBuffer()->allocatedBytes();
    1071           0 :             cx->zone()->updateMallocCounter(nAllocated);
    1072             :         }
    1073             :     } else {
    1074           0 :         MOZ_ASSERT(ownsState == OwnsData);
    1075           0 :         size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
    1076           0 :         if (nbytes <= usableSlots * sizeof(Value)) {
    1077           0 :             int newSlots = JS_HOWMANY(nbytes, sizeof(Value));
    1078           0 :             MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
    1079           0 :             nslots = reservedSlots + newSlots;
    1080           0 :             contents = BufferContents::createPlain(nullptr);
    1081             :         } else {
    1082           0 :             contents = AllocateArrayBufferContents(cx, nbytes);
    1083           0 :             if (!contents)
    1084           0 :                 return nullptr;
    1085           0 :             allocated = true;
    1086             :         }
    1087             :     }
    1088             : 
    1089           0 :     MOZ_ASSERT(!(class_.flags & JSCLASS_HAS_PRIVATE));
    1090           0 :     gc::AllocKind allocKind = GetGCObjectKind(nslots);
    1091             : 
    1092           0 :     AutoSetNewObjectMetadata metadata(cx);
    1093             :     Rooted<ArrayBufferObject*> obj(cx,
    1094           0 :         NewObjectWithClassProto<ArrayBufferObject>(cx, proto, allocKind, newKind));
    1095           0 :     if (!obj) {
    1096           0 :         if (allocated)
    1097           0 :             js_free(contents.data());
    1098           0 :         return nullptr;
    1099             :     }
    1100             : 
    1101           0 :     MOZ_ASSERT(obj->getClass() == &class_);
    1102           0 :     MOZ_ASSERT(!gc::IsInsideNursery(obj));
    1103             : 
    1104           0 :     if (!contents) {
    1105           0 :         void* data = obj->inlineDataPointer();
    1106           0 :         memset(data, 0, nbytes);
    1107           0 :         obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
    1108             :     } else {
    1109           0 :         obj->initialize(nbytes, contents, ownsState);
    1110             :     }
    1111             : 
    1112           0 :     return obj;
    1113             : }
    1114             : 
    1115             : ArrayBufferObject*
    1116           0 : ArrayBufferObject::create(JSContext* cx, uint32_t nbytes,
    1117             :                           HandleObject proto /* = nullptr */,
    1118             :                           NewObjectKind newKind /* = GenericObject */)
    1119             : {
    1120           0 :     return create(cx, nbytes, BufferContents::createPlain(nullptr),
    1121           0 :                   OwnsState::OwnsData, proto);
    1122             : }
    1123             : 
    1124             : ArrayBufferObject*
    1125           0 : ArrayBufferObject::createEmpty(JSContext* cx)
    1126             : {
    1127           0 :     AutoSetNewObjectMetadata metadata(cx);
    1128           0 :     ArrayBufferObject* obj = NewObjectWithClassProto<ArrayBufferObject>(cx, nullptr);
    1129           0 :     if (!obj)
    1130           0 :         return nullptr;
    1131             : 
    1132           0 :     obj->initEmpty();
    1133           0 :     return obj;
    1134             : }
    1135             : 
    1136             : /* static */ ArrayBufferObject::BufferContents
    1137           0 : ArrayBufferObject::externalizeContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
    1138             :                                        bool hasStealableContents)
    1139             : {
    1140           0 :     MOZ_ASSERT(buffer->isPlain(), "Only support doing this on plain ABOs");
    1141           0 :     MOZ_ASSERT(!buffer->isDetached(), "must have contents to externalize");
    1142           0 :     MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents());
    1143             : 
    1144           0 :     BufferContents contents(buffer->dataPointer(), buffer->bufferKind());
    1145             : 
    1146           0 :     if (hasStealableContents) {
    1147           0 :         buffer->setOwnsData(DoesntOwnData);
    1148           0 :         return contents;
    1149             :     }
    1150             : 
    1151             :     // Create a new chunk of memory to return since we cannot steal the
    1152             :     // existing contents away from the buffer.
    1153           0 :     BufferContents newContents = AllocateArrayBufferContents(cx, buffer->byteLength());
    1154           0 :     if (!newContents)
    1155           0 :         return BufferContents::createPlain(nullptr);
    1156           0 :     memcpy(newContents.data(), contents.data(), buffer->byteLength());
    1157           0 :     buffer->changeContents(cx, newContents, DoesntOwnData);
    1158             : 
    1159           0 :     return newContents;
    1160             : }
    1161             : 
    1162             : /* static */ ArrayBufferObject::BufferContents
    1163           0 : ArrayBufferObject::stealContents(JSContext* cx, Handle<ArrayBufferObject*> buffer,
    1164             :                                  bool hasStealableContents)
    1165             : {
    1166             :     // While wasm buffers cannot generally be transferred by content, the
    1167             :     // stealContents() is used internally by the impl of memory growth.
    1168           0 :     MOZ_ASSERT_IF(hasStealableContents, buffer->hasStealableContents() ||
    1169             :                                         (buffer->isWasm() && !buffer->isPreparedForAsmJS()));
    1170           0 :     assertSameCompartment(cx, buffer);
    1171             : 
    1172           0 :     BufferContents oldContents(buffer->dataPointer(), buffer->bufferKind());
    1173             : 
    1174           0 :     if (hasStealableContents) {
    1175             :         // Return the old contents and reset the detached buffer's data
    1176             :         // pointer. This pointer should never be accessed.
    1177           0 :         auto newContents = BufferContents::createPlain(nullptr);
    1178           0 :         buffer->setOwnsData(DoesntOwnData); // Do not free the stolen data.
    1179           0 :         ArrayBufferObject::detach(cx, buffer, newContents);
    1180           0 :         buffer->setOwnsData(DoesntOwnData); // Do not free the nullptr.
    1181           0 :         return oldContents;
    1182             :     }
    1183             : 
    1184             :     // Create a new chunk of memory to return since we cannot steal the
    1185             :     // existing contents away from the buffer.
    1186           0 :     BufferContents contentsCopy = AllocateArrayBufferContents(cx, buffer->byteLength());
    1187           0 :     if (!contentsCopy)
    1188           0 :         return BufferContents::createPlain(nullptr);
    1189             : 
    1190           0 :     if (buffer->byteLength() > 0)
    1191           0 :         memcpy(contentsCopy.data(), oldContents.data(), buffer->byteLength());
    1192           0 :     ArrayBufferObject::detach(cx, buffer, oldContents);
    1193           0 :     return contentsCopy;
    1194             : }
    1195             : 
    1196             : /* static */ void
    1197           0 : ArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
    1198             :                                           JS::ClassInfo* info)
    1199             : {
    1200           0 :     ArrayBufferObject& buffer = AsArrayBuffer(obj);
    1201             : 
    1202           0 :     if (!buffer.ownsData())
    1203           0 :         return;
    1204             : 
    1205           0 :     switch (buffer.bufferKind()) {
    1206             :       case PLAIN:
    1207           0 :         if (buffer.isPreparedForAsmJS())
    1208           0 :             info->objectsMallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
    1209             :         else
    1210           0 :             info->objectsMallocHeapElementsNormal += mallocSizeOf(buffer.dataPointer());
    1211           0 :         break;
    1212             :       case MAPPED:
    1213           0 :         info->objectsNonHeapElementsNormal += buffer.byteLength();
    1214           0 :         break;
    1215             :       case WASM:
    1216           0 :         info->objectsNonHeapElementsWasm += buffer.byteLength();
    1217           0 :         MOZ_ASSERT(buffer.wasmMappedSize() >= buffer.byteLength());
    1218           0 :         info->wasmGuardPages += buffer.wasmMappedSize() - buffer.byteLength();
    1219           0 :         break;
    1220             :       case KIND_MASK:
    1221           0 :         MOZ_CRASH("bad bufferKind()");
    1222             :     }
    1223             : }
    1224             : 
    1225             : /* static */ void
    1226           0 : ArrayBufferObject::finalize(FreeOp* fop, JSObject* obj)
    1227             : {
    1228           0 :     ArrayBufferObject& buffer = obj->as<ArrayBufferObject>();
    1229             : 
    1230           0 :     if (buffer.ownsData())
    1231           0 :         buffer.releaseData(fop);
    1232           0 : }
    1233             : 
    1234             : /* static */ void
    1235           0 : ArrayBufferObject::copyData(Handle<ArrayBufferObject*> toBuffer, uint32_t toIndex,
    1236             :                             Handle<ArrayBufferObject*> fromBuffer, uint32_t fromIndex,
    1237             :                             uint32_t count)
    1238             : {
    1239           0 :     MOZ_ASSERT(toBuffer->byteLength() >= count);
    1240           0 :     MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
    1241           0 :     MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
    1242           0 :     MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
    1243             : 
    1244           0 :     memcpy(toBuffer->dataPointer() + toIndex, fromBuffer->dataPointer() + fromIndex, count);
    1245           0 : }
    1246             : 
    1247             : /* static */ void
    1248           0 : ArrayBufferObject::trace(JSTracer* trc, JSObject* obj)
    1249             : {
    1250             :     // If this buffer is associated with an inline typed object,
    1251             :     // fix up the data pointer if the typed object was moved.
    1252           0 :     ArrayBufferObject& buf = obj->as<ArrayBufferObject>();
    1253             : 
    1254           0 :     if (!buf.forInlineTypedObject())
    1255           0 :         return;
    1256             : 
    1257           0 :     JSObject* view = MaybeForwarded(buf.firstView());
    1258           0 :     MOZ_ASSERT(view && view->is<InlineTransparentTypedObject>());
    1259             : 
    1260           0 :     TraceManuallyBarrieredEdge(trc, &view, "array buffer inline typed object owner");
    1261           0 :     buf.setSlot(DATA_SLOT, PrivateValue(view->as<InlineTransparentTypedObject>().inlineTypedMem()));
    1262             : }
    1263             : 
    1264             : /* static */ void
    1265           0 : ArrayBufferObject::objectMoved(JSObject* obj, const JSObject* old)
    1266             : {
    1267           0 :     ArrayBufferObject& dst = obj->as<ArrayBufferObject>();
    1268           0 :     const ArrayBufferObject& src = old->as<ArrayBufferObject>();
    1269             : 
    1270             :     // Fix up possible inline data pointer.
    1271           0 :     if (src.hasInlineData())
    1272           0 :         dst.setSlot(DATA_SLOT, PrivateValue(dst.inlineDataPointer()));
    1273           0 : }
    1274             : 
    1275             : ArrayBufferViewObject*
    1276           0 : ArrayBufferObject::firstView()
    1277             : {
    1278           0 :     return getSlot(FIRST_VIEW_SLOT).isObject()
    1279           0 :         ? static_cast<ArrayBufferViewObject*>(&getSlot(FIRST_VIEW_SLOT).toObject())
    1280           0 :         : nullptr;
    1281             : }
    1282             : 
    1283             : void
    1284           0 : ArrayBufferObject::setFirstView(ArrayBufferViewObject* view)
    1285             : {
    1286           0 :     setSlot(FIRST_VIEW_SLOT, ObjectOrNullValue(view));
    1287           0 : }
    1288             : 
    1289             : bool
    1290           0 : ArrayBufferObject::addView(JSContext* cx, JSObject* viewArg)
    1291             : {
    1292             :     // Note: we don't pass in an ArrayBufferViewObject as the argument due to
    1293             :     // tricky inheritance in the various view classes. View classes do not
    1294             :     // inherit from ArrayBufferViewObject so won't be upcast automatically.
    1295           0 :     MOZ_ASSERT(viewArg->is<ArrayBufferViewObject>() || viewArg->is<TypedObject>());
    1296           0 :     ArrayBufferViewObject* view = static_cast<ArrayBufferViewObject*>(viewArg);
    1297             : 
    1298           0 :     if (!firstView()) {
    1299           0 :         setFirstView(view);
    1300           0 :         return true;
    1301             :     }
    1302           0 :     return cx->compartment()->innerViews.get().addView(cx, this, view);
    1303             : }
    1304             : 
    1305             : /*
    1306             :  * InnerViewTable
    1307             :  */
    1308             : 
    1309             : static size_t VIEW_LIST_MAX_LENGTH = 500;
    1310             : 
    1311             : bool
    1312           0 : InnerViewTable::addView(JSContext* cx, ArrayBufferObject* buffer, ArrayBufferViewObject* view)
    1313             : {
    1314             :     // ArrayBufferObject entries are only added when there are multiple views.
    1315           0 :     MOZ_ASSERT(buffer->firstView());
    1316             : 
    1317           0 :     if (!map.initialized() && !map.init()) {
    1318           0 :         ReportOutOfMemory(cx);
    1319           0 :         return false;
    1320             :     }
    1321             : 
    1322           0 :     Map::AddPtr p = map.lookupForAdd(buffer);
    1323             : 
    1324           0 :     MOZ_ASSERT(!gc::IsInsideNursery(buffer));
    1325           0 :     bool addToNursery = nurseryKeysValid && gc::IsInsideNursery(view);
    1326             : 
    1327           0 :     if (p) {
    1328           0 :         ViewVector& views = p->value();
    1329           0 :         MOZ_ASSERT(!views.empty());
    1330             : 
    1331           0 :         if (addToNursery) {
    1332             :             // Only add the entry to |nurseryKeys| if it isn't already there.
    1333           0 :             if (views.length() >= VIEW_LIST_MAX_LENGTH) {
    1334             :                 // To avoid quadratic blowup, skip the loop below if we end up
    1335             :                 // adding enormous numbers of views for the same object.
    1336           0 :                 nurseryKeysValid = false;
    1337             :             } else {
    1338           0 :                 for (size_t i = 0; i < views.length(); i++) {
    1339           0 :                     if (gc::IsInsideNursery(views[i])) {
    1340           0 :                         addToNursery = false;
    1341           0 :                         break;
    1342             :                     }
    1343             :                 }
    1344             :             }
    1345             :         }
    1346             : 
    1347           0 :         if (!views.append(view)) {
    1348           0 :             ReportOutOfMemory(cx);
    1349           0 :             return false;
    1350             :         }
    1351             :     } else {
    1352           0 :         if (!map.add(p, buffer, ViewVector())) {
    1353           0 :             ReportOutOfMemory(cx);
    1354           0 :             return false;
    1355             :         }
    1356             :         // ViewVector has one inline element, so the first insertion is
    1357             :         // guaranteed to succeed.
    1358           0 :         MOZ_ALWAYS_TRUE(p->value().append(view));
    1359             :     }
    1360             : 
    1361           0 :     if (addToNursery && !nurseryKeys.append(buffer))
    1362           0 :         nurseryKeysValid = false;
    1363             : 
    1364           0 :     return true;
    1365             : }
    1366             : 
    1367             : InnerViewTable::ViewVector*
    1368           0 : InnerViewTable::maybeViewsUnbarriered(ArrayBufferObject* buffer)
    1369             : {
    1370           0 :     if (!map.initialized())
    1371           0 :         return nullptr;
    1372             : 
    1373           0 :     Map::Ptr p = map.lookup(buffer);
    1374           0 :     if (p)
    1375           0 :         return &p->value();
    1376           0 :     return nullptr;
    1377             : }
    1378             : 
    1379             : void
    1380           0 : InnerViewTable::removeViews(ArrayBufferObject* buffer)
    1381             : {
    1382           0 :     Map::Ptr p = map.lookup(buffer);
    1383           0 :     MOZ_ASSERT(p);
    1384             : 
    1385           0 :     map.remove(p);
    1386           0 : }
    1387             : 
    1388             : /* static */ bool
    1389           0 : InnerViewTable::sweepEntry(JSObject** pkey, ViewVector& views)
    1390             : {
    1391           0 :     if (IsAboutToBeFinalizedUnbarriered(pkey))
    1392           0 :         return true;
    1393             : 
    1394           0 :     MOZ_ASSERT(!views.empty());
    1395           0 :     for (size_t i = 0; i < views.length(); i++) {
    1396           0 :         if (IsAboutToBeFinalizedUnbarriered(&views[i])) {
    1397           0 :             views[i--] = views.back();
    1398           0 :             views.popBack();
    1399             :         }
    1400             :     }
    1401             : 
    1402           0 :     return views.empty();
    1403             : }
    1404             : 
    1405             : void
    1406           0 : InnerViewTable::sweep()
    1407             : {
    1408           0 :     MOZ_ASSERT(nurseryKeys.empty());
    1409           0 :     map.sweep();
    1410           0 : }
    1411             : 
    1412             : void
    1413           0 : InnerViewTable::sweepAfterMinorGC()
    1414             : {
    1415           0 :     MOZ_ASSERT(needsSweepAfterMinorGC());
    1416             : 
    1417           0 :     if (nurseryKeysValid) {
    1418           0 :         for (size_t i = 0; i < nurseryKeys.length(); i++) {
    1419           0 :             JSObject* buffer = MaybeForwarded(nurseryKeys[i]);
    1420           0 :             Map::Ptr p = map.lookup(buffer);
    1421           0 :             if (!p)
    1422           0 :                 continue;
    1423             : 
    1424           0 :             if (sweepEntry(&p->mutableKey(), p->value()))
    1425           0 :                 map.remove(buffer);
    1426             :         }
    1427           0 :         nurseryKeys.clear();
    1428             :     } else {
    1429             :         // Do the required sweeping by looking at every map entry.
    1430           0 :         nurseryKeys.clear();
    1431           0 :         sweep();
    1432             : 
    1433           0 :         nurseryKeysValid = true;
    1434             :     }
    1435           0 : }
    1436             : 
    1437             : size_t
    1438           0 : InnerViewTable::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
    1439             : {
    1440           0 :     if (!map.initialized())
    1441           0 :         return 0;
    1442             : 
    1443           0 :     size_t vectorSize = 0;
    1444           0 :     for (Map::Enum e(map); !e.empty(); e.popFront())
    1445           0 :         vectorSize += e.front().value().sizeOfExcludingThis(mallocSizeOf);
    1446             : 
    1447             :     return vectorSize
    1448           0 :          + map.sizeOfExcludingThis(mallocSizeOf)
    1449           0 :          + nurseryKeys.sizeOfExcludingThis(mallocSizeOf);
    1450             : }
    1451             : 
    1452             : /*
    1453             :  * ArrayBufferViewObject
    1454             :  */
    1455             : 
    1456             : /*
    1457             :  * This method is used to trace TypedArrayObjects and DataViewObjects. We need
    1458             :  * a custom tracer to move the object's data pointer if its owner was moved and
    1459             :  * stores its data inline.
    1460             :  */
    1461             : /* static */ void
    1462           0 : ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
    1463             : {
    1464           0 :     NativeObject* obj = &objArg->as<NativeObject>();
    1465           0 :     HeapSlot& bufSlot = obj->getFixedSlotRef(TypedArrayObject::BUFFER_SLOT);
    1466           0 :     TraceEdge(trc, &bufSlot, "typedarray.buffer");
    1467             : 
    1468             :     // Update obj's data pointer if it moved.
    1469           0 :     if (bufSlot.isObject()) {
    1470           0 :         if (IsArrayBuffer(&bufSlot.toObject())) {
    1471           0 :             ArrayBufferObject& buf = AsArrayBuffer(MaybeForwarded(&bufSlot.toObject()));
    1472           0 :             uint32_t offset = uint32_t(obj->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT).toInt32());
    1473           0 :             MOZ_ASSERT(offset <= INT32_MAX);
    1474             : 
    1475           0 :             if (buf.forInlineTypedObject()) {
    1476           0 :                 MOZ_ASSERT(buf.dataPointer() != nullptr);
    1477             : 
    1478             :                 // The data is inline with an InlineTypedObject associated with the
    1479             :                 // buffer. Get a new address for the typed object if it moved.
    1480           0 :                 JSObject* view = buf.firstView();
    1481             : 
    1482             :                 // Mark the object to move it into the tenured space.
    1483           0 :                 TraceManuallyBarrieredEdge(trc, &view, "typed array nursery owner");
    1484           0 :                 MOZ_ASSERT(view->is<InlineTypedObject>());
    1485           0 :                 MOZ_ASSERT(view != obj);
    1486             : 
    1487           0 :                 void* srcData = obj->getPrivate();
    1488           0 :                 void* dstData = view->as<InlineTypedObject>().inlineTypedMemForGC() + offset;
    1489           0 :                 obj->setPrivateUnbarriered(dstData);
    1490             : 
    1491             :                 // We can't use a direct forwarding pointer here, as there might
    1492             :                 // not be enough bytes available, and other views might have data
    1493             :                 // pointers whose forwarding pointers would overlap this one.
    1494           0 :                 if (trc->isTenuringTracer()) {
    1495           0 :                     Nursery& nursery = obj->zoneFromAnyThread()->group()->nursery();
    1496           0 :                     nursery.maybeSetForwardingPointer(trc, srcData, dstData, /* direct = */ false);
    1497             :                 }
    1498             :             } else {
    1499           0 :                 MOZ_ASSERT_IF(buf.dataPointer() == nullptr, offset == 0);
    1500             : 
    1501             :                 // The data may or may not be inline with the buffer. The buffer
    1502             :                 // can only move during a compacting GC, in which case its
    1503             :                 // objectMoved hook has already updated the buffer's data pointer.
    1504           0 :                 obj->initPrivate(buf.dataPointer() + offset);
    1505             :             }
    1506             :         }
    1507             :     }
    1508           0 : }
    1509             : 
    1510             : template <>
    1511             : bool
    1512           0 : JSObject::is<js::ArrayBufferViewObject>() const
    1513             : {
    1514           0 :     return is<DataViewObject>() || is<TypedArrayObject>();
    1515             : }
    1516             : 
    1517             : template <>
    1518             : bool
    1519           0 : JSObject::is<js::ArrayBufferObjectMaybeShared>() const
    1520             : {
    1521           0 :     return is<ArrayBufferObject>() || is<SharedArrayBufferObject>();
    1522             : }
    1523             : 
    1524             : void
    1525           0 : ArrayBufferViewObject::notifyBufferDetached(JSContext* cx, void* newData)
    1526             : {
    1527           0 :     if (is<DataViewObject>()) {
    1528           0 :         if (as<DataViewObject>().isSharedMemory())
    1529           0 :             return;
    1530           0 :         as<DataViewObject>().notifyBufferDetached(newData);
    1531           0 :     } else if (is<TypedArrayObject>()) {
    1532           0 :         if (as<TypedArrayObject>().isSharedMemory())
    1533           0 :             return;
    1534           0 :         as<TypedArrayObject>().notifyBufferDetached(cx, newData);
    1535             :     } else {
    1536           0 :         as<OutlineTypedObject>().notifyBufferDetached(newData);
    1537             :     }
    1538             : }
    1539             : 
    1540             : uint8_t*
    1541           0 : ArrayBufferViewObject::dataPointerUnshared(const JS::AutoRequireNoGC& nogc)
    1542             : {
    1543           0 :     if (is<DataViewObject>()) {
    1544           0 :         MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());
    1545           0 :         return static_cast<uint8_t*>(as<DataViewObject>().dataPointerUnshared());
    1546             :     }
    1547           0 :     if (is<TypedArrayObject>()) {
    1548           0 :         MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
    1549           0 :         return static_cast<uint8_t*>(as<TypedArrayObject>().viewDataUnshared());
    1550             :     }
    1551           0 :     return as<TypedObject>().typedMem(nogc);
    1552             : }
    1553             : 
    1554             : #ifdef DEBUG
    1555             : bool
    1556           0 : ArrayBufferViewObject::isSharedMemory()
    1557             : {
    1558           0 :     if (is<TypedArrayObject>())
    1559           0 :         return as<TypedArrayObject>().isSharedMemory();
    1560           0 :     return false;
    1561             : }
    1562             : #endif
    1563             : 
    1564             : void
    1565           0 : ArrayBufferViewObject::setDataPointerUnshared(uint8_t* data)
    1566             : {
    1567           0 :     if (is<DataViewObject>()) {
    1568           0 :         MOZ_ASSERT(!as<DataViewObject>().isSharedMemory());
    1569           0 :         as<DataViewObject>().setPrivate(data);
    1570           0 :     } else if (is<TypedArrayObject>()) {
    1571           0 :         MOZ_ASSERT(!as<TypedArrayObject>().isSharedMemory());
    1572           0 :         as<TypedArrayObject>().setPrivate(data);
    1573           0 :     } else if (is<OutlineTypedObject>()) {
    1574           0 :         as<OutlineTypedObject>().setData(data);
    1575             :     } else {
    1576           0 :         MOZ_CRASH();
    1577             :     }
    1578           0 : }
    1579             : 
    1580             : /* static */ ArrayBufferObjectMaybeShared*
    1581           0 : ArrayBufferViewObject::bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*> thisObject)
    1582             : {
    1583           0 :     if (thisObject->is<TypedArrayObject>()) {
    1584           0 :         Rooted<TypedArrayObject*> typedArray(cx, &thisObject->as<TypedArrayObject>());
    1585           0 :         if (!TypedArrayObject::ensureHasBuffer(cx, typedArray))
    1586           0 :             return nullptr;
    1587           0 :         return thisObject->as<TypedArrayObject>().bufferEither();
    1588             :     }
    1589           0 :     MOZ_ASSERT(thisObject->is<DataViewObject>());
    1590           0 :     return &thisObject->as<DataViewObject>().arrayBufferEither();
    1591             : }
    1592             : 
    1593             : /* JS Friend API */
    1594             : 
    1595             : JS_FRIEND_API(bool)
    1596           0 : JS_IsArrayBufferViewObject(JSObject* obj)
    1597             : {
    1598           0 :     obj = CheckedUnwrap(obj);
    1599           0 :     return obj && obj->is<ArrayBufferViewObject>();
    1600             : }
    1601             : 
    1602             : JS_FRIEND_API(JSObject*)
    1603           0 : js::UnwrapArrayBufferView(JSObject* obj)
    1604             : {
    1605           0 :     if (JSObject* unwrapped = CheckedUnwrap(obj))
    1606           0 :         return unwrapped->is<ArrayBufferViewObject>() ? unwrapped : nullptr;
    1607           0 :     return nullptr;
    1608             : }
    1609             : 
    1610             : JS_FRIEND_API(uint32_t)
    1611           0 : JS_GetArrayBufferByteLength(JSObject* obj)
    1612             : {
    1613           0 :     obj = CheckedUnwrap(obj);
    1614           0 :     return obj ? AsArrayBuffer(obj).byteLength() : 0;
    1615             : }
    1616             : 
    1617             : JS_FRIEND_API(uint8_t*)
    1618           0 : JS_GetArrayBufferData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
    1619             : {
    1620           0 :     obj = CheckedUnwrap(obj);
    1621           0 :     if (!obj)
    1622           0 :         return nullptr;
    1623           0 :     if (!IsArrayBuffer(obj))
    1624           0 :         return nullptr;
    1625           0 :     *isSharedMemory = false;
    1626           0 :     return AsArrayBuffer(obj).dataPointer();
    1627             : }
    1628             : 
    1629             : JS_FRIEND_API(bool)
    1630           0 : JS_DetachArrayBuffer(JSContext* cx, HandleObject obj)
    1631             : {
    1632           0 :     AssertHeapIsIdle();
    1633           0 :     CHECK_REQUEST(cx);
    1634           0 :     assertSameCompartment(cx, obj);
    1635             : 
    1636           0 :     if (!obj->is<ArrayBufferObject>()) {
    1637           0 :         JS_ReportErrorASCII(cx, "ArrayBuffer object required");
    1638           0 :         return false;
    1639             :     }
    1640             : 
    1641           0 :     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
    1642             : 
    1643           0 :     if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
    1644           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_TRANSFER);
    1645           0 :         return false;
    1646             :     }
    1647             : 
    1648             :     ArrayBufferObject::BufferContents newContents =
    1649           0 :         buffer->hasStealableContents() ? ArrayBufferObject::BufferContents::createPlain(nullptr)
    1650           0 :                                        : buffer->contents();
    1651             : 
    1652           0 :     ArrayBufferObject::detach(cx, buffer, newContents);
    1653             : 
    1654           0 :     return true;
    1655             : }
    1656             : 
    1657             : JS_FRIEND_API(bool)
    1658           0 : JS_IsDetachedArrayBufferObject(JSObject* obj)
    1659             : {
    1660           0 :     obj = CheckedUnwrap(obj);
    1661           0 :     if (!obj)
    1662           0 :         return false;
    1663             : 
    1664           0 :     return obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().isDetached();
    1665             : }
    1666             : 
    1667             : JS_FRIEND_API(JSObject*)
    1668           0 : JS_NewArrayBuffer(JSContext* cx, uint32_t nbytes)
    1669             : {
    1670           0 :     AssertHeapIsIdle();
    1671           0 :     CHECK_REQUEST(cx);
    1672           0 :     MOZ_ASSERT(nbytes <= INT32_MAX);
    1673           0 :     return ArrayBufferObject::create(cx, nbytes);
    1674             : }
    1675             : 
    1676             : JS_PUBLIC_API(JSObject*)
    1677           0 : JS_NewArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data)
    1678             : {
    1679           0 :     AssertHeapIsIdle();
    1680           0 :     CHECK_REQUEST(cx);
    1681           0 :     MOZ_ASSERT_IF(!data, nbytes == 0);
    1682             :     ArrayBufferObject::BufferContents contents =
    1683           0 :         ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);
    1684           0 :     return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData,
    1685           0 :                                      /* proto = */ nullptr, TenuredObject);
    1686             : }
    1687             : 
    1688             : JS_PUBLIC_API(JSObject*)
    1689           0 : JS_NewArrayBufferWithExternalContents(JSContext* cx, size_t nbytes, void* data)
    1690             : {
    1691           0 :     AssertHeapIsIdle();
    1692           0 :     CHECK_REQUEST(cx);
    1693           0 :     MOZ_ASSERT_IF(!data, nbytes == 0);
    1694             :     ArrayBufferObject::BufferContents contents =
    1695           0 :         ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(data);
    1696           0 :     return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::DoesntOwnData,
    1697           0 :                                      /* proto = */ nullptr, TenuredObject);
    1698             : }
    1699             : 
    1700             : JS_FRIEND_API(bool)
    1701         138 : JS_IsArrayBufferObject(JSObject* obj)
    1702             : {
    1703         138 :     obj = CheckedUnwrap(obj);
    1704         138 :     return obj && obj->is<ArrayBufferObject>();
    1705             : }
    1706             : 
    1707             : JS_FRIEND_API(bool)
    1708           0 : JS_ArrayBufferHasData(JSObject* obj)
    1709             : {
    1710           0 :     return CheckedUnwrap(obj)->as<ArrayBufferObject>().hasData();
    1711             : }
    1712             : 
    1713             : JS_FRIEND_API(JSObject*)
    1714           0 : js::UnwrapArrayBuffer(JSObject* obj)
    1715             : {
    1716           0 :     if (JSObject* unwrapped = CheckedUnwrap(obj))
    1717           0 :         return unwrapped->is<ArrayBufferObject>() ? unwrapped : nullptr;
    1718           0 :     return nullptr;
    1719             : }
    1720             : 
    1721             : JS_FRIEND_API(JSObject*)
    1722           0 : js::UnwrapSharedArrayBuffer(JSObject* obj)
    1723             : {
    1724           0 :     if (JSObject* unwrapped = CheckedUnwrap(obj))
    1725           0 :         return unwrapped->is<SharedArrayBufferObject>() ? unwrapped : nullptr;
    1726           0 :     return nullptr;
    1727             : }
    1728             : 
    1729             : JS_PUBLIC_API(void*)
    1730           0 : JS_ExternalizeArrayBufferContents(JSContext* cx, HandleObject obj)
    1731             : {
    1732           0 :     AssertHeapIsIdle();
    1733           0 :     CHECK_REQUEST(cx);
    1734           0 :     assertSameCompartment(cx, obj);
    1735             : 
    1736           0 :     if (!obj->is<ArrayBufferObject>()) {
    1737           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
    1738           0 :         return nullptr;
    1739             :     }
    1740             : 
    1741           0 :     Handle<ArrayBufferObject*> buffer = obj.as<ArrayBufferObject>();
    1742           0 :     if (!buffer->isPlain()) {
    1743             :         // This operation isn't supported on mapped or wsm ArrayBufferObjects.
    1744           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
    1745           0 :         return nullptr;
    1746             :     }
    1747           0 :     if (buffer->isDetached()) {
    1748           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
    1749           0 :         return nullptr;
    1750             :     }
    1751             : 
    1752             :     // The caller assumes that a plain malloc'd buffer is returned.
    1753             :     // hasStealableContents is true for mapped buffers, so we must additionally
    1754             :     // require that the buffer is plain. In the future, we could consider
    1755             :     // returning something that handles releasing the memory.
    1756           0 :     bool hasStealableContents = buffer->hasStealableContents();
    1757             : 
    1758           0 :     return ArrayBufferObject::externalizeContents(cx, buffer, hasStealableContents).data();
    1759             : }
    1760             : 
    1761             : JS_PUBLIC_API(void*)
    1762           0 : JS_StealArrayBufferContents(JSContext* cx, HandleObject objArg)
    1763             : {
    1764           0 :     AssertHeapIsIdle();
    1765           0 :     CHECK_REQUEST(cx);
    1766           0 :     assertSameCompartment(cx, objArg);
    1767             : 
    1768           0 :     JSObject* obj = CheckedUnwrap(objArg);
    1769           0 :     if (!obj)
    1770           0 :         return nullptr;
    1771             : 
    1772           0 :     if (!obj->is<ArrayBufferObject>()) {
    1773           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
    1774           0 :         return nullptr;
    1775             :     }
    1776             : 
    1777           0 :     Rooted<ArrayBufferObject*> buffer(cx, &obj->as<ArrayBufferObject>());
    1778           0 :     if (buffer->isDetached()) {
    1779           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
    1780           0 :         return nullptr;
    1781             :     }
    1782             : 
    1783           0 :     if (buffer->isWasm() || buffer->isPreparedForAsmJS()) {
    1784           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_NO_TRANSFER);
    1785           0 :         return nullptr;
    1786             :     }
    1787             : 
    1788             :     // The caller assumes that a plain malloc'd buffer is returned.
    1789             :     // hasStealableContents is true for mapped buffers, so we must additionally
    1790             :     // require that the buffer is plain. In the future, we could consider
    1791             :     // returning something that handles releasing the memory.
    1792           0 :     bool hasStealableContents = buffer->hasStealableContents() && buffer->isPlain();
    1793             : 
    1794           0 :     AutoCompartment ac(cx, buffer);
    1795           0 :     return ArrayBufferObject::stealContents(cx, buffer, hasStealableContents).data();
    1796             : }
    1797             : 
    1798             : JS_PUBLIC_API(JSObject*)
    1799           0 : JS_NewMappedArrayBufferWithContents(JSContext* cx, size_t nbytes, void* data)
    1800             : {
    1801           0 :     AssertHeapIsIdle();
    1802           0 :     CHECK_REQUEST(cx);
    1803             : 
    1804           0 :     MOZ_ASSERT(data);
    1805             :     ArrayBufferObject::BufferContents contents =
    1806           0 :         ArrayBufferObject::BufferContents::create<ArrayBufferObject::MAPPED>(data);
    1807           0 :     return ArrayBufferObject::create(cx, nbytes, contents, ArrayBufferObject::OwnsData,
    1808           0 :                                      /* proto = */ nullptr, TenuredObject);
    1809             : }
    1810             : 
    1811             : JS_PUBLIC_API(void*)
    1812           0 : JS_CreateMappedArrayBufferContents(int fd, size_t offset, size_t length)
    1813             : {
    1814           0 :     return ArrayBufferObject::createMappedContents(fd, offset, length).data();
    1815             : }
    1816             : 
    1817             : JS_PUBLIC_API(void)
    1818           0 : JS_ReleaseMappedArrayBufferContents(void* contents, size_t length)
    1819             : {
    1820           0 :     MemProfiler::RemoveNative(contents);
    1821           0 :     DeallocateMappedContent(contents, length);
    1822           0 : }
    1823             : 
    1824             : JS_FRIEND_API(bool)
    1825           0 : JS_IsMappedArrayBufferObject(JSObject* obj)
    1826             : {
    1827           0 :     obj = CheckedUnwrap(obj);
    1828           0 :     if (!obj)
    1829           0 :         return false;
    1830             : 
    1831           0 :     return obj->is<ArrayBufferObject>() && obj->as<ArrayBufferObject>().isMapped();
    1832             : }
    1833             : 
    1834             : JS_FRIEND_API(void*)
    1835           0 : JS_GetArrayBufferViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
    1836             : {
    1837           0 :     obj = CheckedUnwrap(obj);
    1838           0 :     if (!obj)
    1839           0 :         return nullptr;
    1840           0 :     if (obj->is<DataViewObject>()) {
    1841           0 :         DataViewObject& dv = obj->as<DataViewObject>();
    1842           0 :         *isSharedMemory = dv.isSharedMemory();
    1843           0 :         return dv.dataPointerEither().unwrap(/*safe - caller sees isSharedMemory flag*/);
    1844             :     }
    1845           0 :     TypedArrayObject& ta = obj->as<TypedArrayObject>();
    1846           0 :     *isSharedMemory = ta.isSharedMemory();
    1847           0 :     return ta.viewDataEither().unwrap(/*safe - caller sees isSharedMemory flag*/);
    1848             : }
    1849             : 
    1850             : JS_FRIEND_API(JSObject*)
    1851           0 : JS_GetArrayBufferViewBuffer(JSContext* cx, HandleObject objArg, bool* isSharedMemory)
    1852             : {
    1853           0 :     AssertHeapIsIdle();
    1854           0 :     CHECK_REQUEST(cx);
    1855           0 :     assertSameCompartment(cx, objArg);
    1856             : 
    1857           0 :     JSObject* obj = CheckedUnwrap(objArg);
    1858           0 :     if (!obj)
    1859           0 :         return nullptr;
    1860           0 :     MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
    1861             : 
    1862           0 :     Rooted<ArrayBufferViewObject*> viewObject(cx, static_cast<ArrayBufferViewObject*>(obj));
    1863           0 :     ArrayBufferObjectMaybeShared* buffer = ArrayBufferViewObject::bufferObject(cx, viewObject);
    1864           0 :     *isSharedMemory = buffer->is<SharedArrayBufferObject>();
    1865           0 :     return buffer;
    1866             : }
    1867             : 
    1868             : JS_FRIEND_API(uint32_t)
    1869           0 : JS_GetArrayBufferViewByteLength(JSObject* obj)
    1870             : {
    1871           0 :     obj = CheckedUnwrap(obj);
    1872           0 :     if (!obj)
    1873           0 :         return 0;
    1874           0 :     return obj->is<DataViewObject>()
    1875           0 :            ? obj->as<DataViewObject>().byteLength()
    1876           0 :            : obj->as<TypedArrayObject>().byteLength();
    1877             : }
    1878             : 
    1879             : JS_FRIEND_API(JSObject*)
    1880           0 : JS_GetObjectAsArrayBufferView(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
    1881             : {
    1882           0 :     if (!(obj = CheckedUnwrap(obj)))
    1883           0 :         return nullptr;
    1884           0 :     if (!(obj->is<ArrayBufferViewObject>()))
    1885           0 :         return nullptr;
    1886             : 
    1887           0 :     js::GetArrayBufferViewLengthAndData(obj, length, isSharedMemory, data);
    1888           0 :     return obj;
    1889             : }
    1890             : 
    1891             : JS_FRIEND_API(void)
    1892           0 : js::GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory,
    1893             :                                     uint8_t** data)
    1894             : {
    1895           0 :     MOZ_ASSERT(obj->is<ArrayBufferViewObject>());
    1896             : 
    1897           0 :     *length = obj->is<DataViewObject>()
    1898           0 :               ? obj->as<DataViewObject>().byteLength()
    1899           0 :               : obj->as<TypedArrayObject>().byteLength();
    1900             : 
    1901           0 :     if (obj->is<DataViewObject>()) {
    1902           0 :         DataViewObject& dv = obj->as<DataViewObject>();
    1903           0 :         *isSharedMemory = dv.isSharedMemory();
    1904           0 :         *data = static_cast<uint8_t*>(
    1905           0 :             dv.dataPointerEither().unwrap(/*safe - caller sees isShared flag*/));
    1906             :     }
    1907             :     else {
    1908           0 :         TypedArrayObject& ta = obj->as<TypedArrayObject>();
    1909           0 :         *isSharedMemory = ta.isSharedMemory();
    1910           0 :         *data = static_cast<uint8_t*>(
    1911           0 :             ta.viewDataEither().unwrap(/*safe - caller sees isShared flag*/));
    1912             :     }
    1913           0 : }
    1914             : 
    1915             : JS_FRIEND_API(JSObject*)
    1916           0 : JS_GetObjectAsArrayBuffer(JSObject* obj, uint32_t* length, uint8_t** data)
    1917             : {
    1918           0 :     if (!(obj = CheckedUnwrap(obj)))
    1919           0 :         return nullptr;
    1920           0 :     if (!IsArrayBuffer(obj))
    1921           0 :         return nullptr;
    1922             : 
    1923           0 :     *length = AsArrayBuffer(obj).byteLength();
    1924           0 :     *data = AsArrayBuffer(obj).dataPointer();
    1925             : 
    1926           0 :     return obj;
    1927             : }
    1928             : 
    1929             : JS_FRIEND_API(void)
    1930           0 : js::GetArrayBufferLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
    1931             : {
    1932           0 :     MOZ_ASSERT(IsArrayBuffer(obj));
    1933           0 :     *length = AsArrayBuffer(obj).byteLength();
    1934           0 :     *data = AsArrayBuffer(obj).dataPointer();
    1935           0 :     *isSharedMemory = false;
    1936           0 : }

Generated by: LCOV version 1.13