mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-05 23:38:38 +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>()) {
|
||||
DataViewObject& dataView = view->as<DataViewObject>();
|
||||
uint32_t byteOffset = dataView.byteOffset();
|
||||
uint32_t byteLength = dataView.byteLength();
|
||||
uint32_t byteOffset = dataView.byteOffsetMaybeOutOfBounds();
|
||||
if (byteOffset > newByteLength)
|
||||
return false;
|
||||
uint32_t byteLength = dataView.isLengthTracking()
|
||||
? newByteLength - byteOffset
|
||||
: dataView.fixedByteLengthMaybeOutOfBounds();
|
||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
||||
}
|
||||
|
||||
|
|
@ -441,8 +445,13 @@ ArrayBufferViewFits(ArrayBufferViewObject* view, uint32_t newByteLength)
|
|||
TypedArrayObject& typedArray = view->as<TypedArrayObject>();
|
||||
if (typedArray.isSharedMemory())
|
||||
return true;
|
||||
uint32_t byteOffset = typedArray.byteOffset();
|
||||
uint32_t byteLength = typedArray.byteLength();
|
||||
uint32_t byteOffset = typedArray.byteOffsetMaybeOutOfBounds();
|
||||
if (byteOffset > newByteLength)
|
||||
return false;
|
||||
uint32_t byteLength = typedArray.isLengthTracking()
|
||||
? newByteLength - byteOffset
|
||||
: typedArray.fixedLengthMaybeOutOfBounds() *
|
||||
typedArray.bytesPerElement();
|
||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +541,8 @@ ArrayBufferObject::setNewData(FreeOp* fop, BufferContents newContents, OwnsState
|
|||
|
||||
void
|
||||
ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
||||
uint8_t* oldDataPointer, BufferContents newContents)
|
||||
uint8_t* oldDataPointer, BufferContents newContents,
|
||||
uint32_t newByteLength)
|
||||
{
|
||||
MOZ_ASSERT(!view->isSharedMemory());
|
||||
|
||||
|
|
@ -543,7 +553,18 @@ ArrayBufferObject::changeViewContents(JSContext* cx, ArrayBufferViewObject* view
|
|||
uint8_t* viewDataPointer = view->dataPointerUnshared(nogc);
|
||||
if (viewDataPointer) {
|
||||
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;
|
||||
view->setDataPointerUnshared(viewDataPointer);
|
||||
}
|
||||
|
|
@ -569,10 +590,10 @@ ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents,
|
|||
auto& innerViews = cx->compartment()->innerViews.get();
|
||||
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
||||
for (size_t i = 0; i < views->length(); i++)
|
||||
changeViewContents(cx, (*views)[i], oldDataPointer, newContents);
|
||||
changeViewContents(cx, (*views)[i], oldDataPointer, newContents, byteLength());
|
||||
}
|
||||
if (firstView())
|
||||
changeViewContents(cx, firstView(), oldDataPointer, newContents);
|
||||
changeViewContents(cx, firstView(), oldDataPointer, newContents, byteLength());
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -584,26 +605,29 @@ ArrayBufferObject::changeContentsForResize(JSContext* cx, BufferContents newCont
|
|||
|
||||
uint8_t* oldDataPointer = dataPointer();
|
||||
setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
|
||||
setByteLength(newByteLength);
|
||||
|
||||
auto& innerViews = cx->compartment()->innerViews.get();
|
||||
if (InnerViewTable::ViewVector* views = innerViews.maybeViewsUnbarriered(this)) {
|
||||
for (size_t i = 0; i < views->length(); i++) {
|
||||
ArrayBufferViewObject* view = (*views)[i];
|
||||
if (ArrayBufferViewFits(view, newByteLength))
|
||||
changeViewContents(cx, view, oldDataPointer, newContents);
|
||||
if (view->is<DataViewObject>() || view->is<TypedArrayObject>())
|
||||
changeViewContents(cx, view, oldDataPointer, newContents, newByteLength);
|
||||
else if (ArrayBufferViewFits(view, newByteLength))
|
||||
changeViewContents(cx, view, oldDataPointer, newContents, newByteLength);
|
||||
else
|
||||
NoteViewBufferWasDetached(view, newContents, cx);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstView()) {
|
||||
if (ArrayBufferViewFits(firstView(), newByteLength))
|
||||
changeViewContents(cx, firstView(), oldDataPointer, newContents);
|
||||
if (firstView()->is<DataViewObject>() || firstView()->is<TypedArrayObject>())
|
||||
changeViewContents(cx, firstView(), oldDataPointer, newContents, newByteLength);
|
||||
else if (ArrayBufferViewFits(firstView(), newByteLength))
|
||||
changeViewContents(cx, firstView(), oldDataPointer, newContents, newByteLength);
|
||||
else
|
||||
NoteViewBufferWasDetached(firstView(), newContents, cx);
|
||||
}
|
||||
|
||||
setByteLength(newByteLength);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -1455,20 +1479,22 @@ ArrayBufferObject::createDataViewForThisImpl(JSContext* cx, const CallArgs& args
|
|||
/*
|
||||
* This method is only called for |DataView(alienBuf, ...)| which calls
|
||||
* this as |createDataViewForThis.call(alienBuf, byteOffset, byteLength,
|
||||
* DataView.prototype)|,
|
||||
* ergo there must be exactly 3 arguments.
|
||||
* DataView.prototype, lengthTracking)|,
|
||||
* 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 byteLength = args[1].toPrivateUint32();
|
||||
bool lengthTracking = args[3].toBoolean();
|
||||
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
||||
|
||||
/*
|
||||
* Pop off the passed-along prototype and delegate to normal DataViewObject
|
||||
* 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)
|
||||
return false;
|
||||
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
|
||||
// can only move during a compacting GC, in which case its
|
||||
// objectMoved hook has already updated the buffer's data pointer.
|
||||
if (offset > buf.byteLength())
|
||||
offset = 0;
|
||||
obj->initPrivate(buf.dataPointer() + offset);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -323,7 +323,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
|||
|
||||
private:
|
||||
void changeViewContents(JSContext* cx, ArrayBufferViewObject* view,
|
||||
uint8_t* oldDataPointer, BufferContents newContents);
|
||||
uint8_t* oldDataPointer, BufferContents newContents,
|
||||
uint32_t newByteLength);
|
||||
void setFirstView(ArrayBufferViewObject* view);
|
||||
|
||||
uint8_t* inlineDataPointer() const;
|
||||
|
|
|
|||
|
|
@ -484,8 +484,9 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
}
|
||||
|
||||
static TypedArrayObject*
|
||||
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer, uint32_t byteOffset, uint32_t len,
|
||||
HandleObject proto)
|
||||
makeInstance(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> buffer,
|
||||
uint32_t byteOffset, uint32_t len, HandleObject proto,
|
||||
bool lengthTracking = false)
|
||||
{
|
||||
MOZ_ASSERT_IF(!buffer, byteOffset == 0);
|
||||
|
||||
|
|
@ -549,7 +550,9 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
#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));
|
||||
|
||||
#ifdef DEBUG
|
||||
|
|
@ -898,6 +901,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
return nullptr; // invalid byteOffset
|
||||
}
|
||||
|
||||
bool lengthTracking = false;
|
||||
uint32_t len;
|
||||
if (lengthInt == -1) {
|
||||
len = (buffer->byteLength() - byteOffset) / sizeof(NativeType);
|
||||
|
|
@ -906,6 +910,12 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS);
|
||||
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 {
|
||||
len = uint32_t(lengthInt);
|
||||
}
|
||||
|
|
@ -924,7 +934,7 @@ class TypedArrayObjectTemplate : public TypedArrayObject
|
|||
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
|
||||
|
|
@ -1859,7 +1869,8 @@ 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<ArrayBufferObject*> arrayBuffer, JSObject* protoArg,
|
||||
bool lengthTracking)
|
||||
{
|
||||
if (arrayBuffer->isDetached()) {
|
||||
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>();
|
||||
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.initPrivate(arrayBuffer->dataPointer() + byteOffset);
|
||||
|
||||
|
|
@ -1921,7 +1934,8 @@ DataViewObject::create(JSContext* cx, uint32_t byteOffset, uint32_t byteLength,
|
|||
|
||||
bool
|
||||
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)) {
|
||||
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));
|
||||
uint32_t byteOffset = 0;
|
||||
uint32_t byteLength = buffer->byteLength();
|
||||
bool lengthTracking = buffer->isResizable() && !args.hasDefined(2);
|
||||
|
||||
if (args.length() > 1) {
|
||||
if (!ToUint32(cx, args[1], &byteOffset))
|
||||
|
|
@ -1957,9 +1972,11 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
|
|||
|
||||
if (args.get(2).isUndefined()) {
|
||||
byteLength -= byteOffset;
|
||||
lengthTracking = buffer->isResizable();
|
||||
} else {
|
||||
if (!ToUint32(cx, args[2], &byteLength))
|
||||
return false;
|
||||
lengthTracking = false;
|
||||
if (byteLength > INT32_MAX) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
|
||||
JSMSG_ARG_INDEX_OUT_OF_RANGE, "2");
|
||||
|
|
@ -1983,6 +2000,7 @@ DataViewObject::getAndCheckConstructorArgs(JSContext* cx, JSObject* bufobj, cons
|
|||
|
||||
*byteOffsetPtr = byteOffset;
|
||||
*byteLengthPtr = byteLength;
|
||||
*lengthTrackingPtr = lengthTracking;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1994,7 +2012,8 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
|
|||
assertSameCompartment(cx, bufobj);
|
||||
|
||||
uint32_t byteOffset, byteLength;
|
||||
if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength))
|
||||
bool lengthTracking;
|
||||
if (!getAndCheckConstructorArgs(cx, bufobj, args, &byteOffset, &byteLength, &lengthTracking))
|
||||
return false;
|
||||
|
||||
RootedObject proto(cx);
|
||||
|
|
@ -2003,7 +2022,8 @@ DataViewObject::constructSameCompartment(JSContext* cx, HandleObject bufobj, con
|
|||
return false;
|
||||
|
||||
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)
|
||||
return false;
|
||||
args.rval().setObject(*obj);
|
||||
|
|
@ -2043,8 +2063,12 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
|
|||
|
||||
// NB: This entails the IsArrayBuffer check
|
||||
uint32_t byteOffset, byteLength;
|
||||
if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength))
|
||||
bool lengthTracking;
|
||||
if (!getAndCheckConstructorArgs(cx, unwrapped, args, &byteOffset, &byteLength,
|
||||
&lengthTracking))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure to get the [[Prototype]] for the created view from this
|
||||
// compartment.
|
||||
|
|
@ -2060,11 +2084,12 @@ DataViewObject::constructWrapped(JSContext* cx, HandleObject bufobj, const CallA
|
|||
return false;
|
||||
}
|
||||
|
||||
FixedInvokeArgs<3> args2(cx);
|
||||
FixedInvokeArgs<4> args2(cx);
|
||||
|
||||
args2[0].set(PrivateUint32Value(byteOffset));
|
||||
args2[1].set(PrivateUint32Value(byteLength));
|
||||
args2[2].setObject(*proto);
|
||||
args2[3].setBoolean(lengthTracking);
|
||||
|
||||
RootedValue fval(cx, global->createDataViewForThis());
|
||||
RootedValue thisv(cx, ObjectValue(*bufobj));
|
||||
|
|
|
|||
|
|
@ -52,6 +52,8 @@ class TypedArrayObject : public NativeObject
|
|||
"right buffer slot");
|
||||
|
||||
// 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_assert(LENGTH_SLOT == JS_TYPEDARRAYLAYOUT_LENGTH_SLOT,
|
||||
"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 int32_t LENGTH_TRACKING = -1;
|
||||
|
||||
#ifdef DEBUG
|
||||
static const uint8_t ZeroLengthArrayData = 0x4A;
|
||||
#endif
|
||||
|
|
@ -139,15 +143,13 @@ class TypedArrayObject : public NativeObject
|
|||
return tarr->getFixedSlot(BUFFER_SLOT);
|
||||
}
|
||||
static Value byteOffsetValue(TypedArrayObject* tarr) {
|
||||
Value v = tarr->getFixedSlot(BYTEOFFSET_SLOT);
|
||||
MOZ_ASSERT(v.toInt32() >= 0);
|
||||
return v;
|
||||
return Int32Value(tarr->byteOffset());
|
||||
}
|
||||
static Value byteLengthValue(TypedArrayObject* tarr) {
|
||||
return Int32Value(tarr->getFixedSlot(LENGTH_SLOT).toInt32() * tarr->bytesPerElement());
|
||||
return Int32Value(tarr->byteLength());
|
||||
}
|
||||
static Value lengthValue(TypedArrayObject* tarr) {
|
||||
return tarr->getFixedSlot(LENGTH_SLOT);
|
||||
return Int32Value(tarr->length());
|
||||
}
|
||||
|
||||
static bool
|
||||
|
|
@ -159,14 +161,70 @@ class TypedArrayObject : public NativeObject
|
|||
JSObject* bufferObject() const {
|
||||
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 {
|
||||
return byteOffsetValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
||||
if (isOutOfBounds())
|
||||
return 0;
|
||||
return byteOffsetMaybeOutOfBounds();
|
||||
}
|
||||
uint32_t byteLength() const {
|
||||
return byteLengthValue(const_cast<TypedArrayObject*>(this)).toInt32();
|
||||
return length() * bytesPerElement();
|
||||
}
|
||||
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;
|
||||
|
|
@ -466,28 +524,26 @@ class DataViewObject : public NativeObject
|
|||
defineGetter(JSContext* cx, PropertyName* name, HandleNativeObject proto);
|
||||
|
||||
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 constructWrapped(JSContext* cx, HandleObject bufobj, const CallArgs& args);
|
||||
|
||||
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<ArrayBufferObject*> arrayBuffer, JSObject* proto,
|
||||
bool lengthTracking = false);
|
||||
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
static Value byteOffsetValue(DataViewObject* view) {
|
||||
Value v = view->getFixedSlot(TypedArrayObject::BYTEOFFSET_SLOT);
|
||||
MOZ_ASSERT(v.toInt32() >= 0);
|
||||
return v;
|
||||
return Int32Value(view->byteOffset());
|
||||
}
|
||||
|
||||
static Value byteLengthValue(DataViewObject* view) {
|
||||
Value v = view->getFixedSlot(TypedArrayObject::LENGTH_SLOT);
|
||||
MOZ_ASSERT(v.toInt32() >= 0);
|
||||
return v;
|
||||
return Int32Value(view->byteLength());
|
||||
}
|
||||
|
||||
static Value bufferValue(DataViewObject* view) {
|
||||
|
|
@ -495,11 +551,17 @@ class DataViewObject : public NativeObject
|
|||
}
|
||||
|
||||
uint32_t byteOffset() const {
|
||||
return byteOffsetValue(const_cast<DataViewObject*>(this)).toInt32();
|
||||
if (isOutOfBounds())
|
||||
return 0;
|
||||
return byteOffsetMaybeOutOfBounds();
|
||||
}
|
||||
|
||||
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 {
|
||||
|
|
@ -510,6 +572,39 @@ class DataViewObject : public NativeObject
|
|||
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 getInt8Impl(JSContext* cx, const CallArgs& args);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue