1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "vm/SharedArrayObject.h"
9 #include "mozilla/Atomics.h"
10 #include "mozilla/DebugOnly.h"
11 #include "mozilla/TaggedAnonymousMemory.h"
13 #include "gc/GCContext.h"
14 #include "gc/Memory.h"
15 #include "jit/AtomicOperations.h"
16 #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_*
17 #include "js/PropertySpec.h"
18 #include "js/SharedArrayBuffer.h"
19 #include "util/Memory.h"
20 #include "util/WindowsWrapper.h"
21 #include "vm/SharedMem.h"
22 #include "wasm/WasmConstants.h"
23 #include "wasm/WasmMemory.h"
25 #include "vm/ArrayBufferObject-inl.h"
26 #include "vm/JSObject-inl.h"
27 #include "vm/NativeObject-inl.h"
29 using js::wasm::Pages
;
30 using mozilla::DebugOnly
;
32 using mozilla::Nothing
;
36 using namespace js::jit
;
38 static size_t WasmSharedArrayAccessibleSize(size_t length
) {
39 return AlignBytes(length
, gc::SystemPageSize());
42 static size_t NonWasmSharedArrayAllocSize(size_t length
) {
43 MOZ_ASSERT(length
<= ArrayBufferObject::MaxByteLength
);
44 return sizeof(SharedArrayRawBuffer
) + length
;
47 // The mapped size for a plain shared array buffer, used only for tracking
48 // memory usage. This is incorrect for some WASM cases, and for hypothetical
49 // callers of js::SharedArrayBufferObject::createFromNewRawBuffer that do not
50 // currently exist, but it's fine as a signal of GC pressure.
51 static size_t SharedArrayMappedSize(bool isWasm
, size_t length
) {
52 // Wasm buffers use MapBufferMemory and allocate a full page for the header.
53 // Non-Wasm buffers use malloc.
55 return WasmSharedArrayAccessibleSize(length
) + gc::SystemPageSize();
57 return NonWasmSharedArrayAllocSize(length
);
60 SharedArrayRawBuffer
* SharedArrayRawBuffer::Allocate(bool isGrowable
,
63 MOZ_RELEASE_ASSERT(length
<= ArrayBufferObject::MaxByteLength
);
64 MOZ_RELEASE_ASSERT(maxLength
<= ArrayBufferObject::MaxByteLength
);
65 MOZ_ASSERT_IF(!isGrowable
, length
== maxLength
);
66 MOZ_ASSERT_IF(isGrowable
, length
<= maxLength
);
68 size_t allocSize
= NonWasmSharedArrayAllocSize(maxLength
);
69 uint8_t* p
= js_pod_calloc
<uint8_t>(allocSize
);
74 uint8_t* buffer
= p
+ sizeof(SharedArrayRawBuffer
);
75 return new (p
) SharedArrayRawBuffer(isGrowable
, buffer
, length
);
78 WasmSharedArrayRawBuffer
* WasmSharedArrayRawBuffer::AllocateWasm(
79 wasm::IndexType indexType
, Pages initialPages
, wasm::Pages clampedMaxPages
,
80 const mozilla::Maybe
<wasm::Pages
>& sourceMaxPages
,
81 const mozilla::Maybe
<size_t>& mappedSize
) {
82 // Prior code has asserted that initial pages is within our implementation
83 // limits (wasm::MaxMemoryPages()) and we can assume it is a valid size_t.
84 MOZ_ASSERT(initialPages
.hasByteLength());
85 size_t length
= initialPages
.byteLength();
87 MOZ_RELEASE_ASSERT(length
<= ArrayBufferObject::MaxByteLength
);
89 size_t accessibleSize
= WasmSharedArrayAccessibleSize(length
);
90 if (accessibleSize
< length
) {
94 size_t computedMappedSize
= mappedSize
.isSome()
96 : wasm::ComputeMappedSize(clampedMaxPages
);
97 MOZ_ASSERT(accessibleSize
<= computedMappedSize
);
99 uint64_t mappedSizeWithHeader
= computedMappedSize
+ gc::SystemPageSize();
100 uint64_t accessibleSizeWithHeader
= accessibleSize
+ gc::SystemPageSize();
102 void* p
= MapBufferMemory(indexType
, mappedSizeWithHeader
,
103 accessibleSizeWithHeader
);
108 uint8_t* buffer
= reinterpret_cast<uint8_t*>(p
) + gc::SystemPageSize();
109 uint8_t* base
= buffer
- sizeof(WasmSharedArrayRawBuffer
);
110 return new (base
) WasmSharedArrayRawBuffer(
111 buffer
, length
, indexType
, clampedMaxPages
,
112 sourceMaxPages
.valueOr(Pages(0)), computedMappedSize
);
115 void WasmSharedArrayRawBuffer::tryGrowMaxPagesInPlace(Pages deltaMaxPages
) {
116 Pages newMaxPages
= clampedMaxPages_
;
117 DebugOnly
<bool> valid
= newMaxPages
.checkedIncrement(deltaMaxPages
);
118 // Caller must ensure increment does not overflow or increase over the
119 // specified maximum pages.
121 MOZ_ASSERT(newMaxPages
<= sourceMaxPages_
);
123 size_t newMappedSize
= wasm::ComputeMappedSize(newMaxPages
);
124 MOZ_ASSERT(mappedSize_
<= newMappedSize
);
125 if (mappedSize_
== newMappedSize
) {
129 if (!ExtendBufferMapping(basePointer(), mappedSize_
, newMappedSize
)) {
133 mappedSize_
= newMappedSize
;
134 clampedMaxPages_
= newMaxPages
;
137 bool WasmSharedArrayRawBuffer::wasmGrowToPagesInPlace(const Lock
&,
139 wasm::Pages newPages
) {
140 // Check that the new pages is within our allowable range. This will
141 // simultaneously check against the maximum specified in source and our
142 // implementation limits.
143 if (newPages
> clampedMaxPages_
) {
146 MOZ_ASSERT(newPages
<= wasm::MaxMemoryPages(t
) &&
147 newPages
.byteLength() <= ArrayBufferObject::MaxByteLength
);
149 // We have checked against the clamped maximum and so we know we can convert
150 // to byte lengths now.
151 size_t newLength
= newPages
.byteLength();
153 MOZ_ASSERT(newLength
>= length_
);
155 if (newLength
== length_
) {
159 size_t delta
= newLength
- length_
;
160 MOZ_ASSERT(delta
% wasm::PageSize
== 0);
162 uint8_t* dataEnd
= dataPointerShared().unwrap(/* for resize */) + length_
;
163 MOZ_ASSERT(uintptr_t(dataEnd
) % gc::SystemPageSize() == 0);
165 if (!CommitBufferMemory(dataEnd
, delta
)) {
169 // We rely on CommitBufferMemory (and therefore memmap/VirtualAlloc) to only
170 // return once it has committed memory for all threads. We only update with a
171 // new length once this has occurred.
177 void WasmSharedArrayRawBuffer::discard(size_t byteOffset
, size_t byteLen
) {
178 SharedMem
<uint8_t*> memBase
= dataPointerShared();
180 // The caller is responsible for ensuring these conditions are met; see this
181 // function's comment in SharedArrayObject.h.
182 MOZ_ASSERT(byteOffset
% wasm::PageSize
== 0);
183 MOZ_ASSERT(byteLen
% wasm::PageSize
== 0);
184 MOZ_ASSERT(wasm::MemoryBoundsCheck(uint64_t(byteOffset
), uint64_t(byteLen
),
185 volatileByteLength()));
187 // Discarding zero bytes "succeeds" with no effect.
192 SharedMem
<uint8_t*> addr
= memBase
+ uintptr_t(byteOffset
);
194 // On POSIX-ish platforms, we discard memory by overwriting previously-mapped
195 // pages with freshly-mapped pages (which are all zeroed). The operating
196 // system recognizes this and decreases the process RSS, and eventually
197 // collects the abandoned physical pages.
199 // On Windows, committing over previously-committed pages has no effect. We
200 // could decommit and recommit, but this doesn't work for shared memories
201 // since other threads could access decommitted memory - causing a trap.
202 // Instead, we simply zero memory (memset 0), and then VirtualUnlock(), which
203 // for Historical Reasons immediately removes the pages from the working set.
204 // And then, because the pages were zeroed, Windows will actually reclaim the
205 // memory entirely instead of paging it out to disk. Naturally this behavior
206 // is not officially documented, but a Raymond Chen blog post is basically as
207 // good as MSDN, right?
209 // https://devblogs.microsoft.com/oldnewthing/20170113-00/?p=95185
212 // Discarding the entire region at once causes us to page the entire region
213 // into the working set, only to throw it out again. This can be actually
214 // disastrous when discarding already-discarded memory. To mitigate this, we
215 // discard a chunk of memory at a time - this comes at a small performance
216 // cost from syscalls and potentially less-optimal memsets.
217 size_t numPages
= byteLen
/ wasm::PageSize
;
218 for (size_t i
= 0; i
< numPages
; i
++) {
219 AtomicOperations::memsetSafeWhenRacy(addr
+ (i
* wasm::PageSize
), 0,
221 DebugOnly
<bool> result
=
222 VirtualUnlock(addr
.unwrap() + (i
* wasm::PageSize
), wasm::PageSize
);
223 MOZ_ASSERT(!result
); // this always "fails" when unlocking unlocked
224 // memory...which is the only case we care about
226 #elif defined(__wasi__)
227 AtomicOperations::memsetSafeWhenRacy(addr
, 0, byteLen
);
229 void* data
= MozTaggedAnonymousMmap(
230 addr
.unwrap(), byteLen
, PROT_READ
| PROT_WRITE
,
231 MAP_PRIVATE
| MAP_ANON
| MAP_FIXED
, -1, 0, "wasm-reserved");
232 if (data
== MAP_FAILED
) {
233 MOZ_CRASH("failed to discard wasm memory; memory mappings may be broken");
238 bool SharedArrayRawBuffer::addReference() {
239 MOZ_RELEASE_ASSERT(refcount_
> 0);
241 // Be careful never to overflow the refcount field.
243 uint32_t old_refcount
= refcount_
;
244 uint32_t new_refcount
= old_refcount
+ 1;
245 if (new_refcount
== 0) {
248 if (refcount_
.compareExchange(old_refcount
, new_refcount
)) {
254 void SharedArrayRawBuffer::dropReference() {
255 // Normally if the refcount is zero then the memory will have been unmapped
256 // and this test may just crash, but if the memory has been retained for any
257 // reason we will catch the underflow here.
258 MOZ_RELEASE_ASSERT(refcount_
> 0);
260 // Drop the reference to the buffer.
261 uint32_t new_refcount
= --refcount_
; // Atomic.
266 // This was the final reference, so release the buffer.
268 WasmSharedArrayRawBuffer
* wasmBuf
= toWasmBuffer();
269 wasm::IndexType indexType
= wasmBuf
->wasmIndexType();
270 uint8_t* basePointer
= wasmBuf
->basePointer();
271 size_t mappedSizeWithHeader
= wasmBuf
->mappedSize() + gc::SystemPageSize();
272 // Call the destructor to destroy the growLock_ Mutex.
273 wasmBuf
->~WasmSharedArrayRawBuffer();
274 UnmapBufferMemory(indexType
, basePointer
, mappedSizeWithHeader
);
280 bool SharedArrayRawBuffer::grow(size_t newByteLength
) {
281 MOZ_RELEASE_ASSERT(isGrowable());
283 // The caller is responsible to ensure |newByteLength| doesn't exceed the
284 // maximum allowed byte length.
287 // `mozilla::Atomic::compareExchange` doesn't return the current value, so
288 // we need to perform a normal load here. (bug 1005335)
289 size_t oldByteLength
= length_
;
290 if (newByteLength
== oldByteLength
) {
293 if (newByteLength
< oldByteLength
) {
296 if (length_
.compareExchange(oldByteLength
, newByteLength
)) {
302 static bool IsSharedArrayBuffer(HandleValue v
) {
303 return v
.isObject() && v
.toObject().is
<SharedArrayBufferObject
>();
307 static bool IsGrowableSharedArrayBuffer(HandleValue v
) {
308 return v
.isObject() && v
.toObject().is
<GrowableSharedArrayBufferObject
>();
312 MOZ_ALWAYS_INLINE
bool SharedArrayBufferObject::byteLengthGetterImpl(
313 JSContext
* cx
, const CallArgs
& args
) {
314 MOZ_ASSERT(IsSharedArrayBuffer(args
.thisv()));
315 auto* buffer
= &args
.thisv().toObject().as
<SharedArrayBufferObject
>();
316 args
.rval().setNumber(buffer
->byteLength());
320 bool SharedArrayBufferObject::byteLengthGetter(JSContext
* cx
, unsigned argc
,
322 CallArgs args
= CallArgsFromVp(argc
, vp
);
323 return CallNonGenericMethod
<IsSharedArrayBuffer
, byteLengthGetterImpl
>(cx
,
329 * get SharedArrayBuffer.prototype.maxByteLength
331 bool SharedArrayBufferObject::maxByteLengthGetterImpl(JSContext
* cx
,
332 const CallArgs
& args
) {
333 MOZ_ASSERT(IsSharedArrayBuffer(args
.thisv()));
334 auto* buffer
= &args
.thisv().toObject().as
<SharedArrayBufferObject
>();
337 args
.rval().setNumber(buffer
->byteLengthOrMaxByteLength());
342 * get SharedArrayBuffer.prototype.maxByteLength
344 bool SharedArrayBufferObject::maxByteLengthGetter(JSContext
* cx
, unsigned argc
,
347 CallArgs args
= CallArgsFromVp(argc
, vp
);
348 return CallNonGenericMethod
<IsSharedArrayBuffer
, maxByteLengthGetterImpl
>(
353 * get SharedArrayBuffer.prototype.growable
355 bool SharedArrayBufferObject::growableGetterImpl(JSContext
* cx
,
356 const CallArgs
& args
) {
357 MOZ_ASSERT(IsSharedArrayBuffer(args
.thisv()));
358 auto* buffer
= &args
.thisv().toObject().as
<SharedArrayBufferObject
>();
361 args
.rval().setBoolean(buffer
->isGrowable());
366 * get SharedArrayBuffer.prototype.growable
368 bool SharedArrayBufferObject::growableGetter(JSContext
* cx
, unsigned argc
,
371 CallArgs args
= CallArgsFromVp(argc
, vp
);
372 return CallNonGenericMethod
<IsSharedArrayBuffer
, growableGetterImpl
>(cx
,
377 * SharedArrayBuffer.prototype.grow ( newLength )
379 bool SharedArrayBufferObject::growImpl(JSContext
* cx
, const CallArgs
& args
) {
380 MOZ_ASSERT(IsGrowableSharedArrayBuffer(args
.thisv()));
381 Rooted
<GrowableSharedArrayBufferObject
*> buffer(
382 cx
, &args
.thisv().toObject().as
<GrowableSharedArrayBufferObject
>());
385 uint64_t newByteLength
;
386 if (!ToIndex(cx
, args
.get(0), &newByteLength
)) {
391 if (newByteLength
> buffer
->maxByteLength()) {
392 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
393 JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM
);
396 if (!buffer
->rawBufferObject()->grow(newByteLength
)) {
397 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
398 JSMSG_SHARED_ARRAY_LENGTH_SMALLER_THAN_CURRENT
);
402 args
.rval().setUndefined();
407 * SharedArrayBuffer.prototype.grow ( newLength )
409 bool SharedArrayBufferObject::grow(JSContext
* cx
, unsigned argc
, Value
* vp
) {
411 CallArgs args
= CallArgsFromVp(argc
, vp
);
412 return CallNonGenericMethod
<IsGrowableSharedArrayBuffer
, growImpl
>(cx
, args
);
416 // ES2017 draft rev 6390c2f1b34b309895d31d8c0512eac8660a0210
417 // 24.2.2.1 SharedArrayBuffer( length )
418 bool SharedArrayBufferObject::class_constructor(JSContext
* cx
, unsigned argc
,
420 CallArgs args
= CallArgsFromVp(argc
, vp
);
423 if (!ThrowIfNotConstructing(cx
, args
, "SharedArrayBuffer")) {
429 if (!ToIndex(cx
, args
.get(0), &byteLength
)) {
433 mozilla::Maybe
<uint64_t> maxByteLength
;
435 // SharedArrayBuffer ( length [ , options ] ), step 3.
436 if (cx
->realm()->creationOptions().getSharedArrayBufferGrowableEnabled()) {
437 // Inline call to GetArrayBufferMaxByteLengthOption.
438 if (args
.get(1).isObject()) {
439 Rooted
<JSObject
*> options(cx
, &args
[1].toObject());
441 Rooted
<Value
> val(cx
);
442 if (!GetProperty(cx
, options
, options
, cx
->names().maxByteLength
, &val
)) {
445 if (!val
.isUndefined()) {
446 uint64_t maxByteLengthInt
;
447 if (!ToIndex(cx
, val
, &maxByteLengthInt
)) {
451 // AllocateSharedArrayBuffer, step 2.
452 if (byteLength
> maxByteLengthInt
) {
453 JS_ReportErrorNumberASCII(
454 cx
, GetErrorMessage
, nullptr,
455 JSMSG_ARRAYBUFFER_LENGTH_LARGER_THAN_MAXIMUM
);
458 maxByteLength
= mozilla::Some(maxByteLengthInt
);
464 // Step 3 (Inlined 24.2.1.1 AllocateSharedArrayBuffer).
465 // 24.2.1.1, step 1 (Inlined 9.1.14 OrdinaryCreateFromConstructor).
466 RootedObject
proto(cx
);
467 if (!GetPrototypeFromBuiltinConstructor(cx
, args
, JSProto_SharedArrayBuffer
,
472 // 24.2.1.1, step 3 (Inlined 6.2.7.2 CreateSharedByteDataBlock, step 2).
473 // Refuse to allocate too large buffers.
474 if (byteLength
> ArrayBufferObject::MaxByteLength
) {
475 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
476 JSMSG_SHARED_ARRAY_BAD_LENGTH
);
481 auto* bufobj
= NewGrowable(cx
, byteLength
, *maxByteLength
, proto
);
485 args
.rval().setObject(*bufobj
);
489 // 24.2.1.1, steps 1 and 4-6.
490 JSObject
* bufobj
= New(cx
, byteLength
, proto
);
494 args
.rval().setObject(*bufobj
);
498 FixedLengthSharedArrayBufferObject
* SharedArrayBufferObject::New(
499 JSContext
* cx
, size_t length
, HandleObject proto
) {
500 bool isGrowable
= false;
501 size_t maxLength
= length
;
502 auto* buffer
= SharedArrayRawBuffer::Allocate(isGrowable
, length
, maxLength
);
504 js::ReportOutOfMemory(cx
);
508 auto* obj
= New(cx
, buffer
, length
, proto
);
510 buffer
->dropReference();
517 FixedLengthSharedArrayBufferObject
* SharedArrayBufferObject::New(
518 JSContext
* cx
, SharedArrayRawBuffer
* buffer
, size_t length
,
519 HandleObject proto
) {
520 return NewWith
<FixedLengthSharedArrayBufferObject
>(cx
, buffer
, length
, proto
);
523 GrowableSharedArrayBufferObject
* SharedArrayBufferObject::NewGrowable(
524 JSContext
* cx
, size_t length
, size_t maxLength
, HandleObject proto
) {
525 bool isGrowable
= true;
526 auto* buffer
= SharedArrayRawBuffer::Allocate(isGrowable
, length
, maxLength
);
528 js::ReportOutOfMemory(cx
);
532 auto* obj
= NewGrowable(cx
, buffer
, maxLength
, proto
);
534 buffer
->dropReference();
541 GrowableSharedArrayBufferObject
* SharedArrayBufferObject::NewGrowable(
542 JSContext
* cx
, SharedArrayRawBuffer
* buffer
, size_t maxLength
,
543 HandleObject proto
) {
544 return NewWith
<GrowableSharedArrayBufferObject
>(cx
, buffer
, maxLength
, proto
);
547 template <class SharedArrayBufferType
>
548 SharedArrayBufferType
* SharedArrayBufferObject::NewWith(
549 JSContext
* cx
, SharedArrayRawBuffer
* buffer
, size_t length
,
550 HandleObject proto
) {
551 MOZ_ASSERT(cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
554 std::is_same_v
<SharedArrayBufferType
,
555 FixedLengthSharedArrayBufferObject
> ||
556 std::is_same_v
<SharedArrayBufferType
, GrowableSharedArrayBufferObject
>);
558 if constexpr (std::is_same_v
<SharedArrayBufferType
,
559 FixedLengthSharedArrayBufferObject
>) {
560 MOZ_ASSERT(!buffer
->isGrowable());
562 MOZ_ASSERT(buffer
->isGrowable());
565 AutoSetNewObjectMetadata
metadata(cx
);
566 auto* obj
= NewObjectWithClassProto
<SharedArrayBufferType
>(cx
, proto
);
571 MOZ_ASSERT(obj
->getClass() == &SharedArrayBufferType::class_
);
573 cx
->runtime()->incSABCount();
575 if (!obj
->acceptRawBuffer(buffer
, length
)) {
576 js::ReportOutOfMemory(cx
);
583 bool SharedArrayBufferObject::acceptRawBuffer(SharedArrayRawBuffer
* buffer
,
585 if (!zone()->addSharedMemory(buffer
,
586 SharedArrayMappedSize(buffer
->isWasm(), length
),
587 MemoryUse::SharedArrayRawBuffer
)) {
591 setFixedSlot(RAWBUF_SLOT
, PrivateValue(buffer
));
592 setFixedSlot(LENGTH_SLOT
, PrivateValue(length
));
596 void SharedArrayBufferObject::dropRawBuffer() {
597 size_t length
= byteLengthOrMaxByteLength();
598 size_t size
= SharedArrayMappedSize(isWasm(), length
);
599 zoneFromAnyThread()->removeSharedMemory(rawBufferObject(), size
,
600 MemoryUse::SharedArrayRawBuffer
);
601 rawBufferObject()->dropReference();
602 setFixedSlot(RAWBUF_SLOT
, UndefinedValue());
605 SharedArrayRawBuffer
* SharedArrayBufferObject::rawBufferObject() const {
606 Value v
= getFixedSlot(RAWBUF_SLOT
);
607 MOZ_ASSERT(!v
.isUndefined());
608 return reinterpret_cast<SharedArrayRawBuffer
*>(v
.toPrivate());
611 void SharedArrayBufferObject::Finalize(JS::GCContext
* gcx
, JSObject
* obj
) {
612 // Must be foreground finalizable so that we can account for the object.
613 MOZ_ASSERT(gcx
->onMainThread());
614 gcx
->runtime()->decSABCount();
616 SharedArrayBufferObject
& buf
= obj
->as
<SharedArrayBufferObject
>();
618 // Detect the case of failure during SharedArrayBufferObject creation,
619 // which causes a SharedArrayRawBuffer to never be attached.
620 Value v
= buf
.getFixedSlot(RAWBUF_SLOT
);
621 if (!v
.isUndefined()) {
627 void SharedArrayBufferObject::addSizeOfExcludingThis(
628 JSObject
* obj
, mozilla::MallocSizeOf mallocSizeOf
, JS::ClassInfo
* info
,
629 JS::RuntimeSizes
* runtimeSizes
) {
630 // Divide the buffer size by the refcount to get the fraction of the buffer
631 // owned by this thread. It's conceivable that the refcount might change in
632 // the middle of memory reporting, in which case the amount reported for
633 // some threads might be to high (if the refcount goes up) or too low (if
634 // the refcount goes down). But that's unlikely and hard to avoid, so we
635 // just live with the risk.
636 const SharedArrayBufferObject
& buf
= obj
->as
<SharedArrayBufferObject
>();
637 size_t nbytes
= buf
.byteLengthOrMaxByteLength();
638 size_t owned
= nbytes
/ buf
.rawBufferObject()->refcount();
640 info
->objectsNonHeapElementsWasmShared
+= owned
;
642 size_t ownedGuardPages
=
643 (buf
.wasmMappedSize() - nbytes
) / buf
.rawBufferObject()->refcount();
644 runtimeSizes
->wasmGuardPages
+= ownedGuardPages
;
647 info
->objectsNonHeapElementsShared
+= owned
;
652 void SharedArrayBufferObject::copyData(
653 Handle
<ArrayBufferObjectMaybeShared
*> toBuffer
, size_t toIndex
,
654 Handle
<ArrayBufferObjectMaybeShared
*> fromBuffer
, size_t fromIndex
,
656 MOZ_ASSERT(toBuffer
->byteLength() >= count
);
657 MOZ_ASSERT(toBuffer
->byteLength() >= toIndex
+ count
);
658 MOZ_ASSERT(fromBuffer
->byteLength() >= fromIndex
);
659 MOZ_ASSERT(fromBuffer
->byteLength() >= fromIndex
+ count
);
661 jit::AtomicOperations::memcpySafeWhenRacy(
662 toBuffer
->dataPointerEither() + toIndex
,
663 fromBuffer
->dataPointerEither() + fromIndex
, count
);
666 SharedArrayBufferObject
* SharedArrayBufferObject::createFromNewRawBuffer(
667 JSContext
* cx
, WasmSharedArrayRawBuffer
* buffer
, size_t initialSize
) {
668 MOZ_ASSERT(cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
670 AutoSetNewObjectMetadata
metadata(cx
);
671 auto* obj
= NewBuiltinClassInstance
<FixedLengthSharedArrayBufferObject
>(cx
);
673 buffer
->dropReference();
677 cx
->runtime()->incSABCount();
679 if (!obj
->acceptRawBuffer(buffer
, initialSize
)) {
680 buffer
->dropReference();
688 void SharedArrayBufferObject::wasmDiscard(Handle
<SharedArrayBufferObject
*> buf
,
691 MOZ_ASSERT(buf
->isWasm());
692 buf
->rawWasmBufferObject()->discard(byteOffset
, byteLen
);
695 static const JSClassOps SharedArrayBufferObjectClassOps
= {
696 nullptr, // addProperty
697 nullptr, // delProperty
698 nullptr, // enumerate
699 nullptr, // newEnumerate
701 nullptr, // mayResolve
702 SharedArrayBufferObject::Finalize
, // finalize
704 nullptr, // construct
708 static const JSFunctionSpec sharedarray_functions
[] = {
712 static const JSPropertySpec sharedarray_properties
[] = {
713 JS_SELF_HOSTED_SYM_GET(species
, "$SharedArrayBufferSpecies", 0),
717 static const JSFunctionSpec sharedarray_proto_functions
[] = {
718 JS_SELF_HOSTED_FN("slice", "SharedArrayBufferSlice", 2, 0),
720 JS_FN("grow", SharedArrayBufferObject::grow
, 1, 0),
725 static const JSPropertySpec sharedarray_proto_properties
[] = {
726 JS_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter
, 0),
728 JS_PSG("maxByteLength", SharedArrayBufferObject::maxByteLengthGetter
, 0),
729 JS_PSG("growable", SharedArrayBufferObject::growableGetter
, 0),
731 JS_STRING_SYM_PS(toStringTag
, "SharedArrayBuffer", JSPROP_READONLY
),
735 static JSObject
* CreateSharedArrayBufferPrototype(JSContext
* cx
,
737 return GlobalObject::createBlankPrototype(
738 cx
, cx
->global(), &SharedArrayBufferObject::protoClass_
);
741 static const ClassSpec SharedArrayBufferObjectClassSpec
= {
742 GenericCreateConstructor
<SharedArrayBufferObject::class_constructor
, 1,
743 gc::AllocKind::FUNCTION
>,
744 CreateSharedArrayBufferPrototype
,
745 sharedarray_functions
,
746 sharedarray_properties
,
747 sharedarray_proto_functions
,
748 sharedarray_proto_properties
,
751 const JSClass
SharedArrayBufferObject::protoClass_
= {
752 "SharedArrayBuffer.prototype",
753 JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer
),
755 &SharedArrayBufferObjectClassSpec
,
758 const JSClass
FixedLengthSharedArrayBufferObject::class_
= {
760 JSCLASS_DELAY_METADATA_BUILDER
|
761 JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS
) |
762 JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer
) |
763 JSCLASS_FOREGROUND_FINALIZE
,
764 &SharedArrayBufferObjectClassOps
,
765 &SharedArrayBufferObjectClassSpec
,
769 const JSClass
GrowableSharedArrayBufferObject::class_
= {
771 JSCLASS_DELAY_METADATA_BUILDER
|
772 JSCLASS_HAS_RESERVED_SLOTS(SharedArrayBufferObject::RESERVED_SLOTS
) |
773 JSCLASS_HAS_CACHED_PROTO(JSProto_SharedArrayBuffer
) |
774 JSCLASS_FOREGROUND_FINALIZE
,
775 &SharedArrayBufferObjectClassOps
,
776 &SharedArrayBufferObjectClassSpec
,
780 JS_PUBLIC_API
size_t JS::GetSharedArrayBufferByteLength(JSObject
* obj
) {
781 auto* aobj
= obj
->maybeUnwrapAs
<SharedArrayBufferObject
>();
782 return aobj
? aobj
->byteLength() : 0;
785 JS_PUBLIC_API
void JS::GetSharedArrayBufferLengthAndData(JSObject
* obj
,
787 bool* isSharedMemory
,
789 MOZ_ASSERT(obj
->is
<SharedArrayBufferObject
>());
790 *length
= obj
->as
<SharedArrayBufferObject
>().byteLength();
791 *data
= obj
->as
<SharedArrayBufferObject
>().dataPointerShared().unwrap(
792 /*safe - caller knows*/);
793 *isSharedMemory
= true;
796 JS_PUBLIC_API JSObject
* JS::NewSharedArrayBuffer(JSContext
* cx
, size_t nbytes
) {
797 MOZ_ASSERT(cx
->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled());
799 if (nbytes
> ArrayBufferObject::MaxByteLength
) {
800 JS_ReportErrorNumberASCII(cx
, GetErrorMessage
, nullptr,
801 JSMSG_SHARED_ARRAY_BAD_LENGTH
);
805 return SharedArrayBufferObject::New(cx
, nbytes
,
806 /* proto = */ nullptr);
809 JS_PUBLIC_API
bool JS::IsSharedArrayBufferObject(JSObject
* obj
) {
810 return obj
->canUnwrapAs
<SharedArrayBufferObject
>();
813 JS_PUBLIC_API
uint8_t* JS::GetSharedArrayBufferData(
814 JSObject
* obj
, bool* isSharedMemory
, const JS::AutoRequireNoGC
&) {
815 auto* aobj
= obj
->maybeUnwrapAs
<SharedArrayBufferObject
>();
819 *isSharedMemory
= true;
820 return aobj
->dataPointerShared().unwrap(/*safe - caller knows*/);
823 JS_PUBLIC_API
bool JS::ContainsSharedArrayBuffer(JSContext
* cx
) {
824 return cx
->runtime()->hasLiveSABs();