1// Copyright 2018 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include 'src/builtins/builtins-data-view-gen.h' 6 7namespace data_view { 8 9const kBuiltinNameByteLength: constexpr string = 10 'DateView.prototype.byteLength'; 11const kBuiltinNameByteOffset: constexpr string = 12 'DateView.prototype.byteOffset'; 13 14macro MakeDataViewGetterNameString(kind: constexpr ElementsKind): String { 15 if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { 16 return 'DataView.prototype.getUint8'; 17 } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { 18 return 'DataView.prototype.getInt8'; 19 } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { 20 return 'DataView.prototype.getUint16'; 21 } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { 22 return 'DataView.prototype.getInt16'; 23 } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { 24 return 'DataView.prototype.getUint32'; 25 } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { 26 return 'DataView.prototype.getInt32'; 27 } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { 28 return 'DataView.prototype.getFloat32'; 29 } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { 30 return 'DataView.prototype.getFloat64'; 31 } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { 32 return 'DataView.prototype.getBigInt64'; 33 } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { 34 return 'DataView.prototype.getBigUint64'; 35 } else { 36 unreachable; 37 } 38} 39 40macro MakeDataViewSetterNameString(kind: constexpr ElementsKind): String { 41 if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { 42 return 'DataView.prototype.setUint8'; 43 } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { 44 return 'DataView.prototype.setInt8'; 45 } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { 46 return 'DataView.prototype.setUint16'; 47 } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { 48 return 'DataView.prototype.setInt16'; 49 } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { 50 return 'DataView.prototype.setUint32'; 51 } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { 52 return 'DataView.prototype.setInt32'; 53 } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { 54 return 'DataView.prototype.setFloat32'; 55 } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { 56 return 'DataView.prototype.setFloat64'; 57 } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { 58 return 'DataView.prototype.setBigInt64'; 59 } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { 60 return 'DataView.prototype.setBigUint64'; 61 } else { 62 unreachable; 63 } 64} 65 66macro WasDetached(view: JSArrayBufferView): bool { 67 return IsDetachedBuffer(view.buffer); 68} 69 70macro ValidateDataView(context: Context, o: JSAny, method: String): JSDataView { 71 try { 72 return Cast<JSDataView>(o) otherwise CastError; 73 } label CastError { 74 ThrowTypeError(MessageTemplate::kIncompatibleMethodReceiver, method); 75 } 76} 77 78// ES6 section 24.2.4.1 get DataView.prototype.buffer 79javascript builtin DataViewPrototypeGetBuffer( 80 js-implicit context: NativeContext, 81 receiver: JSAny)(...arguments): JSArrayBuffer { 82 const dataView: JSDataView = 83 ValidateDataView(context, receiver, 'get DataView.prototype.buffer'); 84 return dataView.buffer; 85} 86 87// ES6 section 24.2.4.2 get DataView.prototype.byteLength 88javascript builtin DataViewPrototypeGetByteLength( 89 js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number { 90 const dataView: JSDataView = 91 ValidateDataView(context, receiver, 'get DataView.prototype.byte_length'); 92 if (IsVariableLengthJSArrayBufferView(dataView)) { 93 try { 94 const byteLength = LoadVariableLengthJSArrayBufferViewByteLength( 95 dataView, dataView.buffer) otherwise DetachedOrOutOfBounds; 96 return Convert<Number>(byteLength); 97 } label DetachedOrOutOfBounds { 98 ThrowTypeError( 99 MessageTemplate::kDetachedOperation, kBuiltinNameByteLength); 100 } 101 } else { 102 if (WasDetached(dataView)) { 103 ThrowTypeError( 104 MessageTemplate::kDetachedOperation, kBuiltinNameByteLength); 105 } 106 return Convert<Number>(dataView.byte_length); 107 } 108} 109 110// ES6 section 24.2.4.3 get DataView.prototype.byteOffset 111javascript builtin DataViewPrototypeGetByteOffset( 112 js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number { 113 const dataView: JSDataView = 114 ValidateDataView(context, receiver, 'get DataView.prototype.byte_offset'); 115 try { 116 typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView) 117 otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds; 118 } label DetachedOrOutOfBounds { 119 ThrowTypeError(MessageTemplate::kDetachedOperation, kBuiltinNameByteOffset); 120 } label NotDetachedNorOutOfBounds { 121 return Convert<Number>(dataView.byte_offset); 122 } 123} 124 125extern macro BitcastInt32ToFloat32(uint32): float32; 126extern macro BitcastFloat32ToInt32(float32): uint32; 127extern macro Float64ExtractLowWord32(float64): uint32; 128extern macro Float64ExtractHighWord32(float64): uint32; 129extern macro Float64InsertLowWord32(float64, uint32): float64; 130extern macro Float64InsertHighWord32(float64, uint32): float64; 131 132extern macro DataViewBuiltinsAssembler::LoadUint8(RawPtr, uintptr): uint32; 133extern macro DataViewBuiltinsAssembler::LoadInt8(RawPtr, uintptr): int32; 134 135macro LoadDataView8( 136 buffer: JSArrayBuffer, offset: uintptr, signed: constexpr bool): Smi { 137 if constexpr (signed) { 138 return Convert<Smi>(LoadInt8(buffer.backing_store_ptr, offset)); 139 } else { 140 return Convert<Smi>(LoadUint8(buffer.backing_store_ptr, offset)); 141 } 142} 143 144macro LoadDataView16( 145 buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, 146 signed: constexpr bool): Number { 147 const dataPointer: RawPtr = buffer.backing_store_ptr; 148 149 let b0: int32; 150 let b1: int32; 151 let result: int32; 152 153 // Sign-extend the most significant byte by loading it as an Int8. 154 if (requestedLittleEndian) { 155 b0 = Signed(LoadUint8(dataPointer, offset)); 156 b1 = LoadInt8(dataPointer, offset + 1); 157 result = (b1 << 8) + b0; 158 } else { 159 b0 = LoadInt8(dataPointer, offset); 160 b1 = Signed(LoadUint8(dataPointer, offset + 1)); 161 result = (b0 << 8) + b1; 162 } 163 if constexpr (signed) { 164 return Convert<Smi>(result); 165 } else { 166 // Bit-mask the higher bits to prevent sign extension if we're unsigned. 167 return Convert<Smi>(result & 0xFFFF); 168 } 169} 170 171macro LoadDataView32( 172 buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, 173 kind: constexpr ElementsKind): Number { 174 const dataPointer: RawPtr = buffer.backing_store_ptr; 175 176 const b0: uint32 = LoadUint8(dataPointer, offset); 177 const b1: uint32 = LoadUint8(dataPointer, offset + 1); 178 const b2: uint32 = LoadUint8(dataPointer, offset + 2); 179 const b3: uint32 = LoadUint8(dataPointer, offset + 3); 180 let result: uint32; 181 182 if (requestedLittleEndian) { 183 result = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; 184 } else { 185 result = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; 186 } 187 188 if constexpr (kind == ElementsKind::INT32_ELEMENTS) { 189 return Convert<Number>(Signed(result)); 190 } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { 191 return Convert<Number>(result); 192 } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { 193 const floatRes: float64 = Convert<float64>(BitcastInt32ToFloat32(result)); 194 return Convert<Number>(floatRes); 195 } else { 196 unreachable; 197 } 198} 199 200macro LoadDataViewFloat64( 201 buffer: JSArrayBuffer, offset: uintptr, 202 requestedLittleEndian: bool): Number { 203 const dataPointer: RawPtr = buffer.backing_store_ptr; 204 205 const b0: uint32 = LoadUint8(dataPointer, offset); 206 const b1: uint32 = LoadUint8(dataPointer, offset + 1); 207 const b2: uint32 = LoadUint8(dataPointer, offset + 2); 208 const b3: uint32 = LoadUint8(dataPointer, offset + 3); 209 const b4: uint32 = LoadUint8(dataPointer, offset + 4); 210 const b5: uint32 = LoadUint8(dataPointer, offset + 5); 211 const b6: uint32 = LoadUint8(dataPointer, offset + 6); 212 const b7: uint32 = LoadUint8(dataPointer, offset + 7); 213 let lowWord: uint32; 214 let highWord: uint32; 215 216 if (requestedLittleEndian) { 217 lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; 218 highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; 219 } else { 220 highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; 221 lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; 222 } 223 224 let result: float64 = 0; 225 result = Float64InsertLowWord32(result, lowWord); 226 result = Float64InsertHighWord32(result, highWord); 227 228 return Convert<Number>(result); 229} 230 231const kZeroDigitBigInt: constexpr int31 = 0; 232const kOneDigitBigInt: constexpr int31 = 1; 233const kTwoDigitBigInt: constexpr int31 = 2; 234 235// Create a BigInt on a 64-bit architecture from two 32-bit values. 236macro MakeBigIntOn64Bit(implicit context: Context)( 237 lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { 238 // 0n is represented by a zero-length BigInt. 239 if (lowWord == 0 && highWord == 0) { 240 return Convert<BigInt>(bigint::AllocateBigInt(kZeroDigitBigInt)); 241 } 242 243 let sign: uint32 = bigint::kPositiveSign; 244 const highPart: intptr = Signed(Convert<uintptr>(highWord)); 245 const lowPart: intptr = Signed(Convert<uintptr>(lowWord)); 246 let rawValue: intptr = (highPart << 32) + lowPart; 247 248 if constexpr (signed) { 249 if (rawValue < 0) { 250 sign = bigint::kNegativeSign; 251 // We have to store the absolute value of rawValue in the digit. 252 rawValue = 0 - rawValue; 253 } 254 } 255 256 // Allocate the BigInt and store the absolute value. 257 const result: MutableBigInt = 258 bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt); 259 bigint::StoreBigIntDigit(result, 0, Unsigned(rawValue)); 260 return Convert<BigInt>(result); 261} 262 263// Create a BigInt on a 32-bit architecture from two 32-bit values. 264macro MakeBigIntOn32Bit(implicit context: Context)( 265 lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { 266 // 0n is represented by a zero-length BigInt. 267 if (lowWord == 0 && highWord == 0) { 268 return Convert<BigInt>(bigint::AllocateBigInt(kZeroDigitBigInt)); 269 } 270 271 // On a 32-bit platform, we might need 1 or 2 digits to store the number. 272 let needTwoDigits: bool = false; 273 let sign: uint32 = bigint::kPositiveSign; 274 275 // We need to do some math on lowWord and highWord, 276 // so Convert them to int32. 277 let lowPart: int32 = Signed(lowWord); 278 let highPart: int32 = Signed(highWord); 279 280 // If highWord == 0, the number is positive, and we only need 1 digit, 281 // so we don't have anything to do. 282 // Otherwise, all cases are possible. 283 if (highWord != 0) { 284 if constexpr (signed) { 285 // If highPart < 0, the number is always negative. 286 if (highPart < 0) { 287 sign = bigint::kNegativeSign; 288 289 // We have to compute the absolute value by hand. 290 // There will be a negative carry from the low word 291 // to the high word iff low != 0. 292 highPart = 0 - highPart; 293 if (lowPart != 0) { 294 highPart = highPart - 1; 295 } 296 lowPart = 0 - lowPart; 297 298 // Here, highPart could be 0 again so we might have 1 or 2 digits. 299 if (highPart != 0) { 300 needTwoDigits = true; 301 } 302 303 } else { 304 // In this case, the number is positive, and we need 2 digits. 305 needTwoDigits = true; 306 } 307 308 } else { 309 // In this case, the number is positive (unsigned), 310 // and we need 2 digits. 311 needTwoDigits = true; 312 } 313 } 314 315 // Allocate the BigInt with the right sign and length. 316 let result: MutableBigInt; 317 if (needTwoDigits) { 318 result = bigint::AllocateEmptyBigInt(sign, kTwoDigitBigInt); 319 } else { 320 result = bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt); 321 } 322 323 // Finally, write the digit(s) to the BigInt. 324 bigint::StoreBigIntDigit(result, 0, Unsigned(Convert<intptr>(lowPart))); 325 if (needTwoDigits) { 326 bigint::StoreBigIntDigit(result, 1, Unsigned(Convert<intptr>(highPart))); 327 } 328 return Convert<BigInt>(result); 329} 330 331macro MakeBigInt(implicit context: Context)( 332 lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt { 333 // A BigInt digit has the platform word size, so we only need one digit 334 // on 64-bit platforms but may need two on 32-bit. 335 if constexpr (Is64()) { 336 return MakeBigIntOn64Bit(lowWord, highWord, signed); 337 } else { 338 return MakeBigIntOn32Bit(lowWord, highWord, signed); 339 } 340} 341 342macro LoadDataViewBigInt(implicit context: Context)( 343 buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool, 344 signed: constexpr bool): BigInt { 345 const dataPointer: RawPtr = buffer.backing_store_ptr; 346 347 const b0: uint32 = LoadUint8(dataPointer, offset); 348 const b1: uint32 = LoadUint8(dataPointer, offset + 1); 349 const b2: uint32 = LoadUint8(dataPointer, offset + 2); 350 const b3: uint32 = LoadUint8(dataPointer, offset + 3); 351 const b4: uint32 = LoadUint8(dataPointer, offset + 4); 352 const b5: uint32 = LoadUint8(dataPointer, offset + 5); 353 const b6: uint32 = LoadUint8(dataPointer, offset + 6); 354 const b7: uint32 = LoadUint8(dataPointer, offset + 7); 355 let lowWord: uint32; 356 let highWord: uint32; 357 358 if (requestedLittleEndian) { 359 lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0; 360 highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4; 361 } else { 362 highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; 363 lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7; 364 } 365 366 return MakeBigInt(lowWord, highWord, signed); 367} 368 369extern macro DataViewBuiltinsAssembler::DataViewElementSize( 370 constexpr ElementsKind): constexpr int31; 371 372// GetViewValue ( view, requestIndex, isLittleEndian, type ) 373// https://tc39.es/ecma262/#sec-getviewvalue 374transitioning macro DataViewGet( 375 context: Context, receiver: JSAny, requestIndex: JSAny, 376 requestedLittleEndian: JSAny, kind: constexpr ElementsKind): Numeric { 377 // 1. Perform ? RequireInternalSlot(view, [[DataView]]). 378 // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. 379 const dataView: JSDataView = 380 ValidateDataView(context, receiver, MakeDataViewGetterNameString(kind)); 381 382 try { 383 // 3. Let getIndex be ? ToIndex(requestIndex). 384 const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError; 385 386 // 4. Set isLittleEndian to ! ToBoolean(isLittleEndian). 387 const littleEndian: bool = ToBoolean(requestedLittleEndian); 388 389 // 5. Let buffer be view.[[ViewedArrayBuffer]]. 390 const buffer: JSArrayBuffer = dataView.buffer; 391 392 // 6. Let getBufferByteLength be 393 // MakeIdempotentArrayBufferByteLengthGetter(Unordered). 394 // 7. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a 395 // TypeError exception. 396 try { 397 typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView) 398 otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds; 399 } label DetachedOrOutOfBounds { 400 ThrowTypeError( 401 MessageTemplate::kDetachedOperation, 402 MakeDataViewGetterNameString(kind)); 403 } label NotDetachedNorOutOfBounds {} 404 405 // 8. Let viewOffset be view.[[ByteOffset]]. 406 const viewOffset: uintptr = dataView.byte_offset; 407 408 // 9. Let viewSize be GetViewByteLength(view, getBufferByteLength). 409 let viewSize: uintptr; 410 if (dataView.bit_field.is_length_tracking) { 411 viewSize = LoadVariableLengthJSArrayBufferViewByteLength( 412 dataView, dataView.buffer) otherwise unreachable; 413 } else { 414 viewSize = dataView.byte_length; 415 } 416 417 // 10. Let elementSize be the Element Size value specified in Table 62 418 // for Element Type type. 419 const elementSize: uintptr = DataViewElementSize(kind); 420 421 // 11. If getIndex + elementSize > viewSize, throw a RangeError exception. 422 CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize) 423 otherwise RangeError; 424 425 // 12. Let bufferIndex be getIndex + viewOffset. 426 const bufferIndex: uintptr = getIndex + viewOffset; 427 428 if constexpr (kind == ElementsKind::UINT8_ELEMENTS) { 429 return LoadDataView8(buffer, bufferIndex, false); 430 } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) { 431 return LoadDataView8(buffer, bufferIndex, true); 432 } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) { 433 return LoadDataView16(buffer, bufferIndex, littleEndian, false); 434 } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) { 435 return LoadDataView16(buffer, bufferIndex, littleEndian, true); 436 } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) { 437 return LoadDataView32(buffer, bufferIndex, littleEndian, kind); 438 } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) { 439 return LoadDataView32(buffer, bufferIndex, littleEndian, kind); 440 } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { 441 return LoadDataView32(buffer, bufferIndex, littleEndian, kind); 442 } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { 443 return LoadDataViewFloat64(buffer, bufferIndex, littleEndian); 444 } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) { 445 return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false); 446 } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) { 447 return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true); 448 } else { 449 unreachable; 450 } 451 } label RangeError { 452 ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset); 453 } 454} 455 456transitioning javascript builtin DataViewPrototypeGetUint8( 457 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 458 const offset: JSAny = arguments[0]; 459 return DataViewGet( 460 context, receiver, offset, Undefined, ElementsKind::UINT8_ELEMENTS); 461} 462 463transitioning javascript builtin DataViewPrototypeGetInt8( 464 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 465 const offset: JSAny = arguments[0]; 466 return DataViewGet( 467 context, receiver, offset, Undefined, ElementsKind::INT8_ELEMENTS); 468} 469 470transitioning javascript builtin DataViewPrototypeGetUint16( 471 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 472 const offset: JSAny = arguments[0]; 473 const isLittleEndian: JSAny = arguments[1]; 474 return DataViewGet( 475 context, receiver, offset, isLittleEndian, ElementsKind::UINT16_ELEMENTS); 476} 477 478transitioning javascript builtin DataViewPrototypeGetInt16( 479 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 480 const offset: JSAny = arguments[0]; 481 const isLittleEndian: JSAny = arguments[1]; 482 return DataViewGet( 483 context, receiver, offset, isLittleEndian, ElementsKind::INT16_ELEMENTS); 484} 485 486transitioning javascript builtin DataViewPrototypeGetUint32( 487 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 488 const offset: JSAny = arguments[0]; 489 const isLittleEndian: JSAny = arguments[1]; 490 return DataViewGet( 491 context, receiver, offset, isLittleEndian, ElementsKind::UINT32_ELEMENTS); 492} 493 494transitioning javascript builtin DataViewPrototypeGetInt32( 495 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 496 const offset: JSAny = arguments[0]; 497 const isLittleEndian: JSAny = arguments[1]; 498 return DataViewGet( 499 context, receiver, offset, isLittleEndian, ElementsKind::INT32_ELEMENTS); 500} 501 502transitioning javascript builtin DataViewPrototypeGetFloat32( 503 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 504 const offset: JSAny = arguments[0]; 505 const isLittleEndian: JSAny = arguments[1]; 506 return DataViewGet( 507 context, receiver, offset, isLittleEndian, 508 ElementsKind::FLOAT32_ELEMENTS); 509} 510 511transitioning javascript builtin DataViewPrototypeGetFloat64( 512 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 513 const offset: JSAny = arguments[0]; 514 const isLittleEndian: JSAny = arguments[1]; 515 return DataViewGet( 516 context, receiver, offset, isLittleEndian, 517 ElementsKind::FLOAT64_ELEMENTS); 518} 519 520transitioning javascript builtin DataViewPrototypeGetBigUint64( 521 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 522 const offset: JSAny = arguments[0]; 523 const isLittleEndian: JSAny = arguments[1]; 524 return DataViewGet( 525 context, receiver, offset, isLittleEndian, 526 ElementsKind::BIGUINT64_ELEMENTS); 527} 528 529transitioning javascript builtin DataViewPrototypeGetBigInt64( 530 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 531 const offset: JSAny = arguments[0]; 532 const isLittleEndian: JSAny = arguments[1]; 533 return DataViewGet( 534 context, receiver, offset, isLittleEndian, 535 ElementsKind::BIGINT64_ELEMENTS); 536} 537 538extern macro ToNumber(Context, JSAny): Number; 539extern macro ToBigInt(Context, JSAny): BigInt; 540extern macro TruncateFloat64ToWord32(float64): uint32; 541 542extern macro DataViewBuiltinsAssembler::StoreWord8( 543 RawPtr, uintptr, uint32): void; 544 545macro StoreDataView8( 546 buffer: JSArrayBuffer, offset: uintptr, value: uint32): void { 547 StoreWord8(buffer.backing_store_ptr, offset, value & 0xFF); 548} 549 550macro StoreDataView16( 551 buffer: JSArrayBuffer, offset: uintptr, value: uint32, 552 requestedLittleEndian: bool): void { 553 const dataPointer: RawPtr = buffer.backing_store_ptr; 554 555 const b0: uint32 = value & 0xFF; 556 const b1: uint32 = (value >>> 8) & 0xFF; 557 558 if (requestedLittleEndian) { 559 StoreWord8(dataPointer, offset, b0); 560 StoreWord8(dataPointer, offset + 1, b1); 561 } else { 562 StoreWord8(dataPointer, offset, b1); 563 StoreWord8(dataPointer, offset + 1, b0); 564 } 565} 566 567macro StoreDataView32( 568 buffer: JSArrayBuffer, offset: uintptr, value: uint32, 569 requestedLittleEndian: bool): void { 570 const dataPointer: RawPtr = buffer.backing_store_ptr; 571 572 const b0: uint32 = value & 0xFF; 573 const b1: uint32 = (value >>> 8) & 0xFF; 574 const b2: uint32 = (value >>> 16) & 0xFF; 575 const b3: uint32 = value >>> 24; // We don't need to mask here. 576 577 if (requestedLittleEndian) { 578 StoreWord8(dataPointer, offset, b0); 579 StoreWord8(dataPointer, offset + 1, b1); 580 StoreWord8(dataPointer, offset + 2, b2); 581 StoreWord8(dataPointer, offset + 3, b3); 582 } else { 583 StoreWord8(dataPointer, offset, b3); 584 StoreWord8(dataPointer, offset + 1, b2); 585 StoreWord8(dataPointer, offset + 2, b1); 586 StoreWord8(dataPointer, offset + 3, b0); 587 } 588} 589 590macro StoreDataView64( 591 buffer: JSArrayBuffer, offset: uintptr, lowWord: uint32, highWord: uint32, 592 requestedLittleEndian: bool): void { 593 const dataPointer: RawPtr = buffer.backing_store_ptr; 594 595 const b0: uint32 = lowWord & 0xFF; 596 const b1: uint32 = (lowWord >>> 8) & 0xFF; 597 const b2: uint32 = (lowWord >>> 16) & 0xFF; 598 const b3: uint32 = lowWord >>> 24; 599 600 const b4: uint32 = highWord & 0xFF; 601 const b5: uint32 = (highWord >>> 8) & 0xFF; 602 const b6: uint32 = (highWord >>> 16) & 0xFF; 603 const b7: uint32 = highWord >>> 24; 604 605 if (requestedLittleEndian) { 606 StoreWord8(dataPointer, offset, b0); 607 StoreWord8(dataPointer, offset + 1, b1); 608 StoreWord8(dataPointer, offset + 2, b2); 609 StoreWord8(dataPointer, offset + 3, b3); 610 StoreWord8(dataPointer, offset + 4, b4); 611 StoreWord8(dataPointer, offset + 5, b5); 612 StoreWord8(dataPointer, offset + 6, b6); 613 StoreWord8(dataPointer, offset + 7, b7); 614 } else { 615 StoreWord8(dataPointer, offset, b7); 616 StoreWord8(dataPointer, offset + 1, b6); 617 StoreWord8(dataPointer, offset + 2, b5); 618 StoreWord8(dataPointer, offset + 3, b4); 619 StoreWord8(dataPointer, offset + 4, b3); 620 StoreWord8(dataPointer, offset + 5, b2); 621 StoreWord8(dataPointer, offset + 6, b1); 622 StoreWord8(dataPointer, offset + 7, b0); 623 } 624} 625 626extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntLength(BigIntBase): 627 uint32; 628extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntSign(BigIntBase): 629 uint32; 630 631// We might get here a BigInt that is bigger than 64 bits, but we're only 632// interested in the 64 lowest ones. This means the lowest BigInt digit 633// on 64-bit platforms, and the 2 lowest BigInt digits on 32-bit ones. 634macro StoreDataViewBigInt( 635 buffer: JSArrayBuffer, offset: uintptr, bigIntValue: BigInt, 636 requestedLittleEndian: bool): void { 637 const length: uint32 = DataViewDecodeBigIntLength(bigIntValue); 638 const sign: uint32 = DataViewDecodeBigIntSign(bigIntValue); 639 640 // The 32-bit words that will hold the BigInt's value in 641 // two's complement representation. 642 let lowWord: uint32 = 0; 643 let highWord: uint32 = 0; 644 645 // The length is nonzero if and only if the BigInt's value is nonzero. 646 if (length != 0) { 647 if constexpr (Is64()) { 648 // There is always exactly 1 BigInt digit to load in this case. 649 const value: uintptr = bigint::LoadBigIntDigit(bigIntValue, 0); 650 lowWord = Convert<uint32>(value); // Truncates value to 32 bits. 651 highWord = Convert<uint32>(value >>> 32); 652 } else { // There might be either 1 or 2 BigInt digits we need to load. 653 lowWord = Convert<uint32>(bigint::LoadBigIntDigit(bigIntValue, 0)); 654 if (length >= 2) { // Only load the second digit if there is one. 655 highWord = Convert<uint32>(bigint::LoadBigIntDigit(bigIntValue, 1)); 656 } 657 } 658 } 659 660 if (sign != 0) { // The number is negative, Convert it. 661 highWord = Unsigned(0 - Signed(highWord)); 662 if (lowWord != 0) { 663 highWord = Unsigned(Signed(highWord) - 1); 664 } 665 lowWord = Unsigned(0 - Signed(lowWord)); 666 } 667 668 StoreDataView64(buffer, offset, lowWord, highWord, requestedLittleEndian); 669} 670 671// SetViewValue ( view, requestIndex, isLittleEndian, type, value ) 672// https://tc39.es/ecma262/#sec-setviewvalue 673transitioning macro DataViewSet( 674 context: Context, receiver: JSAny, requestIndex: JSAny, value: JSAny, 675 requestedLittleEndian: JSAny, kind: constexpr ElementsKind): JSAny { 676 // 1. Perform ? RequireInternalSlot(view, [[DataView]]). 677 // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot. 678 const dataView: JSDataView = 679 ValidateDataView(context, receiver, MakeDataViewSetterNameString(kind)); 680 681 try { 682 // 3. Let getIndex be ? ToIndex(requestIndex). 683 const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError; 684 685 let numberValue: Numeric; 686 if constexpr ( 687 kind == ElementsKind::BIGUINT64_ELEMENTS || 688 kind == ElementsKind::BIGINT64_ELEMENTS) { 689 // 4. If ! IsBigIntElementType(type) is true, let numberValue be 690 // ? ToBigInt(value). 691 numberValue = ToBigInt(context, value); 692 } else { 693 // 5. Otherwise, let numberValue be ? ToNumber(value). 694 numberValue = ToNumber(context, value); 695 } 696 697 // 6. Set isLittleEndian to !ToBoolean(isLittleEndian). 698 const littleEndian: bool = ToBoolean(requestedLittleEndian); 699 700 // 7. Let buffer be view.[[ViewedArrayBuffer]]. 701 const buffer: JSArrayBuffer = dataView.buffer; 702 703 // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. 704 if (IsDetachedBuffer(buffer)) { 705 ThrowTypeError( 706 MessageTemplate::kDetachedOperation, 707 MakeDataViewSetterNameString(kind)); 708 } 709 // 8. Let getBufferByteLength be 710 // MakeIdempotentArrayBufferByteLengthGetter(Unordered). 711 // 9. NOTE: Bounds checking is not a synchronizing operation when view's 712 // backing buffer is a growable SharedArrayBuffer. 713 // 10. If IsViewOutOfBounds(view, getBufferByteLength) is true, throw a 714 // TypeError exception. 715 try { 716 typed_array::IsJSArrayBufferViewDetachedOrOutOfBounds(dataView) 717 otherwise DetachedOrOutOfBounds, NotDetachedNorOutOfBounds; 718 } label DetachedOrOutOfBounds { 719 ThrowTypeError( 720 MessageTemplate::kDetachedOperation, 721 MakeDataViewGetterNameString(kind)); 722 } label NotDetachedNorOutOfBounds {} 723 724 // 11. Let viewOffset be view.[[ByteOffset]]. 725 const viewOffset: uintptr = dataView.byte_offset; 726 727 // 12. Let viewSize be GetViewByteLength(view, getBufferByteLength). 728 let viewSize: uintptr; 729 if (dataView.bit_field.is_length_tracking) { 730 viewSize = LoadVariableLengthJSArrayBufferViewByteLength( 731 dataView, dataView.buffer) otherwise unreachable; 732 } else { 733 viewSize = dataView.byte_length; 734 } 735 736 // 13. Let elementSize be the Element Size value specified in Table 62 737 // for Element Type type. 738 const elementSize: uintptr = DataViewElementSize(kind); 739 740 // 14. If getIndex + elementSize > viewSize, throw a RangeError exception. 741 CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize) 742 otherwise RangeError; 743 744 // 15. Let bufferIndex be getIndex + viewOffset. 745 const bufferIndex: uintptr = getIndex + viewOffset; 746 747 if constexpr ( 748 kind == ElementsKind::BIGUINT64_ELEMENTS || 749 kind == ElementsKind::BIGINT64_ELEMENTS) { 750 // For these elements kinds numberValue is BigInt. 751 const bigIntValue: BigInt = %RawDownCast<BigInt>(numberValue); 752 StoreDataViewBigInt(buffer, bufferIndex, bigIntValue, littleEndian); 753 } else { 754 // For these elements kinds numberValue is Number. 755 const numValue: Number = %RawDownCast<Number>(numberValue); 756 const doubleValue: float64 = ChangeNumberToFloat64(numValue); 757 758 if constexpr ( 759 kind == ElementsKind::UINT8_ELEMENTS || 760 kind == ElementsKind::INT8_ELEMENTS) { 761 StoreDataView8( 762 buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue)); 763 } else if constexpr ( 764 kind == ElementsKind::UINT16_ELEMENTS || 765 kind == ElementsKind::INT16_ELEMENTS) { 766 StoreDataView16( 767 buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), 768 littleEndian); 769 } else if constexpr ( 770 kind == ElementsKind::UINT32_ELEMENTS || 771 kind == ElementsKind::INT32_ELEMENTS) { 772 StoreDataView32( 773 buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue), 774 littleEndian); 775 } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) { 776 const floatValue: float32 = TruncateFloat64ToFloat32(doubleValue); 777 StoreDataView32( 778 buffer, bufferIndex, BitcastFloat32ToInt32(floatValue), 779 littleEndian); 780 } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) { 781 const lowWord: uint32 = Float64ExtractLowWord32(doubleValue); 782 const highWord: uint32 = Float64ExtractHighWord32(doubleValue); 783 StoreDataView64(buffer, bufferIndex, lowWord, highWord, littleEndian); 784 } 785 } 786 return Undefined; 787 } label RangeError { 788 ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset); 789 } 790} 791 792transitioning javascript builtin DataViewPrototypeSetUint8( 793 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 794 const offset: JSAny = arguments[0]; 795 const value: JSAny = arguments[1]; 796 return DataViewSet( 797 context, receiver, offset, value, Undefined, 798 ElementsKind::UINT8_ELEMENTS); 799} 800 801transitioning javascript builtin DataViewPrototypeSetInt8( 802 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 803 const offset: JSAny = arguments[0]; 804 const value: JSAny = arguments[1]; 805 return DataViewSet( 806 context, receiver, offset, value, Undefined, ElementsKind::INT8_ELEMENTS); 807} 808 809transitioning javascript builtin DataViewPrototypeSetUint16( 810 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 811 const offset: JSAny = arguments[0]; 812 const value: JSAny = arguments[1]; 813 const isLittleEndian: JSAny = arguments[2]; 814 return DataViewSet( 815 context, receiver, offset, value, isLittleEndian, 816 ElementsKind::UINT16_ELEMENTS); 817} 818 819transitioning javascript builtin DataViewPrototypeSetInt16( 820 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 821 const offset: JSAny = arguments[0]; 822 const value: JSAny = arguments[1]; 823 const isLittleEndian: JSAny = arguments[2]; 824 return DataViewSet( 825 context, receiver, offset, value, isLittleEndian, 826 ElementsKind::INT16_ELEMENTS); 827} 828 829transitioning javascript builtin DataViewPrototypeSetUint32( 830 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 831 const offset: JSAny = arguments[0]; 832 const value: JSAny = arguments[1]; 833 const isLittleEndian: JSAny = arguments[2]; 834 return DataViewSet( 835 context, receiver, offset, value, isLittleEndian, 836 ElementsKind::UINT32_ELEMENTS); 837} 838 839transitioning javascript builtin DataViewPrototypeSetInt32( 840 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 841 const offset: JSAny = arguments[0]; 842 const value: JSAny = arguments[1]; 843 const isLittleEndian: JSAny = arguments[2]; 844 return DataViewSet( 845 context, receiver, offset, value, isLittleEndian, 846 ElementsKind::INT32_ELEMENTS); 847} 848 849transitioning javascript builtin DataViewPrototypeSetFloat32( 850 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 851 const offset: JSAny = arguments[0]; 852 const value: JSAny = arguments[1]; 853 const isLittleEndian: JSAny = arguments[2]; 854 return DataViewSet( 855 context, receiver, offset, value, isLittleEndian, 856 ElementsKind::FLOAT32_ELEMENTS); 857} 858 859transitioning javascript builtin DataViewPrototypeSetFloat64( 860 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 861 const offset: JSAny = arguments[0]; 862 const value: JSAny = arguments[1]; 863 const isLittleEndian: JSAny = arguments[2]; 864 return DataViewSet( 865 context, receiver, offset, value, isLittleEndian, 866 ElementsKind::FLOAT64_ELEMENTS); 867} 868 869transitioning javascript builtin DataViewPrototypeSetBigUint64( 870 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 871 const offset: JSAny = arguments[0]; 872 const value: JSAny = arguments[1]; 873 const isLittleEndian: JSAny = arguments[2]; 874 return DataViewSet( 875 context, receiver, offset, value, isLittleEndian, 876 ElementsKind::BIGUINT64_ELEMENTS); 877} 878 879transitioning javascript builtin DataViewPrototypeSetBigInt64( 880 js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny { 881 const offset: JSAny = arguments[0]; 882 const value: JSAny = arguments[1]; 883 const isLittleEndian: JSAny = arguments[2]; 884 return DataViewSet( 885 context, receiver, offset, value, isLittleEndian, 886 ElementsKind::BIGINT64_ELEMENTS); 887} 888} 889