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