OLD | NEW |
| (Empty) |
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 (function(global, utils) { | |
6 | |
7 "use strict"; | |
8 | |
9 %CheckIsBootstrapping(); | |
10 | |
11 // ------------------------------------------------------------------- | |
12 // Imports | |
13 | |
14 var GlobalArray = global.Array; | |
15 var GlobalArrayBuffer = global.ArrayBuffer; | |
16 var GlobalDataView = global.DataView; | |
17 var GlobalObject = global.Object; | |
18 var iteratorSymbol = utils.ImportNow("iterator_symbol"); | |
19 var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol"); | |
20 | |
21 macro TYPED_ARRAYS(FUNCTION) | |
22 // arrayIds below should be synchronized with Runtime_TypedArrayInitialize. | |
23 FUNCTION(1, Uint8Array, 1) | |
24 FUNCTION(2, Int8Array, 1) | |
25 FUNCTION(3, Uint16Array, 2) | |
26 FUNCTION(4, Int16Array, 2) | |
27 FUNCTION(5, Uint32Array, 4) | |
28 FUNCTION(6, Int32Array, 4) | |
29 FUNCTION(7, Float32Array, 4) | |
30 FUNCTION(8, Float64Array, 8) | |
31 FUNCTION(9, Uint8ClampedArray, 1) | |
32 endmacro | |
33 | |
34 macro DECLARE_GLOBALS(INDEX, NAME, SIZE) | |
35 var GlobalNAME = global.NAME; | |
36 endmacro | |
37 | |
38 TYPED_ARRAYS(DECLARE_GLOBALS) | |
39 | |
40 var InternalArray = utils.InternalArray; | |
41 | |
42 // --------------- Typed Arrays --------------------- | |
43 | |
44 macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE) | |
45 function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) { | |
46 if (!IS_UNDEFINED(byteOffset)) { | |
47 byteOffset = | |
48 $toPositiveInteger(byteOffset, kInvalidTypedArrayLength); | |
49 } | |
50 if (!IS_UNDEFINED(length)) { | |
51 length = $toPositiveInteger(length, kInvalidTypedArrayLength); | |
52 } | |
53 | |
54 var bufferByteLength = %_ArrayBufferGetByteLength(buffer); | |
55 var offset; | |
56 if (IS_UNDEFINED(byteOffset)) { | |
57 offset = 0; | |
58 } else { | |
59 offset = byteOffset; | |
60 | |
61 if (offset % ELEMENT_SIZE !== 0) { | |
62 throw MakeRangeError(kInvalidTypedArrayAlignment, | |
63 "start offset", "NAME", ELEMENT_SIZE); | |
64 } | |
65 if (offset > bufferByteLength) { | |
66 throw MakeRangeError(kInvalidTypedArrayOffset); | |
67 } | |
68 } | |
69 | |
70 var newByteLength; | |
71 var newLength; | |
72 if (IS_UNDEFINED(length)) { | |
73 if (bufferByteLength % ELEMENT_SIZE !== 0) { | |
74 throw MakeRangeError(kInvalidTypedArrayAlignment, | |
75 "byte length", "NAME", ELEMENT_SIZE); | |
76 } | |
77 newByteLength = bufferByteLength - offset; | |
78 newLength = newByteLength / ELEMENT_SIZE; | |
79 } else { | |
80 var newLength = length; | |
81 newByteLength = newLength * ELEMENT_SIZE; | |
82 } | |
83 if ((offset + newByteLength > bufferByteLength) | |
84 || (newLength > %_MaxSmi())) { | |
85 throw MakeRangeError(kInvalidTypedArrayLength); | |
86 } | |
87 %_TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength, true); | |
88 } | |
89 | |
90 function NAMEConstructByLength(obj, length) { | |
91 var l = IS_UNDEFINED(length) ? | |
92 0 : $toPositiveInteger(length, kInvalidTypedArrayLength); | |
93 if (l > %_MaxSmi()) { | |
94 throw MakeRangeError(kInvalidTypedArrayLength); | |
95 } | |
96 var byteLength = l * ELEMENT_SIZE; | |
97 if (byteLength > %_TypedArrayMaxSizeInHeap()) { | |
98 var buffer = new GlobalArrayBuffer(byteLength); | |
99 %_TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength, true); | |
100 } else { | |
101 %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, true); | |
102 } | |
103 } | |
104 | |
105 function NAMEConstructByArrayLike(obj, arrayLike) { | |
106 var length = arrayLike.length; | |
107 var l = $toPositiveInteger(length, kInvalidTypedArrayLength); | |
108 | |
109 if (l > %_MaxSmi()) { | |
110 throw MakeRangeError(kInvalidTypedArrayLength); | |
111 } | |
112 var initialized = false; | |
113 var byteLength = l * ELEMENT_SIZE; | |
114 if (byteLength <= %_TypedArrayMaxSizeInHeap()) { | |
115 %_TypedArrayInitialize(obj, ARRAY_ID, null, 0, byteLength, false); | |
116 } else { | |
117 initialized = | |
118 %TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l); | |
119 } | |
120 if (!initialized) { | |
121 for (var i = 0; i < l; i++) { | |
122 // It is crucial that we let any execptions from arrayLike[i] | |
123 // propagate outside the function. | |
124 obj[i] = arrayLike[i]; | |
125 } | |
126 } | |
127 } | |
128 | |
129 function NAMEConstructByIterable(obj, iterable, iteratorFn) { | |
130 var list = new InternalArray(); | |
131 // Reading the Symbol.iterator property of iterable twice would be | |
132 // observable with getters, so instead, we call the function which | |
133 // was already looked up, and wrap it in another iterable. The | |
134 // __proto__ of the new iterable is set to null to avoid any chance | |
135 // of modifications to Object.prototype being observable here. | |
136 var iterator = %_Call(iteratorFn, iterable); | |
137 var newIterable = { | |
138 __proto__: null | |
139 }; | |
140 // TODO(littledan): Computed properties don't work yet in nosnap. | |
141 // Rephrase when they do. | |
142 newIterable[iteratorSymbol] = function() { return iterator; } | |
143 for (var value of newIterable) { | |
144 list.push(value); | |
145 } | |
146 NAMEConstructByArrayLike(obj, list); | |
147 } | |
148 | |
149 function NAMEConstructor(arg1, arg2, arg3) { | |
150 if (%_IsConstructCall()) { | |
151 if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) { | |
152 NAMEConstructByArrayBuffer(this, arg1, arg2, arg3); | |
153 } else if (IS_NUMBER(arg1) || IS_STRING(arg1) || | |
154 IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) { | |
155 NAMEConstructByLength(this, arg1); | |
156 } else { | |
157 var iteratorFn = arg1[iteratorSymbol]; | |
158 if (IS_UNDEFINED(iteratorFn) || iteratorFn === $arrayValues) { | |
159 NAMEConstructByArrayLike(this, arg1); | |
160 } else { | |
161 NAMEConstructByIterable(this, arg1, iteratorFn); | |
162 } | |
163 } | |
164 } else { | |
165 throw MakeTypeError(kConstructorNotFunction, "NAME") | |
166 } | |
167 } | |
168 | |
169 function NAME_GetBuffer() { | |
170 if (!(%_ClassOf(this) === 'NAME')) { | |
171 throw MakeTypeError(kIncompatibleMethodReceiver, "NAME.buffer", this); | |
172 } | |
173 return %TypedArrayGetBuffer(this); | |
174 } | |
175 | |
176 function NAME_GetByteLength() { | |
177 if (!(%_ClassOf(this) === 'NAME')) { | |
178 throw MakeTypeError(kIncompatibleMethodReceiver, "NAME.byteLength", this); | |
179 } | |
180 return %_ArrayBufferViewGetByteLength(this); | |
181 } | |
182 | |
183 function NAME_GetByteOffset() { | |
184 if (!(%_ClassOf(this) === 'NAME')) { | |
185 throw MakeTypeError(kIncompatibleMethodReceiver, "NAME.byteOffset", this); | |
186 } | |
187 return %_ArrayBufferViewGetByteOffset(this); | |
188 } | |
189 | |
190 function NAME_GetLength() { | |
191 if (!(%_ClassOf(this) === 'NAME')) { | |
192 throw MakeTypeError(kIncompatibleMethodReceiver, "NAME.length", this); | |
193 } | |
194 return %_TypedArrayGetLength(this); | |
195 } | |
196 | |
197 function NAMESubArray(begin, end) { | |
198 if (!(%_ClassOf(this) === 'NAME')) { | |
199 throw MakeTypeError(kIncompatibleMethodReceiver, "NAME.subarray", this); | |
200 } | |
201 var beginInt = TO_INTEGER(begin); | |
202 if (!IS_UNDEFINED(end)) { | |
203 var endInt = TO_INTEGER(end); | |
204 var srcLength = %_TypedArrayGetLength(this); | |
205 } else { | |
206 var srcLength = %_TypedArrayGetLength(this); | |
207 var endInt = srcLength; | |
208 } | |
209 | |
210 if (beginInt < 0) { | |
211 beginInt = MAX_SIMPLE(0, srcLength + beginInt); | |
212 } else { | |
213 beginInt = MIN_SIMPLE(beginInt, srcLength); | |
214 } | |
215 | |
216 if (endInt < 0) { | |
217 endInt = MAX_SIMPLE(0, srcLength + endInt); | |
218 } else { | |
219 endInt = MIN_SIMPLE(endInt, srcLength); | |
220 } | |
221 | |
222 if (endInt < beginInt) { | |
223 endInt = beginInt; | |
224 } | |
225 | |
226 var newLength = endInt - beginInt; | |
227 var beginByteOffset = | |
228 %_ArrayBufferViewGetByteOffset(this) + beginInt * ELEMENT_SIZE; | |
229 return new GlobalNAME(%TypedArrayGetBuffer(this), | |
230 beginByteOffset, newLength); | |
231 } | |
232 endmacro | |
233 | |
234 TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR) | |
235 | |
236 | |
237 function TypedArraySetFromArrayLike(target, source, sourceLength, offset) { | |
238 if (offset > 0) { | |
239 for (var i = 0; i < sourceLength; i++) { | |
240 target[offset + i] = source[i]; | |
241 } | |
242 } | |
243 else { | |
244 for (var i = 0; i < sourceLength; i++) { | |
245 target[i] = source[i]; | |
246 } | |
247 } | |
248 } | |
249 | |
250 function TypedArraySetFromOverlappingTypedArray(target, source, offset) { | |
251 var sourceElementSize = source.BYTES_PER_ELEMENT; | |
252 var targetElementSize = target.BYTES_PER_ELEMENT; | |
253 var sourceLength = source.length; | |
254 | |
255 // Copy left part. | |
256 function CopyLeftPart() { | |
257 // First un-mutated byte after the next write | |
258 var targetPtr = target.byteOffset + (offset + 1) * targetElementSize; | |
259 // Next read at sourcePtr. We do not care for memory changing before | |
260 // sourcePtr - we have already copied it. | |
261 var sourcePtr = source.byteOffset; | |
262 for (var leftIndex = 0; | |
263 leftIndex < sourceLength && targetPtr <= sourcePtr; | |
264 leftIndex++) { | |
265 target[offset + leftIndex] = source[leftIndex]; | |
266 targetPtr += targetElementSize; | |
267 sourcePtr += sourceElementSize; | |
268 } | |
269 return leftIndex; | |
270 } | |
271 var leftIndex = CopyLeftPart(); | |
272 | |
273 // Copy rigth part; | |
274 function CopyRightPart() { | |
275 // First unmutated byte before the next write | |
276 var targetPtr = | |
277 target.byteOffset + (offset + sourceLength - 1) * targetElementSize; | |
278 // Next read before sourcePtr. We do not care for memory changing after | |
279 // sourcePtr - we have already copied it. | |
280 var sourcePtr = | |
281 source.byteOffset + sourceLength * sourceElementSize; | |
282 for(var rightIndex = sourceLength - 1; | |
283 rightIndex >= leftIndex && targetPtr >= sourcePtr; | |
284 rightIndex--) { | |
285 target[offset + rightIndex] = source[rightIndex]; | |
286 targetPtr -= targetElementSize; | |
287 sourcePtr -= sourceElementSize; | |
288 } | |
289 return rightIndex; | |
290 } | |
291 var rightIndex = CopyRightPart(); | |
292 | |
293 var temp = new GlobalArray(rightIndex + 1 - leftIndex); | |
294 for (var i = leftIndex; i <= rightIndex; i++) { | |
295 temp[i - leftIndex] = source[i]; | |
296 } | |
297 for (i = leftIndex; i <= rightIndex; i++) { | |
298 target[offset + i] = temp[i - leftIndex]; | |
299 } | |
300 } | |
301 | |
302 function TypedArraySet(obj, offset) { | |
303 var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset); | |
304 if (intOffset < 0) throw MakeTypeError(kTypedArraySetNegativeOffset); | |
305 | |
306 if (intOffset > %_MaxSmi()) { | |
307 throw MakeRangeError(kTypedArraySetSourceTooLarge); | |
308 } | |
309 switch (%TypedArraySetFastCases(this, obj, intOffset)) { | |
310 // These numbers should be synchronized with runtime.cc. | |
311 case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE | |
312 return; | |
313 case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING | |
314 TypedArraySetFromOverlappingTypedArray(this, obj, intOffset); | |
315 return; | |
316 case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING | |
317 TypedArraySetFromArrayLike(this, obj, obj.length, intOffset); | |
318 return; | |
319 case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY | |
320 var l = obj.length; | |
321 if (IS_UNDEFINED(l)) { | |
322 if (IS_NUMBER(obj)) { | |
323 // For number as a first argument, throw TypeError | |
324 // instead of silently ignoring the call, so that | |
325 // the user knows (s)he did something wrong. | |
326 // (Consistent with Firefox and Blink/WebKit) | |
327 throw MakeTypeError(kInvalidArgument); | |
328 } | |
329 return; | |
330 } | |
331 l = TO_LENGTH(l); | |
332 if (intOffset + l > this.length) { | |
333 throw MakeRangeError(kTypedArraySetSourceTooLarge); | |
334 } | |
335 TypedArraySetFromArrayLike(this, obj, l, intOffset); | |
336 return; | |
337 } | |
338 } | |
339 | |
340 function TypedArrayGetToStringTag() { | |
341 if (!%_IsTypedArray(this)) return; | |
342 var name = %_ClassOf(this); | |
343 if (IS_UNDEFINED(name)) return; | |
344 return name; | |
345 } | |
346 | |
347 // ------------------------------------------------------------------- | |
348 | |
349 macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE) | |
350 %SetCode(GlobalNAME, NAMEConstructor); | |
351 %FunctionSetPrototype(GlobalNAME, new GlobalObject()); | |
352 | |
353 %AddNamedProperty(GlobalNAME, "BYTES_PER_ELEMENT", ELEMENT_SIZE, | |
354 READ_ONLY | DONT_ENUM | DONT_DELETE); | |
355 %AddNamedProperty(GlobalNAME.prototype, | |
356 "constructor", global.NAME, DONT_ENUM); | |
357 %AddNamedProperty(GlobalNAME.prototype, | |
358 "BYTES_PER_ELEMENT", ELEMENT_SIZE, | |
359 READ_ONLY | DONT_ENUM | DONT_DELETE); | |
360 utils.InstallGetter(GlobalNAME.prototype, "buffer", NAME_GetBuffer); | |
361 utils.InstallGetter(GlobalNAME.prototype, "byteOffset", NAME_GetByteOffset, | |
362 DONT_ENUM | DONT_DELETE); | |
363 utils.InstallGetter(GlobalNAME.prototype, "byteLength", NAME_GetByteLength, | |
364 DONT_ENUM | DONT_DELETE); | |
365 utils.InstallGetter(GlobalNAME.prototype, "length", NAME_GetLength, | |
366 DONT_ENUM | DONT_DELETE); | |
367 utils.InstallGetter(GlobalNAME.prototype, toStringTagSymbol, | |
368 TypedArrayGetToStringTag); | |
369 utils.InstallFunctions(GlobalNAME.prototype, DONT_ENUM, [ | |
370 "subarray", NAMESubArray, | |
371 "set", TypedArraySet | |
372 ]); | |
373 endmacro | |
374 | |
375 TYPED_ARRAYS(SETUP_TYPED_ARRAY) | |
376 | |
377 // --------------------------- DataView ----------------------------- | |
378 | |
379 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3 | |
380 if (%_IsConstructCall()) { | |
381 // TODO(binji): support SharedArrayBuffers? | |
382 if (!IS_ARRAYBUFFER(buffer)) throw MakeTypeError(kDataViewNotArrayBuffer); | |
383 if (!IS_UNDEFINED(byteOffset)) { | |
384 byteOffset = $toPositiveInteger(byteOffset, kInvalidDataViewOffset); | |
385 } | |
386 if (!IS_UNDEFINED(byteLength)) { | |
387 byteLength = TO_INTEGER(byteLength); | |
388 } | |
389 | |
390 var bufferByteLength = %_ArrayBufferGetByteLength(buffer); | |
391 | |
392 var offset = IS_UNDEFINED(byteOffset) ? 0 : byteOffset; | |
393 if (offset > bufferByteLength) throw MakeRangeError(kInvalidDataViewOffset); | |
394 | |
395 var length = IS_UNDEFINED(byteLength) | |
396 ? bufferByteLength - offset | |
397 : byteLength; | |
398 if (length < 0 || offset + length > bufferByteLength) { | |
399 throw new MakeRangeError(kInvalidDataViewLength); | |
400 } | |
401 %_DataViewInitialize(this, buffer, offset, length); | |
402 } else { | |
403 throw MakeTypeError(kConstructorNotFunction, "DataView"); | |
404 } | |
405 } | |
406 | |
407 function DataViewGetBufferJS() { | |
408 if (!IS_DATAVIEW(this)) { | |
409 throw MakeTypeError(kIncompatibleMethodReceiver, 'DataView.buffer', this); | |
410 } | |
411 return %DataViewGetBuffer(this); | |
412 } | |
413 | |
414 function DataViewGetByteOffset() { | |
415 if (!IS_DATAVIEW(this)) { | |
416 throw MakeTypeError(kIncompatibleMethodReceiver, | |
417 'DataView.byteOffset', this); | |
418 } | |
419 return %_ArrayBufferViewGetByteOffset(this); | |
420 } | |
421 | |
422 function DataViewGetByteLength() { | |
423 if (!IS_DATAVIEW(this)) { | |
424 throw MakeTypeError(kIncompatibleMethodReceiver, | |
425 'DataView.byteLength', this); | |
426 } | |
427 return %_ArrayBufferViewGetByteLength(this); | |
428 } | |
429 | |
430 macro DATA_VIEW_TYPES(FUNCTION) | |
431 FUNCTION(Int8) | |
432 FUNCTION(Uint8) | |
433 FUNCTION(Int16) | |
434 FUNCTION(Uint16) | |
435 FUNCTION(Int32) | |
436 FUNCTION(Uint32) | |
437 FUNCTION(Float32) | |
438 FUNCTION(Float64) | |
439 endmacro | |
440 | |
441 | |
442 macro DATA_VIEW_GETTER_SETTER(TYPENAME) | |
443 function DataViewGetTYPENAMEJS(offset, little_endian) { | |
444 if (!IS_DATAVIEW(this)) { | |
445 throw MakeTypeError(kIncompatibleMethodReceiver, | |
446 'DataView.getTYPENAME', this); | |
447 } | |
448 if (%_ArgumentsLength() < 1) throw MakeTypeError(kInvalidArgument); | |
449 offset = $toPositiveInteger(offset, kInvalidDataViewAccessorOffset); | |
450 return %DataViewGetTYPENAME(this, offset, !!little_endian); | |
451 } | |
452 | |
453 function DataViewSetTYPENAMEJS(offset, value, little_endian) { | |
454 if (!IS_DATAVIEW(this)) { | |
455 throw MakeTypeError(kIncompatibleMethodReceiver, | |
456 'DataView.setTYPENAME', this); | |
457 } | |
458 if (%_ArgumentsLength() < 2) throw MakeTypeError(kInvalidArgument); | |
459 offset = $toPositiveInteger(offset, kInvalidDataViewAccessorOffset); | |
460 %DataViewSetTYPENAME(this, offset, TO_NUMBER(value), !!little_endian); | |
461 } | |
462 endmacro | |
463 | |
464 DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER) | |
465 | |
466 // Setup the DataView constructor. | |
467 %SetCode(GlobalDataView, DataViewConstructor); | |
468 %FunctionSetPrototype(GlobalDataView, new GlobalObject); | |
469 | |
470 // Set up constructor property on the DataView prototype. | |
471 %AddNamedProperty(GlobalDataView.prototype, "constructor", GlobalDataView, | |
472 DONT_ENUM); | |
473 %AddNamedProperty(GlobalDataView.prototype, toStringTagSymbol, "DataView", | |
474 READ_ONLY|DONT_ENUM); | |
475 | |
476 utils.InstallGetter(GlobalDataView.prototype, "buffer", DataViewGetBufferJS); | |
477 utils.InstallGetter(GlobalDataView.prototype, "byteOffset", | |
478 DataViewGetByteOffset); | |
479 utils.InstallGetter(GlobalDataView.prototype, "byteLength", | |
480 DataViewGetByteLength); | |
481 | |
482 utils.InstallFunctions(GlobalDataView.prototype, DONT_ENUM, [ | |
483 "getInt8", DataViewGetInt8JS, | |
484 "setInt8", DataViewSetInt8JS, | |
485 | |
486 "getUint8", DataViewGetUint8JS, | |
487 "setUint8", DataViewSetUint8JS, | |
488 | |
489 "getInt16", DataViewGetInt16JS, | |
490 "setInt16", DataViewSetInt16JS, | |
491 | |
492 "getUint16", DataViewGetUint16JS, | |
493 "setUint16", DataViewSetUint16JS, | |
494 | |
495 "getInt32", DataViewGetInt32JS, | |
496 "setInt32", DataViewSetInt32JS, | |
497 | |
498 "getUint32", DataViewGetUint32JS, | |
499 "setUint32", DataViewSetUint32JS, | |
500 | |
501 "getFloat32", DataViewGetFloat32JS, | |
502 "setFloat32", DataViewSetFloat32JS, | |
503 | |
504 "getFloat64", DataViewGetFloat64JS, | |
505 "setFloat64", DataViewSetFloat64JS | |
506 ]); | |
507 | |
508 }) | |
OLD | NEW |