Home | History | Annotate | Download | only in runtime
      1 // Copyright 2014 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 #include "src/runtime/runtime-utils.h"
      6 
      7 #include "src/arguments.h"
      8 #include "src/factory.h"
      9 #include "src/messages.h"
     10 #include "src/objects-inl.h"
     11 #include "src/runtime/runtime.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 RUNTIME_FUNCTION(Runtime_ArrayBufferGetByteLength) {
     17   SealHandleScope shs(isolate);
     18   DCHECK_EQ(1, args.length());
     19   CONVERT_ARG_CHECKED(JSArrayBuffer, holder, 0);
     20   return holder->byte_length();
     21 }
     22 
     23 
     24 RUNTIME_FUNCTION(Runtime_ArrayBufferSliceImpl) {
     25   HandleScope scope(isolate);
     26   DCHECK_EQ(4, args.length());
     27   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, source, 0);
     28   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, target, 1);
     29   CONVERT_NUMBER_ARG_HANDLE_CHECKED(first, 2);
     30   CONVERT_NUMBER_ARG_HANDLE_CHECKED(new_length, 3);
     31 
     32   if (source->was_neutered() || target->was_neutered()) {
     33     THROW_NEW_ERROR_RETURN_FAILURE(
     34         isolate, NewTypeError(MessageTemplate::kDetachedOperation,
     35                               isolate->factory()->NewStringFromAsciiChecked(
     36                                   "ArrayBuffer.prototype.slice")));
     37   }
     38 
     39   CHECK(!source.is_identical_to(target));
     40   size_t start = 0, target_length = 0;
     41   CHECK(TryNumberToSize(*first, &start));
     42   CHECK(TryNumberToSize(*new_length, &target_length));
     43   CHECK(NumberToSize(target->byte_length()) >= target_length);
     44 
     45   if (target_length == 0) return isolate->heap()->undefined_value();
     46 
     47   size_t source_byte_length = NumberToSize(source->byte_length());
     48   CHECK(start <= source_byte_length);
     49   CHECK(source_byte_length - start >= target_length);
     50   uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
     51   uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
     52   CopyBytes(target_data, source_data + start, target_length);
     53   return isolate->heap()->undefined_value();
     54 }
     55 
     56 
     57 RUNTIME_FUNCTION(Runtime_ArrayBufferNeuter) {
     58   HandleScope scope(isolate);
     59   DCHECK_EQ(1, args.length());
     60   CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
     61   if (array_buffer->backing_store() == NULL) {
     62     CHECK(Smi::kZero == array_buffer->byte_length());
     63     return isolate->heap()->undefined_value();
     64   }
     65   // Shared array buffers should never be neutered.
     66   CHECK(!array_buffer->is_shared());
     67   DCHECK(!array_buffer->is_external());
     68   void* backing_store = array_buffer->backing_store();
     69   size_t byte_length = NumberToSize(array_buffer->byte_length());
     70   array_buffer->set_is_external(true);
     71   isolate->heap()->UnregisterArrayBuffer(*array_buffer);
     72   array_buffer->Neuter();
     73   isolate->array_buffer_allocator()->Free(backing_store, byte_length);
     74   return isolate->heap()->undefined_value();
     75 }
     76 
     77 
     78 void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type,
     79                                    ElementsKind* fixed_elements_kind,
     80                                    size_t* element_size) {
     81   switch (arrayId) {
     82 #define ARRAY_ID_CASE(Type, type, TYPE, ctype, size)      \
     83   case ARRAY_ID_##TYPE:                                   \
     84     *array_type = kExternal##Type##Array;                 \
     85     *fixed_elements_kind = TYPE##_ELEMENTS;               \
     86     *element_size = size;                                 \
     87     break;
     88 
     89     TYPED_ARRAYS(ARRAY_ID_CASE)
     90 #undef ARRAY_ID_CASE
     91 
     92     default:
     93       UNREACHABLE();
     94   }
     95 }
     96 
     97 
     98 RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
     99   HandleScope scope(isolate);
    100   DCHECK_EQ(6, args.length());
    101   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
    102   CONVERT_SMI_ARG_CHECKED(arrayId, 1);
    103   CONVERT_ARG_HANDLE_CHECKED(Object, maybe_buffer, 2);
    104   CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_offset_object, 3);
    105   CONVERT_NUMBER_ARG_HANDLE_CHECKED(byte_length_object, 4);
    106   CONVERT_BOOLEAN_ARG_CHECKED(initialize, 5);
    107 
    108   CHECK(arrayId >= Runtime::ARRAY_ID_FIRST &&
    109         arrayId <= Runtime::ARRAY_ID_LAST);
    110 
    111   ExternalArrayType array_type = kExternalInt8Array;  // Bogus initialization.
    112   size_t element_size = 1;                            // Bogus initialization.
    113   ElementsKind fixed_elements_kind = INT8_ELEMENTS;  // Bogus initialization.
    114   Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &fixed_elements_kind,
    115                                 &element_size);
    116   CHECK(holder->map()->elements_kind() == fixed_elements_kind);
    117 
    118   size_t byte_offset = 0;
    119   size_t byte_length = 0;
    120   CHECK(TryNumberToSize(*byte_offset_object, &byte_offset));
    121   CHECK(TryNumberToSize(*byte_length_object, &byte_length));
    122 
    123   if (maybe_buffer->IsJSArrayBuffer()) {
    124     Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
    125     size_t array_buffer_byte_length = NumberToSize(buffer->byte_length());
    126     CHECK(byte_offset <= array_buffer_byte_length);
    127     CHECK(array_buffer_byte_length - byte_offset >= byte_length);
    128   } else {
    129     CHECK(maybe_buffer->IsNull(isolate));
    130   }
    131 
    132   CHECK(byte_length % element_size == 0);
    133   size_t length = byte_length / element_size;
    134 
    135   if (length > static_cast<unsigned>(Smi::kMaxValue)) {
    136     THROW_NEW_ERROR_RETURN_FAILURE(
    137         isolate, NewRangeError(MessageTemplate::kInvalidTypedArrayLength));
    138   }
    139 
    140   // All checks are done, now we can modify objects.
    141 
    142   DCHECK_EQ(v8::ArrayBufferView::kInternalFieldCount,
    143             holder->GetInternalFieldCount());
    144   for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
    145     holder->SetInternalField(i, Smi::kZero);
    146   }
    147   Handle<Object> length_obj = isolate->factory()->NewNumberFromSize(length);
    148   holder->set_length(*length_obj);
    149   holder->set_byte_offset(*byte_offset_object);
    150   holder->set_byte_length(*byte_length_object);
    151 
    152   if (!maybe_buffer->IsNull(isolate)) {
    153     Handle<JSArrayBuffer> buffer = Handle<JSArrayBuffer>::cast(maybe_buffer);
    154     holder->set_buffer(*buffer);
    155 
    156     Handle<FixedTypedArrayBase> elements =
    157         isolate->factory()->NewFixedTypedArrayWithExternalPointer(
    158             static_cast<int>(length), array_type,
    159             static_cast<uint8_t*>(buffer->backing_store()) + byte_offset);
    160     holder->set_elements(*elements);
    161   } else {
    162     Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
    163     JSArrayBuffer::Setup(buffer, isolate, true, NULL, byte_length,
    164                          SharedFlag::kNotShared);
    165     holder->set_buffer(*buffer);
    166     Handle<FixedTypedArrayBase> elements =
    167         isolate->factory()->NewFixedTypedArray(static_cast<int>(length),
    168                                                array_type, initialize);
    169     holder->set_elements(*elements);
    170   }
    171   return isolate->heap()->undefined_value();
    172 }
    173 
    174 
    175 // Initializes a typed array from an array-like object.
    176 // If an array-like object happens to be a typed array of the same type,
    177 // initializes backing store using memove.
    178 //
    179 // Returns true if backing store was initialized or false otherwise.
    180 RUNTIME_FUNCTION(Runtime_TypedArrayInitializeFromArrayLike) {
    181   HandleScope scope(isolate);
    182   DCHECK_EQ(4, args.length());
    183   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
    184   CONVERT_SMI_ARG_CHECKED(arrayId, 1);
    185   CONVERT_ARG_HANDLE_CHECKED(Object, source, 2);
    186   CONVERT_NUMBER_ARG_HANDLE_CHECKED(length_obj, 3);
    187 
    188   CHECK(arrayId >= Runtime::ARRAY_ID_FIRST &&
    189         arrayId <= Runtime::ARRAY_ID_LAST);
    190 
    191   ExternalArrayType array_type = kExternalInt8Array;  // Bogus initialization.
    192   size_t element_size = 1;                            // Bogus initialization.
    193   ElementsKind fixed_elements_kind = INT8_ELEMENTS;  // Bogus initialization.
    194   Runtime::ArrayIdToTypeAndSize(arrayId, &array_type, &fixed_elements_kind,
    195                                 &element_size);
    196 
    197   CHECK(holder->map()->elements_kind() == fixed_elements_kind);
    198 
    199   Handle<JSArrayBuffer> buffer = isolate->factory()->NewJSArrayBuffer();
    200   size_t length = 0;
    201   if (source->IsJSTypedArray() &&
    202       JSTypedArray::cast(*source)->type() == array_type) {
    203     length = JSTypedArray::cast(*source)->length_value();
    204   } else {
    205     CHECK(TryNumberToSize(*length_obj, &length));
    206   }
    207 
    208   if ((length > static_cast<unsigned>(Smi::kMaxValue)) ||
    209       (length > (kMaxInt / element_size))) {
    210     THROW_NEW_ERROR_RETURN_FAILURE(
    211         isolate, NewRangeError(MessageTemplate::kInvalidTypedArrayLength));
    212   }
    213   size_t byte_length = length * element_size;
    214 
    215   DCHECK_EQ(v8::ArrayBufferView::kInternalFieldCount,
    216             holder->GetInternalFieldCount());
    217   for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) {
    218     holder->SetInternalField(i, Smi::kZero);
    219   }
    220 
    221   // NOTE: not initializing backing store.
    222   // We assume that the caller of this function will initialize holder
    223   // with the loop
    224   //      for(i = 0; i < length; i++) { holder[i] = source[i]; }
    225   // We assume that the caller of this function is always a typed array
    226   // constructor.
    227   // If source is a typed array, this loop will always run to completion,
    228   // so we are sure that the backing store will be initialized.
    229   // Otherwise, the indexing operation might throw, so the loop will not
    230   // run to completion and the typed array might remain partly initialized.
    231   // However we further assume that the caller of this function is a typed array
    232   // constructor, and the exception will propagate out of the constructor,
    233   // therefore uninitialized memory will not be accessible by a user program.
    234   //
    235   // TODO(dslomov): revise this once we support subclassing.
    236 
    237   if (!JSArrayBuffer::SetupAllocatingData(buffer, isolate, byte_length,
    238                                           false)) {
    239     THROW_NEW_ERROR_RETURN_FAILURE(
    240         isolate, NewRangeError(MessageTemplate::kInvalidArrayBufferLength));
    241   }
    242 
    243   holder->set_buffer(*buffer);
    244   holder->set_byte_offset(Smi::kZero);
    245   Handle<Object> byte_length_obj(
    246       isolate->factory()->NewNumberFromSize(byte_length));
    247   holder->set_byte_length(*byte_length_obj);
    248   length_obj = isolate->factory()->NewNumberFromSize(length);
    249   holder->set_length(*length_obj);
    250 
    251   Handle<FixedTypedArrayBase> elements =
    252       isolate->factory()->NewFixedTypedArrayWithExternalPointer(
    253           static_cast<int>(length), array_type,
    254           static_cast<uint8_t*>(buffer->backing_store()));
    255   holder->set_elements(*elements);
    256 
    257   if (source->IsJSTypedArray()) {
    258     Handle<JSTypedArray> typed_array(JSTypedArray::cast(*source));
    259 
    260     if (typed_array->type() == holder->type()) {
    261       uint8_t* backing_store =
    262           static_cast<uint8_t*>(typed_array->GetBuffer()->backing_store());
    263       size_t source_byte_offset = NumberToSize(typed_array->byte_offset());
    264       memcpy(buffer->backing_store(), backing_store + source_byte_offset,
    265              byte_length);
    266       return isolate->heap()->true_value();
    267     }
    268   }
    269 
    270   return isolate->heap()->false_value();
    271 }
    272 
    273 
    274 #define BUFFER_VIEW_GETTER(Type, getter, accessor)   \
    275   RUNTIME_FUNCTION(Runtime_##Type##Get##getter) {    \
    276     HandleScope scope(isolate);                      \
    277     DCHECK_EQ(1, args.length());                     \
    278     CONVERT_ARG_HANDLE_CHECKED(JS##Type, holder, 0); \
    279     return holder->accessor();                       \
    280   }
    281 
    282 BUFFER_VIEW_GETTER(ArrayBufferView, ByteLength, byte_length)
    283 BUFFER_VIEW_GETTER(ArrayBufferView, ByteOffset, byte_offset)
    284 BUFFER_VIEW_GETTER(TypedArray, Length, length)
    285 
    286 #undef BUFFER_VIEW_GETTER
    287 
    288 RUNTIME_FUNCTION(Runtime_TypedArrayGetBuffer) {
    289   HandleScope scope(isolate);
    290   DCHECK_EQ(1, args.length());
    291   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, holder, 0);
    292   return *holder->GetBuffer();
    293 }
    294 
    295 
    296 // Return codes for Runtime_TypedArraySetFastCases.
    297 // Should be synchronized with typedarray.js natives.
    298 enum TypedArraySetResultCodes {
    299   // Set from typed array of the same type.
    300   // This is processed by TypedArraySetFastCases
    301   TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE = 0,
    302   // Set from typed array of the different type, overlapping in memory.
    303   TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING = 1,
    304   // Set from typed array of the different type, non-overlapping.
    305   TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING = 2,
    306   // Set from non-typed array.
    307   TYPED_ARRAY_SET_NON_TYPED_ARRAY = 3
    308 };
    309 
    310 
    311 RUNTIME_FUNCTION(Runtime_TypedArraySetFastCases) {
    312   HandleScope scope(isolate);
    313   DCHECK_EQ(3, args.length());
    314   if (!args[0]->IsJSTypedArray()) {
    315     THROW_NEW_ERROR_RETURN_FAILURE(
    316         isolate, NewTypeError(MessageTemplate::kNotTypedArray));
    317   }
    318 
    319   if (!args[1]->IsJSTypedArray())
    320     return Smi::FromInt(TYPED_ARRAY_SET_NON_TYPED_ARRAY);
    321 
    322   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, target_obj, 0);
    323   CONVERT_ARG_HANDLE_CHECKED(JSTypedArray, source_obj, 1);
    324   CONVERT_NUMBER_ARG_HANDLE_CHECKED(offset_obj, 2);
    325 
    326   Handle<JSTypedArray> target(JSTypedArray::cast(*target_obj));
    327   Handle<JSTypedArray> source(JSTypedArray::cast(*source_obj));
    328   size_t offset = 0;
    329   CHECK(TryNumberToSize(*offset_obj, &offset));
    330   size_t target_length = target->length_value();
    331   size_t source_length = source->length_value();
    332   size_t target_byte_length = NumberToSize(target->byte_length());
    333   size_t source_byte_length = NumberToSize(source->byte_length());
    334   if (offset > target_length || offset + source_length > target_length ||
    335       offset + source_length < offset) {  // overflow
    336     THROW_NEW_ERROR_RETURN_FAILURE(
    337         isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge));
    338   }
    339 
    340   size_t target_offset = NumberToSize(target->byte_offset());
    341   size_t source_offset = NumberToSize(source->byte_offset());
    342   uint8_t* target_base =
    343       static_cast<uint8_t*>(target->GetBuffer()->backing_store()) +
    344       target_offset;
    345   uint8_t* source_base =
    346       static_cast<uint8_t*>(source->GetBuffer()->backing_store()) +
    347       source_offset;
    348 
    349   // Typed arrays of the same type: use memmove.
    350   if (target->type() == source->type()) {
    351     memmove(target_base + offset * target->element_size(), source_base,
    352             source_byte_length);
    353     return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_SAME_TYPE);
    354   }
    355 
    356   // Typed arrays of different types over the same backing store
    357   if ((source_base <= target_base &&
    358        source_base + source_byte_length > target_base) ||
    359       (target_base <= source_base &&
    360        target_base + target_byte_length > source_base)) {
    361     // We do not support overlapping ArrayBuffers
    362     DCHECK(target->GetBuffer()->backing_store() ==
    363            source->GetBuffer()->backing_store());
    364     return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_OVERLAPPING);
    365   } else {  // Non-overlapping typed arrays
    366     return Smi::FromInt(TYPED_ARRAY_SET_TYPED_ARRAY_NONOVERLAPPING);
    367   }
    368 }
    369 
    370 namespace {
    371 
    372 template <typename T>
    373 bool CompareNum(T x, T y) {
    374   if (x < y) {
    375     return true;
    376   } else if (x > y) {
    377     return false;
    378   } else if (!std::is_integral<T>::value) {
    379     double _x = x, _y = y;
    380     if (x == 0 && x == y) {
    381       /* -0.0 is less than +0.0 */
    382       return std::signbit(_x) && !std::signbit(_y);
    383     } else if (!std::isnan(_x) && std::isnan(_y)) {
    384       /* number is less than NaN */
    385       return true;
    386     }
    387   }
    388   return false;
    389 }
    390 
    391 }  // namespace
    392 
    393 RUNTIME_FUNCTION(Runtime_TypedArraySortFast) {
    394   HandleScope scope(isolate);
    395   DCHECK_EQ(1, args.length());
    396 
    397   CONVERT_ARG_HANDLE_CHECKED(Object, target_obj, 0);
    398 
    399   Handle<JSTypedArray> array;
    400   const char* method = "%TypedArray%.prototype.sort";
    401   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    402       isolate, array, JSTypedArray::Validate(isolate, target_obj, method));
    403 
    404   // This line can be removed when JSTypedArray::Validate throws
    405   // if array.[[ViewedArrayBuffer]] is neutered(v8:4648)
    406   if (V8_UNLIKELY(array->WasNeutered())) return *array;
    407 
    408   size_t length = array->length_value();
    409   if (length <= 1) return *array;
    410 
    411   Handle<FixedTypedArrayBase> elements(
    412       FixedTypedArrayBase::cast(array->elements()));
    413   switch (array->type()) {
    414 #define TYPED_ARRAY_SORT(Type, type, TYPE, ctype, size)     \
    415   case kExternal##Type##Array: {                            \
    416     ctype* data = static_cast<ctype*>(elements->DataPtr()); \
    417     if (kExternal##Type##Array == kExternalFloat64Array ||  \
    418         kExternal##Type##Array == kExternalFloat32Array)    \
    419       std::sort(data, data + length, CompareNum<ctype>);    \
    420     else                                                    \
    421       std::sort(data, data + length);                       \
    422     break;                                                  \
    423   }
    424 
    425     TYPED_ARRAYS(TYPED_ARRAY_SORT)
    426 #undef TYPED_ARRAY_SORT
    427   }
    428 
    429   return *array;
    430 }
    431 
    432 RUNTIME_FUNCTION(Runtime_TypedArrayMaxSizeInHeap) {
    433   DCHECK_EQ(0, args.length());
    434   DCHECK_OBJECT_SIZE(FLAG_typed_array_max_size_in_heap +
    435                      FixedTypedArrayBase::kDataOffset);
    436   return Smi::FromInt(FLAG_typed_array_max_size_in_heap);
    437 }
    438 
    439 
    440 RUNTIME_FUNCTION(Runtime_IsTypedArray) {
    441   HandleScope scope(isolate);
    442   DCHECK_EQ(1, args.length());
    443   return isolate->heap()->ToBoolean(args[0]->IsJSTypedArray());
    444 }
    445 
    446 
    447 RUNTIME_FUNCTION(Runtime_IsSharedTypedArray) {
    448   HandleScope scope(isolate);
    449   DCHECK_EQ(1, args.length());
    450   return isolate->heap()->ToBoolean(
    451       args[0]->IsJSTypedArray() &&
    452       JSTypedArray::cast(args[0])->GetBuffer()->is_shared());
    453 }
    454 
    455 
    456 RUNTIME_FUNCTION(Runtime_IsSharedIntegerTypedArray) {
    457   HandleScope scope(isolate);
    458   DCHECK_EQ(1, args.length());
    459   if (!args[0]->IsJSTypedArray()) {
    460     return isolate->heap()->false_value();
    461   }
    462 
    463   Handle<JSTypedArray> obj(JSTypedArray::cast(args[0]));
    464   return isolate->heap()->ToBoolean(obj->GetBuffer()->is_shared() &&
    465                                     obj->type() != kExternalFloat32Array &&
    466                                     obj->type() != kExternalFloat64Array &&
    467                                     obj->type() != kExternalUint8ClampedArray);
    468 }
    469 
    470 
    471 RUNTIME_FUNCTION(Runtime_IsSharedInteger32TypedArray) {
    472   HandleScope scope(isolate);
    473   DCHECK_EQ(1, args.length());
    474   if (!args[0]->IsJSTypedArray()) {
    475     return isolate->heap()->false_value();
    476   }
    477 
    478   Handle<JSTypedArray> obj(JSTypedArray::cast(args[0]));
    479   return isolate->heap()->ToBoolean(obj->GetBuffer()->is_shared() &&
    480                                     obj->type() == kExternalInt32Array);
    481 }
    482 
    483 }  // namespace internal
    484 }  // namespace v8
    485