| // 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); |
| } |
| } |