blob: c149b35b98e8926094ae7054b977bc29aed2ee10 [file] [log] [blame]
Ben Murdochb8a8cc12014-11-26 15:28:44 +00001// 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;
10var $ArrayBuffer = global.ArrayBuffer;
11
12
13// --------------- Typed Arrays ---------------------
14macro TYPED_ARRAYS(FUNCTION)
15// arrayIds below should be synchronized with Runtime_TypedArrayInitialize.
16FUNCTION(1, Uint8Array, 1)
17FUNCTION(2, Int8Array, 1)
18FUNCTION(3, Uint16Array, 2)
19FUNCTION(4, Int16Array, 2)
20FUNCTION(5, Uint32Array, 4)
21FUNCTION(6, Int32Array, 4)
22FUNCTION(7, Float32Array, 4)
23FUNCTION(8, Float64Array, 8)
24FUNCTION(9, Uint8ClampedArray, 1)
25endmacro
26
27macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
28function 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
73function 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
88function 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
104function 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
119function NAME_GetBuffer() {
120 if (!(%_ClassOf(this) === 'NAME')) {
121 throw MakeTypeError('incompatible_method_receiver',
122 ["NAME.buffer", this]);
123 }
124 return %TypedArrayGetBuffer(this);
125}
126
127function NAME_GetByteLength() {
128 if (!(%_ClassOf(this) === 'NAME')) {
129 throw MakeTypeError('incompatible_method_receiver',
130 ["NAME.byteLength", this]);
131 }
132 return %_ArrayBufferViewGetByteLength(this);
133}
134
135function NAME_GetByteOffset() {
136 if (!(%_ClassOf(this) === 'NAME')) {
137 throw MakeTypeError('incompatible_method_receiver',
138 ["NAME.byteOffset", this]);
139 }
140 return %_ArrayBufferViewGetByteOffset(this);
141}
142
143function NAME_GetLength() {
144 if (!(%_ClassOf(this) === 'NAME')) {
145 throw MakeTypeError('incompatible_method_receiver',
146 ["NAME.length", this]);
147 }
148 return %_TypedArrayGetLength(this);
149}
150
151var $NAME = global.NAME;
152
153function 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 = MathMax(0, srcLength + beginInt);
166 } else {
167 beginInt = MathMin(srcLength, beginInt);
168 }
169
170 var endInt = IS_UNDEFINED(end) ? srcLength : end;
171 if (endInt < 0) {
172 endInt = MathMax(0, srcLength + endInt);
173 } else {
174 endInt = MathMin(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}
185endmacro
186
187TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR)
188
189
190function 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
203function 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
255function 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// -------------------------------------------------------------------
295
296function SetupTypedArrays() {
297macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE)
298 %CheckIsBootstrapping();
299 %SetCode(global.NAME, NAMEConstructor);
300 %FunctionSetPrototype(global.NAME, new $Object());
301
302 %AddNamedProperty(global.NAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE,
303 READ_ONLY | DONT_ENUM | DONT_DELETE);
304 %AddNamedProperty(global.NAME.prototype,
305 "constructor", global.NAME, DONT_ENUM);
306 %AddNamedProperty(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);
313
314 InstallFunctions(global.NAME.prototype, DONT_ENUM, $Array(
315 "subarray", NAMESubArray,
316 "set", TypedArraySet
317 ));
318endmacro
319
320TYPED_ARRAYS(SETUP_TYPED_ARRAY)
321}
322
323SetupTypedArrays();
324
325// --------------------------- DataView -----------------------------
326
327var $DataView = global.DataView;
328
329function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
330 if (%_IsConstructCall()) {
331 if (!IS_ARRAYBUFFER(buffer)) {
332 throw MakeTypeError('data_view_not_array_buffer', []);
333 }
334 if (!IS_UNDEFINED(byteOffset)) {
335 byteOffset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
336 }
337 if (!IS_UNDEFINED(byteLength)) {
338 byteLength = TO_INTEGER(byteLength);
339 }
340
341 var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
342
343 var offset = IS_UNDEFINED(byteOffset) ? 0 : byteOffset;
344 if (offset > bufferByteLength) {
345 throw MakeRangeError('invalid_data_view_offset');
346 }
347
348 var length = IS_UNDEFINED(byteLength)
349 ? bufferByteLength - offset
350 : byteLength;
351 if (length < 0 || offset + length > bufferByteLength) {
352 throw new MakeRangeError('invalid_data_view_length');
353 }
354 %_DataViewInitialize(this, buffer, offset, length);
355 } else {
356 throw MakeTypeError('constructor_not_function', ["DataView"]);
357 }
358}
359
360function DataViewGetBufferJS() {
361 if (!IS_DATAVIEW(this)) {
362 throw MakeTypeError('incompatible_method_receiver',
363 ['DataView.buffer', this]);
364 }
365 return %DataViewGetBuffer(this);
366}
367
368function DataViewGetByteOffset() {
369 if (!IS_DATAVIEW(this)) {
370 throw MakeTypeError('incompatible_method_receiver',
371 ['DataView.byteOffset', this]);
372 }
373 return %_ArrayBufferViewGetByteOffset(this);
374}
375
376function DataViewGetByteLength() {
377 if (!IS_DATAVIEW(this)) {
378 throw MakeTypeError('incompatible_method_receiver',
379 ['DataView.byteLength', this]);
380 }
381 return %_ArrayBufferViewGetByteLength(this);
382}
383
384macro DATA_VIEW_TYPES(FUNCTION)
385 FUNCTION(Int8)
386 FUNCTION(Uint8)
387 FUNCTION(Int16)
388 FUNCTION(Uint16)
389 FUNCTION(Int32)
390 FUNCTION(Uint32)
391 FUNCTION(Float32)
392 FUNCTION(Float64)
393endmacro
394
395function ToPositiveDataViewOffset(offset) {
396 return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset');
397}
398
399
400macro DATA_VIEW_GETTER_SETTER(TYPENAME)
401function DataViewGetTYPENAMEJS(offset, little_endian) {
402 if (!IS_DATAVIEW(this)) {
403 throw MakeTypeError('incompatible_method_receiver',
404 ['DataView.getTYPENAME', this]);
405 }
406 if (%_ArgumentsLength() < 1) {
407 throw MakeTypeError('invalid_argument');
408 }
409 return %DataViewGetTYPENAME(this,
410 ToPositiveDataViewOffset(offset),
411 !!little_endian);
412}
413
414function DataViewSetTYPENAMEJS(offset, value, little_endian) {
415 if (!IS_DATAVIEW(this)) {
416 throw MakeTypeError('incompatible_method_receiver',
417 ['DataView.setTYPENAME', this]);
418 }
419 if (%_ArgumentsLength() < 2) {
420 throw MakeTypeError('invalid_argument');
421 }
422 %DataViewSetTYPENAME(this,
423 ToPositiveDataViewOffset(offset),
424 TO_NUMBER_INLINE(value),
425 !!little_endian);
426}
427endmacro
428
429DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER)
430
431function SetupDataView() {
432 %CheckIsBootstrapping();
433
434 // Setup the DataView constructor.
435 %SetCode($DataView, DataViewConstructor);
436 %FunctionSetPrototype($DataView, new $Object);
437
438 // Set up constructor property on the DataView prototype.
439 %AddNamedProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM);
440
441 InstallGetter($DataView.prototype, "buffer", DataViewGetBufferJS);
442 InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset);
443 InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength);
444
445 InstallFunctions($DataView.prototype, DONT_ENUM, $Array(
446 "getInt8", DataViewGetInt8JS,
447 "setInt8", DataViewSetInt8JS,
448
449 "getUint8", DataViewGetUint8JS,
450 "setUint8", DataViewSetUint8JS,
451
452 "getInt16", DataViewGetInt16JS,
453 "setInt16", DataViewSetInt16JS,
454
455 "getUint16", DataViewGetUint16JS,
456 "setUint16", DataViewSetUint16JS,
457
458 "getInt32", DataViewGetInt32JS,
459 "setInt32", DataViewSetInt32JS,
460
461 "getUint32", DataViewGetUint32JS,
462 "setUint32", DataViewSetUint32JS,
463
464 "getFloat32", DataViewGetFloat32JS,
465 "setFloat32", DataViewSetFloat32JS,
466
467 "getFloat64", DataViewGetFloat64JS,
468 "setFloat64", DataViewSetFloat64JS
469 ));
470}
471
472SetupDataView();