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