1// Copyright 2013 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(function(global, utils) {
6
7"use strict";
8
9%CheckIsBootstrapping();
10
11// -------------------------------------------------------------------
12// Imports
13
14// array.js has to come before typedarray.js for this to work
15var ArrayToString = utils.ImportNow("ArrayToString");
16var ArrayValues;
17var GetIterator;
18var GetMethod;
19var GlobalArray = global.Array;
20var GlobalArrayBuffer = global.ArrayBuffer;
21var GlobalArrayBufferPrototype = GlobalArrayBuffer.prototype;
22var GlobalObject = global.Object;
23var InnerArrayCopyWithin;
24var InnerArrayEvery;
25var InnerArrayFill;
26var InnerArrayFilter;
27var InnerArrayFind;
28var InnerArrayFindIndex;
29var InnerArrayForEach;
30var InnerArrayJoin;
31var InnerArrayReduce;
32var InnerArrayReduceRight;
33var InnerArraySome;
34var InnerArraySort;
35var InnerArrayToLocaleString;
36var InternalArray = utils.InternalArray;
37var MaxSimple;
38var MinSimple;
39var PackedArrayReverse;
40var SpeciesConstructor;
41var ToPositiveInteger;
42var ToIndex;
43var iteratorSymbol = utils.ImportNow("iterator_symbol");
44var speciesSymbol = utils.ImportNow("species_symbol");
45var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
46
47macro TYPED_ARRAYS(FUNCTION)
48// arrayIds below should be synchronized with Runtime_TypedArrayInitialize.
49FUNCTION(1, Uint8Array, 1)
50FUNCTION(2, Int8Array, 1)
51FUNCTION(3, Uint16Array, 2)
52FUNCTION(4, Int16Array, 2)
53FUNCTION(5, Uint32Array, 4)
54FUNCTION(6, Int32Array, 4)
55FUNCTION(7, Float32Array, 4)
56FUNCTION(8, Float64Array, 8)
57FUNCTION(9, Uint8ClampedArray, 1)
58endmacro
59
60macro DECLARE_GLOBALS(INDEX, NAME, SIZE)
61var GlobalNAME = global.NAME;
62endmacro
63
64TYPED_ARRAYS(DECLARE_GLOBALS)
65
66var GlobalTypedArray = %object_get_prototype_of(GlobalUint8Array);
67
68utils.Import(function(from) {
69  ArrayValues = from.ArrayValues;
70  GetIterator = from.GetIterator;
71  GetMethod = from.GetMethod;
72  InnerArrayCopyWithin = from.InnerArrayCopyWithin;
73  InnerArrayEvery = from.InnerArrayEvery;
74  InnerArrayFill = from.InnerArrayFill;
75  InnerArrayFilter = from.InnerArrayFilter;
76  InnerArrayFind = from.InnerArrayFind;
77  InnerArrayFindIndex = from.InnerArrayFindIndex;
78  InnerArrayForEach = from.InnerArrayForEach;
79  InnerArrayJoin = from.InnerArrayJoin;
80  InnerArrayReduce = from.InnerArrayReduce;
81  InnerArrayReduceRight = from.InnerArrayReduceRight;
82  InnerArraySome = from.InnerArraySome;
83  InnerArraySort = from.InnerArraySort;
84  InnerArrayToLocaleString = from.InnerArrayToLocaleString;
85  MaxSimple = from.MaxSimple;
86  MinSimple = from.MinSimple;
87  PackedArrayReverse = from.PackedArrayReverse;
88  SpeciesConstructor = from.SpeciesConstructor;
89  ToPositiveInteger = from.ToPositiveInteger;
90  ToIndex = from.ToIndex;
91});
92
93// --------------- Typed Arrays ---------------------
94
95function TypedArrayDefaultConstructor(typedArray) {
96  switch (%_ClassOf(typedArray)) {
97macro TYPED_ARRAY_CONSTRUCTOR_CASE(ARRAY_ID, NAME, ELEMENT_SIZE)
98    case "NAME":
99      return GlobalNAME;
100endmacro
101TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR_CASE)
102  }
103  // The TypeError should not be generated since all callers should
104  // have already called ValidateTypedArray.
105  throw %make_type_error(kIncompatibleMethodReceiver,
106                      "TypedArrayDefaultConstructor", this);
107}
108
109function TypedArrayCreate(constructor, arg0, arg1, arg2) {
110  if (IS_UNDEFINED(arg1)) {
111    var newTypedArray = new constructor(arg0);
112  } else {
113    var newTypedArray = new constructor(arg0, arg1, arg2);
114  }
115  if (!IS_TYPEDARRAY(newTypedArray)) throw %make_type_error(kNotTypedArray);
116  // TODO(littledan): Check for being detached, here and elsewhere
117  // All callers where the first argument is a Number have no additional
118  // arguments.
119  if (IS_NUMBER(arg0) && %_TypedArrayGetLength(newTypedArray) < arg0) {
120    throw %make_type_error(kTypedArrayTooShort);
121  }
122  return newTypedArray;
123}
124
125function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2, conservative) {
126  var defaultConstructor = TypedArrayDefaultConstructor(exemplar);
127  var constructor = SpeciesConstructor(exemplar, defaultConstructor,
128                                       conservative);
129  return TypedArrayCreate(constructor, arg0, arg1, arg2);
130}
131
132macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
133function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
134  if (!IS_UNDEFINED(byteOffset)) {
135    byteOffset = ToIndex(byteOffset, kInvalidTypedArrayLength);
136  }
137  if (!IS_UNDEFINED(length)) {
138    length = ToIndex(length, kInvalidTypedArrayLength);
139  }
140  if (length > %_MaxSmi()) {
141    // Note: this is not per spec, but rather a constraint of our current
142    // representation (which uses smi's).
143    throw %make_range_error(kInvalidTypedArrayLength);
144  }
145
146  var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
147  var offset;
148  if (IS_UNDEFINED(byteOffset)) {
149    offset = 0;
150  } else {
151    offset = byteOffset;
152
153    if (offset % ELEMENT_SIZE !== 0) {
154      throw %make_range_error(kInvalidTypedArrayAlignment,
155                           "start offset", "NAME", ELEMENT_SIZE);
156    }
157  }
158
159  var newByteLength;
160  if (IS_UNDEFINED(length)) {
161    if (bufferByteLength % ELEMENT_SIZE !== 0) {
162      throw %make_range_error(kInvalidTypedArrayAlignment,
163                           "byte length", "NAME", ELEMENT_SIZE);
164    }
165    newByteLength = bufferByteLength - offset;
166    if (newByteLength < 0) {
167      throw %make_range_error(kInvalidTypedArrayAlignment,
168                           "byte length", "NAME", ELEMENT_SIZE);
169    }
170  } else {
171    newByteLength = length * ELEMENT_SIZE;
172    if (offset + newByteLength > bufferByteLength) {
173      throw %make_range_error(kInvalidTypedArrayLength);
174    }
175  }
176  %_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength, true);
177}
178
179function NAMEConstructByLength(obj, length) {
180  var l = IS_UNDEFINED(length) ?
181    0 : ToIndex(length, kInvalidTypedArrayLength);
182  if (length > %_MaxSmi()) {
183    // Note: this is not per spec, but rather a constraint of our current
184    // representation (which uses smi's).
185    throw %make_range_error(kInvalidTypedArrayLength);
186  }
187  var byteLength = l * ELEMENT_SIZE;
188  if (byteLength > %_TypedArrayMaxSizeInHeap()) {
189    var buffer = new GlobalArrayBuffer(byteLength);
190    %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength, true);
191  } else {
192    %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, true);
193  }
194}
195
196function NAMEConstructByArrayLike(obj, arrayLike, length) {
197  var l = ToPositiveInteger(length, kInvalidTypedArrayLength);
198
199  if (l > %_MaxSmi()) {
200    throw %make_range_error(kInvalidTypedArrayLength);
201  }
202  var initialized = false;
203  var byteLength = l * ELEMENT_SIZE;
204  if (byteLength <= %_TypedArrayMaxSizeInHeap()) {
205    %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, false);
206  } else {
207    initialized =
208        %TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l);
209  }
210  if (!initialized) {
211    for (var i = 0; i < l; i++) {
212      // It is crucial that we let any execptions from arrayLike[i]
213      // propagate outside the function.
214      obj[i] = arrayLike[i];
215    }
216  }
217}
218
219function NAMEConstructByIterable(obj, iterable, iteratorFn) {
220  var list = new InternalArray();
221  // Reading the Symbol.iterator property of iterable twice would be
222  // observable with getters, so instead, we call the function which
223  // was already looked up, and wrap it in another iterable. The
224  // __proto__ of the new iterable is set to null to avoid any chance
225  // of modifications to Object.prototype being observable here.
226  var iterator = %_Call(iteratorFn, iterable);
227  var newIterable = {
228    __proto__: null
229  };
230  // TODO(littledan): Computed properties don't work yet in nosnap.
231  // Rephrase when they do.
232  newIterable[iteratorSymbol] = function() { return iterator; }
233  for (var value of newIterable) {
234    list.push(value);
235  }
236  NAMEConstructByArrayLike(obj, list, list.length);
237}
238
239// ES#sec-typedarray-typedarray TypedArray ( typedArray )
240function NAMEConstructByTypedArray(obj, typedArray) {
241  // TODO(littledan): Throw on detached typedArray
242  var srcData = %TypedArrayGetBuffer(typedArray);
243  var length = %_TypedArrayGetLength(typedArray);
244  var byteLength = %_ArrayBufferViewGetByteLength(typedArray);
245  var newByteLength = length * ELEMENT_SIZE;
246  NAMEConstructByArrayLike(obj, typedArray, length);
247  var bufferConstructor = SpeciesConstructor(srcData, GlobalArrayBuffer);
248  var prototype = bufferConstructor.prototype;
249  // TODO(littledan): Use the right prototype based on bufferConstructor's realm
250  if (IS_RECEIVER(prototype) && prototype !== GlobalArrayBufferPrototype) {
251    %InternalSetPrototype(%TypedArrayGetBuffer(obj), prototype);
252  }
253}
254
255function NAMEConstructor(arg1, arg2, arg3) {
256  if (!IS_UNDEFINED(new.target)) {
257    if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) {
258      NAMEConstructByArrayBuffer(this, arg1, arg2, arg3);
259    } else if (IS_TYPEDARRAY(arg1)) {
260      NAMEConstructByTypedArray(this, arg1);
261    } else if (IS_RECEIVER(arg1)) {
262      var iteratorFn = arg1[iteratorSymbol];
263      if (IS_UNDEFINED(iteratorFn) || iteratorFn === ArrayValues) {
264        NAMEConstructByArrayLike(this, arg1, arg1.length);
265      } else {
266        NAMEConstructByIterable(this, arg1, iteratorFn);
267      }
268    } else {
269      NAMEConstructByLength(this, arg1);
270    }
271  } else {
272    throw %make_type_error(kConstructorNotFunction, "NAME")
273  }
274}
275
276function NAMESubArray(begin, end) {
277  var beginInt = TO_INTEGER(begin);
278  if (!IS_UNDEFINED(end)) {
279    var endInt = TO_INTEGER(end);
280    var srcLength = %_TypedArrayGetLength(this);
281  } else {
282    var srcLength = %_TypedArrayGetLength(this);
283    var endInt = srcLength;
284  }
285
286  if (beginInt < 0) {
287    beginInt = MaxSimple(0, srcLength + beginInt);
288  } else {
289    beginInt = MinSimple(beginInt, srcLength);
290  }
291
292  if (endInt < 0) {
293    endInt = MaxSimple(0, srcLength + endInt);
294  } else {
295    endInt = MinSimple(endInt, srcLength);
296  }
297
298  if (endInt < beginInt) {
299    endInt = beginInt;
300  }
301
302  var newLength = endInt - beginInt;
303  var beginByteOffset =
304      %_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
305  // BUG(v8:4665): For web compatibility, subarray needs to always build an
306  // instance of the default constructor.
307  // TODO(littledan): Switch to the standard or standardize the fix
308  return new GlobalNAME(%TypedArrayGetBuffer(this), beginByteOffset, newLength);
309}
310endmacro
311
312TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
313
314function TypedArraySubArray(begin, end) {
315  switch (%_ClassOf(this)) {
316macro TYPED_ARRAY_SUBARRAY_CASE(ARRAY_ID, NAME, ELEMENT_SIZE)
317    case "NAME":
318      return %_Call(NAMESubArray, this, begin, end);
319endmacro
320TYPED_ARRAYS(TYPED_ARRAY_SUBARRAY_CASE)
321  }
322  throw %make_type_error(kIncompatibleMethodReceiver,
323                      "get TypedArray.prototype.subarray", this);
324}
325%SetForceInlineFlag(TypedArraySubArray);
326
327
328
329function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
330  if (offset > 0) {
331    for (var i = 0; i < sourceLength; i++) {
332      target[offset + i] = source[i];
333    }
334  }
335  else {
336    for (var i = 0; i < sourceLength; i++) {
337      target[i] = source[i];
338    }
339  }
340}
341
342function TypedArraySetFromOverlappingTypedArray(target, source, offset) {
343  var sourceElementSize = source.BYTES_PER_ELEMENT;
344  var targetElementSize = target.BYTES_PER_ELEMENT;
345  var sourceLength = %_TypedArrayGetLength(source);
346
347  // Copy left part.
348  function CopyLeftPart() {
349    // First un-mutated byte after the next write
350    var targetPtr = target.byteOffset + (offset + 1) * targetElementSize;
351    // Next read at sourcePtr. We do not care for memory changing before
352    // sourcePtr - we have already copied it.
353    var sourcePtr = source.byteOffset;
354    for (var leftIndex = 0;
355         leftIndex < sourceLength && targetPtr <= sourcePtr;
356         leftIndex++) {
357      target[offset + leftIndex] = source[leftIndex];
358      targetPtr += targetElementSize;
359      sourcePtr += sourceElementSize;
360    }
361    return leftIndex;
362  }
363  var leftIndex = CopyLeftPart();
364
365  // Copy right part;
366  function CopyRightPart() {
367    // First unmutated byte before the next write
368    var targetPtr =
369      target.byteOffset + (offset + sourceLength - 1) * targetElementSize;
370    // Next read before sourcePtr. We do not care for memory changing after
371    // sourcePtr - we have already copied it.
372    var sourcePtr =
373      source.byteOffset + sourceLength * sourceElementSize;
374    for(var rightIndex = sourceLength - 1;
375        rightIndex >= leftIndex && targetPtr >= sourcePtr;
376        rightIndex--) {
377      target[offset + rightIndex] = source[rightIndex];
378      targetPtr -= targetElementSize;
379      sourcePtr -= sourceElementSize;
380    }
381    return rightIndex;
382  }
383  var rightIndex = CopyRightPart();
384
385  var temp = new GlobalArray(rightIndex + 1 - leftIndex);
386  for (var i = leftIndex; i <= rightIndex; i++) {
387    temp[i - leftIndex] = source[i];
388  }
389  for (i = leftIndex; i <= rightIndex; i++) {
390    target[offset + i] = temp[i - leftIndex];
391  }
392}
393
394function TypedArraySet(obj, offset) {
395  var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
396  if (intOffset < 0) throw %make_type_error(kTypedArraySetNegativeOffset);
397
398  if (intOffset > %_MaxSmi()) {
399    throw %make_range_error(kTypedArraySetSourceTooLarge);
400  }
401  switch (%TypedArraySetFastCases(this, obj, intOffset)) {
402    // These numbers should be synchronized with runtime.cc.
403    case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE
404      return;
405    case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
406      TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
407      return;
408    case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
409      TypedArraySetFromArrayLike(this,
410          obj, %_TypedArrayGetLength(obj), intOffset);
411      return;
412    case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
413      var l = obj.length;
414      if (IS_UNDEFINED(l)) {
415        if (IS_NUMBER(obj)) {
416            // For number as a first argument, throw TypeError
417            // instead of silently ignoring the call, so that
418            // the user knows (s)he did something wrong.
419            // (Consistent with Firefox and Blink/WebKit)
420            throw %make_type_error(kInvalidArgument);
421        }
422        return;
423      }
424      l = TO_LENGTH(l);
425      if (intOffset + l > %_TypedArrayGetLength(this)) {
426        throw %make_range_error(kTypedArraySetSourceTooLarge);
427      }
428      TypedArraySetFromArrayLike(this, obj, l, intOffset);
429      return;
430  }
431}
432%FunctionSetLength(TypedArraySet, 1);
433
434function TypedArrayGetToStringTag() {
435  if (!IS_TYPEDARRAY(this)) return;
436  var name = %_ClassOf(this);
437  if (IS_UNDEFINED(name)) return;
438  return name;
439}
440
441
442function TypedArrayCopyWithin(target, start, end) {
443  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
444
445  var length = %_TypedArrayGetLength(this);
446
447  // TODO(littledan): Replace with a memcpy for better performance
448  return InnerArrayCopyWithin(target, start, end, this, length);
449}
450%FunctionSetLength(TypedArrayCopyWithin, 2);
451
452
453// ES6 draft 05-05-15, section 22.2.3.7
454function TypedArrayEvery(f, receiver) {
455  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
456
457  var length = %_TypedArrayGetLength(this);
458
459  return InnerArrayEvery(f, receiver, this, length);
460}
461%FunctionSetLength(TypedArrayEvery, 1);
462
463
464// ES6 draft 08-24-14, section 22.2.3.12
465function TypedArrayForEach(f, receiver) {
466  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
467
468  var length = %_TypedArrayGetLength(this);
469
470  InnerArrayForEach(f, receiver, this, length);
471}
472%FunctionSetLength(TypedArrayForEach, 1);
473
474
475// ES6 draft 04-05-14 section 22.2.3.8
476function TypedArrayFill(value, start, end) {
477  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
478
479  var length = %_TypedArrayGetLength(this);
480
481  return InnerArrayFill(value, start, end, this, length);
482}
483%FunctionSetLength(TypedArrayFill, 1);
484
485
486// ES6 draft 07-15-13, section 22.2.3.9
487function TypedArrayFilter(f, thisArg) {
488  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
489
490  var length = %_TypedArrayGetLength(this);
491  if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
492  var result = new InternalArray();
493  InnerArrayFilter(f, thisArg, this, length, result);
494  var captured = result.length;
495  var output = TypedArraySpeciesCreate(this, captured);
496  for (var i = 0; i < captured; i++) {
497    output[i] = result[i];
498  }
499  return output;
500}
501%FunctionSetLength(TypedArrayFilter, 1);
502
503
504// ES6 draft 07-15-13, section 22.2.3.10
505function TypedArrayFind(predicate, thisArg) {
506  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
507
508  var length = %_TypedArrayGetLength(this);
509
510  return InnerArrayFind(predicate, thisArg, this, length);
511}
512%FunctionSetLength(TypedArrayFind, 1);
513
514
515// ES6 draft 07-15-13, section 22.2.3.11
516function TypedArrayFindIndex(predicate, thisArg) {
517  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
518
519  var length = %_TypedArrayGetLength(this);
520
521  return InnerArrayFindIndex(predicate, thisArg, this, length);
522}
523%FunctionSetLength(TypedArrayFindIndex, 1);
524
525
526// ES6 draft 05-18-15, section 22.2.3.21
527function TypedArrayReverse() {
528  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
529
530  var length = %_TypedArrayGetLength(this);
531
532  return PackedArrayReverse(this, length);
533}
534
535
536function TypedArrayComparefn(x, y) {
537  if (x === 0 && x === y) {
538    x = 1 / x;
539    y = 1 / y;
540  }
541  if (x < y) {
542    return -1;
543  } else if (x > y) {
544    return 1;
545  } else if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) {
546    return NUMBER_IS_NAN(y) ? 0 : 1;
547  } else if (NUMBER_IS_NAN(x)) {
548    return 1;
549  }
550  return 0;
551}
552
553
554// ES6 draft 05-18-15, section 22.2.3.25
555function TypedArraySort(comparefn) {
556  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
557
558  var length = %_TypedArrayGetLength(this);
559
560  if (IS_UNDEFINED(comparefn)) {
561    comparefn = TypedArrayComparefn;
562  }
563
564  return InnerArraySort(this, length, comparefn);
565}
566
567
568// ES6 section 22.2.3.13
569function TypedArrayIndexOf(element, index) {
570  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
571
572  var length = %_TypedArrayGetLength(this);
573
574  if (length === 0) return -1;
575  if (!IS_NUMBER(element)) return -1;
576  var n = TO_INTEGER(index);
577
578  var k;
579  if (n === 0) {
580    k = 0;
581  } else if (n > 0) {
582    k = n;
583  } else {
584    k = length + n;
585    if (k < 0) {
586      k = 0;
587    }
588  }
589
590  while (k < length) {
591    var elementK = this[k];
592    if (element === elementK) {
593      return k;
594    }
595    ++k;
596  }
597  return -1;
598}
599%FunctionSetLength(TypedArrayIndexOf, 1);
600
601
602// ES6 section 22.2.3.16
603function TypedArrayLastIndexOf(element, index) {
604  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
605
606  var length = %_TypedArrayGetLength(this);
607
608  if (length === 0) return -1;
609  if (!IS_NUMBER(element)) return -1;
610  var n;
611  if (arguments.length < 2) {
612    n = length - 1;
613  } else {
614    n = TO_INTEGER(index);
615  }
616
617  var k;
618  if (n >= 0) {
619    if (length <= n) {
620      k = length - 1;
621    } else if (n === 0) {
622      k = 0;
623    } else {
624      k = n;
625    }
626  } else {
627    k = length + n;
628  }
629
630  while (k >= 0) {
631    var elementK = this[k];
632    if (element === elementK) {
633      return k;
634    }
635    --k;
636  }
637  return -1;
638}
639%FunctionSetLength(TypedArrayLastIndexOf, 1);
640
641
642// ES6 draft 07-15-13, section 22.2.3.18
643function TypedArrayMap(f, thisArg) {
644  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
645
646  var length = %_TypedArrayGetLength(this);
647  var result = TypedArraySpeciesCreate(this, length);
648  if (!IS_CALLABLE(f)) throw %make_type_error(kCalledNonCallable, f);
649  for (var i = 0; i < length; i++) {
650    var element = this[i];
651    result[i] = %_Call(f, thisArg, element, i, this);
652  }
653  return result;
654}
655%FunctionSetLength(TypedArrayMap, 1);
656
657
658// ES6 draft 05-05-15, section 22.2.3.24
659function TypedArraySome(f, receiver) {
660  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
661
662  var length = %_TypedArrayGetLength(this);
663
664  return InnerArraySome(f, receiver, this, length);
665}
666%FunctionSetLength(TypedArraySome, 1);
667
668
669// ES6 section 22.2.3.27
670function TypedArrayToLocaleString() {
671  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
672
673  var length = %_TypedArrayGetLength(this);
674
675  return InnerArrayToLocaleString(this, length);
676}
677
678
679// ES6 section 22.2.3.14
680function TypedArrayJoin(separator) {
681  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
682
683  var length = %_TypedArrayGetLength(this);
684
685  return InnerArrayJoin(separator, this, length);
686}
687
688
689// ES6 draft 07-15-13, section 22.2.3.19
690function TypedArrayReduce(callback, current) {
691  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
692
693  var length = %_TypedArrayGetLength(this);
694  return InnerArrayReduce(callback, current, this, length,
695                          arguments.length);
696}
697%FunctionSetLength(TypedArrayReduce, 1);
698
699
700// ES6 draft 07-15-13, section 22.2.3.19
701function TypedArrayReduceRight(callback, current) {
702  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
703
704  var length = %_TypedArrayGetLength(this);
705  return InnerArrayReduceRight(callback, current, this, length,
706                               arguments.length);
707}
708%FunctionSetLength(TypedArrayReduceRight, 1);
709
710
711function TypedArraySlice(start, end) {
712  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
713  var len = %_TypedArrayGetLength(this);
714
715  var relativeStart = TO_INTEGER(start);
716
717  var k;
718  if (relativeStart < 0) {
719    k = MaxSimple(len + relativeStart, 0);
720  } else {
721    k = MinSimple(relativeStart, len);
722  }
723
724  var relativeEnd;
725  if (IS_UNDEFINED(end)) {
726    relativeEnd = len;
727  } else {
728    relativeEnd = TO_INTEGER(end);
729  }
730
731  var final;
732  if (relativeEnd < 0) {
733    final = MaxSimple(len + relativeEnd, 0);
734  } else {
735    final = MinSimple(relativeEnd, len);
736  }
737
738  var count = MaxSimple(final - k, 0);
739  var array = TypedArraySpeciesCreate(this, count);
740  // The code below is the 'then' branch; the 'else' branch species
741  // a memcpy. Because V8 doesn't canonicalize NaN, the difference is
742  // unobservable.
743  var n = 0;
744  while (k < final) {
745    var kValue = this[k];
746    array[n] = kValue;
747    k++;
748    n++;
749  }
750  return array;
751}
752
753
754// ES2016 draft, section 22.2.3.14
755function TypedArrayIncludes(searchElement, fromIndex) {
756  if (!IS_TYPEDARRAY(this)) throw %make_type_error(kNotTypedArray);
757
758  var length = %_TypedArrayGetLength(this);
759
760  if (length === 0) return false;
761  var n = TO_INTEGER(fromIndex);
762
763  var k;
764  if (n >= 0) {
765    k = n;
766  } else {
767    k = length + n;
768    if (k < 0) {
769      k = 0;
770    }
771  }
772
773  while (k < length) {
774    var elementK = this[k];
775    if (%SameValueZero(searchElement, elementK)) {
776      return true;
777    }
778
779    ++k;
780  }
781
782  return false;
783}
784%FunctionSetLength(TypedArrayIncludes, 1);
785
786
787// ES6 draft 08-24-14, section 22.2.2.2
788function TypedArrayOf() {
789  var length = arguments.length;
790  var array = TypedArrayCreate(this, length);
791  for (var i = 0; i < length; i++) {
792    array[i] = arguments[i];
793  }
794  return array;
795}
796
797
798// ES#sec-iterabletoarraylike Runtime Semantics: IterableToArrayLike( items )
799function IterableToArrayLike(items) {
800  var iterable = GetMethod(items, iteratorSymbol);
801  if (!IS_UNDEFINED(iterable)) {
802    var internal_array = new InternalArray();
803    var i = 0;
804    for (var value of
805         { [iteratorSymbol]() { return GetIterator(items, iterable) } }) {
806      internal_array[i] = value;
807      i++;
808    }
809    var array = [];
810    %MoveArrayContents(internal_array, array);
811    return array;
812  }
813  return TO_OBJECT(items);
814}
815
816
817// ES#sec-%typedarray%.from
818// %TypedArray%.from ( source [ , mapfn [ , thisArg ] ] )
819function TypedArrayFrom(source, mapfn, thisArg) {
820  if (!%IsConstructor(this)) throw %make_type_error(kNotConstructor, this);
821  var mapping;
822  if (!IS_UNDEFINED(mapfn)) {
823    if (!IS_CALLABLE(mapfn)) throw %make_type_error(kCalledNonCallable, this);
824    mapping = true;
825  } else {
826    mapping = false;
827  }
828  var arrayLike = IterableToArrayLike(source);
829  var length = TO_LENGTH(arrayLike.length);
830  var targetObject = TypedArrayCreate(this, length);
831  var value, mappedValue;
832  for (var i = 0; i < length; i++) {
833    value = arrayLike[i];
834    if (mapping) {
835      mappedValue = %_Call(mapfn, thisArg, value, i);
836    } else {
837      mappedValue = value;
838    }
839    targetObject[i] = mappedValue;
840  }
841  return targetObject;
842}
843%FunctionSetLength(TypedArrayFrom, 1);
844
845// TODO(bmeurer): Migrate this to a proper builtin.
846function TypedArrayConstructor() {
847  throw %make_type_error(kConstructAbstractClass, "TypedArray");
848}
849
850function TypedArraySpecies() {
851  return this;
852}
853
854// -------------------------------------------------------------------
855
856%SetCode(GlobalTypedArray, TypedArrayConstructor);
857utils.InstallFunctions(GlobalTypedArray, DONT_ENUM, [
858  "from", TypedArrayFrom,
859  "of", TypedArrayOf
860]);
861utils.InstallGetter(GlobalTypedArray, speciesSymbol, TypedArraySpecies);
862utils.InstallGetter(GlobalTypedArray.prototype, toStringTagSymbol,
863                    TypedArrayGetToStringTag);
864utils.InstallFunctions(GlobalTypedArray.prototype, DONT_ENUM, [
865  "subarray", TypedArraySubArray,
866  "set", TypedArraySet,
867  "copyWithin", TypedArrayCopyWithin,
868  "every", TypedArrayEvery,
869  "fill", TypedArrayFill,
870  "filter", TypedArrayFilter,
871  "find", TypedArrayFind,
872  "findIndex", TypedArrayFindIndex,
873  "includes", TypedArrayIncludes,
874  "indexOf", TypedArrayIndexOf,
875  "join", TypedArrayJoin,
876  "lastIndexOf", TypedArrayLastIndexOf,
877  "forEach", TypedArrayForEach,
878  "map", TypedArrayMap,
879  "reduce", TypedArrayReduce,
880  "reduceRight", TypedArrayReduceRight,
881  "reverse", TypedArrayReverse,
882  "slice", TypedArraySlice,
883  "some", TypedArraySome,
884  "sort", TypedArraySort,
885  "toLocaleString", TypedArrayToLocaleString
886]);
887
888%AddNamedProperty(GlobalTypedArray.prototype, "toString", ArrayToString,
889                  DONT_ENUM);
890
891
892macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
893  %SetCode(GlobalNAME, NAMEConstructor);
894  %FunctionSetPrototype(GlobalNAME, new GlobalObject());
895  %InternalSetPrototype(GlobalNAME, GlobalTypedArray);
896  %InternalSetPrototype(GlobalNAME.prototype, GlobalTypedArray.prototype);
897
898  %AddNamedProperty(GlobalNAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
899                    READ_ONLY | DONT_ENUM | DONT_DELETE);
900
901  %AddNamedProperty(GlobalNAME.prototype,
902                    "constructor", global.NAME, DONT_ENUM);
903  %AddNamedProperty(GlobalNAME.prototype,
904                    "BYTES_PER_ELEMENT", ELEMENT_SIZE,
905                    READ_ONLY | DONT_ENUM | DONT_DELETE);
906endmacro
907
908TYPED_ARRAYS(SETUP_TYPED_ARRAY)
909
910})
911