blob: 09ee4cc2e2d741cd1fc22f0187552fa189cb7cd6 [file] [log] [blame]
Ben Murdochf91f0612016-11-29 16:50:11 +00001// 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.h"
6#include "src/builtins/builtins-utils.h"
7
8#include "src/code-factory.h"
9#include "src/elements.h"
10
11namespace v8 {
12namespace internal {
13
14namespace {
15
16inline bool ClampedToInteger(Isolate* isolate, Object* object, int* out) {
17 // This is an extended version of ECMA-262 7.1.11 handling signed values
18 // Try to convert object to a number and clamp values to [kMinInt, kMaxInt]
19 if (object->IsSmi()) {
20 *out = Smi::cast(object)->value();
21 return true;
22 } else if (object->IsHeapNumber()) {
23 double value = HeapNumber::cast(object)->value();
24 if (std::isnan(value)) {
25 *out = 0;
26 } else if (value > kMaxInt) {
27 *out = kMaxInt;
28 } else if (value < kMinInt) {
29 *out = kMinInt;
30 } else {
31 *out = static_cast<int>(value);
32 }
33 return true;
34 } else if (object->IsUndefined(isolate) || object->IsNull(isolate)) {
35 *out = 0;
36 return true;
37 } else if (object->IsBoolean()) {
38 *out = object->IsTrue(isolate);
39 return true;
40 }
41 return false;
42}
43
44inline bool GetSloppyArgumentsLength(Isolate* isolate, Handle<JSObject> object,
45 int* out) {
46 Context* context = *isolate->native_context();
47 Map* map = object->map();
48 if (map != context->sloppy_arguments_map() &&
49 map != context->strict_arguments_map() &&
50 map != context->fast_aliased_arguments_map()) {
51 return false;
52 }
53 DCHECK(object->HasFastElements() || object->HasFastArgumentsElements());
54 Object* len_obj = object->InObjectPropertyAt(JSArgumentsObject::kLengthIndex);
55 if (!len_obj->IsSmi()) return false;
56 *out = Max(0, Smi::cast(len_obj)->value());
57 return *out <= object->elements()->length();
58}
59
60inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate,
61 JSArray* receiver) {
62 return JSObject::PrototypeHasNoElements(isolate, receiver);
63}
64
65inline bool HasSimpleElements(JSObject* current) {
66 return current->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER &&
67 !current->GetElementsAccessor()->HasAccessors(current);
68}
69
70inline bool HasOnlySimpleReceiverElements(Isolate* isolate,
71 JSObject* receiver) {
72 // Check that we have no accessors on the receiver's elements.
73 if (!HasSimpleElements(receiver)) return false;
74 return JSObject::PrototypeHasNoElements(isolate, receiver);
75}
76
77inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver* receiver) {
78 DisallowHeapAllocation no_gc;
79 PrototypeIterator iter(isolate, receiver, kStartAtReceiver);
80 for (; !iter.IsAtEnd(); iter.Advance()) {
81 if (iter.GetCurrent()->IsJSProxy()) return false;
82 JSObject* current = iter.GetCurrent<JSObject>();
83 if (!HasSimpleElements(current)) return false;
84 }
85 return true;
86}
87
88// Returns |false| if not applicable.
89MUST_USE_RESULT
90inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate,
91 Handle<Object> receiver,
92 BuiltinArguments* args,
93 int first_added_arg) {
94 if (!receiver->IsJSArray()) return false;
95 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
96 ElementsKind origin_kind = array->GetElementsKind();
97 if (IsDictionaryElementsKind(origin_kind)) return false;
98 if (!array->map()->is_extensible()) return false;
99 if (args == nullptr) return true;
100
101 // If there may be elements accessors in the prototype chain, the fast path
102 // cannot be used if there arguments to add to the array.
103 if (!IsJSArrayFastElementMovingAllowed(isolate, *array)) return false;
104
105 // Adding elements to the array prototype would break code that makes sure
106 // it has no elements. Handle that elsewhere.
107 if (isolate->IsAnyInitialArrayPrototype(array)) return false;
108
109 // Need to ensure that the arguments passed in args can be contained in
110 // the array.
111 int args_length = args->length();
112 if (first_added_arg >= args_length) return true;
113
114 if (IsFastObjectElementsKind(origin_kind)) return true;
115 ElementsKind target_kind = origin_kind;
116 {
117 DisallowHeapAllocation no_gc;
118 for (int i = first_added_arg; i < args_length; i++) {
119 Object* arg = (*args)[i];
120 if (arg->IsHeapObject()) {
121 if (arg->IsHeapNumber()) {
122 target_kind = FAST_DOUBLE_ELEMENTS;
123 } else {
124 target_kind = FAST_ELEMENTS;
125 break;
126 }
127 }
128 }
129 }
130 if (target_kind != origin_kind) {
131 // Use a short-lived HandleScope to avoid creating several copies of the
132 // elements handle which would cause issues when left-trimming later-on.
133 HandleScope scope(isolate);
134 JSObject::TransitionElementsKind(array, target_kind);
135 }
136 return true;
137}
138
139MUST_USE_RESULT static Object* CallJsIntrinsic(Isolate* isolate,
140 Handle<JSFunction> function,
141 BuiltinArguments args) {
142 HandleScope handleScope(isolate);
143 int argc = args.length() - 1;
144 ScopedVector<Handle<Object>> argv(argc);
145 for (int i = 0; i < argc; ++i) {
146 argv[i] = args.at<Object>(i + 1);
147 }
148 RETURN_RESULT_OR_FAILURE(
149 isolate,
150 Execution::Call(isolate, function, args.receiver(), argc, argv.start()));
151}
152
153Object* DoArrayPush(Isolate* isolate, BuiltinArguments args) {
154 HandleScope scope(isolate);
155 Handle<Object> receiver = args.receiver();
156 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
157 return CallJsIntrinsic(isolate, isolate->array_push(), args);
158 }
159 // Fast Elements Path
160 int to_add = args.length() - 1;
161 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
162 int len = Smi::cast(array->length())->value();
163 if (to_add == 0) return Smi::FromInt(len);
164
165 // Currently fixed arrays cannot grow too big, so we should never hit this.
166 DCHECK_LE(to_add, Smi::kMaxValue - Smi::cast(array->length())->value());
167
168 if (JSArray::HasReadOnlyLength(array)) {
169 return CallJsIntrinsic(isolate, isolate->array_push(), args);
170 }
171
172 ElementsAccessor* accessor = array->GetElementsAccessor();
173 int new_length = accessor->Push(array, &args, to_add);
174 return Smi::FromInt(new_length);
175}
176} // namespace
177
178BUILTIN(ArrayPush) { return DoArrayPush(isolate, args); }
179
180// TODO(verwaest): This is a temporary helper until the FastArrayPush stub can
181// tailcall to the builtin directly.
182RUNTIME_FUNCTION(Runtime_ArrayPush) {
183 DCHECK_EQ(2, args.length());
184 Arguments* incoming = reinterpret_cast<Arguments*>(args[0]);
185 // Rewrap the arguments as builtins arguments.
186 int argc = incoming->length() + BuiltinArguments::kNumExtraArgsWithReceiver;
187 BuiltinArguments caller_args(argc, incoming->arguments() + 1);
188 return DoArrayPush(isolate, caller_args);
189}
190
191BUILTIN(ArrayPop) {
192 HandleScope scope(isolate);
193 Handle<Object> receiver = args.receiver();
194 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0)) {
195 return CallJsIntrinsic(isolate, isolate->array_pop(), args);
196 }
197
198 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
199
200 uint32_t len = static_cast<uint32_t>(Smi::cast(array->length())->value());
201 if (len == 0) return isolate->heap()->undefined_value();
202
203 if (JSArray::HasReadOnlyLength(array)) {
204 return CallJsIntrinsic(isolate, isolate->array_pop(), args);
205 }
206
207 Handle<Object> result;
208 if (IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
209 // Fast Elements Path
210 result = array->GetElementsAccessor()->Pop(array);
211 } else {
212 // Use Slow Lookup otherwise
213 uint32_t new_length = len - 1;
214 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
215 isolate, result, JSReceiver::GetElement(isolate, array, new_length));
216 JSArray::SetLength(array, new_length);
217 }
218 return *result;
219}
220
221BUILTIN(ArrayShift) {
222 HandleScope scope(isolate);
223 Heap* heap = isolate->heap();
224 Handle<Object> receiver = args.receiver();
225 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0) ||
226 !IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
227 return CallJsIntrinsic(isolate, isolate->array_shift(), args);
228 }
229 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
230
231 int len = Smi::cast(array->length())->value();
232 if (len == 0) return heap->undefined_value();
233
234 if (JSArray::HasReadOnlyLength(array)) {
235 return CallJsIntrinsic(isolate, isolate->array_shift(), args);
236 }
237
238 Handle<Object> first = array->GetElementsAccessor()->Shift(array);
239 return *first;
240}
241
242BUILTIN(ArrayUnshift) {
243 HandleScope scope(isolate);
244 Handle<Object> receiver = args.receiver();
245 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
246 return CallJsIntrinsic(isolate, isolate->array_unshift(), args);
247 }
248 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
249 int to_add = args.length() - 1;
250 if (to_add == 0) return array->length();
251
252 // Currently fixed arrays cannot grow too big, so we should never hit this.
253 DCHECK_LE(to_add, Smi::kMaxValue - Smi::cast(array->length())->value());
254
255 if (JSArray::HasReadOnlyLength(array)) {
256 return CallJsIntrinsic(isolate, isolate->array_unshift(), args);
257 }
258
259 ElementsAccessor* accessor = array->GetElementsAccessor();
260 int new_length = accessor->Unshift(array, &args, to_add);
261 return Smi::FromInt(new_length);
262}
263
264BUILTIN(ArraySlice) {
265 HandleScope scope(isolate);
266 Handle<Object> receiver = args.receiver();
267 int len = -1;
268 int relative_start = 0;
269 int relative_end = 0;
270
271 if (receiver->IsJSArray()) {
272 DisallowHeapAllocation no_gc;
273 JSArray* array = JSArray::cast(*receiver);
274 if (V8_UNLIKELY(!array->HasFastElements() ||
275 !IsJSArrayFastElementMovingAllowed(isolate, array) ||
276 !isolate->IsArraySpeciesLookupChainIntact() ||
277 // If this is a subclass of Array, then call out to JS
278 !array->HasArrayPrototype(isolate))) {
279 AllowHeapAllocation allow_allocation;
280 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
281 }
282 len = Smi::cast(array->length())->value();
283 } else if (receiver->IsJSObject() &&
284 GetSloppyArgumentsLength(isolate, Handle<JSObject>::cast(receiver),
285 &len)) {
286 // Array.prototype.slice.call(arguments, ...) is quite a common idiom
287 // (notably more than 50% of invocations in Web apps).
288 // Treat it in C++ as well.
289 DCHECK(JSObject::cast(*receiver)->HasFastElements() ||
290 JSObject::cast(*receiver)->HasFastArgumentsElements());
291 } else {
292 AllowHeapAllocation allow_allocation;
293 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
294 }
295 DCHECK_LE(0, len);
296 int argument_count = args.length() - 1;
297 // Note carefully chosen defaults---if argument is missing,
298 // it's undefined which gets converted to 0 for relative_start
299 // and to len for relative_end.
300 relative_start = 0;
301 relative_end = len;
302 if (argument_count > 0) {
303 DisallowHeapAllocation no_gc;
304 if (!ClampedToInteger(isolate, args[1], &relative_start)) {
305 AllowHeapAllocation allow_allocation;
306 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
307 }
308 if (argument_count > 1) {
309 Object* end_arg = args[2];
310 // slice handles the end_arg specially
311 if (end_arg->IsUndefined(isolate)) {
312 relative_end = len;
313 } else if (!ClampedToInteger(isolate, end_arg, &relative_end)) {
314 AllowHeapAllocation allow_allocation;
315 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
316 }
317 }
318 }
319
320 // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
321 uint32_t actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
322 : Min(relative_start, len);
323
324 // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
325 uint32_t actual_end =
326 (relative_end < 0) ? Max(len + relative_end, 0) : Min(relative_end, len);
327
328 Handle<JSObject> object = Handle<JSObject>::cast(receiver);
329 ElementsAccessor* accessor = object->GetElementsAccessor();
330 return *accessor->Slice(object, actual_start, actual_end);
331}
332
333BUILTIN(ArraySplice) {
334 HandleScope scope(isolate);
335 Handle<Object> receiver = args.receiver();
336 if (V8_UNLIKELY(
337 !EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3) ||
338 // If this is a subclass of Array, then call out to JS.
339 !Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) ||
340 // If anything with @@species has been messed with, call out to JS.
341 !isolate->IsArraySpeciesLookupChainIntact())) {
342 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
343 }
344 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
345
346 int argument_count = args.length() - 1;
347 int relative_start = 0;
348 if (argument_count > 0) {
349 DisallowHeapAllocation no_gc;
350 if (!ClampedToInteger(isolate, args[1], &relative_start)) {
351 AllowHeapAllocation allow_allocation;
352 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
353 }
354 }
355 int len = Smi::cast(array->length())->value();
356 // clip relative start to [0, len]
357 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
358 : Min(relative_start, len);
359
360 int actual_delete_count;
361 if (argument_count == 1) {
362 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
363 // given as a request to delete all the elements from the start.
364 // And it differs from the case of undefined delete count.
365 // This does not follow ECMA-262, but we do the same for compatibility.
366 DCHECK(len - actual_start >= 0);
367 actual_delete_count = len - actual_start;
368 } else {
369 int delete_count = 0;
370 DisallowHeapAllocation no_gc;
371 if (argument_count > 1) {
372 if (!ClampedToInteger(isolate, args[2], &delete_count)) {
373 AllowHeapAllocation allow_allocation;
374 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
375 }
376 }
377 actual_delete_count = Min(Max(delete_count, 0), len - actual_start);
378 }
379
380 int add_count = (argument_count > 1) ? (argument_count - 2) : 0;
381 int new_length = len - actual_delete_count + add_count;
382
383 if (new_length != len && JSArray::HasReadOnlyLength(array)) {
384 AllowHeapAllocation allow_allocation;
385 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
386 }
387 ElementsAccessor* accessor = array->GetElementsAccessor();
388 Handle<JSArray> result_array = accessor->Splice(
389 array, actual_start, actual_delete_count, &args, add_count);
390 return *result_array;
391}
392
393// Array Concat -------------------------------------------------------------
394
395namespace {
396
397/**
398 * A simple visitor visits every element of Array's.
399 * The backend storage can be a fixed array for fast elements case,
400 * or a dictionary for sparse array. Since Dictionary is a subtype
401 * of FixedArray, the class can be used by both fast and slow cases.
402 * The second parameter of the constructor, fast_elements, specifies
403 * whether the storage is a FixedArray or Dictionary.
404 *
405 * An index limit is used to deal with the situation that a result array
406 * length overflows 32-bit non-negative integer.
407 */
408class ArrayConcatVisitor {
409 public:
410 ArrayConcatVisitor(Isolate* isolate, Handle<Object> storage,
411 bool fast_elements)
412 : isolate_(isolate),
413 storage_(isolate->global_handles()->Create(*storage)),
414 index_offset_(0u),
415 bit_field_(FastElementsField::encode(fast_elements) |
416 ExceedsLimitField::encode(false) |
417 IsFixedArrayField::encode(storage->IsFixedArray())) {
418 DCHECK(!(this->fast_elements() && !is_fixed_array()));
419 }
420
421 ~ArrayConcatVisitor() { clear_storage(); }
422
423 MUST_USE_RESULT bool visit(uint32_t i, Handle<Object> elm) {
424 uint32_t index = index_offset_ + i;
425
426 if (i >= JSObject::kMaxElementCount - index_offset_) {
427 set_exceeds_array_limit(true);
428 // Exception hasn't been thrown at this point. Return true to
429 // break out, and caller will throw. !visit would imply that
430 // there is already a pending exception.
431 return true;
432 }
433
434 if (!is_fixed_array()) {
435 LookupIterator it(isolate_, storage_, index, LookupIterator::OWN);
436 MAYBE_RETURN(
437 JSReceiver::CreateDataProperty(&it, elm, Object::THROW_ON_ERROR),
438 false);
439 return true;
440 }
441
442 if (fast_elements()) {
443 if (index < static_cast<uint32_t>(storage_fixed_array()->length())) {
444 storage_fixed_array()->set(index, *elm);
445 return true;
446 }
447 // Our initial estimate of length was foiled, possibly by
448 // getters on the arrays increasing the length of later arrays
449 // during iteration.
450 // This shouldn't happen in anything but pathological cases.
451 SetDictionaryMode();
452 // Fall-through to dictionary mode.
453 }
454 DCHECK(!fast_elements());
455 Handle<SeededNumberDictionary> dict(
456 SeededNumberDictionary::cast(*storage_));
457 // The object holding this backing store has just been allocated, so
458 // it cannot yet be used as a prototype.
459 Handle<SeededNumberDictionary> result =
460 SeededNumberDictionary::AtNumberPut(dict, index, elm, false);
461 if (!result.is_identical_to(dict)) {
462 // Dictionary needed to grow.
463 clear_storage();
464 set_storage(*result);
465 }
466 return true;
467 }
468
469 void increase_index_offset(uint32_t delta) {
470 if (JSObject::kMaxElementCount - index_offset_ < delta) {
471 index_offset_ = JSObject::kMaxElementCount;
472 } else {
473 index_offset_ += delta;
474 }
475 // If the initial length estimate was off (see special case in visit()),
476 // but the array blowing the limit didn't contain elements beyond the
477 // provided-for index range, go to dictionary mode now.
478 if (fast_elements() &&
479 index_offset_ >
480 static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) {
481 SetDictionaryMode();
482 }
483 }
484
485 bool exceeds_array_limit() const {
486 return ExceedsLimitField::decode(bit_field_);
487 }
488
489 Handle<JSArray> ToArray() {
490 DCHECK(is_fixed_array());
491 Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
492 Handle<Object> length =
493 isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
494 Handle<Map> map = JSObject::GetElementsTransitionMap(
495 array, fast_elements() ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS);
496 array->set_map(*map);
497 array->set_length(*length);
498 array->set_elements(*storage_fixed_array());
499 return array;
500 }
501
502 // Storage is either a FixedArray (if is_fixed_array()) or a JSReciever
503 // (otherwise)
504 Handle<FixedArray> storage_fixed_array() {
505 DCHECK(is_fixed_array());
506 return Handle<FixedArray>::cast(storage_);
507 }
508 Handle<JSReceiver> storage_jsreceiver() {
509 DCHECK(!is_fixed_array());
510 return Handle<JSReceiver>::cast(storage_);
511 }
512
513 private:
514 // Convert storage to dictionary mode.
515 void SetDictionaryMode() {
516 DCHECK(fast_elements() && is_fixed_array());
517 Handle<FixedArray> current_storage = storage_fixed_array();
518 Handle<SeededNumberDictionary> slow_storage(
519 SeededNumberDictionary::New(isolate_, current_storage->length()));
520 uint32_t current_length = static_cast<uint32_t>(current_storage->length());
521 FOR_WITH_HANDLE_SCOPE(
522 isolate_, uint32_t, i = 0, i, i < current_length, i++, {
523 Handle<Object> element(current_storage->get(i), isolate_);
524 if (!element->IsTheHole(isolate_)) {
525 // The object holding this backing store has just been allocated, so
526 // it cannot yet be used as a prototype.
527 Handle<SeededNumberDictionary> new_storage =
528 SeededNumberDictionary::AtNumberPut(slow_storage, i, element,
529 false);
530 if (!new_storage.is_identical_to(slow_storage)) {
531 slow_storage = loop_scope.CloseAndEscape(new_storage);
532 }
533 }
534 });
535 clear_storage();
536 set_storage(*slow_storage);
537 set_fast_elements(false);
538 }
539
540 inline void clear_storage() { GlobalHandles::Destroy(storage_.location()); }
541
542 inline void set_storage(FixedArray* storage) {
543 DCHECK(is_fixed_array());
544 storage_ = isolate_->global_handles()->Create(storage);
545 }
546
547 class FastElementsField : public BitField<bool, 0, 1> {};
548 class ExceedsLimitField : public BitField<bool, 1, 1> {};
549 class IsFixedArrayField : public BitField<bool, 2, 1> {};
550
551 bool fast_elements() const { return FastElementsField::decode(bit_field_); }
552 void set_fast_elements(bool fast) {
553 bit_field_ = FastElementsField::update(bit_field_, fast);
554 }
555 void set_exceeds_array_limit(bool exceeds) {
556 bit_field_ = ExceedsLimitField::update(bit_field_, exceeds);
557 }
558 bool is_fixed_array() const { return IsFixedArrayField::decode(bit_field_); }
559
560 Isolate* isolate_;
561 Handle<Object> storage_; // Always a global handle.
562 // Index after last seen index. Always less than or equal to
563 // JSObject::kMaxElementCount.
564 uint32_t index_offset_;
565 uint32_t bit_field_;
566};
567
568uint32_t EstimateElementCount(Handle<JSArray> array) {
569 DisallowHeapAllocation no_gc;
570 uint32_t length = static_cast<uint32_t>(array->length()->Number());
571 int element_count = 0;
572 switch (array->GetElementsKind()) {
573 case FAST_SMI_ELEMENTS:
574 case FAST_HOLEY_SMI_ELEMENTS:
575 case FAST_ELEMENTS:
576 case FAST_HOLEY_ELEMENTS: {
577 // Fast elements can't have lengths that are not representable by
578 // a 32-bit signed integer.
579 DCHECK(static_cast<int32_t>(FixedArray::kMaxLength) >= 0);
580 int fast_length = static_cast<int>(length);
581 Isolate* isolate = array->GetIsolate();
582 FixedArray* elements = FixedArray::cast(array->elements());
583 for (int i = 0; i < fast_length; i++) {
584 if (!elements->get(i)->IsTheHole(isolate)) element_count++;
585 }
586 break;
587 }
588 case FAST_DOUBLE_ELEMENTS:
589 case FAST_HOLEY_DOUBLE_ELEMENTS: {
590 // Fast elements can't have lengths that are not representable by
591 // a 32-bit signed integer.
592 DCHECK(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0);
593 int fast_length = static_cast<int>(length);
594 if (array->elements()->IsFixedArray()) {
595 DCHECK(FixedArray::cast(array->elements())->length() == 0);
596 break;
597 }
598 FixedDoubleArray* elements = FixedDoubleArray::cast(array->elements());
599 for (int i = 0; i < fast_length; i++) {
600 if (!elements->is_the_hole(i)) element_count++;
601 }
602 break;
603 }
604 case DICTIONARY_ELEMENTS: {
605 SeededNumberDictionary* dictionary =
606 SeededNumberDictionary::cast(array->elements());
607 Isolate* isolate = dictionary->GetIsolate();
608 int capacity = dictionary->Capacity();
609 for (int i = 0; i < capacity; i++) {
610 Object* key = dictionary->KeyAt(i);
611 if (dictionary->IsKey(isolate, key)) {
612 element_count++;
613 }
614 }
615 break;
616 }
617#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
618
619 TYPED_ARRAYS(TYPED_ARRAY_CASE)
620#undef TYPED_ARRAY_CASE
621 // External arrays are always dense.
622 return length;
623 case NO_ELEMENTS:
624 return 0;
625 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
626 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
627 case FAST_STRING_WRAPPER_ELEMENTS:
628 case SLOW_STRING_WRAPPER_ELEMENTS:
629 UNREACHABLE();
630 return 0;
631 }
632 // As an estimate, we assume that the prototype doesn't contain any
633 // inherited elements.
634 return element_count;
635}
636
637// Used for sorting indices in a List<uint32_t>.
638int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
639 uint32_t a = *ap;
640 uint32_t b = *bp;
641 return (a == b) ? 0 : (a < b) ? -1 : 1;
642}
643
644void CollectElementIndices(Handle<JSObject> object, uint32_t range,
645 List<uint32_t>* indices) {
646 Isolate* isolate = object->GetIsolate();
647 ElementsKind kind = object->GetElementsKind();
648 switch (kind) {
649 case FAST_SMI_ELEMENTS:
650 case FAST_ELEMENTS:
651 case FAST_HOLEY_SMI_ELEMENTS:
652 case FAST_HOLEY_ELEMENTS: {
653 DisallowHeapAllocation no_gc;
654 FixedArray* elements = FixedArray::cast(object->elements());
655 uint32_t length = static_cast<uint32_t>(elements->length());
656 if (range < length) length = range;
657 for (uint32_t i = 0; i < length; i++) {
658 if (!elements->get(i)->IsTheHole(isolate)) {
659 indices->Add(i);
660 }
661 }
662 break;
663 }
664 case FAST_HOLEY_DOUBLE_ELEMENTS:
665 case FAST_DOUBLE_ELEMENTS: {
666 if (object->elements()->IsFixedArray()) {
667 DCHECK(object->elements()->length() == 0);
668 break;
669 }
670 Handle<FixedDoubleArray> elements(
671 FixedDoubleArray::cast(object->elements()));
672 uint32_t length = static_cast<uint32_t>(elements->length());
673 if (range < length) length = range;
674 for (uint32_t i = 0; i < length; i++) {
675 if (!elements->is_the_hole(i)) {
676 indices->Add(i);
677 }
678 }
679 break;
680 }
681 case DICTIONARY_ELEMENTS: {
682 DisallowHeapAllocation no_gc;
683 SeededNumberDictionary* dict =
684 SeededNumberDictionary::cast(object->elements());
685 uint32_t capacity = dict->Capacity();
686 FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, j = 0, j, j < capacity, j++, {
687 Object* k = dict->KeyAt(j);
688 if (!dict->IsKey(isolate, k)) continue;
689 DCHECK(k->IsNumber());
690 uint32_t index = static_cast<uint32_t>(k->Number());
691 if (index < range) {
692 indices->Add(index);
693 }
694 });
695 break;
696 }
697#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
698
699 TYPED_ARRAYS(TYPED_ARRAY_CASE)
700#undef TYPED_ARRAY_CASE
701 {
702 uint32_t length = static_cast<uint32_t>(
703 FixedArrayBase::cast(object->elements())->length());
704 if (range <= length) {
705 length = range;
706 // We will add all indices, so we might as well clear it first
707 // and avoid duplicates.
708 indices->Clear();
709 }
710 for (uint32_t i = 0; i < length; i++) {
711 indices->Add(i);
712 }
713 if (length == range) return; // All indices accounted for already.
714 break;
715 }
716 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
717 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
718 ElementsAccessor* accessor = object->GetElementsAccessor();
719 for (uint32_t i = 0; i < range; i++) {
720 if (accessor->HasElement(object, i)) {
721 indices->Add(i);
722 }
723 }
724 break;
725 }
726 case FAST_STRING_WRAPPER_ELEMENTS:
727 case SLOW_STRING_WRAPPER_ELEMENTS: {
728 DCHECK(object->IsJSValue());
729 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
730 DCHECK(js_value->value()->IsString());
731 Handle<String> string(String::cast(js_value->value()), isolate);
732 uint32_t length = static_cast<uint32_t>(string->length());
733 uint32_t i = 0;
734 uint32_t limit = Min(length, range);
735 for (; i < limit; i++) {
736 indices->Add(i);
737 }
738 ElementsAccessor* accessor = object->GetElementsAccessor();
739 for (; i < range; i++) {
740 if (accessor->HasElement(object, i)) {
741 indices->Add(i);
742 }
743 }
744 break;
745 }
746 case NO_ELEMENTS:
747 break;
748 }
749
750 PrototypeIterator iter(isolate, object);
751 if (!iter.IsAtEnd()) {
752 // The prototype will usually have no inherited element indices,
753 // but we have to check.
754 CollectElementIndices(PrototypeIterator::GetCurrent<JSObject>(iter), range,
755 indices);
756 }
757}
758
759bool IterateElementsSlow(Isolate* isolate, Handle<JSReceiver> receiver,
760 uint32_t length, ArrayConcatVisitor* visitor) {
761 FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, i = 0, i, i < length, ++i, {
762 Maybe<bool> maybe = JSReceiver::HasElement(receiver, i);
763 if (!maybe.IsJust()) return false;
764 if (maybe.FromJust()) {
765 Handle<Object> element_value;
766 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
767 isolate, element_value, JSReceiver::GetElement(isolate, receiver, i),
768 false);
769 if (!visitor->visit(i, element_value)) return false;
770 }
771 });
772 visitor->increase_index_offset(length);
773 return true;
774}
775
776/**
777 * A helper function that visits "array" elements of a JSReceiver in numerical
778 * order.
779 *
780 * The visitor argument called for each existing element in the array
781 * with the element index and the element's value.
782 * Afterwards it increments the base-index of the visitor by the array
783 * length.
784 * Returns false if any access threw an exception, otherwise true.
785 */
786bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver,
787 ArrayConcatVisitor* visitor) {
788 uint32_t length = 0;
789
790 if (receiver->IsJSArray()) {
791 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
792 length = static_cast<uint32_t>(array->length()->Number());
793 } else {
794 Handle<Object> val;
795 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
796 isolate, val, Object::GetLengthFromArrayLike(isolate, receiver), false);
797 // TODO(caitp): Support larger element indexes (up to 2^53-1).
798 if (!val->ToUint32(&length)) {
799 length = 0;
800 }
801 // TODO(cbruni): handle other element kind as well
802 return IterateElementsSlow(isolate, receiver, length, visitor);
803 }
804
805 if (!HasOnlySimpleElements(isolate, *receiver)) {
806 return IterateElementsSlow(isolate, receiver, length, visitor);
807 }
808 Handle<JSObject> array = Handle<JSObject>::cast(receiver);
809
810 switch (array->GetElementsKind()) {
811 case FAST_SMI_ELEMENTS:
812 case FAST_ELEMENTS:
813 case FAST_HOLEY_SMI_ELEMENTS:
814 case FAST_HOLEY_ELEMENTS: {
815 // Run through the elements FixedArray and use HasElement and GetElement
816 // to check the prototype for missing elements.
817 Handle<FixedArray> elements(FixedArray::cast(array->elements()));
818 int fast_length = static_cast<int>(length);
819 DCHECK(fast_length <= elements->length());
820 FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
821 Handle<Object> element_value(elements->get(j), isolate);
822 if (!element_value->IsTheHole(isolate)) {
823 if (!visitor->visit(j, element_value)) return false;
824 } else {
825 Maybe<bool> maybe = JSReceiver::HasElement(array, j);
826 if (!maybe.IsJust()) return false;
827 if (maybe.FromJust()) {
828 // Call GetElement on array, not its prototype, or getters won't
829 // have the correct receiver.
830 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
831 isolate, element_value,
832 JSReceiver::GetElement(isolate, array, j), false);
833 if (!visitor->visit(j, element_value)) return false;
834 }
835 }
836 });
837 break;
838 }
839 case FAST_HOLEY_DOUBLE_ELEMENTS:
840 case FAST_DOUBLE_ELEMENTS: {
841 // Empty array is FixedArray but not FixedDoubleArray.
842 if (length == 0) break;
843 // Run through the elements FixedArray and use HasElement and GetElement
844 // to check the prototype for missing elements.
845 if (array->elements()->IsFixedArray()) {
846 DCHECK(array->elements()->length() == 0);
847 break;
848 }
849 Handle<FixedDoubleArray> elements(
850 FixedDoubleArray::cast(array->elements()));
851 int fast_length = static_cast<int>(length);
852 DCHECK(fast_length <= elements->length());
853 FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
854 if (!elements->is_the_hole(j)) {
855 double double_value = elements->get_scalar(j);
856 Handle<Object> element_value =
857 isolate->factory()->NewNumber(double_value);
858 if (!visitor->visit(j, element_value)) return false;
859 } else {
860 Maybe<bool> maybe = JSReceiver::HasElement(array, j);
861 if (!maybe.IsJust()) return false;
862 if (maybe.FromJust()) {
863 // Call GetElement on array, not its prototype, or getters won't
864 // have the correct receiver.
865 Handle<Object> element_value;
866 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
867 isolate, element_value,
868 JSReceiver::GetElement(isolate, array, j), false);
869 if (!visitor->visit(j, element_value)) return false;
870 }
871 }
872 });
873 break;
874 }
875
876 case DICTIONARY_ELEMENTS: {
877 Handle<SeededNumberDictionary> dict(array->element_dictionary());
878 List<uint32_t> indices(dict->Capacity() / 2);
879 // Collect all indices in the object and the prototypes less
880 // than length. This might introduce duplicates in the indices list.
881 CollectElementIndices(array, length, &indices);
882 indices.Sort(&compareUInt32);
883 int n = indices.length();
884 FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < n, (void)0, {
885 uint32_t index = indices[j];
886 Handle<Object> element;
887 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
888 isolate, element, JSReceiver::GetElement(isolate, array, index),
889 false);
890 if (!visitor->visit(index, element)) return false;
891 // Skip to next different index (i.e., omit duplicates).
892 do {
893 j++;
894 } while (j < n && indices[j] == index);
895 });
896 break;
897 }
898 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
899 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
900 FOR_WITH_HANDLE_SCOPE(
901 isolate, uint32_t, index = 0, index, index < length, index++, {
902 Handle<Object> element;
903 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
904 isolate, element, JSReceiver::GetElement(isolate, array, index),
905 false);
906 if (!visitor->visit(index, element)) return false;
907 });
908 break;
909 }
910 case NO_ELEMENTS:
911 break;
912#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
913 TYPED_ARRAYS(TYPED_ARRAY_CASE)
914#undef TYPED_ARRAY_CASE
915 return IterateElementsSlow(isolate, receiver, length, visitor);
916 case FAST_STRING_WRAPPER_ELEMENTS:
917 case SLOW_STRING_WRAPPER_ELEMENTS:
918 // |array| is guaranteed to be an array or typed array.
919 UNREACHABLE();
920 break;
921 }
922 visitor->increase_index_offset(length);
923 return true;
924}
925
926static Maybe<bool> IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
927 HandleScope handle_scope(isolate);
928 if (!obj->IsJSReceiver()) return Just(false);
929 if (!isolate->IsIsConcatSpreadableLookupChainIntact(JSReceiver::cast(*obj))) {
930 // Slow path if @@isConcatSpreadable has been used.
931 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol());
932 Handle<Object> value;
933 MaybeHandle<Object> maybeValue =
934 i::Runtime::GetObjectProperty(isolate, obj, key);
935 if (!maybeValue.ToHandle(&value)) return Nothing<bool>();
936 if (!value->IsUndefined(isolate)) return Just(value->BooleanValue());
937 }
938 return Object::IsArray(obj);
939}
940
941Object* Slow_ArrayConcat(BuiltinArguments* args, Handle<Object> species,
942 Isolate* isolate) {
943 int argument_count = args->length();
944
945 bool is_array_species = *species == isolate->context()->array_function();
946
947 // Pass 1: estimate the length and number of elements of the result.
948 // The actual length can be larger if any of the arguments have getters
949 // that mutate other arguments (but will otherwise be precise).
950 // The number of elements is precise if there are no inherited elements.
951
952 ElementsKind kind = FAST_SMI_ELEMENTS;
953
954 uint32_t estimate_result_length = 0;
955 uint32_t estimate_nof_elements = 0;
956 FOR_WITH_HANDLE_SCOPE(isolate, int, i = 0, i, i < argument_count, i++, {
957 Handle<Object> obj((*args)[i], isolate);
958 uint32_t length_estimate;
959 uint32_t element_estimate;
960 if (obj->IsJSArray()) {
961 Handle<JSArray> array(Handle<JSArray>::cast(obj));
962 length_estimate = static_cast<uint32_t>(array->length()->Number());
963 if (length_estimate != 0) {
964 ElementsKind array_kind =
965 GetPackedElementsKind(array->GetElementsKind());
966 kind = GetMoreGeneralElementsKind(kind, array_kind);
967 }
968 element_estimate = EstimateElementCount(array);
969 } else {
970 if (obj->IsHeapObject()) {
971 kind = GetMoreGeneralElementsKind(
972 kind, obj->IsNumber() ? FAST_DOUBLE_ELEMENTS : FAST_ELEMENTS);
973 }
974 length_estimate = 1;
975 element_estimate = 1;
976 }
977 // Avoid overflows by capping at kMaxElementCount.
978 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) {
979 estimate_result_length = JSObject::kMaxElementCount;
980 } else {
981 estimate_result_length += length_estimate;
982 }
983 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) {
984 estimate_nof_elements = JSObject::kMaxElementCount;
985 } else {
986 estimate_nof_elements += element_estimate;
987 }
988 });
989
990 // If estimated number of elements is more than half of length, a
991 // fixed array (fast case) is more time and space-efficient than a
992 // dictionary.
993 bool fast_case =
994 is_array_species && (estimate_nof_elements * 2) >= estimate_result_length;
995
996 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) {
997 Handle<FixedArrayBase> storage =
998 isolate->factory()->NewFixedDoubleArray(estimate_result_length);
999 int j = 0;
1000 bool failure = false;
1001 if (estimate_result_length > 0) {
1002 Handle<FixedDoubleArray> double_storage =
1003 Handle<FixedDoubleArray>::cast(storage);
1004 for (int i = 0; i < argument_count; i++) {
1005 Handle<Object> obj((*args)[i], isolate);
1006 if (obj->IsSmi()) {
1007 double_storage->set(j, Smi::cast(*obj)->value());
1008 j++;
1009 } else if (obj->IsNumber()) {
1010 double_storage->set(j, obj->Number());
1011 j++;
1012 } else {
1013 DisallowHeapAllocation no_gc;
1014 JSArray* array = JSArray::cast(*obj);
1015 uint32_t length = static_cast<uint32_t>(array->length()->Number());
1016 switch (array->GetElementsKind()) {
1017 case FAST_HOLEY_DOUBLE_ELEMENTS:
1018 case FAST_DOUBLE_ELEMENTS: {
1019 // Empty array is FixedArray but not FixedDoubleArray.
1020 if (length == 0) break;
1021 FixedDoubleArray* elements =
1022 FixedDoubleArray::cast(array->elements());
1023 for (uint32_t i = 0; i < length; i++) {
1024 if (elements->is_the_hole(i)) {
1025 // TODO(jkummerow/verwaest): We could be a bit more clever
1026 // here: Check if there are no elements/getters on the
1027 // prototype chain, and if so, allow creation of a holey
1028 // result array.
1029 // Same thing below (holey smi case).
1030 failure = true;
1031 break;
1032 }
1033 double double_value = elements->get_scalar(i);
1034 double_storage->set(j, double_value);
1035 j++;
1036 }
1037 break;
1038 }
1039 case FAST_HOLEY_SMI_ELEMENTS:
1040 case FAST_SMI_ELEMENTS: {
1041 Object* the_hole = isolate->heap()->the_hole_value();
1042 FixedArray* elements(FixedArray::cast(array->elements()));
1043 for (uint32_t i = 0; i < length; i++) {
1044 Object* element = elements->get(i);
1045 if (element == the_hole) {
1046 failure = true;
1047 break;
1048 }
1049 int32_t int_value = Smi::cast(element)->value();
1050 double_storage->set(j, int_value);
1051 j++;
1052 }
1053 break;
1054 }
1055 case FAST_HOLEY_ELEMENTS:
1056 case FAST_ELEMENTS:
1057 case DICTIONARY_ELEMENTS:
1058 case NO_ELEMENTS:
1059 DCHECK_EQ(0u, length);
1060 break;
1061 default:
1062 UNREACHABLE();
1063 }
1064 }
1065 if (failure) break;
1066 }
1067 }
1068 if (!failure) {
1069 return *isolate->factory()->NewJSArrayWithElements(storage, kind, j);
1070 }
1071 // In case of failure, fall through.
1072 }
1073
1074 Handle<Object> storage;
1075 if (fast_case) {
1076 // The backing storage array must have non-existing elements to preserve
1077 // holes across concat operations.
1078 storage =
1079 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length);
1080 } else if (is_array_species) {
1081 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
1082 uint32_t at_least_space_for =
1083 estimate_nof_elements + (estimate_nof_elements >> 2);
1084 storage = SeededNumberDictionary::New(isolate, at_least_space_for);
1085 } else {
1086 DCHECK(species->IsConstructor());
1087 Handle<Object> length(Smi::FromInt(0), isolate);
1088 Handle<Object> storage_object;
1089 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1090 isolate, storage_object,
1091 Execution::New(isolate, species, species, 1, &length));
1092 storage = storage_object;
1093 }
1094
1095 ArrayConcatVisitor visitor(isolate, storage, fast_case);
1096
1097 for (int i = 0; i < argument_count; i++) {
1098 Handle<Object> obj((*args)[i], isolate);
1099 Maybe<bool> spreadable = IsConcatSpreadable(isolate, obj);
1100 MAYBE_RETURN(spreadable, isolate->heap()->exception());
1101 if (spreadable.FromJust()) {
1102 Handle<JSReceiver> object = Handle<JSReceiver>::cast(obj);
1103 if (!IterateElements(isolate, object, &visitor)) {
1104 return isolate->heap()->exception();
1105 }
1106 } else {
1107 if (!visitor.visit(0, obj)) return isolate->heap()->exception();
1108 visitor.increase_index_offset(1);
1109 }
1110 }
1111
1112 if (visitor.exceeds_array_limit()) {
1113 THROW_NEW_ERROR_RETURN_FAILURE(
1114 isolate, NewRangeError(MessageTemplate::kInvalidArrayLength));
1115 }
1116
1117 if (is_array_species) {
1118 return *visitor.ToArray();
1119 } else {
1120 return *visitor.storage_jsreceiver();
1121 }
1122}
1123
1124bool IsSimpleArray(Isolate* isolate, Handle<JSArray> obj) {
1125 DisallowHeapAllocation no_gc;
1126 Map* map = obj->map();
1127 // If there is only the 'length' property we are fine.
1128 if (map->prototype() ==
1129 isolate->native_context()->initial_array_prototype() &&
1130 map->NumberOfOwnDescriptors() == 1) {
1131 return true;
1132 }
1133 // TODO(cbruni): slower lookup for array subclasses and support slow
1134 // @@IsConcatSpreadable lookup.
1135 return false;
1136}
1137
1138MaybeHandle<JSArray> Fast_ArrayConcat(Isolate* isolate,
1139 BuiltinArguments* args) {
1140 if (!isolate->IsIsConcatSpreadableLookupChainIntact()) {
1141 return MaybeHandle<JSArray>();
1142 }
1143 // We shouldn't overflow when adding another len.
1144 const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2);
1145 STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt);
1146 STATIC_ASSERT(FixedDoubleArray::kMaxLength < kHalfOfMaxInt);
1147 USE(kHalfOfMaxInt);
1148
1149 int n_arguments = args->length();
1150 int result_len = 0;
1151 {
1152 DisallowHeapAllocation no_gc;
1153 // Iterate through all the arguments performing checks
1154 // and calculating total length.
1155 for (int i = 0; i < n_arguments; i++) {
1156 Object* arg = (*args)[i];
1157 if (!arg->IsJSArray()) return MaybeHandle<JSArray>();
1158 if (!HasOnlySimpleReceiverElements(isolate, JSObject::cast(arg))) {
1159 return MaybeHandle<JSArray>();
1160 }
1161 // TODO(cbruni): support fast concatenation of DICTIONARY_ELEMENTS.
1162 if (!JSObject::cast(arg)->HasFastElements()) {
1163 return MaybeHandle<JSArray>();
1164 }
1165 Handle<JSArray> array(JSArray::cast(arg), isolate);
1166 if (!IsSimpleArray(isolate, array)) {
1167 return MaybeHandle<JSArray>();
1168 }
1169 // The Array length is guaranted to be <= kHalfOfMaxInt thus we won't
1170 // overflow.
1171 result_len += Smi::cast(array->length())->value();
1172 DCHECK(result_len >= 0);
1173 // Throw an Error if we overflow the FixedArray limits
1174 if (FixedDoubleArray::kMaxLength < result_len ||
1175 FixedArray::kMaxLength < result_len) {
1176 AllowHeapAllocation gc;
1177 THROW_NEW_ERROR(isolate,
1178 NewRangeError(MessageTemplate::kInvalidArrayLength),
1179 JSArray);
1180 }
1181 }
1182 }
1183 return ElementsAccessor::Concat(isolate, args, n_arguments, result_len);
1184}
1185
1186} // namespace
1187
1188// ES6 22.1.3.1 Array.prototype.concat
1189BUILTIN(ArrayConcat) {
1190 HandleScope scope(isolate);
1191
1192 Handle<Object> receiver = args.receiver();
1193 // TODO(bmeurer): Do we really care about the exact exception message here?
1194 if (receiver->IsNull(isolate) || receiver->IsUndefined(isolate)) {
1195 THROW_NEW_ERROR_RETURN_FAILURE(
1196 isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
1197 isolate->factory()->NewStringFromAsciiChecked(
1198 "Array.prototype.concat")));
1199 }
1200 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1201 isolate, receiver, Object::ToObject(isolate, args.receiver()));
1202 args[0] = *receiver;
1203
1204 Handle<JSArray> result_array;
1205
1206 // Avoid a real species read to avoid extra lookups to the array constructor
1207 if (V8_LIKELY(receiver->IsJSArray() &&
1208 Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) &&
1209 isolate->IsArraySpeciesLookupChainIntact())) {
1210 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
1211 return *result_array;
1212 }
1213 if (isolate->has_pending_exception()) return isolate->heap()->exception();
1214 }
1215 // Reading @@species happens before anything else with a side effect, so
1216 // we can do it here to determine whether to take the fast path.
1217 Handle<Object> species;
1218 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1219 isolate, species, Object::ArraySpeciesConstructor(isolate, receiver));
1220 if (*species == *isolate->array_function()) {
1221 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
1222 return *result_array;
1223 }
1224 if (isolate->has_pending_exception()) return isolate->heap()->exception();
1225 }
1226 return Slow_ArrayConcat(&args, species, isolate);
1227}
1228
1229void Builtins::Generate_ArrayIsArray(CodeStubAssembler* assembler) {
1230 typedef compiler::Node Node;
1231 typedef CodeStubAssembler::Label Label;
1232
1233 Node* object = assembler->Parameter(1);
1234 Node* context = assembler->Parameter(4);
1235
1236 Label call_runtime(assembler), return_true(assembler),
1237 return_false(assembler);
1238
1239 assembler->GotoIf(assembler->WordIsSmi(object), &return_false);
1240 Node* instance_type = assembler->LoadInstanceType(object);
1241
1242 assembler->GotoIf(assembler->Word32Equal(
1243 instance_type, assembler->Int32Constant(JS_ARRAY_TYPE)),
1244 &return_true);
1245
1246 // TODO(verwaest): Handle proxies in-place.
1247 assembler->Branch(assembler->Word32Equal(
1248 instance_type, assembler->Int32Constant(JS_PROXY_TYPE)),
1249 &call_runtime, &return_false);
1250
1251 assembler->Bind(&return_true);
1252 assembler->Return(assembler->BooleanConstant(true));
1253
1254 assembler->Bind(&return_false);
1255 assembler->Return(assembler->BooleanConstant(false));
1256
1257 assembler->Bind(&call_runtime);
1258 assembler->Return(
1259 assembler->CallRuntime(Runtime::kArrayIsArray, context, object));
1260}
1261
1262void Builtins::Generate_ArrayIncludes(CodeStubAssembler* assembler) {
1263 typedef compiler::Node Node;
1264 typedef CodeStubAssembler::Label Label;
1265 typedef CodeStubAssembler::Variable Variable;
1266
1267 Node* array = assembler->Parameter(0);
1268 Node* search_element = assembler->Parameter(1);
1269 Node* start_from = assembler->Parameter(2);
1270 Node* context = assembler->Parameter(3 + 2);
1271
1272 Node* int32_zero = assembler->Int32Constant(0);
1273 Node* int32_one = assembler->Int32Constant(1);
1274
1275 Node* the_hole = assembler->TheHoleConstant();
1276 Node* undefined = assembler->UndefinedConstant();
1277 Node* heap_number_map = assembler->HeapNumberMapConstant();
1278
1279 Variable len_var(assembler, MachineRepresentation::kWord32),
1280 index_var(assembler, MachineRepresentation::kWord32),
1281 start_from_var(assembler, MachineRepresentation::kWord32);
1282
1283 Label init_k(assembler), return_true(assembler), return_false(assembler),
1284 call_runtime(assembler);
1285
1286 Label init_len(assembler);
1287
1288 index_var.Bind(int32_zero);
1289 len_var.Bind(int32_zero);
1290
1291 // Take slow path if not a JSArray, if retrieving elements requires
1292 // traversing prototype, or if access checks are required.
1293 assembler->BranchIfFastJSArray(array, context, &init_len, &call_runtime);
1294
1295 assembler->Bind(&init_len);
1296 {
1297 // Handle case where JSArray length is not an Smi in the runtime
1298 Node* len = assembler->LoadObjectField(array, JSArray::kLengthOffset);
1299 assembler->GotoUnless(assembler->WordIsSmi(len), &call_runtime);
1300
1301 len_var.Bind(assembler->SmiToWord(len));
1302 assembler->Branch(assembler->Word32Equal(len_var.value(), int32_zero),
1303 &return_false, &init_k);
1304 }
1305
1306 assembler->Bind(&init_k);
1307 {
1308 Label done(assembler), init_k_smi(assembler), init_k_heap_num(assembler),
1309 init_k_zero(assembler), init_k_n(assembler);
1310 Callable call_to_integer = CodeFactory::ToInteger(assembler->isolate());
1311 Node* tagged_n = assembler->CallStub(call_to_integer, context, start_from);
1312
1313 assembler->Branch(assembler->WordIsSmi(tagged_n), &init_k_smi,
1314 &init_k_heap_num);
1315
1316 assembler->Bind(&init_k_smi);
1317 {
1318 start_from_var.Bind(assembler->SmiToWord32(tagged_n));
1319 assembler->Goto(&init_k_n);
1320 }
1321
1322 assembler->Bind(&init_k_heap_num);
1323 {
1324 Label do_return_false(assembler);
1325 Node* fp_len = assembler->ChangeInt32ToFloat64(len_var.value());
1326 Node* fp_n = assembler->LoadHeapNumberValue(tagged_n);
1327 assembler->GotoIf(assembler->Float64GreaterThanOrEqual(fp_n, fp_len),
1328 &do_return_false);
1329 start_from_var.Bind(assembler->TruncateFloat64ToWord32(fp_n));
1330 assembler->Goto(&init_k_n);
1331
1332 assembler->Bind(&do_return_false);
1333 {
1334 index_var.Bind(int32_zero);
1335 assembler->Goto(&return_false);
1336 }
1337 }
1338
1339 assembler->Bind(&init_k_n);
1340 {
1341 Label if_positive(assembler), if_negative(assembler), done(assembler);
1342 assembler->Branch(
1343 assembler->Int32LessThan(start_from_var.value(), int32_zero),
1344 &if_negative, &if_positive);
1345
1346 assembler->Bind(&if_positive);
1347 {
1348 index_var.Bind(start_from_var.value());
1349 assembler->Goto(&done);
1350 }
1351
1352 assembler->Bind(&if_negative);
1353 {
1354 index_var.Bind(
1355 assembler->Int32Add(len_var.value(), start_from_var.value()));
1356 assembler->Branch(
1357 assembler->Int32LessThan(index_var.value(), int32_zero),
1358 &init_k_zero, &done);
1359 }
1360
1361 assembler->Bind(&init_k_zero);
1362 {
1363 index_var.Bind(int32_zero);
1364 assembler->Goto(&done);
1365 }
1366
1367 assembler->Bind(&done);
1368 }
1369 }
1370
1371 static int32_t kElementsKind[] = {
1372 FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS,
1373 FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS,
1374 };
1375
1376 Label if_smiorobjects(assembler), if_packed_doubles(assembler),
1377 if_holey_doubles(assembler);
1378 Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects,
1379 &if_smiorobjects, &if_smiorobjects,
1380 &if_packed_doubles, &if_holey_doubles};
1381
1382 Node* map = assembler->LoadMap(array);
1383 Node* bit_field2 = assembler->LoadMapBitField2(map);
1384 Node* elements_kind =
1385 assembler->BitFieldDecode<Map::ElementsKindBits>(bit_field2);
1386 Node* elements = assembler->LoadElements(array);
1387 assembler->Switch(elements_kind, &return_false, kElementsKind,
1388 element_kind_handlers, arraysize(kElementsKind));
1389
1390 assembler->Bind(&if_smiorobjects);
1391 {
1392 Variable search_num(assembler, MachineRepresentation::kFloat64);
1393 Label ident_loop(assembler, &index_var),
1394 heap_num_loop(assembler, &search_num),
1395 string_loop(assembler, &index_var), simd_loop(assembler),
1396 undef_loop(assembler, &index_var), not_smi(assembler),
1397 not_heap_num(assembler);
1398
1399 assembler->GotoUnless(assembler->WordIsSmi(search_element), &not_smi);
1400 search_num.Bind(assembler->SmiToFloat64(search_element));
1401 assembler->Goto(&heap_num_loop);
1402
1403 assembler->Bind(&not_smi);
1404 assembler->GotoIf(assembler->WordEqual(search_element, undefined),
1405 &undef_loop);
1406 Node* map = assembler->LoadMap(search_element);
1407 assembler->GotoIf(assembler->WordNotEqual(map, heap_number_map),
1408 &not_heap_num);
1409 search_num.Bind(assembler->LoadHeapNumberValue(search_element));
1410 assembler->Goto(&heap_num_loop);
1411
1412 assembler->Bind(&not_heap_num);
1413 Node* search_type = assembler->LoadMapInstanceType(map);
1414 assembler->GotoIf(
1415 assembler->Int32LessThan(
1416 search_type, assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
1417 &string_loop);
1418 assembler->GotoIf(
1419 assembler->WordEqual(search_type,
1420 assembler->Int32Constant(SIMD128_VALUE_TYPE)),
1421 &simd_loop);
1422 assembler->Goto(&ident_loop);
1423
1424 assembler->Bind(&ident_loop);
1425 {
1426 assembler->GotoUnless(
1427 assembler->Int32LessThan(index_var.value(), len_var.value()),
1428 &return_false);
1429 Node* element_k =
1430 assembler->LoadFixedArrayElement(elements, index_var.value());
1431 assembler->GotoIf(assembler->WordEqual(element_k, search_element),
1432 &return_true);
1433
1434 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1435 assembler->Goto(&ident_loop);
1436 }
1437
1438 assembler->Bind(&undef_loop);
1439 {
1440 assembler->GotoUnless(
1441 assembler->Int32LessThan(index_var.value(), len_var.value()),
1442 &return_false);
1443 Node* element_k =
1444 assembler->LoadFixedArrayElement(elements, index_var.value());
1445 assembler->GotoIf(assembler->WordEqual(element_k, undefined),
1446 &return_true);
1447 assembler->GotoIf(assembler->WordEqual(element_k, the_hole),
1448 &return_true);
1449
1450 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1451 assembler->Goto(&undef_loop);
1452 }
1453
1454 assembler->Bind(&heap_num_loop);
1455 {
1456 Label nan_loop(assembler, &index_var),
1457 not_nan_loop(assembler, &index_var);
1458 assembler->BranchIfFloat64IsNaN(search_num.value(), &nan_loop,
1459 &not_nan_loop);
1460
1461 assembler->Bind(&not_nan_loop);
1462 {
1463 Label continue_loop(assembler), not_smi(assembler);
1464 assembler->GotoUnless(
1465 assembler->Int32LessThan(index_var.value(), len_var.value()),
1466 &return_false);
1467 Node* element_k =
1468 assembler->LoadFixedArrayElement(elements, index_var.value());
1469 assembler->GotoUnless(assembler->WordIsSmi(element_k), &not_smi);
1470 assembler->Branch(
1471 assembler->Float64Equal(search_num.value(),
1472 assembler->SmiToFloat64(element_k)),
1473 &return_true, &continue_loop);
1474
1475 assembler->Bind(&not_smi);
1476 assembler->GotoIf(assembler->WordNotEqual(assembler->LoadMap(element_k),
1477 heap_number_map),
1478 &continue_loop);
1479 assembler->BranchIfFloat64Equal(
1480 search_num.value(), assembler->LoadHeapNumberValue(element_k),
1481 &return_true, &continue_loop);
1482
1483 assembler->Bind(&continue_loop);
1484 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1485 assembler->Goto(&not_nan_loop);
1486 }
1487
1488 assembler->Bind(&nan_loop);
1489 {
1490 Label continue_loop(assembler);
1491 assembler->GotoUnless(
1492 assembler->Int32LessThan(index_var.value(), len_var.value()),
1493 &return_false);
1494 Node* element_k =
1495 assembler->LoadFixedArrayElement(elements, index_var.value());
1496 assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
1497 assembler->GotoIf(assembler->WordNotEqual(assembler->LoadMap(element_k),
1498 heap_number_map),
1499 &continue_loop);
1500 assembler->BranchIfFloat64IsNaN(
1501 assembler->LoadHeapNumberValue(element_k), &return_true,
1502 &continue_loop);
1503
1504 assembler->Bind(&continue_loop);
1505 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1506 assembler->Goto(&nan_loop);
1507 }
1508 }
1509
1510 assembler->Bind(&string_loop);
1511 {
1512 Label continue_loop(assembler);
1513 assembler->GotoUnless(
1514 assembler->Int32LessThan(index_var.value(), len_var.value()),
1515 &return_false);
1516 Node* element_k =
1517 assembler->LoadFixedArrayElement(elements, index_var.value());
1518 assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
1519 assembler->GotoUnless(assembler->Int32LessThan(
1520 assembler->LoadInstanceType(element_k),
1521 assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
1522 &continue_loop);
1523
1524 // TODO(bmeurer): Consider inlining the StringEqual logic here.
1525 Callable callable = CodeFactory::StringEqual(assembler->isolate());
1526 Node* result =
1527 assembler->CallStub(callable, context, search_element, element_k);
1528 assembler->Branch(
1529 assembler->WordEqual(assembler->BooleanConstant(true), result),
1530 &return_true, &continue_loop);
1531
1532 assembler->Bind(&continue_loop);
1533 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1534 assembler->Goto(&string_loop);
1535 }
1536
1537 assembler->Bind(&simd_loop);
1538 {
1539 Label continue_loop(assembler, &index_var),
1540 loop_body(assembler, &index_var);
1541 Node* map = assembler->LoadMap(search_element);
1542
1543 assembler->Goto(&loop_body);
1544 assembler->Bind(&loop_body);
1545 assembler->GotoUnless(
1546 assembler->Int32LessThan(index_var.value(), len_var.value()),
1547 &return_false);
1548
1549 Node* element_k =
1550 assembler->LoadFixedArrayElement(elements, index_var.value());
1551 assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
1552
1553 Node* map_k = assembler->LoadMap(element_k);
1554 assembler->BranchIfSimd128Equal(search_element, map, element_k, map_k,
1555 &return_true, &continue_loop);
1556
1557 assembler->Bind(&continue_loop);
1558 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1559 assembler->Goto(&loop_body);
1560 }
1561 }
1562
1563 assembler->Bind(&if_packed_doubles);
1564 {
1565 Label nan_loop(assembler, &index_var), not_nan_loop(assembler, &index_var),
1566 hole_loop(assembler, &index_var), search_notnan(assembler);
1567 Variable search_num(assembler, MachineRepresentation::kFloat64);
1568
1569 assembler->GotoUnless(assembler->WordIsSmi(search_element), &search_notnan);
1570 search_num.Bind(assembler->SmiToFloat64(search_element));
1571 assembler->Goto(&not_nan_loop);
1572
1573 assembler->Bind(&search_notnan);
1574 assembler->GotoIf(assembler->WordNotEqual(
1575 assembler->LoadMap(search_element), heap_number_map),
1576 &return_false);
1577
1578 search_num.Bind(assembler->LoadHeapNumberValue(search_element));
1579
1580 assembler->BranchIfFloat64IsNaN(search_num.value(), &nan_loop,
1581 &not_nan_loop);
1582
1583 // Search for HeapNumber
1584 assembler->Bind(&not_nan_loop);
1585 {
1586 Label continue_loop(assembler);
1587 assembler->GotoUnless(
1588 assembler->Int32LessThan(index_var.value(), len_var.value()),
1589 &return_false);
1590 Node* element_k = assembler->LoadFixedDoubleArrayElement(
1591 elements, index_var.value(), MachineType::Float64());
1592 assembler->BranchIfFloat64Equal(element_k, search_num.value(),
1593 &return_true, &continue_loop);
1594 assembler->Bind(&continue_loop);
1595 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1596 assembler->Goto(&not_nan_loop);
1597 }
1598
1599 // Search for NaN
1600 assembler->Bind(&nan_loop);
1601 {
1602 Label continue_loop(assembler);
1603 assembler->GotoUnless(
1604 assembler->Int32LessThan(index_var.value(), len_var.value()),
1605 &return_false);
1606 Node* element_k = assembler->LoadFixedDoubleArrayElement(
1607 elements, index_var.value(), MachineType::Float64());
1608 assembler->BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop);
1609 assembler->Bind(&continue_loop);
1610 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1611 assembler->Goto(&nan_loop);
1612 }
1613 }
1614
1615 assembler->Bind(&if_holey_doubles);
1616 {
1617 Label nan_loop(assembler, &index_var), not_nan_loop(assembler, &index_var),
1618 hole_loop(assembler, &index_var), search_notnan(assembler);
1619 Variable search_num(assembler, MachineRepresentation::kFloat64);
1620
1621 assembler->GotoUnless(assembler->WordIsSmi(search_element), &search_notnan);
1622 search_num.Bind(assembler->SmiToFloat64(search_element));
1623 assembler->Goto(&not_nan_loop);
1624
1625 assembler->Bind(&search_notnan);
1626 assembler->GotoIf(assembler->WordEqual(search_element, undefined),
1627 &hole_loop);
1628 assembler->GotoIf(assembler->WordNotEqual(
1629 assembler->LoadMap(search_element), heap_number_map),
1630 &return_false);
1631
1632 search_num.Bind(assembler->LoadHeapNumberValue(search_element));
1633
1634 assembler->BranchIfFloat64IsNaN(search_num.value(), &nan_loop,
1635 &not_nan_loop);
1636
1637 // Search for HeapNumber
1638 assembler->Bind(&not_nan_loop);
1639 {
1640 Label continue_loop(assembler);
1641 assembler->GotoUnless(
1642 assembler->Int32LessThan(index_var.value(), len_var.value()),
1643 &return_false);
1644
1645 if (kPointerSize == kDoubleSize) {
1646 Node* element = assembler->LoadFixedDoubleArrayElement(
1647 elements, index_var.value(), MachineType::Uint64());
1648 Node* the_hole = assembler->Int64Constant(kHoleNanInt64);
1649 assembler->GotoIf(assembler->Word64Equal(element, the_hole),
1650 &continue_loop);
1651 } else {
1652 Node* element_upper = assembler->LoadFixedDoubleArrayElement(
1653 elements, index_var.value(), MachineType::Uint32(),
1654 kIeeeDoubleExponentWordOffset);
1655 assembler->GotoIf(
1656 assembler->Word32Equal(element_upper,
1657 assembler->Int32Constant(kHoleNanUpper32)),
1658 &continue_loop);
1659 }
1660
1661 Node* element_k = assembler->LoadFixedDoubleArrayElement(
1662 elements, index_var.value(), MachineType::Float64());
1663 assembler->BranchIfFloat64Equal(element_k, search_num.value(),
1664 &return_true, &continue_loop);
1665 assembler->Bind(&continue_loop);
1666 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1667 assembler->Goto(&not_nan_loop);
1668 }
1669
1670 // Search for NaN
1671 assembler->Bind(&nan_loop);
1672 {
1673 Label continue_loop(assembler);
1674 assembler->GotoUnless(
1675 assembler->Int32LessThan(index_var.value(), len_var.value()),
1676 &return_false);
1677
1678 if (kPointerSize == kDoubleSize) {
1679 Node* element = assembler->LoadFixedDoubleArrayElement(
1680 elements, index_var.value(), MachineType::Uint64());
1681 Node* the_hole = assembler->Int64Constant(kHoleNanInt64);
1682 assembler->GotoIf(assembler->Word64Equal(element, the_hole),
1683 &continue_loop);
1684 } else {
1685 Node* element_upper = assembler->LoadFixedDoubleArrayElement(
1686 elements, index_var.value(), MachineType::Uint32(),
1687 kIeeeDoubleExponentWordOffset);
1688 assembler->GotoIf(
1689 assembler->Word32Equal(element_upper,
1690 assembler->Int32Constant(kHoleNanUpper32)),
1691 &continue_loop);
1692 }
1693
1694 Node* element_k = assembler->LoadFixedDoubleArrayElement(
1695 elements, index_var.value(), MachineType::Float64());
1696 assembler->BranchIfFloat64IsNaN(element_k, &return_true, &continue_loop);
1697 assembler->Bind(&continue_loop);
1698 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1699 assembler->Goto(&nan_loop);
1700 }
1701
1702 // Search for the Hole
1703 assembler->Bind(&hole_loop);
1704 {
1705 assembler->GotoUnless(
1706 assembler->Int32LessThan(index_var.value(), len_var.value()),
1707 &return_false);
1708
1709 if (kPointerSize == kDoubleSize) {
1710 Node* element = assembler->LoadFixedDoubleArrayElement(
1711 elements, index_var.value(), MachineType::Uint64());
1712 Node* the_hole = assembler->Int64Constant(kHoleNanInt64);
1713 assembler->GotoIf(assembler->Word64Equal(element, the_hole),
1714 &return_true);
1715 } else {
1716 Node* element_upper = assembler->LoadFixedDoubleArrayElement(
1717 elements, index_var.value(), MachineType::Uint32(),
1718 kIeeeDoubleExponentWordOffset);
1719 assembler->GotoIf(
1720 assembler->Word32Equal(element_upper,
1721 assembler->Int32Constant(kHoleNanUpper32)),
1722 &return_true);
1723 }
1724
1725 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1726 assembler->Goto(&hole_loop);
1727 }
1728 }
1729
1730 assembler->Bind(&return_true);
1731 assembler->Return(assembler->BooleanConstant(true));
1732
1733 assembler->Bind(&return_false);
1734 assembler->Return(assembler->BooleanConstant(false));
1735
1736 assembler->Bind(&call_runtime);
1737 assembler->Return(assembler->CallRuntime(Runtime::kArrayIncludes_Slow,
1738 context, array, search_element,
1739 start_from));
1740}
1741
1742void Builtins::Generate_ArrayIndexOf(CodeStubAssembler* assembler) {
1743 typedef compiler::Node Node;
1744 typedef CodeStubAssembler::Label Label;
1745 typedef CodeStubAssembler::Variable Variable;
1746
1747 Node* array = assembler->Parameter(0);
1748 Node* search_element = assembler->Parameter(1);
1749 Node* start_from = assembler->Parameter(2);
1750 Node* context = assembler->Parameter(3 + 2);
1751
1752 Node* int32_zero = assembler->Int32Constant(0);
1753 Node* int32_one = assembler->Int32Constant(1);
1754
1755 Node* undefined = assembler->UndefinedConstant();
1756 Node* heap_number_map = assembler->HeapNumberMapConstant();
1757
1758 Variable len_var(assembler, MachineRepresentation::kWord32),
1759 index_var(assembler, MachineRepresentation::kWord32),
1760 start_from_var(assembler, MachineRepresentation::kWord32);
1761
1762 Label init_k(assembler), return_found(assembler), return_not_found(assembler),
1763 call_runtime(assembler);
1764
1765 Label init_len(assembler);
1766
1767 index_var.Bind(int32_zero);
1768 len_var.Bind(int32_zero);
1769
1770 // Take slow path if not a JSArray, if retrieving elements requires
1771 // traversing prototype, or if access checks are required.
1772 assembler->BranchIfFastJSArray(array, context, &init_len, &call_runtime);
1773
1774 assembler->Bind(&init_len);
1775 {
1776 // Handle case where JSArray length is not an Smi in the runtime
1777 Node* len = assembler->LoadObjectField(array, JSArray::kLengthOffset);
1778 assembler->GotoUnless(assembler->WordIsSmi(len), &call_runtime);
1779
1780 len_var.Bind(assembler->SmiToWord(len));
1781 assembler->Branch(assembler->Word32Equal(len_var.value(), int32_zero),
1782 &return_not_found, &init_k);
1783 }
1784
1785 assembler->Bind(&init_k);
1786 {
1787 Label done(assembler), init_k_smi(assembler), init_k_heap_num(assembler),
1788 init_k_zero(assembler), init_k_n(assembler);
1789 Callable call_to_integer = CodeFactory::ToInteger(assembler->isolate());
1790 Node* tagged_n = assembler->CallStub(call_to_integer, context, start_from);
1791
1792 assembler->Branch(assembler->WordIsSmi(tagged_n), &init_k_smi,
1793 &init_k_heap_num);
1794
1795 assembler->Bind(&init_k_smi);
1796 {
1797 start_from_var.Bind(assembler->SmiToWord32(tagged_n));
1798 assembler->Goto(&init_k_n);
1799 }
1800
1801 assembler->Bind(&init_k_heap_num);
1802 {
1803 Label do_return_not_found(assembler);
1804 Node* fp_len = assembler->ChangeInt32ToFloat64(len_var.value());
1805 Node* fp_n = assembler->LoadHeapNumberValue(tagged_n);
1806 assembler->GotoIf(assembler->Float64GreaterThanOrEqual(fp_n, fp_len),
1807 &do_return_not_found);
1808 start_from_var.Bind(assembler->TruncateFloat64ToWord32(fp_n));
1809 assembler->Goto(&init_k_n);
1810
1811 assembler->Bind(&do_return_not_found);
1812 {
1813 index_var.Bind(int32_zero);
1814 assembler->Goto(&return_not_found);
1815 }
1816 }
1817
1818 assembler->Bind(&init_k_n);
1819 {
1820 Label if_positive(assembler), if_negative(assembler), done(assembler);
1821 assembler->Branch(
1822 assembler->Int32LessThan(start_from_var.value(), int32_zero),
1823 &if_negative, &if_positive);
1824
1825 assembler->Bind(&if_positive);
1826 {
1827 index_var.Bind(start_from_var.value());
1828 assembler->Goto(&done);
1829 }
1830
1831 assembler->Bind(&if_negative);
1832 {
1833 index_var.Bind(
1834 assembler->Int32Add(len_var.value(), start_from_var.value()));
1835 assembler->Branch(
1836 assembler->Int32LessThan(index_var.value(), int32_zero),
1837 &init_k_zero, &done);
1838 }
1839
1840 assembler->Bind(&init_k_zero);
1841 {
1842 index_var.Bind(int32_zero);
1843 assembler->Goto(&done);
1844 }
1845
1846 assembler->Bind(&done);
1847 }
1848 }
1849
1850 static int32_t kElementsKind[] = {
1851 FAST_SMI_ELEMENTS, FAST_HOLEY_SMI_ELEMENTS, FAST_ELEMENTS,
1852 FAST_HOLEY_ELEMENTS, FAST_DOUBLE_ELEMENTS, FAST_HOLEY_DOUBLE_ELEMENTS,
1853 };
1854
1855 Label if_smiorobjects(assembler), if_packed_doubles(assembler),
1856 if_holey_doubles(assembler);
1857 Label* element_kind_handlers[] = {&if_smiorobjects, &if_smiorobjects,
1858 &if_smiorobjects, &if_smiorobjects,
1859 &if_packed_doubles, &if_holey_doubles};
1860
1861 Node* map = assembler->LoadMap(array);
1862 Node* bit_field2 = assembler->LoadMapBitField2(map);
1863 Node* elements_kind =
1864 assembler->BitFieldDecode<Map::ElementsKindBits>(bit_field2);
1865 Node* elements = assembler->LoadElements(array);
1866 assembler->Switch(elements_kind, &return_not_found, kElementsKind,
1867 element_kind_handlers, arraysize(kElementsKind));
1868
1869 assembler->Bind(&if_smiorobjects);
1870 {
1871 Variable search_num(assembler, MachineRepresentation::kFloat64);
1872 Label ident_loop(assembler, &index_var),
1873 heap_num_loop(assembler, &search_num),
1874 string_loop(assembler, &index_var), simd_loop(assembler),
1875 undef_loop(assembler, &index_var), not_smi(assembler),
1876 not_heap_num(assembler);
1877
1878 assembler->GotoUnless(assembler->WordIsSmi(search_element), &not_smi);
1879 search_num.Bind(assembler->SmiToFloat64(search_element));
1880 assembler->Goto(&heap_num_loop);
1881
1882 assembler->Bind(&not_smi);
1883 assembler->GotoIf(assembler->WordEqual(search_element, undefined),
1884 &undef_loop);
1885 Node* map = assembler->LoadMap(search_element);
1886 assembler->GotoIf(assembler->WordNotEqual(map, heap_number_map),
1887 &not_heap_num);
1888 search_num.Bind(assembler->LoadHeapNumberValue(search_element));
1889 assembler->Goto(&heap_num_loop);
1890
1891 assembler->Bind(&not_heap_num);
1892 Node* search_type = assembler->LoadMapInstanceType(map);
1893 assembler->GotoIf(
1894 assembler->Int32LessThan(
1895 search_type, assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
1896 &string_loop);
1897 assembler->GotoIf(
1898 assembler->WordEqual(search_type,
1899 assembler->Int32Constant(SIMD128_VALUE_TYPE)),
1900 &simd_loop);
1901 assembler->Goto(&ident_loop);
1902
1903 assembler->Bind(&ident_loop);
1904 {
1905 assembler->GotoUnless(
1906 assembler->Int32LessThan(index_var.value(), len_var.value()),
1907 &return_not_found);
1908 Node* element_k =
1909 assembler->LoadFixedArrayElement(elements, index_var.value());
1910 assembler->GotoIf(assembler->WordEqual(element_k, search_element),
1911 &return_found);
1912
1913 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1914 assembler->Goto(&ident_loop);
1915 }
1916
1917 assembler->Bind(&undef_loop);
1918 {
1919 assembler->GotoUnless(
1920 assembler->Int32LessThan(index_var.value(), len_var.value()),
1921 &return_not_found);
1922 Node* element_k =
1923 assembler->LoadFixedArrayElement(elements, index_var.value());
1924 assembler->GotoIf(assembler->WordEqual(element_k, undefined),
1925 &return_found);
1926
1927 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1928 assembler->Goto(&undef_loop);
1929 }
1930
1931 assembler->Bind(&heap_num_loop);
1932 {
1933 Label not_nan_loop(assembler, &index_var);
1934 assembler->BranchIfFloat64IsNaN(search_num.value(), &return_not_found,
1935 &not_nan_loop);
1936
1937 assembler->Bind(&not_nan_loop);
1938 {
1939 Label continue_loop(assembler), not_smi(assembler);
1940 assembler->GotoUnless(
1941 assembler->Int32LessThan(index_var.value(), len_var.value()),
1942 &return_not_found);
1943 Node* element_k =
1944 assembler->LoadFixedArrayElement(elements, index_var.value());
1945 assembler->GotoUnless(assembler->WordIsSmi(element_k), &not_smi);
1946 assembler->Branch(
1947 assembler->Float64Equal(search_num.value(),
1948 assembler->SmiToFloat64(element_k)),
1949 &return_found, &continue_loop);
1950
1951 assembler->Bind(&not_smi);
1952 assembler->GotoIf(assembler->WordNotEqual(assembler->LoadMap(element_k),
1953 heap_number_map),
1954 &continue_loop);
1955 assembler->BranchIfFloat64Equal(
1956 search_num.value(), assembler->LoadHeapNumberValue(element_k),
1957 &return_found, &continue_loop);
1958
1959 assembler->Bind(&continue_loop);
1960 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1961 assembler->Goto(&not_nan_loop);
1962 }
1963 }
1964
1965 assembler->Bind(&string_loop);
1966 {
1967 Label continue_loop(assembler);
1968 assembler->GotoUnless(
1969 assembler->Int32LessThan(index_var.value(), len_var.value()),
1970 &return_not_found);
1971 Node* element_k =
1972 assembler->LoadFixedArrayElement(elements, index_var.value());
1973 assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
1974 assembler->GotoUnless(assembler->Int32LessThan(
1975 assembler->LoadInstanceType(element_k),
1976 assembler->Int32Constant(FIRST_NONSTRING_TYPE)),
1977 &continue_loop);
1978
1979 // TODO(bmeurer): Consider inlining the StringEqual logic here.
1980 Callable callable = CodeFactory::StringEqual(assembler->isolate());
1981 Node* result =
1982 assembler->CallStub(callable, context, search_element, element_k);
1983 assembler->Branch(
1984 assembler->WordEqual(assembler->BooleanConstant(true), result),
1985 &return_found, &continue_loop);
1986
1987 assembler->Bind(&continue_loop);
1988 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
1989 assembler->Goto(&string_loop);
1990 }
1991
1992 assembler->Bind(&simd_loop);
1993 {
1994 Label continue_loop(assembler, &index_var),
1995 loop_body(assembler, &index_var);
1996 Node* map = assembler->LoadMap(search_element);
1997
1998 assembler->Goto(&loop_body);
1999 assembler->Bind(&loop_body);
2000 assembler->GotoUnless(
2001 assembler->Int32LessThan(index_var.value(), len_var.value()),
2002 &return_not_found);
2003
2004 Node* element_k =
2005 assembler->LoadFixedArrayElement(elements, index_var.value());
2006 assembler->GotoIf(assembler->WordIsSmi(element_k), &continue_loop);
2007
2008 Node* map_k = assembler->LoadMap(element_k);
2009 assembler->BranchIfSimd128Equal(search_element, map, element_k, map_k,
2010 &return_found, &continue_loop);
2011
2012 assembler->Bind(&continue_loop);
2013 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
2014 assembler->Goto(&loop_body);
2015 }
2016 }
2017
2018 assembler->Bind(&if_packed_doubles);
2019 {
2020 Label not_nan_loop(assembler, &index_var), search_notnan(assembler);
2021 Variable search_num(assembler, MachineRepresentation::kFloat64);
2022
2023 assembler->GotoUnless(assembler->WordIsSmi(search_element), &search_notnan);
2024 search_num.Bind(assembler->SmiToFloat64(search_element));
2025 assembler->Goto(&not_nan_loop);
2026
2027 assembler->Bind(&search_notnan);
2028 assembler->GotoIf(assembler->WordNotEqual(
2029 assembler->LoadMap(search_element), heap_number_map),
2030 &return_not_found);
2031
2032 search_num.Bind(assembler->LoadHeapNumberValue(search_element));
2033
2034 assembler->BranchIfFloat64IsNaN(search_num.value(), &return_not_found,
2035 &not_nan_loop);
2036
2037 // Search for HeapNumber
2038 assembler->Bind(&not_nan_loop);
2039 {
2040 Label continue_loop(assembler);
2041 assembler->GotoUnless(
2042 assembler->Int32LessThan(index_var.value(), len_var.value()),
2043 &return_not_found);
2044 Node* element_k = assembler->LoadFixedDoubleArrayElement(
2045 elements, index_var.value(), MachineType::Float64());
2046 assembler->BranchIfFloat64Equal(element_k, search_num.value(),
2047 &return_found, &continue_loop);
2048 assembler->Bind(&continue_loop);
2049 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
2050 assembler->Goto(&not_nan_loop);
2051 }
2052 }
2053
2054 assembler->Bind(&if_holey_doubles);
2055 {
2056 Label not_nan_loop(assembler, &index_var), search_notnan(assembler);
2057 Variable search_num(assembler, MachineRepresentation::kFloat64);
2058
2059 assembler->GotoUnless(assembler->WordIsSmi(search_element), &search_notnan);
2060 search_num.Bind(assembler->SmiToFloat64(search_element));
2061 assembler->Goto(&not_nan_loop);
2062
2063 assembler->Bind(&search_notnan);
2064 assembler->GotoIf(assembler->WordNotEqual(
2065 assembler->LoadMap(search_element), heap_number_map),
2066 &return_not_found);
2067
2068 search_num.Bind(assembler->LoadHeapNumberValue(search_element));
2069
2070 assembler->BranchIfFloat64IsNaN(search_num.value(), &return_not_found,
2071 &not_nan_loop);
2072
2073 // Search for HeapNumber
2074 assembler->Bind(&not_nan_loop);
2075 {
2076 Label continue_loop(assembler);
2077 assembler->GotoUnless(
2078 assembler->Int32LessThan(index_var.value(), len_var.value()),
2079 &return_not_found);
2080
2081 if (kPointerSize == kDoubleSize) {
2082 Node* element = assembler->LoadFixedDoubleArrayElement(
2083 elements, index_var.value(), MachineType::Uint64());
2084 Node* the_hole = assembler->Int64Constant(kHoleNanInt64);
2085 assembler->GotoIf(assembler->Word64Equal(element, the_hole),
2086 &continue_loop);
2087 } else {
2088 Node* element_upper = assembler->LoadFixedDoubleArrayElement(
2089 elements, index_var.value(), MachineType::Uint32(),
2090 kIeeeDoubleExponentWordOffset);
2091 assembler->GotoIf(
2092 assembler->Word32Equal(element_upper,
2093 assembler->Int32Constant(kHoleNanUpper32)),
2094 &continue_loop);
2095 }
2096
2097 Node* element_k = assembler->LoadFixedDoubleArrayElement(
2098 elements, index_var.value(), MachineType::Float64());
2099 assembler->BranchIfFloat64Equal(element_k, search_num.value(),
2100 &return_found, &continue_loop);
2101 assembler->Bind(&continue_loop);
2102 index_var.Bind(assembler->Int32Add(index_var.value(), int32_one));
2103 assembler->Goto(&not_nan_loop);
2104 }
2105 }
2106
2107 assembler->Bind(&return_found);
2108 assembler->Return(assembler->ChangeInt32ToTagged(index_var.value()));
2109
2110 assembler->Bind(&return_not_found);
2111 assembler->Return(assembler->NumberConstant(-1));
2112
2113 assembler->Bind(&call_runtime);
2114 assembler->Return(assembler->CallRuntime(Runtime::kArrayIndexOf, context,
2115 array, search_element, start_from));
2116}
2117
2118} // namespace internal
2119} // namespace v8