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
17namespace v8 {
18namespace internal {
19
20RUNTIME_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
34static 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
54static 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
63RUNTIME_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
86RUNTIME_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
95RUNTIME_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
106RUNTIME_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.
123RUNTIME_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)
135RUNTIME_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?
158RUNTIME_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.
201RUNTIME_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
254namespace {
255
256Object* 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
342RUNTIME_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
358RUNTIME_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.
370RUNTIME_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
394RUNTIME_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
416RUNTIME_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
425RUNTIME_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
432RUNTIME_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
441RUNTIME_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
541RUNTIME_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
641RUNTIME_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
658RUNTIME_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