data-view.tq 31.2 KB
Newer Older
1 2 3 4
// 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.

5 6
#include 'src/builtins/builtins-data-view-gen.h'

7
namespace data_view {
8

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
macro MakeDataViewGetterNameString(kind: constexpr ElementsKind): String {
  if constexpr (kind == ElementsKind::UINT8_ELEMENTS) {
    return 'DataView.prototype.getUint8';
  } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) {
    return 'DataView.prototype.getInt8';
  } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) {
    return 'DataView.prototype.getUint16';
  } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) {
    return 'DataView.prototype.getInt16';
  } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
    return 'DataView.prototype.getUint32';
  } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
    return 'DataView.prototype.getInt32';
  } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
    return 'DataView.prototype.getFloat32';
  } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
    return 'DataView.prototype.getFloat64';
  } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) {
    return 'DataView.prototype.getBigInt64';
  } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) {
    return 'DataView.prototype.getBigUint64';
  } else {
    unreachable;
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
macro MakeDataViewSetterNameString(kind: constexpr ElementsKind): String {
  if constexpr (kind == ElementsKind::UINT8_ELEMENTS) {
    return 'DataView.prototype.setUint8';
  } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) {
    return 'DataView.prototype.setInt8';
  } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) {
    return 'DataView.prototype.setUint16';
  } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) {
    return 'DataView.prototype.setInt16';
  } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
    return 'DataView.prototype.setUint32';
  } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
    return 'DataView.prototype.setInt32';
  } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
    return 'DataView.prototype.setFloat32';
  } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
    return 'DataView.prototype.setFloat64';
  } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) {
    return 'DataView.prototype.setBigInt64';
  } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) {
    return 'DataView.prototype.setBigUint64';
  } else {
    unreachable;
58
  }
59
}
60

61 62 63
macro WasDetached(view: JSArrayBufferView): bool {
  return IsDetachedBuffer(view.buffer);
}
64

65 66 67 68 69
macro ValidateDataView(context: Context, o: JSAny, method: String): JSDataView {
  try {
    return Cast<JSDataView>(o) otherwise CastError;
  } label CastError {
    ThrowTypeError(MessageTemplate::kIncompatibleMethodReceiver, method);
70
  }
71
}
72

73 74 75 76 77 78 79 80
// ES6 section 24.2.4.1 get DataView.prototype.buffer
javascript builtin DataViewPrototypeGetBuffer(
    js-implicit context: NativeContext,
    receiver: JSAny)(...arguments): JSArrayBuffer {
  const dataView: JSDataView =
      ValidateDataView(context, receiver, 'get DataView.prototype.buffer');
  return dataView.buffer;
}
81

82 83 84 85 86 87 88 89 90 91 92 93
// ES6 section 24.2.4.2 get DataView.prototype.byteLength
javascript builtin DataViewPrototypeGetByteLength(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
  const dataView: JSDataView =
      ValidateDataView(context, receiver, 'get DataView.prototype.byte_length');
  if (WasDetached(dataView)) {
    // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
    // here if the JSArrayBuffer of the {dataView} was detached.
    return 0;
  }
  return Convert<Number>(dataView.byte_length);
}
94

95 96 97 98 99 100 101 102 103 104 105 106
// ES6 section 24.2.4.3 get DataView.prototype.byteOffset
javascript builtin DataViewPrototypeGetByteOffset(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): Number {
  const dataView: JSDataView =
      ValidateDataView(context, receiver, 'get DataView.prototype.byte_offset');
  if (WasDetached(dataView)) {
    // TODO(bmeurer): According to the ES6 spec, we should throw a TypeError
    // here if the JSArrayBuffer of the {dataView} was detached.
    return 0;
  }
  return Convert<Number>(dataView.byte_offset);
}
107

108 109 110 111 112 113
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;
114

115 116
extern macro DataViewBuiltinsAssembler::LoadUint8(RawPtr, uintptr): uint32;
extern macro DataViewBuiltinsAssembler::LoadInt8(RawPtr, uintptr): int32;
117

118 119 120 121 122 123
macro LoadDataView8(
    buffer: JSArrayBuffer, offset: uintptr, signed: constexpr bool): Smi {
  if constexpr (signed) {
    return Convert<Smi>(LoadInt8(buffer.backing_store_ptr, offset));
  } else {
    return Convert<Smi>(LoadUint8(buffer.backing_store_ptr, offset));
124
  }
125
}
126

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
macro LoadDataView16(
    buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
    signed: constexpr bool): Number {
  const dataPointer: RawPtr = buffer.backing_store_ptr;

  let b0: int32;
  let b1: int32;
  let result: int32;

  // Sign-extend the most significant byte by loading it as an Int8.
  if (requestedLittleEndian) {
    b0 = Signed(LoadUint8(dataPointer, offset));
    b1 = LoadInt8(dataPointer, offset + 1);
    result = (b1 << 8) + b0;
  } else {
    b0 = LoadInt8(dataPointer, offset);
    b1 = Signed(LoadUint8(dataPointer, offset + 1));
    result = (b0 << 8) + b1;
  }
  if constexpr (signed) {
    return Convert<Smi>(result);
  } else {
    // Bit-mask the higher bits to prevent sign extension if we're unsigned.
    return Convert<Smi>(result & 0xFFFF);
  }
}
153

154 155 156 157
macro LoadDataView32(
    buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
    kind: constexpr ElementsKind): Number {
  const dataPointer: RawPtr = buffer.backing_store_ptr;
158

159 160 161 162 163 164 165 166 167 168
  const b0: uint32 = LoadUint8(dataPointer, offset);
  const b1: uint32 = LoadUint8(dataPointer, offset + 1);
  const b2: uint32 = LoadUint8(dataPointer, offset + 2);
  const b3: uint32 = LoadUint8(dataPointer, offset + 3);
  let result: uint32;

  if (requestedLittleEndian) {
    result = (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
  } else {
    result = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
169 170
  }

171 172 173 174 175 176 177 178 179 180 181
  if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
    return Convert<Number>(Signed(result));
  } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
    return Convert<Number>(result);
  } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
    const floatRes: float64 = Convert<float64>(BitcastInt32ToFloat32(result));
    return Convert<Number>(floatRes);
  } else {
    unreachable;
  }
}
182

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212
macro LoadDataViewFloat64(
    buffer: JSArrayBuffer, offset: uintptr,
    requestedLittleEndian: bool): Number {
  const dataPointer: RawPtr = buffer.backing_store_ptr;

  const b0: uint32 = LoadUint8(dataPointer, offset);
  const b1: uint32 = LoadUint8(dataPointer, offset + 1);
  const b2: uint32 = LoadUint8(dataPointer, offset + 2);
  const b3: uint32 = LoadUint8(dataPointer, offset + 3);
  const b4: uint32 = LoadUint8(dataPointer, offset + 4);
  const b5: uint32 = LoadUint8(dataPointer, offset + 5);
  const b6: uint32 = LoadUint8(dataPointer, offset + 6);
  const b7: uint32 = LoadUint8(dataPointer, offset + 7);
  let lowWord: uint32;
  let highWord: uint32;

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

  let result: float64 = 0;
  result = Float64InsertLowWord32(result, lowWord);
  result = Float64InsertHighWord32(result, highWord);

  return Convert<Number>(result);
}
213

214 215 216
const kZeroDigitBigInt: constexpr int31 = 0;
const kOneDigitBigInt: constexpr int31 = 1;
const kTwoDigitBigInt: constexpr int31 = 2;
217

218 219 220 221 222 223
// Create a BigInt on a 64-bit architecture from two 32-bit values.
macro MakeBigIntOn64Bit(implicit context: Context)(
    lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
  // 0n is represented by a zero-length BigInt.
  if (lowWord == 0 && highWord == 0) {
    return Convert<BigInt>(bigint::AllocateBigInt(kZeroDigitBigInt));
224 225
  }

226 227 228 229 230 231 232 233 234 235
  let sign: uint32 = bigint::kPositiveSign;
  const highPart: intptr = Signed(Convert<uintptr>(highWord));
  const lowPart: intptr = Signed(Convert<uintptr>(lowWord));
  let rawValue: intptr = (highPart << 32) + lowPart;

  if constexpr (signed) {
    if (rawValue < 0) {
      sign = bigint::kNegativeSign;
      // We have to store the absolute value of rawValue in the digit.
      rawValue = 0 - rawValue;
236
    }
237
  }
238

239 240 241 242 243 244
  // Allocate the BigInt and store the absolute value.
  const result: MutableBigInt =
      bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt);
  bigint::StoreBigIntDigit(result, 0, Unsigned(rawValue));
  return Convert<BigInt>(result);
}
245

246 247 248 249 250 251
// Create a BigInt on a 32-bit architecture from two 32-bit values.
macro MakeBigIntOn32Bit(implicit context: Context)(
    lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
  // 0n is represented by a zero-length BigInt.
  if (lowWord == 0 && highWord == 0) {
    return Convert<BigInt>(bigint::AllocateBigInt(kZeroDigitBigInt));
252 253
  }

254 255 256
  // On a 32-bit platform, we might need 1 or 2 digits to store the number.
  let needTwoDigits: bool = false;
  let sign: uint32 = bigint::kPositiveSign;
257

258 259 260 261
  // 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);
262

263 264 265 266
  // If highWord == 0, the number is positive, and we only need 1 digit,
  // so we don't have anything to do.
  // Otherwise, all cases are possible.
  if (highWord != 0) {
267
    if constexpr (signed) {
268 269
      // If highPart < 0, the number is always negative.
      if (highPart < 0) {
270
        sign = bigint::kNegativeSign;
271

272 273 274 275 276 277 278 279
        // We have to compute the absolute value by hand.
        // There will be a negative carry from the low word
        // to the high word iff low != 0.
        highPart = 0 - highPart;
        if (lowPart != 0) {
          highPart = highPart - 1;
        }
        lowPart = 0 - lowPart;
280

281 282
        // Here, highPart could be 0 again so we might have 1 or 2 digits.
        if (highPart != 0) {
283
          needTwoDigits = true;
284 285 286
        }

      } else {
287
        // In this case, the number is positive, and we need 2 digits.
288
        needTwoDigits = true;
289 290
      }

291
    } else {
292 293 294
      // In this case, the number is positive (unsigned),
      // and we need 2 digits.
      needTwoDigits = true;
295
    }
296
  }
297

298 299 300 301 302 303
  // Allocate the BigInt with the right sign and length.
  let result: MutableBigInt;
  if (needTwoDigits) {
    result = bigint::AllocateEmptyBigInt(sign, kTwoDigitBigInt);
  } else {
    result = bigint::AllocateEmptyBigInt(sign, kOneDigitBigInt);
304 305
  }

306 307 308 309
  // Finally, write the digit(s) to the BigInt.
  bigint::StoreBigIntDigit(result, 0, Unsigned(Convert<intptr>(lowPart)));
  if (needTwoDigits) {
    bigint::StoreBigIntDigit(result, 1, Unsigned(Convert<intptr>(highPart)));
310
  }
311 312
  return Convert<BigInt>(result);
}
313

314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
macro MakeBigInt(implicit context: Context)(
    lowWord: uint32, highWord: uint32, signed: constexpr bool): BigInt {
  // A BigInt digit has the platform word size, so we only need one digit
  // on 64-bit platforms but may need two on 32-bit.
  if constexpr (Is64()) {
    return MakeBigIntOn64Bit(lowWord, highWord, signed);
  } else {
    return MakeBigIntOn32Bit(lowWord, highWord, signed);
  }
}

macro LoadDataViewBigInt(implicit context: Context)(
    buffer: JSArrayBuffer, offset: uintptr, requestedLittleEndian: bool,
    signed: constexpr bool): BigInt {
  const dataPointer: RawPtr = buffer.backing_store_ptr;

  const b0: uint32 = LoadUint8(dataPointer, offset);
  const b1: uint32 = LoadUint8(dataPointer, offset + 1);
  const b2: uint32 = LoadUint8(dataPointer, offset + 2);
  const b3: uint32 = LoadUint8(dataPointer, offset + 3);
  const b4: uint32 = LoadUint8(dataPointer, offset + 4);
  const b5: uint32 = LoadUint8(dataPointer, offset + 5);
  const b6: uint32 = LoadUint8(dataPointer, offset + 6);
  const b7: uint32 = LoadUint8(dataPointer, offset + 7);
  let lowWord: uint32;
  let highWord: uint32;

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

  return MakeBigInt(lowWord, highWord, signed);
}

extern macro DataViewBuiltinsAssembler::DataViewElementSize(
    constexpr ElementsKind): constexpr int31;

// GetViewValue ( view, requestIndex, isLittleEndian, type )
// https://tc39.es/ecma262/#sec-getviewvalue
transitioning macro DataViewGet(
    context: Context, receiver: JSAny, requestIndex: JSAny,
    requestedLittleEndian: JSAny, kind: constexpr ElementsKind): Numeric {
  // 1. Perform ? RequireInternalSlot(view, [[DataView]]).
  // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
  const dataView: JSDataView =
      ValidateDataView(context, receiver, MakeDataViewGetterNameString(kind));

  try {
    // 3. Let getIndex be ? ToIndex(requestIndex).
    const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError;

    // 4. Set isLittleEndian to ! ToBoolean(isLittleEndian).
    const littleEndian: bool = ToBoolean(requestedLittleEndian);

    // 5. Let buffer be view.[[ViewedArrayBuffer]].
    const buffer: JSArrayBuffer = dataView.buffer;

    // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
    if (IsDetachedBuffer(buffer)) {
      ThrowTypeError(
          MessageTemplate::kDetachedOperation,
          MakeDataViewGetterNameString(kind));
380 381
    }

382 383
    // 7. Let viewOffset be view.[[ByteOffset]].
    const viewOffset: uintptr = dataView.byte_offset;
384

385 386
    // 8. Let viewSize be view.[[ByteLength]].
    const viewSize: uintptr = dataView.byte_length;
387

388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
    // 9. Let elementSize be the Element Size value specified in Table 62
    // for Element Type type.
    const elementSize: uintptr = DataViewElementSize(kind);

    // 10. If getIndex + elementSize > viewSize, throw a RangeError exception.
    CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
        otherwise RangeError;

    // 11. Let bufferIndex be getIndex + viewOffset.
    const bufferIndex: uintptr = getIndex + viewOffset;

    if constexpr (kind == ElementsKind::UINT8_ELEMENTS) {
      return LoadDataView8(buffer, bufferIndex, false);
    } else if constexpr (kind == ElementsKind::INT8_ELEMENTS) {
      return LoadDataView8(buffer, bufferIndex, true);
    } else if constexpr (kind == ElementsKind::UINT16_ELEMENTS) {
      return LoadDataView16(buffer, bufferIndex, littleEndian, false);
    } else if constexpr (kind == ElementsKind::INT16_ELEMENTS) {
      return LoadDataView16(buffer, bufferIndex, littleEndian, true);
    } else if constexpr (kind == ElementsKind::UINT32_ELEMENTS) {
      return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
    } else if constexpr (kind == ElementsKind::INT32_ELEMENTS) {
      return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
    } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
      return LoadDataView32(buffer, bufferIndex, littleEndian, kind);
    } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
      return LoadDataViewFloat64(buffer, bufferIndex, littleEndian);
    } else if constexpr (kind == ElementsKind::BIGUINT64_ELEMENTS) {
      return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, false);
    } else if constexpr (kind == ElementsKind::BIGINT64_ELEMENTS) {
      return LoadDataViewBigInt(buffer, bufferIndex, littleEndian, true);
    } else {
      unreachable;
421
    }
422 423
  } label RangeError {
    ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset);
424
  }
425
}
426

427 428 429 430 431 432
transitioning javascript builtin DataViewPrototypeGetUint8(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  return DataViewGet(
      context, receiver, offset, Undefined, ElementsKind::UINT8_ELEMENTS);
}
433

434 435 436 437 438 439
transitioning javascript builtin DataViewPrototypeGetInt8(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  return DataViewGet(
      context, receiver, offset, Undefined, ElementsKind::INT8_ELEMENTS);
}
440

441 442 443 444 445 446 447
transitioning javascript builtin DataViewPrototypeGetUint16(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian, ElementsKind::UINT16_ELEMENTS);
}
448

449 450 451 452 453 454 455
transitioning javascript builtin DataViewPrototypeGetInt16(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian, ElementsKind::INT16_ELEMENTS);
}
456

457 458 459 460 461 462 463
transitioning javascript builtin DataViewPrototypeGetUint32(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian, ElementsKind::UINT32_ELEMENTS);
}
464

465 466 467 468 469 470 471
transitioning javascript builtin DataViewPrototypeGetInt32(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian, ElementsKind::INT32_ELEMENTS);
}
472

473 474 475 476 477 478 479 480
transitioning javascript builtin DataViewPrototypeGetFloat32(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian,
      ElementsKind::FLOAT32_ELEMENTS);
}
481

482 483 484 485 486 487 488 489
transitioning javascript builtin DataViewPrototypeGetFloat64(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian,
      ElementsKind::FLOAT64_ELEMENTS);
}
490

491 492 493 494 495 496 497 498
transitioning javascript builtin DataViewPrototypeGetBigUint64(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian,
      ElementsKind::BIGUINT64_ELEMENTS);
}
499

500 501 502 503 504 505 506 507
transitioning javascript builtin DataViewPrototypeGetBigInt64(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const isLittleEndian: JSAny = arguments[1];
  return DataViewGet(
      context, receiver, offset, isLittleEndian,
      ElementsKind::BIGINT64_ELEMENTS);
}
508

509 510 511
extern macro ToNumber(Context, JSAny): Number;
extern macro ToBigInt(Context, JSAny): BigInt;
extern macro TruncateFloat64ToWord32(float64): uint32;
512

513 514
extern macro DataViewBuiltinsAssembler::StoreWord8(
    RawPtr, uintptr, uint32): void;
515

516 517 518
macro StoreDataView8(buffer: JSArrayBuffer, offset: uintptr, value: uint32) {
  StoreWord8(buffer.backing_store_ptr, offset, value & 0xFF);
}
519

520 521 522 523
macro StoreDataView16(
    buffer: JSArrayBuffer, offset: uintptr, value: uint32,
    requestedLittleEndian: bool) {
  const dataPointer: RawPtr = buffer.backing_store_ptr;
524

525 526
  const b0: uint32 = value & 0xFF;
  const b1: uint32 = (value >>> 8) & 0xFF;
527

528 529 530 531 532 533
  if (requestedLittleEndian) {
    StoreWord8(dataPointer, offset, b0);
    StoreWord8(dataPointer, offset + 1, b1);
  } else {
    StoreWord8(dataPointer, offset, b1);
    StoreWord8(dataPointer, offset + 1, b0);
534
  }
535
}
536

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
macro StoreDataView32(
    buffer: JSArrayBuffer, offset: uintptr, value: uint32,
    requestedLittleEndian: bool) {
  const dataPointer: RawPtr = buffer.backing_store_ptr;

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

  if (requestedLittleEndian) {
    StoreWord8(dataPointer, offset, b0);
    StoreWord8(dataPointer, offset + 1, b1);
    StoreWord8(dataPointer, offset + 2, b2);
    StoreWord8(dataPointer, offset + 3, b3);
  } else {
    StoreWord8(dataPointer, offset, b3);
    StoreWord8(dataPointer, offset + 1, b2);
    StoreWord8(dataPointer, offset + 2, b1);
    StoreWord8(dataPointer, offset + 3, b0);
557
  }
558
}
559

560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
macro StoreDataView64(
    buffer: JSArrayBuffer, offset: uintptr, lowWord: uint32, highWord: uint32,
    requestedLittleEndian: bool) {
  const dataPointer: RawPtr = buffer.backing_store_ptr;

  const b0: uint32 = lowWord & 0xFF;
  const b1: uint32 = (lowWord >>> 8) & 0xFF;
  const b2: uint32 = (lowWord >>> 16) & 0xFF;
  const b3: uint32 = lowWord >>> 24;

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

  if (requestedLittleEndian) {
    StoreWord8(dataPointer, offset, b0);
    StoreWord8(dataPointer, offset + 1, b1);
    StoreWord8(dataPointer, offset + 2, b2);
    StoreWord8(dataPointer, offset + 3, b3);
    StoreWord8(dataPointer, offset + 4, b4);
    StoreWord8(dataPointer, offset + 5, b5);
    StoreWord8(dataPointer, offset + 6, b6);
    StoreWord8(dataPointer, offset + 7, b7);
  } else {
    StoreWord8(dataPointer, offset, b7);
    StoreWord8(dataPointer, offset + 1, b6);
    StoreWord8(dataPointer, offset + 2, b5);
    StoreWord8(dataPointer, offset + 3, b4);
    StoreWord8(dataPointer, offset + 4, b3);
    StoreWord8(dataPointer, offset + 5, b2);
    StoreWord8(dataPointer, offset + 6, b1);
    StoreWord8(dataPointer, offset + 7, b0);
593
  }
594
}
595

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntLength(BigIntBase):
    uint32;
extern macro DataViewBuiltinsAssembler::DataViewDecodeBigIntSign(BigIntBase):
    uint32;

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

  // The 32-bit words that will hold the BigInt's value in
  // two's complement representation.
  let lowWord: uint32 = 0;
  let highWord: uint32 = 0;

  // The length is nonzero if and only if the BigInt's value is nonzero.
  if (length != 0) {
    if constexpr (Is64()) {
      // There is always exactly 1 BigInt digit to load in this case.
      const value: uintptr = bigint::LoadBigIntDigit(bigIntValue, 0);
      lowWord = Convert<uint32>(value);  // Truncates value to 32 bits.
      highWord = Convert<uint32>(value >>> 32);
    } else {  // There might be either 1 or 2 BigInt digits we need to load.
      lowWord = Convert<uint32>(bigint::LoadBigIntDigit(bigIntValue, 0));
      if (length >= 2) {  // Only load the second digit if there is one.
        highWord = Convert<uint32>(bigint::LoadBigIntDigit(bigIntValue, 1));
626 627
      }
    }
628
  }
629

630 631 632 633
  if (sign != 0) {  // The number is negative, Convert it.
    highWord = Unsigned(0 - Signed(highWord));
    if (lowWord != 0) {
      highWord = Unsigned(Signed(highWord) - 1);
634
    }
635
    lowWord = Unsigned(0 - Signed(lowWord));
636 637
  }

638 639
  StoreDataView64(buffer, offset, lowWord, highWord, requestedLittleEndian);
}
640

641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
// SetViewValue ( view, requestIndex, isLittleEndian, type, value )
// https://tc39.es/ecma262/#sec-setviewvalue
transitioning macro DataViewSet(
    context: Context, receiver: JSAny, requestIndex: JSAny, value: JSAny,
    requestedLittleEndian: JSAny, kind: constexpr ElementsKind): JSAny {
  // 1. Perform ? RequireInternalSlot(view, [[DataView]]).
  // 2. Assert: view has a [[ViewedArrayBuffer]] internal slot.
  const dataView: JSDataView =
      ValidateDataView(context, receiver, MakeDataViewSetterNameString(kind));

  try {
    // 3. Let getIndex be ? ToIndex(requestIndex).
    const getIndex: uintptr = ToIndex(requestIndex) otherwise RangeError;

    const littleEndian: bool = ToBoolean(requestedLittleEndian);
    const buffer: JSArrayBuffer = dataView.buffer;

    let numberValue: Numeric;
    if constexpr (
        kind == ElementsKind::BIGUINT64_ELEMENTS ||
        kind == ElementsKind::BIGINT64_ELEMENTS) {
      // 4. If ! IsBigIntElementType(type) is true, let numberValue be
      // ? ToBigInt(value).
      numberValue = ToBigInt(context, value);
    } else {
      // 5. Otherwise, let numberValue be ? ToNumber(value).
      numberValue = ToNumber(context, value);
    }
669

670 671 672 673 674 675
    // 6. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
    if (IsDetachedBuffer(buffer)) {
      ThrowTypeError(
          MessageTemplate::kDetachedOperation,
          MakeDataViewSetterNameString(kind));
    }
676

677 678
    // 9. Let viewOffset be view.[[ByteOffset]].
    const viewOffset: uintptr = dataView.byte_offset;
679

680 681
    // 10. Let viewSize be view.[[ByteLength]].
    const viewSize: uintptr = dataView.byte_length;
682

683 684 685
    // 11. Let elementSize be the Element Size value specified in Table 62
    // for Element Type type.
    const elementSize: uintptr = DataViewElementSize(kind);
686

687 688 689
    // 12. If getIndex + elementSize > viewSize, throw a RangeError exception.
    CheckIntegerIndexAdditionOverflow(getIndex, elementSize, viewSize)
        otherwise RangeError;
690

691 692
    // 13. Let bufferIndex be getIndex + viewOffset.
    const bufferIndex: uintptr = getIndex + viewOffset;
693

694 695 696 697 698 699 700 701 702 703
    if constexpr (
        kind == ElementsKind::BIGUINT64_ELEMENTS ||
        kind == ElementsKind::BIGINT64_ELEMENTS) {
      // For these elements kinds numberValue is BigInt.
      const bigIntValue: BigInt = %RawDownCast<BigInt>(numberValue);
      StoreDataViewBigInt(buffer, bufferIndex, bigIntValue, littleEndian);
    } else {
      // For these elements kinds numberValue is Number.
      const numValue: Number = %RawDownCast<Number>(numberValue);
      const doubleValue: float64 = ChangeNumberToFloat64(numValue);
704

705
      if constexpr (
706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730
          kind == ElementsKind::UINT8_ELEMENTS ||
          kind == ElementsKind::INT8_ELEMENTS) {
        StoreDataView8(
            buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue));
      } else if constexpr (
          kind == ElementsKind::UINT16_ELEMENTS ||
          kind == ElementsKind::INT16_ELEMENTS) {
        StoreDataView16(
            buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue),
            littleEndian);
      } else if constexpr (
          kind == ElementsKind::UINT32_ELEMENTS ||
          kind == ElementsKind::INT32_ELEMENTS) {
        StoreDataView32(
            buffer, bufferIndex, TruncateFloat64ToWord32(doubleValue),
            littleEndian);
      } else if constexpr (kind == ElementsKind::FLOAT32_ELEMENTS) {
        const floatValue: float32 = TruncateFloat64ToFloat32(doubleValue);
        StoreDataView32(
            buffer, bufferIndex, BitcastFloat32ToInt32(floatValue),
            littleEndian);
      } else if constexpr (kind == ElementsKind::FLOAT64_ELEMENTS) {
        const lowWord: uint32 = Float64ExtractLowWord32(doubleValue);
        const highWord: uint32 = Float64ExtractHighWord32(doubleValue);
        StoreDataView64(buffer, bufferIndex, lowWord, highWord, littleEndian);
731 732
      }
    }
733 734 735
    return Undefined;
  } label RangeError {
    ThrowRangeError(MessageTemplate::kInvalidDataViewAccessorOffset);
736
  }
737
}
738

739 740 741 742 743 744 745 746
transitioning javascript builtin DataViewPrototypeSetUint8(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  return DataViewSet(
      context, receiver, offset, value, Undefined,
      ElementsKind::UINT8_ELEMENTS);
}
747

748 749 750 751 752 753 754
transitioning javascript builtin DataViewPrototypeSetInt8(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  return DataViewSet(
      context, receiver, offset, value, Undefined, ElementsKind::INT8_ELEMENTS);
}
755

756 757 758 759 760 761 762 763 764
transitioning javascript builtin DataViewPrototypeSetUint16(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::UINT16_ELEMENTS);
}
765

766 767 768 769 770 771 772 773 774
transitioning javascript builtin DataViewPrototypeSetInt16(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::INT16_ELEMENTS);
}
775

776 777 778 779 780 781 782 783 784
transitioning javascript builtin DataViewPrototypeSetUint32(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::UINT32_ELEMENTS);
}
785

786 787 788 789 790 791 792 793 794
transitioning javascript builtin DataViewPrototypeSetInt32(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::INT32_ELEMENTS);
}
795

796 797 798 799 800 801 802 803 804
transitioning javascript builtin DataViewPrototypeSetFloat32(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::FLOAT32_ELEMENTS);
}
805

806 807 808 809 810 811 812 813 814
transitioning javascript builtin DataViewPrototypeSetFloat64(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::FLOAT64_ELEMENTS);
}
815

816 817 818 819 820 821 822 823 824
transitioning javascript builtin DataViewPrototypeSetBigUint64(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::BIGUINT64_ELEMENTS);
}
825

826 827 828 829 830 831 832 833 834
transitioning javascript builtin DataViewPrototypeSetBigInt64(
    js-implicit context: NativeContext, receiver: JSAny)(...arguments): JSAny {
  const offset: JSAny = arguments[0];
  const value: JSAny = arguments[1];
  const isLittleEndian: JSAny = arguments[2];
  return DataViewSet(
      context, receiver, offset, value, isLittleEndian,
      ElementsKind::BIGINT64_ELEMENTS);
}
835
}