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 4 // met: 5 // 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. 15 // 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. 27 28 "use strict"; 29 30 // This file relies on the fact that the following declaration has been made 31 // in runtime.js: 32 // var $Array = global.Array; 33 34 35 36 // --------------- Typed Arrays --------------------- 37 38 function CreateTypedArrayConstructor(name, elementSize, arrayId, constructor) { 39 function ConstructByArrayBuffer(obj, buffer, byteOffset, length) { 40 var offset = ToPositiveInteger(byteOffset, "invalid_typed_array_length") 41 42 if (offset % elementSize !== 0) { 43 throw MakeRangeError("invalid_typed_array_alignment", 44 "start offset", name, elementSize); 45 } 46 var bufferByteLength = %ArrayBufferGetByteLength(buffer); 47 if (offset > bufferByteLength) { 48 throw MakeRangeError("invalid_typed_array_offset"); 49 } 50 51 var newByteLength; 52 var newLength; 53 if (IS_UNDEFINED(length)) { 54 if (bufferByteLength % elementSize !== 0) { 55 throw MakeRangeError("invalid_typed_array_alignment", 56 "byte length", name, elementSize); 57 } 58 newByteLength = bufferByteLength - offset; 59 newLength = newByteLength / elementSize; 60 } else { 61 var newLength = ToPositiveInteger(length, "invalid_typed_array_length"); 62 newByteLength = newLength * elementSize; 63 } 64 if (offset + newByteLength > bufferByteLength) { 65 throw MakeRangeError("invalid_typed_array_length"); 66 } 67 %TypedArrayInitialize(obj, arrayId, buffer, offset, newByteLength); 68 } 69 70 function ConstructByLength(obj, length) { 71 var l = ToPositiveInteger(length, "invalid_typed_array_length"); 72 var byteLength = l * elementSize; 73 var buffer = new global.ArrayBuffer(byteLength); 74 %TypedArrayInitialize(obj, arrayId, buffer, 0, byteLength); 75 } 76 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 obj[i] = arrayLike[i]; 83 } 84 } 85 } 86 87 return function (arg1, arg2, arg3) { 88 if (%_IsConstructCall()) { 89 if (IS_ARRAYBUFFER(arg1)) { 90 ConstructByArrayBuffer(this, arg1, arg2, arg3); 91 } else if (IS_NUMBER(arg1) || IS_STRING(arg1) || 92 IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) { 93 ConstructByLength(this, arg1); 94 } else { 95 ConstructByArrayLike(this, arg1); 96 } 97 } else { 98 throw MakeTypeError("constructor_not_function", [name]) 99 } 100 } 101 } 102 103 function TypedArrayGetBuffer() { 104 return %TypedArrayGetBuffer(this); 105 } 106 107 function TypedArrayGetByteLength() { 108 return %TypedArrayGetByteLength(this); 109 } 110 111 function TypedArrayGetByteOffset() { 112 return %TypedArrayGetByteOffset(this); 113 } 114 115 function TypedArrayGetLength() { 116 return %TypedArrayGetLength(this); 117 } 118 119 function CreateSubArray(elementSize, constructor) { 120 return function(begin, end) { 121 var srcLength = %TypedArrayGetLength(this); 122 var beginInt = TO_INTEGER(begin); 123 if (beginInt < 0) { 124 beginInt = MathMax(0, srcLength + beginInt); 125 } else { 126 beginInt = MathMin(srcLength, beginInt); 127 } 128 129 var endInt = IS_UNDEFINED(end) ? srcLength : TO_INTEGER(end); 130 if (endInt < 0) { 131 endInt = MathMax(0, srcLength + endInt); 132 } else { 133 endInt = MathMin(endInt, srcLength); 134 } 135 if (endInt < beginInt) { 136 endInt = beginInt; 137 } 138 var newLength = endInt - beginInt; 139 var beginByteOffset = 140 %TypedArrayGetByteOffset(this) + beginInt * elementSize; 141 return new constructor(%TypedArrayGetBuffer(this), 142 beginByteOffset, newLength); 143 } 144 } 145 146 function TypedArraySetFromArrayLike(target, source, sourceLength, offset) { 147 if (offset > 0) { 148 for (var i = 0; i < sourceLength; i++) { 149 target[offset + i] = source[i]; 150 } 151 } 152 else { 153 for (var i = 0; i < sourceLength; i++) { 154 target[i] = source[i]; 155 } 156 } 157 } 158 159 function TypedArraySetFromOverlappingTypedArray(target, source, offset) { 160 var sourceElementSize = source.BYTES_PER_ELEMENT; 161 var targetElementSize = target.BYTES_PER_ELEMENT; 162 var sourceLength = source.length; 163 164 // Copy left part. 165 function CopyLeftPart() { 166 // First un-mutated byte after the next write 167 var targetPtr = target.byteOffset + (offset + 1) * targetElementSize; 168 // Next read at sourcePtr. We do not care for memory changing before 169 // sourcePtr - we have already copied it. 170 var sourcePtr = source.byteOffset; 171 for (var leftIndex = 0; 172 leftIndex < sourceLength && targetPtr <= sourcePtr; 173 leftIndex++) { 174 target[offset + leftIndex] = source[leftIndex]; 175 targetPtr += targetElementSize; 176 sourcePtr += sourceElementSize; 177 } 178 return leftIndex; 179 } 180 var leftIndex = CopyLeftPart(); 181 182 // Copy rigth part; 183 function CopyRightPart() { 184 // First unmutated byte before the next write 185 var targetPtr = 186 target.byteOffset + (offset + sourceLength - 1) * targetElementSize; 187 // Next read before sourcePtr. We do not care for memory changing after 188 // sourcePtr - we have already copied it. 189 var sourcePtr = 190 source.byteOffset + sourceLength * sourceElementSize; 191 for(var rightIndex = sourceLength - 1; 192 rightIndex >= leftIndex && targetPtr >= sourcePtr; 193 rightIndex--) { 194 target[offset + rightIndex] = source[rightIndex]; 195 targetPtr -= targetElementSize; 196 sourcePtr -= sourceElementSize; 197 } 198 return rightIndex; 199 } 200 var rightIndex = CopyRightPart(); 201 202 var temp = new $Array(rightIndex + 1 - leftIndex); 203 for (var i = leftIndex; i <= rightIndex; i++) { 204 temp[i - leftIndex] = source[i]; 205 } 206 for (i = leftIndex; i <= rightIndex; i++) { 207 target[offset + i] = temp[i - leftIndex]; 208 } 209 } 210 211 function TypedArraySet(obj, offset) { 212 var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset); 213 if (intOffset < 0) { 214 throw MakeTypeError("typed_array_set_negative_offset"); 215 } 216 switch (%TypedArraySetFastCases(this, obj, intOffset)) { 217 // These numbers should be synchronized with runtime.cc. 218 case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE 219 return; 220 case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING 221 TypedArraySetFromOverlappingTypedArray(this, obj, intOffset); 222 return; 223 case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING 224 TypedArraySetFromArrayLike(this, obj, obj.length, intOffset); 225 return; 226 case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY 227 var l = obj.length; 228 if (IS_UNDEFINED(l)) { 229 if (IS_NUMBER(obj)) { 230 // For number as a first argument, throw TypeError 231 // instead of silently ignoring the call, so that 232 // the user knows (s)he did something wrong. 233 // (Consistent with Firefox and Blink/WebKit) 234 throw MakeTypeError("invalid_argument"); 235 } 236 return; 237 } 238 if (intOffset + l > this.length) { 239 throw MakeRangeError("typed_array_set_source_too_large"); 240 } 241 TypedArraySetFromArrayLike(this, obj, l, intOffset); 242 return; 243 } 244 } 245 246 // ------------------------------------------------------------------- 247 248 function SetupTypedArray(arrayId, name, constructor, elementSize) { 249 %CheckIsBootstrapping(); 250 var fun = CreateTypedArrayConstructor(name, elementSize, 251 arrayId, constructor); 252 %SetCode(constructor, fun); 253 %FunctionSetPrototype(constructor, new $Object()); 254 255 %SetProperty(constructor, "BYTES_PER_ELEMENT", elementSize, 256 READ_ONLY | DONT_ENUM | DONT_DELETE); 257 %SetProperty(constructor.prototype, 258 "constructor", constructor, DONT_ENUM); 259 %SetProperty(constructor.prototype, 260 "BYTES_PER_ELEMENT", elementSize, 261 READ_ONLY | DONT_ENUM | DONT_DELETE); 262 InstallGetter(constructor.prototype, "buffer", TypedArrayGetBuffer); 263 InstallGetter(constructor.prototype, "byteOffset", TypedArrayGetByteOffset); 264 InstallGetter(constructor.prototype, "byteLength", TypedArrayGetByteLength); 265 InstallGetter(constructor.prototype, "length", TypedArrayGetLength); 266 267 InstallFunctions(constructor.prototype, DONT_ENUM, $Array( 268 "subarray", CreateSubArray(elementSize, constructor), 269 "set", TypedArraySet 270 )); 271 } 272 273 // arrayIds below should be synchronized with Runtime_TypedArrayInitialize. 274 SetupTypedArray(1, "Uint8Array", global.Uint8Array, 1); 275 SetupTypedArray(2, "Int8Array", global.Int8Array, 1); 276 SetupTypedArray(3, "Uint16Array", global.Uint16Array, 2); 277 SetupTypedArray(4, "Int16Array", global.Int16Array, 2); 278 SetupTypedArray(5, "Uint32Array", global.Uint32Array, 4); 279 SetupTypedArray(6, "Int32Array", global.Int32Array, 4); 280 SetupTypedArray(7, "Float32Array", global.Float32Array, 4); 281 SetupTypedArray(8, "Float64Array", global.Float64Array, 8); 282 SetupTypedArray(9, "Uint8ClampedArray", global.Uint8ClampedArray, 1); 283 284 285 // --------------------------- DataView ----------------------------- 286 287 var $DataView = global.DataView; 288 289 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3 290 if (%_IsConstructCall()) { 291 if (!IS_ARRAYBUFFER(buffer)) { 292 throw MakeTypeError('data_view_not_array_buffer', []); 293 } 294 var bufferByteLength = %ArrayBufferGetByteLength(buffer); 295 var offset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset'); 296 if (offset > bufferByteLength) { 297 throw MakeRangeError('invalid_data_view_offset'); 298 } 299 var length = IS_UNDEFINED(byteLength) ? 300 bufferByteLength - offset : TO_INTEGER(byteLength); 301 if (length < 0 || offset + length > bufferByteLength) { 302 throw new MakeRangeError('invalid_data_view_length'); 303 } 304 %DataViewInitialize(this, buffer, offset, length); 305 } else { 306 throw MakeTypeError('constructor_not_function', ["DataView"]); 307 } 308 } 309 310 function DataViewGetBuffer() { 311 if (!IS_DATAVIEW(this)) { 312 throw MakeTypeError('incompatible_method_receiver', 313 ['DataView.buffer', this]); 314 } 315 return %DataViewGetBuffer(this); 316 } 317 318 function DataViewGetByteOffset() { 319 if (!IS_DATAVIEW(this)) { 320 throw MakeTypeError('incompatible_method_receiver', 321 ['DataView.byteOffset', this]); 322 } 323 return %DataViewGetByteOffset(this); 324 } 325 326 function DataViewGetByteLength() { 327 if (!IS_DATAVIEW(this)) { 328 throw MakeTypeError('incompatible_method_receiver', 329 ['DataView.byteLength', this]); 330 } 331 return %DataViewGetByteLength(this); 332 } 333 334 function ToPositiveDataViewOffset(offset) { 335 return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset'); 336 } 337 338 function DataViewGetInt8(offset, little_endian) { 339 if (!IS_DATAVIEW(this)) { 340 throw MakeTypeError('incompatible_method_receiver', 341 ['DataView.getInt8', this]); 342 } 343 if (%_ArgumentsLength() < 1) { 344 throw MakeTypeError('invalid_argument'); 345 } 346 return %DataViewGetInt8(this, 347 ToPositiveDataViewOffset(offset), 348 !!little_endian); 349 } 350 351 function DataViewSetInt8(offset, value, little_endian) { 352 if (!IS_DATAVIEW(this)) { 353 throw MakeTypeError('incompatible_method_receiver', 354 ['DataView.setInt8', this]); 355 } 356 if (%_ArgumentsLength() < 2) { 357 throw MakeTypeError('invalid_argument'); 358 } 359 %DataViewSetInt8(this, 360 ToPositiveDataViewOffset(offset), 361 TO_NUMBER_INLINE(value), 362 !!little_endian); 363 } 364 365 function DataViewGetUint8(offset, little_endian) { 366 if (!IS_DATAVIEW(this)) { 367 throw MakeTypeError('incompatible_method_receiver', 368 ['DataView.getUint8', this]); 369 } 370 if (%_ArgumentsLength() < 1) { 371 throw MakeTypeError('invalid_argument'); 372 } 373 return %DataViewGetUint8(this, 374 ToPositiveDataViewOffset(offset), 375 !!little_endian); 376 } 377 378 function DataViewSetUint8(offset, value, little_endian) { 379 if (!IS_DATAVIEW(this)) { 380 throw MakeTypeError('incompatible_method_receiver', 381 ['DataView.setUint8', this]); 382 } 383 if (%_ArgumentsLength() < 2) { 384 throw MakeTypeError('invalid_argument'); 385 } 386 %DataViewSetUint8(this, 387 ToPositiveDataViewOffset(offset), 388 TO_NUMBER_INLINE(value), 389 !!little_endian); 390 } 391 392 function DataViewGetInt16(offset, little_endian) { 393 if (!IS_DATAVIEW(this)) { 394 throw MakeTypeError('incompatible_method_receiver', 395 ['DataView.getInt16', this]); 396 } 397 if (%_ArgumentsLength() < 1) { 398 throw MakeTypeError('invalid_argument'); 399 } 400 return %DataViewGetInt16(this, 401 ToPositiveDataViewOffset(offset), 402 !!little_endian); 403 } 404 405 function DataViewSetInt16(offset, value, little_endian) { 406 if (!IS_DATAVIEW(this)) { 407 throw MakeTypeError('incompatible_method_receiver', 408 ['DataView.setInt16', this]); 409 } 410 if (%_ArgumentsLength() < 2) { 411 throw MakeTypeError('invalid_argument'); 412 } 413 %DataViewSetInt16(this, 414 ToPositiveDataViewOffset(offset), 415 TO_NUMBER_INLINE(value), 416 !!little_endian); 417 } 418 419 function DataViewGetUint16(offset, little_endian) { 420 if (!IS_DATAVIEW(this)) { 421 throw MakeTypeError('incompatible_method_receiver', 422 ['DataView.getUint16', this]); 423 } 424 if (%_ArgumentsLength() < 1) { 425 throw MakeTypeError('invalid_argument'); 426 } 427 return %DataViewGetUint16(this, 428 ToPositiveDataViewOffset(offset), 429 !!little_endian); 430 } 431 432 function DataViewSetUint16(offset, value, little_endian) { 433 if (!IS_DATAVIEW(this)) { 434 throw MakeTypeError('incompatible_method_receiver', 435 ['DataView.setUint16', this]); 436 } 437 if (%_ArgumentsLength() < 2) { 438 throw MakeTypeError('invalid_argument'); 439 } 440 %DataViewSetUint16(this, 441 ToPositiveDataViewOffset(offset), 442 TO_NUMBER_INLINE(value), 443 !!little_endian); 444 } 445 446 function DataViewGetInt32(offset, little_endian) { 447 if (!IS_DATAVIEW(this)) { 448 throw MakeTypeError('incompatible_method_receiver', 449 ['DataView.getInt32', this]); 450 } 451 if (%_ArgumentsLength() < 1) { 452 throw MakeTypeError('invalid_argument'); 453 } 454 return %DataViewGetInt32(this, 455 ToPositiveDataViewOffset(offset), 456 !!little_endian); 457 } 458 459 function DataViewSetInt32(offset, value, little_endian) { 460 if (!IS_DATAVIEW(this)) { 461 throw MakeTypeError('incompatible_method_receiver', 462 ['DataView.setInt32', this]); 463 } 464 if (%_ArgumentsLength() < 2) { 465 throw MakeTypeError('invalid_argument'); 466 } 467 %DataViewSetInt32(this, 468 ToPositiveDataViewOffset(offset), 469 TO_NUMBER_INLINE(value), 470 !!little_endian); 471 } 472 473 function DataViewGetUint32(offset, little_endian) { 474 if (!IS_DATAVIEW(this)) { 475 throw MakeTypeError('incompatible_method_receiver', 476 ['DataView.getUint32', this]); 477 } 478 if (%_ArgumentsLength() < 1) { 479 throw MakeTypeError('invalid_argument'); 480 } 481 return %DataViewGetUint32(this, 482 ToPositiveDataViewOffset(offset), 483 !!little_endian); 484 } 485 486 function DataViewSetUint32(offset, value, little_endian) { 487 if (!IS_DATAVIEW(this)) { 488 throw MakeTypeError('incompatible_method_receiver', 489 ['DataView.setUint32', this]); 490 } 491 if (%_ArgumentsLength() < 2) { 492 throw MakeTypeError('invalid_argument'); 493 } 494 %DataViewSetUint32(this, 495 ToPositiveDataViewOffset(offset), 496 TO_NUMBER_INLINE(value), 497 !!little_endian); 498 } 499 500 function DataViewGetFloat32(offset, little_endian) { 501 if (!IS_DATAVIEW(this)) { 502 throw MakeTypeError('incompatible_method_receiver', 503 ['DataView.getFloat32', this]); 504 } 505 if (%_ArgumentsLength() < 1) { 506 throw MakeTypeError('invalid_argument'); 507 } 508 return %DataViewGetFloat32(this, 509 ToPositiveDataViewOffset(offset), 510 !!little_endian); 511 } 512 513 function DataViewSetFloat32(offset, value, little_endian) { 514 if (!IS_DATAVIEW(this)) { 515 throw MakeTypeError('incompatible_method_receiver', 516 ['DataView.setFloat32', this]); 517 } 518 if (%_ArgumentsLength() < 2) { 519 throw MakeTypeError('invalid_argument'); 520 } 521 %DataViewSetFloat32(this, 522 ToPositiveDataViewOffset(offset), 523 TO_NUMBER_INLINE(value), 524 !!little_endian); 525 } 526 527 function DataViewGetFloat64(offset, little_endian) { 528 if (!IS_DATAVIEW(this)) { 529 throw MakeTypeError('incompatible_method_receiver', 530 ['DataView.getFloat64', this]); 531 } 532 if (%_ArgumentsLength() < 1) { 533 throw MakeTypeError('invalid_argument'); 534 } 535 return %DataViewGetFloat64(this, 536 ToPositiveDataViewOffset(offset), 537 !!little_endian); 538 } 539 540 function DataViewSetFloat64(offset, value, little_endian) { 541 if (!IS_DATAVIEW(this)) { 542 throw MakeTypeError('incompatible_method_receiver', 543 ['DataView.setFloat64', this]); 544 } 545 if (%_ArgumentsLength() < 2) { 546 throw MakeTypeError('invalid_argument'); 547 } 548 %DataViewSetFloat64(this, 549 ToPositiveDataViewOffset(offset), 550 TO_NUMBER_INLINE(value), 551 !!little_endian); 552 } 553 554 function SetupDataView() { 555 %CheckIsBootstrapping(); 556 557 // Setup the DataView constructor. 558 %SetCode($DataView, DataViewConstructor); 559 %FunctionSetPrototype($DataView, new $Object); 560 561 // Set up constructor property on the DataView prototype. 562 %SetProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM); 563 564 InstallGetter($DataView.prototype, "buffer", DataViewGetBuffer); 565 InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset); 566 InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength); 567 568 InstallFunctions($DataView.prototype, DONT_ENUM, $Array( 569 "getInt8", DataViewGetInt8, 570 "setInt8", DataViewSetInt8, 571 572 "getUint8", DataViewGetUint8, 573 "setUint8", DataViewSetUint8, 574 575 "getInt16", DataViewGetInt16, 576 "setInt16", DataViewSetInt16, 577 578 "getUint16", DataViewGetUint16, 579 "setUint16", DataViewSetUint16, 580 581 "getInt32", DataViewGetInt32, 582 "setInt32", DataViewSetInt32, 583 584 "getUint32", DataViewGetUint32, 585 "setUint32", DataViewSetUint32, 586 587 "getFloat32", DataViewGetFloat32, 588 "setFloat32", DataViewSetFloat32, 589 590 "getFloat64", DataViewGetFloat64, 591 "setFloat64", DataViewSetFloat64 592 )); 593 } 594 595 SetupDataView(); 596