| // 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 LoadJSArrayBufferViewBuffer(JSArrayBufferView): JSArrayBuffer; |
| extern operator '.byte_length' |
| macro LoadJSArrayBufferViewByteLength(JSArrayBufferView): uintptr; |
| extern operator '.byte_offset' |
| macro LoadJSArrayBufferViewByteOffset(JSArrayBufferView): uintptr; |
| extern operator '.external_pointer' |
| macro LoadJSDataViewExternalPointer(JSDataView): 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 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( |
| context: Context, receiver: Object, ...arguments): Number { |
| let dataView: JSDataView = ValidateDataView( |
| context, receiver, 'get DataView.prototype.byte_length'); |
| if (WasNeutered(dataView)) { |
| // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError |
| // here if the JSArrayBuffer of the {dataView} was neutered. |
| return 0; |
| } |
| return Convert<Number>(dataView.byte_length); |
| } |
| |
| // ES6 section 24.2.4.3 get DataView.prototype.byteOffset |
| javascript builtin DataViewPrototypeGetByteOffset( |
| context: Context, receiver: Object, ...arguments): Number { |
| let dataView: JSDataView = ValidateDataView( |
| context, receiver, 'get DataView.prototype.byte_offset'); |
| if (WasNeutered(dataView)) { |
| // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError |
| // here if the JSArrayBuffer of the {dataView} was neutered. |
| 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 LoadUint8(RawPtr, uintptr): uint32; |
| extern macro LoadInt8(RawPtr, uintptr): int32; |
| |
| macro LoadDataView8(dataView: JSDataView, offset: uintptr, |
| signed: constexpr bool): Smi { |
| if constexpr (signed) { |
| return Convert<Smi>(LoadInt8(dataView.external_pointer, offset)); |
| } else { |
| return Convert<Smi>(LoadUint8(dataView.external_pointer, offset)); |
| } |
| } |
| |
| macro LoadDataView16(dataView: JSDataView, offset: uintptr, |
| requestedLittleEndian: bool, |
| signed: constexpr bool): Number { |
| let dataPointer: RawPtr = dataView.external_pointer; |
| 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(dataView: JSDataView, offset: uintptr, |
| requestedLittleEndian: bool, |
| kind: constexpr ElementsKind): Number { |
| let dataPointer: RawPtr = dataView.external_pointer; |
| let b0: uint32 = LoadUint8(dataPointer, offset); |
| let b1: uint32 = LoadUint8(dataPointer, offset + 1); |
| let b2: uint32 = LoadUint8(dataPointer, offset + 2); |
| let 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 == INT32_ELEMENTS) { |
| return Convert<Number>(Signed(result)); |
| } else if constexpr (kind == UINT32_ELEMENTS) { |
| return Convert<Number>(result); |
| } else if constexpr (kind == FLOAT32_ELEMENTS) { |
| let floatRes: float64 = Convert<float64>(BitcastInt32ToFloat32(result)); |
| return Convert<Number>(floatRes); |
| } else { |
| unreachable; |
| } |
| } |
| |
| macro LoadDataViewFloat64(dataView: JSDataView, offset: uintptr, |
| requestedLittleEndian: bool): Number { |
| let dataPointer: RawPtr = dataView.external_pointer; |
| let b0: uint32 = LoadUint8(dataPointer, offset); |
| let b1: uint32 = LoadUint8(dataPointer, offset + 1); |
| let b2: uint32 = LoadUint8(dataPointer, offset + 2); |
| let b3: uint32 = LoadUint8(dataPointer, offset + 3); |
| let b4: uint32 = LoadUint8(dataPointer, offset + 4); |
| let b5: uint32 = LoadUint8(dataPointer, offset + 5); |
| let b6: uint32 = LoadUint8(dataPointer, offset + 6); |
| let 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); |
| } |
| |
| 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(isPositive: 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 (isPositive) { |
| 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(lowWord: uint32, highWord: uint32, |
| signed: constexpr bool): BigInt { |
| |
| // 0n is represented by a zero-length BigInt. |
| if (lowWord == 0 && highWord == 0) { |
| return AllocateBigInt(kZeroDigitBigInt); |
| } |
| |
| let isPositive: bool = true; |
| let highPart: intptr = Signed(Convert<uintptr>(highWord)); |
| let lowPart: intptr = Signed(Convert<uintptr>(lowWord)); |
| let rawValue: intptr = (highPart << 32) + lowPart; |
| |
| if constexpr (signed) { |
| if (rawValue < 0) { |
| isPositive = false; |
| // We have to store the absolute value of rawValue in the digit. |
| rawValue = 0 - rawValue; |
| } |
| } |
| |
| // Allocate the BigInt and store the absolute value. |
| let result: BigInt = CreateEmptyBigInt(isPositive, kOneDigitBigInt); |
| |
| StoreBigIntDigit(result, 0, Unsigned(rawValue)); |
| |
| return result; |
| } |
| |
| // Create a BigInt on a 32-bit architecture from two 32-bit values. |
| macro MakeBigIntOn32Bit(lowWord: uint32, highWord: uint32, |
| signed: constexpr bool): BigInt { |
| |
| // 0n is represented by a zero-length BigInt. |
| if (lowWord == 0 && highWord == 0) { |
| return AllocateBigInt(kZeroDigitBigInt); |
| } |
| |
| // On a 32-bit platform, we might need 1 or 2 digits to store the number. |
| let needTwoDigits: bool = false; |
| let isPositive: bool = true; |
| |
| // 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) { |
| isPositive = 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. |
| 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: BigInt; |
| if (needTwoDigits) { |
| result = CreateEmptyBigInt(isPositive, kTwoDigitBigInt); |
| } else { |
| result = CreateEmptyBigInt(isPositive, kOneDigitBigInt); |
| } |
| |
| // Finally, write the digit(s) to the BigInt. |
| StoreBigIntDigit(result, 0, Unsigned(Convert<intptr>(lowPart))); |
| |
| if (needTwoDigits) { |
| StoreBigIntDigit(result, 1, Unsigned(Convert<intptr>(highPart))); |
| } |
| |
| return result; |
| } |
| |
| macro MakeBigInt(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(dataView: JSDataView, offset: uintptr, |
| requestedLittleEndian: bool, |
| signed: constexpr bool): BigInt { |
| let dataPointer: RawPtr = dataView.external_pointer; |
| let b0: uint32 = LoadUint8(dataPointer, offset); |
| let b1: uint32 = LoadUint8(dataPointer, offset + 1); |
| let b2: uint32 = LoadUint8(dataPointer, offset + 2); |
| let b3: uint32 = LoadUint8(dataPointer, offset + 3); |
| let b4: uint32 = LoadUint8(dataPointer, offset + 4); |
| let b5: uint32 = LoadUint8(dataPointer, offset + 5); |
| let b6: uint32 = LoadUint8(dataPointer, offset + 6); |
| let 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 ToSmiIndex(Object, Context): Smi labels RangeError; |
| extern macro DataViewElementSize(constexpr ElementsKind): constexpr int31; |
| |
| macro DataViewGet(context: Context, |
| receiver: Object, |
| offset: Object, |
| requestedLittleEndian: Object, |
| kind: constexpr ElementsKind): Numeric { |
| |
| let dataView: 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(requestedLittleEndian); |
| let buffer: JSArrayBuffer = dataView.buffer; |
| |
| if (IsDetachedBuffer(buffer)) { |
| ThrowTypeError(context, kDetachedOperation, |
| MakeDataViewGetterNameString(kind)); |
| } |
| |
| let getIndexFloat: float64 = Convert<float64>(getIndex); |
| let getIndexWord: uintptr = Convert<uintptr>(getIndexFloat); |
| |
| let viewSizeFloat: float64 = Convert<float64>(dataView.byte_length); |
| let elementSizeFloat: float64 = Convert<float64>(DataViewElementSize(kind)); |
| |
| if (getIndexFloat + elementSizeFloat > viewSizeFloat) { |
| ThrowRangeError(context, kInvalidDataViewAccessorOffset); |
| } |
| |
| if constexpr (kind == UINT8_ELEMENTS) { |
| return LoadDataView8(dataView, getIndexWord, false); |
| } else if constexpr (kind == INT8_ELEMENTS) { |
| return LoadDataView8(dataView, getIndexWord, true); |
| } else if constexpr (kind == UINT16_ELEMENTS) { |
| return LoadDataView16(dataView, getIndexWord, littleEndian, false); |
| } else if constexpr (kind == INT16_ELEMENTS) { |
| return LoadDataView16(dataView, getIndexWord, littleEndian, true); |
| } else if constexpr (kind == UINT32_ELEMENTS) { |
| return LoadDataView32(dataView, getIndexWord, littleEndian, kind); |
| } else if constexpr (kind == INT32_ELEMENTS) { |
| return LoadDataView32(dataView, getIndexWord, littleEndian, kind); |
| } else if constexpr (kind == FLOAT32_ELEMENTS) { |
| return LoadDataView32(dataView, getIndexWord, littleEndian, kind); |
| } else if constexpr (kind == FLOAT64_ELEMENTS) { |
| return LoadDataViewFloat64(dataView, getIndexWord, littleEndian); |
| } else if constexpr (kind == BIGUINT64_ELEMENTS) { |
| return LoadDataViewBigInt(dataView, getIndexWord, littleEndian, false); |
| } else if constexpr (kind == BIGINT64_ELEMENTS) { |
| return LoadDataViewBigInt(dataView, getIndexWord, 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 isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| UINT16_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetInt16( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| INT16_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetUint32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| UINT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetInt32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| INT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetFloat32( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| FLOAT32_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetFloat64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| FLOAT64_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetBigUint64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| BIGUINT64_ELEMENTS); |
| } |
| |
| javascript builtin DataViewPrototypeGetBigInt64( |
| context: Context, receiver: Object, ...arguments): Object { |
| let offset: Object = arguments.length > 0 ? |
| arguments[0] : |
| Undefined; |
| let isLittleEndian : Object = arguments.length > 1 ? |
| arguments[1] : |
| Undefined; |
| return DataViewGet(context, receiver, offset, isLittleEndian, |
| 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, uintptr, uint32): void; |
| |
| macro StoreDataView8(dataView: JSDataView, offset: uintptr, |
| value: uint32) { |
| StoreWord8(dataView.external_pointer, offset, value & 0xFF); |
| } |
| |
| macro StoreDataView16(dataView: JSDataView, offset: uintptr, value: uint32, |
| requestedLittleEndian: bool) { |
| let dataPointer: RawPtr = dataView.external_pointer; |
| let b0: uint32 = value & 0xFF; |
| let 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(dataView: JSDataView, offset: uintptr, value: uint32, |
| requestedLittleEndian: bool) { |
| let dataPointer: RawPtr = dataView.external_pointer; |
| 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 (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(dataView: JSDataView, offset: uintptr, |
| lowWord: uint32, highWord: uint32, |
| requestedLittleEndian: bool) { |
| let dataPointer: RawPtr = dataView.external_pointer; |
| let b0: uint32 = lowWord & 0xFF; |
| let b1: uint32 = (lowWord >>> 8) & 0xFF; |
| let b2: uint32 = (lowWord >>> 16) & 0xFF; |
| let b3: uint32 = lowWord >>> 24; |
| |
| let b4: uint32 = highWord & 0xFF; |
| let b5: uint32 = (highWord >>> 8) & 0xFF; |
| let b6: uint32 = (highWord >>> 16) & 0xFF; |
| let 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 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(dataView: JSDataView, offset: uintptr, |
| bigIntValue: BigInt, |
| requestedLittleEndian: bool) { |
| |
| let length: uintptr = DataViewDecodeBigIntLength(bigIntValue); |
| let sign: uintptr = 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. |
| let value: uintptr = 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>(LoadBigIntDigit(bigIntValue, 0)); |
| if (length >= 2) { // Only load the second digit if there is one. |
| highWord = Convert<uint32>(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(dataView, offset, lowWord, highWord, |
| requestedLittleEndian); |
| } |
| |
| macro DataViewSet(context: Context, |
| receiver: Object, |
| offset: Object, |
| value: Object, |
| requestedLittleEndian: Object, |
| kind: constexpr ElementsKind): Object { |
| |
| let dataView: 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(requestedLittleEndian); |
| let buffer: JSArrayBuffer = dataView.buffer; |
| |
| let bigIntValue: BigInt; |
| let numValue: 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) { |
| bigIntValue = ToBigInt(context, value); |
| } else { |
| numValue = ToNumber(context, value); |
| } |
| |
| if (IsDetachedBuffer(buffer)) { |
| ThrowTypeError(context, kDetachedOperation, |
| MakeDataViewSetterNameString(kind)); |
| } |
| |
| let getIndexFloat: float64 = Convert<float64>(getIndex); |
| let getIndexWord: uintptr = Convert<uintptr>(getIndexFloat); |
| |
| let viewSizeFloat: float64 = Convert<float64>(dataView.byte_length); |
| let elementSizeFloat: float64 = Convert<float64>(DataViewElementSize(kind)); |
| |
| if (getIndexFloat + elementSizeFloat > viewSizeFloat) { |
| ThrowRangeError(context, kInvalidDataViewAccessorOffset); |
| } |
| |
| if constexpr (kind == BIGUINT64_ELEMENTS || kind == BIGINT64_ELEMENTS) { |
| StoreDataViewBigInt(dataView, getIndexWord, bigIntValue, |
| littleEndian); |
| } |
| else { |
| let doubleValue: float64 = ChangeNumberToFloat64(numValue); |
| |
| if constexpr (kind == UINT8_ELEMENTS || kind == INT8_ELEMENTS) { |
| StoreDataView8(dataView, getIndexWord, |
| TruncateFloat64ToWord32(doubleValue)); |
| } |
| else if constexpr (kind == UINT16_ELEMENTS || kind == INT16_ELEMENTS) { |
| StoreDataView16(dataView, getIndexWord, |
| TruncateFloat64ToWord32(doubleValue), littleEndian); |
| } |
| else if constexpr (kind == UINT32_ELEMENTS || kind == INT32_ELEMENTS) { |
| StoreDataView32(dataView, getIndexWord, |
| TruncateFloat64ToWord32(doubleValue), littleEndian); |
| } |
| else if constexpr (kind == FLOAT32_ELEMENTS) { |
| let floatValue: float32 = TruncateFloat64ToFloat32(doubleValue); |
| StoreDataView32(dataView, getIndexWord, |
| BitcastFloat32ToInt32(floatValue), littleEndian); |
| } |
| else if constexpr (kind == FLOAT64_ELEMENTS) { |
| let lowWord: uint32 = Float64ExtractLowWord32(doubleValue); |
| let highWord: uint32 = Float64ExtractHighWord32(doubleValue); |
| StoreDataView64(dataView, getIndexWord, lowWord, highWord, |
| 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, 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 isLittleEndian : Object = arguments.length > 2 ? |
| arguments[2] : |
| Undefined; |
| return DataViewSet(context, receiver, offset, value, |
| isLittleEndian, BIGINT64_ELEMENTS); |
| } |
| |
| } |