dbe06b9896a8c820bb0b43c429e9fa1219baa800
[WebKit.git] / Source / JavaScriptCore / builtins / TypedArrayPrototype.js
1 /*
2  * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 // Note that the intrisic @typedArrayLength checks that the argument passed is a typed array
27 // and throws if it is not.
28
29
30 // Typed Arrays have their own species constructor function since they need
31 // to look up their default constructor, which is expensive. If we used the
32 // normal speciesConstructor helper we would need to look up the default
33 // constructor every time.
34 @globalPrivate
35 function typedArraySpeciesConstructor(value)
36 {
37     "use strict";
38     let constructor = value.constructor;
39     if (constructor === @undefined)
40         return @typedArrayGetOriginalConstructor(value);
41
42     if (!@isObject(constructor))
43         @throwTypeError("|this|.constructor is not an Object or undefined");
44
45     constructor = constructor.@speciesSymbol;
46     if (constructor == null)
47         return @typedArrayGetOriginalConstructor(value);
48     // The lack of an @isConstructor(constructor) check here is not observable because
49     // the first thing we will do with the value is attempt to construct the result with it.
50     // If any user of this function does not immediately construct the result they need to
51     // verify that the result is a constructor.
52     return constructor;
53 }
54
55 @globalPrivate
56 function typedArrayClampArgumentToStartOrEnd(value, length, undefinedValue)
57 {
58     "use strict";
59
60     if (value === @undefined)
61         return undefinedValue;
62
63     let int = @toInteger(value);
64     if (int < 0) {
65         int += length;
66         return int < 0 ? 0 : int;
67     }
68     return int > length ? length : int;
69 }
70
71 function values()
72 {
73     "use strict";
74     @typedArrayLength(this);
75     return new @createArrayIterator(this, "value", @arrayIteratorValueNext);
76 }
77
78 function keys()
79 {
80     "use strict";
81     @typedArrayLength(this);
82     return new @createArrayIterator(this, "key", @arrayIteratorKeyNext);
83 }
84
85 function entries()
86 {
87     "use strict";
88     @typedArrayLength(this);
89     return new @createArrayIterator(this, "key+value", @arrayIteratorKeyValueNext);
90 }
91
92 function every(callback /*, thisArg */)
93 {
94     "use strict";
95     var length = @typedArrayLength(this);
96     var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
97
98     if (typeof callback !== "function")
99         @throwTypeError("TypedArray.prototype.every callback must be a function");
100
101     for (var i = 0; i < length; i++) {
102         if (!callback.@call(thisArg, this[i], i, this))
103             return false;
104     }
105
106     return true;
107 }
108
109 function fill(value /* [, start [, end]] */)
110 {
111     "use strict";
112
113     let length = @typedArrayLength(this);
114     let start;
115     let end;
116
117     if (arguments.length > 1) {
118         start = arguments[1];
119         if (arguments.length > 2) {
120             end = arguments[2];
121         }
122     }
123
124     start = @typedArrayClampArgumentToStartOrEnd(start, length, 0);
125     end = @typedArrayClampArgumentToStartOrEnd(end, length, length);
126
127     for (let i = start; i < end; i++)
128         this[i] = value;
129     return this;
130 }
131
132 function find(callback /* [, thisArg] */)
133 {
134     "use strict";
135     var length = @typedArrayLength(this);
136     var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
137
138     if (typeof callback !== "function")
139         @throwTypeError("TypedArray.prototype.find callback must be a function");
140
141     for (var i = 0; i < length; i++) {
142         let elem = this[i];
143         if (callback.@call(thisArg, elem, i, this))
144             return elem;
145     }
146     return @undefined;
147 }
148
149 function findIndex(callback /* [, thisArg] */)
150 {
151     "use strict";
152     var length = @typedArrayLength(this);
153     var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
154
155     if (typeof callback !== "function")
156         @throwTypeError("TypedArray.prototype.findIndex callback must be a function");
157
158     for (var i = 0; i < length; i++) {
159         if (callback.@call(thisArg, this[i], i, this))
160             return i;
161     }
162     return -1;
163 }
164
165 function forEach(callback /* [, thisArg] */)
166 {
167     "use strict";
168     var length = @typedArrayLength(this);
169     var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
170
171     if (typeof callback !== "function")
172         @throwTypeError("TypedArray.prototype.forEach callback must be a function");
173
174     for (var i = 0; i < length; i++)
175         callback.@call(thisArg, this[i], i, this);
176 }
177
178 function some(callback /* [, thisArg] */)
179 {
180     // 22.2.3.24
181     "use strict";
182     var length = @typedArrayLength(this);
183     var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
184
185     if (typeof callback !== "function")
186         @throwTypeError("TypedArray.prototype.some callback must be a function");
187
188     for (var i = 0; i < length; i++) {
189         if (callback.@call(thisArg, this[i], i, this))
190             return true;
191     }
192
193     return false;
194 }
195
196 function sort(comparator)
197 {
198     // 22.2.3.25
199     "use strict";
200
201     function min(a, b)
202     {
203         return a < b ? a : b;
204     }
205
206     function merge(dst, src, srcIndex, srcEnd, width, comparator)
207     {
208         var left = srcIndex;
209         var leftEnd = min(left + width, srcEnd);
210         var right = leftEnd;
211         var rightEnd = min(right + width, srcEnd);
212
213         for (var dstIndex = left; dstIndex < rightEnd; ++dstIndex) {
214             if (right < rightEnd) {
215                 if (left >= leftEnd || comparator(src[right], src[left]) < 0) {
216                     dst[dstIndex] = src[right++];
217                     continue;
218                 }
219             }
220
221             dst[dstIndex] = src[left++];
222         }
223     }
224
225     function mergeSort(array, valueCount, comparator)
226     {
227         var buffer = [ ];
228         buffer.length = valueCount;
229
230         var dst = buffer;
231         var src = array;
232
233         for (var width = 1; width < valueCount; width *= 2) {
234             for (var srcIndex = 0; srcIndex < valueCount; srcIndex += 2 * width)
235                 merge(dst, src, srcIndex, valueCount, width, comparator);
236
237             var tmp = src;
238             src = dst;
239             dst = tmp;
240         }
241
242         if (src != array) {
243             for(var i = 0; i < valueCount; i++)
244                 array[i] = src[i];
245         }
246     }
247
248     var length = @typedArrayLength(this);
249
250     if (length < 2)
251         return;
252
253     if (typeof comparator == "function")
254         mergeSort(this, length, comparator);
255     else
256         @typedArraySort(this);
257     
258     return this;
259 }
260
261 function subarray(begin, end)
262 {
263     "use strict";
264
265     if (!@isTypedArrayView(this))
266         @throwTypeError("|this| should be a typed array view");
267
268     let start = @toInteger(begin);
269     let finish;
270     if (end !== @undefined)
271         finish = @toInteger(end);
272
273     let constructor = @typedArraySpeciesConstructor(this);
274
275     return @typedArraySubarrayCreate.@call(this, start, finish, constructor);
276 }
277
278 function reduce(callback /* [, initialValue] */)
279 {
280     // 22.2.3.19
281     "use strict";
282
283     var length = @typedArrayLength(this);
284
285     if (typeof callback !== "function")
286         @throwTypeError("TypedArray.prototype.reduce callback must be a function");
287
288     if (length === 0 && arguments.length < 2)
289         @throwTypeError("TypedArray.prototype.reduce of empty array with no initial value");
290
291     var accumulator, k = 0;
292     if (arguments.length > 1)
293         accumulator = arguments[1];
294     else
295         accumulator = this[k++];
296
297     for (; k < length; k++)
298         accumulator = callback.@call(@undefined, accumulator, this[k], k, this);
299
300     return accumulator;
301 }
302
303 function reduceRight(callback /* [, initialValue] */)
304 {
305     // 22.2.3.20
306     "use strict";
307
308     var length = @typedArrayLength(this);
309
310     if (typeof callback !== "function")
311         @throwTypeError("TypedArray.prototype.reduceRight callback must be a function");
312
313     if (length === 0 && arguments.length < 2)
314         @throwTypeError("TypedArray.prototype.reduceRight of empty array with no initial value");
315
316     var accumulator, k = length - 1;
317     if (arguments.length > 1)
318         accumulator = arguments[1];
319     else
320         accumulator = this[k--];
321
322     for (; k >= 0; k--)
323         accumulator = callback.@call(@undefined, accumulator, this[k], k, this);
324
325     return accumulator;
326 }
327
328 function map(callback /*, thisArg */)
329 {
330     // 22.2.3.18
331     "use strict";
332
333     var length = @typedArrayLength(this);
334
335     if (typeof callback !== "function")
336         @throwTypeError("TypedArray.prototype.map callback must be a function");
337
338     var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
339
340     // Do species construction
341     var constructor = this.constructor;
342     var result;
343     if (constructor === @undefined)
344         result = new (@typedArrayGetOriginalConstructor(this))(length);
345     else {
346         var speciesConstructor = @Object(constructor).@speciesSymbol;
347         if (speciesConstructor === null || speciesConstructor === @undefined)
348             result = new (@typedArrayGetOriginalConstructor(this))(length);
349         else {
350             result = new speciesConstructor(length);
351             // typedArrayLength throws if it doesn't get a view.
352             @typedArrayLength(result);
353         }
354     }
355
356     for (var i = 0; i < length; i++) {
357         var mappedValue = callback.@call(thisArg, this[i], i, this);
358         result[i] = mappedValue;
359     }
360     return result;
361 }
362
363 function filter(callback /*, thisArg */)
364 {
365     "use strict";
366
367     var length = @typedArrayLength(this);
368
369     if (typeof callback !== "function")
370         @throwTypeError("TypedArray.prototype.filter callback must be a function");
371
372     var thisArg = arguments.length > 1 ? arguments[1] : @undefined;
373     var kept = [];
374
375     for (var i = 0; i < length; i++) {
376         var value = this[i];
377         if (callback.@call(thisArg, value, i, this))
378             kept.@push(value);
379     }
380
381     var constructor = this.constructor;
382     var result;
383     var resultLength = kept.length;
384     if (constructor === @undefined)
385         result = new (@typedArrayGetOriginalConstructor(this))(resultLength);
386     else {
387         var speciesConstructor = @Object(constructor).@speciesSymbol;
388         if (speciesConstructor === null || speciesConstructor === @undefined)
389             result = new (@typedArrayGetOriginalConstructor(this))(resultLength);
390         else {
391             result = new speciesConstructor(resultLength);
392             // typedArrayLength throws if it doesn't get a view.
393             @typedArrayLength(result);
394         }
395     }
396
397     for (var i = 0; i < kept.length; i++)
398         result[i] = kept[i];
399
400     return result;
401 }
402
403 function toLocaleString()
404 {
405     "use strict";
406
407     var length = @typedArrayLength(this);
408
409     if (length == 0)
410         return "";
411
412     var string = this[0].toLocaleString();
413     for (var i = 1; i < length; i++)
414         string += "," + this[i].toLocaleString();
415
416     return string;
417 }