45557b1f89
Bug: v8:7793 Change-Id: Id2a93f8ac8c512dbc5cdeb43a97e04d8d6684954 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2196130 Commit-Queue: Tobias Tebbi <tebbi@chromium.org> Reviewed-by: Jakob Gruber <jgruber@chromium.org> Cr-Commit-Position: refs/heads/master@{#67748}
836 lines
31 KiB
Plaintext
836 lines
31 KiB
Plaintext
// Copyright 2018 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include 'src/builtins/builtins-data-view-gen.h'
|
|
|
|
namespace data_view {
|
|
|
|
macro MakeDataViewGetterNameString(kind: constexpr ElementsKind): String {
|
|
if constexpr (kind == ElementsKind::UINT8_ELEMENTS) {
|
|
return 'DataView.prototype.getUint8';
|
|
} else if constexpr (kind == ElementsKind::INT8_ELEMENTS) {
|
|
return 'DataView.prototype.getInt8';
|
|
} else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) {
|
|
return 'DataView.prototype.getUint16';
|
|
} else if constexpr (kind == ElementsKind::INT16_ELEMENTS) {
|
|
return 'DataView.prototype.getInt16';
|
|
} else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
|
|
return 'DataView.prototype.getUint32';
|
|
} else if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
|
|
return 'DataView.prototype.getInt32';
|
|
} else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
|
|
return 'DataView.prototype.getFloat32';
|
|
} else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
|
|
return 'DataView.prototype.getFloat64';
|
|
} else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) {
|
|
return 'DataView.prototype.getBigInt64';
|
|
} else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) {
|
|
return 'DataView.prototype.getBigUint64';
|
|
} else {
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
macro MakeDataViewSetterNameString(kind: constexpr ElementsKind): String {
|
|
if constexpr (kind == ElementsKind::UINT8_ELEMENTS) {
|
|
return 'DataView.prototype.setUint8';
|
|
} else if constexpr (kind == ElementsKind::INT8_ELEMENTS) {
|
|
return 'DataView.prototype.setInt8';
|
|
} else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) {
|
|
return 'DataView.prototype.setUint16';
|
|
} else if constexpr (kind == ElementsKind::INT16_ELEMENTS) {
|
|
return 'DataView.prototype.setInt16';
|
|
} else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
|
|
return 'DataView.prototype.setUint32';
|
|
} else if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
|
|
return 'DataView.prototype.setInt32';
|
|
} else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
|
|
return 'DataView.prototype.setFloat32';
|
|
} else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
|
|
return 'DataView.prototype.setFloat64';
|
|
} else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) {
|
|
return 'DataView.prototype.setBigInt64';
|
|
} else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) {
|
|
return 'DataView.prototype.setBigUint64';
|
|
} else {
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
macro WasDetached(view: JSArrayBufferView): bool {
|
|
return IsDetachedBuffer(view.buffer);
|
|
}
|
|
|
|
macro ValidateDataView(context: Context, o: JSAny, method: String): JSDataView {
|
|
try {
|
|
return Cast<JSDataView>(o) otherwise CastError;
|
|
} label CastError {
|
|
ThrowTypeError(MessageTemplate::kIncompatibleMethodReceiver, method);
|
|
}
|
|
}
|
|
|
|
// ES6 section 24.2.4.1 get DataView.prototype.buffer
|
|
javascript builtin DataViewPrototypeGetBuffer(
|
|
js-implicit context: NativeContext,
|
|
receiver: JSAny)(...arguments): JSArrayBuffer {
|
|
const dataView: JSDataView =
|
|
ValidateDataView(context, receiver, 'get DataView.prototype.buffer');
|
|
return dataView.buffer;
|
|
}
|
|
|
|
// ES6 section 24.2.4.2 get DataView.prototype.byteLength
|
|
javascript builtin DataViewPrototypeGetByteLength(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
|
|
const dataView: JSDataView =
|
|
ValidateDataView(context, receiver, 'get DataView.prototype.byte_length');
|
|
if (WasDetached(dataView)) {
|
|
// TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
|
|
// here if the JSArrayBuffer of the {dataView} was detached.
|
|
return 0;
|
|
}
|
|
return Convert<Number>(dataView.byte_length);
|
|
}
|
|
|
|
// ES6 section 24.2.4.3 get DataView.prototype.byteOffset
|
|
javascript builtin DataViewPrototypeGetByteOffset(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
|
|
const dataView: JSDataView =
|
|
ValidateDataView(context, receiver, 'get DataView.prototype.byte_offset');
|
|
if (WasDetached(dataView)) {
|
|
// TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
|
|
// here if the JSArrayBuffer of the {dataView} was detached.
|
|
return 0;
|
|
}
|
|
return Convert<Number>(dataView.byte_offset);
|
|
}
|
|
|
|
extern macro BitcastInt32ToFloat32(uint32): float32;
|
|
extern macro BitcastFloat32ToInt32(float32): uint32;
|
|
extern macro Float64ExtractLowWord32(float64): uint32;
|
|
extern macro Float64ExtractHighWord32(float64): uint32;
|
|
extern macro Float64InsertLowWord32(float64, uint32): float64;
|
|
extern macro Float64InsertHighWord32(float64, uint32): float64;
|
|
|
|
extern macro DataViewBuiltinsAssembler::LoadUint8(RawPtr, uintptr): uint32;
|
|
extern macro DataViewBuiltinsAssembler::LoadInt8(RawPtr, uintptr): int32;
|
|
|
|
macro LoadDataView8(
|
|
buffer: JSArrayBuffer, offset: uintptr, signed: constexpr bool): Smi {
|
|
if constexpr (signed) {
|
|
return Convert<Smi>(LoadInt8(buffer.backing_store_ptr, offset));
|
|
} else {
|
|
return Convert<Smi>(LoadUint8(buffer.backing_store_ptr, offset));
|
|
}
|
|
}
|
|
|
|
macro LoadDataView16(
|
|
buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
|
|
signed: constexpr bool): Number {
|
|
const dataPointer: RawPtr = buffer.backing_store_ptr;
|
|
|
|
let b0: int32;
|
|
let b1: int32;
|
|
let result: int32;
|
|
|
|
// Sign-extend the most significant byte by loading it as an Int8.
|
|
if (requestedLittleEndian) {
|
|
b0 = Signed(LoadUint8(dataPointer, offset));
|
|
b1 = LoadInt8(dataPointer, offset + 1);
|
|
result = (b1 << 8) + b0;
|
|
} else {
|
|
b0 = LoadInt8(dataPointer, offset);
|
|
b1 = Signed(LoadUint8(dataPointer, offset + 1));
|
|
result = (b0 << 8) + b1;
|
|
}
|
|
if constexpr (signed) {
|
|
return Convert<Smi>(result);
|
|
} else {
|
|
// Bit-mask the higher bits to prevent sign extension if we're unsigned.
|
|
return Convert<Smi>(result & 0xFFFF);
|
|
}
|
|
}
|
|
|
|
macro LoadDataView32(
|
|
buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
|
|
kind: constexpr ElementsKind): Number {
|
|
const dataPointer: RawPtr = buffer.backing_store_ptr;
|
|
|
|
const b0: uint32 = LoadUint8(dataPointer, offset);
|
|
const b1: uint32 = LoadUint8(dataPointer, offset + 1);
|
|
const b2: uint32 = LoadUint8(dataPointer, offset + 2);
|
|
const b3: uint32 = LoadUint8(dataPointer, offset + 3);
|
|
let result: uint32;
|
|
|
|
if (requestedLittleEndian) {
|
|
result = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
|
|
} else {
|
|
result = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
|
}
|
|
|
|
if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
|
|
return Convert<Number>(Signed(result));
|
|
} else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
|
|
return Convert<Number>(result);
|
|
} else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
|
|
const floatRes: float64 = Convert<float64>(BitcastInt32ToFloat32(result));
|
|
return Convert<Number>(floatRes);
|
|
} else {
|
|
unreachable;
|
|
}
|
|
}
|
|
|
|
macro LoadDataViewFloat64(
|
|
buffer: JSArrayBuffer, offset: uintptr,
|
|
requestedLittleEndian: bool): Number {
|
|
const dataPointer: RawPtr = buffer.backing_store_ptr;
|
|
|
|
const b0: uint32 = LoadUint8(dataPointer, offset);
|
|
const b1: uint32 = LoadUint8(dataPointer, offset + 1);
|
|
const b2: uint32 = LoadUint8(dataPointer, offset + 2);
|
|
const b3: uint32 = LoadUint8(dataPointer, offset + 3);
|
|
const b4: uint32 = LoadUint8(dataPointer, offset + 4);
|
|
const b5: uint32 = LoadUint8(dataPointer, offset + 5);
|
|
const b6: uint32 = LoadUint8(dataPointer, offset + 6);
|
|
const b7: uint32 = LoadUint8(dataPointer, offset + 7);
|
|
let lowWord: uint32;
|
|
let highWord: uint32;
|
|
|
|
if (requestedLittleEndian) {
|
|
lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
|
|
highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4;
|
|
} else {
|
|
highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
|
lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
|
|
}
|
|
|
|
let result: float64 = 0;
|
|
result = Float64InsertLowWord32(result, lowWord);
|
|
result = Float64InsertHighWord32(result, highWord);
|
|
|
|
return Convert<Number>(result);
|
|
}
|
|
|
|
const kZeroDigitBigInt: constexpr int31 = 0;
|
|
const kOneDigitBigInt: constexpr int31 = 1;
|
|
const kTwoDigitBigInt: constexpr int31 = 2;
|
|
|
|
// Create a BigInt on a 64-bit architecture from two 32-bit values.
|
|
macro MakeBigIntOn64Bit(implicit context: Context)(
|
|
lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
|
|
// 0n is represented by a zero-length BigInt.
|
|
if (lowWord == 0 && highWord == 0) {
|
|
return Convert<BigInt>(bigint::AllocateBigInt(kZeroDigitBigInt));
|
|
}
|
|
|
|
let sign: uint32 = bigint::kPositiveSign;
|
|
const highPart: intptr = Signed(Convert<uintptr>(highWord));
|
|
const lowPart: intptr = Signed(Convert<uintptr>(lowWord));
|
|
let rawValue: intptr = (highPart << 32) + lowPart;
|
|
|
|
if constexpr (signed) {
|
|
if (rawValue < 0) {
|
|
sign = bigint::kNegativeSign;
|
|
// We have to store the absolute value of rawValue in the digit.
|
|
rawValue = 0 - rawValue;
|
|
}
|
|
}
|
|
|
|
// Allocate the BigInt and store the absolute value.
|
|
const result: MutableBigInt =
|
|
bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt);
|
|
bigint::StoreBigIntDigit(result, 0, Unsigned(rawValue));
|
|
return Convert<BigInt>(result);
|
|
}
|
|
|
|
// Create a BigInt on a 32-bit architecture from two 32-bit values.
|
|
macro MakeBigIntOn32Bit(implicit context: Context)(
|
|
lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
|
|
// 0n is represented by a zero-length BigInt.
|
|
if (lowWord == 0 && highWord == 0) {
|
|
return Convert<BigInt>(bigint::AllocateBigInt(kZeroDigitBigInt));
|
|
}
|
|
|
|
// On a 32-bit platform, we might need 1 or 2 digits to store the number.
|
|
let needTwoDigits: bool = false;
|
|
let sign: uint32 = bigint::kPositiveSign;
|
|
|
|
// We need to do some math on lowWord and highWord,
|
|
// so Convert them to int32.
|
|
let lowPart: int32 = Signed(lowWord);
|
|
let highPart: int32 = Signed(highWord);
|
|
|
|
// If highWord == 0, the number is positive, and we only need 1 digit,
|
|
// so we don't have anything to do.
|
|
// Otherwise, all cases are possible.
|
|
if (highWord != 0) {
|
|
if constexpr (signed) {
|
|
// If highPart < 0, the number is always negative.
|
|
if (highPart < 0) {
|
|
sign = bigint::kNegativeSign;
|
|
|
|
// We have to compute the absolute value by hand.
|
|
// There will be a negative carry from the low word
|
|
// to the high word iff low != 0.
|
|
highPart = 0 - highPart;
|
|
if (lowPart != 0) {
|
|
highPart = highPart - 1;
|
|
}
|
|
lowPart = 0 - lowPart;
|
|
|
|
// Here, highPart could be 0 again so we might have 1 or 2 digits.
|
|
if (highPart != 0) {
|
|
needTwoDigits = true;
|
|
}
|
|
|
|
} else {
|
|
// In this case, the number is positive, and we need 2 digits.
|
|
needTwoDigits = true;
|
|
}
|
|
|
|
} else {
|
|
// In this case, the number is positive (unsigned),
|
|
// and we need 2 digits.
|
|
needTwoDigits = true;
|
|
}
|
|
}
|
|
|
|
// Allocate the BigInt with the right sign and length.
|
|
let result: MutableBigInt;
|
|
if (needTwoDigits) {
|
|
result = bigint::AllocateEmptyBigInt(sign, kTwoDigitBigInt);
|
|
} else {
|
|
result = bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt);
|
|
}
|
|
|
|
// Finally, write the digit(s) to the BigInt.
|
|
bigint::StoreBigIntDigit(result, 0, Unsigned(Convert<intptr>(lowPart)));
|
|
if (needTwoDigits) {
|
|
bigint::StoreBigIntDigit(result, 1, Unsigned(Convert<intptr>(highPart)));
|
|
}
|
|
return Convert<BigInt>(result);
|
|
}
|
|
|
|
macro MakeBigInt(implicit context: Context)(
|
|
lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
|
|
// A BigInt digit has the platform word size, so we only need one digit
|
|
// on 64-bit platforms but may need two on 32-bit.
|
|
if constexpr (Is64()) {
|
|
return MakeBigIntOn64Bit(lowWord, highWord, signed);
|
|
} else {
|
|
return MakeBigIntOn32Bit(lowWord, highWord, signed);
|
|
}
|
|
}
|
|
|
|
macro LoadDataViewBigInt(implicit context: Context)(
|
|
buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
|
|
signed: constexpr bool): BigInt {
|
|
const dataPointer: RawPtr = buffer.backing_store_ptr;
|
|
|
|
const b0: uint32 = LoadUint8(dataPointer, offset);
|
|
const b1: uint32 = LoadUint8(dataPointer, offset + 1);
|
|
const b2: uint32 = LoadUint8(dataPointer, offset + 2);
|
|
const b3: uint32 = LoadUint8(dataPointer, offset + 3);
|
|
const b4: uint32 = LoadUint8(dataPointer, offset + 4);
|
|
const b5: uint32 = LoadUint8(dataPointer, offset + 5);
|
|
const b6: uint32 = LoadUint8(dataPointer, offset + 6);
|
|
const b7: uint32 = LoadUint8(dataPointer, offset + 7);
|
|
let lowWord: uint32;
|
|
let highWord: uint32;
|
|
|
|
if (requestedLittleEndian) {
|
|
lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
|
|
highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4;
|
|
} else {
|
|
highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
|
|
lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
|
|
}
|
|
|
|
return MakeBigInt(lowWord, highWord, signed);
|
|
}
|
|
|
|
extern macro DataViewBuiltinsAssembler::DataViewElementSize(
|
|
constexpr ElementsKind): constexpr int31;
|
|
|
|
// GetViewValue ( view, requestIndex, isLittleEndian, type )
|
|
// https://tc39.es/ecma262/#sec-getviewvalue
|
|
transitioning macro DataViewGet(
|
|
context: Context, receiver: JSAny, requestIndex: JSAny,
|
|
requestedLittleEndian: JSAny, kind: constexpr ElementsKind): Numeric {
|
|
// 1. Perform ? RequireInternalSlot(view, [[DataView]]).
|
|
// 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
|
|
const dataView: JSDataView =
|
|
ValidateDataView(context, receiver, MakeDataViewGetterNameString(kind));
|
|
|
|
try {
|
|
// 3. Let getIndex be ? ToIndex(requestIndex).
|
|
const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError;
|
|
|
|
// 4. Set isLittleEndian to ! ToBoolean(isLittleEndian).
|
|
const littleEndian: bool = ToBoolean(requestedLittleEndian);
|
|
|
|
// 5. Let buffer be view.[[ViewedArrayBuffer]].
|
|
const buffer: JSArrayBuffer = dataView.buffer;
|
|
|
|
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
|
if (IsDetachedBuffer(buffer)) {
|
|
ThrowTypeError(
|
|
MessageTemplate::kDetachedOperation,
|
|
MakeDataViewGetterNameString(kind));
|
|
}
|
|
|
|
// 7. Let viewOffset be view.[[ByteOffset]].
|
|
const viewOffset: uintptr = dataView.byte_offset;
|
|
|
|
// 8. Let viewSize be view.[[ByteLength]].
|
|
const viewSize: uintptr = dataView.byte_length;
|
|
|
|
// 9. Let elementSize be the Element Size value specified in Table 62
|
|
// for Element Type type.
|
|
const elementSize: uintptr = DataViewElementSize(kind);
|
|
|
|
// 10. If getIndex + elementSize > viewSize, throw a RangeError exception.
|
|
CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
|
|
otherwise RangeError;
|
|
|
|
// 11. Let bufferIndex be getIndex + viewOffset.
|
|
const bufferIndex: uintptr = getIndex + viewOffset;
|
|
|
|
if constexpr (kind == ElementsKind::UINT8_ELEMENTS) {
|
|
return LoadDataView8(buffer, bufferIndex, false);
|
|
} else if constexpr (kind == ElementsKind::INT8_ELEMENTS) {
|
|
return LoadDataView8(buffer, bufferIndex, true);
|
|
} else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) {
|
|
return LoadDataView16(buffer, bufferIndex, littleEndian, false);
|
|
} else if constexpr (kind == ElementsKind::INT16_ELEMENTS) {
|
|
return LoadDataView16(buffer, bufferIndex, littleEndian, true);
|
|
} else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
|
|
return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
|
|
} else if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
|
|
return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
|
|
} else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
|
|
return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
|
|
} else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
|
|
return LoadDataViewFloat64(buffer, bufferIndex, littleEndian);
|
|
} else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) {
|
|
return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false);
|
|
} else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) {
|
|
return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true);
|
|
} else {
|
|
unreachable;
|
|
}
|
|
} label RangeError {
|
|
ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset);
|
|
}
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetUint8(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
return DataViewGet(
|
|
context, receiver, offset, Undefined, ElementsKind::UINT8_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetInt8(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
return DataViewGet(
|
|
context, receiver, offset, Undefined, ElementsKind::INT8_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetUint16(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian, ElementsKind::UINT16_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetInt16(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian, ElementsKind::INT16_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetUint32(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian, ElementsKind::UINT32_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetInt32(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian, ElementsKind::INT32_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetFloat32(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian,
|
|
ElementsKind::FLOAT32_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetFloat64(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian,
|
|
ElementsKind::FLOAT64_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetBigUint64(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian,
|
|
ElementsKind::BIGUINT64_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeGetBigInt64(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const isLittleEndian: JSAny = arguments[1];
|
|
return DataViewGet(
|
|
context, receiver, offset, isLittleEndian,
|
|
ElementsKind::BIGINT64_ELEMENTS);
|
|
}
|
|
|
|
extern macro ToNumber(Context, JSAny): Number;
|
|
extern macro ToBigInt(Context, JSAny): BigInt;
|
|
extern macro TruncateFloat64ToWord32(float64): uint32;
|
|
|
|
extern macro DataViewBuiltinsAssembler::StoreWord8(
|
|
RawPtr, uintptr, uint32): void;
|
|
|
|
macro StoreDataView8(buffer: JSArrayBuffer, offset: uintptr, value: uint32) {
|
|
StoreWord8(buffer.backing_store_ptr, offset, value & 0xFF);
|
|
}
|
|
|
|
macro StoreDataView16(
|
|
buffer: JSArrayBuffer, offset: uintptr, value: uint32,
|
|
requestedLittleEndian: bool) {
|
|
const dataPointer: RawPtr = buffer.backing_store_ptr;
|
|
|
|
const b0: uint32 = value & 0xFF;
|
|
const b1: uint32 = (value >>> 8) & 0xFF;
|
|
|
|
if (requestedLittleEndian) {
|
|
StoreWord8(dataPointer, offset, b0);
|
|
StoreWord8(dataPointer, offset + 1, b1);
|
|
} else {
|
|
StoreWord8(dataPointer, offset, b1);
|
|
StoreWord8(dataPointer, offset + 1, b0);
|
|
}
|
|
}
|
|
|
|
macro StoreDataView32(
|
|
buffer: JSArrayBuffer, offset: uintptr, value: uint32,
|
|
requestedLittleEndian: bool) {
|
|
const dataPointer: RawPtr = buffer.backing_store_ptr;
|
|
|
|
const b0: uint32 = value & 0xFF;
|
|
const b1: uint32 = (value >>> 8) & 0xFF;
|
|
const b2: uint32 = (value >>> 16) & 0xFF;
|
|
const b3: uint32 = value >>> 24; // We don't need to mask here.
|
|
|
|
if (requestedLittleEndian) {
|
|
StoreWord8(dataPointer, offset, b0);
|
|
StoreWord8(dataPointer, offset + 1, b1);
|
|
StoreWord8(dataPointer, offset + 2, b2);
|
|
StoreWord8(dataPointer, offset + 3, b3);
|
|
} else {
|
|
StoreWord8(dataPointer, offset, b3);
|
|
StoreWord8(dataPointer, offset + 1, b2);
|
|
StoreWord8(dataPointer, offset + 2, b1);
|
|
StoreWord8(dataPointer, offset + 3, b0);
|
|
}
|
|
}
|
|
|
|
macro StoreDataView64(
|
|
buffer: JSArrayBuffer, offset: uintptr, lowWord: uint32, highWord: uint32,
|
|
requestedLittleEndian: bool) {
|
|
const dataPointer: RawPtr = buffer.backing_store_ptr;
|
|
|
|
const b0: uint32 = lowWord & 0xFF;
|
|
const b1: uint32 = (lowWord >>> 8) & 0xFF;
|
|
const b2: uint32 = (lowWord >>> 16) & 0xFF;
|
|
const b3: uint32 = lowWord >>> 24;
|
|
|
|
const b4: uint32 = highWord & 0xFF;
|
|
const b5: uint32 = (highWord >>> 8) & 0xFF;
|
|
const b6: uint32 = (highWord >>> 16) & 0xFF;
|
|
const b7: uint32 = highWord >>> 24;
|
|
|
|
if (requestedLittleEndian) {
|
|
StoreWord8(dataPointer, offset, b0);
|
|
StoreWord8(dataPointer, offset + 1, b1);
|
|
StoreWord8(dataPointer, offset + 2, b2);
|
|
StoreWord8(dataPointer, offset + 3, b3);
|
|
StoreWord8(dataPointer, offset + 4, b4);
|
|
StoreWord8(dataPointer, offset + 5, b5);
|
|
StoreWord8(dataPointer, offset + 6, b6);
|
|
StoreWord8(dataPointer, offset + 7, b7);
|
|
} else {
|
|
StoreWord8(dataPointer, offset, b7);
|
|
StoreWord8(dataPointer, offset + 1, b6);
|
|
StoreWord8(dataPointer, offset + 2, b5);
|
|
StoreWord8(dataPointer, offset + 3, b4);
|
|
StoreWord8(dataPointer, offset + 4, b3);
|
|
StoreWord8(dataPointer, offset + 5, b2);
|
|
StoreWord8(dataPointer, offset + 6, b1);
|
|
StoreWord8(dataPointer, offset + 7, b0);
|
|
}
|
|
}
|
|
|
|
extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntLength(BigIntBase):
|
|
uint32;
|
|
extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntSign(BigIntBase):
|
|
uint32;
|
|
|
|
// We might get here a BigInt that is bigger than 64 bits, but we're only
|
|
// interested in the 64 lowest ones. This means the lowest BigInt digit
|
|
// on 64-bit platforms, and the 2 lowest BigInt digits on 32-bit ones.
|
|
macro StoreDataViewBigInt(
|
|
buffer: JSArrayBuffer, offset: uintptr, bigIntValue: BigInt,
|
|
requestedLittleEndian: bool) {
|
|
const length: uint32 = DataViewDecodeBigIntLength(bigIntValue);
|
|
const sign: uint32 = DataViewDecodeBigIntSign(bigIntValue);
|
|
|
|
// The 32-bit words that will hold the BigInt's value in
|
|
// two's complement representation.
|
|
let lowWord: uint32 = 0;
|
|
let highWord: uint32 = 0;
|
|
|
|
// The length is nonzero if and only if the BigInt's value is nonzero.
|
|
if (length != 0) {
|
|
if constexpr (Is64()) {
|
|
// There is always exactly 1 BigInt digit to load in this case.
|
|
const value: uintptr = bigint::LoadBigIntDigit(bigIntValue, 0);
|
|
lowWord = Convert<uint32>(value); // Truncates value to 32 bits.
|
|
highWord = Convert<uint32>(value >>> 32);
|
|
} else { // There might be either 1 or 2 BigInt digits we need to load.
|
|
lowWord = Convert<uint32>(bigint::LoadBigIntDigit(bigIntValue, 0));
|
|
if (length >= 2) { // Only load the second digit if there is one.
|
|
highWord = Convert<uint32>(bigint::LoadBigIntDigit(bigIntValue, 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sign != 0) { // The number is negative, Convert it.
|
|
highWord = Unsigned(0 - Signed(highWord));
|
|
if (lowWord != 0) {
|
|
highWord = Unsigned(Signed(highWord) - 1);
|
|
}
|
|
lowWord = Unsigned(0 - Signed(lowWord));
|
|
}
|
|
|
|
StoreDataView64(buffer, offset, lowWord, highWord, requestedLittleEndian);
|
|
}
|
|
|
|
// SetViewValue ( view, requestIndex, isLittleEndian, type, value )
|
|
// https://tc39.es/ecma262/#sec-setviewvalue
|
|
transitioning macro DataViewSet(
|
|
context: Context, receiver: JSAny, requestIndex: JSAny, value: JSAny,
|
|
requestedLittleEndian: JSAny, kind: constexpr ElementsKind): JSAny {
|
|
// 1. Perform ? RequireInternalSlot(view, [[DataView]]).
|
|
// 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
|
|
const dataView: JSDataView =
|
|
ValidateDataView(context, receiver, MakeDataViewSetterNameString(kind));
|
|
|
|
try {
|
|
// 3. Let getIndex be ? ToIndex(requestIndex).
|
|
const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError;
|
|
|
|
const littleEndian: bool = ToBoolean(requestedLittleEndian);
|
|
const buffer: JSArrayBuffer = dataView.buffer;
|
|
|
|
let numberValue: Numeric;
|
|
if constexpr (
|
|
kind == ElementsKind::BIGUINT64_ELEMENTS ||
|
|
kind == ElementsKind::BIGINT64_ELEMENTS) {
|
|
// 4. If ! IsBigIntElementType(type) is true, let numberValue be
|
|
// ? ToBigInt(value).
|
|
numberValue = ToBigInt(context, value);
|
|
} else {
|
|
// 5. Otherwise, let numberValue be ? ToNumber(value).
|
|
numberValue = ToNumber(context, value);
|
|
}
|
|
|
|
// 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
|
|
if (IsDetachedBuffer(buffer)) {
|
|
ThrowTypeError(
|
|
MessageTemplate::kDetachedOperation,
|
|
MakeDataViewSetterNameString(kind));
|
|
}
|
|
|
|
// 9. Let viewOffset be view.[[ByteOffset]].
|
|
const viewOffset: uintptr = dataView.byte_offset;
|
|
|
|
// 10. Let viewSize be view.[[ByteLength]].
|
|
const viewSize: uintptr = dataView.byte_length;
|
|
|
|
// 11. Let elementSize be the Element Size value specified in Table 62
|
|
// for Element Type type.
|
|
const elementSize: uintptr = DataViewElementSize(kind);
|
|
|
|
// 12. If getIndex + elementSize > viewSize, throw a RangeError exception.
|
|
CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
|
|
otherwise RangeError;
|
|
|
|
// 13. Let bufferIndex be getIndex + viewOffset.
|
|
const bufferIndex: uintptr = getIndex + viewOffset;
|
|
|
|
if constexpr (
|
|
kind == ElementsKind::BIGUINT64_ELEMENTS ||
|
|
kind == ElementsKind::BIGINT64_ELEMENTS) {
|
|
// For these elements kinds numberValue is BigInt.
|
|
const bigIntValue: BigInt = %RawDownCast<BigInt>(numberValue);
|
|
StoreDataViewBigInt(buffer, bufferIndex, bigIntValue, littleEndian);
|
|
} else {
|
|
// For these elements kinds numberValue is Number.
|
|
const numValue: Number = %RawDownCast<Number>(numberValue);
|
|
const doubleValue: float64 = ChangeNumberToFloat64(numValue);
|
|
|
|
if constexpr (
|
|
kind == ElementsKind::UINT8_ELEMENTS ||
|
|
kind == ElementsKind::INT8_ELEMENTS) {
|
|
StoreDataView8(
|
|
buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue));
|
|
} else if constexpr (
|
|
kind == ElementsKind::UINT16_ELEMENTS ||
|
|
kind == ElementsKind::INT16_ELEMENTS) {
|
|
StoreDataView16(
|
|
buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue),
|
|
littleEndian);
|
|
} else if constexpr (
|
|
kind == ElementsKind::UINT32_ELEMENTS ||
|
|
kind == ElementsKind::INT32_ELEMENTS) {
|
|
StoreDataView32(
|
|
buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue),
|
|
littleEndian);
|
|
} else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
|
|
const floatValue: float32 = TruncateFloat64ToFloat32(doubleValue);
|
|
StoreDataView32(
|
|
buffer, bufferIndex, BitcastFloat32ToInt32(floatValue),
|
|
littleEndian);
|
|
} else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
|
|
const lowWord: uint32 = Float64ExtractLowWord32(doubleValue);
|
|
const highWord: uint32 = Float64ExtractHighWord32(doubleValue);
|
|
StoreDataView64(buffer, bufferIndex, lowWord, highWord, littleEndian);
|
|
}
|
|
}
|
|
return Undefined;
|
|
} label RangeError {
|
|
ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset);
|
|
}
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetUint8(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, Undefined,
|
|
ElementsKind::UINT8_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetInt8(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, Undefined, ElementsKind::INT8_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetUint16(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::UINT16_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetInt16(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::INT16_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetUint32(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::UINT32_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetInt32(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::INT32_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetFloat32(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::FLOAT32_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetFloat64(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::FLOAT64_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetBigUint64(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::BIGUINT64_ELEMENTS);
|
|
}
|
|
|
|
transitioning javascript builtin DataViewPrototypeSetBigInt64(
|
|
js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
|
|
const offset: JSAny = arguments[0];
|
|
const value: JSAny = arguments[1];
|
|
const isLittleEndian: JSAny = arguments[2];
|
|
return DataViewSet(
|
|
context, receiver, offset, value, isLittleEndian,
|
|
ElementsKind::BIGINT64_ELEMENTS);
|
|
}
|
|
}
|