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