LCOV - code coverage report
Current view: top level - src/builtins - builtins-array.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 353 404 87.4 %
Date: 2019-03-21 Functions: 39 47 83.0 %

          Line data    Source code
       1             : // Copyright 2016 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/builtins/builtins-utils-inl.h"
       6             : #include "src/builtins/builtins.h"
       7             : #include "src/code-factory.h"
       8             : #include "src/contexts.h"
       9             : #include "src/counters.h"
      10             : #include "src/debug/debug.h"
      11             : #include "src/elements-inl.h"
      12             : #include "src/global-handles.h"
      13             : #include "src/isolate.h"
      14             : #include "src/lookup.h"
      15             : #include "src/objects-inl.h"
      16             : #include "src/objects/hash-table-inl.h"
      17             : #include "src/objects/js-array-inl.h"
      18             : #include "src/objects/smi.h"
      19             : #include "src/prototype.h"
      20             : 
      21             : namespace v8 {
      22             : namespace internal {
      23             : 
      24             : namespace {
      25             : 
      26             : inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate,
      27             :                                               JSArray receiver) {
      28     1156991 :   return JSObject::PrototypeHasNoElements(isolate, receiver);
      29             : }
      30             : 
      31      595887 : inline bool HasSimpleElements(JSObject current) {
      32     1191765 :   return !current->map()->IsCustomElementsReceiverMap() &&
      33     1191765 :          !current->GetElementsAccessor()->HasAccessors(current);
      34             : }
      35             : 
      36      574956 : inline bool HasOnlySimpleReceiverElements(Isolate* isolate, JSObject receiver) {
      37             :   // Check that we have no accessors on the receiver's elements.
      38      574956 :   if (!HasSimpleElements(receiver)) return false;
      39      574776 :   return JSObject::PrototypeHasNoElements(isolate, receiver);
      40             : }
      41             : 
      42        6845 : inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver receiver) {
      43             :   DisallowHeapAllocation no_gc;
      44             :   PrototypeIterator iter(isolate, receiver, kStartAtReceiver);
      45       48311 :   for (; !iter.IsAtEnd(); iter.Advance()) {
      46       21129 :     if (iter.GetCurrent()->IsJSProxy()) return false;
      47       20931 :     JSObject current = iter.GetCurrent<JSObject>();
      48       20931 :     if (!HasSimpleElements(current)) return false;
      49             :   }
      50             :   return true;
      51             : }
      52             : 
      53             : // This method may transition the elements kind of the JSArray once, to make
      54             : // sure that all elements provided as arguments in the specified range can be
      55             : // added without further elements kinds transitions.
      56      919862 : void MatchArrayElementsKindToArguments(Isolate* isolate, Handle<JSArray> array,
      57             :                                        BuiltinArguments* args,
      58             :                                        int first_arg_index, int num_arguments) {
      59      919862 :   int args_length = args->length();
      60      921217 :   if (first_arg_index >= args_length) return;
      61             : 
      62      919770 :   ElementsKind origin_kind = array->GetElementsKind();
      63             : 
      64             :   // We do not need to transition for PACKED/HOLEY_ELEMENTS.
      65      919770 :   if (IsObjectElementsKind(origin_kind)) return;
      66             : 
      67             :   ElementsKind target_kind = origin_kind;
      68             :   {
      69             :     DisallowHeapAllocation no_gc;
      70     1837014 :     int last_arg_index = std::min(first_arg_index + num_arguments, args_length);
      71     2754415 :     for (int i = first_arg_index; i < last_arg_index; i++) {
      72             :       Object arg = (*args)[i];
      73      918571 :       if (arg->IsHeapObject()) {
      74         818 :         if (arg->IsHeapNumber()) {
      75             :           target_kind = PACKED_DOUBLE_ELEMENTS;
      76             :         } else {
      77             :           target_kind = PACKED_ELEMENTS;
      78             :           break;
      79             :         }
      80             :       }
      81             :     }
      82             :   }
      83      918507 :   if (target_kind != origin_kind) {
      84             :     // Use a short-lived HandleScope to avoid creating several copies of the
      85             :     // elements handle which would cause issues when left-trimming later-on.
      86             :     HandleScope scope(isolate);
      87        1480 :     JSObject::TransitionElementsKind(array, target_kind);
      88             :   }
      89             : }
      90             : 
      91             : // Returns |false| if not applicable.
      92             : // TODO(szuend): Refactor this function because it is getting hard to
      93             : //               understand what each call-site actually checks.
      94             : V8_WARN_UNUSED_RESULT
      95     1162729 : inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate,
      96             :                                                   Handle<Object> receiver,
      97             :                                                   BuiltinArguments* args,
      98             :                                                   int first_arg_index,
      99             :                                                   int num_arguments) {
     100     1162729 :   if (!receiver->IsJSArray()) return false;
     101             :   Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     102     1159606 :   ElementsKind origin_kind = array->GetElementsKind();
     103     1159606 :   if (IsDictionaryElementsKind(origin_kind)) return false;
     104     1157272 :   if (!array->map()->is_extensible()) return false;
     105     1157272 :   if (args == nullptr) return true;
     106             : 
     107             :   // If there may be elements accessors in the prototype chain, the fast path
     108             :   // cannot be used if there arguments to add to the array.
     109      919763 :   if (!IsJSArrayFastElementMovingAllowed(isolate, *array)) return false;
     110             : 
     111             :   // Adding elements to the array prototype would break code that makes sure
     112             :   // it has no elements. Handle that elsewhere.
     113      914294 :   if (isolate->IsAnyInitialArrayPrototype(array)) return false;
     114             : 
     115             :   // Need to ensure that the arguments passed in args can be contained in
     116             :   // the array.
     117             :   MatchArrayElementsKindToArguments(isolate, array, args, first_arg_index,
     118      913994 :                                     num_arguments);
     119      913994 :   return true;
     120             : }
     121             : 
     122             : // If |index| is Undefined, returns init_if_undefined.
     123             : // If |index| is negative, returns length + index.
     124             : // If |index| is positive, returns index.
     125             : // Returned value is guaranteed to be in the interval of [0, length].
     126       14768 : V8_WARN_UNUSED_RESULT Maybe<double> GetRelativeIndex(Isolate* isolate,
     127             :                                                      double length,
     128             :                                                      Handle<Object> index,
     129             :                                                      double init_if_undefined) {
     130       14768 :   double relative_index = init_if_undefined;
     131       14768 :   if (!index->IsUndefined()) {
     132             :     Handle<Object> relative_index_obj;
     133         639 :     ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, relative_index_obj,
     134             :                                      Object::ToInteger(isolate, index),
     135             :                                      Nothing<double>());
     136         639 :     relative_index = relative_index_obj->Number();
     137             :   }
     138             : 
     139       14768 :   if (relative_index < 0) {
     140         144 :     return Just(std::max(length + relative_index, 0.0));
     141             :   }
     142             : 
     143             :   return Just(std::min(relative_index, length));
     144             : }
     145             : 
     146             : // Returns "length", has "fast-path" for JSArrays.
     147      189558 : V8_WARN_UNUSED_RESULT Maybe<double> GetLengthProperty(
     148             :     Isolate* isolate, Handle<JSReceiver> receiver) {
     149      189558 :   if (receiver->IsJSArray()) {
     150             :     Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     151             :     double length = array->length()->Number();
     152             :     DCHECK(0 <= length && length <= kMaxSafeInteger);
     153             : 
     154             :     return Just(length);
     155             :   }
     156             : 
     157             :   Handle<Object> raw_length_number;
     158         486 :   ASSIGN_RETURN_ON_EXCEPTION_VALUE(
     159             :       isolate, raw_length_number,
     160             :       Object::GetLengthFromArrayLike(isolate, receiver), Nothing<double>());
     161             :   return Just(raw_length_number->Number());
     162             : }
     163             : 
     164             : // Set "length" property, has "fast-path" for JSArrays.
     165             : // Returns Nothing if something went wrong.
     166           0 : V8_WARN_UNUSED_RESULT MaybeHandle<Object> SetLengthProperty(
     167             :     Isolate* isolate, Handle<JSReceiver> receiver, double length) {
     168           0 :   if (receiver->IsJSArray()) {
     169           0 :     Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     170           0 :     if (!JSArray::HasReadOnlyLength(array)) {
     171             :       DCHECK_LE(length, kMaxUInt32);
     172           0 :       JSArray::SetLength(array, static_cast<uint32_t>(length));
     173           0 :       return receiver;
     174             :     }
     175             :   }
     176             : 
     177             :   return Object::SetProperty(
     178             :       isolate, receiver, isolate->factory()->length_string(),
     179             :       isolate->factory()->NewNumber(length), StoreOrigin::kMaybeKeyed,
     180           0 :       Just(ShouldThrow::kThrowOnError));
     181             : }
     182             : 
     183         377 : V8_WARN_UNUSED_RESULT Object GenericArrayFill(Isolate* isolate,
     184             :                                               Handle<JSReceiver> receiver,
     185             :                                               Handle<Object> value,
     186             :                                               double start, double end) {
     187             :   // 7. Repeat, while k < final.
     188        2351 :   while (start < end) {
     189             :     // a. Let Pk be ! ToString(k).
     190             :     Handle<String> index = isolate->factory()->NumberToString(
     191        1059 :         isolate->factory()->NewNumber(start));
     192             : 
     193             :     // b. Perform ? Set(O, Pk, value, true).
     194        2190 :     RETURN_FAILURE_ON_EXCEPTION(isolate, Object::SetPropertyOrElement(
     195             :                                              isolate, receiver, index, value,
     196             :                                              Just(ShouldThrow::kThrowOnError)));
     197             : 
     198             :     // c. Increase k by 1.
     199         987 :     ++start;
     200             :   }
     201             : 
     202             :   // 8. Return O.
     203         305 :   return *receiver;
     204             : }
     205             : 
     206        7322 : V8_WARN_UNUSED_RESULT bool TryFastArrayFill(
     207             :     Isolate* isolate, BuiltinArguments* args, Handle<JSReceiver> receiver,
     208             :     Handle<Object> value, double start_index, double end_index) {
     209             :   // If indices are too large, use generic path since they are stored as
     210             :   // properties, not in the element backing store.
     211        7322 :   if (end_index > kMaxUInt32) return false;
     212        7313 :   if (!receiver->IsJSObject()) return false;
     213             : 
     214        7313 :   if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, args, 1, 1)) {
     215             :     return false;
     216             :   }
     217             : 
     218             :   Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     219             : 
     220             :   // If no argument was provided, we fill the array with 'undefined'.
     221             :   // EnsureJSArrayWith... does not handle that case so we do it here.
     222             :   // TODO(szuend): Pass target elements kind to EnsureJSArrayWith... when
     223             :   //               it gets refactored.
     224        6970 :   if (args->length() == 1 && array->GetElementsKind() != PACKED_ELEMENTS) {
     225             :     // Use a short-lived HandleScope to avoid creating several copies of the
     226             :     // elements handle which would cause issues when left-trimming later-on.
     227             :     HandleScope scope(isolate);
     228          25 :     JSObject::TransitionElementsKind(array, PACKED_ELEMENTS);
     229             :   }
     230             : 
     231             :   DCHECK_LE(start_index, kMaxUInt32);
     232             :   DCHECK_LE(end_index, kMaxUInt32);
     233             : 
     234             :   uint32_t start, end;
     235        6945 :   CHECK(DoubleToUint32IfEqualToSelf(start_index, &start));
     236        6945 :   CHECK(DoubleToUint32IfEqualToSelf(end_index, &end));
     237             : 
     238        6945 :   ElementsAccessor* accessor = array->GetElementsAccessor();
     239       13890 :   accessor->Fill(array, value, start, end);
     240        6945 :   return true;
     241             : }
     242             : }  // namespace
     243             : 
     244       37100 : BUILTIN(ArrayPrototypeFill) {
     245             :   HandleScope scope(isolate);
     246             : 
     247        7420 :   if (isolate->debug_execution_mode() == DebugInfo::kSideEffects) {
     248          27 :     if (!isolate->debug()->PerformSideEffectCheckForObject(args.receiver())) {
     249          18 :       return ReadOnlyRoots(isolate).exception();
     250             :     }
     251             :   }
     252             : 
     253             :   // 1. Let O be ? ToObject(this value).
     254             :   Handle<JSReceiver> receiver;
     255       14822 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     256             :       isolate, receiver, Object::ToObject(isolate, args.receiver()));
     257             : 
     258             :   // 2. Let len be ? ToLength(? Get(O, "length")).
     259             :   double length;
     260       14768 :   MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     261             :       isolate, length, GetLengthProperty(isolate, receiver));
     262             : 
     263             :   // 3. Let relativeStart be ? ToInteger(start).
     264             :   // 4. If relativeStart < 0, let k be max((len + relativeStart), 0);
     265             :   //    else let k be min(relativeStart, len).
     266        7384 :   Handle<Object> start = args.atOrUndefined(isolate, 2);
     267             : 
     268             :   double start_index;
     269       14768 :   MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     270             :       isolate, start_index, GetRelativeIndex(isolate, length, start, 0));
     271             : 
     272             :   // 5. If end is undefined, let relativeEnd be len;
     273             :   //    else let relativeEnd be ? ToInteger(end).
     274             :   // 6. If relativeEnd < 0, let final be max((len + relativeEnd), 0);
     275             :   //    else let final be min(relativeEnd, len).
     276        7384 :   Handle<Object> end = args.atOrUndefined(isolate, 3);
     277             : 
     278             :   double end_index;
     279       14768 :   MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     280             :       isolate, end_index, GetRelativeIndex(isolate, length, end, length));
     281             : 
     282        7446 :   if (start_index >= end_index) return *receiver;
     283             : 
     284             :   // Ensure indexes are within array bounds
     285             :   DCHECK_LE(0, start_index);
     286             :   DCHECK_LE(start_index, end_index);
     287             :   DCHECK_LE(end_index, length);
     288             : 
     289        7322 :   Handle<Object> value = args.atOrUndefined(isolate, 1);
     290             : 
     291        7322 :   if (TryFastArrayFill(isolate, &args, receiver, value, start_index,
     292             :                        end_index)) {
     293        6945 :     return *receiver;
     294             :   }
     295         377 :   return GenericArrayFill(isolate, receiver, value, start_index, end_index);
     296             : }
     297             : 
     298             : namespace {
     299        7719 : V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
     300             :                                               BuiltinArguments* args) {
     301             :   // 1. Let O be ? ToObject(this value).
     302             :   Handle<JSReceiver> receiver;
     303       15699 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     304             :       isolate, receiver, Object::ToObject(isolate, args->receiver()));
     305             : 
     306             :   // 2. Let len be ? ToLength(? Get(O, "length")).
     307             :   Handle<Object> raw_length_number;
     308       14925 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     309             :       isolate, raw_length_number,
     310             :       Object::GetLengthFromArrayLike(isolate, receiver));
     311             : 
     312             :   // 3. Let args be a List whose elements are, in left to right order,
     313             :   //    the arguments that were passed to this function invocation.
     314             :   // 4. Let arg_count be the number of elements in args.
     315        7449 :   int arg_count = args->length() - 1;
     316             : 
     317             :   // 5. If len + arg_count > 2^53-1, throw a TypeError exception.
     318             :   double length = raw_length_number->Number();
     319        7449 :   if (arg_count > kMaxSafeInteger - length) {
     320          18 :     THROW_NEW_ERROR_RETURN_FAILURE(
     321             :         isolate, NewTypeError(MessageTemplate::kPushPastSafeLength,
     322             :                               isolate->factory()->NewNumberFromInt(arg_count),
     323             :                               raw_length_number));
     324             :   }
     325             : 
     326             :   // 6. Repeat, while args is not empty.
     327       22722 :   for (int i = 0; i < arg_count; ++i) {
     328             :     // a. Remove the first element from args and let E be the value of the
     329             :     //    element.
     330       15628 :     Handle<Object> element = args->at(i + 1);
     331             : 
     332             :     // b. Perform ? Set(O, ! ToString(len), E, true).
     333        7814 :     if (length <= static_cast<double>(JSArray::kMaxArrayIndex)) {
     334       23615 :       RETURN_FAILURE_ON_EXCEPTION(
     335             :           isolate, Object::SetElement(isolate, receiver, length, element,
     336             :                                       ShouldThrow::kThrowOnError));
     337             :     } else {
     338             :       bool success;
     339             :       LookupIterator it = LookupIterator::PropertyOrElement(
     340           0 :           isolate, receiver, isolate->factory()->NewNumber(length), &success);
     341             :       // Must succeed since we always pass a valid key.
     342             :       DCHECK(success);
     343           0 :       MAYBE_RETURN(Object::SetProperty(&it, element, StoreOrigin::kMaybeKeyed,
     344             :                                        Just(ShouldThrow::kThrowOnError)),
     345             :                    ReadOnlyRoots(isolate).exception());
     346             :     }
     347             : 
     348             :     // c. Let len be len+1.
     349        7641 :     ++length;
     350             :   }
     351             : 
     352             :   // 7. Perform ? Set(O, "length", len, true).
     353        7267 :   Handle<Object> final_length = isolate->factory()->NewNumber(length);
     354       14570 :   RETURN_FAILURE_ON_EXCEPTION(
     355             :       isolate, Object::SetProperty(isolate, receiver,
     356             :                                    isolate->factory()->length_string(),
     357             :                                    final_length, StoreOrigin::kMaybeKeyed,
     358             :                                    Just(ShouldThrow::kThrowOnError)));
     359             : 
     360             :   // 8. Return len.
     361             :   return *final_length;
     362             : }
     363             : }  // namespace
     364             : 
     365     4573480 : BUILTIN(ArrayPush) {
     366             :   HandleScope scope(isolate);
     367             :   Handle<Object> receiver = args.receiver();
     368      914696 :   if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1,
     369             :                                              args.length() - 1)) {
     370        7647 :     return GenericArrayPush(isolate, &args);
     371             :   }
     372             : 
     373             :   // Fast Elements Path
     374      907049 :   int to_add = args.length() - 1;
     375             :   Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     376      907049 :   uint32_t len = static_cast<uint32_t>(array->length()->Number());
     377      907094 :   if (to_add == 0) return *isolate->factory()->NewNumberFromUint(len);
     378             : 
     379             :   // Currently fixed arrays cannot grow too big, so we should never hit this.
     380             :   DCHECK_LE(to_add, Smi::kMaxValue - Smi::ToInt(array->length()));
     381             : 
     382      907004 :   if (JSArray::HasReadOnlyLength(array)) {
     383          72 :     return GenericArrayPush(isolate, &args);
     384             :   }
     385             : 
     386      906932 :   ElementsAccessor* accessor = array->GetElementsAccessor();
     387      906932 :   uint32_t new_length = accessor->Push(array, &args, to_add);
     388     1813864 :   return *isolate->factory()->NewNumberFromUint((new_length));
     389             : }
     390             : 
     391             : namespace {
     392             : 
     393        3436 : V8_WARN_UNUSED_RESULT Object GenericArrayPop(Isolate* isolate,
     394             :                                              BuiltinArguments* args) {
     395             :   // 1. Let O be ? ToObject(this value).
     396             :   Handle<JSReceiver> receiver;
     397        7043 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     398             :       isolate, receiver, Object::ToObject(isolate, args->receiver()));
     399             : 
     400             :   // 2. Let len be ? ToLength(? Get(O, "length")).
     401             :   Handle<Object> raw_length_number;
     402        6539 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     403             :       isolate, raw_length_number,
     404             :       Object::GetLengthFromArrayLike(isolate, receiver));
     405             :   double length = raw_length_number->Number();
     406             : 
     407             :   // 3. If len is zero, then.
     408        3256 :   if (length == 0) {
     409             :     // a. Perform ? Set(O, "length", 0, true).
     410         234 :     RETURN_FAILURE_ON_EXCEPTION(
     411             :         isolate, Object::SetProperty(isolate, receiver,
     412             :                                      isolate->factory()->length_string(),
     413             :                                      Handle<Smi>(Smi::zero(), isolate),
     414             :                                      StoreOrigin::kMaybeKeyed,
     415             :                                      Just(ShouldThrow::kThrowOnError)));
     416             : 
     417             :     // b. Return undefined.
     418         117 :     return ReadOnlyRoots(isolate).undefined_value();
     419             :   }
     420             : 
     421             :   // 4. Else len > 0.
     422             :   // a. Let new_len be len-1.
     423        3139 :   Handle<Object> new_length = isolate->factory()->NewNumber(length - 1);
     424             : 
     425             :   // b. Let index be ! ToString(newLen).
     426        3139 :   Handle<String> index = isolate->factory()->NumberToString(new_length);
     427             : 
     428             :   // c. Let element be ? Get(O, index).
     429             :   Handle<Object> element;
     430        6287 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     431             :       isolate, element, Object::GetPropertyOrElement(isolate, receiver, index));
     432             : 
     433             :   // d. Perform ? DeletePropertyOrThrow(O, index).
     434        6366 :   MAYBE_RETURN(JSReceiver::DeletePropertyOrElement(receiver, index,
     435             :                                                    LanguageMode::kStrict),
     436             :                ReadOnlyRoots(isolate).exception());
     437             : 
     438             :   // e. Perform ? Set(O, "length", newLen, true).
     439        6273 :   RETURN_FAILURE_ON_EXCEPTION(
     440             :       isolate, Object::SetProperty(isolate, receiver,
     441             :                                    isolate->factory()->length_string(),
     442             :                                    new_length, StoreOrigin::kMaybeKeyed,
     443             :                                    Just(ShouldThrow::kThrowOnError)));
     444             : 
     445             :   // f. Return element.
     446             :   return *element;
     447             : }
     448             : 
     449             : }  // namespace
     450             : 
     451      292730 : BUILTIN(ArrayPop) {
     452             :   HandleScope scope(isolate);
     453             :   Handle<Object> receiver = args.receiver();
     454       58546 :   if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0,
     455             :                                              0)) {
     456        3211 :     return GenericArrayPop(isolate, &args);
     457             :   }
     458             :   Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     459             : 
     460       55335 :   uint32_t len = static_cast<uint32_t>(array->length()->Number());
     461       55391 :   if (len == 0) return ReadOnlyRoots(isolate).undefined_value();
     462             : 
     463       55279 :   if (JSArray::HasReadOnlyLength(array)) {
     464         225 :     return GenericArrayPop(isolate, &args);
     465             :   }
     466             : 
     467             :   Handle<Object> result;
     468       55054 :   if (IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
     469             :     // Fast Elements Path
     470       53218 :     result = array->GetElementsAccessor()->Pop(array);
     471             :   } else {
     472             :     // Use Slow Lookup otherwise
     473        1836 :     uint32_t new_length = len - 1;
     474        3672 :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     475             :         isolate, result, JSReceiver::GetElement(isolate, array, new_length));
     476        1836 :     JSArray::SetLength(array, new_length);
     477             :   }
     478             : 
     479             :   return *result;
     480             : }
     481             : 
     482             : namespace {
     483             : 
     484             : // Returns true, iff we can use ElementsAccessor for shifting.
     485      182174 : V8_WARN_UNUSED_RESULT bool CanUseFastArrayShift(Isolate* isolate,
     486             :                                                 Handle<JSReceiver> receiver) {
     487      364348 :   if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0,
     488      364348 :                                              0) ||
     489             :       !IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
     490             :     return false;
     491             :   }
     492             : 
     493      182174 :   Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     494      182174 :   return !JSArray::HasReadOnlyLength(array);
     495             : }
     496             : 
     497           0 : V8_WARN_UNUSED_RESULT Object GenericArrayShift(Isolate* isolate,
     498             :                                                Handle<JSReceiver> receiver,
     499             :                                                double length) {
     500             :   // 4. Let first be ? Get(O, "0").
     501             :   Handle<Object> first;
     502           0 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, first,
     503             :                                      Object::GetElement(isolate, receiver, 0));
     504             : 
     505             :   // 5. Let k be 1.
     506             :   double k = 1;
     507             : 
     508             :   // 6. Repeat, while k < len.
     509           0 :   while (k < length) {
     510             :     // a. Let from be ! ToString(k).
     511             :     Handle<String> from =
     512           0 :         isolate->factory()->NumberToString(isolate->factory()->NewNumber(k));
     513             : 
     514             :     // b. Let to be ! ToString(k-1).
     515             :     Handle<String> to = isolate->factory()->NumberToString(
     516           0 :         isolate->factory()->NewNumber(k - 1));
     517             : 
     518             :     // c. Let fromPresent be ? HasProperty(O, from).
     519             :     bool from_present;
     520           0 :     MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     521             :         isolate, from_present, JSReceiver::HasProperty(receiver, from));
     522             : 
     523             :     // d. If fromPresent is true, then.
     524           0 :     if (from_present) {
     525             :       // i. Let fromVal be ? Get(O, from).
     526             :       Handle<Object> from_val;
     527           0 :       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     528             :           isolate, from_val,
     529             :           Object::GetPropertyOrElement(isolate, receiver, from));
     530             : 
     531             :       // ii. Perform ? Set(O, to, fromVal, true).
     532           0 :       RETURN_FAILURE_ON_EXCEPTION(
     533             :           isolate,
     534             :           Object::SetPropertyOrElement(isolate, receiver, to, from_val,
     535             :                                        Just(ShouldThrow::kThrowOnError)));
     536             :     } else {  // e. Else fromPresent is false,
     537             :       // i. Perform ? DeletePropertyOrThrow(O, to).
     538           0 :       MAYBE_RETURN(JSReceiver::DeletePropertyOrElement(receiver, to,
     539             :                                                        LanguageMode::kStrict),
     540             :                    ReadOnlyRoots(isolate).exception());
     541             :     }
     542             : 
     543             :     // f. Increase k by 1.
     544           0 :     ++k;
     545             :   }
     546             : 
     547             :   // 7. Perform ? DeletePropertyOrThrow(O, ! ToString(len-1)).
     548             :   Handle<String> new_length = isolate->factory()->NumberToString(
     549           0 :       isolate->factory()->NewNumber(length - 1));
     550           0 :   MAYBE_RETURN(JSReceiver::DeletePropertyOrElement(receiver, new_length,
     551             :                                                    LanguageMode::kStrict),
     552             :                ReadOnlyRoots(isolate).exception());
     553             : 
     554             :   // 8. Perform ? Set(O, "length", len-1, true).
     555           0 :   RETURN_FAILURE_ON_EXCEPTION(isolate,
     556             :                               SetLengthProperty(isolate, receiver, length - 1));
     557             : 
     558             :   // 9. Return first.
     559             :   return *first;
     560             : }
     561             : }  // namespace
     562             : 
     563      910870 : BUILTIN(ArrayShift) {
     564             :   HandleScope scope(isolate);
     565             : 
     566             :   // 1. Let O be ? ToObject(this value).
     567             :   Handle<JSReceiver> receiver;
     568      364348 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     569             :       isolate, receiver, Object::ToObject(isolate, args.receiver()));
     570             : 
     571             :   // 2. Let len be ? ToLength(? Get(O, "length")).
     572             :   double length;
     573      364348 :   MAYBE_ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
     574             :       isolate, length, GetLengthProperty(isolate, receiver));
     575             : 
     576             :   // 3. If len is zero, then.
     577      182174 :   if (length == 0) {
     578             :     // a. Perform ? Set(O, "length", 0, true).
     579           0 :     RETURN_FAILURE_ON_EXCEPTION(isolate,
     580             :                                 SetLengthProperty(isolate, receiver, length));
     581             : 
     582             :     // b. Return undefined.
     583           0 :     return ReadOnlyRoots(isolate).undefined_value();
     584             :   }
     585             : 
     586      182174 :   if (CanUseFastArrayShift(isolate, receiver)) {
     587             :     Handle<JSArray> array = Handle<JSArray>::cast(receiver);
     588      364348 :     return *array->GetElementsAccessor()->Shift(array);
     589             :   }
     590             : 
     591           0 :   return GenericArrayShift(isolate, receiver, length);
     592             : }
     593             : 
     594       29340 : BUILTIN(ArrayUnshift) {
     595             :   HandleScope scope(isolate);
     596             :   DCHECK(args.receiver()->IsJSArray());
     597             :   Handle<JSArray> array = Handle<JSArray>::cast(args.receiver());
     598             : 
     599             :   // These are checked in the Torque builtin.
     600             :   DCHECK(array->map()->is_extensible());
     601             :   DCHECK(!IsDictionaryElementsKind(array->GetElementsKind()));
     602             :   DCHECK(IsJSArrayFastElementMovingAllowed(isolate, *array));
     603             :   DCHECK(!isolate->IsAnyInitialArrayPrototype(array));
     604             : 
     605        5868 :   MatchArrayElementsKindToArguments(isolate, array, &args, 1,
     606        5868 :                                     args.length() - 1);
     607             : 
     608        5868 :   int to_add = args.length() - 1;
     609        5868 :   if (to_add == 0) return array->length();
     610             : 
     611             :   // Currently fixed arrays cannot grow too big, so we should never hit this.
     612             :   DCHECK_LE(to_add, Smi::kMaxValue - Smi::ToInt(array->length()));
     613             :   DCHECK(!JSArray::HasReadOnlyLength(array));
     614             : 
     615        5846 :   ElementsAccessor* accessor = array->GetElementsAccessor();
     616        5846 :   int new_length = accessor->Unshift(array, &args, to_add);
     617        5846 :   return Smi::FromInt(new_length);
     618             : }
     619             : 
     620             : // Array Concat -------------------------------------------------------------
     621             : 
     622             : namespace {
     623             : 
     624             : /**
     625             :  * A simple visitor visits every element of Array's.
     626             :  * The backend storage can be a fixed array for fast elements case,
     627             :  * or a dictionary for sparse array. Since Dictionary is a subtype
     628             :  * of FixedArray, the class can be used by both fast and slow cases.
     629             :  * The second parameter of the constructor, fast_elements, specifies
     630             :  * whether the storage is a FixedArray or Dictionary.
     631             :  *
     632             :  * An index limit is used to deal with the situation that a result array
     633             :  * length overflows 32-bit non-negative integer.
     634             :  */
     635             : class ArrayConcatVisitor {
     636             :  public:
     637        6074 :   ArrayConcatVisitor(Isolate* isolate, Handle<HeapObject> storage,
     638             :                      bool fast_elements)
     639             :       : isolate_(isolate),
     640             :         storage_(isolate->global_handles()->Create(*storage)),
     641             :         index_offset_(0u),
     642             :         bit_field_(FastElementsField::encode(fast_elements) |
     643        6074 :                    ExceedsLimitField::encode(false) |
     644        6074 :                    IsFixedArrayField::encode(storage->IsFixedArray()) |
     645             :                    HasSimpleElementsField::encode(
     646        6362 :                        storage->IsFixedArray() ||
     647       18222 :                        !storage->map()->IsCustomElementsReceiverMap())) {
     648             :     DCHECK(!(this->fast_elements() && !is_fixed_array()));
     649        6074 :   }
     650             : 
     651        6074 :   ~ArrayConcatVisitor() { clear_storage(); }
     652             : 
     653     1285742 :   V8_WARN_UNUSED_RESULT bool visit(uint32_t i, Handle<Object> elm) {
     654     1285742 :     uint32_t index = index_offset_ + i;
     655             : 
     656     1285742 :     if (i >= JSObject::kMaxElementCount - index_offset_) {
     657             :       set_exceeds_array_limit(true);
     658             :       // Exception hasn't been thrown at this point. Return true to
     659             :       // break out, and caller will throw. !visit would imply that
     660             :       // there is already a pending exception.
     661          36 :       return true;
     662             :     }
     663             : 
     664     1285706 :     if (!is_fixed_array()) {
     665         324 :       LookupIterator it(isolate_, storage_, index, LookupIterator::OWN);
     666         648 :       MAYBE_RETURN(
     667             :           JSReceiver::CreateDataProperty(&it, elm, Just(kThrowOnError)), false);
     668         279 :       return true;
     669             :     }
     670             : 
     671     1285382 :     if (fast_elements()) {
     672      906988 :       if (index < static_cast<uint32_t>(storage_fixed_array()->length())) {
     673      906970 :         storage_fixed_array()->set(index, *elm);
     674      906970 :         return true;
     675             :       }
     676             :       // Our initial estimate of length was foiled, possibly by
     677             :       // getters on the arrays increasing the length of later arrays
     678             :       // during iteration.
     679             :       // This shouldn't happen in anything but pathological cases.
     680          18 :       SetDictionaryMode();
     681             :       // Fall-through to dictionary mode.
     682             :     }
     683             :     DCHECK(!fast_elements());
     684      378412 :     Handle<NumberDictionary> dict(NumberDictionary::cast(*storage_), isolate_);
     685             :     // The object holding this backing store has just been allocated, so
     686             :     // it cannot yet be used as a prototype.
     687             :     Handle<JSObject> not_a_prototype_holder;
     688             :     Handle<NumberDictionary> result = NumberDictionary::Set(
     689      378412 :         isolate_, dict, index, elm, not_a_prototype_holder);
     690      378412 :     if (!result.is_identical_to(dict)) {
     691             :       // Dictionary needed to grow.
     692             :       clear_storage();
     693             :       set_storage(*result);
     694             :     }
     695             :     return true;
     696             :   }
     697             : 
     698             :   uint32_t index_offset() const { return index_offset_; }
     699             : 
     700      912085 :   void increase_index_offset(uint32_t delta) {
     701      912085 :     if (JSObject::kMaxElementCount - index_offset_ < delta) {
     702          45 :       index_offset_ = JSObject::kMaxElementCount;
     703             :     } else {
     704      912040 :       index_offset_ += delta;
     705             :     }
     706             :     // If the initial length estimate was off (see special case in visit()),
     707             :     // but the array blowing the limit didn't contain elements beyond the
     708             :     // provided-for index range, go to dictionary mode now.
     709     1816733 :     if (fast_elements() &&
     710     1809296 :         index_offset_ >
     711             :             static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) {
     712           9 :       SetDictionaryMode();
     713             :     }
     714      912085 :   }
     715             : 
     716             :   bool exceeds_array_limit() const {
     717             :     return ExceedsLimitField::decode(bit_field_);
     718             :   }
     719             : 
     720        5588 :   Handle<JSArray> ToArray() {
     721             :     DCHECK(is_fixed_array());
     722        5588 :     Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
     723             :     Handle<Object> length =
     724        5588 :         isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
     725             :     Handle<Map> map = JSObject::GetElementsTransitionMap(
     726       11176 :         array, fast_elements() ? HOLEY_ELEMENTS : DICTIONARY_ELEMENTS);
     727        5588 :     array->set_length(*length);
     728       11176 :     array->set_elements(*storage_fixed_array());
     729        5588 :     array->synchronized_set_map(*map);
     730        5588 :     return array;
     731             :   }
     732             : 
     733         234 :   V8_WARN_UNUSED_RESULT MaybeHandle<JSReceiver> ToJSReceiver() {
     734             :     DCHECK(!is_fixed_array());
     735             :     Handle<JSReceiver> result = Handle<JSReceiver>::cast(storage_);
     736             :     Handle<Object> length =
     737         234 :         isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
     738         702 :     RETURN_ON_EXCEPTION(
     739             :         isolate_,
     740             :         Object::SetProperty(
     741             :             isolate_, result, isolate_->factory()->length_string(), length,
     742             :             StoreOrigin::kMaybeKeyed, Just(ShouldThrow::kThrowOnError)),
     743             :         JSReceiver);
     744         225 :     return result;
     745             :   }
     746             :   bool has_simple_elements() const {
     747             :     return HasSimpleElementsField::decode(bit_field_);
     748             :   }
     749             : 
     750             :  private:
     751             :   // Convert storage to dictionary mode.
     752          27 :   void SetDictionaryMode() {
     753             :     DCHECK(fast_elements() && is_fixed_array());
     754             :     Handle<FixedArray> current_storage = storage_fixed_array();
     755             :     Handle<NumberDictionary> slow_storage(
     756          27 :         NumberDictionary::New(isolate_, current_storage->length()));
     757          27 :     uint32_t current_length = static_cast<uint32_t>(current_storage->length());
     758         486 :     FOR_WITH_HANDLE_SCOPE(
     759             :         isolate_, uint32_t, i = 0, i, i < current_length, i++, {
     760             :           Handle<Object> element(current_storage->get(i), isolate_);
     761             :           if (!element->IsTheHole(isolate_)) {
     762             :             // The object holding this backing store has just been allocated, so
     763             :             // it cannot yet be used as a prototype.
     764             :             Handle<JSObject> not_a_prototype_holder;
     765             :             Handle<NumberDictionary> new_storage = NumberDictionary::Set(
     766             :                 isolate_, slow_storage, i, element, not_a_prototype_holder);
     767             :             if (!new_storage.is_identical_to(slow_storage)) {
     768             :               slow_storage = loop_scope.CloseAndEscape(new_storage);
     769             :             }
     770             :           }
     771             :         });
     772             :     clear_storage();
     773             :     set_storage(*slow_storage);
     774             :     set_fast_elements(false);
     775          27 :   }
     776             : 
     777        7991 :   inline void clear_storage() { GlobalHandles::Destroy(storage_.location()); }
     778             : 
     779             :   inline void set_storage(FixedArray storage) {
     780             :     DCHECK(is_fixed_array());
     781             :     DCHECK(has_simple_elements());
     782        3834 :     storage_ = isolate_->global_handles()->Create(storage);
     783             :   }
     784             : 
     785             :   class FastElementsField : public BitField<bool, 0, 1> {};
     786             :   class ExceedsLimitField : public BitField<bool, 1, 1> {};
     787             :   class IsFixedArrayField : public BitField<bool, 2, 1> {};
     788             :   class HasSimpleElementsField : public BitField<bool, 3, 1> {};
     789             : 
     790             :   bool fast_elements() const { return FastElementsField::decode(bit_field_); }
     791             :   void set_fast_elements(bool fast) {
     792          54 :     bit_field_ = FastElementsField::update(bit_field_, fast);
     793             :   }
     794             :   void set_exceeds_array_limit(bool exceeds) {
     795          72 :     bit_field_ = ExceedsLimitField::update(bit_field_, exceeds);
     796             :   }
     797             :   bool is_fixed_array() const { return IsFixedArrayField::decode(bit_field_); }
     798             :   Handle<FixedArray> storage_fixed_array() {
     799             :     DCHECK(is_fixed_array());
     800             :     DCHECK(has_simple_elements());
     801             :     return Handle<FixedArray>::cast(storage_);
     802             :   }
     803             : 
     804             :   Isolate* isolate_;
     805             :   Handle<Object> storage_;  // Always a global handle.
     806             :   // Index after last seen index. Always less than or equal to
     807             :   // JSObject::kMaxElementCount.
     808             :   uint32_t index_offset_;
     809             :   uint32_t bit_field_;
     810             : };
     811             : 
     812        7520 : uint32_t EstimateElementCount(Isolate* isolate, Handle<JSArray> array) {
     813             :   DisallowHeapAllocation no_gc;
     814        7520 :   uint32_t length = static_cast<uint32_t>(array->length()->Number());
     815             :   int element_count = 0;
     816        7520 :   switch (array->GetElementsKind()) {
     817             :     case PACKED_SMI_ELEMENTS:
     818             :     case HOLEY_SMI_ELEMENTS:
     819             :     case PACKED_ELEMENTS:
     820             :     case HOLEY_ELEMENTS: {
     821             :       // Fast elements can't have lengths that are not representable by
     822             :       // a 32-bit signed integer.
     823             :       DCHECK_GE(static_cast<int32_t>(FixedArray::kMaxLength), 0);
     824        6806 :       int fast_length = static_cast<int>(length);
     825             :       FixedArray elements = FixedArray::cast(array->elements());
     826       94028 :       for (int i = 0; i < fast_length; i++) {
     827       43611 :         if (!elements->get(i)->IsTheHole(isolate)) element_count++;
     828             :       }
     829             :       break;
     830             :     }
     831             :     case PACKED_DOUBLE_ELEMENTS:
     832             :     case HOLEY_DOUBLE_ELEMENTS: {
     833             :       // Fast elements can't have lengths that are not representable by
     834             :       // a 32-bit signed integer.
     835             :       DCHECK_GE(static_cast<int32_t>(FixedDoubleArray::kMaxLength), 0);
     836          94 :       int fast_length = static_cast<int>(length);
     837          94 :       if (array->elements()->IsFixedArray()) {
     838             :         DCHECK_EQ(FixedArray::cast(array->elements())->length(), 0);
     839             :         break;
     840             :       }
     841             :       FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
     842      591443 :       for (int i = 0; i < fast_length; i++) {
     843      591358 :         if (!elements->is_the_hole(i)) element_count++;
     844             :       }
     845             :       break;
     846             :     }
     847             :     case DICTIONARY_ELEMENTS: {
     848         620 :       NumberDictionary dictionary = NumberDictionary::cast(array->elements());
     849             :       int capacity = dictionary->Capacity();
     850             :       ReadOnlyRoots roots(isolate);
     851       24672 :       for (int i = 0; i < capacity; i++) {
     852       12026 :         Object key = dictionary->KeyAt(i);
     853       12026 :         if (dictionary->IsKey(roots, key)) {
     854        4692 :           element_count++;
     855             :         }
     856             :       }
     857             :       break;
     858             :     }
     859             : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
     860             : 
     861             :       TYPED_ARRAYS(TYPED_ARRAY_CASE)
     862             : #undef TYPED_ARRAY_CASE
     863             :       // External arrays are always dense.
     864           0 :       return length;
     865             :     case NO_ELEMENTS:
     866             :       return 0;
     867             :     case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
     868             :     case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
     869             :     case FAST_STRING_WRAPPER_ELEMENTS:
     870             :     case SLOW_STRING_WRAPPER_ELEMENTS:
     871           0 :       UNREACHABLE();
     872             :   }
     873             :   // As an estimate, we assume that the prototype doesn't contain any
     874             :   // inherited elements.
     875        7520 :   return element_count;
     876             : }
     877             : 
     878        1590 : void CollectElementIndices(Isolate* isolate, Handle<JSObject> object,
     879             :                            uint32_t range, std::vector<uint32_t>* indices) {
     880        1590 :   ElementsKind kind = object->GetElementsKind();
     881        1590 :   switch (kind) {
     882             :     case PACKED_SMI_ELEMENTS:
     883             :     case PACKED_ELEMENTS:
     884             :     case HOLEY_SMI_ELEMENTS:
     885             :     case HOLEY_ELEMENTS: {
     886             :       DisallowHeapAllocation no_gc;
     887             :       FixedArray elements = FixedArray::cast(object->elements());
     888        1051 :       uint32_t length = static_cast<uint32_t>(elements->length());
     889        1051 :       if (range < length) length = range;
     890       41209 :       for (uint32_t i = 0; i < length; i++) {
     891       80316 :         if (!elements->get(i)->IsTheHole(isolate)) {
     892         207 :           indices->push_back(i);
     893             :         }
     894             :       }
     895             :       break;
     896             :     }
     897             :     case HOLEY_DOUBLE_ELEMENTS:
     898             :     case PACKED_DOUBLE_ELEMENTS: {
     899           9 :       if (object->elements()->IsFixedArray()) {
     900             :         DCHECK_EQ(object->elements()->length(), 0);
     901             :         break;
     902             :       }
     903             :       Handle<FixedDoubleArray> elements(
     904             :           FixedDoubleArray::cast(object->elements()), isolate);
     905           9 :       uint32_t length = static_cast<uint32_t>(elements->length());
     906           9 :       if (range < length) length = range;
     907          18 :       for (uint32_t i = 0; i < length; i++) {
     908          18 :         if (!elements->is_the_hole(i)) {
     909           9 :           indices->push_back(i);
     910             :         }
     911             :       }
     912           9 :       break;
     913             :     }
     914             :     case DICTIONARY_ELEMENTS: {
     915             :       DisallowHeapAllocation no_gc;
     916         476 :       NumberDictionary dict = NumberDictionary::cast(object->elements());
     917         476 :       uint32_t capacity = dict->Capacity();
     918             :       ReadOnlyRoots roots(isolate);
     919        5293 :       FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, j = 0, j, j < capacity, j++, {
     920             :         Object k = dict->KeyAt(j);
     921             :         if (!dict->IsKey(roots, k)) continue;
     922             :         DCHECK(k->IsNumber());
     923             :         uint32_t index = static_cast<uint32_t>(k->Number());
     924             :         if (index < range) {
     925             :           indices->push_back(index);
     926             :         }
     927             :       });
     928             :       break;
     929             :     }
     930             : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
     931             : 
     932             :       TYPED_ARRAYS(TYPED_ARRAY_CASE)
     933             : #undef TYPED_ARRAY_CASE
     934             :       {
     935           9 :         uint32_t length = static_cast<uint32_t>(object->elements()->length());
     936           9 :         if (range <= length) {
     937             :           length = range;
     938             :           // We will add all indices, so we might as well clear it first
     939             :           // and avoid duplicates.
     940             :           indices->clear();
     941             :         }
     942          27 :         for (uint32_t i = 0; i < length; i++) {
     943          18 :           indices->push_back(i);
     944             :         }
     945           9 :         if (length == range) return;  // All indices accounted for already.
     946             :         break;
     947             :       }
     948             :     case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
     949             :     case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
     950             :       DisallowHeapAllocation no_gc;
     951          45 :       FixedArrayBase elements = object->elements();
     952          45 :       JSObject raw_object = *object;
     953          45 :       ElementsAccessor* accessor = object->GetElementsAccessor();
     954      270072 :       for (uint32_t i = 0; i < range; i++) {
     955      270027 :         if (accessor->HasElement(raw_object, i, elements)) {
     956          54 :           indices->push_back(i);
     957             :         }
     958             :       }
     959             :       break;
     960             :     }
     961             :     case FAST_STRING_WRAPPER_ELEMENTS:
     962             :     case SLOW_STRING_WRAPPER_ELEMENTS: {
     963             :       DCHECK(object->IsJSValue());
     964             :       Handle<JSValue> js_value = Handle<JSValue>::cast(object);
     965             :       DCHECK(js_value->value()->IsString());
     966             :       Handle<String> string(String::cast(js_value->value()), isolate);
     967           0 :       uint32_t length = static_cast<uint32_t>(string->length());
     968           0 :       uint32_t i = 0;
     969             :       uint32_t limit = Min(length, range);
     970           0 :       for (; i < limit; i++) {
     971           0 :         indices->push_back(i);
     972             :       }
     973           0 :       ElementsAccessor* accessor = object->GetElementsAccessor();
     974           0 :       for (; i < range; i++) {
     975           0 :         if (accessor->HasElement(*object, i)) {
     976           0 :           indices->push_back(i);
     977             :         }
     978             :       }
     979             :       break;
     980             :     }
     981             :     case NO_ELEMENTS:
     982             :       break;
     983             :   }
     984             : 
     985        1590 :   PrototypeIterator iter(isolate, object);
     986        1590 :   if (!iter.IsAtEnd()) {
     987             :     // The prototype will usually have no inherited element indices,
     988             :     // but we have to check.
     989             :     CollectElementIndices(
     990        1114 :         isolate, PrototypeIterator::GetCurrent<JSObject>(iter), range, indices);
     991             :   }
     992             : }
     993             : 
     994        1144 : bool IterateElementsSlow(Isolate* isolate, Handle<JSReceiver> receiver,
     995             :                          uint32_t length, ArrayConcatVisitor* visitor) {
     996     5861706 :   FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, i = 0, i, i < length, ++i, {
     997             :     Maybe<bool> maybe = JSReceiver::HasElement(receiver, i);
     998             :     if (maybe.IsNothing()) return false;
     999             :     if (maybe.FromJust()) {
    1000             :       Handle<Object> element_value;
    1001             :       ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    1002             :           isolate, element_value, JSReceiver::GetElement(isolate, receiver, i),
    1003             :           false);
    1004             :       if (!visitor->visit(i, element_value)) return false;
    1005             :     }
    1006             :   });
    1007        1126 :   visitor->increase_index_offset(length);
    1008        1126 :   return true;
    1009             : }
    1010             : /**
    1011             :  * A helper function that visits "array" elements of a JSReceiver in numerical
    1012             :  * order.
    1013             :  *
    1014             :  * The visitor argument called for each existing element in the array
    1015             :  * with the element index and the element's value.
    1016             :  * Afterwards it increments the base-index of the visitor by the array
    1017             :  * length.
    1018             :  * Returns false if any access threw an exception, otherwise true.
    1019             :  */
    1020        7872 : bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver,
    1021             :                      ArrayConcatVisitor* visitor) {
    1022        7872 :   uint32_t length = 0;
    1023             : 
    1024        7872 :   if (receiver->IsJSArray()) {
    1025             :     Handle<JSArray> array = Handle<JSArray>::cast(receiver);
    1026        6845 :     length = static_cast<uint32_t>(array->length()->Number());
    1027             :   } else {
    1028             :     Handle<Object> val;
    1029        2054 :     ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    1030             :         isolate, val, Object::GetLengthFromArrayLike(isolate, receiver), false);
    1031        1892 :     if (visitor->index_offset() + val->Number() > kMaxSafeInteger) {
    1032          54 :       isolate->Throw(*isolate->factory()->NewTypeError(
    1033          54 :           MessageTemplate::kInvalidArrayLength));
    1034          27 :       return false;
    1035             :     }
    1036             :     // TODO(caitp): Support larger element indexes (up to 2^53-1).
    1037         919 :     if (!val->ToUint32(&length)) {
    1038           0 :       length = 0;
    1039             :     }
    1040             :     // TODO(cbruni): handle other element kind as well
    1041         919 :     return IterateElementsSlow(isolate, receiver, length, visitor);
    1042             :   }
    1043             : 
    1044       13492 :   if (!HasOnlySimpleElements(isolate, *receiver) ||
    1045             :       !visitor->has_simple_elements()) {
    1046         225 :     return IterateElementsSlow(isolate, receiver, length, visitor);
    1047             :   }
    1048             :   Handle<JSObject> array = Handle<JSObject>::cast(receiver);
    1049             : 
    1050        6620 :   switch (array->GetElementsKind()) {
    1051             :     case PACKED_SMI_ELEMENTS:
    1052             :     case PACKED_ELEMENTS:
    1053             :     case HOLEY_SMI_ELEMENTS:
    1054             :     case HOLEY_ELEMENTS: {
    1055             :       // Run through the elements FixedArray and use HasElement and GetElement
    1056             :       // to check the prototype for missing elements.
    1057             :       Handle<FixedArray> elements(FixedArray::cast(array->elements()), isolate);
    1058        6104 :       int fast_length = static_cast<int>(length);
    1059             :       DCHECK(fast_length <= elements->length());
    1060      213369 :       FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
    1061             :         Handle<Object> element_value(elements->get(j), isolate);
    1062             :         if (!element_value->IsTheHole(isolate)) {
    1063             :           if (!visitor->visit(j, element_value)) return false;
    1064             :         } else {
    1065             :           Maybe<bool> maybe = JSReceiver::HasElement(array, j);
    1066             :           if (maybe.IsNothing()) return false;
    1067             :           if (maybe.FromJust()) {
    1068             :             // Call GetElement on array, not its prototype, or getters won't
    1069             :             // have the correct receiver.
    1070             :             ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    1071             :                 isolate, element_value,
    1072             :                 JSReceiver::GetElement(isolate, array, j), false);
    1073             :             if (!visitor->visit(j, element_value)) return false;
    1074             :           }
    1075             :         }
    1076             :       });
    1077             :       break;
    1078             :     }
    1079             :     case HOLEY_DOUBLE_ELEMENTS:
    1080             :     case PACKED_DOUBLE_ELEMENTS: {
    1081             :       // Empty array is FixedArray but not FixedDoubleArray.
    1082          40 :       if (length == 0) break;
    1083             :       // Run through the elements FixedArray and use HasElement and GetElement
    1084             :       // to check the prototype for missing elements.
    1085          31 :       if (array->elements()->IsFixedArray()) {
    1086             :         DCHECK_EQ(array->elements()->length(), 0);
    1087             :         break;
    1088             :       }
    1089             :       Handle<FixedDoubleArray> elements(
    1090             :           FixedDoubleArray::cast(array->elements()), isolate);
    1091          31 :       int fast_length = static_cast<int>(length);
    1092             :       DCHECK(fast_length <= elements->length());
    1093        2760 :       FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
    1094             :         if (!elements->is_the_hole(j)) {
    1095             :           double double_value = elements->get_scalar(j);
    1096             :           Handle<Object> element_value =
    1097             :               isolate->factory()->NewNumber(double_value);
    1098             :           if (!visitor->visit(j, element_value)) return false;
    1099             :         } else {
    1100             :           Maybe<bool> maybe = JSReceiver::HasElement(array, j);
    1101             :           if (maybe.IsNothing()) return false;
    1102             :           if (maybe.FromJust()) {
    1103             :             // Call GetElement on array, not its prototype, or getters won't
    1104             :             // have the correct receiver.
    1105             :             Handle<Object> element_value;
    1106             :             ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    1107             :                 isolate, element_value,
    1108             :                 JSReceiver::GetElement(isolate, array, j), false);
    1109             :             if (!visitor->visit(j, element_value)) return false;
    1110             :           }
    1111             :         }
    1112             :       });
    1113             :       break;
    1114             :     }
    1115             : 
    1116             :     case DICTIONARY_ELEMENTS: {
    1117             :       Handle<NumberDictionary> dict(array->element_dictionary(), isolate);
    1118             :       std::vector<uint32_t> indices;
    1119         476 :       indices.reserve(dict->Capacity() / 2);
    1120             : 
    1121             :       // Collect all indices in the object and the prototypes less
    1122             :       // than length. This might introduce duplicates in the indices list.
    1123         476 :       CollectElementIndices(isolate, array, length, &indices);
    1124             :       std::sort(indices.begin(), indices.end());
    1125             :       size_t n = indices.size();
    1126        6700 :       FOR_WITH_HANDLE_SCOPE(isolate, size_t, j = 0, j, j < n, (void)0, {
    1127             :         uint32_t index = indices[j];
    1128             :         Handle<Object> element;
    1129             :         ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    1130             :             isolate, element, JSReceiver::GetElement(isolate, array, index),
    1131             :             false);
    1132             :         if (!visitor->visit(index, element)) return false;
    1133             :         // Skip to next different index (i.e., omit duplicates).
    1134             :         do {
    1135             :           j++;
    1136             :         } while (j < n && indices[j] == index);
    1137             :       });
    1138             :       break;
    1139             :     }
    1140             :     case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
    1141             :     case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
    1142           0 :       FOR_WITH_HANDLE_SCOPE(
    1143             :           isolate, uint32_t, index = 0, index, index < length, index++, {
    1144             :             Handle<Object> element;
    1145             :             ASSIGN_RETURN_ON_EXCEPTION_VALUE(
    1146             :                 isolate, element, JSReceiver::GetElement(isolate, array, index),
    1147             :                 false);
    1148             :             if (!visitor->visit(index, element)) return false;
    1149             :           });
    1150             :       break;
    1151             :     }
    1152             :     case NO_ELEMENTS:
    1153             :       break;
    1154             : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) case TYPE##_ELEMENTS:
    1155             :       TYPED_ARRAYS(TYPED_ARRAY_CASE)
    1156             : #undef TYPED_ARRAY_CASE
    1157           0 :       return IterateElementsSlow(isolate, receiver, length, visitor);
    1158             :     case FAST_STRING_WRAPPER_ELEMENTS:
    1159             :     case SLOW_STRING_WRAPPER_ELEMENTS:
    1160             :       // |array| is guaranteed to be an array or typed array.
    1161           0 :       UNREACHABLE();
    1162             :       break;
    1163             :   }
    1164        6584 :   visitor->increase_index_offset(length);
    1165        6584 :   return true;
    1166             : }
    1167             : 
    1168      912301 : static Maybe<bool> IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
    1169             :   HandleScope handle_scope(isolate);
    1170      912301 :   if (!obj->IsJSReceiver()) return Just(false);
    1171       10321 :   if (!isolate->IsIsConcatSpreadableLookupChainIntact(JSReceiver::cast(*obj))) {
    1172             :     // Slow path if @@isConcatSpreadable has been used.
    1173             :     Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol());
    1174             :     Handle<Object> value;
    1175             :     MaybeHandle<Object> maybeValue =
    1176        6132 :         i::Runtime::GetObjectProperty(isolate, obj, key);
    1177        8545 :     if (!maybeValue.ToHandle(&value)) return Nothing<bool>();
    1178        8473 :     if (!value->IsUndefined(isolate)) return Just(value->BooleanValue(isolate));
    1179             :   }
    1180             :   return Object::IsArray(obj);
    1181             : }
    1182             : 
    1183        6083 : Object Slow_ArrayConcat(BuiltinArguments* args, Handle<Object> species,
    1184             :                         Isolate* isolate) {
    1185             :   int argument_count = args->length();
    1186             : 
    1187       12166 :   bool is_array_species = *species == isolate->context()->array_function();
    1188             : 
    1189             :   // Pass 1: estimate the length and number of elements of the result.
    1190             :   // The actual length can be larger if any of the arguments have getters
    1191             :   // that mutate other arguments (but will otherwise be precise).
    1192             :   // The number of elements is precise if there are no inherited elements.
    1193             : 
    1194             :   ElementsKind kind = PACKED_SMI_ELEMENTS;
    1195             : 
    1196             :   uint32_t estimate_result_length = 0;
    1197             :   uint32_t estimate_nof = 0;
    1198     4594053 :   FOR_WITH_HANDLE_SCOPE(isolate, int, i = 0, i, i < argument_count, i++, {
    1199             :     Handle<Object> obj = args->at(i);
    1200             :     uint32_t length_estimate;
    1201             :     uint32_t element_estimate;
    1202             :     if (obj->IsJSArray()) {
    1203             :       Handle<JSArray> array(Handle<JSArray>::cast(obj));
    1204             :       length_estimate = static_cast<uint32_t>(array->length()->Number());
    1205             :       if (length_estimate != 0) {
    1206             :         ElementsKind array_kind =
    1207             :             GetPackedElementsKind(array->GetElementsKind());
    1208             :         kind = GetMoreGeneralElementsKind(kind, array_kind);
    1209             :       }
    1210             :       element_estimate = EstimateElementCount(isolate, array);
    1211             :     } else {
    1212             :       if (obj->IsHeapObject()) {
    1213             :         kind = GetMoreGeneralElementsKind(
    1214             :             kind, obj->IsNumber() ? PACKED_DOUBLE_ELEMENTS : PACKED_ELEMENTS);
    1215             :       }
    1216             :       length_estimate = 1;
    1217             :       element_estimate = 1;
    1218             :     }
    1219             :     // Avoid overflows by capping at kMaxElementCount.
    1220             :     if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) {
    1221             :       estimate_result_length = JSObject::kMaxElementCount;
    1222             :     } else {
    1223             :       estimate_result_length += length_estimate;
    1224             :     }
    1225             :     if (JSObject::kMaxElementCount - estimate_nof < element_estimate) {
    1226             :       estimate_nof = JSObject::kMaxElementCount;
    1227             :     } else {
    1228             :       estimate_nof += element_estimate;
    1229             :     }
    1230             :   });
    1231             : 
    1232             :   // If estimated number of elements is more than half of length, a
    1233             :   // fixed array (fast case) is more time and space-efficient than a
    1234             :   // dictionary.
    1235        5795 :   bool fast_case = is_array_species &&
    1236       11167 :                    (estimate_nof * 2) >= estimate_result_length &&
    1237        5084 :                    isolate->IsIsConcatSpreadableLookupChainIntact();
    1238             : 
    1239        6083 :   if (fast_case && kind == PACKED_DOUBLE_ELEMENTS) {
    1240             :     Handle<FixedArrayBase> storage =
    1241          18 :         isolate->factory()->NewFixedDoubleArray(estimate_result_length);
    1242             :     int j = 0;
    1243             :     bool failure = false;
    1244          18 :     if (estimate_result_length > 0) {
    1245             :       Handle<FixedDoubleArray> double_storage =
    1246             :           Handle<FixedDoubleArray>::cast(storage);
    1247          54 :       for (int i = 0; i < argument_count; i++) {
    1248             :         Handle<Object> obj = args->at(i);
    1249          27 :         if (obj->IsSmi()) {
    1250           0 :           double_storage->set(j, Smi::ToInt(*obj));
    1251           0 :           j++;
    1252          27 :         } else if (obj->IsNumber()) {
    1253           9 :           double_storage->set(j, obj->Number());
    1254           9 :           j++;
    1255             :         } else {
    1256             :           DisallowHeapAllocation no_gc;
    1257          18 :           JSArray array = JSArray::cast(*obj);
    1258          18 :           uint32_t length = static_cast<uint32_t>(array->length()->Number());
    1259          18 :           switch (array->GetElementsKind()) {
    1260             :             case HOLEY_DOUBLE_ELEMENTS:
    1261             :             case PACKED_DOUBLE_ELEMENTS: {
    1262             :               // Empty array is FixedArray but not FixedDoubleArray.
    1263           9 :               if (length == 0) break;
    1264             :               FixedDoubleArray elements =
    1265             :                   FixedDoubleArray::cast(array->elements());
    1266           9 :               for (uint32_t i = 0; i < length; i++) {
    1267          18 :                 if (elements->is_the_hole(i)) {
    1268             :                   // TODO(jkummerow/verwaest): We could be a bit more clever
    1269             :                   // here: Check if there are no elements/getters on the
    1270             :                   // prototype chain, and if so, allow creation of a holey
    1271             :                   // result array.
    1272             :                   // Same thing below (holey smi case).
    1273             :                   failure = true;
    1274             :                   break;
    1275             :                 }
    1276             :                 double double_value = elements->get_scalar(i);
    1277           0 :                 double_storage->set(j, double_value);
    1278           0 :                 j++;
    1279             :               }
    1280             :               break;
    1281             :             }
    1282             :             case HOLEY_SMI_ELEMENTS:
    1283             :             case PACKED_SMI_ELEMENTS: {
    1284             :               Object the_hole = ReadOnlyRoots(isolate).the_hole_value();
    1285             :               FixedArray elements(FixedArray::cast(array->elements()));
    1286           0 :               for (uint32_t i = 0; i < length; i++) {
    1287           0 :                 Object element = elements->get(i);
    1288           0 :                 if (element == the_hole) {
    1289             :                   failure = true;
    1290             :                   break;
    1291             :                 }
    1292             :                 int32_t int_value = Smi::ToInt(element);
    1293           0 :                 double_storage->set(j, int_value);
    1294           0 :                 j++;
    1295             :               }
    1296             :               break;
    1297             :             }
    1298             :             case HOLEY_ELEMENTS:
    1299             :             case PACKED_ELEMENTS:
    1300             :             case DICTIONARY_ELEMENTS:
    1301             :             case NO_ELEMENTS:
    1302             :               DCHECK_EQ(0u, length);
    1303             :               break;
    1304             :             default:
    1305           0 :               UNREACHABLE();
    1306             :           }
    1307             :         }
    1308          27 :         if (failure) break;
    1309             :       }
    1310             :     }
    1311          18 :     if (!failure) {
    1312          18 :       return *isolate->factory()->NewJSArrayWithElements(storage, kind, j);
    1313             :     }
    1314             :     // In case of failure, fall through.
    1315             :   }
    1316             : 
    1317             :   Handle<HeapObject> storage;
    1318        6074 :   if (fast_case) {
    1319             :     // The backing storage array must have non-existing elements to preserve
    1320             :     // holes across concat operations.
    1321             :     storage =
    1322        2342 :         isolate->factory()->NewFixedArrayWithHoles(estimate_result_length);
    1323        3732 :   } else if (is_array_species) {
    1324        3444 :     storage = NumberDictionary::New(isolate, estimate_nof);
    1325             :   } else {
    1326             :     DCHECK(species->IsConstructor());
    1327             :     Handle<Object> length(Smi::kZero, isolate);
    1328             :     Handle<Object> storage_object;
    1329         576 :     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    1330             :         isolate, storage_object,
    1331             :         Execution::New(isolate, species, species, 1, &length));
    1332         288 :     storage = Handle<HeapObject>::cast(storage_object);
    1333             :   }
    1334             : 
    1335        6074 :   ArrayConcatVisitor visitor(isolate, storage, fast_case);
    1336             : 
    1337     1830244 :   for (int i = 0; i < argument_count; i++) {
    1338             :     Handle<Object> obj = args->at(i);
    1339      912301 :     Maybe<bool> spreadable = IsConcatSpreadable(isolate, obj);
    1340      912355 :     MAYBE_RETURN(spreadable, ReadOnlyRoots(isolate).exception());
    1341      912247 :     if (spreadable.FromJust()) {
    1342        7872 :       Handle<JSReceiver> object = Handle<JSReceiver>::cast(obj);
    1343        7872 :       if (!IterateElements(isolate, object, &visitor)) {
    1344         162 :         return ReadOnlyRoots(isolate).exception();
    1345             :       }
    1346             :     } else {
    1347      904375 :       if (!visitor.visit(0, obj)) return ReadOnlyRoots(isolate).exception();
    1348      904375 :       visitor.increase_index_offset(1);
    1349             :     }
    1350             :   }
    1351             : 
    1352        5858 :   if (visitor.exceeds_array_limit()) {
    1353          72 :     THROW_NEW_ERROR_RETURN_FAILURE(
    1354             :         isolate, NewRangeError(MessageTemplate::kInvalidArrayLength));
    1355             :   }
    1356             : 
    1357        5822 :   if (is_array_species) {
    1358       11176 :     return *visitor.ToArray();
    1359             :   } else {
    1360         477 :     RETURN_RESULT_OR_FAILURE(isolate, visitor.ToJSReceiver());
    1361             :   }
    1362             : }
    1363             : 
    1364      573804 : bool IsSimpleArray(Isolate* isolate, Handle<JSArray> obj) {
    1365             :   DisallowHeapAllocation no_gc;
    1366             :   Map map = obj->map();
    1367             :   // If there is only the 'length' property we are fine.
    1368      573804 :   if (map->prototype() ==
    1369     2295162 :           isolate->native_context()->initial_array_prototype() &&
    1370             :       map->NumberOfOwnDescriptors() == 1) {
    1371             :     return true;
    1372             :   }
    1373             :   // TODO(cbruni): slower lookup for array subclasses and support slow
    1374             :   // @@IsConcatSpreadable lookup.
    1375          54 :   return false;
    1376             : }
    1377             : 
    1378      294953 : MaybeHandle<JSArray> Fast_ArrayConcat(Isolate* isolate,
    1379             :                                       BuiltinArguments* args) {
    1380      294953 :   if (!isolate->IsIsConcatSpreadableLookupChainIntact()) {
    1381        5151 :     return MaybeHandle<JSArray>();
    1382             :   }
    1383             :   // We shouldn't overflow when adding another len.
    1384             :   const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2);
    1385             :   STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt);
    1386             :   STATIC_ASSERT(FixedDoubleArray::kMaxLength < kHalfOfMaxInt);
    1387             :   USE(kHalfOfMaxInt);
    1388             : 
    1389             :   int n_arguments = args->length();
    1390             :   int result_len = 0;
    1391             :   {
    1392             :     DisallowHeapAllocation no_gc;
    1393             :     // Iterate through all the arguments performing checks
    1394             :     // and calculating total length.
    1395     1437284 :     for (int i = 0; i < n_arguments; i++) {
    1396             :       Object arg = (*args)[i];
    1397      578992 :       if (!arg->IsJSArray()) return MaybeHandle<JSArray>();
    1398      574956 :       if (!HasOnlySimpleReceiverElements(isolate, JSObject::cast(arg))) {
    1399        1010 :         return MaybeHandle<JSArray>();
    1400             :       }
    1401             :       // TODO(cbruni): support fast concatenation of DICTIONARY_ELEMENTS.
    1402     1147892 :       if (!JSObject::cast(arg)->HasFastElements()) {
    1403         142 :         return MaybeHandle<JSArray>();
    1404             :       }
    1405             :       Handle<JSArray> array(JSArray::cast(arg), isolate);
    1406      573804 :       if (!IsSimpleArray(isolate, array)) {
    1407          54 :         return MaybeHandle<JSArray>();
    1408             :       }
    1409             :       // The Array length is guaranted to be <= kHalfOfMaxInt thus we won't
    1410             :       // overflow.
    1411      573750 :       result_len += Smi::ToInt(array->length());
    1412             :       DCHECK_GE(result_len, 0);
    1413             :       // Throw an Error if we overflow the FixedArray limits
    1414      573750 :       if (FixedDoubleArray::kMaxLength < result_len ||
    1415             :           FixedArray::kMaxLength < result_len) {
    1416             :         AllowHeapAllocation gc;
    1417          18 :         THROW_NEW_ERROR(isolate,
    1418             :                         NewRangeError(MessageTemplate::kInvalidArrayLength),
    1419             :                         JSArray);
    1420             :       }
    1421             :     }
    1422             :   }
    1423      284551 :   return ElementsAccessor::Concat(isolate, args, n_arguments, result_len);
    1424             : }
    1425             : 
    1426             : }  // namespace
    1427             : 
    1428             : // ES6 22.1.3.1 Array.prototype.concat
    1429     1454520 : BUILTIN(ArrayConcat) {
    1430             :   HandleScope scope(isolate);
    1431             : 
    1432             :   Handle<Object> receiver = args.receiver();
    1433      582069 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    1434             :       isolate, receiver,
    1435             :       Object::ToObject(isolate, args.receiver(), "Array.prototype.concat"));
    1436             :   args.set_at(0, *receiver);
    1437             : 
    1438             :   Handle<JSArray> result_array;
    1439             : 
    1440             :   // Avoid a real species read to avoid extra lookups to the array constructor
    1441      869787 :   if (V8_LIKELY(receiver->IsJSArray() &&
    1442             :                 Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) &&
    1443             :                 isolate->IsArraySpeciesLookupChainIntact())) {
    1444      578220 :     if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
    1445      284503 :       return *result_array;
    1446             :     }
    1447        4607 :     if (isolate->has_pending_exception())
    1448           9 :       return ReadOnlyRoots(isolate).exception();
    1449             :   }
    1450             :   // Reading @@species happens before anything else with a side effect, so
    1451             :   // we can do it here to determine whether to take the fast path.
    1452             :   Handle<Object> species;
    1453       12262 :   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
    1454             :       isolate, species, Object::ArraySpeciesConstructor(isolate, receiver));
    1455       12262 :   if (*species == *isolate->array_function()) {
    1456       11686 :     if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
    1457          48 :       return *result_array;
    1458             :     }
    1459        5795 :     if (isolate->has_pending_exception())
    1460           0 :       return ReadOnlyRoots(isolate).exception();
    1461             :   }
    1462        6083 :   return Slow_ArrayConcat(&args, species, isolate);
    1463             : }
    1464             : 
    1465             : }  // namespace internal
    1466      120216 : }  // namespace v8

Generated by: LCOV version 1.10