// 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 == 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 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(kIncompatibleMethodReceiver, method); } } // ES6 section 24.2.4.1 get DataView.prototype.buffer javascript builtin DataViewPrototypeGetBuffer( js-implicit context: Context, 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: Context, 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: Context, 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, offset)); } else { return Convert<Smi>(LoadUint8(buffer.backing_store, offset)); } } macro LoadDataView16( buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, signed: constexpr bool): Number { const dataPointer: 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 (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; 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 == INT32_ELEMENTS) { return Convert<Number>(Signed(result)); } else if constexpr (kind == UINT32_ELEMENTS) { return Convert<Number>(result); } else if constexpr (kind == 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; 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; 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(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. if constexpr (Is64()) { // Given that // a) getIndex is in [0, kMaxSafeInteger] range // b) elementSize is in [1, 8] range // the addition can't overflow. if (getIndex + elementSize > viewSize) goto RangeError; } else { // In order to avoid operating on float64s we deal with uintptr values // and do two comparisons to handle potential uintptr overflow on // 32-bit architectures. const lastPossibleElementOffset: uintptr = viewSize - elementSize; // Check if lastPossibleElementOffset underflowed. if (lastPossibleElementOffset > viewSize) goto RangeError; if (getIndex > lastPossibleElementOffset) goto RangeError; } // 11. Let bufferIndex be getIndex + viewOffset. const bufferIndex: uintptr = getIndex + viewOffset; 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; } } label RangeError { ThrowRangeError(kInvalidDataViewAccessorOffset); } } transitioning javascript builtin DataViewPrototypeGetUint8( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; return DataViewGet(context, receiver, offset, Undefined, UINT8_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetInt8( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; return DataViewGet(context, receiver, offset, Undefined, INT8_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetUint16( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, UINT16_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetInt16( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, INT16_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetUint32( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, UINT32_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetInt32( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, INT32_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetFloat32( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, FLOAT32_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetFloat64( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, FLOAT64_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetBigUint64( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, BIGUINT64_ELEMENTS); } transitioning javascript builtin DataViewPrototypeGetBigInt64( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const isLittleEndian: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewGet( context, receiver, offset, isLittleEndian, 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, offset, value & 0xFF); } macro StoreDataView16( buffer: JSArrayBuffer, offset: uintptr, value: uint32, requestedLittleEndian: bool) { const dataPointer: RawPtr = buffer.backing_store; 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; 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; 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 == BIGUINT64_ELEMENTS || kind == 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(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. if constexpr (Is64()) { // Given that // a) getIndex is in [0, kMaxSafeInteger] range // b) elementSize is in [1, 8] range // the addition can't overflow. if (getIndex + elementSize > viewSize) goto RangeError; } else { // In order to avoid operating on float64s we deal with uintptr values // and do two comparisons to handle potential uintptr overflow on // 32-bit architectures. const lastPossibleElementOffset: uintptr = viewSize - elementSize; // Check if lastPossibleElementOffset underflowed. if (lastPossibleElementOffset > viewSize) goto RangeError; if (getIndex > lastPossibleElementOffset) goto RangeError; } // 13. Let bufferIndex be getIndex + viewOffset. const bufferIndex: uintptr = getIndex + viewOffset; if constexpr (kind == BIGUINT64_ELEMENTS || kind == 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 == UINT8_ELEMENTS || kind == INT8_ELEMENTS) { StoreDataView8( buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue)); } else if constexpr (kind == UINT16_ELEMENTS || kind == INT16_ELEMENTS) { StoreDataView16( buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), littleEndian); } else if constexpr (kind == UINT32_ELEMENTS || kind == INT32_ELEMENTS) { StoreDataView32( buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), littleEndian); } else if constexpr (kind == FLOAT32_ELEMENTS) { const floatValue: float32 = TruncateFloat64ToFloat32(doubleValue); StoreDataView32( buffer, bufferIndex, BitcastFloat32ToInt32(floatValue), littleEndian); } else if constexpr (kind == FLOAT64_ELEMENTS) { const lowWord: uint32 = Float64ExtractLowWord32(doubleValue); const highWord: uint32 = Float64ExtractHighWord32(doubleValue); StoreDataView64(buffer, bufferIndex, lowWord, highWord, littleEndian); } } return Undefined; } label RangeError { ThrowRangeError(kInvalidDataViewAccessorOffset); } } transitioning javascript builtin DataViewPrototypeSetUint8( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewSet( context, receiver, offset, value, Undefined, UINT8_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetInt8( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; return DataViewSet( context, receiver, offset, value, Undefined, INT8_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetUint16( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, UINT16_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetInt16( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, INT16_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetUint32( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, UINT32_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetInt32( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, INT32_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetFloat32( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, FLOAT32_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetFloat64( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, FLOAT64_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetBigUint64( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, BIGUINT64_ELEMENTS); } transitioning javascript builtin DataViewPrototypeSetBigInt64( js-implicit context: Context, receiver: JSAny)(...arguments): JSAny { const offset: JSAny = arguments.length > 0 ? arguments[0] : Undefined; const value: JSAny = arguments.length > 1 ? arguments[1] : Undefined; const isLittleEndian: JSAny = arguments.length > 2 ? arguments[2] : Undefined; return DataViewSet( context, receiver, offset, value, isLittleEndian, BIGINT64_ELEMENTS); } }