1
0
mirror of https://github.com/roytam1/UXP.git synced 2026-05-26 13:58:49 +00:00

Support DataView on shared array buffers

This commit is contained in:
Basilisk-Dev
2026-05-15 20:15:09 -04:00
committed by roytam1
parent d97a2eb04f
commit 366476589f
6 changed files with 73 additions and 31 deletions
@@ -66,6 +66,23 @@ assertEq(sharedTracking.length, 8);
sharedTracking[6] = 33;
assertEq(new Uint8Array(gsab)[6], 33);
var sharedDv = new DataView(gsab);
assertEq(sharedDv.buffer, gsab);
assertEq(sharedDv.byteOffset, 0);
assertEq(sharedDv.byteLength, 8);
sharedDv.setUint8(7, 99);
assertEq(sharedTracking[7], 99);
gsab.grow(12);
assertEq(sharedDv.byteLength, 12);
assertEq(sharedDv.getUint8(7), 99);
var fixedSharedDv = new DataView(gsab, 4, 2);
assertEq(fixedSharedDv.byteOffset, 4);
assertEq(fixedSharedDv.byteLength, 2);
gsab.grow(16);
assertEq(fixedSharedDv.byteOffset, 4);
assertEq(fixedSharedDv.byteLength, 2);
function readLength(view) {
return view.length;
}
+9 -6
View File
@@ -1474,7 +1474,7 @@ ArrayBufferObject::createEmpty(JSContext* cx)
bool
ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args)
{
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
MOZ_ASSERT(IsAnyArrayBuffer(args.thisv()));
/*
* This method is only called for |DataView(alienBuf, ...)| which calls
@@ -1487,7 +1487,8 @@ ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args
uint32_t byteOffset = args[0].toPrivateUint32();
uint32_t byteLength = args[1].toPrivateUint32();
bool lengthTracking = args[3].toBoolean();
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx,
&args.thisv().toObject().as<ArrayBufferObjectMaybeShared>());
/*
* Pop off the passed-along prototype and delegate to normal DataViewObject
@@ -1505,7 +1506,7 @@ bool
ArrayBufferObject::createDataViewForThis(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
return CallNonGenericMethod<IsArrayBuffer, createDataViewForThisImpl>(cx, args);
return CallNonGenericMethod<IsAnyArrayBuffer, createDataViewForThisImpl>(cx, args);
}
/* static */ ArrayBufferObject::BufferContents
@@ -1926,6 +1927,8 @@ ArrayBufferViewObject::dataPointerUnshared(const JS::AutoRequireNoGC& nogc)
bool
ArrayBufferViewObject::isSharedMemory()
{
if (is<DataViewObject>())
return as<DataViewObject>().isSharedMemory();
if (is<TypedArrayObject>())
return as<TypedArrayObject>().isSharedMemory();
return false;
@@ -1957,7 +1960,7 @@ ArrayBufferViewObject::bufferObject(JSContext* cx, Handle<ArrayBufferViewObject*
return thisObject->as<TypedArrayObject>().bufferEither();
}
MOZ_ASSERT(thisObject->is<DataViewObject>());
return &thisObject->as<DataViewObject>().arrayBuffer();
return &thisObject->as<DataViewObject>().arrayBufferEither();
}
/* JS Friend API */
@@ -2208,7 +2211,7 @@ JS_GetArrayBufferViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoChe
if (!obj)
return nullptr;
if (obj->is<DataViewObject>()) {
*isSharedMemory = false;
*isSharedMemory = obj->as<DataViewObject>().isSharedMemory();
return obj->as<DataViewObject>().dataPointer();
}
TypedArrayObject& ta = obj->as<TypedArrayObject>();
@@ -2278,7 +2281,7 @@ js::GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, bool* isSha
: obj->as<TypedArrayObject>().byteLength();
if (obj->is<DataViewObject>()) {
*isSharedMemory = false;
*isSharedMemory = obj->as<DataViewObject>().isSharedMemory();
*data = static_cast<uint8_t*>(obj->as<DataViewObject>().dataPointer());
}
else {
+1 -1
View File
@@ -82,7 +82,7 @@ ArrayBufferObjectMaybeShared& AsAnyArrayBuffer(HandleValue val);
class ArrayBufferObjectMaybeShared : public NativeObject
{
public:
uint32_t byteLength() {
uint32_t byteLength() const {
return AnyArrayBufferByteLength(this);
}
+1
View File
@@ -45,6 +45,7 @@
#include "builtin/MapObject.h"
#include "js/Date.h"
#include "js/GCHashTable.h"
#include "vm/ArrayBufferObject-inl.h"
#include "vm/SavedFrame.h"
#include "vm/SharedArrayObject.h"
#include "vm/TypedArrayObject.h"
+34 -19
View File
@@ -1874,7 +1874,7 @@ DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto)
DataViewObject*
DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
Handle<ArrayBufferObject*> arrayBuffer, JSObject* protoArg,
Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, JSObject* protoArg,
bool lengthTracking)
{
if (arrayBuffer->isDetached()) {
@@ -1885,7 +1885,6 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
MOZ_ASSERT(byteOffset <= INT32_MAX);
MOZ_ASSERT(byteLength <= INT32_MAX);
MOZ_ASSERT(byteOffset + byteLength < UINT32_MAX);
MOZ_ASSERT(!arrayBuffer || !arrayBuffer->is<SharedArrayBufferObject>());
RootedObject proto(cx, protoArg);
RootedObject obj(cx);
@@ -1921,18 +1920,25 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
Int32Value(lengthTracking ? TypedArrayObject::LENGTH_TRACKING
: int32_t(byteLength)));
dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
auto dataPointer = arrayBuffer->dataPointerEither();
dvobj.initPrivate((dataPointer + byteOffset).unwrap(/*safe - stored as private data*/));
// Include a barrier if the data view's data pointer is in the nursery, as
// is done for typed arrays.
if (!IsInsideNursery(obj) && cx->runtime()->gc.nursery.isInside(arrayBuffer->dataPointer()))
if (arrayBuffer->is<ArrayBufferObject>() &&
!IsInsideNursery(obj) &&
cx->runtime()->gc.nursery.isInside(dataPointer))
{
cx->runtime()->gc.storeBuffer.putWholeCell(obj);
}
// Verify that the private slot is at the expected place
MOZ_ASSERT(dvobj.numFixedSlots() == TypedArrayObject::DATA_SLOT);
if (!arrayBuffer->addView(cx, &dvobj))
return nullptr;
if (arrayBuffer->is<ArrayBufferObject>()) {
if (!arrayBuffer->as<ArrayBufferObject>().addView(cx, &dvobj))
return nullptr;
}
return &dvobj;
}
@@ -1942,28 +1948,35 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr,
bool* lengthTrackingPtr)
{
if (!IsArrayBuffer(bufobj)) {
if (!IsAnyArrayBuffer(bufobj)) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
"DataView", "ArrayBuffer", bufobj->getClass()->name);
"DataView", "ArrayBuffer or SharedArrayBuffer",
bufobj->getClass()->name);
return false;
}
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &bufobj->as<ArrayBufferObjectMaybeShared>());
uint32_t byteOffset = 0;
uint32_t byteLength = buffer->byteLength();
bool lengthTracking = buffer->isResizable() && !args.hasDefined(2);
bool isResizableOrGrowable =
(buffer->is<ArrayBufferObject>() && buffer->as<ArrayBufferObject>().isResizable()) ||
(buffer->is<SharedArrayBufferObject>() &&
buffer->as<SharedArrayBufferObject>().isGrowable());
bool lengthTracking = isResizableOrGrowable && !args.hasDefined(2);
if (args.length() > 1) {
if (!ToUint32(cx, args[1], &byteOffset))
uint64_t offset;
if (!ToIndex(cx, args[1], &offset))
return false;
if (byteOffset > INT32_MAX) {
if (offset > INT32_MAX) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_ARG_INDEX_OUT_OF_RANGE,
"1");
return false;
}
byteOffset = uint32_t(offset);
}
if (buffer->isDetached()) {
if (buffer->is<ArrayBufferObject>() && buffer->as<ArrayBufferObject>().isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
@@ -1977,16 +1990,18 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
if (args.get(2).isUndefined()) {
byteLength -= byteOffset;
lengthTracking = buffer->isResizable();
lengthTracking = isResizableOrGrowable;
} else {
if (!ToUint32(cx, args[2], &byteLength))
uint64_t viewByteLength;
if (!ToIndex(cx, args[2], &viewByteLength))
return false;
lengthTracking = false;
if (byteLength > INT32_MAX) {
if (viewByteLength > INT32_MAX) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
return false;
}
byteLength = uint32_t(viewByteLength);
MOZ_ASSERT(byteOffset + byteLength >= byteOffset,
"can't overflow: both numbers are less than INT32_MAX");
@@ -2026,7 +2041,7 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
return false;
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
Rooted<ArrayBufferObjectMaybeShared*> buffer(cx, &bufobj->as<ArrayBufferObjectMaybeShared>());
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto,
lengthTracking);
if (!obj)
@@ -2226,7 +2241,7 @@ DataViewObject::read(JSContext* cx, Handle<DataViewObject*> obj,
bool isLittleEndian = args.length() >= 2 && ToBoolean(args[1]);
// Steps 6-7.
if (obj->arrayBuffer().isDetached()) {
if (obj->arrayBufferEither().isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
@@ -2325,7 +2340,7 @@ DataViewObject::write(JSContext* cx, Handle<DataViewObject*> obj,
bool isLittleEndian = args.length() >= 3 && ToBoolean(args[2]);
// Steps 7-8.
if (obj->arrayBuffer().isDetached()) {
if (obj->arrayBufferEither().isDetached()) {
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
return false;
}
+11 -5
View File
@@ -532,7 +532,7 @@ class DataViewObject : public NativeObject
friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
static DataViewObject*
create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
Handle<ArrayBufferObject*> arrayBuffer, JSObject* proto,
Handle<ArrayBufferObjectMaybeShared*> arrayBuffer, JSObject* proto,
bool lengthTracking = false);
public:
@@ -560,12 +560,18 @@ class DataViewObject : public NativeObject
if (isOutOfBounds())
return 0;
if (isLengthTracking())
return arrayBuffer().byteLength() - byteOffsetMaybeOutOfBounds();
return arrayBufferEither().byteLength() - byteOffsetMaybeOutOfBounds();
return fixedByteLengthMaybeOutOfBounds();
}
ArrayBufferObject& arrayBuffer() const {
return bufferValue(const_cast<DataViewObject*>(this)).toObject().as<ArrayBufferObject>();
ArrayBufferObjectMaybeShared& arrayBufferEither() const {
return bufferValue(const_cast<DataViewObject*>(this)).
toObject().as<ArrayBufferObjectMaybeShared>();
}
bool isSharedMemory() const {
return bufferValue(const_cast<DataViewObject*>(this)).
toObject().is<SharedArrayBufferObject>();
}
void* dataPointer() const {
@@ -590,7 +596,7 @@ class DataViewObject : public NativeObject
}
bool isOutOfBounds() const {
const ArrayBufferObject& buffer = arrayBuffer();
const ArrayBufferObjectMaybeShared& buffer = arrayBufferEither();
if (buffer.isDetached())
return true;