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"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(); 473