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