deps: update v8 to 4.3.61.21
[platform/upstream/nodejs.git] / deps / 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 = $max(0, srcLength + beginInt);
166   } else {
167     beginInt = $min(srcLength, beginInt);
168   }
169
170   var endInt = IS_UNDEFINED(end) ? srcLength : end;
171   if (endInt < 0) {
172     endInt = $max(0, srcLength + endInt);
173   } else {
174     endInt = $min(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 function TypedArrayGetToStringTag() {
295   if (!%IsTypedArray(this)) return;
296   var name = %_ClassOf(this);
297   if (IS_UNDEFINED(name)) return;
298   return name;
299 }
300
301 // -------------------------------------------------------------------
302
303 function SetupTypedArrays() {
304 macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
305   %CheckIsBootstrapping();
306   %SetCode(global.NAME, NAMEConstructor);
307   %FunctionSetPrototype(global.NAME, new $Object());
308
309   %AddNamedProperty(global.NAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
310                     READ_ONLY | DONT_ENUM | DONT_DELETE);
311   %AddNamedProperty(global.NAME.prototype,
312                     "constructor", global.NAME, DONT_ENUM);
313   %AddNamedProperty(global.NAME.prototype,
314                     "BYTES_PER_ELEMENT", ELEMENT_SIZE,
315                     READ_ONLY | DONT_ENUM | DONT_DELETE);
316   InstallGetter(global.NAME.prototype, "buffer", NAME_GetBuffer);
317   InstallGetter(global.NAME.prototype, "byteOffset", NAME_GetByteOffset);
318   InstallGetter(global.NAME.prototype, "byteLength", NAME_GetByteLength);
319   InstallGetter(global.NAME.prototype, "length", NAME_GetLength);
320   InstallGetter(global.NAME.prototype, symbolToStringTag,
321                 TypedArrayGetToStringTag);
322   InstallFunctions(global.NAME.prototype, DONT_ENUM, $Array(
323         "subarray", NAMESubArray,
324         "set", TypedArraySet
325   ));
326 endmacro
327
328 TYPED_ARRAYS(SETUP_TYPED_ARRAY)
329 }
330
331 SetupTypedArrays();
332
333 // --------------------------- DataView -----------------------------
334
335 var $DataView = global.DataView;
336
337 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
338   if (%_IsConstructCall()) {
339     if (!IS_ARRAYBUFFER(buffer)) {
340       throw MakeTypeError('data_view_not_array_buffer', []);
341     }
342     if (!IS_UNDEFINED(byteOffset)) {
343         byteOffset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
344     }
345     if (!IS_UNDEFINED(byteLength)) {
346         byteLength = TO_INTEGER(byteLength);
347     }
348
349     var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
350
351     var offset = IS_UNDEFINED(byteOffset) ?  0 : byteOffset;
352     if (offset > bufferByteLength) {
353       throw MakeRangeError('invalid_data_view_offset');
354     }
355
356     var length = IS_UNDEFINED(byteLength)
357         ? bufferByteLength - offset
358         : byteLength;
359     if (length < 0 || offset + length > bufferByteLength) {
360       throw new MakeRangeError('invalid_data_view_length');
361     }
362     %_DataViewInitialize(this, buffer, offset, length);
363   } else {
364     throw MakeTypeError('constructor_not_function', ["DataView"]);
365   }
366 }
367
368 function DataViewGetBufferJS() {
369   if (!IS_DATAVIEW(this)) {
370     throw MakeTypeError('incompatible_method_receiver',
371                         ['DataView.buffer', this]);
372   }
373   return %DataViewGetBuffer(this);
374 }
375
376 function DataViewGetByteOffset() {
377   if (!IS_DATAVIEW(this)) {
378     throw MakeTypeError('incompatible_method_receiver',
379                         ['DataView.byteOffset', this]);
380   }
381   return %_ArrayBufferViewGetByteOffset(this);
382 }
383
384 function DataViewGetByteLength() {
385   if (!IS_DATAVIEW(this)) {
386     throw MakeTypeError('incompatible_method_receiver',
387                         ['DataView.byteLength', this]);
388   }
389   return %_ArrayBufferViewGetByteLength(this);
390 }
391
392 macro DATA_VIEW_TYPES(FUNCTION)
393   FUNCTION(Int8)
394   FUNCTION(Uint8)
395   FUNCTION(Int16)
396   FUNCTION(Uint16)
397   FUNCTION(Int32)
398   FUNCTION(Uint32)
399   FUNCTION(Float32)
400   FUNCTION(Float64)
401 endmacro
402
403 function ToPositiveDataViewOffset(offset) {
404   return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
405 }
406
407
408 macro DATA_VIEW_GETTER_SETTER(TYPENAME)
409 function DataViewGetTYPENAMEJS(offset, little_endian) {
410   if (!IS_DATAVIEW(this)) {
411     throw MakeTypeError('incompatible_method_receiver',
412                         ['DataView.getTYPENAME', this]);
413   }
414   if (%_ArgumentsLength() < 1) {
415     throw MakeTypeError('invalid_argument');
416   }
417   return %DataViewGetTYPENAME(this,
418                           ToPositiveDataViewOffset(offset),
419                           !!little_endian);
420 }
421
422 function DataViewSetTYPENAMEJS(offset, value, little_endian) {
423   if (!IS_DATAVIEW(this)) {
424     throw MakeTypeError('incompatible_method_receiver',
425                         ['DataView.setTYPENAME', this]);
426   }
427   if (%_ArgumentsLength() < 2) {
428     throw MakeTypeError('invalid_argument');
429   }
430   %DataViewSetTYPENAME(this,
431                    ToPositiveDataViewOffset(offset),
432                    TO_NUMBER_INLINE(value),
433                    !!little_endian);
434 }
435 endmacro
436
437 DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER)
438
439 function SetupDataView() {
440   %CheckIsBootstrapping();
441
442   // Setup the DataView constructor.
443   %SetCode($DataView, DataViewConstructor);
444   %FunctionSetPrototype($DataView, new $Object);
445
446   // Set up constructor property on the DataView prototype.
447   %AddNamedProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
448   %AddNamedProperty(
449       $DataView.prototype, symbolToStringTag, "DataView", READ_ONLY|DONT_ENUM);
450
451   InstallGetter($DataView.prototype, "buffer", DataViewGetBufferJS);
452   InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
453   InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
454
455   InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
456       "getInt8", DataViewGetInt8JS,
457       "setInt8", DataViewSetInt8JS,
458
459       "getUint8", DataViewGetUint8JS,
460       "setUint8", DataViewSetUint8JS,
461
462       "getInt16", DataViewGetInt16JS,
463       "setInt16", DataViewSetInt16JS,
464
465       "getUint16", DataViewGetUint16JS,
466       "setUint16", DataViewSetUint16JS,
467
468       "getInt32", DataViewGetInt32JS,
469       "setInt32", DataViewSetInt32JS,
470
471       "getUint32", DataViewGetUint32JS,
472       "setUint32", DataViewSetUint32JS,
473
474       "getFloat32", DataViewGetFloat32JS,
475       "setFloat32", DataViewSetFloat32JS,
476
477       "getFloat64", DataViewGetFloat64JS,
478       "setFloat64", DataViewSetFloat64JS
479   ));
480 }
481
482 SetupDataView();