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