- add third_party src.
[platform/framework/web/crosswalk.git] / src / v8 / src / typedarray.js
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 //     * Redistributions of source code must retain the above copyright
7 //       notice, this list of conditions and the following disclaimer.
8 //     * Redistributions in binary form must reproduce the above
9 //       copyright notice, this list of conditions and the following
10 //       disclaimer in the documentation and/or other materials provided
11 //       with the distribution.
12 //     * Neither the name of Google Inc. nor the names of its
13 //       contributors may be used to endorse or promote products derived
14 //       from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 "use strict";
29
30 // This file relies on the fact that the following declaration has been made
31 // in runtime.js:
32 // var $Array = global.Array;
33 var $ArrayBuffer = global.ArrayBuffer;
34
35
36 // --------------- Typed Arrays ---------------------
37
38 function CreateTypedArrayConstructor(name, elementSize, arrayId, constructor) {
39   function ConstructByArrayBuffer(obj, buffer, byteOffset, length) {
40     var offset = ToPositiveInteger(byteOffset, "invalid_typed_array_length")
41
42     if (offset % elementSize !== 0) {
43       throw MakeRangeError("invalid_typed_array_alignment",
44           "start offset", name, elementSize);
45     }
46     var bufferByteLength = %ArrayBufferGetByteLength(buffer);
47     if (offset > bufferByteLength) {
48       throw MakeRangeError("invalid_typed_array_offset");
49     }
50
51     var newByteLength;
52     var newLength;
53     if (IS_UNDEFINED(length)) {
54       if (bufferByteLength % elementSize !== 0) {
55         throw MakeRangeError("invalid_typed_array_alignment",
56           "byte length", name, elementSize);
57       }
58       newByteLength = bufferByteLength - offset;
59       newLength = newByteLength / elementSize;
60     } else {
61       var newLength = ToPositiveInteger(length, "invalid_typed_array_length");
62       newByteLength = newLength * elementSize;
63     }
64     if (offset + newByteLength > bufferByteLength) {
65       throw MakeRangeError("invalid_typed_array_length");
66     }
67     %TypedArrayInitialize(obj, arrayId, buffer, offset, newByteLength);
68   }
69
70   function ConstructByLength(obj, length) {
71     var l = ToPositiveInteger(length, "invalid_typed_array_length");
72     var byteLength = l * elementSize;
73     var buffer = new $ArrayBuffer(byteLength);
74     %TypedArrayInitialize(obj, arrayId, buffer, 0, byteLength);
75   }
76
77   function ConstructByArrayLike(obj, arrayLike) {
78     var length = arrayLike.length;
79     var l = ToPositiveInteger(length, "invalid_typed_array_length");
80     if(!%TypedArrayInitializeFromArrayLike(obj, arrayId, arrayLike, l)) {
81       for (var i = 0; i < l; i++) {
82         // It is crucial that we let any execptions from arrayLike[i]
83         // propagate outside the function.
84         obj[i] = arrayLike[i];
85       }
86     }
87   }
88
89   return function (arg1, arg2, arg3) {
90     if (%_IsConstructCall()) {
91       if (IS_ARRAYBUFFER(arg1)) {
92         ConstructByArrayBuffer(this, arg1, arg2, arg3);
93       } else if (IS_NUMBER(arg1) || IS_STRING(arg1) ||
94                  IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) {
95         ConstructByLength(this, arg1);
96       } else {
97         ConstructByArrayLike(this, arg1);
98       }
99     } else {
100       throw MakeTypeError("constructor_not_function", [name])
101     }
102   }
103 }
104
105 function TypedArrayGetBuffer() {
106   return %TypedArrayGetBuffer(this);
107 }
108
109 function TypedArrayGetByteLength() {
110   return %TypedArrayGetByteLength(this);
111 }
112
113 function TypedArrayGetByteOffset() {
114   return %TypedArrayGetByteOffset(this);
115 }
116
117 function TypedArrayGetLength() {
118   return %TypedArrayGetLength(this);
119 }
120
121 function CreateSubArray(elementSize, constructor) {
122   return function(begin, end) {
123     var srcLength = %TypedArrayGetLength(this);
124     var beginInt = TO_INTEGER(begin);
125     if (beginInt < 0) {
126       beginInt = MathMax(0, srcLength + beginInt);
127     } else {
128       beginInt = MathMin(srcLength, beginInt);
129     }
130
131     var endInt = IS_UNDEFINED(end) ? srcLength : TO_INTEGER(end);
132     if (endInt < 0) {
133       endInt = MathMax(0, srcLength + endInt);
134     } else {
135       endInt = MathMin(endInt, srcLength);
136     }
137     if (endInt < beginInt) {
138       endInt = beginInt;
139     }
140     var newLength = endInt - beginInt;
141     var beginByteOffset =
142         %TypedArrayGetByteOffset(this) + beginInt * elementSize;
143     return new constructor(%TypedArrayGetBuffer(this),
144                            beginByteOffset, newLength);
145   }
146 }
147
148 function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
149   if (offset > 0) {
150     for (var i = 0; i < sourceLength; i++) {
151       target[offset + i] = source[i];
152     }
153   }
154   else {
155     for (var i = 0; i < sourceLength; i++) {
156       target[i] = source[i];
157     }
158   }
159 }
160
161 function TypedArraySetFromOverlappingTypedArray(target, source, offset) {
162   var sourceElementSize = source.BYTES_PER_ELEMENT;
163   var targetElementSize = target.BYTES_PER_ELEMENT;
164   var sourceLength = source.length;
165
166   // Copy left part.
167   function CopyLeftPart() {
168     // First un-mutated byte after the next write
169     var targetPtr = target.byteOffset + (offset + 1) * targetElementSize;
170     // Next read at sourcePtr. We do not care for memory changing before
171     // sourcePtr - we have already copied it.
172     var sourcePtr = source.byteOffset;
173     for (var leftIndex = 0;
174          leftIndex < sourceLength && targetPtr <= sourcePtr;
175          leftIndex++) {
176       target[offset + leftIndex] = source[leftIndex];
177       targetPtr += targetElementSize;
178       sourcePtr += sourceElementSize;
179     }
180     return leftIndex;
181   }
182   var leftIndex = CopyLeftPart();
183
184   // Copy rigth part;
185   function CopyRightPart() {
186     // First unmutated byte before the next write
187     var targetPtr =
188       target.byteOffset + (offset + sourceLength - 1) * targetElementSize;
189     // Next read before sourcePtr. We do not care for memory changing after
190     // sourcePtr - we have already copied it.
191     var sourcePtr =
192       source.byteOffset + sourceLength * sourceElementSize;
193     for(var rightIndex = sourceLength - 1;
194         rightIndex >= leftIndex && targetPtr >= sourcePtr;
195         rightIndex--) {
196       target[offset + rightIndex] = source[rightIndex];
197       targetPtr -= targetElementSize;
198       sourcePtr -= sourceElementSize;
199     }
200     return rightIndex;
201   }
202   var rightIndex = CopyRightPart();
203
204   var temp = new $Array(rightIndex + 1 - leftIndex);
205   for (var i = leftIndex; i <= rightIndex; i++) {
206     temp[i - leftIndex] = source[i];
207   }
208   for (i = leftIndex; i <= rightIndex; i++) {
209     target[offset + i] = temp[i - leftIndex];
210   }
211 }
212
213 function TypedArraySet(obj, offset) {
214   var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
215   if (intOffset < 0) {
216     throw MakeTypeError("typed_array_set_negative_offset");
217   }
218   switch (%TypedArraySetFastCases(this, obj, intOffset)) {
219     // These numbers should be synchronized with runtime.cc.
220     case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE
221       return;
222     case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
223       TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
224       return;
225     case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
226       TypedArraySetFromArrayLike(this, obj, obj.length, intOffset);
227       return;
228     case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
229       var l = obj.length;
230       if (IS_UNDEFINED(l)) {
231         if (IS_NUMBER(obj)) {
232             // For number as a first argument, throw TypeError
233             // instead of silently ignoring the call, so that
234             // the user knows (s)he did something wrong.
235             // (Consistent with Firefox and Blink/WebKit)
236             throw MakeTypeError("invalid_argument");
237         }
238         return;
239       }
240       if (intOffset + l > this.length) {
241         throw MakeRangeError("typed_array_set_source_too_large");
242       }
243       TypedArraySetFromArrayLike(this, obj, l, intOffset);
244       return;
245   }
246 }
247
248 // -------------------------------------------------------------------
249
250 function SetupTypedArray(arrayId, name, constructor, elementSize) {
251   %CheckIsBootstrapping();
252   var fun = CreateTypedArrayConstructor(name, elementSize,
253                                         arrayId, constructor);
254   %SetCode(constructor, fun);
255   %FunctionSetPrototype(constructor, new $Object());
256
257   %SetProperty(constructor, "BYTES_PER_ELEMENT", elementSize,
258                READ_ONLY | DONT_ENUM | DONT_DELETE);
259   %SetProperty(constructor.prototype,
260                "constructor", constructor, DONT_ENUM);
261   %SetProperty(constructor.prototype,
262                "BYTES_PER_ELEMENT", elementSize,
263                READ_ONLY | DONT_ENUM | DONT_DELETE);
264   InstallGetter(constructor.prototype, "buffer", TypedArrayGetBuffer);
265   InstallGetter(constructor.prototype, "byteOffset", TypedArrayGetByteOffset);
266   InstallGetter(constructor.prototype, "byteLength", TypedArrayGetByteLength);
267   InstallGetter(constructor.prototype, "length", TypedArrayGetLength);
268
269   InstallFunctions(constructor.prototype, DONT_ENUM, $Array(
270         "subarray", CreateSubArray(elementSize, constructor),
271         "set", TypedArraySet
272   ));
273 }
274
275 // arrayIds below should be synchronized with Runtime_TypedArrayInitialize.
276 SetupTypedArray(1, "Uint8Array", global.Uint8Array, 1);
277 SetupTypedArray(2, "Int8Array", global.Int8Array, 1);
278 SetupTypedArray(3, "Uint16Array", global.Uint16Array, 2);
279 SetupTypedArray(4, "Int16Array", global.Int16Array, 2);
280 SetupTypedArray(5, "Uint32Array", global.Uint32Array, 4);
281 SetupTypedArray(6, "Int32Array", global.Int32Array, 4);
282 SetupTypedArray(7, "Float32Array", global.Float32Array, 4);
283 SetupTypedArray(8, "Float64Array", global.Float64Array, 8);
284 SetupTypedArray(9, "Uint8ClampedArray", global.Uint8ClampedArray, 1);
285
286
287 // --------------------------- DataView -----------------------------
288
289 var $DataView = global.DataView;
290
291 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
292   if (%_IsConstructCall()) {
293     if (!IS_ARRAYBUFFER(buffer)) {
294       throw MakeTypeError('data_view_not_array_buffer', []);
295     }
296     var bufferByteLength = %ArrayBufferGetByteLength(buffer);
297     var offset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
298     if (offset > bufferByteLength) {
299       throw MakeRangeError('invalid_data_view_offset');
300     }
301     var length = IS_UNDEFINED(byteLength) ?
302       bufferByteLength - offset : TO_INTEGER(byteLength);
303     if (length < 0 || offset + length > bufferByteLength) {
304       throw new MakeRangeError('invalid_data_view_length');
305     }
306     %DataViewInitialize(this, buffer, offset, length);
307   } else {
308     throw MakeTypeError('constructor_not_function', ["DataView"]);
309   }
310 }
311
312 function DataViewGetBuffer() {
313   if (!IS_DATAVIEW(this)) {
314     throw MakeTypeError('incompatible_method_receiver',
315                         ['DataView.buffer', this]);
316   }
317   return %DataViewGetBuffer(this);
318 }
319
320 function DataViewGetByteOffset() {
321   if (!IS_DATAVIEW(this)) {
322     throw MakeTypeError('incompatible_method_receiver',
323                         ['DataView.byteOffset', this]);
324   }
325   return %DataViewGetByteOffset(this);
326 }
327
328 function DataViewGetByteLength() {
329   if (!IS_DATAVIEW(this)) {
330     throw MakeTypeError('incompatible_method_receiver',
331                         ['DataView.byteLength', this]);
332   }
333   return %DataViewGetByteLength(this);
334 }
335
336 function ToPositiveDataViewOffset(offset) {
337   return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
338 }
339
340 function DataViewGetInt8(offset, little_endian) {
341   if (!IS_DATAVIEW(this)) {
342     throw MakeTypeError('incompatible_method_receiver',
343                         ['DataView.getInt8', this]);
344   }
345   if (%_ArgumentsLength() < 1) {
346     throw MakeTypeError('invalid_argument');
347   }
348   return %DataViewGetInt8(this,
349                           ToPositiveDataViewOffset(offset),
350                           !!little_endian);
351 }
352
353 function DataViewSetInt8(offset, value, little_endian) {
354   if (!IS_DATAVIEW(this)) {
355     throw MakeTypeError('incompatible_method_receiver',
356                         ['DataView.setInt8', this]);
357   }
358   if (%_ArgumentsLength() < 2) {
359     throw MakeTypeError('invalid_argument');
360   }
361   %DataViewSetInt8(this,
362                    ToPositiveDataViewOffset(offset),
363                    TO_NUMBER_INLINE(value),
364                    !!little_endian);
365 }
366
367 function DataViewGetUint8(offset, little_endian) {
368   if (!IS_DATAVIEW(this)) {
369     throw MakeTypeError('incompatible_method_receiver',
370                         ['DataView.getUint8', this]);
371   }
372   if (%_ArgumentsLength() < 1) {
373     throw MakeTypeError('invalid_argument');
374   }
375   return %DataViewGetUint8(this,
376                            ToPositiveDataViewOffset(offset),
377                            !!little_endian);
378 }
379
380 function DataViewSetUint8(offset, value, little_endian) {
381   if (!IS_DATAVIEW(this)) {
382     throw MakeTypeError('incompatible_method_receiver',
383                         ['DataView.setUint8', this]);
384   }
385   if (%_ArgumentsLength() < 2) {
386     throw MakeTypeError('invalid_argument');
387   }
388   %DataViewSetUint8(this,
389                    ToPositiveDataViewOffset(offset),
390                    TO_NUMBER_INLINE(value),
391                    !!little_endian);
392 }
393
394 function DataViewGetInt16(offset, little_endian) {
395   if (!IS_DATAVIEW(this)) {
396     throw MakeTypeError('incompatible_method_receiver',
397                         ['DataView.getInt16', this]);
398   }
399   if (%_ArgumentsLength() < 1) {
400     throw MakeTypeError('invalid_argument');
401   }
402   return %DataViewGetInt16(this,
403                            ToPositiveDataViewOffset(offset),
404                            !!little_endian);
405 }
406
407 function DataViewSetInt16(offset, value, little_endian) {
408   if (!IS_DATAVIEW(this)) {
409     throw MakeTypeError('incompatible_method_receiver',
410                         ['DataView.setInt16', this]);
411   }
412   if (%_ArgumentsLength() < 2) {
413     throw MakeTypeError('invalid_argument');
414   }
415   %DataViewSetInt16(this,
416                     ToPositiveDataViewOffset(offset),
417                     TO_NUMBER_INLINE(value),
418                     !!little_endian);
419 }
420
421 function DataViewGetUint16(offset, little_endian) {
422   if (!IS_DATAVIEW(this)) {
423     throw MakeTypeError('incompatible_method_receiver',
424                         ['DataView.getUint16', this]);
425   }
426   if (%_ArgumentsLength() < 1) {
427     throw MakeTypeError('invalid_argument');
428   }
429   return %DataViewGetUint16(this,
430                             ToPositiveDataViewOffset(offset),
431                             !!little_endian);
432 }
433
434 function DataViewSetUint16(offset, value, little_endian) {
435   if (!IS_DATAVIEW(this)) {
436     throw MakeTypeError('incompatible_method_receiver',
437                         ['DataView.setUint16', this]);
438   }
439   if (%_ArgumentsLength() < 2) {
440     throw MakeTypeError('invalid_argument');
441   }
442   %DataViewSetUint16(this,
443                      ToPositiveDataViewOffset(offset),
444                      TO_NUMBER_INLINE(value),
445                      !!little_endian);
446 }
447
448 function DataViewGetInt32(offset, little_endian) {
449   if (!IS_DATAVIEW(this)) {
450     throw MakeTypeError('incompatible_method_receiver',
451                         ['DataView.getInt32', this]);
452   }
453   if (%_ArgumentsLength() < 1) {
454     throw MakeTypeError('invalid_argument');
455   }
456   return %DataViewGetInt32(this,
457                            ToPositiveDataViewOffset(offset),
458                            !!little_endian);
459 }
460
461 function DataViewSetInt32(offset, value, little_endian) {
462   if (!IS_DATAVIEW(this)) {
463     throw MakeTypeError('incompatible_method_receiver',
464                         ['DataView.setInt32', this]);
465   }
466   if (%_ArgumentsLength() < 2) {
467     throw MakeTypeError('invalid_argument');
468   }
469   %DataViewSetInt32(this,
470                     ToPositiveDataViewOffset(offset),
471                     TO_NUMBER_INLINE(value),
472                     !!little_endian);
473 }
474
475 function DataViewGetUint32(offset, little_endian) {
476   if (!IS_DATAVIEW(this)) {
477     throw MakeTypeError('incompatible_method_receiver',
478                         ['DataView.getUint32', this]);
479   }
480   if (%_ArgumentsLength() < 1) {
481     throw MakeTypeError('invalid_argument');
482   }
483   return %DataViewGetUint32(this,
484                             ToPositiveDataViewOffset(offset),
485                             !!little_endian);
486 }
487
488 function DataViewSetUint32(offset, value, little_endian) {
489   if (!IS_DATAVIEW(this)) {
490     throw MakeTypeError('incompatible_method_receiver',
491                         ['DataView.setUint32', this]);
492   }
493   if (%_ArgumentsLength() < 2) {
494     throw MakeTypeError('invalid_argument');
495   }
496   %DataViewSetUint32(this,
497                      ToPositiveDataViewOffset(offset),
498                      TO_NUMBER_INLINE(value),
499                      !!little_endian);
500 }
501
502 function DataViewGetFloat32(offset, little_endian) {
503   if (!IS_DATAVIEW(this)) {
504     throw MakeTypeError('incompatible_method_receiver',
505                         ['DataView.getFloat32', this]);
506   }
507   if (%_ArgumentsLength() < 1) {
508     throw MakeTypeError('invalid_argument');
509   }
510   return %DataViewGetFloat32(this,
511                              ToPositiveDataViewOffset(offset),
512                              !!little_endian);
513 }
514
515 function DataViewSetFloat32(offset, value, little_endian) {
516   if (!IS_DATAVIEW(this)) {
517     throw MakeTypeError('incompatible_method_receiver',
518                         ['DataView.setFloat32', this]);
519   }
520   if (%_ArgumentsLength() < 2) {
521     throw MakeTypeError('invalid_argument');
522   }
523   %DataViewSetFloat32(this,
524                       ToPositiveDataViewOffset(offset),
525                       TO_NUMBER_INLINE(value),
526                       !!little_endian);
527 }
528
529 function DataViewGetFloat64(offset, little_endian) {
530   if (!IS_DATAVIEW(this)) {
531     throw MakeTypeError('incompatible_method_receiver',
532                         ['DataView.getFloat64', this]);
533   }
534   if (%_ArgumentsLength() < 1) {
535     throw MakeTypeError('invalid_argument');
536   }
537   return %DataViewGetFloat64(this,
538                              ToPositiveDataViewOffset(offset),
539                              !!little_endian);
540 }
541
542 function DataViewSetFloat64(offset, value, little_endian) {
543   if (!IS_DATAVIEW(this)) {
544     throw MakeTypeError('incompatible_method_receiver',
545                         ['DataView.setFloat64', this]);
546   }
547   if (%_ArgumentsLength() < 2) {
548     throw MakeTypeError('invalid_argument');
549   }
550   %DataViewSetFloat64(this,
551                       ToPositiveDataViewOffset(offset),
552                       TO_NUMBER_INLINE(value),
553                       !!little_endian);
554 }
555
556 function SetupDataView() {
557   %CheckIsBootstrapping();
558
559   // Setup the DataView constructor.
560   %SetCode($DataView, DataViewConstructor);
561   %FunctionSetPrototype($DataView, new $Object);
562
563   // Set up constructor property on the DataView prototype.
564   %SetProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
565
566   InstallGetter($DataView.prototype, "buffer", DataViewGetBuffer);
567   InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
568   InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
569
570   InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
571       "getInt8", DataViewGetInt8,
572       "setInt8", DataViewSetInt8,
573
574       "getUint8", DataViewGetUint8,
575       "setUint8", DataViewSetUint8,
576
577       "getInt16", DataViewGetInt16,
578       "setInt16", DataViewSetInt16,
579
580       "getUint16", DataViewGetUint16,
581       "setUint16", DataViewSetUint16,
582
583       "getInt32", DataViewGetInt32,
584       "setInt32", DataViewSetInt32,
585
586       "getUint32", DataViewGetUint32,
587       "setUint32", DataViewSetUint32,
588
589       "getFloat32", DataViewGetFloat32,
590       "setFloat32", DataViewSetFloat32,
591
592       "getFloat64", DataViewGetFloat64,
593       "setFloat64", DataViewSetFloat64
594   ));
595 }
596
597 SetupDataView();