diff --git a/js/src/tests/non262/ArrayBuffer/resizable-views.js b/js/src/tests/non262/ArrayBuffer/resizable-views.js index 94a409f637..40cb0260bf 100644 --- a/js/src/tests/non262/ArrayBuffer/resizable-views.js +++ b/js/src/tests/non262/ArrayBuffer/resizable-views.js @@ -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; } diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index 17052a808a..540b85acf8 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -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 buffer(cx, &args.thisv().toObject().as()); + Rooted buffer(cx, + &args.thisv().toObject().as()); /* * 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(cx, args); + return CallNonGenericMethod(cx, args); } /* static */ ArrayBufferObject::BufferContents @@ -1926,6 +1927,8 @@ ArrayBufferViewObject::dataPointerUnshared(const JS::AutoRequireNoGC& nogc) bool ArrayBufferViewObject::isSharedMemory() { + if (is()) + return as().isSharedMemory(); if (is()) return as().isSharedMemory(); return false; @@ -1957,7 +1960,7 @@ ArrayBufferViewObject::bufferObject(JSContext* cx, Handleas().bufferEither(); } MOZ_ASSERT(thisObject->is()); - return &thisObject->as().arrayBuffer(); + return &thisObject->as().arrayBufferEither(); } /* JS Friend API */ @@ -2208,7 +2211,7 @@ JS_GetArrayBufferViewData(JSObject* obj, bool* isSharedMemory, const JS::AutoChe if (!obj) return nullptr; if (obj->is()) { - *isSharedMemory = false; + *isSharedMemory = obj->as().isSharedMemory(); return obj->as().dataPointer(); } TypedArrayObject& ta = obj->as(); @@ -2278,7 +2281,7 @@ js::GetArrayBufferViewLengthAndData(JSObject* obj, uint32_t* length, bool* isSha : obj->as().byteLength(); if (obj->is()) { - *isSharedMemory = false; + *isSharedMemory = obj->as().isSharedMemory(); *data = static_cast(obj->as().dataPointer()); } else { diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index c9dc80a598..f9e3353b7e 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -82,7 +82,7 @@ ArrayBufferObjectMaybeShared& AsAnyArrayBuffer(HandleValue val); class ArrayBufferObjectMaybeShared : public NativeObject { public: - uint32_t byteLength() { + uint32_t byteLength() const { return AnyArrayBufferByteLength(this); } diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index e1f1b2777e..50e6162923 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -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" diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index 7259f242fd..01dd80f4be 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -1874,7 +1874,7 @@ DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto) DataViewObject* DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength, - Handle arrayBuffer, JSObject* protoArg, + Handle 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()); 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() && + !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()) { + if (!arrayBuffer->as().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 buffer(cx, &AsArrayBuffer(bufobj)); + Rooted buffer(cx, &bufobj->as()); uint32_t byteOffset = 0; uint32_t byteLength = buffer->byteLength(); - bool lengthTracking = buffer->isResizable() && !args.hasDefined(2); + bool isResizableOrGrowable = + (buffer->is() && buffer->as().isResizable()) || + (buffer->is() && + buffer->as().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() && buffer->as().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 buffer(cx, &AsArrayBuffer(bufobj)); + Rooted buffer(cx, &bufobj->as()); JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto, lengthTracking); if (!obj) @@ -2226,7 +2241,7 @@ DataViewObject::read(JSContext* cx, Handle 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 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; } diff --git a/js/src/vm/TypedArrayObject.h b/js/src/vm/TypedArrayObject.h index 9d7f667979..8c08bb870e 100644 --- a/js/src/vm/TypedArrayObject.h +++ b/js/src/vm/TypedArrayObject.h @@ -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 arrayBuffer, JSObject* proto, + Handle 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(this)).toObject().as(); + ArrayBufferObjectMaybeShared& arrayBufferEither() const { + return bufferValue(const_cast(this)). + toObject().as(); + } + + bool isSharedMemory() const { + return bufferValue(const_cast(this)). + toObject().is(); } 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;