| // 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. |
| |
| module data_view { |
| |
| extern operator '.buffer' |
| macro LoadArrayBufferViewBuffer(JSArrayBufferView): JSArrayBuffer; |
| extern operator '.byte_length' |
| macro LoadDataViewByteLength(JSDataView): Number; |
| extern operator '.byte_offset' |
| macro LoadDataViewByteOffset(JSDataView): Number; |
| extern operator '.backing_store' |
| macro LoadArrayBufferBackingStore(JSArrayBuffer): RawPtr; |
| |
| macro MakeDataViewGetterNameString(kind: constexpr ElementsKind): String { |
| if constexpr (kind == UINT8_ELEMENTS) { |
| return 'DataView.prototype.getUint8'; |
| } else if constexpr (kind == INT8_ELEMENTS) { |
| return 'DataView.prototype.getInt8'; |
| } else if constexpr (kind == UINT16_ELEMENTS) { |
| return 'DataView.prototype.getUint16'; |
| } else if constexpr (kind == INT16_ELEMENTS) { |
| return 'DataView.prototype.getInt16'; |
| } else if constexpr (kind == UINT32_ELEMENTS) { |
| return 'DataView.prototype.getUint32'; |
| } else if constexpr (kind == INT32_ELEMENTS) { |
| return 'DataView.prototype.getInt32'; |
| } else if constexpr (kind == FLOAT32_ELEMENTS) { |
| return 'DataView.prototype.getFloat32'; |
| } else if constexpr (kind == FLOAT64_ELEMENTS) { |
| return 'DataView.prototype.getFloat64'; |
| } else if constexpr (kind == BIGINT64_ELEMENTS) { |
| return 'DataView.prototype.getBigInt64'; |
| } else if constexpr (kind == BIGUINT64_ELEMENTS) { |
| return 'DataView.prototype.getBigUint64'; |
| } else { |
| unreachable; |
| } |
| } |
| |
| macro MakeDataViewSetterNameString(kind: constexpr ElementsKind): String { |
| if constexpr (kind == UINT8_ELEMENTS) { |
| return 'DataView.prototype.setUint8'; |
| } else if constexpr (kind == INT8_ELEMENTS) { |
| return 'DataView.prototype.setInt8'; |
| } else if constexpr (kind == UINT16_ELEMENTS) { |
| return 'DataView.prototype.setUint16'; |
| } else if constexpr (kind == INT16_ELEMENTS) { |
| return 'DataView.prototype.setInt16'; |
| } else if constexpr (kind == UINT32_ELEMENTS) { |
| return 'DataView.prototype.setUint32'; |
| } else if constexpr (kind == INT32_ELEMENTS) { |
| return 'DataView.prototype.setInt32'; |
| } else if constexpr (kind == FLOAT32_ELEMENTS) { |
| return 'DataView.prototype.setFloat32'; |
| } else if constexpr (kind == FLOAT64_ELEMENTS) { |
| return 'DataView.prototype.setFloat64'; |
| } else if constexpr (kind == BIGINT64_ELEMENTS) { |
| return 'DataView.prototype.setBigInt64'; |
| } else if constexpr (kind == BIGUINT64_ELEMENTS) { |
| return 'DataView.prototype.setBigUint64'; |
| } else { |
| unreachable; |
| } |
| } |
| |
| macro WasNeutered(view: JSArrayBufferView): bool { |
| return IsDetachedBuffer(view.buffer); |
| } |
| |
| macro ValidateDataView(context: Context, |
| o: Object, method: String): JSDataView { |
| try { |
| return cast<JSDataView>(o) otherwise CastError; |
| } |
| label CastError { |
| ThrowTypeError(context, kIncompatibleMethodReceiver, method); |
| } |
| } |
| |
| // ES6 section 24.2.4.1 get DataView.prototype.buffer |
| javascript builtin DataViewPrototypeGetBuffer( |
| context: Context, receiver: Object, ...arguments): JSArrayBuffer { |
| let data_view: JSDataView = ValidateDataView( |
| context, receiver, 'get DataView.prototype.buffer'); |
| return data_view.buffer; |
| } |
| |
| // ES6 section 24.2.4.2 get DataView.prototype.byteLength |
| javascript builtin DataViewPrototypeGetByteLength( |
| context: Context, receiver: Object, ...arguments): Number { |
| let data_view: JSDataView = ValidateDataView( |
| context, receiver, 'get DataView.prototype.byte_length'); |
| if (WasNeutered(data_view)) { |
| // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError |
| // here if the JSArrayBuffer of the {data_view} was neutered. |
| return 0; |
| } |
| return data_view.byte_length; |
| } |
| |
| // ES6 section 24.2.4.3 get DataView.prototype.byteOffset |
| javascript builtin DataViewPrototypeGetByteOffset( |
| context: Context, receiver: Object, ...arguments): Number { |
| let data_view: JSDataView = ValidateDataView( |
| context, receiver, 'get DataView.prototype.byte_offset'); |
| if (WasNeutered(data_view)) { |
| // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError |
| // here if the JSArrayBuffer of the {data_view} was neutered. |
| return 0; |
| } |
| return data_view.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 LoadUint8(RawPtr, intptr): uint32; |
| extern macro LoadInt8(RawPtr, intptr): int32; |
| |
| macro LoadDataView8(buffer: JSArrayBuffer, offset: intptr, |
| signed: constexpr bool): Smi { |
| if constexpr (signed) { |
| return convert<Smi>(LoadInt8(buffer.backing_store, offset)); |
| } else { |
| return convert<Smi>(LoadUint8(buffer.backing_store, offset)); |
| } |
| } |
| |
| macro LoadDataView16(buffer: JSArrayBuffer, offset: intptr, |
| requested_little_endian: bool, |
| signed: constexpr bool): Number { |
| let data_pointer: RawPtr = buffer.backing_store; |
| |
| let b0: int32; |
| let b1: int32; |
| let result: int32; |
| |
| // Sign-extend the most significant byte by loading it as an Int8. |
| if (requested_little_endian) { |
| b0 = Signed(LoadUint8(data_pointer, offset)); |
| b1 = LoadInt8(data_pointer, offset + 1); |
| result = (b1 << 8) + b0; |
| } else { |
| b0 = LoadInt8(data_pointer, offset); |
| b1 = Signed(LoadUint8(data_pointer, 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: intptr, |
| requested_little_endian: bool, |
| kind: constexpr ElementsKind): Number { |
| let data_pointer: RawPtr = buffer.backing_store; |
| |
| let b0: uint32 = LoadUint8(data_pointer, offset); |
| let b1: uint32 = LoadUint8(data_pointer, offset + 1); |
| let b2: uint32 = LoadUint8(data_pointer, offset + 2); |
| let b3: uint32 = LoadUint8(data_pointer, offset + 3); |
| let result: uint32; |
| |
| if (requested_little_endian) { |
| result = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; |
| } else { |
| result = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; |
| } |
| |
| if constexpr (kind == INT32_ELEMENTS) { |
| return convert<Number>(Signed(result)); |
| } else if constexpr (kind == UINT32_ELEMENTS) { |
| return convert<Number>(result); |
| } else if constexpr (kind == FLOAT32_ELEMENTS) { |
| let float_res: float64 = convert<float64>(BitcastInt32ToFloat32(result)); |
| return convert<Number>(float_res); |
| } else { |
| unreachable; |
| } |
| } |
| |
| macro LoadDataViewFloat64(buffer: JSArrayBuffer, offset: intptr, |
| requested_little_endian: bool): Number { |
| let data_pointer: RawPtr = buffer.backing_store; |
| |
| let b0: uint32 = LoadUint8(data_pointer, offset); |
| let b1: uint32 = LoadUint8(data_pointer, offset + 1); |
| let b2: uint32 = LoadUint8(data_pointer, offset + 2); |
| let b3: uint32 = LoadUint8(data_pointer, offset + 3); |
| let b4: uint32 = LoadUint8(data_pointer, offset + 4); |
| let b5: uint32 = LoadUint8(data_pointer, offset + 5); |
| let b6: uint32 = LoadUint8(data_pointer, offset + 6); |
| let b7: uint32 = LoadUint8(data_pointer, offset + 7); |
| let low_word: uint32; |
| let high_word: uint32; |
| |
| if (requested_little_endian) { |
| low_word = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; |
| high_word = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; |
| } else { |
| high_word = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; |
| low_word = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; |
| } |
| |
| let result: float64 = 0; |
| result = Float64InsertLowWord32(result, low_word); |
| result = Float64InsertHighWord32(result, high_word); |
| |
| return convert<Number>(result); |
| } |
| |
| extern macro AllocateBigInt(intptr): BigInt; |
| extern macro StoreBigIntBitfield(BigInt, intptr): void; |
| extern macro StoreBigIntDigit(BigInt, constexpr int31, uintptr): void; |
| extern macro DataViewEncodeBigIntBits(constexpr bool, |
| constexpr int31): intptr; |
| |
| const kPositiveBigInt: constexpr bool = false; |
| const kNegativeBigInt: constexpr bool = true; |
| const kZeroDigitBigInt: constexpr int31 = 0; |
| const kOneDigitBigInt: constexpr int31 = 1; |
| const kTwoDigitBigInt: constexpr int31 = 2; |
| |
| macro CreateEmptyBigInt(is_positive: bool, length: constexpr int31): BigInt { |
| // Allocate a BigInt with the desired length (number of digits). |
| let result: BigInt = AllocateBigInt(length); |
| |
| // Write the desired sign and length to the BigInt bitfield. |
| if (is_positive) { |
| StoreBigIntBitfield(result, |
| DataViewEncodeBigIntBits(kPositiveBigInt, length)); |
| } else { |
| StoreBigIntBitfield(result, |
| DataViewEncodeBigIntBits(kNegativeBigInt, length)); |
| } |
| |
| return result; |
| } |
| |
| // Create a BigInt on a 64-bit architecture from two 32-bit values. |
| macro MakeBigIntOn64Bit(low_word: uint32, high_word: uint32, |
| signed: constexpr bool): BigInt { |
| |
| // 0n is represented by a zero-length BigInt. |
| if (low_word == 0 && high_word == 0) { |
| return AllocateBigInt(kZeroDigitBigInt); |
| } |
| |
| let is_positive: bool = true; |
| let high_part: intptr = Signed(convert<uintptr>(high_word)); |
| let low_part: intptr = Signed(convert<uintptr>(low_word)); |
| let raw_value: intptr = (high_part << 32) + low_part; |
| |
| if constexpr (signed) { |
| if (raw_value < 0) { |
| is_positive = false; |
| // We have to store the absolute value of raw_value in the digit. |
| raw_value = 0 - raw_value; |
| } |
| } |
| |
| // Allocate the BigInt and store the absolute value. |
| let result: BigInt = CreateEmptyBigInt(is_positive, kOneDigitBigInt); |
| |
| StoreBigIntDigit(result, 0, Unsigned(raw_value)); |
| |
| return result; |
| } |
| |
| // Create a BigInt on a 32-bit architecture from two 32-bit values. |
| macro MakeBigIntOn32Bit(low_word: uint32, high_word: uint32, |
| signed: constexpr bool): BigInt { |
| |
| // 0n is represented by a zero-length BigInt. |
| if (low_word == 0 && high_word == 0) { |
| return AllocateBigInt(kZeroDigitBigInt); |
| } |
| |
| // On a 32-bit platform, we might need 1 or 2 digits to store the number. |
| let need_two_digits: bool = false; |
| let is_positive: bool = true; |
| |
| // We need to do some math on low_word and high_word, |
| // so convert them to int32. |
| let low_part: int32 = Signed(low_word); |
| let high_part: int32 = Signed(high_word); |
| |
| // If high_word == 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 (high_word != 0) { |
| if constexpr (signed) { |
| |
| // If high_part < 0, the number is always negative. |
| if (high_part < 0) { |
| is_positive = false; |
| |
| // 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. |
| high_part = 0 - high_part; |
| if (low_part != 0) { |
| high_part = high_part - 1; |
| } |
| low_part = 0 - low_part; |
| |
| // Here, high_part could be 0 again so we might have 1 or 2 digits. |
| if (high_part != 0) { |
| need_two_digits = true; |
| } |
| |
| } else { |
| // In this case, the number is positive, and we need 2 digits. |
| need_two_digits = true; |
| } |
| |
| } else { |
| // In this case, the number is positive (unsigned), |
| // and we need 2 digits. |
| need_two_digits = true; |
| } |
| } |
| |
| // Allocate the BigInt with the right sign and length. |
| let result: BigInt; |
| if (need_two_digits) { |
| result = CreateEmptyBigInt(is_positive, kTwoDigitBigInt); |
| } else { |
| result = CreateEmptyBigInt(is_positive, kOneDigitBigInt); |
| } |
| |
| // Finally, write the digit(s) to the BigInt. |
| StoreBigIntDigit(result, 0, Unsigned(convert<intptr>(low_part))); |
| |
| if (need_two_digits) { |
| StoreBigIntDigit(result, 1, Unsigned(convert<intptr>(high_part))); |
| } |
| |
| return result; |
| } |
| |
| macro MakeBigInt(low_word: uint32, high_word: 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(low_word, high_word, signed); |
| } else { |
| return MakeBigIntOn32Bit(low_word, high_word, signed); |
| } |
| } |
| |
| macro LoadDataViewBigInt(buffer: JSArrayBuffer, offset: intptr, |
| requested_little_endian: bool, |
| signed: constexpr bool): BigInt { |
| let data_pointer: RawPtr = buffer.backing_store; |
| |
| let b0: uint32 = LoadUint8(data_pointer, offset); |
| let b1: uint32 = LoadUint8(data_pointer, offset + 1); |
| let b2: uint32 = LoadUint8(data_pointer, offset + 2); |
| let b3: uint32 = LoadUint8(data_pointer, offset + 3); |
| let b4: uint32 = LoadUint8(data_pointer, offset + 4); |
| let b5: uint32 = LoadUint8(data_pointer, offset + 5); |
| let b6: uint32 = LoadUint8(data_pointer, offset + 6); |
| let b7: uint32 = LoadUint8(data_pointer, offset + 7); |
| let low_word: uint32; |
| let high_word: uint32; |
| |
| if (requested_little_endian) { |
| low_word = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; |
| high_word = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; |
| } else { |
| high_word = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; |
| low_word = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; |
| } |
| |
| return MakeBigInt(low_word, high_word, signed); |
| } |
| |
| extern macro ToSmiIndex(Object, Context): Smi labels RangeError; |
| extern macro DataViewElementSize(constexpr ElementsKind): constexpr int31; |
| |
| macro DataViewGet(context: Context, |
| receiver: Object, |
| offset: Object, |
| requested_little_endian: Object, |
| kind: constexpr ElementsKind): Numeric { |
| |
| let data_view: JSDataView = ValidateDataView( |
| context, receiver, MakeDataViewGetterNameString(kind)); |
| |
| let getIndex: Number; |
| try { |
| getIndex = ToIndex(offset, context) otherwise RangeError; |
| } |
| label RangeError { |
| ThrowRangeError(context, kInvalidDataViewAccessorOffset); |
| } |
| |
| let littleEndian: bool = ToBoolean(requested_little_endian); |
| let buffer: JSArrayBuffer = data_view.buffer; |
| |
| if (IsDetachedBuffer(buffer)) { |
| ThrowTypeError(context, kDetachedOperation, |
| MakeDataViewGetterNameString(kind)); |
| } |
| |
| let viewOffset: Number = data_view.byte_offset; |
| let viewSize: Number = data_view.byte_length; |
| let elementSize: Number = DataViewElementSize(kind); |
| |
| if (getIndex + elementSize > viewSize) { |
| ThrowRangeError(context, kInvalidDataViewAccessorOffset); |
| } |
| |
| let getIndexFloat: float64 = convert<float64>(getIndex); |
| let getIndexIntptr: intptr = Signed(convert<uintptr>(getIndexFloat)); |
| let viewOffsetFloat: float64 = convert<float64>(viewOffset); |
| let viewOffsetIntptr: intptr = Signed(convert<uintptr>(viewOffsetFloat)); |
| |
| let bufferIndex: intptr = getIndexIntptr + viewOffsetIntptr; |
| |
| if constexpr (kind == UINT8_ELEMENTS) { |
| return LoadDataView8(buffer, bufferIndex, false); |
| } else if constexpr (kind == INT8_ELEMENTS) { |
| return LoadDataView8(buffer, bufferIndex, true); |
| } else if constexpr (kind == UINT16_ELEMENTS) { |
| return LoadDataView16(buffer, bufferIndex, littleEndian, false); |
| } else if constexpr (kind == INT16_ELEMENTS) { |
| return LoadDataView16(buffer, bufferIndex, littleEndian, true); |
| } else if constexpr (kind == UINT32_ELEMENTS) { |
| return LoadDataView32(buffer, bufferIndex, littleEndian, kind); |
| } else if constexpr (kind == INT32_ELEMENTS) { |
| return LoadDataView32(buffer, bufferIndex, littleEndian, kind); |
| } else if constexpr (kind == FLOAT32_ELEMENTS) { |
| return LoadDataView32(buffer, bufferIndex, littleEndian, kind); |
| } else if constexpr (kind == FLOAT64_ELEMENTS) { |
| return LoadDataViewFloat64(buffer, bufferIndex, littleEndian); |
| } else if constexpr (kind == BIGUINT64_ELEMENTS) { |
| return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false); |
| } else if constexpr (kind == BIGINT64_ELEMENTS) { |
| return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true); |
| } else { |
| unreachable; |
| } |
| } |
| |
| javascript builtin DataViewPrototypeGetUint8( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, Undefined, UINT8_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetInt8( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, Undefined, INT8_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetUint16( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| UINT16_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetInt16( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| INT16_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetUint32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| UINT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetInt32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| INT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetFloat32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| FLOAT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetFloat64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| FLOAT64_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetBigUint64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| BIGUINT64_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetBigInt64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, is_little_endian, |
| BIGINT64_ELEMENTS); |
| } |
| |
| extern macro ToNumber(Context, Object): Number; |
| extern macro ToBigInt(Context, Object): BigInt; |
| extern macro TruncateFloat64ToFloat32(float64): float32; |
| extern macro TruncateFloat64ToWord32(float64): uint32; |
| |
| extern macro StoreWord8(RawPtr, intptr, uint32): void; |
| |
| macro StoreDataView8(buffer: JSArrayBuffer, offset: intptr, |
| value: uint32) { |
| StoreWord8(buffer.backing_store, offset, value & 0xFF); |
| } |
| |
| macro StoreDataView16(buffer: JSArrayBuffer, offset: intptr, value: uint32, |
| requested_little_endian: bool) { |
| let data_pointer: RawPtr = buffer.backing_store; |
| |
| let b0: uint32 = value & 0xFF; |
| let b1: uint32 = (value >>> 8) & 0xFF; |
| |
| if (requested_little_endian) { |
| StoreWord8(data_pointer, offset, b0); |
| StoreWord8(data_pointer, offset + 1, b1); |
| } else { |
| StoreWord8(data_pointer, offset, b1); |
| StoreWord8(data_pointer, offset + 1, b0); |
| } |
| } |
| |
| macro StoreDataView32(buffer: JSArrayBuffer, offset: intptr, value: uint32, |
| requested_little_endian: bool) { |
| let data_pointer: RawPtr = buffer.backing_store; |
| |
| let b0: uint32 = value & 0xFF; |
| let b1: uint32 = (value >>> 8) & 0xFF; |
| let b2: uint32 = (value >>> 16) & 0xFF; |
| let b3: uint32 = value >>> 24; // We don't need to mask here. |
| |
| if (requested_little_endian) { |
| StoreWord8(data_pointer, offset, b0); |
| StoreWord8(data_pointer, offset + 1, b1); |
| StoreWord8(data_pointer, offset + 2, b2); |
| StoreWord8(data_pointer, offset + 3, b3); |
| } else { |
| StoreWord8(data_pointer, offset, b3); |
| StoreWord8(data_pointer, offset + 1, b2); |
| StoreWord8(data_pointer, offset + 2, b1); |
| StoreWord8(data_pointer, offset + 3, b0); |
| } |
| } |
| |
| macro StoreDataView64(buffer: JSArrayBuffer, offset: intptr, |
| low_word: uint32, high_word: uint32, |
| requested_little_endian: bool) { |
| let data_pointer: RawPtr = buffer.backing_store; |
| |
| let b0: uint32 = low_word & 0xFF; |
| let b1: uint32 = (low_word >>> 8) & 0xFF; |
| let b2: uint32 = (low_word >>> 16) & 0xFF; |
| let b3: uint32 = low_word >>> 24; |
| |
| let b4: uint32 = high_word & 0xFF; |
| let b5: uint32 = (high_word >>> 8) & 0xFF; |
| let b6: uint32 = (high_word >>> 16) & 0xFF; |
| let b7: uint32 = high_word >>> 24; |
| |
| |
| if (requested_little_endian) { |
| StoreWord8(data_pointer, offset, b0); |
| StoreWord8(data_pointer, offset + 1, b1); |
| StoreWord8(data_pointer, offset + 2, b2); |
| StoreWord8(data_pointer, offset + 3, b3); |
| StoreWord8(data_pointer, offset + 4, b4); |
| StoreWord8(data_pointer, offset + 5, b5); |
| StoreWord8(data_pointer, offset + 6, b6); |
| StoreWord8(data_pointer, offset + 7, b7); |
| } else { |
| StoreWord8(data_pointer, offset, b7); |
| StoreWord8(data_pointer, offset + 1, b6); |
| StoreWord8(data_pointer, offset + 2, b5); |
| StoreWord8(data_pointer, offset + 3, b4); |
| StoreWord8(data_pointer, offset + 4, b3); |
| StoreWord8(data_pointer, offset + 5, b2); |
| StoreWord8(data_pointer, offset + 6, b1); |
| StoreWord8(data_pointer, offset + 7, b0); |
| } |
| } |
| |
| extern macro DataViewDecodeBigIntLength(BigInt): uintptr; |
| extern macro DataViewDecodeBigIntSign(BigInt): uintptr; |
| extern macro LoadBigIntDigit(BigInt, constexpr int31): uintptr; |
| |
| // 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: intptr, |
| bigint_value: BigInt, |
| requested_little_endian: bool) { |
| |
| let length: uintptr = DataViewDecodeBigIntLength(bigint_value); |
| let sign: uintptr = DataViewDecodeBigIntSign(bigint_value); |
| |
| // The 32-bit words that will hold the BigInt's value in |
| // two's complement representation. |
| let low_word: uint32 = 0; |
| let high_word: 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. |
| let value: uintptr = LoadBigIntDigit(bigint_value, 0); |
| low_word = convert<uint32>(value); // Truncates value to 32 bits. |
| high_word = convert<uint32>(value >>> 32); |
| } |
| else { // There might be either 1 or 2 BigInt digits we need to load. |
| low_word = convert<uint32>(LoadBigIntDigit(bigint_value, 0)); |
| if (length >= 2) { // Only load the second digit if there is one. |
| high_word = convert<uint32>(LoadBigIntDigit(bigint_value, 1)); |
| } |
| } |
| } |
| |
| if (sign != 0) { // The number is negative, convert it. |
| high_word = Unsigned(0 - Signed(high_word)); |
| if (low_word != 0) { |
| high_word = Unsigned(Signed(high_word) - 1); |
| } |
| low_word = Unsigned(0 - Signed(low_word)); |
| } |
| |
| StoreDataView64(buffer, offset, low_word, high_word, |
| requested_little_endian); |
| } |
| |
| macro DataViewSet(context: Context, |
| receiver: Object, |
| offset: Object, |
| value: Object, |
| requested_little_endian: Object, |
| kind: constexpr ElementsKind): Object { |
| |
| let data_view: JSDataView = ValidateDataView( |
| context, receiver, MakeDataViewSetterNameString(kind)); |
| |
| let getIndex: Number; |
| try { |
| getIndex = ToIndex(offset, context) otherwise RangeError; |
| } |
| label RangeError { |
| ThrowRangeError(context, kInvalidDataViewAccessorOffset); |
| } |
| |
| let littleEndian: bool = ToBoolean(requested_little_endian); |
| let buffer: JSArrayBuffer = data_view.buffer; |
| |
| let bigint_value: BigInt; |
| let num_value: Number; |
| // According to ES6 section 24.2.1.2 SetViewValue, we must perform |
| // the conversion before doing the bounds check. |
| if constexpr (kind == BIGUINT64_ELEMENTS || kind == BIGINT64_ELEMENTS) { |
| bigint_value = ToBigInt(context, value); |
| } else { |
| num_value = ToNumber(context, value); |
| } |
| |
| if (IsDetachedBuffer(buffer)) { |
| ThrowTypeError(context, kDetachedOperation, |
| MakeDataViewSetterNameString(kind)); |
| } |
| |
| let viewOffset: Number = data_view.byte_offset; |
| let viewSize: Number = data_view.byte_length; |
| let elementSize: Number = DataViewElementSize(kind); |
| |
| if (getIndex + elementSize > viewSize) { |
| ThrowRangeError(context, kInvalidDataViewAccessorOffset); |
| } |
| |
| let getIndexFloat: float64 = convert<float64>(getIndex); |
| let getIndexIntptr: intptr = Signed(convert<uintptr>(getIndexFloat)); |
| let viewOffsetFloat: float64 = convert<float64>(viewOffset); |
| let viewOffsetIntptr: intptr = Signed(convert<uintptr>(viewOffsetFloat)); |
| |
| let bufferIndex: intptr = getIndexIntptr + viewOffsetIntptr; |
| |
| if constexpr (kind == BIGUINT64_ELEMENTS || kind == BIGINT64_ELEMENTS) { |
| StoreDataViewBigInt(buffer, bufferIndex, bigint_value, |
| littleEndian); |
| } |
| else { |
| let double_value: float64 = ChangeNumberToFloat64(num_value); |
| |
| if constexpr (kind == UINT8_ELEMENTS || kind == INT8_ELEMENTS) { |
| StoreDataView8(buffer, bufferIndex, |
| TruncateFloat64ToWord32(double_value)); |
| } |
| else if constexpr (kind == UINT16_ELEMENTS || kind == INT16_ELEMENTS) { |
| StoreDataView16(buffer, bufferIndex, |
| TruncateFloat64ToWord32(double_value), littleEndian); |
| } |
| else if constexpr (kind == UINT32_ELEMENTS || kind == INT32_ELEMENTS) { |
| StoreDataView32(buffer, bufferIndex, |
| TruncateFloat64ToWord32(double_value), littleEndian); |
| } |
| else if constexpr (kind == FLOAT32_ELEMENTS) { |
| let float_value: float32 = TruncateFloat64ToFloat32(double_value); |
| StoreDataView32(buffer, bufferIndex, |
| BitcastFloat32ToInt32(float_value), littleEndian); |
| } |
| else if constexpr (kind == FLOAT64_ELEMENTS) { |
| let low_word: uint32 = Float64ExtractLowWord32(double_value); |
| let high_word: uint32 = Float64ExtractHighWord32(double_value); |
| StoreDataView64(buffer, bufferIndex, low_word, high_word, |
| littleEndian); |
| } |
| } |
| return Undefined; |
| } |
| |
| javascript builtin DataViewPrototypeSetUint8( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, Undefined, |
| UINT8_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetInt8( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, Undefined, |
| INT8_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetUint16( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, UINT16_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetInt16( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, INT16_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetUint32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, UINT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetInt32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, INT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetFloat32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, FLOAT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetFloat64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, FLOAT64_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetBigUint64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, BIGUINT64_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeSetBigInt64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let value : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| let is_little_endian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| is_little_endian, BIGINT64_ELEMENTS); |
| } |
| |
| } |