Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / v8 / src / typedarray.js
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 "use strict";
6
7 // This file relies on the fact that the following declaration has been made
8 // in runtime.js:
9 // var $Array = global.Array;
10 var $ArrayBuffer = global.ArrayBuffer;
11
12
13 // --------------- Typed Arrays ---------------------
14 macro TYPED_ARRAYS(FUNCTION)
15 // arrayIds below should be synchronized with Runtime_TypedArrayInitialize.
16 FUNCTION(1, Uint8Array, 1)
17 FUNCTION(2, Int8Array, 1)
18 FUNCTION(3, Uint16Array, 2)
19 FUNCTION(4, Int16Array, 2)
20 FUNCTION(5, Uint32Array, 4)
21 FUNCTION(6, Int32Array, 4)
22 FUNCTION(7, Float32Array, 4)
23 FUNCTION(8, Float64Array, 8)
24 FUNCTION(9, Uint8ClampedArray, 1)
25 endmacro
26
27 macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
28   function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
29     if (!IS_UNDEFINED(byteOffset)) {
30         byteOffset =
31             ToPositiveInteger(byteOffset,  "invalid_typed_array_length");
32     }
33     if (!IS_UNDEFINED(length)) {
34         length = ToPositiveInteger(length, "invalid_typed_array_length");
35     }
36
37     var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
38     var offset;
39     if (IS_UNDEFINED(byteOffset)) {
40       offset = 0;
41     } else {
42       offset = byteOffset;
43
44       if (offset % ELEMENT_SIZE !== 0) {
45         throw MakeRangeError("invalid_typed_array_alignment",
46             ["start offset", "NAME", ELEMENT_SIZE]);
47       }
48       if (offset > bufferByteLength) {
49         throw MakeRangeError("invalid_typed_array_offset");
50       }
51     }
52
53     var newByteLength;
54     var newLength;
55     if (IS_UNDEFINED(length)) {
56       if (bufferByteLength % ELEMENT_SIZE !== 0) {
57         throw MakeRangeError("invalid_typed_array_alignment",
58           ["byte length", "NAME", ELEMENT_SIZE]);
59       }
60       newByteLength = bufferByteLength - offset;
61       newLength = newByteLength / ELEMENT_SIZE;
62     } else {
63       var newLength = length;
64       newByteLength = newLength * ELEMENT_SIZE;
65     }
66     if ((offset + newByteLength > bufferByteLength)
67         || (newLength > %_MaxSmi())) {
68       throw MakeRangeError("invalid_typed_array_length");
69     }
70     %_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength);
71   }
72
73   function NAMEConstructByLength(obj, length) {
74     var l = IS_UNDEFINED(length) ?
75       0 : ToPositiveInteger(length, "invalid_typed_array_length");
76     if (l > %_MaxSmi()) {
77       throw MakeRangeError("invalid_typed_array_length");
78     }
79     var byteLength = l * ELEMENT_SIZE;
80     if (byteLength > %_TypedArrayMaxSizeInHeap()) {
81       var buffer = new $ArrayBuffer(byteLength);
82       %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength);
83     } else {
84       %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength);
85     }
86   }
87
88   function NAMEConstructByArrayLike(obj, arrayLike) {
89     var length = arrayLike.length;
90     var l = ToPositiveInteger(length, "invalid_typed_array_length");
91
92     if (l > %_MaxSmi()) {
93       throw MakeRangeError("invalid_typed_array_length");
94     }
95     if(!%TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l)) {
96       for (var i = 0; i < l; i++) {
97         // It is crucial that we let any execptions from arrayLike[i]
98         // propagate outside the function.
99         obj[i] = arrayLike[i];
100       }
101     }
102   }
103
104   function NAMEConstructor(arg1, arg2, arg3) {
105     if (%_IsConstructCall()) {
106       if (IS_ARRAYBUFFER(arg1)) {
107         NAMEConstructByArrayBuffer(this, arg1, arg2, arg3);
108       } else if (IS_NUMBER(arg1) || IS_STRING(arg1) ||
109                  IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) {
110         NAMEConstructByLength(this, arg1);
111       } else {
112         NAMEConstructByArrayLike(this, arg1);
113       }
114     } else {
115       throw MakeTypeError("constructor_not_function", ["NAME"])
116     }
117   }
118
119   function NAME_GetBuffer() {
120     if (!(%_ClassOf(this) === 'NAME')) {
121       throw MakeTypeError('incompatible_method_receiver',
122                           ["NAME.buffer", this]);
123     }
124     return %TypedArrayGetBuffer(this);
125   }
126
127   function NAME_GetByteLength() {
128     if (!(%_ClassOf(this) === 'NAME')) {
129       throw MakeTypeError('incompatible_method_receiver',
130                           ["NAME.byteLength", this]);
131     }
132     return %_ArrayBufferViewGetByteLength(this);
133   }
134
135   function NAME_GetByteOffset() {
136     if (!(%_ClassOf(this) === 'NAME')) {
137       throw MakeTypeError('incompatible_method_receiver',
138                           ["NAME.byteOffset", this]);
139     }
140     return %_ArrayBufferViewGetByteOffset(this);
141   }
142
143   function NAME_GetLength() {
144     if (!(%_ClassOf(this) === 'NAME')) {
145       throw MakeTypeError('incompatible_method_receiver',
146                           ["NAME.length", this]);
147     }
148     return %_TypedArrayGetLength(this);
149   }
150
151   var $NAME = global.NAME;
152
153   function NAMESubArray(begin, end) {
154     if (!(%_ClassOf(this) === 'NAME')) {
155       throw MakeTypeError('incompatible_method_receiver',
156                           ["NAME.subarray", this]);
157     }
158     var beginInt = TO_INTEGER(begin);
159     if (!IS_UNDEFINED(end)) {
160       end = TO_INTEGER(end);
161     }
162
163     var srcLength = %_TypedArrayGetLength(this);
164     if (beginInt < 0) {
165       beginInt = MathMax(0, srcLength + beginInt);
166     } else {
167       beginInt = MathMin(srcLength, beginInt);
168     }
169
170     var endInt = IS_UNDEFINED(end) ? srcLength : end;
171     if (endInt < 0) {
172       endInt = MathMax(0, srcLength + endInt);
173     } else {
174       endInt = MathMin(endInt, srcLength);
175     }
176     if (endInt < beginInt) {
177       endInt = beginInt;
178     }
179     var newLength = endInt - beginInt;
180     var beginByteOffset =
181         %_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
182     return new $NAME(%TypedArrayGetBuffer(this),
183                      beginByteOffset, newLength);
184   }
185 endmacro
186
187 TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
188
189
190 function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
191   if (offset > 0) {
192     for (var i = 0; i < sourceLength; i++) {
193       target[offset + i] = source[i];
194     }
195   }
196   else {
197     for (var i = 0; i < sourceLength; i++) {
198       target[i] = source[i];
199     }
200   }
201 }
202
203 function TypedArraySetFromOverlappingTypedArray(target, source, offset) {
204   var sourceElementSize = source.BYTES_PER_ELEMENT;
205   var targetElementSize = target.BYTES_PER_ELEMENT;
206   var sourceLength = source.length;
207
208   // Copy left part.
209   function CopyLeftPart() {
210     // First un-mutated byte after the next write
211     var targetPtr = target.byteOffset + (offset + 1) * targetElementSize;
212     // Next read at sourcePtr. We do not care for memory changing before
213     // sourcePtr - we have already copied it.
214     var sourcePtr = source.byteOffset;
215     for (var leftIndex = 0;
216          leftIndex < sourceLength && targetPtr <= sourcePtr;
217          leftIndex++) {
218       target[offset + leftIndex] = source[leftIndex];
219       targetPtr += targetElementSize;
220       sourcePtr += sourceElementSize;
221     }
222     return leftIndex;
223   }
224   var leftIndex = CopyLeftPart();
225
226   // Copy rigth part;
227   function CopyRightPart() {
228     // First unmutated byte before the next write
229     var targetPtr =
230       target.byteOffset + (offset + sourceLength - 1) * targetElementSize;
231     // Next read before sourcePtr. We do not care for memory changing after
232     // sourcePtr - we have already copied it.
233     var sourcePtr =
234       source.byteOffset + sourceLength * sourceElementSize;
235     for(var rightIndex = sourceLength - 1;
236         rightIndex >= leftIndex && targetPtr >= sourcePtr;
237         rightIndex--) {
238       target[offset + rightIndex] = source[rightIndex];
239       targetPtr -= targetElementSize;
240       sourcePtr -= sourceElementSize;
241     }
242     return rightIndex;
243   }
244   var rightIndex = CopyRightPart();
245
246   var temp = new $Array(rightIndex + 1 - leftIndex);
247   for (var i = leftIndex; i <= rightIndex; i++) {
248     temp[i - leftIndex] = source[i];
249   }
250   for (i = leftIndex; i <= rightIndex; i++) {
251     target[offset + i] = temp[i - leftIndex];
252   }
253 }
254
255 function TypedArraySet(obj, offset) {
256   var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
257   if (intOffset < 0) {
258     throw MakeTypeError("typed_array_set_negative_offset");
259   }
260
261   if (intOffset > %_MaxSmi()) {
262     throw MakeRangeError("typed_array_set_source_too_large");
263   }
264   switch (%TypedArraySetFastCases(this, obj, intOffset)) {
265     // These numbers should be synchronized with runtime.cc.
266     case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE
267       return;
268     case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
269       TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
270       return;
271     case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
272       TypedArraySetFromArrayLike(this, obj, obj.length, intOffset);
273       return;
274     case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
275       var l = obj.length;
276       if (IS_UNDEFINED(l)) {
277         if (IS_NUMBER(obj)) {
278             // For number as a first argument, throw TypeError
279             // instead of silently ignoring the call, so that
280             // the user knows (s)he did something wrong.
281             // (Consistent with Firefox and Blink/WebKit)
282             throw MakeTypeError("invalid_argument");
283         }
284         return;
285       }
286       if (intOffset + l > this.length) {
287         throw MakeRangeError("typed_array_set_source_too_large");
288       }
289       TypedArraySetFromArrayLike(this, obj, l, intOffset);
290       return;
291   }
292 }
293
294 // -------------------------------------------------------------------
295
296 function SetupTypedArrays() {
297 macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
298   %CheckIsBootstrapping();
299   %SetCode(global.NAME, NAMEConstructor);
300   %FunctionSetPrototype(global.NAME, new $Object());
301
302   %SetProperty(global.NAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
303                READ_ONLY | DONT_ENUM | DONT_DELETE);
304   %SetProperty(global.NAME.prototype,
305                "constructor", global.NAME, DONT_ENUM);
306   %SetProperty(global.NAME.prototype,
307                "BYTES_PER_ELEMENT", ELEMENT_SIZE,
308                READ_ONLY | DONT_ENUM | DONT_DELETE);
309   InstallGetter(global.NAME.prototype, "buffer", NAME_GetBuffer);
310   InstallGetter(global.NAME.prototype, "byteOffset", NAME_GetByteOffset);
311   InstallGetter(global.NAME.prototype, "byteLength", NAME_GetByteLength);
312   InstallGetter(global.NAME.prototype, "length", NAME_GetLength);
313
314   InstallFunctions(global.NAME.prototype, DONT_ENUM, $Array(
315         "subarray", NAMESubArray,
316         "set", TypedArraySet
317   ));
318 endmacro
319
320 TYPED_ARRAYS(SETUP_TYPED_ARRAY)
321 }
322
323 SetupTypedArrays();
324
325 // --------------------------- DataView -----------------------------
326
327 var $DataView = global.DataView;
328
329 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
330   if (%_IsConstructCall()) {
331     if (!IS_ARRAYBUFFER(buffer)) {
332       throw MakeTypeError('data_view_not_array_buffer', []);
333     }
334     if (!IS_UNDEFINED(byteOffset)) {
335         byteOffset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
336     }
337     if (!IS_UNDEFINED(byteLength)) {
338         byteLength = TO_INTEGER(byteLength);
339     }
340
341     var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
342
343     var offset = IS_UNDEFINED(byteOffset) ?  0 : byteOffset;
344     if (offset > bufferByteLength) {
345       throw MakeRangeError('invalid_data_view_offset');
346     }
347
348     var length = IS_UNDEFINED(byteLength)
349         ? bufferByteLength - offset
350         : byteLength;
351     if (length < 0 || offset + length > bufferByteLength) {
352       throw new MakeRangeError('invalid_data_view_length');
353     }
354     %_DataViewInitialize(this, buffer, offset, length);
355   } else {
356     throw MakeTypeError('constructor_not_function', ["DataView"]);
357   }
358 }
359
360 function DataViewGetBuffer() {
361   if (!IS_DATAVIEW(this)) {
362     throw MakeTypeError('incompatible_method_receiver',
363                         ['DataView.buffer', this]);
364   }
365   return %DataViewGetBuffer(this);
366 }
367
368 function DataViewGetByteOffset() {
369   if (!IS_DATAVIEW(this)) {
370     throw MakeTypeError('incompatible_method_receiver',
371                         ['DataView.byteOffset', this]);
372   }
373   return %_ArrayBufferViewGetByteOffset(this);
374 }
375
376 function DataViewGetByteLength() {
377   if (!IS_DATAVIEW(this)) {
378     throw MakeTypeError('incompatible_method_receiver',
379                         ['DataView.byteLength', this]);
380   }
381   return %_ArrayBufferViewGetByteLength(this);
382 }
383
384 macro DATA_VIEW_TYPES(FUNCTION)
385   FUNCTION(Int8)
386   FUNCTION(Uint8)
387   FUNCTION(Int16)
388   FUNCTION(Uint16)
389   FUNCTION(Int32)
390   FUNCTION(Uint32)
391   FUNCTION(Float32)
392   FUNCTION(Float64)
393 endmacro
394
395 function ToPositiveDataViewOffset(offset) {
396   return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
397 }
398
399
400 macro DATA_VIEW_GETTER_SETTER(TYPENAME)
401 function DataViewGetTYPENAME(offset, little_endian) {
402   if (!IS_DATAVIEW(this)) {
403     throw MakeTypeError('incompatible_method_receiver',
404                         ['DataView.getTYPENAME', this]);
405   }
406   if (%_ArgumentsLength() < 1) {
407     throw MakeTypeError('invalid_argument');
408   }
409   return %DataViewGetTYPENAME(this,
410                           ToPositiveDataViewOffset(offset),
411                           !!little_endian);
412 }
413
414 function DataViewSetTYPENAME(offset, value, little_endian) {
415   if (!IS_DATAVIEW(this)) {
416     throw MakeTypeError('incompatible_method_receiver',
417                         ['DataView.setTYPENAME', this]);
418   }
419   if (%_ArgumentsLength() < 2) {
420     throw MakeTypeError('invalid_argument');
421   }
422   %DataViewSetTYPENAME(this,
423                    ToPositiveDataViewOffset(offset),
424                    TO_NUMBER_INLINE(value),
425                    !!little_endian);
426 }
427 endmacro
428
429 DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER)
430
431 function SetupDataView() {
432   %CheckIsBootstrapping();
433
434   // Setup the DataView constructor.
435   %SetCode($DataView, DataViewConstructor);
436   %FunctionSetPrototype($DataView, new $Object);
437
438   // Set up constructor property on the DataView prototype.
439   %SetProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
440
441   InstallGetter($DataView.prototype, "buffer", DataViewGetBuffer);
442   InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
443   InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
444
445   InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
446       "getInt8", DataViewGetInt8,
447       "setInt8", DataViewSetInt8,
448
449       "getUint8", DataViewGetUint8,
450       "setUint8", DataViewSetUint8,
451
452       "getInt16", DataViewGetInt16,
453       "setInt16", DataViewSetInt16,
454
455       "getUint16", DataViewGetUint16,
456       "setUint16", DataViewSetUint16,
457
458       "getInt32", DataViewGetInt32,
459       "setInt32", DataViewSetInt32,
460
461       "getUint32", DataViewGetUint32,
462       "setUint32", DataViewSetUint32,
463
464       "getFloat32", DataViewGetFloat32,
465       "setFloat32", DataViewSetFloat32,
466
467       "getFloat64", DataViewGetFloat64,
468       "setFloat64", DataViewSetFloat64
469   ));
470 }
471
472 SetupDataView();