mirror of
https://github.com/roytam1/basilisk55.git
synced 2026-05-26 15:02:46 +00:00
ported from UXP: Implement ES2024 ArrayBuffer transfer APIs (3be309fa)
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
// |reftest| skip-if(!ArrayBuffer.prototype.transfer)
|
||||
|
||||
var fixed = new ArrayBuffer(4);
|
||||
assertEq(fixed.byteLength, 4);
|
||||
assertEq(fixed.maxByteLength, 4);
|
||||
assertEq(fixed.resizable, false);
|
||||
assertEq(fixed.detached, false);
|
||||
assertThrowsInstanceOf(() => fixed.resize(2), TypeError);
|
||||
assertEq(ArrayBuffer.prototype.transfer.length, 0);
|
||||
assertEq(ArrayBuffer.prototype.transferToFixedLength.length, 0);
|
||||
var resizeArgumentConverted = false;
|
||||
assertThrowsInstanceOf(() => fixed.resize({ valueOf() { resizeArgumentConverted = true; return 1; } }),
|
||||
TypeError);
|
||||
assertEq(resizeArgumentConverted, false);
|
||||
|
||||
var resizable = new ArrayBuffer(4, { maxByteLength: 8 });
|
||||
assertEq(resizable.byteLength, 4);
|
||||
assertEq(resizable.maxByteLength, 8);
|
||||
assertEq(resizable.resizable, true);
|
||||
|
||||
var bytes = new Uint8Array(resizable);
|
||||
bytes[0] = 11;
|
||||
bytes[3] = 44;
|
||||
resizable.resize(6);
|
||||
assertEq(resizable.byteLength, 6);
|
||||
assertEq(new Uint8Array(resizable)[0], 11);
|
||||
assertEq(new Uint8Array(resizable)[3], 44);
|
||||
assertEq(new Uint8Array(resizable)[4], 0);
|
||||
assertThrowsInstanceOf(() => resizable.resize(9), RangeError);
|
||||
|
||||
var source = new ArrayBuffer(4);
|
||||
var sourceBytes = new Uint8Array(source);
|
||||
sourceBytes[0] = 1;
|
||||
sourceBytes[1] = 2;
|
||||
var sourceView = new Uint8Array(source);
|
||||
var moved = source.transfer(6);
|
||||
assertEq(source.detached, true);
|
||||
assertEq(source.byteLength, 0);
|
||||
assertEq(source.maxByteLength, 0);
|
||||
assertEq(sourceView.length, 0);
|
||||
assertEq(moved.byteLength, 6);
|
||||
assertEq(moved.resizable, false);
|
||||
assertEq(moved.maxByteLength, 6);
|
||||
assertEq(new Uint8Array(moved)[0], 1);
|
||||
assertEq(new Uint8Array(moved)[1], 2);
|
||||
assertEq(new Uint8Array(moved)[4], 0);
|
||||
|
||||
var resizableSource = new ArrayBuffer(4, { maxByteLength: 8 });
|
||||
new Uint8Array(resizableSource)[0] = 7;
|
||||
var resizableMoved = resizableSource.transfer();
|
||||
assertEq(resizableSource.detached, true);
|
||||
assertEq(resizableSource.resizable, true);
|
||||
assertEq(resizableMoved.byteLength, 4);
|
||||
assertEq(resizableMoved.maxByteLength, 8);
|
||||
assertEq(resizableMoved.resizable, true);
|
||||
assertEq(new Uint8Array(resizableMoved)[0], 7);
|
||||
|
||||
var fixedMoved = resizableMoved.transferToFixedLength(10);
|
||||
assertEq(resizableMoved.detached, true);
|
||||
assertEq(fixedMoved.byteLength, 10);
|
||||
assertEq(fixedMoved.maxByteLength, 10);
|
||||
assertEq(fixedMoved.resizable, false);
|
||||
assertEq(new Uint8Array(fixedMoved)[0], 7);
|
||||
|
||||
assertThrowsInstanceOf(() => new ArrayBuffer(4, { maxByteLength: 3 }), RangeError);
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(0, 0);
|
||||
@@ -22,6 +22,7 @@
|
||||
# include <valgrind/memcheck.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include "jsapi.h"
|
||||
#include "jsarray.h"
|
||||
#include "jscntxt.h"
|
||||
@@ -44,6 +45,7 @@
|
||||
#include "vm/GlobalObject.h"
|
||||
#include "vm/Interpreter.h"
|
||||
#include "vm/SharedArrayObject.h"
|
||||
#include "vm/TypedArrayObject.h"
|
||||
#include "vm/WrapperObject.h"
|
||||
#include "wasm/WasmSignalHandlers.h"
|
||||
#include "wasm/WasmTypes.h"
|
||||
@@ -131,12 +133,18 @@ static const JSPropertySpec static_properties[] = {
|
||||
|
||||
|
||||
static const JSFunctionSpec prototype_functions[] = {
|
||||
JS_FN("resize", ArrayBufferObject::fun_resize, 1, 0),
|
||||
JS_SELF_HOSTED_FN("slice", "ArrayBufferSlice", 2, 0),
|
||||
JS_FN("transfer", ArrayBufferObject::fun_transfer, 0, 0),
|
||||
JS_FN("transferToFixedLength", ArrayBufferObject::fun_transferToFixedLength, 0, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
static const JSPropertySpec prototype_properties[] = {
|
||||
JS_PSG("byteLength", ArrayBufferObject::byteLengthGetter, 0),
|
||||
JS_PSG("detached", ArrayBufferObject::detachedGetter, 0),
|
||||
JS_PSG("maxByteLength", ArrayBufferObject::maxByteLengthGetter, 0),
|
||||
JS_PSG("resizable", ArrayBufferObject::resizableGetter, 0),
|
||||
JS_STRING_SYM_PS(toStringTag, "ArrayBuffer", JSPROP_READONLY),
|
||||
JS_PS_END
|
||||
};
|
||||
@@ -252,6 +260,52 @@ ArrayBufferObject::byteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
return CallNonGenericMethod<IsArrayBuffer, byteLengthGetterImpl>(cx, args);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ArrayBufferObject::detachedGetterImpl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
||||
args.rval().setBoolean(args.thisv().toObject().as<ArrayBufferObject>().isDetached());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::detachedGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsArrayBuffer, detachedGetterImpl>(cx, args);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
||||
ArrayBufferObject& buffer = args.thisv().toObject().as<ArrayBufferObject>();
|
||||
args.rval().setInt32(buffer.isDetached() ? 0 : int32_t(buffer.maxByteLength()));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsArrayBuffer, maxByteLengthGetterImpl>(cx, args);
|
||||
}
|
||||
|
||||
MOZ_ALWAYS_INLINE bool
|
||||
ArrayBufferObject::resizableGetterImpl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
||||
args.rval().setBoolean(args.thisv().toObject().as<ArrayBufferObject>().isResizable());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::resizableGetter(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsArrayBuffer, resizableGetterImpl>(cx, args);
|
||||
}
|
||||
|
||||
/*
|
||||
* ArrayBuffer.isView(obj); ES6 (Dec 2013 draft) 24.1.3.1
|
||||
*/
|
||||
@@ -264,6 +318,39 @@ ArrayBufferObject::fun_isView(JSContext* cx, unsigned argc, Value* vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetArrayBufferMaxByteLengthOption(JSContext* cx, HandleValue options,
|
||||
uint32_t byteLength, uint32_t* maxByteLength,
|
||||
bool* resizable)
|
||||
{
|
||||
*maxByteLength = byteLength;
|
||||
*resizable = false;
|
||||
|
||||
if (!options.isObject())
|
||||
return true;
|
||||
|
||||
RootedObject opts(cx, &options.toObject());
|
||||
RootedValue maxByteLengthValue(cx);
|
||||
if (!GetProperty(cx, opts, opts, cx->names().maxByteLength, &maxByteLengthValue))
|
||||
return false;
|
||||
|
||||
if (maxByteLengthValue.isUndefined())
|
||||
return true;
|
||||
|
||||
uint64_t max;
|
||||
if (!ToIndex(cx, maxByteLengthValue, &max))
|
||||
return false;
|
||||
|
||||
if (max > INT32_MAX || max < byteLength) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
*maxByteLength = uint32_t(max);
|
||||
*resizable = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ES2017 draft 24.1.2.1
|
||||
bool
|
||||
ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
@@ -280,6 +367,15 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
return false;
|
||||
|
||||
// Step 3.
|
||||
uint32_t maxByteLength;
|
||||
bool resizable;
|
||||
if (!GetArrayBufferMaxByteLengthOption(cx, args.get(1), uint32_t(byteLength),
|
||||
&maxByteLength, &resizable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Step 4.
|
||||
RootedObject proto(cx);
|
||||
RootedObject newTarget(cx, &args.newTarget().toObject());
|
||||
if (!GetPrototypeFromConstructor(cx, newTarget, &proto))
|
||||
@@ -293,7 +389,8 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
}
|
||||
|
||||
// 24.1.1.1, steps 1 and 4-6.
|
||||
JSObject* bufobj = create(cx, uint32_t(byteLength), proto);
|
||||
JSObject* bufobj = create(cx, uint32_t(byteLength), BufferContents::createPlain(nullptr),
|
||||
OwnsData, proto, GenericObject, maxByteLength, resizable);
|
||||
if (!bufobj)
|
||||
return false;
|
||||
args.rval().setObject(*bufobj);
|
||||
@@ -303,13 +400,57 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
|
||||
static ArrayBufferObject::BufferContents
|
||||
AllocateArrayBufferContents(JSContext* cx, uint32_t nbytes)
|
||||
{
|
||||
uint8_t* p = cx->runtime()->pod_callocCanGC<uint8_t>(nbytes);
|
||||
uint8_t* p = cx->runtime()->pod_callocCanGC<uint8_t>(nbytes ? nbytes : 1);
|
||||
if (!p)
|
||||
ReportOutOfMemory(cx);
|
||||
|
||||
return ArrayBufferObject::BufferContents::create<ArrayBufferObject::PLAIN>(p);
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportArrayBufferNotResizable(JSContext* cx)
|
||||
{
|
||||
JS_ReportErrorASCII(cx, "ArrayBuffer is not resizable");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportArrayBufferCannotDetach(JSContext* cx)
|
||||
{
|
||||
JS_ReportErrorASCII(cx, "ArrayBuffer cannot be detached");
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ReportArrayBufferLengthOutOfRange(JSContext* cx)
|
||||
{
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
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();
|
||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
||||
}
|
||||
|
||||
if (view->is<TypedArrayObject>()) {
|
||||
TypedArrayObject& typedArray = view->as<TypedArrayObject>();
|
||||
if (typedArray.isSharedMemory())
|
||||
return true;
|
||||
uint32_t byteOffset = typedArray.byteOffset();
|
||||
uint32_t byteLength = typedArray.byteLength();
|
||||
return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset;
|
||||
}
|
||||
|
||||
// Outline typed objects don't have a recoverable fixed byte range here.
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
NoteViewBufferWasDetached(ArrayBufferViewObject* view,
|
||||
ArrayBufferObject::BufferContents newContents,
|
||||
@@ -435,6 +576,173 @@ ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents,
|
||||
changeViewContents(cx, firstView(), oldDataPointer, newContents);
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::changeContentsForResize(JSContext* cx, BufferContents newContents,
|
||||
OwnsState ownsState, uint32_t newByteLength)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(!isWasm());
|
||||
MOZ_ASSERT(!forInlineTypedObject());
|
||||
|
||||
uint8_t* oldDataPointer = dataPointer();
|
||||
setNewData(cx->runtime()->defaultFreeOp(), newContents, ownsState);
|
||||
|
||||
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);
|
||||
else
|
||||
NoteViewBufferWasDetached(view, newContents, cx);
|
||||
}
|
||||
}
|
||||
|
||||
if (firstView()) {
|
||||
if (ArrayBufferViewFits(firstView(), newByteLength))
|
||||
changeViewContents(cx, firstView(), oldDataPointer, newContents);
|
||||
else
|
||||
NoteViewBufferWasDetached(firstView(), newContents, cx);
|
||||
}
|
||||
|
||||
setByteLength(newByteLength);
|
||||
}
|
||||
|
||||
static bool
|
||||
ResizeArrayBuffer(JSContext* cx, Handle<ArrayBufferObject*> buffer, uint32_t newByteLength)
|
||||
{
|
||||
if (!buffer->isResizable())
|
||||
return ReportArrayBufferNotResizable(cx);
|
||||
|
||||
if (buffer->isDetached()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newByteLength > buffer->maxByteLength())
|
||||
return ReportArrayBufferLengthOutOfRange(cx);
|
||||
|
||||
if (!buffer->isPlain() || buffer->isPreparedForAsmJS() || buffer->forInlineTypedObject())
|
||||
return ReportArrayBufferCannotDetach(cx);
|
||||
|
||||
if (newByteLength == buffer->byteLength())
|
||||
return true;
|
||||
|
||||
ArrayBufferObject::BufferContents newContents = AllocateArrayBufferContents(cx, newByteLength);
|
||||
if (!newContents)
|
||||
return false;
|
||||
|
||||
uint32_t copyLength = std::min(newByteLength, buffer->byteLength());
|
||||
if (copyLength > 0)
|
||||
memcpy(newContents.data(), buffer->dataPointer(), copyLength);
|
||||
|
||||
buffer->changeContentsForResize(cx, newContents, ArrayBufferObject::OwnsData, newByteLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::fun_resize_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
||||
|
||||
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
||||
if (!buffer->isResizable())
|
||||
return ReportArrayBufferNotResizable(cx);
|
||||
|
||||
uint64_t newByteLength;
|
||||
if (!ToIndex(cx, args.get(0), &newByteLength))
|
||||
return false;
|
||||
if (newByteLength > INT32_MAX)
|
||||
return ReportArrayBufferLengthOutOfRange(cx);
|
||||
|
||||
if (!ResizeArrayBuffer(cx, buffer, uint32_t(newByteLength)))
|
||||
return false;
|
||||
|
||||
args.rval().setUndefined();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::fun_resize(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsArrayBuffer, fun_resize_impl>(cx, args);
|
||||
}
|
||||
|
||||
static bool
|
||||
ArrayBufferTransfer(JSContext* cx, const CallArgs& args, bool preserveResizability)
|
||||
{
|
||||
MOZ_ASSERT(IsArrayBuffer(args.thisv()));
|
||||
Rooted<ArrayBufferObject*> buffer(cx, &args.thisv().toObject().as<ArrayBufferObject>());
|
||||
|
||||
uint32_t newByteLength = buffer->byteLength();
|
||||
if (args.hasDefined(0)) {
|
||||
uint64_t newLength;
|
||||
if (!ToIndex(cx, args.get(0), &newLength))
|
||||
return false;
|
||||
if (newLength > INT32_MAX)
|
||||
return ReportArrayBufferLengthOutOfRange(cx);
|
||||
newByteLength = uint32_t(newLength);
|
||||
}
|
||||
|
||||
if (buffer->isDetached()) {
|
||||
JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (buffer->isWasm() || buffer->isPreparedForAsmJS())
|
||||
return ReportArrayBufferCannotDetach(cx);
|
||||
|
||||
bool newResizable = preserveResizability && buffer->isResizable();
|
||||
uint32_t newMaxByteLength = newResizable ? buffer->maxByteLength() : newByteLength;
|
||||
if (newResizable && newByteLength > newMaxByteLength)
|
||||
return ReportArrayBufferLengthOutOfRange(cx);
|
||||
|
||||
Rooted<ArrayBufferObject*> newBuffer(cx,
|
||||
ArrayBufferObject::create(cx, newByteLength, ArrayBufferObject::BufferContents::createPlain(nullptr),
|
||||
ArrayBufferObject::OwnsData, nullptr, GenericObject,
|
||||
newMaxByteLength, newResizable));
|
||||
if (!newBuffer)
|
||||
return false;
|
||||
|
||||
uint32_t copyLength = std::min(newByteLength, buffer->byteLength());
|
||||
if (copyLength > 0)
|
||||
memcpy(newBuffer->dataPointer(), buffer->dataPointer(), copyLength);
|
||||
|
||||
ArrayBufferObject::BufferContents detachedContents =
|
||||
buffer->hasStealableContents() ? ArrayBufferObject::BufferContents::createPlain(nullptr)
|
||||
: buffer->contents();
|
||||
ArrayBufferObject::detach(cx, buffer, detachedContents);
|
||||
|
||||
args.rval().setObject(*newBuffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::fun_transfer_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
return ArrayBufferTransfer(cx, args, true);
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::fun_transfer(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsArrayBuffer, fun_transfer_impl>(cx, args);
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::fun_transferToFixedLength_impl(JSContext* cx, const CallArgs& args)
|
||||
{
|
||||
return ArrayBufferTransfer(cx, args, false);
|
||||
}
|
||||
|
||||
bool
|
||||
ArrayBufferObject::fun_transferToFixedLength(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
return CallNonGenericMethod<IsArrayBuffer, fun_transferToFixedLength_impl>(cx, args);
|
||||
}
|
||||
|
||||
/*
|
||||
* Wasm Raw Buf Linear Memory Structure
|
||||
*
|
||||
@@ -888,6 +1196,14 @@ ArrayBufferObject::byteLength() const
|
||||
return getSlot(BYTE_LENGTH_SLOT).toInt32();
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ArrayBufferObject::maxByteLength() const
|
||||
{
|
||||
if (!isResizable())
|
||||
return byteLength();
|
||||
return getSlot(MAX_BYTE_LENGTH_SLOT).toInt32();
|
||||
}
|
||||
|
||||
void
|
||||
ArrayBufferObject::setByteLength(uint32_t length)
|
||||
{
|
||||
@@ -1036,9 +1352,12 @@ ArrayBufferObject*
|
||||
ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents contents,
|
||||
OwnsState ownsState /* = OwnsData */,
|
||||
HandleObject proto /* = nullptr */,
|
||||
NewObjectKind newKind /* = GenericObject */)
|
||||
NewObjectKind newKind /* = GenericObject */,
|
||||
uint32_t maxByteLength /* = 0 */,
|
||||
bool resizable /* = false */)
|
||||
{
|
||||
MOZ_ASSERT_IF(contents.kind() == MAPPED, contents);
|
||||
MOZ_ASSERT_IF(resizable, maxByteLength >= nbytes);
|
||||
|
||||
// 24.1.1.1, step 3 (Inlined 6.2.6.1 CreateByteDataBlock, step 2).
|
||||
// Refuse to allocate too large buffers, currently limited to ~2 GiB.
|
||||
@@ -1069,7 +1388,7 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
|
||||
MOZ_ASSERT(ownsState == OwnsData);
|
||||
size_t usableSlots = NativeObject::MAX_FIXED_SLOTS - reservedSlots;
|
||||
if (nbytes <= usableSlots * sizeof(Value)) {
|
||||
int newSlots = (nbytes - 1) / sizeof(Value) + 1;
|
||||
int newSlots = nbytes == 0 ? 0 : (nbytes - 1) / sizeof(Value) + 1;
|
||||
MOZ_ASSERT(int(nbytes) <= newSlots * int(sizeof(Value)));
|
||||
nslots = reservedSlots + newSlots;
|
||||
contents = BufferContents::createPlain(nullptr);
|
||||
@@ -1099,9 +1418,10 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content
|
||||
if (!contents) {
|
||||
void* data = obj->inlineDataPointer();
|
||||
memset(data, 0, nbytes);
|
||||
obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData);
|
||||
obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData,
|
||||
maxByteLength, resizable);
|
||||
} else {
|
||||
obj->initialize(nbytes, contents, ownsState);
|
||||
obj->initialize(nbytes, contents, ownsState, maxByteLength, resizable);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
@@ -128,15 +128,22 @@ typedef MutableHandle<ArrayBufferObjectMaybeShared*> MutableHandleArrayBufferObj
|
||||
class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
{
|
||||
static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
||||
static bool maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args);
|
||||
static bool resizableGetterImpl(JSContext* cx, const CallArgs& args);
|
||||
static bool detachedGetterImpl(JSContext* cx, const CallArgs& args);
|
||||
static bool fun_slice_impl(JSContext* cx, const CallArgs& args);
|
||||
static bool fun_resize_impl(JSContext* cx, const CallArgs& args);
|
||||
static bool fun_transfer_impl(JSContext* cx, const CallArgs& args);
|
||||
static bool fun_transferToFixedLength_impl(JSContext* cx, const CallArgs& args);
|
||||
|
||||
public:
|
||||
static const uint8_t DATA_SLOT = 0;
|
||||
static const uint8_t BYTE_LENGTH_SLOT = 1;
|
||||
static const uint8_t FIRST_VIEW_SLOT = 2;
|
||||
static const uint8_t FLAGS_SLOT = 3;
|
||||
static const uint8_t MAX_BYTE_LENGTH_SLOT = 4;
|
||||
|
||||
static const uint8_t RESERVED_SLOTS = 4;
|
||||
static const uint8_t RESERVED_SLOTS = 5;
|
||||
|
||||
static const size_t ARRAY_BUFFER_ALIGNMENT = 8;
|
||||
|
||||
@@ -189,7 +196,11 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
|
||||
// This PLAIN or WASM buffer has been prepared for asm.js and cannot
|
||||
// henceforth be transferred/detached.
|
||||
FOR_ASMJS = 0x40
|
||||
FOR_ASMJS = 0x40,
|
||||
|
||||
// This buffer was created with [[ArrayBufferMaxByteLength]] and can
|
||||
// be resized up to that maximum.
|
||||
RESIZABLE = 0x80
|
||||
};
|
||||
|
||||
static_assert(JS_ARRAYBUFFER_DETACHED_FLAG == DETACHED,
|
||||
@@ -231,8 +242,14 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
static const Class protoClass_;
|
||||
|
||||
static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool maxByteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool resizableGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool detachedGetter(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
static bool fun_slice(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool fun_resize(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool fun_transfer(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool fun_transferToFixedLength(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
static bool fun_isView(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
@@ -244,7 +261,9 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
BufferContents contents,
|
||||
OwnsState ownsState = OwnsData,
|
||||
HandleObject proto = nullptr,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
NewObjectKind newKind = GenericObject,
|
||||
uint32_t maxByteLength = 0,
|
||||
bool resizable = false);
|
||||
static ArrayBufferObject* create(JSContext* cx, uint32_t nbytes,
|
||||
HandleObject proto = nullptr,
|
||||
NewObjectKind newKind = GenericObject);
|
||||
@@ -291,6 +310,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
|
||||
void setNewData(FreeOp* fop, BufferContents newContents, OwnsState ownsState);
|
||||
void changeContents(JSContext* cx, BufferContents newContents, OwnsState ownsState);
|
||||
void changeContentsForResize(JSContext* cx, BufferContents newContents,
|
||||
OwnsState ownsState, uint32_t newByteLength);
|
||||
|
||||
// Detach this buffer from its original memory. (This necessarily makes
|
||||
// views of this buffer unusable for modifying that original memory.)
|
||||
@@ -308,6 +329,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
uint8_t* dataPointer() const;
|
||||
SharedMem<uint8_t*> dataPointerShared() const;
|
||||
uint32_t byteLength() const;
|
||||
uint32_t maxByteLength() const;
|
||||
|
||||
BufferContents contents() const {
|
||||
return BufferContents(dataPointer(), bufferKind());
|
||||
@@ -331,6 +353,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
bool isWasm() const { return bufferKind() == WASM; }
|
||||
bool isMapped() const { return bufferKind() == MAPPED; }
|
||||
bool isDetached() const { return flags() & DETACHED; }
|
||||
bool isResizable() const { return flags() & RESIZABLE; }
|
||||
bool isPreparedForAsmJS() const { return flags() & FOR_ASMJS; }
|
||||
|
||||
// WebAssembly support:
|
||||
@@ -388,12 +411,17 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
|
||||
void setIsDetached() { setFlags(flags() | DETACHED); }
|
||||
void setIsPreparedForAsmJS() { setFlags(flags() | FOR_ASMJS); }
|
||||
void setIsResizable() { setFlags(flags() | RESIZABLE); }
|
||||
|
||||
void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState) {
|
||||
void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState,
|
||||
uint32_t maxByteLength = 0, bool resizable = false) {
|
||||
setByteLength(byteLength);
|
||||
setFlags(0);
|
||||
setFixedSlot(MAX_BYTE_LENGTH_SLOT, Int32Value(maxByteLength ? maxByteLength : byteLength));
|
||||
setFirstView(nullptr);
|
||||
setDataPointer(contents, ownsState);
|
||||
if (resizable)
|
||||
setIsResizable();
|
||||
}
|
||||
|
||||
// Note: initialize() may be called after initEmpty(); initEmpty() must
|
||||
@@ -401,6 +429,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
||||
void initEmpty() {
|
||||
setByteLength(0);
|
||||
setFlags(0);
|
||||
setFixedSlot(MAX_BYTE_LENGTH_SLOT, Int32Value(0));
|
||||
setFirstView(nullptr);
|
||||
setDataPointer(BufferContents::createPlain(nullptr), DoesntOwnData);
|
||||
}
|
||||
|
||||
@@ -247,6 +247,7 @@
|
||||
macro(lookupSetter, lookupSetter, "__lookupSetter__") \
|
||||
macro(MapConstructorInit, MapConstructorInit, "MapConstructorInit") \
|
||||
macro(MapIterator, MapIterator, "Map Iterator") \
|
||||
macro(maxByteLength, maxByteLength, "maxByteLength") \
|
||||
macro(maximumFractionDigits, maximumFractionDigits, "maximumFractionDigits") \
|
||||
macro(maximumSignificantDigits, maximumSignificantDigits, "maximumSignificantDigits") \
|
||||
macro(message, message, "message") \
|
||||
|
||||
Reference in New Issue
Block a user