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 var $ArrayBuffer = global.ArrayBuffer; 34 35 36 // --------------- Typed Arrays --------------------- 37 macro TYPED_ARRAYS(FUNCTION) 38 // arrayIds below should be synchronized with Runtime_TypedArrayInitialize. 39 FUNCTION(1, Uint8Array, 1) 40 FUNCTION(2, Int8Array, 1) 41 FUNCTION(3, Uint16Array, 2) 42 FUNCTION(4, Int16Array, 2) 43 FUNCTION(5, Uint32Array, 4) 44 FUNCTION(6, Int32Array, 4) 45 FUNCTION(7, Float32Array, 4) 46 FUNCTION(8, Float64Array, 8) 47 FUNCTION(9, Uint8ClampedArray, 1) 48 endmacro 49 50 macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE) 51 function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) { 52 var bufferByteLength = %ArrayBufferGetByteLength(buffer); 53 var offset; 54 if (IS_UNDEFINED(byteOffset)) { 55 offset = 0; 56 } else { 57 offset = ToPositiveInteger(byteOffset, "invalid_typed_array_length"); 58 59 if (offset % ELEMENT_SIZE !== 0) { 60 throw MakeRangeError("invalid_typed_array_alignment", 61 "start offset", "NAME", ELEMENT_SIZE); 62 } 63 if (offset > bufferByteLength) { 64 throw MakeRangeError("invalid_typed_array_offset"); 65 } 66 } 67 68 var newByteLength; 69 var newLength; 70 if (IS_UNDEFINED(length)) { 71 if (bufferByteLength % ELEMENT_SIZE !== 0) { 72 throw MakeRangeError("invalid_typed_array_alignment", 73 "byte length", "NAME", ELEMENT_SIZE); 74 } 75 newByteLength = bufferByteLength - offset; 76 newLength = newByteLength / ELEMENT_SIZE; 77 } else { 78 var newLength = ToPositiveInteger(length, "invalid_typed_array_length"); 79 newByteLength = newLength * ELEMENT_SIZE; 80 } 81 if ((offset + newByteLength > bufferByteLength) 82 || (newLength > %MaxSmi())) { 83 throw MakeRangeError("invalid_typed_array_length"); 84 } 85 %TypedArrayInitialize(obj, ARRAY_ID, buffer, offset, newByteLength); 86 } 87 88 function NAMEConstructByLength(obj, length) { 89 var l = IS_UNDEFINED(length) ? 90 0 : ToPositiveInteger(length, "invalid_typed_array_length"); 91 if (l > %MaxSmi()) { 92 throw MakeRangeError("invalid_typed_array_length"); 93 } 94 var byteLength = l * ELEMENT_SIZE; 95 var buffer = new $ArrayBuffer(byteLength); 96 %TypedArrayInitialize(obj, ARRAY_ID, buffer, 0, byteLength); 97 } 98 99 function NAMEConstructByArrayLike(obj, arrayLike) { 100 var length = arrayLike.length; 101 var l = ToPositiveInteger(length, "invalid_typed_array_length"); 102 if (l > %MaxSmi()) { 103 throw MakeRangeError("invalid_typed_array_length"); 104 } 105 if(!%TypedArrayInitializeFromArrayLike(obj, ARRAY_ID, arrayLike, l)) { 106 for (var i = 0; i < l; i++) { 107 // It is crucial that we let any execptions from arrayLike[i] 108 // propagate outside the function. 109 obj[i] = arrayLike[i]; 110 } 111 } 112 } 113 114 function NAMEConstructor(arg1, arg2, arg3) { 115 116 if (%_IsConstructCall()) { 117 if (IS_ARRAYBUFFER(arg1)) { 118 NAMEConstructByArrayBuffer(this, arg1, arg2, arg3); 119 } else if (IS_NUMBER(arg1) || IS_STRING(arg1) || 120 IS_BOOLEAN(arg1) || IS_UNDEFINED(arg1)) { 121 NAMEConstructByLength(this, arg1); 122 } else { 123 NAMEConstructByArrayLike(this, arg1); 124 } 125 } else { 126 throw MakeTypeError("constructor_not_function", ["NAME"]) 127 } 128 } 129 endmacro 130 131 TYPED_ARRAYS(TYPED_ARRAY_CONSTRUCTOR) 132 133 function TypedArrayGetBuffer() { 134 return %TypedArrayGetBuffer(this); 135 } 136 137 function TypedArrayGetByteLength() { 138 return %TypedArrayGetByteLength(this); 139 } 140 141 function TypedArrayGetByteOffset() { 142 return %TypedArrayGetByteOffset(this); 143 } 144 145 function TypedArrayGetLength() { 146 return %TypedArrayGetLength(this); 147 } 148 149 function CreateSubArray(elementSize, constructor) { 150 return function(begin, end) { 151 var srcLength = %TypedArrayGetLength(this); 152 var beginInt = TO_INTEGER(begin); 153 if (beginInt < 0) { 154 beginInt = MathMax(0, srcLength + beginInt); 155 } else { 156 beginInt = MathMin(srcLength, beginInt); 157 } 158 159 var endInt = IS_UNDEFINED(end) ? srcLength : TO_INTEGER(end); 160 if (endInt < 0) { 161 endInt = MathMax(0, srcLength + endInt); 162 } else { 163 endInt = MathMin(endInt, srcLength); 164 } 165 if (endInt < beginInt) { 166 endInt = beginInt; 167 } 168 var newLength = endInt - beginInt; 169 var beginByteOffset = 170 %TypedArrayGetByteOffset(this) + beginInt * elementSize; 171 return new constructor(%TypedArrayGetBuffer(this), 172 beginByteOffset, newLength); 173 } 174 } 175 176 function TypedArraySetFromArrayLike(target, source, sourceLength, offset) { 177 if (offset > 0) { 178 for (var i = 0; i < sourceLength; i++) { 179 target[offset + i] = source[i]; 180 } 181 } 182 else { 183 for (var i = 0; i < sourceLength; i++) { 184 target[i] = source[i]; 185 } 186 } 187 } 188 189 function TypedArraySetFromOverlappingTypedArray(target, source, offset) { 190 var sourceElementSize = source.BYTES_PER_ELEMENT; 191 var targetElementSize = target.BYTES_PER_ELEMENT; 192 var sourceLength = source.length; 193 194 // Copy left part. 195 function CopyLeftPart() { 196 // First un-mutated byte after the next write 197 var targetPtr = target.byteOffset + (offset + 1) * targetElementSize; 198 // Next read at sourcePtr. We do not care for memory changing before 199 // sourcePtr - we have already copied it. 200 var sourcePtr = source.byteOffset; 201 for (var leftIndex = 0; 202 leftIndex < sourceLength && targetPtr <= sourcePtr; 203 leftIndex++) { 204 target[offset + leftIndex] = source[leftIndex]; 205 targetPtr += targetElementSize; 206 sourcePtr += sourceElementSize; 207 } 208 return leftIndex; 209 } 210 var leftIndex = CopyLeftPart(); 211 212 // Copy rigth part; 213 function CopyRightPart() { 214 // First unmutated byte before the next write 215 var targetPtr = 216 target.byteOffset + (offset + sourceLength - 1) * targetElementSize; 217 // Next read before sourcePtr. We do not care for memory changing after 218 // sourcePtr - we have already copied it. 219 var sourcePtr = 220 source.byteOffset + sourceLength * sourceElementSize; 221 for(var rightIndex = sourceLength - 1; 222 rightIndex >= leftIndex && targetPtr >= sourcePtr; 223 rightIndex--) { 224 target[offset + rightIndex] = source[rightIndex]; 225 targetPtr -= targetElementSize; 226 sourcePtr -= sourceElementSize; 227 } 228 return rightIndex; 229 } 230 var rightIndex = CopyRightPart(); 231 232 var temp = new $Array(rightIndex + 1 - leftIndex); 233 for (var i = leftIndex; i <= rightIndex; i++) { 234 temp[i - leftIndex] = source[i]; 235 } 236 for (i = leftIndex; i <= rightIndex; i++) { 237 target[offset + i] = temp[i - leftIndex]; 238 } 239 } 240 241 function TypedArraySet(obj, offset) { 242 var intOffset = IS_UNDEFINED(offset) ? 0 : TO_INTEGER(offset); 243 if (intOffset < 0) { 244 throw MakeTypeError("typed_array_set_negative_offset"); 245 } 246 switch (%TypedArraySetFastCases(this, obj, intOffset)) { 247 // These numbers should be synchronized with runtime.cc. 248 case 0: // TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE 249 return; 250 case 1: // TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING 251 TypedArraySetFromOverlappingTypedArray(this, obj, intOffset); 252 return; 253 case 2: // TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING 254 TypedArraySetFromArrayLike(this, obj, obj.length, intOffset); 255 return; 256 case 3: // TYPED_ARRAY_SET_NON_TYPED_ARRAY 257 var l = obj.length; 258 if (IS_UNDEFINED(l)) { 259 if (IS_NUMBER(obj)) { 260 // For number as a first argument, throw TypeError 261 // instead of silently ignoring the call, so that 262 // the user knows (s)he did something wrong. 263 // (Consistent with Firefox and Blink/WebKit) 264 throw MakeTypeError("invalid_argument"); 265 } 266 return; 267 } 268 if (intOffset + l > this.length) { 269 throw MakeRangeError("typed_array_set_source_too_large"); 270 } 271 TypedArraySetFromArrayLike(this, obj, l, intOffset); 272 return; 273 } 274 } 275 276 // ------------------------------------------------------------------- 277 278 function SetupTypedArray(constructor, fun, elementSize) { 279 %CheckIsBootstrapping(); 280 %SetCode(constructor, fun); 281 %FunctionSetPrototype(constructor, new $Object()); 282 283 %SetProperty(constructor, "BYTES_PER_ELEMENT", elementSize, 284 READ_ONLY | DONT_ENUM | DONT_DELETE); 285 %SetProperty(constructor.prototype, 286 "constructor", constructor, DONT_ENUM); 287 %SetProperty(constructor.prototype, 288 "BYTES_PER_ELEMENT", elementSize, 289 READ_ONLY | DONT_ENUM | DONT_DELETE); 290 InstallGetter(constructor.prototype, "buffer", TypedArrayGetBuffer); 291 InstallGetter(constructor.prototype, "byteOffset", TypedArrayGetByteOffset); 292 InstallGetter(constructor.prototype, "byteLength", TypedArrayGetByteLength); 293 InstallGetter(constructor.prototype, "length", TypedArrayGetLength); 294 295 InstallFunctions(constructor.prototype, DONT_ENUM, $Array( 296 "subarray", CreateSubArray(elementSize, constructor), 297 "set", TypedArraySet 298 )); 299 } 300 301 macro SETUP_TYPED_ARRAY(ARRAY_ID, NAME, ELEMENT_SIZE) 302 SetupTypedArray (global.NAME, NAMEConstructor, ELEMENT_SIZE); 303 endmacro 304 305 TYPED_ARRAYS(SETUP_TYPED_ARRAY) 306 307 // --------------------------- DataView ----------------------------- 308 309 var $DataView = global.DataView; 310 311 function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3 312 if (%_IsConstructCall()) { 313 if (!IS_ARRAYBUFFER(buffer)) { 314 throw MakeTypeError('data_view_not_array_buffer', []); 315 } 316 var bufferByteLength = %ArrayBufferGetByteLength(buffer); 317 var offset = IS_UNDEFINED(byteOffset) ? 318 0 : ToPositiveInteger(byteOffset, 'invalid_data_view_offset'); 319 if (offset > bufferByteLength) { 320 throw MakeRangeError('invalid_data_view_offset'); 321 } 322 var length = IS_UNDEFINED(byteLength) ? 323 bufferByteLength - offset : TO_INTEGER(byteLength); 324 if (length < 0 || offset + length > bufferByteLength) { 325 throw new MakeRangeError('invalid_data_view_length'); 326 } 327 %DataViewInitialize(this, buffer, offset, length); 328 } else { 329 throw MakeTypeError('constructor_not_function', ["DataView"]); 330 } 331 } 332 333 function DataViewGetBuffer() { 334 if (!IS_DATAVIEW(this)) { 335 throw MakeTypeError('incompatible_method_receiver', 336 ['DataView.buffer', this]); 337 } 338 return %DataViewGetBuffer(this); 339 } 340 341 function DataViewGetByteOffset() { 342 if (!IS_DATAVIEW(this)) { 343 throw MakeTypeError('incompatible_method_receiver', 344 ['DataView.byteOffset', this]); 345 } 346 return %DataViewGetByteOffset(this); 347 } 348 349 function DataViewGetByteLength() { 350 if (!IS_DATAVIEW(this)) { 351 throw MakeTypeError('incompatible_method_receiver', 352 ['DataView.byteLength', this]); 353 } 354 return %DataViewGetByteLength(this); 355 } 356 357 macro DATA_VIEW_TYPES(FUNCTION) 358 FUNCTION(Int8) 359 FUNCTION(Uint8) 360 FUNCTION(Int16) 361 FUNCTION(Uint16) 362 FUNCTION(Int32) 363 FUNCTION(Uint32) 364 FUNCTION(Float32) 365 FUNCTION(Float64) 366 endmacro 367 368 function ToPositiveDataViewOffset(offset) { 369 return ToPositiveInteger(offset, 'invalid_data_view_accessor_offset'); 370 } 371 372 373 macro DATA_VIEW_GETTER_SETTER(TYPENAME) 374 function DataViewGetTYPENAME(offset, little_endian) { 375 if (!IS_DATAVIEW(this)) { 376 throw MakeTypeError('incompatible_method_receiver', 377 ['DataView.getTYPENAME', this]); 378 } 379 if (%_ArgumentsLength() < 1) { 380 throw MakeTypeError('invalid_argument'); 381 } 382 return %DataViewGetTYPENAME(this, 383 ToPositiveDataViewOffset(offset), 384 !!little_endian); 385 } 386 387 function DataViewSetTYPENAME(offset, value, little_endian) { 388 if (!IS_DATAVIEW(this)) { 389 throw MakeTypeError('incompatible_method_receiver', 390 ['DataView.setTYPENAME', this]); 391 } 392 if (%_ArgumentsLength() < 2) { 393 throw MakeTypeError('invalid_argument'); 394 } 395 %DataViewSetTYPENAME(this, 396 ToPositiveDataViewOffset(offset), 397 TO_NUMBER_INLINE(value), 398 !!little_endian); 399 } 400 endmacro 401 402 DATA_VIEW_TYPES(DATA_VIEW_GETTER_SETTER) 403 404 function SetupDataView() { 405 %CheckIsBootstrapping(); 406 407 // Setup the DataView constructor. 408 %SetCode($DataView, DataViewConstructor); 409 %FunctionSetPrototype($DataView, new $Object); 410 411 // Set up constructor property on the DataView prototype. 412 %SetProperty($DataView.prototype, "constructor", $DataView, DONT_ENUM); 413 414 InstallGetter($DataView.prototype, "buffer", DataViewGetBuffer); 415 InstallGetter($DataView.prototype, "byteOffset", DataViewGetByteOffset); 416 InstallGetter($DataView.prototype, "byteLength", DataViewGetByteLength); 417 418 InstallFunctions($DataView.prototype, DONT_ENUM, $Array( 419 "getInt8", DataViewGetInt8, 420 "setInt8", DataViewSetInt8, 421 422 "getUint8", DataViewGetUint8, 423 "setUint8", DataViewSetUint8, 424 425 "getInt16", DataViewGetInt16, 426 "setInt16", DataViewSetInt16, 427 428 "getUint16", DataViewGetUint16, 429 "setUint16", DataViewSetUint16, 430 431 "getInt32", DataViewGetInt32, 432 "setInt32", DataViewSetInt32, 433 434 "getUint32", DataViewGetUint32, 435 "setUint32", DataViewSetUint32, 436 437 "getFloat32", DataViewGetFloat32, 438 "setFloat32", DataViewSetFloat32, 439 440 "getFloat64", DataViewGetFloat64, 441 "setFloat64", DataViewSetFloat64 442 )); 443 } 444 445 SetupDataView(); 446