Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: src/builtins/builtins-array.cc

Issue 2158793003: [builtins] move array builtins into its own file. (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@declares
Patch Set: rebase Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/builtins/builtins.cc ('k') | src/builtins/builtins-utils.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/builtins/builtins.h"
6 #include "src/builtins/builtins-utils.h"
7 #include "src/elements.h"
8
9 namespace v8 {
10 namespace internal {
11
12 namespace {
13
14 inline bool ClampedToInteger(Isolate* isolate, Object* object, int* out) {
15 // This is an extended version of ECMA-262 7.1.11 handling signed values
16 // Try to convert object to a number and clamp values to [kMinInt, kMaxInt]
17 if (object->IsSmi()) {
18 *out = Smi::cast(object)->value();
19 return true;
20 } else if (object->IsHeapNumber()) {
21 double value = HeapNumber::cast(object)->value();
22 if (std::isnan(value)) {
23 *out = 0;
24 } else if (value > kMaxInt) {
25 *out = kMaxInt;
26 } else if (value < kMinInt) {
27 *out = kMinInt;
28 } else {
29 *out = static_cast<int>(value);
30 }
31 return true;
32 } else if (object->IsUndefined(isolate) || object->IsNull(isolate)) {
33 *out = 0;
34 return true;
35 } else if (object->IsBoolean()) {
36 *out = object->IsTrue(isolate);
37 return true;
38 }
39 return false;
40 }
41
42 inline bool GetSloppyArgumentsLength(Isolate* isolate, Handle<JSObject> object,
43 int* out) {
44 Context* context = *isolate->native_context();
45 Map* map = object->map();
46 if (map != context->sloppy_arguments_map() &&
47 map != context->strict_arguments_map() &&
48 map != context->fast_aliased_arguments_map()) {
49 return false;
50 }
51 DCHECK(object->HasFastElements() || object->HasFastArgumentsElements());
52 Object* len_obj = object->InObjectPropertyAt(JSArgumentsObject::kLengthIndex);
53 if (!len_obj->IsSmi()) return false;
54 *out = Max(0, Smi::cast(len_obj)->value());
55 return *out <= object->elements()->length();
56 }
57
58 inline bool PrototypeHasNoElements(Isolate* isolate, JSObject* object) {
59 DisallowHeapAllocation no_gc;
60 HeapObject* prototype = HeapObject::cast(object->map()->prototype());
61 HeapObject* null = isolate->heap()->null_value();
62 HeapObject* empty = isolate->heap()->empty_fixed_array();
63 while (prototype != null) {
64 Map* map = prototype->map();
65 if (map->instance_type() <= LAST_CUSTOM_ELEMENTS_RECEIVER) return false;
66 if (JSObject::cast(prototype)->elements() != empty) return false;
67 prototype = HeapObject::cast(map->prototype());
68 }
69 return true;
70 }
71
72 inline bool IsJSArrayFastElementMovingAllowed(Isolate* isolate,
73 JSArray* receiver) {
74 return PrototypeHasNoElements(isolate, receiver);
75 }
76
77 inline bool HasSimpleElements(JSObject* current) {
78 return current->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER &&
79 !current->GetElementsAccessor()->HasAccessors(current);
80 }
81
82 inline bool HasOnlySimpleReceiverElements(Isolate* isolate,
83 JSObject* receiver) {
84 // Check that we have no accessors on the receiver's elements.
85 if (!HasSimpleElements(receiver)) return false;
86 return PrototypeHasNoElements(isolate, receiver);
87 }
88
89 inline bool HasOnlySimpleElements(Isolate* isolate, JSReceiver* receiver) {
90 DisallowHeapAllocation no_gc;
91 PrototypeIterator iter(isolate, receiver, kStartAtReceiver);
92 for (; !iter.IsAtEnd(); iter.Advance()) {
93 if (iter.GetCurrent()->IsJSProxy()) return false;
94 JSObject* current = iter.GetCurrent<JSObject>();
95 if (!HasSimpleElements(current)) return false;
96 }
97 return true;
98 }
99
100 // Returns |false| if not applicable.
101 MUST_USE_RESULT
102 inline bool EnsureJSArrayWithWritableFastElements(Isolate* isolate,
103 Handle<Object> receiver,
104 BuiltinArguments* args,
105 int first_added_arg) {
106 if (!receiver->IsJSArray()) return false;
107 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
108 ElementsKind origin_kind = array->GetElementsKind();
109 if (IsDictionaryElementsKind(origin_kind)) return false;
110 if (!array->map()->is_extensible()) return false;
111 if (args == nullptr) return true;
112
113 // If there may be elements accessors in the prototype chain, the fast path
114 // cannot be used if there arguments to add to the array.
115 if (!IsJSArrayFastElementMovingAllowed(isolate, *array)) return false;
116
117 // Adding elements to the array prototype would break code that makes sure
118 // it has no elements. Handle that elsewhere.
119 if (isolate->IsAnyInitialArrayPrototype(array)) return false;
120
121 // Need to ensure that the arguments passed in args can be contained in
122 // the array.
123 int args_length = args->length();
124 if (first_added_arg >= args_length) return true;
125
126 if (IsFastObjectElementsKind(origin_kind)) return true;
127 ElementsKind target_kind = origin_kind;
128 {
129 DisallowHeapAllocation no_gc;
130 for (int i = first_added_arg; i < args_length; i++) {
131 Object* arg = (*args)[i];
132 if (arg->IsHeapObject()) {
133 if (arg->IsHeapNumber()) {
134 target_kind = FAST_DOUBLE_ELEMENTS;
135 } else {
136 target_kind = FAST_ELEMENTS;
137 break;
138 }
139 }
140 }
141 }
142 if (target_kind != origin_kind) {
143 // Use a short-lived HandleScope to avoid creating several copies of the
144 // elements handle which would cause issues when left-trimming later-on.
145 HandleScope scope(isolate);
146 JSObject::TransitionElementsKind(array, target_kind);
147 }
148 return true;
149 }
150
151 MUST_USE_RESULT static Object* CallJsIntrinsic(Isolate* isolate,
152 Handle<JSFunction> function,
153 BuiltinArguments args) {
154 HandleScope handleScope(isolate);
155 int argc = args.length() - 1;
156 ScopedVector<Handle<Object>> argv(argc);
157 for (int i = 0; i < argc; ++i) {
158 argv[i] = args.at<Object>(i + 1);
159 }
160 RETURN_RESULT_OR_FAILURE(
161 isolate,
162 Execution::Call(isolate, function, args.receiver(), argc, argv.start()));
163 }
164
165 Object* DoArrayPush(Isolate* isolate, BuiltinArguments args) {
166 HandleScope scope(isolate);
167 Handle<Object> receiver = args.receiver();
168 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
169 return CallJsIntrinsic(isolate, isolate->array_push(), args);
170 }
171 // Fast Elements Path
172 int to_add = args.length() - 1;
173 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
174 int len = Smi::cast(array->length())->value();
175 if (to_add == 0) return Smi::FromInt(len);
176
177 // Currently fixed arrays cannot grow too big, so we should never hit this.
178 DCHECK_LE(to_add, Smi::kMaxValue - Smi::cast(array->length())->value());
179
180 if (JSArray::HasReadOnlyLength(array)) {
181 return CallJsIntrinsic(isolate, isolate->array_push(), args);
182 }
183
184 ElementsAccessor* accessor = array->GetElementsAccessor();
185 int new_length = accessor->Push(array, &args, to_add);
186 return Smi::FromInt(new_length);
187 }
188 } // namespace
189
190 BUILTIN(ArrayPush) { return DoArrayPush(isolate, args); }
191
192 // TODO(verwaest): This is a temporary helper until the FastArrayPush stub can
193 // tailcall to the builtin directly.
194 RUNTIME_FUNCTION(Runtime_ArrayPush) {
195 DCHECK_EQ(2, args.length());
196 Arguments* incoming = reinterpret_cast<Arguments*>(args[0]);
197 // Rewrap the arguments as builtins arguments.
198 int argc = incoming->length() + BuiltinArguments::kNumExtraArgsWithReceiver;
199 BuiltinArguments caller_args(argc, incoming->arguments() + 1);
200 return DoArrayPush(isolate, caller_args);
201 }
202
203 BUILTIN(ArrayPop) {
204 HandleScope scope(isolate);
205 Handle<Object> receiver = args.receiver();
206 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0)) {
207 return CallJsIntrinsic(isolate, isolate->array_pop(), args);
208 }
209
210 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
211
212 uint32_t len = static_cast<uint32_t>(Smi::cast(array->length())->value());
213 if (len == 0) return isolate->heap()->undefined_value();
214
215 if (JSArray::HasReadOnlyLength(array)) {
216 return CallJsIntrinsic(isolate, isolate->array_pop(), args);
217 }
218
219 Handle<Object> result;
220 if (IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
221 // Fast Elements Path
222 result = array->GetElementsAccessor()->Pop(array);
223 } else {
224 // Use Slow Lookup otherwise
225 uint32_t new_length = len - 1;
226 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
227 isolate, result, JSReceiver::GetElement(isolate, array, new_length));
228 JSArray::SetLength(array, new_length);
229 }
230 return *result;
231 }
232
233 BUILTIN(ArrayShift) {
234 HandleScope scope(isolate);
235 Heap* heap = isolate->heap();
236 Handle<Object> receiver = args.receiver();
237 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, nullptr, 0) ||
238 !IsJSArrayFastElementMovingAllowed(isolate, JSArray::cast(*receiver))) {
239 return CallJsIntrinsic(isolate, isolate->array_shift(), args);
240 }
241 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
242
243 int len = Smi::cast(array->length())->value();
244 if (len == 0) return heap->undefined_value();
245
246 if (JSArray::HasReadOnlyLength(array)) {
247 return CallJsIntrinsic(isolate, isolate->array_shift(), args);
248 }
249
250 Handle<Object> first = array->GetElementsAccessor()->Shift(array);
251 return *first;
252 }
253
254 BUILTIN(ArrayUnshift) {
255 HandleScope scope(isolate);
256 Handle<Object> receiver = args.receiver();
257 if (!EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1)) {
258 return CallJsIntrinsic(isolate, isolate->array_unshift(), args);
259 }
260 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
261 int to_add = args.length() - 1;
262 if (to_add == 0) return array->length();
263
264 // Currently fixed arrays cannot grow too big, so we should never hit this.
265 DCHECK_LE(to_add, Smi::kMaxValue - Smi::cast(array->length())->value());
266
267 if (JSArray::HasReadOnlyLength(array)) {
268 return CallJsIntrinsic(isolate, isolate->array_unshift(), args);
269 }
270
271 ElementsAccessor* accessor = array->GetElementsAccessor();
272 int new_length = accessor->Unshift(array, &args, to_add);
273 return Smi::FromInt(new_length);
274 }
275
276 BUILTIN(ArraySlice) {
277 HandleScope scope(isolate);
278 Handle<Object> receiver = args.receiver();
279 int len = -1;
280 int relative_start = 0;
281 int relative_end = 0;
282
283 if (receiver->IsJSArray()) {
284 DisallowHeapAllocation no_gc;
285 JSArray* array = JSArray::cast(*receiver);
286 if (V8_UNLIKELY(!array->HasFastElements() ||
287 !IsJSArrayFastElementMovingAllowed(isolate, array) ||
288 !isolate->IsArraySpeciesLookupChainIntact() ||
289 // If this is a subclass of Array, then call out to JS
290 !array->HasArrayPrototype(isolate))) {
291 AllowHeapAllocation allow_allocation;
292 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
293 }
294 len = Smi::cast(array->length())->value();
295 } else if (receiver->IsJSObject() &&
296 GetSloppyArgumentsLength(isolate, Handle<JSObject>::cast(receiver),
297 &len)) {
298 // Array.prototype.slice.call(arguments, ...) is quite a common idiom
299 // (notably more than 50% of invocations in Web apps).
300 // Treat it in C++ as well.
301 DCHECK(JSObject::cast(*receiver)->HasFastElements() ||
302 JSObject::cast(*receiver)->HasFastArgumentsElements());
303 } else {
304 AllowHeapAllocation allow_allocation;
305 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
306 }
307 DCHECK_LE(0, len);
308 int argument_count = args.length() - 1;
309 // Note carefully chosen defaults---if argument is missing,
310 // it's undefined which gets converted to 0 for relative_start
311 // and to len for relative_end.
312 relative_start = 0;
313 relative_end = len;
314 if (argument_count > 0) {
315 DisallowHeapAllocation no_gc;
316 if (!ClampedToInteger(isolate, args[1], &relative_start)) {
317 AllowHeapAllocation allow_allocation;
318 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
319 }
320 if (argument_count > 1) {
321 Object* end_arg = args[2];
322 // slice handles the end_arg specially
323 if (end_arg->IsUndefined(isolate)) {
324 relative_end = len;
325 } else if (!ClampedToInteger(isolate, end_arg, &relative_end)) {
326 AllowHeapAllocation allow_allocation;
327 return CallJsIntrinsic(isolate, isolate->array_slice(), args);
328 }
329 }
330 }
331
332 // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
333 uint32_t actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
334 : Min(relative_start, len);
335
336 // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
337 uint32_t actual_end =
338 (relative_end < 0) ? Max(len + relative_end, 0) : Min(relative_end, len);
339
340 Handle<JSObject> object = Handle<JSObject>::cast(receiver);
341 ElementsAccessor* accessor = object->GetElementsAccessor();
342 return *accessor->Slice(object, actual_start, actual_end);
343 }
344
345 BUILTIN(ArraySplice) {
346 HandleScope scope(isolate);
347 Handle<Object> receiver = args.receiver();
348 if (V8_UNLIKELY(
349 !EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3) ||
350 // If this is a subclass of Array, then call out to JS.
351 !Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) ||
352 // If anything with @@species has been messed with, call out to JS.
353 !isolate->IsArraySpeciesLookupChainIntact())) {
354 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
355 }
356 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
357
358 int argument_count = args.length() - 1;
359 int relative_start = 0;
360 if (argument_count > 0) {
361 DisallowHeapAllocation no_gc;
362 if (!ClampedToInteger(isolate, args[1], &relative_start)) {
363 AllowHeapAllocation allow_allocation;
364 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
365 }
366 }
367 int len = Smi::cast(array->length())->value();
368 // clip relative start to [0, len]
369 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0)
370 : Min(relative_start, len);
371
372 int actual_delete_count;
373 if (argument_count == 1) {
374 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
375 // given as a request to delete all the elements from the start.
376 // And it differs from the case of undefined delete count.
377 // This does not follow ECMA-262, but we do the same for compatibility.
378 DCHECK(len - actual_start >= 0);
379 actual_delete_count = len - actual_start;
380 } else {
381 int delete_count = 0;
382 DisallowHeapAllocation no_gc;
383 if (argument_count > 1) {
384 if (!ClampedToInteger(isolate, args[2], &delete_count)) {
385 AllowHeapAllocation allow_allocation;
386 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
387 }
388 }
389 actual_delete_count = Min(Max(delete_count, 0), len - actual_start);
390 }
391
392 int add_count = (argument_count > 1) ? (argument_count - 2) : 0;
393 int new_length = len - actual_delete_count + add_count;
394
395 if (new_length != len && JSArray::HasReadOnlyLength(array)) {
396 AllowHeapAllocation allow_allocation;
397 return CallJsIntrinsic(isolate, isolate->array_splice(), args);
398 }
399 ElementsAccessor* accessor = array->GetElementsAccessor();
400 Handle<JSArray> result_array = accessor->Splice(
401 array, actual_start, actual_delete_count, &args, add_count);
402 return *result_array;
403 }
404
405 // Array Concat -------------------------------------------------------------
406
407 namespace {
408
409 /**
410 * A simple visitor visits every element of Array's.
411 * The backend storage can be a fixed array for fast elements case,
412 * or a dictionary for sparse array. Since Dictionary is a subtype
413 * of FixedArray, the class can be used by both fast and slow cases.
414 * The second parameter of the constructor, fast_elements, specifies
415 * whether the storage is a FixedArray or Dictionary.
416 *
417 * An index limit is used to deal with the situation that a result array
418 * length overflows 32-bit non-negative integer.
419 */
420 class ArrayConcatVisitor {
421 public:
422 ArrayConcatVisitor(Isolate* isolate, Handle<Object> storage,
423 bool fast_elements)
424 : isolate_(isolate),
425 storage_(isolate->global_handles()->Create(*storage)),
426 index_offset_(0u),
427 bit_field_(FastElementsField::encode(fast_elements) |
428 ExceedsLimitField::encode(false) |
429 IsFixedArrayField::encode(storage->IsFixedArray())) {
430 DCHECK(!(this->fast_elements() && !is_fixed_array()));
431 }
432
433 ~ArrayConcatVisitor() { clear_storage(); }
434
435 MUST_USE_RESULT bool visit(uint32_t i, Handle<Object> elm) {
436 uint32_t index = index_offset_ + i;
437
438 if (i >= JSObject::kMaxElementCount - index_offset_) {
439 set_exceeds_array_limit(true);
440 // Exception hasn't been thrown at this point. Return true to
441 // break out, and caller will throw. !visit would imply that
442 // there is already a pending exception.
443 return true;
444 }
445
446 if (!is_fixed_array()) {
447 LookupIterator it(isolate_, storage_, index, LookupIterator::OWN);
448 MAYBE_RETURN(
449 JSReceiver::CreateDataProperty(&it, elm, Object::THROW_ON_ERROR),
450 false);
451 return true;
452 }
453
454 if (fast_elements()) {
455 if (index < static_cast<uint32_t>(storage_fixed_array()->length())) {
456 storage_fixed_array()->set(index, *elm);
457 return true;
458 }
459 // Our initial estimate of length was foiled, possibly by
460 // getters on the arrays increasing the length of later arrays
461 // during iteration.
462 // This shouldn't happen in anything but pathological cases.
463 SetDictionaryMode();
464 // Fall-through to dictionary mode.
465 }
466 DCHECK(!fast_elements());
467 Handle<SeededNumberDictionary> dict(
468 SeededNumberDictionary::cast(*storage_));
469 // The object holding this backing store has just been allocated, so
470 // it cannot yet be used as a prototype.
471 Handle<SeededNumberDictionary> result =
472 SeededNumberDictionary::AtNumberPut(dict, index, elm, false);
473 if (!result.is_identical_to(dict)) {
474 // Dictionary needed to grow.
475 clear_storage();
476 set_storage(*result);
477 }
478 return true;
479 }
480
481 void increase_index_offset(uint32_t delta) {
482 if (JSObject::kMaxElementCount - index_offset_ < delta) {
483 index_offset_ = JSObject::kMaxElementCount;
484 } else {
485 index_offset_ += delta;
486 }
487 // If the initial length estimate was off (see special case in visit()),
488 // but the array blowing the limit didn't contain elements beyond the
489 // provided-for index range, go to dictionary mode now.
490 if (fast_elements() &&
491 index_offset_ >
492 static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) {
493 SetDictionaryMode();
494 }
495 }
496
497 bool exceeds_array_limit() const {
498 return ExceedsLimitField::decode(bit_field_);
499 }
500
501 Handle<JSArray> ToArray() {
502 DCHECK(is_fixed_array());
503 Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
504 Handle<Object> length =
505 isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
506 Handle<Map> map = JSObject::GetElementsTransitionMap(
507 array, fast_elements() ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS);
508 array->set_map(*map);
509 array->set_length(*length);
510 array->set_elements(*storage_fixed_array());
511 return array;
512 }
513
514 // Storage is either a FixedArray (if is_fixed_array()) or a JSReciever
515 // (otherwise)
516 Handle<FixedArray> storage_fixed_array() {
517 DCHECK(is_fixed_array());
518 return Handle<FixedArray>::cast(storage_);
519 }
520 Handle<JSReceiver> storage_jsreceiver() {
521 DCHECK(!is_fixed_array());
522 return Handle<JSReceiver>::cast(storage_);
523 }
524
525 private:
526 // Convert storage to dictionary mode.
527 void SetDictionaryMode() {
528 DCHECK(fast_elements() && is_fixed_array());
529 Handle<FixedArray> current_storage = storage_fixed_array();
530 Handle<SeededNumberDictionary> slow_storage(
531 SeededNumberDictionary::New(isolate_, current_storage->length()));
532 uint32_t current_length = static_cast<uint32_t>(current_storage->length());
533 FOR_WITH_HANDLE_SCOPE(
534 isolate_, uint32_t, i = 0, i, i < current_length, i++, {
535 Handle<Object> element(current_storage->get(i), isolate_);
536 if (!element->IsTheHole(isolate_)) {
537 // The object holding this backing store has just been allocated, so
538 // it cannot yet be used as a prototype.
539 Handle<SeededNumberDictionary> new_storage =
540 SeededNumberDictionary::AtNumberPut(slow_storage, i, element,
541 false);
542 if (!new_storage.is_identical_to(slow_storage)) {
543 slow_storage = loop_scope.CloseAndEscape(new_storage);
544 }
545 }
546 });
547 clear_storage();
548 set_storage(*slow_storage);
549 set_fast_elements(false);
550 }
551
552 inline void clear_storage() { GlobalHandles::Destroy(storage_.location()); }
553
554 inline void set_storage(FixedArray* storage) {
555 DCHECK(is_fixed_array());
556 storage_ = isolate_->global_handles()->Create(storage);
557 }
558
559 class FastElementsField : public BitField<bool, 0, 1> {};
560 class ExceedsLimitField : public BitField<bool, 1, 1> {};
561 class IsFixedArrayField : public BitField<bool, 2, 1> {};
562
563 bool fast_elements() const { return FastElementsField::decode(bit_field_); }
564 void set_fast_elements(bool fast) {
565 bit_field_ = FastElementsField::update(bit_field_, fast);
566 }
567 void set_exceeds_array_limit(bool exceeds) {
568 bit_field_ = ExceedsLimitField::update(bit_field_, exceeds);
569 }
570 bool is_fixed_array() const { return IsFixedArrayField::decode(bit_field_); }
571
572 Isolate* isolate_;
573 Handle<Object> storage_; // Always a global handle.
574 // Index after last seen index. Always less than or equal to
575 // JSObject::kMaxElementCount.
576 uint32_t index_offset_;
577 uint32_t bit_field_;
578 };
579
580 uint32_t EstimateElementCount(Handle<JSArray> array) {
581 DisallowHeapAllocation no_gc;
582 uint32_t length = static_cast<uint32_t>(array->length()->Number());
583 int element_count = 0;
584 switch (array->GetElementsKind()) {
585 case FAST_SMI_ELEMENTS:
586 case FAST_HOLEY_SMI_ELEMENTS:
587 case FAST_ELEMENTS:
588 case FAST_HOLEY_ELEMENTS: {
589 // Fast elements can't have lengths that are not representable by
590 // a 32-bit signed integer.
591 DCHECK(static_cast<int32_t>(FixedArray::kMaxLength) >= 0);
592 int fast_length = static_cast<int>(length);
593 Isolate* isolate = array->GetIsolate();
594 FixedArray* elements = FixedArray::cast(array->elements());
595 for (int i = 0; i < fast_length; i++) {
596 if (!elements->get(i)->IsTheHole(isolate)) element_count++;
597 }
598 break;
599 }
600 case FAST_DOUBLE_ELEMENTS:
601 case FAST_HOLEY_DOUBLE_ELEMENTS: {
602 // Fast elements can't have lengths that are not representable by
603 // a 32-bit signed integer.
604 DCHECK(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0);
605 int fast_length = static_cast<int>(length);
606 if (array->elements()->IsFixedArray()) {
607 DCHECK(FixedArray::cast(array->elements())->length() == 0);
608 break;
609 }
610 FixedDoubleArray* elements = FixedDoubleArray::cast(array->elements());
611 for (int i = 0; i < fast_length; i++) {
612 if (!elements->is_the_hole(i)) element_count++;
613 }
614 break;
615 }
616 case DICTIONARY_ELEMENTS: {
617 SeededNumberDictionary* dictionary =
618 SeededNumberDictionary::cast(array->elements());
619 Isolate* isolate = dictionary->GetIsolate();
620 int capacity = dictionary->Capacity();
621 for (int i = 0; i < capacity; i++) {
622 Object* key = dictionary->KeyAt(i);
623 if (dictionary->IsKey(isolate, key)) {
624 element_count++;
625 }
626 }
627 break;
628 }
629 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
630
631 TYPED_ARRAYS(TYPED_ARRAY_CASE)
632 #undef TYPED_ARRAY_CASE
633 // External arrays are always dense.
634 return length;
635 case NO_ELEMENTS:
636 return 0;
637 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
638 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS:
639 case FAST_STRING_WRAPPER_ELEMENTS:
640 case SLOW_STRING_WRAPPER_ELEMENTS:
641 UNREACHABLE();
642 return 0;
643 }
644 // As an estimate, we assume that the prototype doesn't contain any
645 // inherited elements.
646 return element_count;
647 }
648
649 // Used for sorting indices in a List<uint32_t>.
650 int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
651 uint32_t a = *ap;
652 uint32_t b = *bp;
653 return (a == b) ? 0 : (a < b) ? -1 : 1;
654 }
655
656 void CollectElementIndices(Handle<JSObject> object, uint32_t range,
657 List<uint32_t>* indices) {
658 Isolate* isolate = object->GetIsolate();
659 ElementsKind kind = object->GetElementsKind();
660 switch (kind) {
661 case FAST_SMI_ELEMENTS:
662 case FAST_ELEMENTS:
663 case FAST_HOLEY_SMI_ELEMENTS:
664 case FAST_HOLEY_ELEMENTS: {
665 DisallowHeapAllocation no_gc;
666 FixedArray* elements = FixedArray::cast(object->elements());
667 uint32_t length = static_cast<uint32_t>(elements->length());
668 if (range < length) length = range;
669 for (uint32_t i = 0; i < length; i++) {
670 if (!elements->get(i)->IsTheHole(isolate)) {
671 indices->Add(i);
672 }
673 }
674 break;
675 }
676 case FAST_HOLEY_DOUBLE_ELEMENTS:
677 case FAST_DOUBLE_ELEMENTS: {
678 if (object->elements()->IsFixedArray()) {
679 DCHECK(object->elements()->length() == 0);
680 break;
681 }
682 Handle<FixedDoubleArray> elements(
683 FixedDoubleArray::cast(object->elements()));
684 uint32_t length = static_cast<uint32_t>(elements->length());
685 if (range < length) length = range;
686 for (uint32_t i = 0; i < length; i++) {
687 if (!elements->is_the_hole(i)) {
688 indices->Add(i);
689 }
690 }
691 break;
692 }
693 case DICTIONARY_ELEMENTS: {
694 DisallowHeapAllocation no_gc;
695 SeededNumberDictionary* dict =
696 SeededNumberDictionary::cast(object->elements());
697 uint32_t capacity = dict->Capacity();
698 FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, j = 0, j, j < capacity, j++, {
699 Object* k = dict->KeyAt(j);
700 if (!dict->IsKey(isolate, k)) continue;
701 DCHECK(k->IsNumber());
702 uint32_t index = static_cast<uint32_t>(k->Number());
703 if (index < range) {
704 indices->Add(index);
705 }
706 });
707 break;
708 }
709 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
710
711 TYPED_ARRAYS(TYPED_ARRAY_CASE)
712 #undef TYPED_ARRAY_CASE
713 {
714 uint32_t length = static_cast<uint32_t>(
715 FixedArrayBase::cast(object->elements())->length());
716 if (range <= length) {
717 length = range;
718 // We will add all indices, so we might as well clear it first
719 // and avoid duplicates.
720 indices->Clear();
721 }
722 for (uint32_t i = 0; i < length; i++) {
723 indices->Add(i);
724 }
725 if (length == range) return; // All indices accounted for already.
726 break;
727 }
728 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
729 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
730 ElementsAccessor* accessor = object->GetElementsAccessor();
731 for (uint32_t i = 0; i < range; i++) {
732 if (accessor->HasElement(object, i)) {
733 indices->Add(i);
734 }
735 }
736 break;
737 }
738 case FAST_STRING_WRAPPER_ELEMENTS:
739 case SLOW_STRING_WRAPPER_ELEMENTS: {
740 DCHECK(object->IsJSValue());
741 Handle<JSValue> js_value = Handle<JSValue>::cast(object);
742 DCHECK(js_value->value()->IsString());
743 Handle<String> string(String::cast(js_value->value()), isolate);
744 uint32_t length = static_cast<uint32_t>(string->length());
745 uint32_t i = 0;
746 uint32_t limit = Min(length, range);
747 for (; i < limit; i++) {
748 indices->Add(i);
749 }
750 ElementsAccessor* accessor = object->GetElementsAccessor();
751 for (; i < range; i++) {
752 if (accessor->HasElement(object, i)) {
753 indices->Add(i);
754 }
755 }
756 break;
757 }
758 case NO_ELEMENTS:
759 break;
760 }
761
762 PrototypeIterator iter(isolate, object);
763 if (!iter.IsAtEnd()) {
764 // The prototype will usually have no inherited element indices,
765 // but we have to check.
766 CollectElementIndices(PrototypeIterator::GetCurrent<JSObject>(iter), range,
767 indices);
768 }
769 }
770
771 bool IterateElementsSlow(Isolate* isolate, Handle<JSReceiver> receiver,
772 uint32_t length, ArrayConcatVisitor* visitor) {
773 FOR_WITH_HANDLE_SCOPE(isolate, uint32_t, i = 0, i, i < length, ++i, {
774 Maybe<bool> maybe = JSReceiver::HasElement(receiver, i);
775 if (!maybe.IsJust()) return false;
776 if (maybe.FromJust()) {
777 Handle<Object> element_value;
778 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
779 isolate, element_value, JSReceiver::GetElement(isolate, receiver, i),
780 false);
781 if (!visitor->visit(i, element_value)) return false;
782 }
783 });
784 visitor->increase_index_offset(length);
785 return true;
786 }
787
788 /**
789 * A helper function that visits "array" elements of a JSReceiver in numerical
790 * order.
791 *
792 * The visitor argument called for each existing element in the array
793 * with the element index and the element's value.
794 * Afterwards it increments the base-index of the visitor by the array
795 * length.
796 * Returns false if any access threw an exception, otherwise true.
797 */
798 bool IterateElements(Isolate* isolate, Handle<JSReceiver> receiver,
799 ArrayConcatVisitor* visitor) {
800 uint32_t length = 0;
801
802 if (receiver->IsJSArray()) {
803 Handle<JSArray> array = Handle<JSArray>::cast(receiver);
804 length = static_cast<uint32_t>(array->length()->Number());
805 } else {
806 Handle<Object> val;
807 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
808 isolate, val, Object::GetLengthFromArrayLike(isolate, receiver), false);
809 // TODO(caitp): Support larger element indexes (up to 2^53-1).
810 if (!val->ToUint32(&length)) {
811 length = 0;
812 }
813 // TODO(cbruni): handle other element kind as well
814 return IterateElementsSlow(isolate, receiver, length, visitor);
815 }
816
817 if (!HasOnlySimpleElements(isolate, *receiver)) {
818 return IterateElementsSlow(isolate, receiver, length, visitor);
819 }
820 Handle<JSObject> array = Handle<JSObject>::cast(receiver);
821
822 switch (array->GetElementsKind()) {
823 case FAST_SMI_ELEMENTS:
824 case FAST_ELEMENTS:
825 case FAST_HOLEY_SMI_ELEMENTS:
826 case FAST_HOLEY_ELEMENTS: {
827 // Run through the elements FixedArray and use HasElement and GetElement
828 // to check the prototype for missing elements.
829 Handle<FixedArray> elements(FixedArray::cast(array->elements()));
830 int fast_length = static_cast<int>(length);
831 DCHECK(fast_length <= elements->length());
832 FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
833 Handle<Object> element_value(elements->get(j), isolate);
834 if (!element_value->IsTheHole(isolate)) {
835 if (!visitor->visit(j, element_value)) return false;
836 } else {
837 Maybe<bool> maybe = JSReceiver::HasElement(array, j);
838 if (!maybe.IsJust()) return false;
839 if (maybe.FromJust()) {
840 // Call GetElement on array, not its prototype, or getters won't
841 // have the correct receiver.
842 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
843 isolate, element_value,
844 JSReceiver::GetElement(isolate, array, j), false);
845 if (!visitor->visit(j, element_value)) return false;
846 }
847 }
848 });
849 break;
850 }
851 case FAST_HOLEY_DOUBLE_ELEMENTS:
852 case FAST_DOUBLE_ELEMENTS: {
853 // Empty array is FixedArray but not FixedDoubleArray.
854 if (length == 0) break;
855 // Run through the elements FixedArray and use HasElement and GetElement
856 // to check the prototype for missing elements.
857 if (array->elements()->IsFixedArray()) {
858 DCHECK(array->elements()->length() == 0);
859 break;
860 }
861 Handle<FixedDoubleArray> elements(
862 FixedDoubleArray::cast(array->elements()));
863 int fast_length = static_cast<int>(length);
864 DCHECK(fast_length <= elements->length());
865 FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < fast_length, j++, {
866 if (!elements->is_the_hole(j)) {
867 double double_value = elements->get_scalar(j);
868 Handle<Object> element_value =
869 isolate->factory()->NewNumber(double_value);
870 if (!visitor->visit(j, element_value)) return false;
871 } else {
872 Maybe<bool> maybe = JSReceiver::HasElement(array, j);
873 if (!maybe.IsJust()) return false;
874 if (maybe.FromJust()) {
875 // Call GetElement on array, not its prototype, or getters won't
876 // have the correct receiver.
877 Handle<Object> element_value;
878 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
879 isolate, element_value,
880 JSReceiver::GetElement(isolate, array, j), false);
881 if (!visitor->visit(j, element_value)) return false;
882 }
883 }
884 });
885 break;
886 }
887
888 case DICTIONARY_ELEMENTS: {
889 Handle<SeededNumberDictionary> dict(array->element_dictionary());
890 List<uint32_t> indices(dict->Capacity() / 2);
891 // Collect all indices in the object and the prototypes less
892 // than length. This might introduce duplicates in the indices list.
893 CollectElementIndices(array, length, &indices);
894 indices.Sort(&compareUInt32);
895 int n = indices.length();
896 FOR_WITH_HANDLE_SCOPE(isolate, int, j = 0, j, j < n, (void)0, {
897 uint32_t index = indices[j];
898 Handle<Object> element;
899 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
900 isolate, element, JSReceiver::GetElement(isolate, array, index),
901 false);
902 if (!visitor->visit(index, element)) return false;
903 // Skip to next different index (i.e., omit duplicates).
904 do {
905 j++;
906 } while (j < n && indices[j] == index);
907 });
908 break;
909 }
910 case FAST_SLOPPY_ARGUMENTS_ELEMENTS:
911 case SLOW_SLOPPY_ARGUMENTS_ELEMENTS: {
912 FOR_WITH_HANDLE_SCOPE(
913 isolate, uint32_t, index = 0, index, index < length, index++, {
914 Handle<Object> element;
915 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
916 isolate, element, JSReceiver::GetElement(isolate, array, index),
917 false);
918 if (!visitor->visit(index, element)) return false;
919 });
920 break;
921 }
922 case NO_ELEMENTS:
923 break;
924 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) case TYPE##_ELEMENTS:
925 TYPED_ARRAYS(TYPED_ARRAY_CASE)
926 #undef TYPED_ARRAY_CASE
927 return IterateElementsSlow(isolate, receiver, length, visitor);
928 case FAST_STRING_WRAPPER_ELEMENTS:
929 case SLOW_STRING_WRAPPER_ELEMENTS:
930 // |array| is guaranteed to be an array or typed array.
931 UNREACHABLE();
932 break;
933 }
934 visitor->increase_index_offset(length);
935 return true;
936 }
937
938 static Maybe<bool> IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
939 HandleScope handle_scope(isolate);
940 if (!obj->IsJSReceiver()) return Just(false);
941 if (!isolate->IsIsConcatSpreadableLookupChainIntact()) {
942 // Slow path if @@isConcatSpreadable has been used.
943 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol());
944 Handle<Object> value;
945 MaybeHandle<Object> maybeValue =
946 i::Runtime::GetObjectProperty(isolate, obj, key);
947 if (!maybeValue.ToHandle(&value)) return Nothing<bool>();
948 if (!value->IsUndefined(isolate)) return Just(value->BooleanValue());
949 }
950 return Object::IsArray(obj);
951 }
952
953 Object* Slow_ArrayConcat(BuiltinArguments* args, Handle<Object> species,
954 Isolate* isolate) {
955 int argument_count = args->length();
956
957 bool is_array_species = *species == isolate->context()->array_function();
958
959 // Pass 1: estimate the length and number of elements of the result.
960 // The actual length can be larger if any of the arguments have getters
961 // that mutate other arguments (but will otherwise be precise).
962 // The number of elements is precise if there are no inherited elements.
963
964 ElementsKind kind = FAST_SMI_ELEMENTS;
965
966 uint32_t estimate_result_length = 0;
967 uint32_t estimate_nof_elements = 0;
968 FOR_WITH_HANDLE_SCOPE(isolate, int, i = 0, i, i < argument_count, i++, {
969 Handle<Object> obj((*args)[i], isolate);
970 uint32_t length_estimate;
971 uint32_t element_estimate;
972 if (obj->IsJSArray()) {
973 Handle<JSArray> array(Handle<JSArray>::cast(obj));
974 length_estimate = static_cast<uint32_t>(array->length()->Number());
975 if (length_estimate != 0) {
976 ElementsKind array_kind =
977 GetPackedElementsKind(array->GetElementsKind());
978 kind = GetMoreGeneralElementsKind(kind, array_kind);
979 }
980 element_estimate = EstimateElementCount(array);
981 } else {
982 if (obj->IsHeapObject()) {
983 kind = GetMoreGeneralElementsKind(
984 kind, obj->IsNumber() ? FAST_DOUBLE_ELEMENTS : FAST_ELEMENTS);
985 }
986 length_estimate = 1;
987 element_estimate = 1;
988 }
989 // Avoid overflows by capping at kMaxElementCount.
990 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) {
991 estimate_result_length = JSObject::kMaxElementCount;
992 } else {
993 estimate_result_length += length_estimate;
994 }
995 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) {
996 estimate_nof_elements = JSObject::kMaxElementCount;
997 } else {
998 estimate_nof_elements += element_estimate;
999 }
1000 });
1001
1002 // If estimated number of elements is more than half of length, a
1003 // fixed array (fast case) is more time and space-efficient than a
1004 // dictionary.
1005 bool fast_case =
1006 is_array_species && (estimate_nof_elements * 2) >= estimate_result_length;
1007
1008 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) {
1009 Handle<FixedArrayBase> storage =
1010 isolate->factory()->NewFixedDoubleArray(estimate_result_length);
1011 int j = 0;
1012 bool failure = false;
1013 if (estimate_result_length > 0) {
1014 Handle<FixedDoubleArray> double_storage =
1015 Handle<FixedDoubleArray>::cast(storage);
1016 for (int i = 0; i < argument_count; i++) {
1017 Handle<Object> obj((*args)[i], isolate);
1018 if (obj->IsSmi()) {
1019 double_storage->set(j, Smi::cast(*obj)->value());
1020 j++;
1021 } else if (obj->IsNumber()) {
1022 double_storage->set(j, obj->Number());
1023 j++;
1024 } else {
1025 DisallowHeapAllocation no_gc;
1026 JSArray* array = JSArray::cast(*obj);
1027 uint32_t length = static_cast<uint32_t>(array->length()->Number());
1028 switch (array->GetElementsKind()) {
1029 case FAST_HOLEY_DOUBLE_ELEMENTS:
1030 case FAST_DOUBLE_ELEMENTS: {
1031 // Empty array is FixedArray but not FixedDoubleArray.
1032 if (length == 0) break;
1033 FixedDoubleArray* elements =
1034 FixedDoubleArray::cast(array->elements());
1035 for (uint32_t i = 0; i < length; i++) {
1036 if (elements->is_the_hole(i)) {
1037 // TODO(jkummerow/verwaest): We could be a bit more clever
1038 // here: Check if there are no elements/getters on the
1039 // prototype chain, and if so, allow creation of a holey
1040 // result array.
1041 // Same thing below (holey smi case).
1042 failure = true;
1043 break;
1044 }
1045 double double_value = elements->get_scalar(i);
1046 double_storage->set(j, double_value);
1047 j++;
1048 }
1049 break;
1050 }
1051 case FAST_HOLEY_SMI_ELEMENTS:
1052 case FAST_SMI_ELEMENTS: {
1053 Object* the_hole = isolate->heap()->the_hole_value();
1054 FixedArray* elements(FixedArray::cast(array->elements()));
1055 for (uint32_t i = 0; i < length; i++) {
1056 Object* element = elements->get(i);
1057 if (element == the_hole) {
1058 failure = true;
1059 break;
1060 }
1061 int32_t int_value = Smi::cast(element)->value();
1062 double_storage->set(j, int_value);
1063 j++;
1064 }
1065 break;
1066 }
1067 case FAST_HOLEY_ELEMENTS:
1068 case FAST_ELEMENTS:
1069 case DICTIONARY_ELEMENTS:
1070 case NO_ELEMENTS:
1071 DCHECK_EQ(0u, length);
1072 break;
1073 default:
1074 UNREACHABLE();
1075 }
1076 }
1077 if (failure) break;
1078 }
1079 }
1080 if (!failure) {
1081 return *isolate->factory()->NewJSArrayWithElements(storage, kind, j);
1082 }
1083 // In case of failure, fall through.
1084 }
1085
1086 Handle<Object> storage;
1087 if (fast_case) {
1088 // The backing storage array must have non-existing elements to preserve
1089 // holes across concat operations.
1090 storage =
1091 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length);
1092 } else if (is_array_species) {
1093 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
1094 uint32_t at_least_space_for =
1095 estimate_nof_elements + (estimate_nof_elements >> 2);
1096 storage = SeededNumberDictionary::New(isolate, at_least_space_for);
1097 } else {
1098 DCHECK(species->IsConstructor());
1099 Handle<Object> length(Smi::FromInt(0), isolate);
1100 Handle<Object> storage_object;
1101 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1102 isolate, storage_object,
1103 Execution::New(isolate, species, species, 1, &length));
1104 storage = storage_object;
1105 }
1106
1107 ArrayConcatVisitor visitor(isolate, storage, fast_case);
1108
1109 for (int i = 0; i < argument_count; i++) {
1110 Handle<Object> obj((*args)[i], isolate);
1111 Maybe<bool> spreadable = IsConcatSpreadable(isolate, obj);
1112 MAYBE_RETURN(spreadable, isolate->heap()->exception());
1113 if (spreadable.FromJust()) {
1114 Handle<JSReceiver> object = Handle<JSReceiver>::cast(obj);
1115 if (!IterateElements(isolate, object, &visitor)) {
1116 return isolate->heap()->exception();
1117 }
1118 } else {
1119 if (!visitor.visit(0, obj)) return isolate->heap()->exception();
1120 visitor.increase_index_offset(1);
1121 }
1122 }
1123
1124 if (visitor.exceeds_array_limit()) {
1125 THROW_NEW_ERROR_RETURN_FAILURE(
1126 isolate, NewRangeError(MessageTemplate::kInvalidArrayLength));
1127 }
1128
1129 if (is_array_species) {
1130 return *visitor.ToArray();
1131 } else {
1132 return *visitor.storage_jsreceiver();
1133 }
1134 }
1135
1136 bool IsSimpleArray(Isolate* isolate, Handle<JSArray> obj) {
1137 DisallowHeapAllocation no_gc;
1138 Map* map = obj->map();
1139 // If there is only the 'length' property we are fine.
1140 if (map->prototype() ==
1141 isolate->native_context()->initial_array_prototype() &&
1142 map->NumberOfOwnDescriptors() == 1) {
1143 return true;
1144 }
1145 // TODO(cbruni): slower lookup for array subclasses and support slow
1146 // @@IsConcatSpreadable lookup.
1147 return false;
1148 }
1149
1150 MaybeHandle<JSArray> Fast_ArrayConcat(Isolate* isolate,
1151 BuiltinArguments* args) {
1152 if (!isolate->IsIsConcatSpreadableLookupChainIntact()) {
1153 return MaybeHandle<JSArray>();
1154 }
1155 // We shouldn't overflow when adding another len.
1156 const int kHalfOfMaxInt = 1 << (kBitsPerInt - 2);
1157 STATIC_ASSERT(FixedArray::kMaxLength < kHalfOfMaxInt);
1158 STATIC_ASSERT(FixedDoubleArray::kMaxLength < kHalfOfMaxInt);
1159 USE(kHalfOfMaxInt);
1160
1161 int n_arguments = args->length();
1162 int result_len = 0;
1163 {
1164 DisallowHeapAllocation no_gc;
1165 // Iterate through all the arguments performing checks
1166 // and calculating total length.
1167 for (int i = 0; i < n_arguments; i++) {
1168 Object* arg = (*args)[i];
1169 if (!arg->IsJSArray()) return MaybeHandle<JSArray>();
1170 if (!HasOnlySimpleReceiverElements(isolate, JSObject::cast(arg))) {
1171 return MaybeHandle<JSArray>();
1172 }
1173 // TODO(cbruni): support fast concatenation of DICTIONARY_ELEMENTS.
1174 if (!JSObject::cast(arg)->HasFastElements()) {
1175 return MaybeHandle<JSArray>();
1176 }
1177 Handle<JSArray> array(JSArray::cast(arg), isolate);
1178 if (!IsSimpleArray(isolate, array)) {
1179 return MaybeHandle<JSArray>();
1180 }
1181 // The Array length is guaranted to be <= kHalfOfMaxInt thus we won't
1182 // overflow.
1183 result_len += Smi::cast(array->length())->value();
1184 DCHECK(result_len >= 0);
1185 // Throw an Error if we overflow the FixedArray limits
1186 if (FixedDoubleArray::kMaxLength < result_len ||
1187 FixedArray::kMaxLength < result_len) {
1188 AllowHeapAllocation gc;
1189 THROW_NEW_ERROR(isolate,
1190 NewRangeError(MessageTemplate::kInvalidArrayLength),
1191 JSArray);
1192 }
1193 }
1194 }
1195 return ElementsAccessor::Concat(isolate, args, n_arguments, result_len);
1196 }
1197
1198 } // namespace
1199
1200 // ES6 22.1.3.1 Array.prototype.concat
1201 BUILTIN(ArrayConcat) {
1202 HandleScope scope(isolate);
1203
1204 Handle<Object> receiver = args.receiver();
1205 // TODO(bmeurer): Do we really care about the exact exception message here?
1206 if (receiver->IsNull(isolate) || receiver->IsUndefined(isolate)) {
1207 THROW_NEW_ERROR_RETURN_FAILURE(
1208 isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined,
1209 isolate->factory()->NewStringFromAsciiChecked(
1210 "Array.prototype.concat")));
1211 }
1212 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1213 isolate, receiver, Object::ToObject(isolate, args.receiver()));
1214 args[0] = *receiver;
1215
1216 Handle<JSArray> result_array;
1217
1218 // Avoid a real species read to avoid extra lookups to the array constructor
1219 if (V8_LIKELY(receiver->IsJSArray() &&
1220 Handle<JSArray>::cast(receiver)->HasArrayPrototype(isolate) &&
1221 isolate->IsArraySpeciesLookupChainIntact())) {
1222 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
1223 return *result_array;
1224 }
1225 if (isolate->has_pending_exception()) return isolate->heap()->exception();
1226 }
1227 // Reading @@species happens before anything else with a side effect, so
1228 // we can do it here to determine whether to take the fast path.
1229 Handle<Object> species;
1230 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1231 isolate, species, Object::ArraySpeciesConstructor(isolate, receiver));
1232 if (*species == *isolate->array_function()) {
1233 if (Fast_ArrayConcat(isolate, &args).ToHandle(&result_array)) {
1234 return *result_array;
1235 }
1236 if (isolate->has_pending_exception()) return isolate->heap()->exception();
1237 }
1238 return Slow_ArrayConcat(&args, species, isolate);
1239 }
1240
1241 void Builtins::Generate_ArrayIsArray(CodeStubAssembler* assembler) {
1242 typedef compiler::Node Node;
1243 typedef CodeStubAssembler::Label Label;
1244
1245 Node* object = assembler->Parameter(1);
1246 Node* context = assembler->Parameter(4);
1247
1248 Label call_runtime(assembler), return_true(assembler),
1249 return_false(assembler);
1250
1251 assembler->GotoIf(assembler->WordIsSmi(object), &return_false);
1252 Node* instance_type = assembler->LoadInstanceType(object);
1253
1254 assembler->GotoIf(assembler->Word32Equal(
1255 instance_type, assembler->Int32Constant(JS_ARRAY_TYPE)),
1256 &return_true);
1257
1258 // TODO(verwaest): Handle proxies in-place.
1259 assembler->Branch(assembler->Word32Equal(
1260 instance_type, assembler->Int32Constant(JS_PROXY_TYPE)),
1261 &call_runtime, &return_false);
1262
1263 assembler->Bind(&return_true);
1264 assembler->Return(assembler->BooleanConstant(true));
1265
1266 assembler->Bind(&return_false);
1267 assembler->Return(assembler->BooleanConstant(false));
1268
1269 assembler->Bind(&call_runtime);
1270 assembler->Return(
1271 assembler->CallRuntime(Runtime::kArrayIsArray, context, object));
1272 }
1273
1274 } // namespace internal
1275 } // namespace v8
OLDNEW
« no previous file with comments | « src/builtins/builtins.cc ('k') | src/builtins/builtins-utils.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698