Home | History | Annotate | Download | only in src
      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