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/code-stubs.h"
      9 #include "src/conversions-inl.h"
     10 #include "src/elements.h"
     11 #include "src/factory.h"
     12 #include "src/isolate-inl.h"
     13 #include "src/keys.h"
     14 #include "src/messages.h"
     15 #include "src/prototype.h"
     16 
     17 namespace v8 {
     18 namespace internal {
     19 
     20 RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) {
     21   HandleScope scope(isolate);
     22   DCHECK_EQ(1, args.length());
     23   CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0);
     24   Object* length = prototype->length();
     25   CHECK(length->IsSmi());
     26   CHECK(Smi::cast(length)->value() == 0);
     27   CHECK(prototype->HasFastSmiOrObjectElements());
     28   // This is necessary to enable fast checks for absence of elements
     29   // on Array.prototype and below.
     30   prototype->set_elements(isolate->heap()->empty_fixed_array());
     31   return Smi::kZero;
     32 }
     33 
     34 static void InstallCode(
     35     Isolate* isolate, Handle<JSObject> holder, const char* name,
     36     Handle<Code> code, int argc = -1,
     37     BuiltinFunctionId id = static_cast<BuiltinFunctionId>(-1)) {
     38   Handle<String> key = isolate->factory()->InternalizeUtf8String(name);
     39   Handle<JSFunction> optimized =
     40       isolate->factory()->NewFunctionWithoutPrototype(key, code, true);
     41   if (argc < 0) {
     42     optimized->shared()->DontAdaptArguments();
     43   } else {
     44     optimized->shared()->set_internal_formal_parameter_count(argc);
     45   }
     46   if (id >= 0) {
     47     optimized->shared()->set_builtin_function_id(id);
     48   }
     49   optimized->shared()->set_language_mode(STRICT);
     50   optimized->shared()->set_native(true);
     51   JSObject::AddProperty(holder, key, optimized, NONE);
     52 }
     53 
     54 static void InstallBuiltin(
     55     Isolate* isolate, Handle<JSObject> holder, const char* name,
     56     Builtins::Name builtin_name, int argc = -1,
     57     BuiltinFunctionId id = static_cast<BuiltinFunctionId>(-1)) {
     58   InstallCode(isolate, holder, name,
     59               handle(isolate->builtins()->builtin(builtin_name), isolate), argc,
     60               id);
     61 }
     62 
     63 RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
     64   HandleScope scope(isolate);
     65   DCHECK_EQ(0, args.length());
     66   Handle<JSObject> holder =
     67       isolate->factory()->NewJSObject(isolate->object_function());
     68 
     69   InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop);
     70   InstallBuiltin(isolate, holder, "push", Builtins::kFastArrayPush);
     71   InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift);
     72   InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
     73   InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
     74   InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
     75   InstallBuiltin(isolate, holder, "includes", Builtins::kArrayIncludes, 2);
     76   InstallBuiltin(isolate, holder, "indexOf", Builtins::kArrayIndexOf, 2);
     77   InstallBuiltin(isolate, holder, "keys", Builtins::kArrayPrototypeKeys, 0,
     78                  kArrayKeys);
     79   InstallBuiltin(isolate, holder, "values", Builtins::kArrayPrototypeValues, 0,
     80                  kArrayValues);
     81   InstallBuiltin(isolate, holder, "entries", Builtins::kArrayPrototypeEntries,
     82                  0, kArrayEntries);
     83   return *holder;
     84 }
     85 
     86 RUNTIME_FUNCTION(Runtime_FixedArrayGet) {
     87   SealHandleScope shs(isolate);
     88   DCHECK_EQ(2, args.length());
     89   CONVERT_ARG_CHECKED(FixedArray, object, 0);
     90   CONVERT_SMI_ARG_CHECKED(index, 1);
     91   return object->get(index);
     92 }
     93 
     94 
     95 RUNTIME_FUNCTION(Runtime_FixedArraySet) {
     96   SealHandleScope shs(isolate);
     97   DCHECK_EQ(3, args.length());
     98   CONVERT_ARG_CHECKED(FixedArray, object, 0);
     99   CONVERT_SMI_ARG_CHECKED(index, 1);
    100   CONVERT_ARG_CHECKED(Object, value, 2);
    101   object->set(index, value);
    102   return isolate->heap()->undefined_value();
    103 }
    104 
    105 
    106 RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
    107   HandleScope scope(isolate);
    108   DCHECK_EQ(2, args.length());
    109   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
    110   CONVERT_ARG_HANDLE_CHECKED(Map, to_map, 1);
    111   ElementsKind to_kind = to_map->elements_kind();
    112   ElementsAccessor::ForKind(to_kind)->TransitionElementsKind(object, to_map);
    113   return *object;
    114 }
    115 
    116 
    117 // Moves all own elements of an object, that are below a limit, to positions
    118 // starting at zero. All undefined values are placed after non-undefined values,
    119 // and are followed by non-existing element. Does not change the length
    120 // property.
    121 // Returns the number of non-undefined elements collected.
    122 // Returns -1 if hole removal is not supported by this method.
    123 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) {
    124   HandleScope scope(isolate);
    125   DCHECK_EQ(2, args.length());
    126   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
    127   CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
    128   if (object->IsJSProxy()) return Smi::FromInt(-1);
    129   return *JSObject::PrepareElementsForSort(Handle<JSObject>::cast(object),
    130                                            limit);
    131 }
    132 
    133 
    134 // Move contents of argument 0 (an array) to argument 1 (an array)
    135 RUNTIME_FUNCTION(Runtime_MoveArrayContents) {
    136   HandleScope scope(isolate);
    137   DCHECK_EQ(2, args.length());
    138   CONVERT_ARG_HANDLE_CHECKED(JSArray, from, 0);
    139   CONVERT_ARG_HANDLE_CHECKED(JSArray, to, 1);
    140   JSObject::ValidateElements(from);
    141   JSObject::ValidateElements(to);
    142 
    143   Handle<FixedArrayBase> new_elements(from->elements());
    144   ElementsKind from_kind = from->GetElementsKind();
    145   Handle<Map> new_map = JSObject::GetElementsTransitionMap(to, from_kind);
    146   JSObject::SetMapAndElements(to, new_map, new_elements);
    147   to->set_length(from->length());
    148 
    149   JSObject::ResetElements(from);
    150   from->set_length(Smi::kZero);
    151 
    152   JSObject::ValidateElements(to);
    153   return *to;
    154 }
    155 
    156 
    157 // How many elements does this object/array have?
    158 RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements) {
    159   HandleScope scope(isolate);
    160   DCHECK_EQ(1, args.length());
    161   CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
    162   Handle<FixedArrayBase> elements(array->elements(), isolate);
    163   SealHandleScope shs(isolate);
    164   if (elements->IsDictionary()) {
    165     int result =
    166         Handle<SeededNumberDictionary>::cast(elements)->NumberOfElements();
    167     return Smi::FromInt(result);
    168   } else {
    169     DCHECK(array->length()->IsSmi());
    170     // For packed elements, we know the exact number of elements
    171     int length = elements->length();
    172     ElementsKind kind = array->GetElementsKind();
    173     if (IsFastPackedElementsKind(kind)) {
    174       return Smi::FromInt(length);
    175     }
    176     // For holey elements, take samples from the buffer checking for holes
    177     // to generate the estimate.
    178     const int kNumberOfHoleCheckSamples = 97;
    179     int increment = (length < kNumberOfHoleCheckSamples)
    180                         ? 1
    181                         : static_cast<int>(length / kNumberOfHoleCheckSamples);
    182     ElementsAccessor* accessor = array->GetElementsAccessor();
    183     int holes = 0;
    184     for (int i = 0; i < length; i += increment) {
    185       if (!accessor->HasElement(array, i, elements)) {
    186         ++holes;
    187       }
    188     }
    189     int estimate = static_cast<int>((kNumberOfHoleCheckSamples - holes) /
    190                                     kNumberOfHoleCheckSamples * length);
    191     return Smi::FromInt(estimate);
    192   }
    193 }
    194 
    195 
    196 // Returns an array that tells you where in the [0, length) interval an array
    197 // might have elements.  Can either return an array of keys (positive integers
    198 // or undefined) or a number representing the positive length of an interval
    199 // starting at index 0.
    200 // Intervals can span over some keys that are not in the object.
    201 RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
    202   HandleScope scope(isolate);
    203   DCHECK_EQ(2, args.length());
    204   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
    205   CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
    206   ElementsKind kind = array->GetElementsKind();
    207 
    208   if (IsFastElementsKind(kind) || IsFixedTypedArrayElementsKind(kind)) {
    209     uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
    210     return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
    211   }
    212 
    213   if (kind == FAST_STRING_WRAPPER_ELEMENTS) {
    214     int string_length =
    215         String::cast(Handle<JSValue>::cast(array)->value())->length();
    216     int backing_store_length = array->elements()->length();
    217     return *isolate->factory()->NewNumberFromUint(
    218         Min(length,
    219             static_cast<uint32_t>(Max(string_length, backing_store_length))));
    220   }
    221 
    222   KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
    223                              ALL_PROPERTIES);
    224   for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
    225        !iter.IsAtEnd(); iter.Advance()) {
    226     if (PrototypeIterator::GetCurrent(iter)->IsJSProxy() ||
    227         PrototypeIterator::GetCurrent<JSObject>(iter)
    228             ->HasIndexedInterceptor()) {
    229       // Bail out if we find a proxy or interceptor, likely not worth
    230       // collecting keys in that case.
    231       return *isolate->factory()->NewNumberFromUint(length);
    232     }
    233     Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
    234     accumulator.CollectOwnElementIndices(array, current);
    235   }
    236   // Erase any keys >= length.
    237   Handle<FixedArray> keys =
    238       accumulator.GetKeys(GetKeysConversion::kKeepNumbers);
    239   int j = 0;
    240   for (int i = 0; i < keys->length(); i++) {
    241     if (NumberToUint32(keys->get(i)) >= length) continue;
    242     if (i != j) keys->set(j, keys->get(i));
    243     j++;
    244   }
    245 
    246   if (j != keys->length()) {
    247     isolate->heap()->RightTrimFixedArray(*keys, keys->length() - j);
    248   }
    249 
    250   return *isolate->factory()->NewJSArrayWithElements(keys);
    251 }
    252 
    253 
    254 namespace {
    255 
    256 Object* ArrayConstructorCommon(Isolate* isolate, Handle<JSFunction> constructor,
    257                                Handle<JSReceiver> new_target,
    258                                Handle<AllocationSite> site,
    259                                Arguments* caller_args) {
    260   Factory* factory = isolate->factory();
    261 
    262   // If called through new, new.target can be:
    263   // - a subclass of constructor,
    264   // - a proxy wrapper around constructor, or
    265   // - the constructor itself.
    266   // If called through Reflect.construct, it's guaranteed to be a constructor by
    267   // REFLECT_CONSTRUCT_PREPARE.
    268   DCHECK(new_target->IsConstructor());
    269 
    270   bool holey = false;
    271   bool can_use_type_feedback = !site.is_null();
    272   bool can_inline_array_constructor = true;
    273   if (caller_args->length() == 1) {
    274     Handle<Object> argument_one = caller_args->at<Object>(0);
    275     if (argument_one->IsSmi()) {
    276       int value = Handle<Smi>::cast(argument_one)->value();
    277       if (value < 0 ||
    278           JSArray::SetLengthWouldNormalize(isolate->heap(), value)) {
    279         // the array is a dictionary in this case.
    280         can_use_type_feedback = false;
    281       } else if (value != 0) {
    282         holey = true;
    283         if (value >= JSArray::kInitialMaxFastElementArray) {
    284           can_inline_array_constructor = false;
    285         }
    286       }
    287     } else {
    288       // Non-smi length argument produces a dictionary
    289       can_use_type_feedback = false;
    290     }
    291   }
    292 
    293   Handle<Map> initial_map;
    294   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    295       isolate, initial_map,
    296       JSFunction::GetDerivedMap(isolate, constructor, new_target));
    297 
    298   ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind()
    299                                                : initial_map->elements_kind();
    300   if (holey && !IsFastHoleyElementsKind(to_kind)) {
    301     to_kind = GetHoleyElementsKind(to_kind);
    302     // Update the allocation site info to reflect the advice alteration.
    303     if (!site.is_null()) site->SetElementsKind(to_kind);
    304   }
    305 
    306   // We should allocate with an initial map that reflects the allocation site
    307   // advice. Therefore we use AllocateJSObjectFromMap instead of passing
    308   // the constructor.
    309   if (to_kind != initial_map->elements_kind()) {
    310     initial_map = Map::AsElementsKind(initial_map, to_kind);
    311   }
    312 
    313   // If we don't care to track arrays of to_kind ElementsKind, then
    314   // don't emit a memento for them.
    315   Handle<AllocationSite> allocation_site;
    316   if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) {
    317     allocation_site = site;
    318   }
    319 
    320   Handle<JSArray> array = Handle<JSArray>::cast(
    321       factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site));
    322 
    323   factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
    324 
    325   ElementsKind old_kind = array->GetElementsKind();
    326   RETURN_FAILURE_ON_EXCEPTION(
    327       isolate, ArrayConstructInitializeElements(array, caller_args));
    328   if (!site.is_null() &&
    329       (old_kind != array->GetElementsKind() || !can_use_type_feedback ||
    330        !can_inline_array_constructor)) {
    331     // The arguments passed in caused a transition. This kind of complexity
    332     // can't be dealt with in the inlined hydrogen array constructor case.
    333     // We must mark the allocationsite as un-inlinable.
    334     site->SetDoNotInlineCall();
    335   }
    336 
    337   return *array;
    338 }
    339 
    340 }  // namespace
    341 
    342 RUNTIME_FUNCTION(Runtime_NewArray) {
    343   HandleScope scope(isolate);
    344   DCHECK_LE(3, args.length());
    345   int const argc = args.length() - 3;
    346   // TODO(bmeurer): Remove this Arguments nonsense.
    347   Arguments argv(argc, args.arguments() - 1);
    348   CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
    349   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1);
    350   CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2);
    351   // TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite.
    352   Handle<AllocationSite> site = type_info->IsAllocationSite()
    353                                     ? Handle<AllocationSite>::cast(type_info)
    354                                     : Handle<AllocationSite>::null();
    355   return ArrayConstructorCommon(isolate, constructor, new_target, site, &argv);
    356 }
    357 
    358 RUNTIME_FUNCTION(Runtime_NormalizeElements) {
    359   HandleScope scope(isolate);
    360   DCHECK_EQ(1, args.length());
    361   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
    362   CHECK(!array->HasFixedTypedArrayElements());
    363   CHECK(!array->IsJSGlobalProxy());
    364   JSObject::NormalizeElements(array);
    365   return *array;
    366 }
    367 
    368 
    369 // GrowArrayElements returns a sentinel Smi if the object was normalized.
    370 RUNTIME_FUNCTION(Runtime_GrowArrayElements) {
    371   HandleScope scope(isolate);
    372   DCHECK_EQ(2, args.length());
    373   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
    374   CONVERT_NUMBER_CHECKED(int, key, Int32, args[1]);
    375 
    376   if (key < 0) {
    377     return object->elements();
    378   }
    379 
    380   uint32_t capacity = static_cast<uint32_t>(object->elements()->length());
    381   uint32_t index = static_cast<uint32_t>(key);
    382 
    383   if (index >= capacity) {
    384     if (!object->GetElementsAccessor()->GrowCapacity(object, index)) {
    385       return Smi::kZero;
    386     }
    387   }
    388 
    389   // On success, return the fixed array elements.
    390   return object->elements();
    391 }
    392 
    393 
    394 RUNTIME_FUNCTION(Runtime_HasComplexElements) {
    395   HandleScope scope(isolate);
    396   DCHECK_EQ(1, args.length());
    397   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
    398   for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
    399        !iter.IsAtEnd(); iter.Advance()) {
    400     if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
    401       return isolate->heap()->true_value();
    402     }
    403     Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
    404     if (current->HasIndexedInterceptor()) {
    405       return isolate->heap()->true_value();
    406     }
    407     if (!current->HasDictionaryElements()) continue;
    408     if (current->element_dictionary()->HasComplexElements()) {
    409       return isolate->heap()->true_value();
    410     }
    411   }
    412   return isolate->heap()->false_value();
    413 }
    414 
    415 // ES6 22.1.2.2 Array.isArray
    416 RUNTIME_FUNCTION(Runtime_ArrayIsArray) {
    417   HandleScope shs(isolate);
    418   DCHECK_EQ(1, args.length());
    419   CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
    420   Maybe<bool> result = Object::IsArray(object);
    421   MAYBE_RETURN(result, isolate->heap()->exception());
    422   return isolate->heap()->ToBoolean(result.FromJust());
    423 }
    424 
    425 RUNTIME_FUNCTION(Runtime_IsArray) {
    426   SealHandleScope shs(isolate);
    427   DCHECK_EQ(1, args.length());
    428   CONVERT_ARG_CHECKED(Object, obj, 0);
    429   return isolate->heap()->ToBoolean(obj->IsJSArray());
    430 }
    431 
    432 RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
    433   HandleScope scope(isolate);
    434   DCHECK_EQ(1, args.length());
    435   CONVERT_ARG_HANDLE_CHECKED(Object, original_array, 0);
    436   RETURN_RESULT_OR_FAILURE(
    437       isolate, Object::ArraySpeciesConstructor(isolate, original_array));
    438 }
    439 
    440 // ES7 22.1.3.11 Array.prototype.includes
    441 RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
    442   HandleScope shs(isolate);
    443   DCHECK_EQ(3, args.length());
    444   CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
    445   CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
    446 
    447   // Let O be ? ToObject(this value).
    448   Handle<JSReceiver> object;
    449   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    450       isolate, object, Object::ToObject(isolate, handle(args[0], isolate)));
    451 
    452   // Let len be ? ToLength(? Get(O, "length")).
    453   int64_t len;
    454   {
    455     if (object->map()->instance_type() == JS_ARRAY_TYPE) {
    456       uint32_t len32 = 0;
    457       bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32);
    458       DCHECK(success);
    459       USE(success);
    460       len = len32;
    461     } else {
    462       Handle<Object> len_;
    463       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    464           isolate, len_,
    465           Object::GetProperty(object, isolate->factory()->length_string()));
    466 
    467       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
    468                                          Object::ToLength(isolate, len_));
    469       len = static_cast<int64_t>(len_->Number());
    470       DCHECK_EQ(len, len_->Number());
    471     }
    472   }
    473 
    474   if (len == 0) return isolate->heap()->false_value();
    475 
    476   // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
    477   // produces the value 0.)
    478   int64_t index = 0;
    479   if (!from_index->IsUndefined(isolate)) {
    480     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
    481                                        Object::ToInteger(isolate, from_index));
    482 
    483     if (V8_LIKELY(from_index->IsSmi())) {
    484       int start_from = Smi::cast(*from_index)->value();
    485       if (start_from < 0) {
    486         index = std::max<int64_t>(len + start_from, 0);
    487       } else {
    488         index = start_from;
    489       }
    490     } else {
    491       DCHECK(from_index->IsHeapNumber());
    492       double start_from = from_index->Number();
    493       if (start_from >= len) return isolate->heap()->false_value();
    494       if (V8_LIKELY(std::isfinite(start_from))) {
    495         if (start_from < 0) {
    496           index = static_cast<int64_t>(std::max<double>(start_from + len, 0));
    497         } else {
    498           index = start_from;
    499         }
    500       }
    501     }
    502 
    503     DCHECK_GE(index, 0);
    504   }
    505 
    506   // If the receiver is not a special receiver type, and the length is a valid
    507   // element index, perform fast operation tailored to specific ElementsKinds.
    508   if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
    509       JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
    510     Handle<JSObject> obj = Handle<JSObject>::cast(object);
    511     ElementsAccessor* elements = obj->GetElementsAccessor();
    512     Maybe<bool> result = elements->IncludesValue(isolate, obj, search_element,
    513                                                  static_cast<uint32_t>(index),
    514                                                  static_cast<uint32_t>(len));
    515     MAYBE_RETURN(result, isolate->heap()->exception());
    516     return *isolate->factory()->ToBoolean(result.FromJust());
    517   }
    518 
    519   // Otherwise, perform slow lookups for special receiver types
    520   for (; index < len; ++index) {
    521     // Let elementK be the result of ? Get(O, ! ToString(k)).
    522     Handle<Object> element_k;
    523     {
    524       Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index);
    525       bool success;
    526       LookupIterator it = LookupIterator::PropertyOrElement(
    527           isolate, object, index_obj, &success);
    528       DCHECK(success);
    529       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
    530                                          Object::GetProperty(&it));
    531     }
    532 
    533     // If SameValueZero(searchElement, elementK) is true, return true.
    534     if (search_element->SameValueZero(*element_k)) {
    535       return isolate->heap()->true_value();
    536     }
    537   }
    538   return isolate->heap()->false_value();
    539 }
    540 
    541 RUNTIME_FUNCTION(Runtime_ArrayIndexOf) {
    542   HandleScope shs(isolate);
    543   DCHECK_EQ(3, args.length());
    544   CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
    545   CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
    546 
    547   // Let O be ? ToObject(this value).
    548   Handle<Object> receiver_obj = args.at(0);
    549   if (receiver_obj->IsNullOrUndefined(isolate)) {
    550     THROW_NEW_ERROR_RETURN_FAILURE(
    551         isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
    552                               isolate->factory()->NewStringFromAsciiChecked(
    553                                   "Array.prototype.indexOf")));
    554   }
    555   Handle<JSReceiver> object;
    556   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, object,
    557                                      Object::ToObject(isolate, args.at(0)));
    558 
    559   // Let len be ? ToLength(? Get(O, "length")).
    560   int64_t len;
    561   {
    562     if (object->IsJSArray()) {
    563       uint32_t len32 = 0;
    564       bool success = JSArray::cast(*object)->length()->ToArrayLength(&len32);
    565       DCHECK(success);
    566       USE(success);
    567       len = len32;
    568     } else {
    569       Handle<Object> len_;
    570       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    571           isolate, len_,
    572           Object::GetProperty(object, isolate->factory()->length_string()));
    573 
    574       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
    575                                          Object::ToLength(isolate, len_));
    576       len = static_cast<int64_t>(len_->Number());
    577       DCHECK_EQ(len, len_->Number());
    578     }
    579   }
    580 
    581   if (len == 0) return Smi::FromInt(-1);
    582 
    583   // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
    584   // produces the value 0.)
    585   int64_t start_from;
    586   {
    587     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
    588                                        Object::ToInteger(isolate, from_index));
    589     double fp = from_index->Number();
    590     if (fp > len) return Smi::FromInt(-1);
    591     start_from = static_cast<int64_t>(fp);
    592   }
    593 
    594   int64_t index;
    595   if (start_from >= 0) {
    596     index = start_from;
    597   } else {
    598     index = len + start_from;
    599     if (index < 0) {
    600       index = 0;
    601     }
    602   }
    603 
    604   // If the receiver is not a special receiver type, and the length is a valid
    605   // element index, perform fast operation tailored to specific ElementsKinds.
    606   if (!object->map()->IsSpecialReceiverMap() && len < kMaxUInt32 &&
    607       JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
    608     Handle<JSObject> obj = Handle<JSObject>::cast(object);
    609     ElementsAccessor* elements = obj->GetElementsAccessor();
    610     Maybe<int64_t> result = elements->IndexOfValue(isolate, obj, search_element,
    611                                                    static_cast<uint32_t>(index),
    612                                                    static_cast<uint32_t>(len));
    613     MAYBE_RETURN(result, isolate->heap()->exception());
    614     return *isolate->factory()->NewNumberFromInt64(result.FromJust());
    615   }
    616 
    617   // Otherwise, perform slow lookups for special receiver types
    618   for (; index < len; ++index) {
    619     // Let elementK be the result of ? Get(O, ! ToString(k)).
    620     Handle<Object> element_k;
    621     {
    622       Handle<Object> index_obj = isolate->factory()->NewNumberFromInt64(index);
    623       bool success;
    624       LookupIterator it = LookupIterator::PropertyOrElement(
    625           isolate, object, index_obj, &success);
    626       DCHECK(success);
    627       if (!JSReceiver::HasProperty(&it).FromJust()) {
    628         continue;
    629       }
    630       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
    631                                          Object::GetProperty(&it));
    632       if (search_element->StrictEquals(*element_k)) {
    633         return *index_obj;
    634       }
    635     }
    636   }
    637   return Smi::FromInt(-1);
    638 }
    639 
    640 
    641 RUNTIME_FUNCTION(Runtime_SpreadIterablePrepare) {
    642   HandleScope scope(isolate);
    643   DCHECK_EQ(1, args.length());
    644   CONVERT_ARG_HANDLE_CHECKED(Object, spread, 0);
    645 
    646   // Iterate over the spread if we need to.
    647   if (spread->IterationHasObservableEffects()) {
    648     Handle<JSFunction> spread_iterable_function = isolate->spread_iterable();
    649     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    650         isolate, spread,
    651         Execution::Call(isolate, spread_iterable_function,
    652                         isolate->factory()->undefined_value(), 1, &spread));
    653   }
    654 
    655   return *spread;
    656 }
    657 
    658 RUNTIME_FUNCTION(Runtime_SpreadIterableFixed) {
    659   HandleScope scope(isolate);
    660   DCHECK_EQ(1, args.length());
    661   CONVERT_ARG_HANDLE_CHECKED(Object, spread, 0);
    662 
    663   // The caller should check if proper iteration is necessary.
    664   Handle<JSFunction> spread_iterable_function = isolate->spread_iterable();
    665   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    666       isolate, spread,
    667       Execution::Call(isolate, spread_iterable_function,
    668                       isolate->factory()->undefined_value(), 1, &spread));
    669 
    670   // Create a new FixedArray and put the result of the spread into it.
    671   Handle<JSArray> spread_array = Handle<JSArray>::cast(spread);
    672   uint32_t spread_length;
    673   CHECK(spread_array->length()->ToArrayIndex(&spread_length));
    674 
    675   Handle<FixedArray> result = isolate->factory()->NewFixedArray(spread_length);
    676   ElementsAccessor* accessor = spread_array->GetElementsAccessor();
    677   for (uint32_t i = 0; i < spread_length; i++) {
    678     DCHECK(accessor->HasElement(spread_array, i));
    679     Handle<Object> element = accessor->Get(spread_array, i);
    680     result->set(i, *element);
    681   }
    682 
    683   return *result;
    684 }
    685 
    686 }  // namespace internal
    687 }  // namespace v8
    688