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