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