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