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.
7 // This file relies on the fact that the following declaration has been made
9 // var $Array = global.Array;
10 var $ArrayBuffer = global.ArrayBuffer;
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)
27 macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
28 function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
29 if (!IS_UNDEFINED(byteOffset)) {
31 ToPositiveInteger(byteOffset, "invalid_typed_array_length");
33 if (!IS_UNDEFINED(length)) {
34 length = ToPositiveInteger(length, "invalid_typed_array_length");
37 var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
39 if (IS_UNDEFINED(byteOffset)) {
44 if (offset % ELEMENT_SIZE !== 0) {
45 throw MakeRangeError("invalid_typed_array_alignment",
46 ["start offset", "NAME", ELEMENT_SIZE]);
48 if (offset > bufferByteLength) {
49 throw MakeRangeError("invalid_typed_array_offset");
55 if (IS_UNDEFINED(length)) {
56 if (bufferByteLength % ELEMENT_SIZE !== 0) {
57 throw MakeRangeError("invalid_typed_array_alignment",
58 ["byte length", "NAME", ELEMENT_SIZE]);
60 newByteLength = bufferByteLength - offset;
61 newLength = newByteLength / ELEMENT_SIZE;
63 var newLength = length;
64 newByteLength = newLength * ELEMENT_SIZE;
66 if ((offset + newByteLength > bufferByteLength)
67 || (newLength > %_MaxSmi())) {
68 throw MakeRangeError("invalid_typed_array_length");
70 %_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength);
73 function NAMEConstructByLength(obj, length) {
74 var l = IS_UNDEFINED(length) ?
75 0 : ToPositiveInteger(length, "invalid_typed_array_length");
77 throw MakeRangeError("invalid_typed_array_length");
79 var byteLength = l * ELEMENT_SIZE;
80 if (byteLength > %_TypedArrayMaxSizeInHeap()) {
81 var buffer = new $ArrayBuffer(byteLength);
82 %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength);
84 %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength);
88 function NAMEConstructByArrayLike(obj, arrayLike) {
89 var length = arrayLike.length;
90 var l = ToPositiveInteger(length, "invalid_typed_array_length");
93 throw MakeRangeError("invalid_typed_array_length");
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];
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);
112 NAMEConstructByArrayLike(this, arg1);
115 throw MakeTypeError("constructor_not_function", ["NAME"])
119 function NAME_GetBuffer() {
120 if (!(%_ClassOf(this) === 'NAME')) {
121 throw MakeTypeError('incompatible_method_receiver',
122 ["NAME.buffer", this]);
124 return %TypedArrayGetBuffer(this);
127 function NAME_GetByteLength() {
128 if (!(%_ClassOf(this) === 'NAME')) {
129 throw MakeTypeError('incompatible_method_receiver',
130 ["NAME.byteLength", this]);
132 return %_ArrayBufferViewGetByteLength(this);
135 function NAME_GetByteOffset() {
136 if (!(%_ClassOf(this) === 'NAME')) {
137 throw MakeTypeError('incompatible_method_receiver',
138 ["NAME.byteOffset", this]);
140 return %_ArrayBufferViewGetByteOffset(this);
143 function NAME_GetLength() {
144 if (!(%_ClassOf(this) === 'NAME')) {
145 throw MakeTypeError('incompatible_method_receiver',
146 ["NAME.length", this]);
148 return %_TypedArrayGetLength(this);
151 var $NAME = global.NAME;
153 function NAMESubArray(begin, end) {
154 if (!(%_ClassOf(this) === 'NAME')) {
155 throw MakeTypeError('incompatible_method_receiver',
156 ["NAME.subarray", this]);
158 var beginInt = TO_INTEGER(begin);
159 if (!IS_UNDEFINED(end)) {
160 end = TO_INTEGER(end);
163 var srcLength = %_TypedArrayGetLength(this);
165 beginInt = MathMax(0, srcLength + beginInt);
167 beginInt = MathMin(srcLength, beginInt);
170 var endInt = IS_UNDEFINED(end) ? srcLength : end;
172 endInt = MathMax(0, srcLength + endInt);
174 endInt = MathMin(endInt, srcLength);
176 if (endInt < beginInt) {
179 var newLength = endInt - beginInt;
180 var beginByteOffset =
181 %_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE;
182 return new $NAME(%TypedArrayGetBuffer(this),
183 beginByteOffset, newLength);
187 TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
190 function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
192 for (var i = 0; i < sourceLength; i++) {
193 target[offset + i] = source[i];
197 for (var i = 0; i < sourceLength; i++) {
198 target[i] = source[i];
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;
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;
218 target[offset + leftIndex] = source[leftIndex];
219 targetPtr += targetElementSize;
220 sourcePtr += sourceElementSize;
224 var leftIndex = CopyLeftPart();
227 function CopyRightPart() {
228 // First unmutated byte before the next write
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.
234 source.byteOffset + sourceLength * sourceElementSize;
235 for(var rightIndex = sourceLength - 1;
236 rightIndex >= leftIndex && targetPtr >= sourcePtr;
238 target[offset + rightIndex] = source[rightIndex];
239 targetPtr -= targetElementSize;
240 sourcePtr -= sourceElementSize;
244 var rightIndex = CopyRightPart();
246 var temp = new $Array(rightIndex + 1 - leftIndex);
247 for (var i = leftIndex; i <= rightIndex; i++) {
248 temp[i - leftIndex] = source[i];
250 for (i = leftIndex; i <= rightIndex; i++) {
251 target[offset + i] = temp[i - leftIndex];
255 function TypedArraySet(obj, offset) {
256 var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
258 throw MakeTypeError("typed_array_set_negative_offset");
261 if (intOffset > %_MaxSmi()) {
262 throw MakeRangeError("typed_array_set_source_too_large");
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
268 case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
269 TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
271 case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
272 TypedArraySetFromArrayLike(this, obj, obj.length, intOffset);
274 case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
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");
286 if (intOffset + l > this.length) {
287 throw MakeRangeError("typed_array_set_source_too_large");
289 TypedArraySetFromArrayLike(this, obj, l, intOffset);
294 // -------------------------------------------------------------------
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());
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);
314 InstallFunctions(global.NAME.prototype, DONT_ENUM, $Array(
315 "subarray", NAMESubArray,
320 TYPED_ARRAYS(SETUP_TYPED_ARRAY)
325 // --------------------------- DataView -----------------------------
327 var $DataView = global.DataView;
329 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
330 if (%_IsConstructCall()) {
331 if (!IS_ARRAYBUFFER(buffer)) {
332 throw MakeTypeError('data_view_not_array_buffer', []);
334 if (!IS_UNDEFINED(byteOffset)) {
335 byteOffset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
337 if (!IS_UNDEFINED(byteLength)) {
338 byteLength = TO_INTEGER(byteLength);
341 var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
343 var offset = IS_UNDEFINED(byteOffset) ? 0 : byteOffset;
344 if (offset > bufferByteLength) {
345 throw MakeRangeError('invalid_data_view_offset');
348 var length = IS_UNDEFINED(byteLength)
349 ? bufferByteLength - offset
351 if (length < 0 || offset + length > bufferByteLength) {
352 throw new MakeRangeError('invalid_data_view_length');
354 %_DataViewInitialize(this, buffer, offset, length);
356 throw MakeTypeError('constructor_not_function', ["DataView"]);
360 function DataViewGetBuffer() {
361 if (!IS_DATAVIEW(this)) {
362 throw MakeTypeError('incompatible_method_receiver',
363 ['DataView.buffer', this]);
365 return %DataViewGetBuffer(this);
368 function DataViewGetByteOffset() {
369 if (!IS_DATAVIEW(this)) {
370 throw MakeTypeError('incompatible_method_receiver',
371 ['DataView.byteOffset', this]);
373 return %_ArrayBufferViewGetByteOffset(this);
376 function DataViewGetByteLength() {
377 if (!IS_DATAVIEW(this)) {
378 throw MakeTypeError('incompatible_method_receiver',
379 ['DataView.byteLength', this]);
381 return %_ArrayBufferViewGetByteLength(this);
384 macro DATA_VIEW_TYPES(FUNCTION)
395 function ToPositiveDataViewOffset(offset) {
396 return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
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]);
406 if (%_ArgumentsLength() < 1) {
407 throw MakeTypeError('invalid_argument');
409 return %DataViewGetTYPENAME(this,
410 ToPositiveDataViewOffset(offset),
414 function DataViewSetTYPENAME(offset, value, little_endian) {
415 if (!IS_DATAVIEW(this)) {
416 throw MakeTypeError('incompatible_method_receiver',
417 ['DataView.setTYPENAME', this]);
419 if (%_ArgumentsLength() < 2) {
420 throw MakeTypeError('invalid_argument');
422 %DataViewSetTYPENAME(this,
423 ToPositiveDataViewOffset(offset),
424 TO_NUMBER_INLINE(value),
429 DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER)
431 function SetupDataView() {
432 %CheckIsBootstrapping();
434 // Setup the DataView constructor.
435 %SetCode($DataView, DataViewConstructor);
436 %FunctionSetPrototype($DataView, new $Object);
438 // Set up constructor property on the DataView prototype.
439 %SetProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
441 InstallGetter($DataView.prototype, "buffer", DataViewGetBuffer);
442 InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
443 InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
445 InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
446 "getInt8", DataViewGetInt8,
447 "setInt8", DataViewSetInt8,
449 "getUint8", DataViewGetUint8,
450 "setUint8", DataViewSetUint8,
452 "getInt16", DataViewGetInt16,
453 "setInt16", DataViewSetInt16,
455 "getUint16", DataViewGetUint16,
456 "setUint16", DataViewSetUint16,
458 "getInt32", DataViewGetInt32,
459 "setInt32", DataViewSetInt32,
461 "getUint32", DataViewGetUint32,
462 "setUint32", DataViewSetUint32,
464 "getFloat32", DataViewGetFloat32,
465 "setFloat32", DataViewSetFloat32,
467 "getFloat64", DataViewGetFloat64,
468 "setFloat64", DataViewSetFloat64