mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-27 01:27:30 +09:00
Implement resizable buffer view semantics
This commit is contained in:
parent
f20f604ce4
commit
2e2d399564
5 changed files with 267 additions and 48 deletions
70
js/src/tests/non262/ArrayBuffer/resizable-views.js
Normal file
70
js/src/tests/non262/ArrayBuffer/resizable-views.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// |reftest| skip-if(!this.SharedArrayBuffer)
|
||||||
|
|
||||||
|
var rab = new ArrayBuffer(4, { maxByteLength: 16 });
|
||||||
|
var tracking = new Uint8Array(rab);
|
||||||
|
var fixed = new Uint8Array(rab, 1, 2);
|
||||||
|
var bytes = new Uint8Array(rab);
|
||||||
|
|
||||||
|
bytes[1] = 11;
|
||||||
|
bytes[2] = 22;
|
||||||
|
|
||||||
|
assertEq(tracking.length, 4);
|
||||||
|
assertEq(tracking.byteLength, 4);
|
||||||
|
assertEq(tracking.byteOffset, 0);
|
||||||
|
assertEq(fixed.length, 2);
|
||||||
|
assertEq(fixed.byteLength, 2);
|
||||||
|
assertEq(fixed.byteOffset, 1);
|
||||||
|
|
||||||
|
rab.resize(2);
|
||||||
|
assertEq(tracking.length, 2);
|
||||||
|
assertEq(tracking.byteLength, 2);
|
||||||
|
assertEq(fixed.length, 0);
|
||||||
|
assertEq(fixed.byteLength, 0);
|
||||||
|
assertEq(fixed.byteOffset, 0);
|
||||||
|
assertEq(fixed[0], undefined);
|
||||||
|
|
||||||
|
rab.resize(8);
|
||||||
|
assertEq(tracking.length, 8);
|
||||||
|
assertEq(tracking.byteLength, 8);
|
||||||
|
assertEq(fixed.length, 2);
|
||||||
|
assertEq(fixed.byteLength, 2);
|
||||||
|
assertEq(fixed.byteOffset, 1);
|
||||||
|
assertEq(fixed[0], 11);
|
||||||
|
assertEq(fixed[1], 0);
|
||||||
|
tracking[6] = 66;
|
||||||
|
assertEq(new Uint8Array(rab)[6], 66);
|
||||||
|
|
||||||
|
var dv = new DataView(rab, 4);
|
||||||
|
assertEq(dv.byteOffset, 4);
|
||||||
|
assertEq(dv.byteLength, 4);
|
||||||
|
dv.setUint8(0, 44);
|
||||||
|
assertEq(tracking[4], 44);
|
||||||
|
|
||||||
|
rab.resize(3);
|
||||||
|
assertEq(dv.byteOffset, 0);
|
||||||
|
assertEq(dv.byteLength, 0);
|
||||||
|
assertThrowsInstanceOf(() => dv.getUint8(0), RangeError);
|
||||||
|
|
||||||
|
rab.resize(6);
|
||||||
|
assertEq(dv.byteOffset, 4);
|
||||||
|
assertEq(dv.byteLength, 2);
|
||||||
|
assertEq(dv.getUint8(0), 0);
|
||||||
|
|
||||||
|
var fixedDv = new DataView(rab, 4, 2);
|
||||||
|
rab.resize(5);
|
||||||
|
assertEq(fixedDv.byteOffset, 0);
|
||||||
|
assertEq(fixedDv.byteLength, 0);
|
||||||
|
rab.resize(6);
|
||||||
|
assertEq(fixedDv.byteOffset, 4);
|
||||||
|
assertEq(fixedDv.byteLength, 2);
|
||||||
|
|
||||||
|
var gsab = new SharedArrayBuffer(4, { maxByteLength: 16 });
|
||||||
|
var sharedTracking = new Uint8Array(gsab);
|
||||||
|
assertEq(sharedTracking.length, 4);
|
||||||
|
gsab.grow(8);
|
||||||
|
assertEq(sharedTracking.length, 8);
|
||||||
|
sharedTracking[6] = 33;
|
||||||
|
assertEq(new Uint8Array(gsab)[6], 33);
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
@ -432,8 +432,12 @@ ArrayBufferViewFits(ArrayBufferViewObject* view, uint32_t newByteLength)
|
||||||
{
|
{
|
||||||
if (view->is<DataViewObject>()) {
|
if (view->is<DataViewObject>()) {
|
||||||
DataViewObject& dataView = view->as<DataViewObject>();
|
DataViewObject& dataView = view->as<DataViewObject>();
|
||||||
uint32_t byteOffset = dataView.byteOffset();
|
uint32_t byteOffset = dataView.byteOffsetMaybeOutOfBounds();
|
||||||
uint32_t byteLength = dataView.byteLength();
|
if (byteOffset > newByteLength)
|
||||||
|
return false;
|
||||||
|
uint32_t byteLength = dataView.isLengthTracking()
|
||||||
|
? newByteLength - byteOffset
|
||||||
|
: dataView.fixedByteLengthMaybeOutOfBounds();
|
||||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -441,8 +445,13 @@ ArrayBufferViewFits(ArrayBufferViewObject* view, uint32_t newByteLength)
|
||||||
TypedArrayObject& typedArray = view->as<TypedArrayObject>();
|
TypedArrayObject& typedArray = view->as<TypedArrayObject>();
|
||||||
if (typedArray.isSharedMemory())
|
if (typedArray.isSharedMemory())
|
||||||
return true;
|
return true;
|
||||||
uint32_t byteOffset = typedArray.byteOffset();
|
uint32_t byteOffset = typedArray.byteOffsetMaybeOutOfBounds();
|
||||||
uint32_t byteLength = typedArray.byteLength();
|
if (byteOffset > newByteLength)
|
||||||
|
return false;
|
||||||
|
uint32_t byteLength = typedArray.isLengthTracking()
|
||||||
|
? newByteLength - byteOffset
|
||||||
|
: typedArray.fixedLengthMaybeOutOfBounds() *
|
||||||
|
typedArray.bytesPerElement();
|
||||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -532,7 +541,8 @@ ArrayBufferObject::setNewData(FreeOp* fop, BufferContents newContents, OwnsState
|
||||||
|
|
||||||
void
|
void
|
||||||
ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
||||||
uint8_t* oldDataPointer, BufferContents newContents)
|
uint8_t* oldDataPointer, BufferContents newContents,
|
||||||
|
uint32_t newByteLength)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(!view->isSharedMemory());
|
MOZ_ASSERT(!view->isSharedMemory());
|
||||||
|
|
||||||
|
|
@ -543,7 +553,18 @@ ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view
|
||||||
uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
|
uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
|
||||||
if (viewDataPointer) {
|
if (viewDataPointer) {
|
||||||
MOZ_ASSERT(newContents);
|
MOZ_ASSERT(newContents);
|
||||||
ptrdiff_t offset = viewDataPointer - oldDataPointer;
|
uint32_t offset;
|
||||||
|
if (view->is<DataViewObject>()) {
|
||||||
|
offset = view->as<DataViewObject>().byteOffsetMaybeOutOfBounds();
|
||||||
|
} else if (view->is<TypedArrayObject>()) {
|
||||||
|
offset = view->as<TypedArrayObject>().byteOffsetMaybeOutOfBounds();
|
||||||
|
} else {
|
||||||
|
ptrdiff_t oldOffset = viewDataPointer - oldDataPointer;
|
||||||
|
MOZ_ASSERT(oldOffset >= 0);
|
||||||
|
offset = uint32_t(oldOffset);
|
||||||
|
}
|
||||||
|
if (offset > newByteLength)
|
||||||
|
offset = 0;
|
||||||
viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset;
|
viewDataPointer = static_cast<uint8_t*>(newContents.data()) + offset;
|
||||||
view->setDataPointerUnshared(viewDataPointer);
|
view->setDataPointerUnshared(viewDataPointer);
|
||||||
}
|
}
|
||||||
|
|
@ -569,10 +590,10 @@ ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents,
|
||||||
auto& innerViews = cx->compartment()->innerViews.get();
|
auto& innerViews = cx->compartment()->innerViews.get();
|
||||||
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
||||||
for (size_t i = 0; i < views->length(); i++)
|
for (size_t i = 0; i < views->length(); i++)
|
||||||
changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
|
changeViewContents(cx, (*views)[i], oldDataPointer, newContents, byteLength());
|
||||||
}
|
}
|
||||||
if (firstView())
|
if (firstView())
|
||||||
changeViewContents(cx, firstView(), oldDataPointer, newContents);
|
changeViewContents(cx, firstView(), oldDataPointer, newContents, byteLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -584,26 +605,29 @@ ArrayBufferObject::changeContentsForResize(JSContext* cx, BufferContents newCont
|
||||||
|
|
||||||
uint8_t* oldDataPointer = dataPointer();
|
uint8_t* oldDataPointer = dataPointer();
|
||||||
setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
|
setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
|
||||||
|
setByteLength(newByteLength);
|
||||||
|
|
||||||
auto& innerViews = cx->compartment()->innerViews.get();
|
auto& innerViews = cx->compartment()->innerViews.get();
|
||||||
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
||||||
for (size_t i = 0; i < views->length(); i++) {
|
for (size_t i = 0; i < views->length(); i++) {
|
||||||
ArrayBufferViewObject* view = (*views)[i];
|
ArrayBufferViewObject* view = (*views)[i];
|
||||||
if (ArrayBufferViewFits(view, newByteLength))
|
if (view->is<DataViewObject>() || view->is<TypedArrayObject>())
|
||||||
changeViewContents(cx, view, oldDataPointer, newContents);
|
changeViewContents(cx, view, oldDataPointer, newContents, newByteLength);
|
||||||
|
else if (ArrayBufferViewFits(view, newByteLength))
|
||||||
|
changeViewContents(cx, view, oldDataPointer, newContents, newByteLength);
|
||||||
else
|
else
|
||||||
NoteViewBufferWasDetached(view, newContents, cx);
|
NoteViewBufferWasDetached(view, newContents, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstView()) {
|
if (firstView()) {
|
||||||
if (ArrayBufferViewFits(firstView(), newByteLength))
|
if (firstView()->is<DataViewObject>() || firstView()->is<TypedArrayObject>())
|
||||||
changeViewContents(cx, firstView(), oldDataPointer, newContents);
|
changeViewContents(cx, firstView(), oldDataPointer, newContents, newByteLength);
|
||||||
|
else if (ArrayBufferViewFits(firstView(), newByteLength))
|
||||||
|
changeViewContents(cx, firstView(), oldDataPointer, newContents, newByteLength);
|
||||||
else
|
else
|
||||||
NoteViewBufferWasDetached(firstView(), newContents, cx);
|
NoteViewBufferWasDetached(firstView(), newContents, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
setByteLength(newByteLength);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -1455,20 +1479,22 @@ ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args
|
||||||
/*
|
/*
|
||||||
* This method is only called for |DataView(alienBuf, ...)| which calls
|
* This method is only called for |DataView(alienBuf, ...)| which calls
|
||||||
* this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength,
|
* this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength,
|
||||||
* DataView.prototype)|,
|
* DataView.prototype, lengthTracking)|,
|
||||||
* ergo there must be exactly 3 arguments.
|
* ergo there must be exactly 4 arguments.
|
||||||
*/
|
*/
|
||||||
MOZ_ASSERT(args.length() == 3);
|
MOZ_ASSERT(args.length() == 4);
|
||||||
|
|
||||||
uint32_t byteOffset = args[0].toPrivateUint32();
|
uint32_t byteOffset = args[0].toPrivateUint32();
|
||||||
uint32_t byteLength = args[1].toPrivateUint32();
|
uint32_t byteLength = args[1].toPrivateUint32();
|
||||||
|
bool lengthTracking = args[3].toBoolean();
|
||||||
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pop off the passed-along prototype and delegate to normal DataViewObject
|
* Pop off the passed-along prototype and delegate to normal DataViewObject
|
||||||
* construction.
|
* construction.
|
||||||
*/
|
*/
|
||||||
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, &args[2].toObject());
|
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer,
|
||||||
|
&args[2].toObject(), lengthTracking);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setObject(*obj);
|
args.rval().setObject(*obj);
|
||||||
|
|
@ -1848,6 +1874,8 @@ ArrayBufferViewObject::trace(JSTracer* trc, JSObject* objArg)
|
||||||
// The data may or may not be inline with the buffer. The buffer
|
// The data may or may not be inline with the buffer. The buffer
|
||||||
// can only move during a compacting GC, in which case its
|
// can only move during a compacting GC, in which case its
|
||||||
// objectMoved hook has already updated the buffer's data pointer.
|
// objectMoved hook has already updated the buffer's data pointer.
|
||||||
|
if (offset > buf.byteLength())
|
||||||
|
offset = 0;
|
||||||
obj->initPrivate(buf.dataPointer() + offset);
|
obj->initPrivate(buf.dataPointer() + offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -323,7 +323,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
||||||
uint8_t* oldDataPointer, BufferContents newContents);
|
uint8_t* oldDataPointer, BufferContents newContents,
|
||||||
|
uint32_t newByteLength);
|
||||||
void setFirstView(ArrayBufferViewObject* view);
|
void setFirstView(ArrayBufferViewObject* view);
|
||||||
|
|
||||||
uint8_t* inlineDataPointer() const;
|
uint8_t* inlineDataPointer() const;
|
||||||
|
|
|
||||||
|
|
@ -484,8 +484,9 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
}
|
}
|
||||||
|
|
||||||
static TypedArrayObject*
|
static TypedArrayObject*
|
||||||
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset, uint32_t len,
|
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
|
||||||
HandleObject proto)
|
uint32_t byteOffset, uint32_t len, HandleObject proto,
|
||||||
|
bool lengthTracking = false)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT_IF(!buffer, byteOffset == 0);
|
MOZ_ASSERT_IF(!buffer, byteOffset == 0);
|
||||||
|
|
||||||
|
|
@ -549,7 +550,9 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(len));
|
obj->setFixedSlot(TypedArrayObject::LENGTH_SLOT,
|
||||||
|
Int32Value(lengthTracking ? TypedArrayObject::LENGTH_TRACKING
|
||||||
|
: int32_t(len)));
|
||||||
obj->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
obj->setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
|
|
@ -898,6 +901,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
return nullptr; // invalid byteOffset
|
return nullptr; // invalid byteOffset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool lengthTracking = false;
|
||||||
uint32_t len;
|
uint32_t len;
|
||||||
if (lengthInt == -1) {
|
if (lengthInt == -1) {
|
||||||
len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
|
len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
|
||||||
|
|
@ -906,6 +910,12 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
|
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
|
||||||
return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
|
return nullptr; // given byte array doesn't map exactly to sizeof(NativeType) * N
|
||||||
}
|
}
|
||||||
|
if ((buffer->is<ArrayBufferObject>() && buffer->as<ArrayBufferObject>().isResizable()) ||
|
||||||
|
(buffer->is<SharedArrayBufferObject>() &&
|
||||||
|
buffer->as<SharedArrayBufferObject>().isGrowable()))
|
||||||
|
{
|
||||||
|
lengthTracking = true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
len = uint32_t(lengthInt);
|
len = uint32_t(lengthInt);
|
||||||
}
|
}
|
||||||
|
|
@ -924,7 +934,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
||||||
return nullptr; // byteOffset + len is too big for the arraybuffer
|
return nullptr; // byteOffset + len is too big for the arraybuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
return makeInstance(cx, buffer, byteOffset, len, proto);
|
return makeInstance(cx, buffer, byteOffset, len, proto, lengthTracking);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -1859,7 +1869,8 @@ DataViewNewObjectKind(JSContext* cx, uint32_t byteLength, JSObject* proto)
|
||||||
|
|
||||||
DataViewObject*
|
DataViewObject*
|
||||||
DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
Handle<ArrayBufferObject*> arrayBuffer, JSObject* protoArg)
|
Handle<ArrayBufferObject*> arrayBuffer, JSObject* protoArg,
|
||||||
|
bool lengthTracking)
|
||||||
{
|
{
|
||||||
if (arrayBuffer->isDetached()) {
|
if (arrayBuffer->isDetached()) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||||
|
|
@ -1901,7 +1912,9 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
|
|
||||||
DataViewObject& dvobj = obj->as<DataViewObject>();
|
DataViewObject& dvobj = obj->as<DataViewObject>();
|
||||||
dvobj.setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
dvobj.setFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT, Int32Value(byteOffset));
|
||||||
dvobj.setFixedSlot(TypedArrayObject::LENGTH_SLOT, Int32Value(byteLength));
|
dvobj.setFixedSlot(TypedArrayObject::LENGTH_SLOT,
|
||||||
|
Int32Value(lengthTracking ? TypedArrayObject::LENGTH_TRACKING
|
||||||
|
: int32_t(byteLength)));
|
||||||
dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
|
dvobj.setFixedSlot(TypedArrayObject::BUFFER_SLOT, ObjectValue(*arrayBuffer));
|
||||||
dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
|
dvobj.initPrivate(arrayBuffer->dataPointer() + byteOffset);
|
||||||
|
|
||||||
|
|
@ -1921,7 +1934,8 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
|
|
||||||
bool
|
bool
|
||||||
DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
||||||
uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr)
|
uint32_t* byteOffsetPtr, uint32_t* byteLengthPtr,
|
||||||
|
bool* lengthTrackingPtr)
|
||||||
{
|
{
|
||||||
if (!IsArrayBuffer(bufobj)) {
|
if (!IsArrayBuffer(bufobj)) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NOT_EXPECTED_TYPE,
|
||||||
|
|
@ -1932,6 +1946,7 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
|
||||||
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
|
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
|
||||||
uint32_t byteOffset = 0;
|
uint32_t byteOffset = 0;
|
||||||
uint32_t byteLength = buffer->byteLength();
|
uint32_t byteLength = buffer->byteLength();
|
||||||
|
bool lengthTracking = buffer->isResizable() && !args.hasDefined(2);
|
||||||
|
|
||||||
if (args.length() > 1) {
|
if (args.length() > 1) {
|
||||||
if (!ToUint32(cx, args[1], &byteOffset))
|
if (!ToUint32(cx, args[1], &byteOffset))
|
||||||
|
|
@ -1957,9 +1972,11 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
|
||||||
|
|
||||||
if (args.get(2).isUndefined()) {
|
if (args.get(2).isUndefined()) {
|
||||||
byteLength -= byteOffset;
|
byteLength -= byteOffset;
|
||||||
|
lengthTracking = buffer->isResizable();
|
||||||
} else {
|
} else {
|
||||||
if (!ToUint32(cx, args[2], &byteLength))
|
if (!ToUint32(cx, args[2], &byteLength))
|
||||||
return false;
|
return false;
|
||||||
|
lengthTracking = false;
|
||||||
if (byteLength > INT32_MAX) {
|
if (byteLength > INT32_MAX) {
|
||||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||||
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
|
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
|
||||||
|
|
@ -1983,6 +2000,7 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
|
||||||
|
|
||||||
*byteOffsetPtr = byteOffset;
|
*byteOffsetPtr = byteOffset;
|
||||||
*byteLengthPtr = byteLength;
|
*byteLengthPtr = byteLength;
|
||||||
|
*lengthTrackingPtr = lengthTracking;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -1994,7 +2012,8 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
|
||||||
assertSameCompartment(cx, bufobj);
|
assertSameCompartment(cx, bufobj);
|
||||||
|
|
||||||
uint32_t byteOffset, byteLength;
|
uint32_t byteOffset, byteLength;
|
||||||
if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength))
|
bool lengthTracking;
|
||||||
|
if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength, &lengthTracking))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
RootedObject proto(cx);
|
RootedObject proto(cx);
|
||||||
|
|
@ -2003,7 +2022,8 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
|
Rooted<ArrayBufferObject*> buffer(cx, &AsArrayBuffer(bufobj));
|
||||||
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto);
|
JSObject* obj = DataViewObject::create(cx, byteOffset, byteLength, buffer, proto,
|
||||||
|
lengthTracking);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return false;
|
return false;
|
||||||
args.rval().setObject(*obj);
|
args.rval().setObject(*obj);
|
||||||
|
|
@ -2043,8 +2063,12 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
|
||||||
|
|
||||||
// NB: This entails the IsArrayBuffer check
|
// NB: This entails the IsArrayBuffer check
|
||||||
uint32_t byteOffset, byteLength;
|
uint32_t byteOffset, byteLength;
|
||||||
if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength))
|
bool lengthTracking;
|
||||||
|
if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength,
|
||||||
|
&lengthTracking))
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Make sure to get the [[Prototype]] for the created view from this
|
// Make sure to get the [[Prototype]] for the created view from this
|
||||||
// compartment.
|
// compartment.
|
||||||
|
|
@ -2060,11 +2084,12 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FixedInvokeArgs<3> args2(cx);
|
FixedInvokeArgs<4> args2(cx);
|
||||||
|
|
||||||
args2[0].set(PrivateUint32Value(byteOffset));
|
args2[0].set(PrivateUint32Value(byteOffset));
|
||||||
args2[1].set(PrivateUint32Value(byteLength));
|
args2[1].set(PrivateUint32Value(byteLength));
|
||||||
args2[2].setObject(*proto);
|
args2[2].setObject(*proto);
|
||||||
|
args2[3].setBoolean(lengthTracking);
|
||||||
|
|
||||||
RootedValue fval(cx, global->createDataViewForThis());
|
RootedValue fval(cx, global->createDataViewForThis());
|
||||||
RootedValue thisv(cx, ObjectValue(*bufobj));
|
RootedValue thisv(cx, ObjectValue(*bufobj));
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,8 @@ class TypedArrayObject : public NativeObject
|
||||||
"right buffer slot");
|
"right buffer slot");
|
||||||
|
|
||||||
// Slot containing length of the view in number of typed elements.
|
// Slot containing length of the view in number of typed elements.
|
||||||
|
// Length-tracking views on resizable/growable buffers store
|
||||||
|
// LENGTH_TRACKING here and compute their visible length from the buffer.
|
||||||
static const size_t LENGTH_SLOT = 1;
|
static const size_t LENGTH_SLOT = 1;
|
||||||
static_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT,
|
static_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT,
|
||||||
"self-hosted code with burned-in constants must get the "
|
"self-hosted code with burned-in constants must get the "
|
||||||
|
|
@ -65,6 +67,8 @@ class TypedArrayObject : public NativeObject
|
||||||
|
|
||||||
static const size_t RESERVED_SLOTS = 3;
|
static const size_t RESERVED_SLOTS = 3;
|
||||||
|
|
||||||
|
static const int32_t LENGTH_TRACKING = -1;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
static const uint8_t ZeroLengthArrayData = 0x4A;
|
static const uint8_t ZeroLengthArrayData = 0x4A;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -139,15 +143,13 @@ class TypedArrayObject : public NativeObject
|
||||||
return tarr->getFixedSlot(BUFFER_SLOT);
|
return tarr->getFixedSlot(BUFFER_SLOT);
|
||||||
}
|
}
|
||||||
static Value byteOffsetValue(TypedArrayObject* tarr) {
|
static Value byteOffsetValue(TypedArrayObject* tarr) {
|
||||||
Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT);
|
return Int32Value(tarr->byteOffset());
|
||||||
MOZ_ASSERT(v.toInt32() >= 0);
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
static Value byteLengthValue(TypedArrayObject* tarr) {
|
static Value byteLengthValue(TypedArrayObject* tarr) {
|
||||||
return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement());
|
return Int32Value(tarr->byteLength());
|
||||||
}
|
}
|
||||||
static Value lengthValue(TypedArrayObject* tarr) {
|
static Value lengthValue(TypedArrayObject* tarr) {
|
||||||
return tarr->getFixedSlot(LENGTH_SLOT);
|
return Int32Value(tarr->length());
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -159,14 +161,70 @@ class TypedArrayObject : public NativeObject
|
||||||
JSObject* bufferObject() const {
|
JSObject* bufferObject() const {
|
||||||
return bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
return bufferValue(const_cast<TypedArrayObject*>(this)).toObjectOrNull();
|
||||||
}
|
}
|
||||||
|
bool isLengthTracking() const {
|
||||||
|
return getFixedSlot(LENGTH_SLOT).toInt32() == LENGTH_TRACKING;
|
||||||
|
}
|
||||||
|
bool hasResizableOrGrowableBuffer() const {
|
||||||
|
if (!hasBuffer())
|
||||||
|
return false;
|
||||||
|
if (isSharedMemory())
|
||||||
|
return bufferShared()->isGrowable();
|
||||||
|
return bufferUnshared()->isResizable();
|
||||||
|
}
|
||||||
|
uint32_t byteOffsetMaybeOutOfBounds() const {
|
||||||
|
Value v = getFixedSlot(BYTEOFFSET_SLOT);
|
||||||
|
MOZ_ASSERT(v.toInt32() >= 0);
|
||||||
|
return v.toInt32();
|
||||||
|
}
|
||||||
|
uint32_t fixedLengthMaybeOutOfBounds() const {
|
||||||
|
int32_t length = getFixedSlot(LENGTH_SLOT).toInt32();
|
||||||
|
MOZ_ASSERT(length >= 0);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
uint32_t bufferByteLength() const {
|
||||||
|
MOZ_ASSERT(hasBuffer());
|
||||||
|
if (isSharedMemory())
|
||||||
|
return bufferShared()->byteLength();
|
||||||
|
return bufferUnshared()->byteLength();
|
||||||
|
}
|
||||||
|
bool isOutOfBounds() const {
|
||||||
|
if (!hasBuffer())
|
||||||
|
return false;
|
||||||
|
if (hasDetachedBuffer())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32_t bufferByteLength = this->bufferByteLength();
|
||||||
|
uint32_t offset = byteOffsetMaybeOutOfBounds();
|
||||||
|
if (offset > bufferByteLength)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (isLengthTracking())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint32_t byteLength = fixedLengthMaybeOutOfBounds() * bytesPerElement();
|
||||||
|
return byteLength > bufferByteLength - offset;
|
||||||
|
}
|
||||||
uint32_t byteOffset() const {
|
uint32_t byteOffset() const {
|
||||||
return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
if (isOutOfBounds())
|
||||||
|
return 0;
|
||||||
|
return byteOffsetMaybeOutOfBounds();
|
||||||
}
|
}
|
||||||
uint32_t byteLength() const {
|
uint32_t byteLength() const {
|
||||||
return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
return length() * bytesPerElement();
|
||||||
}
|
}
|
||||||
uint32_t length() const {
|
uint32_t length() const {
|
||||||
return lengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
if (!isLengthTracking()) {
|
||||||
|
if (isOutOfBounds())
|
||||||
|
return 0;
|
||||||
|
return fixedLengthMaybeOutOfBounds();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isOutOfBounds())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
uint32_t bufferByteLength = this->bufferByteLength();
|
||||||
|
uint32_t offset = byteOffsetMaybeOutOfBounds();
|
||||||
|
return (bufferByteLength - offset) / bytesPerElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasInlineElements() const;
|
bool hasInlineElements() const;
|
||||||
|
|
@ -466,28 +524,26 @@ class DataViewObject : public NativeObject
|
||||||
defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto);
|
defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto);
|
||||||
|
|
||||||
static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
static bool getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, const CallArgs& args,
|
||||||
uint32_t *byteOffset, uint32_t* byteLength);
|
uint32_t *byteOffset, uint32_t* byteLength,
|
||||||
|
bool* lengthTracking);
|
||||||
static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
static bool constructSameCompartment(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
||||||
static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
static bool constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
||||||
|
|
||||||
friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
|
friend bool ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args);
|
||||||
static DataViewObject*
|
static DataViewObject*
|
||||||
create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
||||||
Handle<ArrayBufferObject*> arrayBuffer, JSObject* proto);
|
Handle<ArrayBufferObject*> arrayBuffer, JSObject* proto,
|
||||||
|
bool lengthTracking = false);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
|
|
||||||
static Value byteOffsetValue(DataViewObject* view) {
|
static Value byteOffsetValue(DataViewObject* view) {
|
||||||
Value v = view->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT);
|
return Int32Value(view->byteOffset());
|
||||||
MOZ_ASSERT(v.toInt32() >= 0);
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value byteLengthValue(DataViewObject* view) {
|
static Value byteLengthValue(DataViewObject* view) {
|
||||||
Value v = view->getFixedSlot(TypedArrayObject::LENGTH_SLOT);
|
return Int32Value(view->byteLength());
|
||||||
MOZ_ASSERT(v.toInt32() >= 0);
|
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static Value bufferValue(DataViewObject* view) {
|
static Value bufferValue(DataViewObject* view) {
|
||||||
|
|
@ -495,11 +551,17 @@ class DataViewObject : public NativeObject
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t byteOffset() const {
|
uint32_t byteOffset() const {
|
||||||
return byteOffsetValue(const_cast<DataViewObject*>(this)).toInt32();
|
if (isOutOfBounds())
|
||||||
|
return 0;
|
||||||
|
return byteOffsetMaybeOutOfBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t byteLength() const {
|
uint32_t byteLength() const {
|
||||||
return byteLengthValue(const_cast<DataViewObject*>(this)).toInt32();
|
if (isOutOfBounds())
|
||||||
|
return 0;
|
||||||
|
if (isLengthTracking())
|
||||||
|
return arrayBuffer().byteLength() - byteOffsetMaybeOutOfBounds();
|
||||||
|
return fixedByteLengthMaybeOutOfBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayBufferObject& arrayBuffer() const {
|
ArrayBufferObject& arrayBuffer() const {
|
||||||
|
|
@ -510,6 +572,39 @@ class DataViewObject : public NativeObject
|
||||||
return getPrivate();
|
return getPrivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isLengthTracking() const {
|
||||||
|
return getFixedSlot(TypedArrayObject::LENGTH_SLOT).toInt32() ==
|
||||||
|
TypedArrayObject::LENGTH_TRACKING;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t byteOffsetMaybeOutOfBounds() const {
|
||||||
|
Value v = getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT);
|
||||||
|
MOZ_ASSERT(v.toInt32() >= 0);
|
||||||
|
return v.toInt32();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t fixedByteLengthMaybeOutOfBounds() const {
|
||||||
|
int32_t length = getFixedSlot(TypedArrayObject::LENGTH_SLOT).toInt32();
|
||||||
|
MOZ_ASSERT(length >= 0);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOutOfBounds() const {
|
||||||
|
const ArrayBufferObject& buffer = arrayBuffer();
|
||||||
|
if (buffer.isDetached())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
uint32_t bufferByteLength = buffer.byteLength();
|
||||||
|
uint32_t offset = byteOffsetMaybeOutOfBounds();
|
||||||
|
if (offset > bufferByteLength)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (isLengthTracking())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return fixedByteLengthMaybeOutOfBounds() > bufferByteLength - offset;
|
||||||
|
}
|
||||||
|
|
||||||
static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
|
static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
static bool getInt8Impl(JSContext* cx, const CallArgs& args);
|
static bool getInt8Impl(JSContext* cx, const CallArgs& args);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue