diff --git a/js/src/tests/non262/ArrayBuffer/resizable-transfer.js b/js/src/tests/non262/ArrayBuffer/resizable-transfer.js new file mode 100644 index 0000000000..b9f9dbb13a --- /dev/null +++ b/js/src/tests/non262/ArrayBuffer/resizable-transfer.js @@ -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); diff --git a/js/src/vm/ArrayBufferObject.cpp b/js/src/vm/ArrayBufferObject.cpp index e43edab690..7eab0f0e24 100644 --- a/js/src/vm/ArrayBufferObject.cpp +++ b/js/src/vm/ArrayBufferObject.cpp @@ -22,6 +22,7 @@ # include #endif +#include #include "jsapi.h" #include "jsarray.h" #include "jscntxt.h" @@ -45,6 +46,7 @@ #include "vm/Interpreter.h" #include "vm/SelfHosting.h" #include "vm/SharedArrayObject.h" +#include "vm/TypedArrayObject.h" #include "vm/WrapperObject.h" #include "wasm/WasmSignalHandlers.h" #include "wasm/WasmTypes.h" @@ -170,12 +172,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(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().isDetached()); + return true; +} + +bool +ArrayBufferObject::detachedGetter(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(cx, args); +} + +MOZ_ALWAYS_INLINE bool +ArrayBufferObject::maxByteLengthGetterImpl(JSContext* cx, const CallArgs& args) +{ + MOZ_ASSERT(IsArrayBuffer(args.thisv())); + ArrayBufferObject& buffer = args.thisv().toObject().as(); + 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(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().isResizable()); + return true; +} + +bool +ArrayBufferObject::resizableGetter(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return CallNonGenericMethod(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 @@ -287,12 +374,22 @@ ArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) } // 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)) return false; - 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); @@ -302,13 +399,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(nbytes); + uint8_t* p = cx->runtime()->pod_callocCanGC(nbytes ? nbytes : 1); if (!p) ReportOutOfMemory(cx); return ArrayBufferObject::BufferContents::create(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& dataView = view->as(); + uint32_t byteOffset = dataView.byteOffset(); + uint32_t byteLength = dataView.byteLength(); + return byteOffset <= newByteLength && byteLength <= newByteLength - byteOffset; + } + + if (view->is()) { + TypedArrayObject& typedArray = view->as(); + 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, @@ -434,6 +575,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 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 buffer(cx, &args.thisv().toObject().as()); + 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(cx, args); +} + +static bool +ArrayBufferTransfer(JSContext* cx, const CallArgs& args, bool preserveResizability) +{ + MOZ_ASSERT(IsArrayBuffer(args.thisv())); + Rooted buffer(cx, &args.thisv().toObject().as()); + + 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 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(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(cx, args); +} + /* * Wasm Raw Buf Linear Memory Structure * @@ -887,6 +1195,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) { @@ -1035,9 +1351,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. @@ -1068,7 +1387,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); @@ -1097,10 +1416,11 @@ ArrayBufferObject::create(JSContext* cx, uint32_t nbytes, BufferContents content if (!contents) { void* data = obj->inlineDataPointer(); - js_memset(data, 0, nbytes); - obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData); + memset(data, 0, nbytes); + obj->initialize(nbytes, BufferContents::createPlain(data), DoesntOwnData, + maxByteLength, resizable); } else { - obj->initialize(nbytes, contents, ownsState); + obj->initialize(nbytes, contents, ownsState, maxByteLength, resizable); } return obj; diff --git a/js/src/vm/ArrayBufferObject.h b/js/src/vm/ArrayBufferObject.h index f4010c6c77..d0d83c431d 100644 --- a/js/src/vm/ArrayBufferObject.h +++ b/js/src/vm/ArrayBufferObject.h @@ -128,15 +128,22 @@ typedef MutableHandle 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, @@ -230,8 +241,14 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared static const Class class_; 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); @@ -243,7 +260,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); @@ -294,6 +313,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.) @@ -311,6 +332,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared uint8_t* dataPointer() const; SharedMem dataPointerShared() const; uint32_t byteLength() const; + uint32_t maxByteLength() const; BufferContents contents() const { return BufferContents(dataPointer(), bufferKind()); @@ -334,6 +356,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: @@ -391,12 +414,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 @@ -404,6 +432,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); } diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h index 6edd49c6c0..82d49b201a 100644 --- a/js/src/vm/CommonPropertyNames.h +++ b/js/src/vm/CommonPropertyNames.h @@ -248,6 +248,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") \