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