LCOV - code coverage report
Current view: top level - js/src/vm - SharedArrayObject.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 6 180 3.3 %
Date: 2017-07-14 16:53:18 Functions: 2 30 6.7 %
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/SharedArrayObject.h"
       8             : 
       9             : #include "mozilla/Atomics.h"
      10             : 
      11             : #include "jsfriendapi.h"
      12             : #include "jsprf.h"
      13             : 
      14             : #ifdef XP_WIN
      15             : # include "jswin.h"
      16             : #endif
      17             : #include "jswrapper.h"
      18             : #ifndef XP_WIN
      19             : # include <sys/mman.h>
      20             : #endif
      21             : #ifdef MOZ_VALGRIND
      22             : # include <valgrind/memcheck.h>
      23             : #endif
      24             : 
      25             : #include "jit/AtomicOperations.h"
      26             : #include "vm/SharedMem.h"
      27             : #include "wasm/AsmJS.h"
      28             : #include "wasm/WasmTypes.h"
      29             : 
      30             : #include "jsobjinlines.h"
      31             : 
      32             : #include "vm/NativeObject-inl.h"
      33             : 
      34             : using namespace js;
      35             : 
      36             : static inline void*
      37           0 : MapMemory(size_t length, bool commit)
      38             : {
      39             : #ifdef XP_WIN
      40             :     int prot = (commit ? MEM_COMMIT : MEM_RESERVE);
      41             :     int flags = (commit ? PAGE_READWRITE : PAGE_NOACCESS);
      42             :     return VirtualAlloc(nullptr, length, prot, flags);
      43             : #else
      44           0 :     int prot = (commit ? (PROT_READ | PROT_WRITE) : PROT_NONE);
      45           0 :     void* p = mmap(nullptr, length, prot, MAP_PRIVATE | MAP_ANON, -1, 0);
      46           0 :     if (p == MAP_FAILED)
      47           0 :         return nullptr;
      48           0 :     return p;
      49             : #endif
      50             : }
      51             : 
      52             : static inline void
      53           0 : UnmapMemory(void* addr, size_t len)
      54             : {
      55             : #ifdef XP_WIN
      56             :     VirtualFree(addr, 0, MEM_RELEASE);
      57             : #else
      58           0 :     munmap(addr, len);
      59             : #endif
      60           0 : }
      61             : 
      62             : static inline bool
      63           0 : MarkValidRegion(void* addr, size_t len)
      64             : {
      65             : #ifdef XP_WIN
      66             :     if (!VirtualAlloc(addr, len, MEM_COMMIT, PAGE_READWRITE))
      67             :         return false;
      68             :     return true;
      69             : #else
      70           0 :     if (mprotect(addr, len, PROT_READ | PROT_WRITE))
      71           0 :         return false;
      72           0 :     return true;
      73             : #endif
      74             : }
      75             : 
      76             : // Since this SharedArrayBuffer will likely be used for asm.js code, prepare it
      77             : // for asm.js by mapping the 4gb protected zone described in WasmTypes.h.
      78             : // Since we want to put the SharedArrayBuffer header immediately before the
      79             : // heap but keep the heap page-aligned, allocate an extra page before the heap.
      80             : static uint64_t
      81           0 : SharedArrayMappedSize(uint32_t allocSize)
      82             : {
      83           0 :     MOZ_RELEASE_ASSERT(sizeof(SharedArrayRawBuffer) < gc::SystemPageSize());
      84             : #ifdef WASM_HUGE_MEMORY
      85           0 :     return wasm::HugeMappedSize + gc::SystemPageSize();
      86             : #else
      87             :     return allocSize + wasm::GuardSize;
      88             : #endif
      89             : }
      90             : 
      91             : // If there are too many 4GB buffers live we run up against system resource
      92             : // exhaustion (address space or number of memory map descriptors), see
      93             : // bug 1068684, bug 1073934 for details.  The limiting case seems to be
      94             : // Windows Vista Home 64-bit, where the per-process address space is limited
      95             : // to 8TB.  Thus we track the number of live objects, and set a limit of
      96             : // 1000 live objects per process; we run synchronous GC if necessary; and
      97             : // we throw an OOM error if the per-process limit is exceeded.
      98             : static mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> numLive;
      99             : static const uint32_t maxLive = 1000;
     100             : 
     101             : #ifdef DEBUG
     102             : static mozilla::Atomic<int32_t> liveBuffers_;
     103             : #endif
     104             : 
     105             : static uint32_t
     106           0 : SharedArrayAllocSize(uint32_t length)
     107             : {
     108           0 :     return AlignBytes(length + gc::SystemPageSize(), gc::SystemPageSize());
     109             : }
     110             : 
     111             : int32_t
     112           0 : SharedArrayRawBuffer::liveBuffers()
     113             : {
     114             : #ifdef DEBUG
     115           0 :     return liveBuffers_;
     116             : #else
     117             :     return 0;
     118             : #endif
     119             : }
     120             : 
     121             : SharedArrayRawBuffer*
     122           0 : SharedArrayRawBuffer::New(JSContext* cx, uint32_t length)
     123             : {
     124             :     // The value (uint32_t)-1 is used as a signal in various places,
     125             :     // so guard against it on principle.
     126           0 :     MOZ_ASSERT(length != (uint32_t)-1);
     127             : 
     128             :     // Add a page for the header and round to a page boundary.
     129           0 :     uint32_t allocSize = SharedArrayAllocSize(length);
     130           0 :     if (allocSize <= length)
     131           0 :         return nullptr;
     132             : 
     133           0 :     bool preparedForAsmJS = jit::JitOptions.asmJSAtomicsEnable && IsValidAsmJSHeapLength(length);
     134             : 
     135           0 :     void* p = nullptr;
     136           0 :     if (preparedForAsmJS) {
     137             :         // Test >= to guard against the case where multiple extant runtimes
     138             :         // race to allocate.
     139           0 :         if (++numLive >= maxLive) {
     140           0 :             if (OnLargeAllocationFailure)
     141           0 :                 OnLargeAllocationFailure();
     142           0 :             if (numLive >= maxLive) {
     143           0 :                 numLive--;
     144           0 :                 return nullptr;
     145             :             }
     146             :         }
     147             : 
     148           0 :         uint32_t mappedSize = SharedArrayMappedSize(allocSize);
     149             : 
     150             :         // Get the entire reserved region (with all pages inaccessible)
     151           0 :         p = MapMemory(mappedSize, false);
     152           0 :         if (!p) {
     153           0 :             numLive--;
     154           0 :             return nullptr;
     155             :         }
     156             : 
     157           0 :         if (!MarkValidRegion(p, allocSize)) {
     158           0 :             UnmapMemory(p, mappedSize);
     159           0 :             numLive--;
     160           0 :             return nullptr;
     161             :         }
     162             : 
     163             : # if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
     164             :         // Tell Valgrind/Memcheck to not report accesses in the inaccessible region.
     165             :         VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)p + allocSize,
     166             :                                                        mappedSize - allocSize);
     167             : # endif
     168             :     } else {
     169           0 :         p = MapMemory(allocSize, true);
     170           0 :         if (!p)
     171           0 :             return nullptr;
     172             :     }
     173             : 
     174           0 :     uint8_t* buffer = reinterpret_cast<uint8_t*>(p) + gc::SystemPageSize();
     175           0 :     uint8_t* base = buffer - sizeof(SharedArrayRawBuffer);
     176           0 :     SharedArrayRawBuffer* rawbuf = new (base) SharedArrayRawBuffer(buffer, length, preparedForAsmJS);
     177           0 :     MOZ_ASSERT(rawbuf->length == length); // Deallocation needs this
     178             : #ifdef DEBUG
     179           0 :     liveBuffers_++;
     180             : #endif
     181           0 :     return rawbuf;
     182             : }
     183             : 
     184             : bool
     185           0 : SharedArrayRawBuffer::addReference()
     186             : {
     187           0 :     MOZ_RELEASE_ASSERT(this->refcount_ > 0);
     188             : 
     189             :     // Be careful never to overflow the refcount field.
     190             :     for (;;) {
     191           0 :         uint32_t old_refcount = this->refcount_;
     192           0 :         uint32_t new_refcount = old_refcount+1;
     193           0 :         if (new_refcount == 0)
     194           0 :             return false;
     195           0 :         if (this->refcount_.compareExchange(old_refcount, new_refcount))
     196           0 :             return true;
     197           0 :     }
     198             : }
     199             : 
     200             : void
     201           0 : SharedArrayRawBuffer::dropReference()
     202             : {
     203             :     // Normally if the refcount is zero then the memory will have been unmapped
     204             :     // and this test may just crash, but if the memory has been retained for any
     205             :     // reason we will catch the underflow here.
     206           0 :     MOZ_RELEASE_ASSERT(this->refcount_ > 0);
     207             : 
     208             :     // Drop the reference to the buffer.
     209           0 :     uint32_t refcount = --this->refcount_; // Atomic.
     210           0 :     if (refcount)
     211           0 :         return;
     212             : 
     213             :     // If this was the final reference, release the buffer.
     214             : 
     215             : #ifdef DEBUG
     216           0 :     liveBuffers_--;
     217             : #endif
     218             : 
     219           0 :     SharedMem<uint8_t*> p = this->dataPointerShared() - gc::SystemPageSize();
     220           0 :     MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
     221             : 
     222           0 :     uint8_t* address = p.unwrap(/*safe - only reference*/);
     223           0 :     uint32_t allocSize = SharedArrayAllocSize(this->length);
     224             : 
     225           0 :     if (this->preparedForAsmJS) {
     226           0 :         numLive--;
     227             : 
     228           0 :         uint32_t mappedSize = SharedArrayMappedSize(allocSize);
     229           0 :         UnmapMemory(address, mappedSize);
     230             : 
     231             : # if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
     232             :         // Tell Valgrind/Memcheck to recommence reporting accesses in the
     233             :         // previously-inaccessible region.
     234             :         VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(address, mappedSize);
     235             : # endif
     236             :     } else {
     237           0 :         UnmapMemory(address, allocSize);
     238             :     }
     239             : }
     240             : 
     241             : 
     242             : MOZ_ALWAYS_INLINE bool
     243           0 : SharedArrayBufferObject::byteLengthGetterImpl(JSContext* cx, const CallArgs& args)
     244             : {
     245           0 :     MOZ_ASSERT(IsSharedArrayBuffer(args.thisv()));
     246           0 :     args.rval().setInt32(args.thisv().toObject().as<SharedArrayBufferObject>().byteLength());
     247           0 :     return true;
     248             : }
     249             : 
     250             : bool
     251           0 : SharedArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
     252             : {
     253           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     254           0 :     return CallNonGenericMethod<IsSharedArrayBuffer, byteLengthGetterImpl>(cx, args);
     255             : }
     256             : 
     257             : // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
     258             : // 24.2.2.1 SharedArrayBuffer( length )
     259             : bool
     260           0 : SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
     261             : {
     262           0 :     CallArgs args = CallArgsFromVp(argc, vp);
     263             : 
     264             :     // Step 1.
     265           0 :     if (!ThrowIfNotConstructing(cx, args, "SharedArrayBuffer"))
     266           0 :         return false;
     267             : 
     268             :     // Step 2.
     269             :     uint64_t byteLength;
     270           0 :     if (!ToIndex(cx, args.get(0), &byteLength))
     271           0 :         return false;
     272             : 
     273             :     // Step 3 (Inlined 24.2.1.1 AllocateSharedArrayBuffer).
     274             :     // 24.2.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
     275           0 :     RootedObject proto(cx);
     276           0 :     if (!GetPrototypeFromBuiltinConstructor(cx, args, &proto))
     277           0 :         return false;
     278             : 
     279             :     // 24.2.1.1, step 3 (Inlined 6.2.7.2 CreateSharedByteDataBlock, step 2).
     280             :     // Refuse to allocate too large buffers, currently limited to ~2 GiB.
     281           0 :     if (byteLength > INT32_MAX) {
     282           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_LENGTH);
     283           0 :         return false;
     284             :     }
     285             : 
     286             :     // 24.2.1.1, steps 1 and 4-6.
     287           0 :     JSObject* bufobj = New(cx, uint32_t(byteLength), proto);
     288           0 :     if (!bufobj)
     289           0 :         return false;
     290           0 :     args.rval().setObject(*bufobj);
     291           0 :     return true;
     292             : }
     293             : 
     294             : SharedArrayBufferObject*
     295           0 : SharedArrayBufferObject::New(JSContext* cx, uint32_t length, HandleObject proto)
     296             : {
     297           0 :     SharedArrayRawBuffer* buffer = SharedArrayRawBuffer::New(cx, length);
     298           0 :     if (!buffer)
     299           0 :         return nullptr;
     300             : 
     301           0 :     return New(cx, buffer, proto);
     302             : }
     303             : 
     304             : SharedArrayBufferObject*
     305           0 : SharedArrayBufferObject::New(JSContext* cx, SharedArrayRawBuffer* buffer, HandleObject proto)
     306             : {
     307           0 :     MOZ_ASSERT(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
     308             : 
     309           0 :     AutoSetNewObjectMetadata metadata(cx);
     310             :     Rooted<SharedArrayBufferObject*> obj(cx,
     311           0 :         NewObjectWithClassProto<SharedArrayBufferObject>(cx, proto));
     312           0 :     if (!obj)
     313           0 :         return nullptr;
     314             : 
     315           0 :     MOZ_ASSERT(obj->getClass() == &class_);
     316             : 
     317           0 :     obj->acceptRawBuffer(buffer);
     318             : 
     319           0 :     return obj;
     320             : }
     321             : 
     322             : void
     323           0 : SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer* buffer)
     324             : {
     325           0 :     setReservedSlot(RAWBUF_SLOT, PrivateValue(buffer));
     326           0 : }
     327             : 
     328             : void
     329           0 : SharedArrayBufferObject::dropRawBuffer()
     330             : {
     331           0 :     setReservedSlot(RAWBUF_SLOT, UndefinedValue());
     332           0 : }
     333             : 
     334             : SharedArrayRawBuffer*
     335           0 : SharedArrayBufferObject::rawBufferObject() const
     336             : {
     337           0 :     Value v = getReservedSlot(RAWBUF_SLOT);
     338           0 :     MOZ_ASSERT(!v.isUndefined());
     339           0 :     return reinterpret_cast<SharedArrayRawBuffer*>(v.toPrivate());
     340             : }
     341             : 
     342             : void
     343           0 : SharedArrayBufferObject::Finalize(FreeOp* fop, JSObject* obj)
     344             : {
     345           0 :     MOZ_ASSERT(fop->maybeOnHelperThread());
     346             : 
     347           0 :     SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
     348             : 
     349             :     // Detect the case of failure during SharedArrayBufferObject creation,
     350             :     // which causes a SharedArrayRawBuffer to never be attached.
     351           0 :     Value v = buf.getReservedSlot(RAWBUF_SLOT);
     352           0 :     if (!v.isUndefined()) {
     353           0 :         buf.rawBufferObject()->dropReference();
     354           0 :         buf.dropRawBuffer();
     355             :     }
     356           0 : }
     357             : 
     358             : /* static */ void
     359           0 : SharedArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
     360             :                                                 JS::ClassInfo* info)
     361             : {
     362             :     // Divide the buffer size by the refcount to get the fraction of the buffer
     363             :     // owned by this thread. It's conceivable that the refcount might change in
     364             :     // the middle of memory reporting, in which case the amount reported for
     365             :     // some threads might be to high (if the refcount goes up) or too low (if
     366             :     // the refcount goes down). But that's unlikely and hard to avoid, so we
     367             :     // just live with the risk.
     368           0 :     const SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
     369           0 :     info->objectsNonHeapElementsShared +=
     370           0 :         buf.byteLength() / buf.rawBufferObject()->refcount();
     371           0 : }
     372             : 
     373             : /* static */ void
     374           0 : SharedArrayBufferObject::copyData(Handle<SharedArrayBufferObject*> toBuffer, uint32_t toIndex,
     375             :                                   Handle<SharedArrayBufferObject*> fromBuffer, uint32_t fromIndex,
     376             :                                   uint32_t count)
     377             : {
     378           0 :     MOZ_ASSERT(toBuffer->byteLength() >= count);
     379           0 :     MOZ_ASSERT(toBuffer->byteLength() >= toIndex + count);
     380           0 :     MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
     381           0 :     MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
     382             : 
     383           0 :     jit::AtomicOperations::memcpySafeWhenRacy(toBuffer->dataPointerShared() + toIndex,
     384           0 :                                               fromBuffer->dataPointerShared() + fromIndex,
     385           0 :                                               count);
     386           0 : }
     387             : 
     388             : static JSObject*
     389           6 : CreateSharedArrayBufferPrototype(JSContext* cx, JSProtoKey key)
     390             : {
     391           6 :     return GlobalObject::createBlankPrototype(cx, cx->global(),
     392           6 :                                               &SharedArrayBufferObject::protoClass_);
     393             : }
     394             : 
     395             : static const ClassOps SharedArrayBufferObjectClassOps = {
     396             :     nullptr, /* addProperty */
     397             :     nullptr, /* delProperty */
     398             :     nullptr, /* getProperty */
     399             :     nullptr, /* setProperty */
     400             :     nullptr, /* enumerate */
     401             :     nullptr, /* newEnumerate */
     402             :     nullptr, /* resolve */
     403             :     nullptr, /* mayResolve */
     404             :     SharedArrayBufferObject::Finalize,
     405             :     nullptr, /* call */
     406             :     nullptr, /* hasInstance */
     407             :     nullptr, /* construct */
     408             :     nullptr, /* trace */
     409             : };
     410             : 
     411             : static const JSFunctionSpec static_functions[] = {
     412             :     JS_FS_END
     413             : };
     414             : 
     415             : static const JSPropertySpec static_properties[] = {
     416             :     JS_SELF_HOSTED_SYM_GET(species, "SharedArrayBufferSpecies", 0),
     417             :     JS_PS_END
     418             : };
     419             : 
     420             : static const JSFunctionSpec prototype_functions[] = {
     421             :     JS_SELF_HOSTED_FN("slice", "SharedArrayBufferSlice", 2, 0),
     422             :     JS_FS_END
     423             : };
     424             : 
     425             : static const JSPropertySpec prototype_properties[] = {
     426             :     JS_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0),
     427             :     JS_STRING_SYM_PS(toStringTag, "SharedArrayBuffer", JSPROP_READONLY),
     428             :     JS_PS_END
     429             : };
     430             : 
     431             : static const ClassSpec SharedArrayBufferObjectClassSpec = {
     432             :     GenericCreateConstructor<SharedArrayBufferObject::class_constructor, 1, gc::AllocKind::FUNCTION>,
     433             :     CreateSharedArrayBufferPrototype,
     434             :     static_functions,
     435             :     static_properties,
     436             :     prototype_functions,
     437             :     prototype_properties
     438             : };
     439             : 
     440             : const Class SharedArrayBufferObject::class_ = {
     441             :     "SharedArrayBuffer",
     442             :     JSCLASS_DELAY_METADATA_BUILDER |
     443             :     JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS) |
     444             :     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer) |
     445             :     JSCLASS_BACKGROUND_FINALIZE,
     446             :     &SharedArrayBufferObjectClassOps,
     447             :     &SharedArrayBufferObjectClassSpec,
     448             :     JS_NULL_CLASS_EXT
     449             : };
     450             : 
     451             : const Class SharedArrayBufferObject::protoClass_ = {
     452             :     "SharedArrayBufferPrototype",
     453             :     JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer),
     454             :     JS_NULL_CLASS_OPS,
     455             :     &SharedArrayBufferObjectClassSpec
     456             : };
     457             : 
     458             : bool
     459           0 : js::IsSharedArrayBuffer(HandleValue v)
     460             : {
     461           0 :     return v.isObject() && v.toObject().is<SharedArrayBufferObject>();
     462             : }
     463             : 
     464             : bool
     465           0 : js::IsSharedArrayBuffer(HandleObject o)
     466             : {
     467           0 :     return o->is<SharedArrayBufferObject>();
     468             : }
     469             : 
     470             : bool
     471           0 : js::IsSharedArrayBuffer(JSObject* o)
     472             : {
     473           0 :     return o->is<SharedArrayBufferObject>();
     474             : }
     475             : 
     476             : SharedArrayBufferObject&
     477           0 : js::AsSharedArrayBuffer(HandleObject obj)
     478             : {
     479           0 :     MOZ_ASSERT(IsSharedArrayBuffer(obj));
     480           0 :     return obj->as<SharedArrayBufferObject>();
     481             : }
     482             : 
     483             : JS_FRIEND_API(uint32_t)
     484           0 : JS_GetSharedArrayBufferByteLength(JSObject* obj)
     485             : {
     486           0 :     obj = CheckedUnwrap(obj);
     487           0 :     return obj ? obj->as<SharedArrayBufferObject>().byteLength() : 0;
     488             : }
     489             : 
     490             : JS_FRIEND_API(void)
     491           0 : js::GetSharedArrayBufferLengthAndData(JSObject* obj, uint32_t* length, bool* isSharedMemory, uint8_t** data)
     492             : {
     493           0 :     MOZ_ASSERT(obj->is<SharedArrayBufferObject>());
     494           0 :     *length = obj->as<SharedArrayBufferObject>().byteLength();
     495           0 :     *data = obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(/*safe - caller knows*/);
     496           0 :     *isSharedMemory = true;
     497           0 : }
     498             : 
     499             : JS_FRIEND_API(JSObject*)
     500           0 : JS_NewSharedArrayBuffer(JSContext* cx, uint32_t nbytes)
     501             : {
     502           0 :     MOZ_ASSERT(cx->compartment()->creationOptions().getSharedMemoryAndAtomicsEnabled());
     503             : 
     504           0 :     MOZ_ASSERT(nbytes <= INT32_MAX);
     505           0 :     return SharedArrayBufferObject::New(cx, nbytes, /* proto = */ nullptr);
     506             : }
     507             : 
     508             : JS_FRIEND_API(bool)
     509         138 : JS_IsSharedArrayBufferObject(JSObject* obj)
     510             : {
     511         138 :     obj = CheckedUnwrap(obj);
     512         138 :     return obj ? obj->is<SharedArrayBufferObject>() : false;
     513             : }
     514             : 
     515             : JS_FRIEND_API(uint8_t*)
     516           0 : JS_GetSharedArrayBufferData(JSObject* obj, bool* isSharedMemory, const JS::AutoCheckCannotGC&)
     517             : {
     518           0 :     obj = CheckedUnwrap(obj);
     519           0 :     if (!obj)
     520           0 :         return nullptr;
     521           0 :     *isSharedMemory = true;
     522           0 :     return obj->as<SharedArrayBufferObject>().dataPointerShared().unwrap(/*safe - caller knows*/);
     523             : }

Generated by: LCOV version 1.13