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_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 
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, 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 
InstallBuiltin(Isolate * isolate,Handle<JSObject> holder,const char * name,Builtins::Name builtin_name,int argc=-1,BuiltinFunctionId id=static_cast<BuiltinFunctionId> (-1))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 
RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions)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 
RUNTIME_FUNCTION(Runtime_FixedArrayGet)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 
RUNTIME_FUNCTION(Runtime_FixedArraySet)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 
RUNTIME_FUNCTION(Runtime_TransitionElementsKind)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.
RUNTIME_FUNCTION(Runtime_RemoveArrayHoles)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)
RUNTIME_FUNCTION(Runtime_MoveArrayContents)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?
RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements)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.
RUNTIME_FUNCTION(Runtime_GetArrayKeys)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 
ArrayConstructorCommon(Isolate * isolate,Handle<JSFunction> constructor,Handle<JSReceiver> new_target,Handle<AllocationSite> site,Arguments * caller_args)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 
RUNTIME_FUNCTION(Runtime_NewArray)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 
RUNTIME_FUNCTION(Runtime_NormalizeElements)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.
RUNTIME_FUNCTION(Runtime_GrowArrayElements)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 
RUNTIME_FUNCTION(Runtime_HasComplexElements)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
RUNTIME_FUNCTION(Runtime_ArrayIsArray)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 
RUNTIME_FUNCTION(Runtime_IsArray)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 
RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor)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
RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow)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 
RUNTIME_FUNCTION(Runtime_ArrayIndexOf)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 
RUNTIME_FUNCTION(Runtime_SpreadIterablePrepare)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 
RUNTIME_FUNCTION(Runtime_SpreadIterableFixed)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