data-view.tq 31.7 KB
Newer Older
1 2 3 4 5
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module data_view {
6
  extern operator '.buffer'
7
      macro LoadJSArrayBufferViewBuffer(JSArrayBufferView): JSArrayBuffer;
8
  extern operator '.byte_length'
9
      macro LoadJSArrayBufferViewByteLength(JSArrayBufferView): uintptr;
10
  extern operator '.byte_offset'
11
      macro LoadJSArrayBufferViewByteOffset(JSArrayBufferView): uintptr;
12 13
  extern operator '.backing_store'
      macro LoadJSArrayBufferBackingStore(JSArrayBuffer): RawPtr;
14

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
  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;
    }
  }

67 68 69 70
  macro WasNeutered(view: JSArrayBufferView): bool {
    return IsDetachedBuffer(view.buffer);
  }

71 72
  macro ValidateDataView(
      context: Context, o: Object, method: String): JSDataView {
73
    try {
74
      return Cast<JSDataView>(o) otherwise CastError;
75 76 77 78 79 80 81 82
    }
    label CastError {
      ThrowTypeError(context, kIncompatibleMethodReceiver, method);
    }
  }

  // ES6 section 24.2.4.1 get DataView.prototype.buffer
  javascript builtin DataViewPrototypeGetBuffer(
83
      context: Context, receiver: Object, ...arguments): JSArrayBuffer {
84 85
    let dataView: JSDataView =
        ValidateDataView(context, receiver, 'get DataView.prototype.buffer');
86
    return dataView.buffer;
87 88 89 90
  }

  // ES6 section 24.2.4.2 get DataView.prototype.byteLength
  javascript builtin DataViewPrototypeGetByteLength(
91
      context: Context, receiver: Object, ...arguments): Number {
92
    let dataView: JSDataView = ValidateDataView(
93
        context, receiver, 'get DataView.prototype.byte_length');
94
    if (WasNeutered(dataView)) {
95
      // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
96
      // here if the JSArrayBuffer of the {dataView} was neutered.
97 98
      return 0;
    }
99
    return Convert<Number>(dataView.byte_length);
100 101 102 103
  }

  // ES6 section 24.2.4.3 get DataView.prototype.byteOffset
  javascript builtin DataViewPrototypeGetByteOffset(
104
      context: Context, receiver: Object, ...arguments): Number {
105
    let dataView: JSDataView = ValidateDataView(
106
        context, receiver, 'get DataView.prototype.byte_offset');
107
    if (WasNeutered(dataView)) {
108
      // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
109
      // here if the JSArrayBuffer of the {dataView} was neutered.
110 111
      return 0;
    }
112
    return Convert<Number>(dataView.byte_offset);
113 114
  }

115 116 117 118 119 120 121
  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;

122 123
  extern macro LoadUint8(RawPtr, uintptr): uint32;
  extern macro LoadInt8(RawPtr, uintptr): int32;
124

125 126
  macro LoadDataView8(
      buffer: JSArrayBuffer, offset: uintptr, signed: constexpr bool): Smi {
127
    if constexpr (signed) {
128
      return Convert<Smi>(LoadInt8(buffer.backing_store, offset));
129
    } else {
130
      return Convert<Smi>(LoadUint8(buffer.backing_store, offset));
131
    }
132 133
  }

134 135 136
  macro LoadDataView16(
      buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
      signed: constexpr bool): Number {
137 138
    let dataPointer: RawPtr = buffer.backing_store;

139 140 141 142 143
    let b0: int32;
    let b1: int32;
    let result: int32;

    // Sign-extend the most significant byte by loading it as an Int8.
144 145 146
    if (requestedLittleEndian) {
      b0 = Signed(LoadUint8(dataPointer, offset));
      b1 = LoadInt8(dataPointer, offset + 1);
147
      result = (b1 << 8) + b0;
148
    } else {
149 150
      b0 = LoadInt8(dataPointer, offset);
      b1 = Signed(LoadUint8(dataPointer, offset + 1));
151
      result = (b0 << 8) + b1;
152 153
    }
    if constexpr (signed) {
154
      return Convert<Smi>(result);
155 156
    } else {
      // Bit-mask the higher bits to prevent sign extension if we're unsigned.
157
      return Convert<Smi>(result & 0xFFFF);
158 159 160
    }
  }

161 162 163
  macro LoadDataView32(
      buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
      kind: constexpr ElementsKind): Number {
164 165
    let dataPointer: RawPtr = buffer.backing_store;

166 167 168 169
    let b0: uint32 = LoadUint8(dataPointer, offset);
    let b1: uint32 = LoadUint8(dataPointer, offset + 1);
    let b2: uint32 = LoadUint8(dataPointer, offset + 2);
    let b3: uint32 = LoadUint8(dataPointer, offset + 3);
170
    let result: uint32;
171

172
    if (requestedLittleEndian) {
173
      result = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
174
    } else {
175
      result = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
176
    }
177

178
    if constexpr (kind == INT32_ELEMENTS) {
179
      return Convert<Number>(Signed(result));
180
    } else if constexpr (kind == UINT32_ELEMENTS) {
181
      return Convert<Number>(result);
182
    } else if constexpr (kind == FLOAT32_ELEMENTS) {
183 184
      let floatRes: float64 = Convert<float64>(BitcastInt32ToFloat32(result));
      return Convert<Number>(floatRes);
185
    } else {
186
      unreachable;
187 188 189
    }
  }

190 191 192
  macro LoadDataViewFloat64(
      buffer: JSArrayBuffer, offset: uintptr,
      requestedLittleEndian: bool): Number {
193 194
    let dataPointer: RawPtr = buffer.backing_store;

195 196 197 198 199 200 201 202 203 204 205 206 207 208
    let b0: uint32 = LoadUint8(dataPointer, offset);
    let b1: uint32 = LoadUint8(dataPointer, offset + 1);
    let b2: uint32 = LoadUint8(dataPointer, offset + 2);
    let b3: uint32 = LoadUint8(dataPointer, offset + 3);
    let b4: uint32 = LoadUint8(dataPointer, offset + 4);
    let b5: uint32 = LoadUint8(dataPointer, offset + 5);
    let b6: uint32 = LoadUint8(dataPointer, offset + 6);
    let b7: uint32 = LoadUint8(dataPointer, offset + 7);
    let lowWord: uint32;
    let highWord: uint32;

    if (requestedLittleEndian) {
      lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
      highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4;
209
    } else {
210 211
      highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
      lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
212 213
    }

214
    let result: float64 = 0;
215 216
    result = Float64InsertLowWord32(result, lowWord);
    result = Float64InsertHighWord32(result, highWord);
217

218
    return Convert<Number>(result);
219 220
  }

221 222 223
  extern macro AllocateBigInt(intptr): BigInt;
  extern macro StoreBigIntBitfield(BigInt, intptr): void;
  extern macro StoreBigIntDigit(BigInt, constexpr int31, uintptr): void;
224 225
  extern macro DataViewEncodeBigIntBits(
      constexpr bool, constexpr int31): intptr;
226

227 228 229 230 231
  const kPositiveBigInt: constexpr bool = false;
  const kNegativeBigInt: constexpr bool = true;
  const kZeroDigitBigInt: constexpr int31 = 0;
  const kOneDigitBigInt: constexpr int31 = 1;
  const kTwoDigitBigInt: constexpr int31 = 2;
232

233
  macro CreateEmptyBigInt(isPositive: bool, length: constexpr int31): BigInt {
234 235 236 237
    // Allocate a BigInt with the desired length (number of digits).
    let result: BigInt = AllocateBigInt(length);

    // Write the desired sign and length to the BigInt bitfield.
238
    if (isPositive) {
239 240
      StoreBigIntBitfield(
          result, DataViewEncodeBigIntBits(kPositiveBigInt, length));
241
    } else {
242 243
      StoreBigIntBitfield(
          result, DataViewEncodeBigIntBits(kNegativeBigInt, length));
244 245 246 247 248
    }

    return result;
  }

249
  // Create a BigInt on a 64-bit architecture from two 32-bit values.
250 251
  macro MakeBigIntOn64Bit(
      lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
252
    // 0n is represented by a zero-length BigInt.
253
    if (lowWord == 0 && highWord == 0) {
254 255 256
      return AllocateBigInt(kZeroDigitBigInt);
    }

257 258 259 260
    let isPositive: bool = true;
    let highPart: intptr = Signed(Convert<uintptr>(highWord));
    let lowPart: intptr = Signed(Convert<uintptr>(lowWord));
    let rawValue: intptr = (highPart << 32) + lowPart;
261 262

    if constexpr (signed) {
263 264 265 266
      if (rawValue < 0) {
        isPositive = false;
        // We have to store the absolute value of rawValue in the digit.
        rawValue = 0 - rawValue;
267 268 269
      }
    }

270
    // Allocate the BigInt and store the absolute value.
271
    let result: BigInt = CreateEmptyBigInt(isPositive, kOneDigitBigInt);
272

273
    StoreBigIntDigit(result, 0, Unsigned(rawValue));
274 275 276 277 278

    return result;
  }

  // Create a BigInt on a 32-bit architecture from two 32-bit values.
279 280
  macro MakeBigIntOn32Bit(
      lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
281
    // 0n is represented by a zero-length BigInt.
282
    if (lowWord == 0 && highWord == 0) {
283 284
      return AllocateBigInt(kZeroDigitBigInt);
    }
285 286

    // On a 32-bit platform, we might need 1 or 2 digits to store the number.
287 288
    let needTwoDigits: bool = false;
    let isPositive: bool = true;
289

290 291 292 293
    // 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);
294

295
    // If highWord == 0, the number is positive, and we only need 1 digit,
296 297
    // so we don't have anything to do.
    // Otherwise, all cases are possible.
298
    if (highWord != 0) {
299
      if constexpr (signed) {
300 301 302
        // If highPart < 0, the number is always negative.
        if (highPart < 0) {
          isPositive = false;
303

304 305 306
          // 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.
307 308 309
          highPart = 0 - highPart;
          if (lowPart != 0) {
            highPart = highPart - 1;
310
          }
311
          lowPart = 0 - lowPart;
312

313 314 315
          // Here, highPart could be 0 again so we might have 1 or 2 digits.
          if (highPart != 0) {
            needTwoDigits = true;
316 317 318 319
          }

        } else {
          // In this case, the number is positive, and we need 2 digits.
320
          needTwoDigits = true;
321 322 323 324 325
        }

      } else {
        // In this case, the number is positive (unsigned),
        // and we need 2 digits.
326
        needTwoDigits = true;
327 328 329
      }
    }

330 331
    // Allocate the BigInt with the right sign and length.
    let result: BigInt;
332 333
    if (needTwoDigits) {
      result = CreateEmptyBigInt(isPositive, kTwoDigitBigInt);
334
    } else {
335
      result = CreateEmptyBigInt(isPositive, kOneDigitBigInt);
336 337
    }

338
    // Finally, write the digit(s) to the BigInt.
339
    StoreBigIntDigit(result, 0, Unsigned(Convert<intptr>(lowPart)));
340

341 342
    if (needTwoDigits) {
      StoreBigIntDigit(result, 1, Unsigned(Convert<intptr>(highPart)));
343 344 345 346 347
    }

    return result;
  }

348 349
  macro MakeBigInt(
      lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
350 351 352
    // 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()) {
353
      return MakeBigIntOn64Bit(lowWord, highWord, signed);
354
    } else {
355
      return MakeBigIntOn32Bit(lowWord, highWord, signed);
356 357 358
    }
  }

359 360 361
  macro LoadDataViewBigInt(
      buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
      signed: constexpr bool): BigInt {
362 363
    let dataPointer: RawPtr = buffer.backing_store;

364 365 366 367 368 369 370 371 372 373 374 375 376 377
    let b0: uint32 = LoadUint8(dataPointer, offset);
    let b1: uint32 = LoadUint8(dataPointer, offset + 1);
    let b2: uint32 = LoadUint8(dataPointer, offset + 2);
    let b3: uint32 = LoadUint8(dataPointer, offset + 3);
    let b4: uint32 = LoadUint8(dataPointer, offset + 4);
    let b5: uint32 = LoadUint8(dataPointer, offset + 5);
    let b6: uint32 = LoadUint8(dataPointer, offset + 6);
    let b7: uint32 = LoadUint8(dataPointer, offset + 7);
    let lowWord: uint32;
    let highWord: uint32;

    if (requestedLittleEndian) {
      lowWord = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
      highWord = (b7 << 24) | (b6 << 16) | (b5 << 8) | b4;
378
    } else {
379 380
      highWord = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
      lowWord = (b4 << 24) | (b5 << 16) | (b6 << 8) | b7;
381 382
    }

383
    return MakeBigInt(lowWord, highWord, signed);
384
  }
385

386 387
  extern macro ToSmiIndex(Object, Context): Smi
      labels RangeError;
388
  extern macro DataViewElementSize(constexpr ElementsKind): constexpr int31;
389

390 391 392 393 394
  macro DataViewGet(
      context: Context, receiver: Object, offset: Object,
      requestedLittleEndian: Object, kind: constexpr ElementsKind): Numeric {
    let dataView: JSDataView =
        ValidateDataView(context, receiver, MakeDataViewGetterNameString(kind));
395

396
    let getIndex: Number;
397
    try {
398
      getIndex = ToIndex(offset, context) otherwise RangeError;
399 400 401 402 403
    }
    label RangeError {
      ThrowRangeError(context, kInvalidDataViewAccessorOffset);
    }

404 405
    let littleEndian: bool = ToBoolean(requestedLittleEndian);
    let buffer: JSArrayBuffer = dataView.buffer;
406 407

    if (IsDetachedBuffer(buffer)) {
408 409
      ThrowTypeError(
          context, kDetachedOperation, MakeDataViewGetterNameString(kind));
410 411
    }

412 413 414
    let getIndexFloat: float64 = Convert<float64>(getIndex);
    let getIndexWord: uintptr = Convert<uintptr>(getIndexFloat);

415
    let viewOffsetWord: uintptr = dataView.byte_offset;
416 417
    let viewSizeFloat: float64 = Convert<float64>(dataView.byte_length);
    let elementSizeFloat: float64 = Convert<float64>(DataViewElementSize(kind));
418

419
    if (getIndexFloat + elementSizeFloat > viewSizeFloat) {
420 421 422
      ThrowRangeError(context, kInvalidDataViewAccessorOffset);
    }

423 424
    let bufferIndex: uintptr = getIndexWord + viewOffsetWord;

425
    if constexpr (kind == UINT8_ELEMENTS) {
426
      return LoadDataView8(buffer, bufferIndex, false);
427
    } else if constexpr (kind == INT8_ELEMENTS) {
428
      return LoadDataView8(buffer, bufferIndex, true);
429
    } else if constexpr (kind == UINT16_ELEMENTS) {
430
      return LoadDataView16(buffer, bufferIndex, littleEndian, false);
431
    } else if constexpr (kind == INT16_ELEMENTS) {
432
      return LoadDataView16(buffer, bufferIndex, littleEndian, true);
433
    } else if constexpr (kind == UINT32_ELEMENTS) {
434
      return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
435
    } else if constexpr (kind == INT32_ELEMENTS) {
436
      return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
437
    } else if constexpr (kind == FLOAT32_ELEMENTS) {
438
      return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
439
    } else if constexpr (kind == FLOAT64_ELEMENTS) {
440
      return LoadDataViewFloat64(buffer, bufferIndex, littleEndian);
441
    } else if constexpr (kind == BIGUINT64_ELEMENTS) {
442
      return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false);
443
    } else if constexpr (kind == BIGINT64_ELEMENTS) {
444
      return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true);
445 446 447 448 449
    } else {
      unreachable;
    }
  }

450
  javascript builtin DataViewPrototypeGetUint8(
451 452 453 454
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    return DataViewGet(context, receiver, offset, Undefined, UINT8_ELEMENTS);
  }
455

456 457
  javascript builtin DataViewPrototypeGetInt8(
      context: Context, receiver: Object, ...arguments): Object {
458
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
459 460 461 462
    return DataViewGet(context, receiver, offset, Undefined, INT8_ELEMENTS);
  }

  javascript builtin DataViewPrototypeGetUint16(
463 464 465 466 467 468 469
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, UINT16_ELEMENTS);
  }
470

471
  javascript builtin DataViewPrototypeGetInt16(
472 473 474 475 476 477 478
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, INT16_ELEMENTS);
  }
479

480
  javascript builtin DataViewPrototypeGetUint32(
481 482 483 484 485 486 487
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, UINT32_ELEMENTS);
  }
488

489
  javascript builtin DataViewPrototypeGetInt32(
490 491 492 493 494 495 496
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, INT32_ELEMENTS);
  }
497 498

  javascript builtin DataViewPrototypeGetFloat32(
499 500 501 502 503 504 505
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, FLOAT32_ELEMENTS);
  }
506 507

  javascript builtin DataViewPrototypeGetFloat64(
508 509 510 511 512 513 514
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, FLOAT64_ELEMENTS);
  }
515

516
  javascript builtin DataViewPrototypeGetBigUint64(
517 518 519 520 521 522 523
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, BIGUINT64_ELEMENTS);
  }
524

525
  javascript builtin DataViewPrototypeGetBigInt64(
526 527 528 529 530 531 532
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewGet(
        context, receiver, offset, isLittleEndian, BIGINT64_ELEMENTS);
  }
533

534 535 536
  extern macro ToNumber(Context, Object): Number;
  extern macro ToBigInt(Context, Object): BigInt;
  extern macro TruncateFloat64ToFloat32(float64): float32;
537
  extern macro TruncateFloat64ToWord32(float64): uint32;
538

539
  extern macro StoreWord8(RawPtr, uintptr, uint32): void;
540

541
  macro StoreDataView8(buffer: JSArrayBuffer, offset: uintptr, value: uint32) {
542
    StoreWord8(buffer.backing_store, offset, value & 0xFF);
543 544
  }

545 546 547
  macro StoreDataView16(
      buffer: JSArrayBuffer, offset: uintptr, value: uint32,
      requestedLittleEndian: bool) {
548 549
    let dataPointer: RawPtr = buffer.backing_store;

550 551 552
    let b0: uint32 = value & 0xFF;
    let b1: uint32 = (value >>> 8) & 0xFF;

553 554 555
    if (requestedLittleEndian) {
      StoreWord8(dataPointer, offset, b0);
      StoreWord8(dataPointer, offset + 1, b1);
556
    } else {
557 558
      StoreWord8(dataPointer, offset, b1);
      StoreWord8(dataPointer, offset + 1, b0);
559 560 561
    }
  }

562 563 564
  macro StoreDataView32(
      buffer: JSArrayBuffer, offset: uintptr, value: uint32,
      requestedLittleEndian: bool) {
565 566
    let dataPointer: RawPtr = buffer.backing_store;

567 568 569
    let b0: uint32 = value & 0xFF;
    let b1: uint32 = (value >>> 8) & 0xFF;
    let b2: uint32 = (value >>> 16) & 0xFF;
570
    let b3: uint32 = value >>> 24;  // We don't need to mask here.
571

572 573 574 575 576
    if (requestedLittleEndian) {
      StoreWord8(dataPointer, offset, b0);
      StoreWord8(dataPointer, offset + 1, b1);
      StoreWord8(dataPointer, offset + 2, b2);
      StoreWord8(dataPointer, offset + 3, b3);
577
    } else {
578 579 580 581
      StoreWord8(dataPointer, offset, b3);
      StoreWord8(dataPointer, offset + 1, b2);
      StoreWord8(dataPointer, offset + 2, b1);
      StoreWord8(dataPointer, offset + 3, b0);
582 583 584
    }
  }

585 586 587
  macro StoreDataView64(
      buffer: JSArrayBuffer, offset: uintptr, lowWord: uint32, highWord: uint32,
      requestedLittleEndian: bool) {
588 589
    let dataPointer: RawPtr = buffer.backing_store;

590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
    let b0: uint32 = lowWord & 0xFF;
    let b1: uint32 = (lowWord >>> 8) & 0xFF;
    let b2: uint32 = (lowWord >>> 16) & 0xFF;
    let b3: uint32 = lowWord >>> 24;

    let b4: uint32 = highWord & 0xFF;
    let b5: uint32 = (highWord >>> 8) & 0xFF;
    let b6: uint32 = (highWord >>> 16) & 0xFF;
    let b7: uint32 = highWord >>> 24;

    if (requestedLittleEndian) {
      StoreWord8(dataPointer, offset, b0);
      StoreWord8(dataPointer, offset + 1, b1);
      StoreWord8(dataPointer, offset + 2, b2);
      StoreWord8(dataPointer, offset + 3, b3);
      StoreWord8(dataPointer, offset + 4, b4);
      StoreWord8(dataPointer, offset + 5, b5);
      StoreWord8(dataPointer, offset + 6, b6);
      StoreWord8(dataPointer, offset + 7, b7);
609
    } else {
610 611 612 613 614 615 616 617
      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);
618 619 620 621 622 623 624 625 626 627
    }
  }

  extern macro DataViewDecodeBigIntLength(BigInt): uintptr;
  extern macro DataViewDecodeBigIntSign(BigInt): uintptr;
  extern macro LoadBigIntDigit(BigInt, constexpr int31): uintptr;

  // We might get here a BigInt that is bigger than 64 bits, but we're only
  // interested in the 64 lowest ones. This means the lowest BigInt digit
  // on 64-bit platforms, and the 2 lowest BigInt digits on 32-bit ones.
628 629 630
  macro StoreDataViewBigInt(
      buffer: JSArrayBuffer, offset: uintptr, bigIntValue: BigInt,
      requestedLittleEndian: bool) {
631 632
    let length: uintptr = DataViewDecodeBigIntLength(bigIntValue);
    let sign: uintptr = DataViewDecodeBigIntSign(bigIntValue);
633 634 635

    // The 32-bit words that will hold the BigInt's value in
    // two's complement representation.
636 637
    let lowWord: uint32 = 0;
    let highWord: uint32 = 0;
638 639 640 641 642

    // 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.
643
        let value: uintptr = LoadBigIntDigit(bigIntValue, 0);
644
        lowWord = Convert<uint32>(value);  // Truncates value to 32 bits.
645
        highWord = Convert<uint32>(value >>> 32);
646
      } else {  // There might be either 1 or 2 BigInt digits we need to load.
647
        lowWord = Convert<uint32>(LoadBigIntDigit(bigIntValue, 0));
648
        if (length >= 2) {  // Only load the second digit if there is one.
649
          highWord = Convert<uint32>(LoadBigIntDigit(bigIntValue, 1));
650 651 652 653
        }
      }
    }

654
    if (sign != 0) {  // The number is negative, Convert it.
655 656 657
      highWord = Unsigned(0 - Signed(highWord));
      if (lowWord != 0) {
        highWord = Unsigned(Signed(highWord) - 1);
658
      }
659
      lowWord = Unsigned(0 - Signed(lowWord));
660 661
    }

662
    StoreDataView64(buffer, offset, lowWord, highWord, requestedLittleEndian);
663 664
  }

665 666 667 668 669
  macro DataViewSet(
      context: Context, receiver: Object, offset: Object, value: Object,
      requestedLittleEndian: Object, kind: constexpr ElementsKind): Object {
    let dataView: JSDataView =
        ValidateDataView(context, receiver, MakeDataViewSetterNameString(kind));
670

671
    let getIndex: Number;
672
    try {
673
      getIndex = ToIndex(offset, context) otherwise RangeError;
674 675 676 677 678
    }
    label RangeError {
      ThrowRangeError(context, kInvalidDataViewAccessorOffset);
    }

679 680
    let littleEndian: bool = ToBoolean(requestedLittleEndian);
    let buffer: JSArrayBuffer = dataView.buffer;
681

682 683
    let bigIntValue: BigInt;
    let numValue: Number;
684 685 686
    // According to ES6 section 24.2.1.2 SetViewValue, we must perform
    // the conversion before doing the bounds check.
    if constexpr (kind == BIGUINT64_ELEMENTS || kind == BIGINT64_ELEMENTS) {
687
      bigIntValue = ToBigInt(context, value);
688
    } else {
689
      numValue = ToNumber(context, value);
690 691 692
    }

    if (IsDetachedBuffer(buffer)) {
693 694
      ThrowTypeError(
          context, kDetachedOperation, MakeDataViewSetterNameString(kind));
695 696
    }

697 698 699
    let getIndexFloat: float64 = Convert<float64>(getIndex);
    let getIndexWord: uintptr = Convert<uintptr>(getIndexFloat);

700
    let viewOffsetWord: uintptr = dataView.byte_offset;
701 702
    let viewSizeFloat: float64 = Convert<float64>(dataView.byte_length);
    let elementSizeFloat: float64 = Convert<float64>(DataViewElementSize(kind));
703

704
    if (getIndexFloat + elementSizeFloat > viewSizeFloat) {
705 706 707
      ThrowRangeError(context, kInvalidDataViewAccessorOffset);
    }

708 709
    let bufferIndex: uintptr = getIndexWord + viewOffsetWord;

710
    if constexpr (kind == BIGUINT64_ELEMENTS || kind == BIGINT64_ELEMENTS) {
711 712
      StoreDataViewBigInt(buffer, bufferIndex, bigIntValue, littleEndian);
    } else {
713
      let doubleValue: float64 = ChangeNumberToFloat64(numValue);
714 715

      if constexpr (kind == UINT8_ELEMENTS || kind == INT8_ELEMENTS) {
716 717 718 719 720 721 722 723 724 725 726
        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) {
727
        let floatValue: float32 = TruncateFloat64ToFloat32(doubleValue);
728 729 730 731
        StoreDataView32(
            buffer, bufferIndex, BitcastFloat32ToInt32(floatValue),
            littleEndian);
      } else if constexpr (kind == FLOAT64_ELEMENTS) {
732 733
        let lowWord: uint32 = Float64ExtractLowWord32(doubleValue);
        let highWord: uint32 = Float64ExtractHighWord32(doubleValue);
734
        StoreDataView64(buffer, bufferIndex, lowWord, highWord, littleEndian);
735 736 737 738
      }
    }
    return Undefined;
  }
739

740
  javascript builtin DataViewPrototypeSetUint8(
741 742 743 744 745 746
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, Undefined, UINT8_ELEMENTS);
  }
747

748
  javascript builtin DataViewPrototypeSetInt8(
749 750 751 752 753 754
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, Undefined, INT8_ELEMENTS);
  }
755

756
  javascript builtin DataViewPrototypeSetUint16(
757 758 759 760 761 762 763 764
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, UINT16_ELEMENTS);
  }
765

766
  javascript builtin DataViewPrototypeSetInt16(
767 768 769 770 771 772 773 774
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, INT16_ELEMENTS);
  }
775

776
  javascript builtin DataViewPrototypeSetUint32(
777 778 779 780 781 782 783 784
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, UINT32_ELEMENTS);
  }
785

786
  javascript builtin DataViewPrototypeSetInt32(
787 788 789 790 791 792 793 794
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, INT32_ELEMENTS);
  }
795 796

  javascript builtin DataViewPrototypeSetFloat32(
797 798 799 800 801 802 803 804
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, FLOAT32_ELEMENTS);
  }
805 806

  javascript builtin DataViewPrototypeSetFloat64(
807 808 809 810 811 812 813 814
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, FLOAT64_ELEMENTS);
  }
815

816
  javascript builtin DataViewPrototypeSetBigUint64(
817 818 819 820 821 822 823 824
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, BIGUINT64_ELEMENTS);
  }
825

826
  javascript builtin DataViewPrototypeSetBigInt64(
827 828 829 830 831 832 833 834
      context: Context, receiver: Object, ...arguments): Object {
    let offset: Object = arguments.length > 0 ? arguments[0] : Undefined;
    let value: Object = arguments.length > 1 ? arguments[1] : Undefined;
    let isLittleEndian: Object =
        arguments.length > 2 ? arguments[2] : Undefined;
    return DataViewSet(
        context, receiver, offset, value, isLittleEndian, BIGINT64_ELEMENTS);
  }
835
}