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 = $max(0, srcLength + beginInt);
167 beginInt = $min(srcLength, beginInt);
170 var endInt = IS_UNDEFINED(end) ? srcLength : end;
172 endInt = $max(0, srcLength + endInt);
174 endInt = $min(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 function TypedArrayGetToStringTag() {
295 if (!%IsTypedArray(this)) return;
296 var name = %_ClassOf(this);
297 if (IS_UNDEFINED(name)) return;
301 // -------------------------------------------------------------------
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());
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,
328 TYPED_ARRAYS(SETUP_TYPED_ARRAY)
333 // --------------------------- DataView -----------------------------
335 var $DataView = global.DataView;
337 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
338 if (%_IsConstructCall()) {
339 if (!IS_ARRAYBUFFER(buffer)) {
340 throw MakeTypeError('data_view_not_array_buffer', []);
342 if (!IS_UNDEFINED(byteOffset)) {
343 byteOffset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
345 if (!IS_UNDEFINED(byteLength)) {
346 byteLength = TO_INTEGER(byteLength);
349 var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
351 var offset = IS_UNDEFINED(byteOffset) ? 0 : byteOffset;
352 if (offset > bufferByteLength) {
353 throw MakeRangeError('invalid_data_view_offset');
356 var length = IS_UNDEFINED(byteLength)
357 ? bufferByteLength - offset
359 if (length < 0 || offset + length > bufferByteLength) {
360 throw new MakeRangeError('invalid_data_view_length');
362 %_DataViewInitialize(this, buffer, offset, length);
364 throw MakeTypeError('constructor_not_function', ["DataView"]);
368 function DataViewGetBufferJS() {
369 if (!IS_DATAVIEW(this)) {
370 throw MakeTypeError('incompatible_method_receiver',
371 ['DataView.buffer', this]);
373 return %DataViewGetBuffer(this);
376 function DataViewGetByteOffset() {
377 if (!IS_DATAVIEW(this)) {
378 throw MakeTypeError('incompatible_method_receiver',
379 ['DataView.byteOffset', this]);
381 return %_ArrayBufferViewGetByteOffset(this);
384 function DataViewGetByteLength() {
385 if (!IS_DATAVIEW(this)) {
386 throw MakeTypeError('incompatible_method_receiver',
387 ['DataView.byteLength', this]);
389 return %_ArrayBufferViewGetByteLength(this);
392 macro DATA_VIEW_TYPES(FUNCTION)
403 function ToPositiveDataViewOffset(offset) {
404 return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
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]);
414 if (%_ArgumentsLength() < 1) {
415 throw MakeTypeError('invalid_argument');
417 return %DataViewGetTYPENAME(this,
418 ToPositiveDataViewOffset(offset),
422 function DataViewSetTYPENAMEJS(offset, value, little_endian) {
423 if (!IS_DATAVIEW(this)) {
424 throw MakeTypeError('incompatible_method_receiver',
425 ['DataView.setTYPENAME', this]);
427 if (%_ArgumentsLength() < 2) {
428 throw MakeTypeError('invalid_argument');
430 %DataViewSetTYPENAME(this,
431 ToPositiveDataViewOffset(offset),
432 TO_NUMBER_INLINE(value),
437 DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER)
439 function SetupDataView() {
440 %CheckIsBootstrapping();
442 // Setup the DataView constructor.
443 %SetCode($DataView, DataViewConstructor);
444 %FunctionSetPrototype($DataView, new $Object);
446 // Set up constructor property on the DataView prototype.
447 %AddNamedProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
449 $DataView.prototype, symbolToStringTag, "DataView", READ_ONLY|DONT_ENUM);
451 InstallGetter($DataView.prototype, "buffer", DataViewGetBufferJS);
452 InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
453 InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
455 InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
456 "getInt8", DataViewGetInt8JS,
457 "setInt8", DataViewSetInt8JS,
459 "getUint8", DataViewGetUint8JS,
460 "setUint8", DataViewSetUint8JS,
462 "getInt16", DataViewGetInt16JS,
463 "setInt16", DataViewSetInt16JS,
465 "getUint16", DataViewGetUint16JS,
466 "setUint16", DataViewSetUint16JS,
468 "getInt32", DataViewGetInt32JS,
469 "setInt32", DataViewSetInt32JS,
471 "getUint32", DataViewGetUint32JS,
472 "setUint32", DataViewSetUint32JS,
474 "getFloat32", DataViewGetFloat32JS,
475 "setFloat32", DataViewSetFloat32JS,
477 "getFloat64", DataViewGetFloat64JS,
478 "setFloat64", DataViewSetFloat64JS