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
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.
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.
30 // This file relies on the fact that the following declaration has been made
32 // var $Array = global.Array;
33 var $ArrayBuffer = global.ArrayBuffer;
36 // --------------- Typed Arrays ---------------------
38 function CreateTypedArrayConstructor(name, elementSize, arrayId, constructor) {
39 function ConstructByArrayBuffer(obj, buffer, byteOffset, length) {
40 var offset = ToPositiveInteger(byteOffset, "invalid_typed_array_length")
42 if (offset % elementSize !== 0) {
43 throw MakeRangeError("invalid_typed_array_alignment",
44 "start offset", name, elementSize);
46 var bufferByteLength = %ArrayBufferGetByteLength(buffer);
47 if (offset > bufferByteLength) {
48 throw MakeRangeError("invalid_typed_array_offset");
53 if (IS_UNDEFINED(length)) {
54 if (bufferByteLength % elementSize !== 0) {
55 throw MakeRangeError("invalid_typed_array_alignment",
56 "byte length", name, elementSize);
58 newByteLength = bufferByteLength - offset;
59 newLength = newByteLength / elementSize;
61 var newLength = ToPositiveInteger(length, "invalid_typed_array_length");
62 newByteLength = newLength * elementSize;
64 if (offset + newByteLength > bufferByteLength) {
65 throw MakeRangeError("invalid_typed_array_length");
67 %TypedArrayInitialize(obj, arrayId, buffer, offset, newByteLength);
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);
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];
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);
97 ConstructByArrayLike(this, arg1);
100 throw MakeTypeError("constructor_not_function", [name])
105 function TypedArrayGetBuffer() {
106 return %TypedArrayGetBuffer(this);
109 function TypedArrayGetByteLength() {
110 return %TypedArrayGetByteLength(this);
113 function TypedArrayGetByteOffset() {
114 return %TypedArrayGetByteOffset(this);
117 function TypedArrayGetLength() {
118 return %TypedArrayGetLength(this);
121 function CreateSubArray(elementSize, constructor) {
122 return function(begin, end) {
123 var srcLength = %TypedArrayGetLength(this);
124 var beginInt = TO_INTEGER(begin);
126 beginInt = MathMax(0, srcLength + beginInt);
128 beginInt = MathMin(srcLength, beginInt);
131 var endInt = IS_UNDEFINED(end) ? srcLength : TO_INTEGER(end);
133 endInt = MathMax(0, srcLength + endInt);
135 endInt = MathMin(endInt, srcLength);
137 if (endInt < beginInt) {
140 var newLength = endInt - beginInt;
141 var beginByteOffset =
142 %TypedArrayGetByteOffset(this) + beginInt * elementSize;
143 return new constructor(%TypedArrayGetBuffer(this),
144 beginByteOffset, newLength);
148 function TypedArraySetFromArrayLike(target, source, sourceLength, offset) {
150 for (var i = 0; i < sourceLength; i++) {
151 target[offset + i] = source[i];
155 for (var i = 0; i < sourceLength; i++) {
156 target[i] = source[i];
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;
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;
176 target[offset + leftIndex] = source[leftIndex];
177 targetPtr += targetElementSize;
178 sourcePtr += sourceElementSize;
182 var leftIndex = CopyLeftPart();
185 function CopyRightPart() {
186 // First unmutated byte before the next write
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.
192 source.byteOffset + sourceLength * sourceElementSize;
193 for(var rightIndex = sourceLength - 1;
194 rightIndex >= leftIndex && targetPtr >= sourcePtr;
196 target[offset + rightIndex] = source[rightIndex];
197 targetPtr -= targetElementSize;
198 sourcePtr -= sourceElementSize;
202 var rightIndex = CopyRightPart();
204 var temp = new $Array(rightIndex + 1 - leftIndex);
205 for (var i = leftIndex; i <= rightIndex; i++) {
206 temp[i - leftIndex] = source[i];
208 for (i = leftIndex; i <= rightIndex; i++) {
209 target[offset + i] = temp[i - leftIndex];
213 function TypedArraySet(obj, offset) {
214 var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset);
216 throw MakeTypeError("typed_array_set_negative_offset");
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
222 case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING
223 TypedArraySetFromOverlappingTypedArray(this, obj, intOffset);
225 case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING
226 TypedArraySetFromArrayLike(this, obj, obj.length, intOffset);
228 case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY
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");
240 if (intOffset + l > this.length) {
241 throw MakeRangeError("typed_array_set_source_too_large");
243 TypedArraySetFromArrayLike(this, obj, l, intOffset);
248 // -------------------------------------------------------------------
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());
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);
269 InstallFunctions(constructor.prototype, DONT_ENUM, $Array(
270 "subarray", CreateSubArray(elementSize, constructor),
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);
287 // --------------------------- DataView -----------------------------
289 var $DataView = global.DataView;
291 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
292 if (%_IsConstructCall()) {
293 if (!IS_ARRAYBUFFER(buffer)) {
294 throw MakeTypeError('data_view_not_array_buffer', []);
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');
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');
306 %DataViewInitialize(this, buffer, offset, length);
308 throw MakeTypeError('constructor_not_function', ["DataView"]);
312 function DataViewGetBuffer() {
313 if (!IS_DATAVIEW(this)) {
314 throw MakeTypeError('incompatible_method_receiver',
315 ['DataView.buffer', this]);
317 return %DataViewGetBuffer(this);
320 function DataViewGetByteOffset() {
321 if (!IS_DATAVIEW(this)) {
322 throw MakeTypeError('incompatible_method_receiver',
323 ['DataView.byteOffset', this]);
325 return %DataViewGetByteOffset(this);
328 function DataViewGetByteLength() {
329 if (!IS_DATAVIEW(this)) {
330 throw MakeTypeError('incompatible_method_receiver',
331 ['DataView.byteLength', this]);
333 return %DataViewGetByteLength(this);
336 function ToPositiveDataViewOffset(offset) {
337 return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
340 function DataViewGetInt8(offset, little_endian) {
341 if (!IS_DATAVIEW(this)) {
342 throw MakeTypeError('incompatible_method_receiver',
343 ['DataView.getInt8', this]);
345 if (%_ArgumentsLength() < 1) {
346 throw MakeTypeError('invalid_argument');
348 return %DataViewGetInt8(this,
349 ToPositiveDataViewOffset(offset),
353 function DataViewSetInt8(offset, value, little_endian) {
354 if (!IS_DATAVIEW(this)) {
355 throw MakeTypeError('incompatible_method_receiver',
356 ['DataView.setInt8', this]);
358 if (%_ArgumentsLength() < 2) {
359 throw MakeTypeError('invalid_argument');
361 %DataViewSetInt8(this,
362 ToPositiveDataViewOffset(offset),
363 TO_NUMBER_INLINE(value),
367 function DataViewGetUint8(offset, little_endian) {
368 if (!IS_DATAVIEW(this)) {
369 throw MakeTypeError('incompatible_method_receiver',
370 ['DataView.getUint8', this]);
372 if (%_ArgumentsLength() < 1) {
373 throw MakeTypeError('invalid_argument');
375 return %DataViewGetUint8(this,
376 ToPositiveDataViewOffset(offset),
380 function DataViewSetUint8(offset, value, little_endian) {
381 if (!IS_DATAVIEW(this)) {
382 throw MakeTypeError('incompatible_method_receiver',
383 ['DataView.setUint8', this]);
385 if (%_ArgumentsLength() < 2) {
386 throw MakeTypeError('invalid_argument');
388 %DataViewSetUint8(this,
389 ToPositiveDataViewOffset(offset),
390 TO_NUMBER_INLINE(value),
394 function DataViewGetInt16(offset, little_endian) {
395 if (!IS_DATAVIEW(this)) {
396 throw MakeTypeError('incompatible_method_receiver',
397 ['DataView.getInt16', this]);
399 if (%_ArgumentsLength() < 1) {
400 throw MakeTypeError('invalid_argument');
402 return %DataViewGetInt16(this,
403 ToPositiveDataViewOffset(offset),
407 function DataViewSetInt16(offset, value, little_endian) {
408 if (!IS_DATAVIEW(this)) {
409 throw MakeTypeError('incompatible_method_receiver',
410 ['DataView.setInt16', this]);
412 if (%_ArgumentsLength() < 2) {
413 throw MakeTypeError('invalid_argument');
415 %DataViewSetInt16(this,
416 ToPositiveDataViewOffset(offset),
417 TO_NUMBER_INLINE(value),
421 function DataViewGetUint16(offset, little_endian) {
422 if (!IS_DATAVIEW(this)) {
423 throw MakeTypeError('incompatible_method_receiver',
424 ['DataView.getUint16', this]);
426 if (%_ArgumentsLength() < 1) {
427 throw MakeTypeError('invalid_argument');
429 return %DataViewGetUint16(this,
430 ToPositiveDataViewOffset(offset),
434 function DataViewSetUint16(offset, value, little_endian) {
435 if (!IS_DATAVIEW(this)) {
436 throw MakeTypeError('incompatible_method_receiver',
437 ['DataView.setUint16', this]);
439 if (%_ArgumentsLength() < 2) {
440 throw MakeTypeError('invalid_argument');
442 %DataViewSetUint16(this,
443 ToPositiveDataViewOffset(offset),
444 TO_NUMBER_INLINE(value),
448 function DataViewGetInt32(offset, little_endian) {
449 if (!IS_DATAVIEW(this)) {
450 throw MakeTypeError('incompatible_method_receiver',
451 ['DataView.getInt32', this]);
453 if (%_ArgumentsLength() < 1) {
454 throw MakeTypeError('invalid_argument');
456 return %DataViewGetInt32(this,
457 ToPositiveDataViewOffset(offset),
461 function DataViewSetInt32(offset, value, little_endian) {
462 if (!IS_DATAVIEW(this)) {
463 throw MakeTypeError('incompatible_method_receiver',
464 ['DataView.setInt32', this]);
466 if (%_ArgumentsLength() < 2) {
467 throw MakeTypeError('invalid_argument');
469 %DataViewSetInt32(this,
470 ToPositiveDataViewOffset(offset),
471 TO_NUMBER_INLINE(value),
475 function DataViewGetUint32(offset, little_endian) {
476 if (!IS_DATAVIEW(this)) {
477 throw MakeTypeError('incompatible_method_receiver',
478 ['DataView.getUint32', this]);
480 if (%_ArgumentsLength() < 1) {
481 throw MakeTypeError('invalid_argument');
483 return %DataViewGetUint32(this,
484 ToPositiveDataViewOffset(offset),
488 function DataViewSetUint32(offset, value, little_endian) {
489 if (!IS_DATAVIEW(this)) {
490 throw MakeTypeError('incompatible_method_receiver',
491 ['DataView.setUint32', this]);
493 if (%_ArgumentsLength() < 2) {
494 throw MakeTypeError('invalid_argument');
496 %DataViewSetUint32(this,
497 ToPositiveDataViewOffset(offset),
498 TO_NUMBER_INLINE(value),
502 function DataViewGetFloat32(offset, little_endian) {
503 if (!IS_DATAVIEW(this)) {
504 throw MakeTypeError('incompatible_method_receiver',
505 ['DataView.getFloat32', this]);
507 if (%_ArgumentsLength() < 1) {
508 throw MakeTypeError('invalid_argument');
510 return %DataViewGetFloat32(this,
511 ToPositiveDataViewOffset(offset),
515 function DataViewSetFloat32(offset, value, little_endian) {
516 if (!IS_DATAVIEW(this)) {
517 throw MakeTypeError('incompatible_method_receiver',
518 ['DataView.setFloat32', this]);
520 if (%_ArgumentsLength() < 2) {
521 throw MakeTypeError('invalid_argument');
523 %DataViewSetFloat32(this,
524 ToPositiveDataViewOffset(offset),
525 TO_NUMBER_INLINE(value),
529 function DataViewGetFloat64(offset, little_endian) {
530 if (!IS_DATAVIEW(this)) {
531 throw MakeTypeError('incompatible_method_receiver',
532 ['DataView.getFloat64', this]);
534 if (%_ArgumentsLength() < 1) {
535 throw MakeTypeError('invalid_argument');
537 return %DataViewGetFloat64(this,
538 ToPositiveDataViewOffset(offset),
542 function DataViewSetFloat64(offset, value, little_endian) {
543 if (!IS_DATAVIEW(this)) {
544 throw MakeTypeError('incompatible_method_receiver',
545 ['DataView.setFloat64', this]);
547 if (%_ArgumentsLength() < 2) {
548 throw MakeTypeError('invalid_argument');
550 %DataViewSetFloat64(this,
551 ToPositiveDataViewOffset(offset),
552 TO_NUMBER_INLINE(value),
556 function SetupDataView() {
557 %CheckIsBootstrapping();
559 // Setup the DataView constructor.
560 %SetCode($DataView, DataViewConstructor);
561 %FunctionSetPrototype($DataView, new $Object);
563 // Set up constructor property on the DataView prototype.
564 %SetProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
566 InstallGetter($DataView.prototype, "buffer", DataViewGetBuffer);
567 InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
568 InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
570 InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
571 "getInt8", DataViewGetInt8,
572 "setInt8", DataViewSetInt8,
574 "getUint8", DataViewGetUint8,
575 "setUint8", DataViewSetUint8,
577 "getInt16", DataViewGetInt16,
578 "setInt16", DataViewSetInt16,
580 "getUint16", DataViewGetUint16,
581 "setUint16", DataViewSetUint16,
583 "getInt32", DataViewGetInt32,
584 "setInt32", DataViewSetInt32,
586 "getUint32", DataViewGetUint32,
587 "setUint32", DataViewSetUint32,
589 "getFloat32", DataViewGetFloat32,
590 "setFloat32", DataViewSetFloat32,
592 "getFloat64", DataViewGetFloat64,
593 "setFloat64", DataViewSetFloat64