blob: a017236a540643ef08e8b0f6b64c95d4dc0752b5 [file] [log] [blame]
Emily Bernierd0a1eb72015-03-24 16:35:39 -04001// Copyright 2014 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/v8.h"
6
7#include "src/arguments.h"
8#include "src/runtime/runtime-utils.h"
9
10namespace v8 {
11namespace internal {
12
13RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) {
14 HandleScope scope(isolate);
15 DCHECK(args.length() == 1);
16 CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0);
17 Object* length = prototype->length();
18 RUNTIME_ASSERT(length->IsSmi() && Smi::cast(length)->value() == 0);
19 RUNTIME_ASSERT(prototype->HasFastSmiOrObjectElements());
20 // This is necessary to enable fast checks for absence of elements
21 // on Array.prototype and below.
22 prototype->set_elements(isolate->heap()->empty_fixed_array());
23 return Smi::FromInt(0);
24}
25
26
27static void InstallBuiltin(Isolate* isolate, Handle<JSObject> holder,
28 const char* name, Builtins::Name builtin_name) {
29 Handle<String> key = isolate->factory()->InternalizeUtf8String(name);
30 Handle<Code> code(isolate->builtins()->builtin(builtin_name));
31 Handle<JSFunction> optimized =
32 isolate->factory()->NewFunctionWithoutPrototype(key, code);
33 optimized->shared()->DontAdaptArguments();
34 JSObject::AddProperty(holder, key, optimized, NONE);
35}
36
37
38RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
39 HandleScope scope(isolate);
40 DCHECK(args.length() == 0);
41 Handle<JSObject> holder =
42 isolate->factory()->NewJSObject(isolate->object_function());
43
44 InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop);
45 InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush);
46 InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift);
47 InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
48 InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
49 InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
50 InstallBuiltin(isolate, holder, "concat", Builtins::kArrayConcat);
51
52 return *holder;
53}
54
55
56RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
57 HandleScope scope(isolate);
58 RUNTIME_ASSERT(args.length() == 2);
59 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
60 CONVERT_ARG_HANDLE_CHECKED(Map, map, 1);
61 JSObject::TransitionElementsKind(array, map->elements_kind());
62 return *array;
63}
64
65
66// Push an object unto an array of objects if it is not already in the
67// array. Returns true if the element was pushed on the stack and
68// false otherwise.
69RUNTIME_FUNCTION(Runtime_PushIfAbsent) {
70 HandleScope scope(isolate);
71 DCHECK(args.length() == 2);
72 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
73 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, element, 1);
74 RUNTIME_ASSERT(array->HasFastSmiOrObjectElements());
75 int length = Smi::cast(array->length())->value();
76 FixedArray* elements = FixedArray::cast(array->elements());
77 for (int i = 0; i < length; i++) {
78 if (elements->get(i) == *element) return isolate->heap()->false_value();
79 }
80
81 // Strict not needed. Used for cycle detection in Array join implementation.
82 RETURN_FAILURE_ON_EXCEPTION(
83 isolate, JSObject::SetFastElement(array, length, element, SLOPPY, true));
84 return isolate->heap()->true_value();
85}
86
87
88/**
89 * A simple visitor visits every element of Array's.
90 * The backend storage can be a fixed array for fast elements case,
91 * or a dictionary for sparse array. Since Dictionary is a subtype
92 * of FixedArray, the class can be used by both fast and slow cases.
93 * The second parameter of the constructor, fast_elements, specifies
94 * whether the storage is a FixedArray or Dictionary.
95 *
96 * An index limit is used to deal with the situation that a result array
97 * length overflows 32-bit non-negative integer.
98 */
99class ArrayConcatVisitor {
100 public:
101 ArrayConcatVisitor(Isolate* isolate, Handle<FixedArray> storage,
102 bool fast_elements)
103 : isolate_(isolate),
104 storage_(Handle<FixedArray>::cast(
105 isolate->global_handles()->Create(*storage))),
106 index_offset_(0u),
107 bit_field_(FastElementsField::encode(fast_elements) |
108 ExceedsLimitField::encode(false)) {}
109
110 ~ArrayConcatVisitor() { clear_storage(); }
111
112 void visit(uint32_t i, Handle<Object> elm) {
113 if (i > JSObject::kMaxElementCount - index_offset_) {
114 set_exceeds_array_limit(true);
115 return;
116 }
117 uint32_t index = index_offset_ + i;
118
119 if (fast_elements()) {
120 if (index < static_cast<uint32_t>(storage_->length())) {
121 storage_->set(index, *elm);
122 return;
123 }
124 // Our initial estimate of length was foiled, possibly by
125 // getters on the arrays increasing the length of later arrays
126 // during iteration.
127 // This shouldn't happen in anything but pathological cases.
128 SetDictionaryMode();
129 // Fall-through to dictionary mode.
130 }
131 DCHECK(!fast_elements());
132 Handle<SeededNumberDictionary> dict(
133 SeededNumberDictionary::cast(*storage_));
134 Handle<SeededNumberDictionary> result =
135 SeededNumberDictionary::AtNumberPut(dict, index, elm);
136 if (!result.is_identical_to(dict)) {
137 // Dictionary needed to grow.
138 clear_storage();
139 set_storage(*result);
140 }
141 }
142
143 void increase_index_offset(uint32_t delta) {
144 if (JSObject::kMaxElementCount - index_offset_ < delta) {
145 index_offset_ = JSObject::kMaxElementCount;
146 } else {
147 index_offset_ += delta;
148 }
149 // If the initial length estimate was off (see special case in visit()),
150 // but the array blowing the limit didn't contain elements beyond the
151 // provided-for index range, go to dictionary mode now.
152 if (fast_elements() &&
153 index_offset_ >
154 static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) {
155 SetDictionaryMode();
156 }
157 }
158
159 bool exceeds_array_limit() const {
160 return ExceedsLimitField::decode(bit_field_);
161 }
162
163 Handle<JSArray> ToArray() {
164 Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
165 Handle<Object> length =
166 isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
167 Handle<Map> map = JSObject::GetElementsTransitionMap(
168 array, fast_elements() ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS);
169 array->set_map(*map);
170 array->set_length(*length);
171 array->set_elements(*storage_);
172 return array;
173 }
174
175 private:
176 // Convert storage to dictionary mode.
177 void SetDictionaryMode() {
178 DCHECK(fast_elements());
179 Handle<FixedArray> current_storage(*storage_);
180 Handle<SeededNumberDictionary> slow_storage(
181 SeededNumberDictionary::New(isolate_, current_storage->length()));
182 uint32_t current_length = static_cast<uint32_t>(current_storage->length());
183 for (uint32_t i = 0; i < current_length; i++) {
184 HandleScope loop_scope(isolate_);
185 Handle<Object> element(current_storage->get(i), isolate_);
186 if (!element->IsTheHole()) {
187 Handle<SeededNumberDictionary> new_storage =
188 SeededNumberDictionary::AtNumberPut(slow_storage, i, element);
189 if (!new_storage.is_identical_to(slow_storage)) {
190 slow_storage = loop_scope.CloseAndEscape(new_storage);
191 }
192 }
193 }
194 clear_storage();
195 set_storage(*slow_storage);
196 set_fast_elements(false);
197 }
198
199 inline void clear_storage() {
200 GlobalHandles::Destroy(Handle<Object>::cast(storage_).location());
201 }
202
203 inline void set_storage(FixedArray* storage) {
204 storage_ =
205 Handle<FixedArray>::cast(isolate_->global_handles()->Create(storage));
206 }
207
208 class FastElementsField : public BitField<bool, 0, 1> {};
209 class ExceedsLimitField : public BitField<bool, 1, 1> {};
210
211 bool fast_elements() const { return FastElementsField::decode(bit_field_); }
212 void set_fast_elements(bool fast) {
213 bit_field_ = FastElementsField::update(bit_field_, fast);
214 }
215 void set_exceeds_array_limit(bool exceeds) {
216 bit_field_ = ExceedsLimitField::update(bit_field_, exceeds);
217 }
218
219 Isolate* isolate_;
220 Handle<FixedArray> storage_; // Always a global handle.
221 // Index after last seen index. Always less than or equal to
222 // JSObject::kMaxElementCount.
223 uint32_t index_offset_;
224 uint32_t bit_field_;
225};
226
227
228static uint32_t EstimateElementCount(Handle<JSArray> array) {
229 uint32_t length = static_cast<uint32_t>(array->length()->Number());
230 int element_count = 0;
231 switch (array->GetElementsKind()) {
232 case FAST_SMI_ELEMENTS:
233 case FAST_HOLEY_SMI_ELEMENTS:
234 case FAST_ELEMENTS:
235 case FAST_HOLEY_ELEMENTS: {
236 // Fast elements can't have lengths that are not representable by
237 // a 32-bit signed integer.
238 DCHECK(static_cast<int32_t>(FixedArray::kMaxLength) >= 0);
239 int fast_length = static_cast<int>(length);
240 Handle<FixedArray> elements(FixedArray::cast(array->elements()));
241 for (int i = 0; i < fast_length; i++) {
242 if (!elements->get(i)->IsTheHole()) element_count++;
243 }
244 break;
245 }
246 case FAST_DOUBLE_ELEMENTS:
247 case FAST_HOLEY_DOUBLE_ELEMENTS: {
248 // Fast elements can't have lengths that are not representable by
249 // a 32-bit signed integer.
250 DCHECK(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0);
251 int fast_length = static_cast<int>(length);
252 if (array->elements()->IsFixedArray()) {
253 DCHECK(FixedArray::cast(array->elements())->length() == 0);
254 break;
255 }
256 Handle<FixedDoubleArray> elements(
257 FixedDoubleArray::cast(array->elements()));
258 for (int i = 0; i < fast_length; i++) {
259 if (!elements->is_the_hole(i)) element_count++;
260 }
261 break;
262 }
263 case DICTIONARY_ELEMENTS: {
264 Handle<SeededNumberDictionary> dictionary(
265 SeededNumberDictionary::cast(array->elements()));
266 int capacity = dictionary->Capacity();
267 for (int i = 0; i < capacity; i++) {
268 Handle<Object> key(dictionary->KeyAt(i), array->GetIsolate());
269 if (dictionary->IsKey(*key)) {
270 element_count++;
271 }
272 }
273 break;
274 }
275 case SLOPPY_ARGUMENTS_ELEMENTS:
276#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
277 case EXTERNAL_##TYPE##_ELEMENTS: \
278 case TYPE##_ELEMENTS:
279
280 TYPED_ARRAYS(TYPED_ARRAY_CASE)
281#undef TYPED_ARRAY_CASE
282 // External arrays are always dense.
283 return length;
284 }
285 // As an estimate, we assume that the prototype doesn't contain any
286 // inherited elements.
287 return element_count;
288}
289
290
291template <class ExternalArrayClass, class ElementType>
292static void IterateTypedArrayElements(Isolate* isolate,
293 Handle<JSObject> receiver,
294 bool elements_are_ints,
295 bool elements_are_guaranteed_smis,
296 ArrayConcatVisitor* visitor) {
297 Handle<ExternalArrayClass> array(
298 ExternalArrayClass::cast(receiver->elements()));
299 uint32_t len = static_cast<uint32_t>(array->length());
300
301 DCHECK(visitor != NULL);
302 if (elements_are_ints) {
303 if (elements_are_guaranteed_smis) {
304 for (uint32_t j = 0; j < len; j++) {
305 HandleScope loop_scope(isolate);
306 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get_scalar(j))),
307 isolate);
308 visitor->visit(j, e);
309 }
310 } else {
311 for (uint32_t j = 0; j < len; j++) {
312 HandleScope loop_scope(isolate);
313 int64_t val = static_cast<int64_t>(array->get_scalar(j));
314 if (Smi::IsValid(static_cast<intptr_t>(val))) {
315 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)), isolate);
316 visitor->visit(j, e);
317 } else {
318 Handle<Object> e =
319 isolate->factory()->NewNumber(static_cast<ElementType>(val));
320 visitor->visit(j, e);
321 }
322 }
323 }
324 } else {
325 for (uint32_t j = 0; j < len; j++) {
326 HandleScope loop_scope(isolate);
327 Handle<Object> e = isolate->factory()->NewNumber(array->get_scalar(j));
328 visitor->visit(j, e);
329 }
330 }
331}
332
333
334// Used for sorting indices in a List<uint32_t>.
335static int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
336 uint32_t a = *ap;
337 uint32_t b = *bp;
338 return (a == b) ? 0 : (a < b) ? -1 : 1;
339}
340
341
342static void CollectElementIndices(Handle<JSObject> object, uint32_t range,
343 List<uint32_t>* indices) {
344 Isolate* isolate = object->GetIsolate();
345 ElementsKind kind = object->GetElementsKind();
346 switch (kind) {
347 case FAST_SMI_ELEMENTS:
348 case FAST_ELEMENTS:
349 case FAST_HOLEY_SMI_ELEMENTS:
350 case FAST_HOLEY_ELEMENTS: {
351 Handle<FixedArray> elements(FixedArray::cast(object->elements()));
352 uint32_t length = static_cast<uint32_t>(elements->length());
353 if (range < length) length = range;
354 for (uint32_t i = 0; i < length; i++) {
355 if (!elements->get(i)->IsTheHole()) {
356 indices->Add(i);
357 }
358 }
359 break;
360 }
361 case FAST_HOLEY_DOUBLE_ELEMENTS:
362 case FAST_DOUBLE_ELEMENTS: {
363 if (object->elements()->IsFixedArray()) {
364 DCHECK(object->elements()->length() == 0);
365 break;
366 }
367 Handle<FixedDoubleArray> elements(
368 FixedDoubleArray::cast(object->elements()));
369 uint32_t length = static_cast<uint32_t>(elements->length());
370 if (range < length) length = range;
371 for (uint32_t i = 0; i < length; i++) {
372 if (!elements->is_the_hole(i)) {
373 indices->Add(i);
374 }
375 }
376 break;
377 }
378 case DICTIONARY_ELEMENTS: {
379 Handle<SeededNumberDictionary> dict(
380 SeededNumberDictionary::cast(object->elements()));
381 uint32_t capacity = dict->Capacity();
382 for (uint32_t j = 0; j < capacity; j++) {
383 HandleScope loop_scope(isolate);
384 Handle<Object> k(dict->KeyAt(j), isolate);
385 if (dict->IsKey(*k)) {
386 DCHECK(k->IsNumber());
387 uint32_t index = static_cast<uint32_t>(k->Number());
388 if (index < range) {
389 indices->Add(index);
390 }
391 }
392 }
393 break;
394 }
395#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
396 case TYPE##_ELEMENTS: \
397 case EXTERNAL_##TYPE##_ELEMENTS:
398
399 TYPED_ARRAYS(TYPED_ARRAY_CASE)
400#undef TYPED_ARRAY_CASE
401 {
402 uint32_t length = static_cast<uint32_t>(
403 FixedArrayBase::cast(object->elements())->length());
404 if (range <= length) {
405 length = range;
406 // We will add all indices, so we might as well clear it first
407 // and avoid duplicates.
408 indices->Clear();
409 }
410 for (uint32_t i = 0; i < length; i++) {
411 indices->Add(i);
412 }
413 if (length == range) return; // All indices accounted for already.
414 break;
415 }
416 case SLOPPY_ARGUMENTS_ELEMENTS: {
417 MaybeHandle<Object> length_obj =
418 Object::GetProperty(object, isolate->factory()->length_string());
419 double length_num = length_obj.ToHandleChecked()->Number();
420 uint32_t length = static_cast<uint32_t>(DoubleToInt32(length_num));
421 ElementsAccessor* accessor = object->GetElementsAccessor();
422 for (uint32_t i = 0; i < length; i++) {
423 if (accessor->HasElement(object, object, i)) {
424 indices->Add(i);
425 }
426 }
427 break;
428 }
429 }
430
431 PrototypeIterator iter(isolate, object);
432 if (!iter.IsAtEnd()) {
433 // The prototype will usually have no inherited element indices,
434 // but we have to check.
435 CollectElementIndices(
436 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), range,
437 indices);
438 }
439}
440
441
442static bool IterateElementsSlow(Isolate* isolate, Handle<JSObject> receiver,
443 uint32_t length, ArrayConcatVisitor* visitor) {
444 for (uint32_t i = 0; i < length; ++i) {
445 HandleScope loop_scope(isolate);
446 Maybe<bool> maybe = JSReceiver::HasElement(receiver, i);
447 if (!maybe.has_value) return false;
448 if (maybe.value) {
449 Handle<Object> element_value;
450 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
451 isolate, element_value,
452 Runtime::GetElementOrCharAt(isolate, receiver, i), false);
453 visitor->visit(i, element_value);
454 }
455 }
456 visitor->increase_index_offset(length);
457 return true;
458}
459
460
461/**
462 * A helper function that visits elements of a JSObject in numerical
463 * order.
464 *
465 * The visitor argument called for each existing element in the array
466 * with the element index and the element's value.
467 * Afterwards it increments the base-index of the visitor by the array
468 * length.
469 * Returns false if any access threw an exception, otherwise true.
470 */
471static bool IterateElements(Isolate* isolate, Handle<JSObject> receiver,
472 ArrayConcatVisitor* visitor) {
473 uint32_t length = 0;
474
475 if (receiver->IsJSArray()) {
476 Handle<JSArray> array(Handle<JSArray>::cast(receiver));
477 length = static_cast<uint32_t>(array->length()->Number());
478 } else {
479 Handle<Object> val;
480 Handle<Object> key(isolate->heap()->length_string(), isolate);
481 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val,
482 Runtime::GetObjectProperty(isolate, receiver, key), false);
483 // TODO(caitp): Support larger element indexes (up to 2^53-1).
484 if (!val->ToUint32(&length)) {
485 ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, val,
486 Execution::ToLength(isolate, val), false);
487 val->ToUint32(&length);
488 }
489 }
490
491 if (!(receiver->IsJSArray() || receiver->IsJSTypedArray())) {
492 // For classes which are not known to be safe to access via elements alone,
493 // use the slow case.
494 return IterateElementsSlow(isolate, receiver, length, visitor);
495 }
496
497 switch (receiver->GetElementsKind()) {
498 case FAST_SMI_ELEMENTS:
499 case FAST_ELEMENTS:
500 case FAST_HOLEY_SMI_ELEMENTS:
501 case FAST_HOLEY_ELEMENTS: {
502 // Run through the elements FixedArray and use HasElement and GetElement
503 // to check the prototype for missing elements.
504 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
505 int fast_length = static_cast<int>(length);
506 DCHECK(fast_length <= elements->length());
507 for (int j = 0; j < fast_length; j++) {
508 HandleScope loop_scope(isolate);
509 Handle<Object> element_value(elements->get(j), isolate);
510 if (!element_value->IsTheHole()) {
511 visitor->visit(j, element_value);
512 } else {
513 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j);
514 if (!maybe.has_value) return false;
515 if (maybe.value) {
516 // Call GetElement on receiver, not its prototype, or getters won't
517 // have the correct receiver.
518 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
519 isolate, element_value,
520 Object::GetElement(isolate, receiver, j), false);
521 visitor->visit(j, element_value);
522 }
523 }
524 }
525 break;
526 }
527 case FAST_HOLEY_DOUBLE_ELEMENTS:
528 case FAST_DOUBLE_ELEMENTS: {
529 // Empty array is FixedArray but not FixedDoubleArray.
530 if (length == 0) break;
531 // Run through the elements FixedArray and use HasElement and GetElement
532 // to check the prototype for missing elements.
533 if (receiver->elements()->IsFixedArray()) {
534 DCHECK(receiver->elements()->length() == 0);
535 break;
536 }
537 Handle<FixedDoubleArray> elements(
538 FixedDoubleArray::cast(receiver->elements()));
539 int fast_length = static_cast<int>(length);
540 DCHECK(fast_length <= elements->length());
541 for (int j = 0; j < fast_length; j++) {
542 HandleScope loop_scope(isolate);
543 if (!elements->is_the_hole(j)) {
544 double double_value = elements->get_scalar(j);
545 Handle<Object> element_value =
546 isolate->factory()->NewNumber(double_value);
547 visitor->visit(j, element_value);
548 } else {
549 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j);
550 if (!maybe.has_value) return false;
551 if (maybe.value) {
552 // Call GetElement on receiver, not its prototype, or getters won't
553 // have the correct receiver.
554 Handle<Object> element_value;
555 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
556 isolate, element_value,
557 Object::GetElement(isolate, receiver, j), false);
558 visitor->visit(j, element_value);
559 }
560 }
561 }
562 break;
563 }
564 case DICTIONARY_ELEMENTS: {
565 Handle<SeededNumberDictionary> dict(receiver->element_dictionary());
566 List<uint32_t> indices(dict->Capacity() / 2);
567 // Collect all indices in the object and the prototypes less
568 // than length. This might introduce duplicates in the indices list.
569 CollectElementIndices(receiver, length, &indices);
570 indices.Sort(&compareUInt32);
571 int j = 0;
572 int n = indices.length();
573 while (j < n) {
574 HandleScope loop_scope(isolate);
575 uint32_t index = indices[j];
576 Handle<Object> element;
577 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
578 isolate, element, Object::GetElement(isolate, receiver, index),
579 false);
580 visitor->visit(index, element);
581 // Skip to next different index (i.e., omit duplicates).
582 do {
583 j++;
584 } while (j < n && indices[j] == index);
585 }
586 break;
587 }
588 case EXTERNAL_UINT8_CLAMPED_ELEMENTS: {
589 Handle<ExternalUint8ClampedArray> pixels(
590 ExternalUint8ClampedArray::cast(receiver->elements()));
591 for (uint32_t j = 0; j < length; j++) {
592 Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate);
593 visitor->visit(j, e);
594 }
595 break;
596 }
597 case UINT8_CLAMPED_ELEMENTS: {
598 Handle<FixedUint8ClampedArray> pixels(
599 FixedUint8ClampedArray::cast(receiver->elements()));
600 for (uint32_t j = 0; j < length; j++) {
601 Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate);
602 visitor->visit(j, e);
603 }
604 break;
605 }
606 case EXTERNAL_INT8_ELEMENTS: {
607 IterateTypedArrayElements<ExternalInt8Array, int8_t>(
608 isolate, receiver, true, true, visitor);
609 break;
610 }
611 case INT8_ELEMENTS: {
612 IterateTypedArrayElements<FixedInt8Array, int8_t>(
613 isolate, receiver, true, true, visitor);
614 break;
615 }
616 case EXTERNAL_UINT8_ELEMENTS: {
617 IterateTypedArrayElements<ExternalUint8Array, uint8_t>(
618 isolate, receiver, true, true, visitor);
619 break;
620 }
621 case UINT8_ELEMENTS: {
622 IterateTypedArrayElements<FixedUint8Array, uint8_t>(
623 isolate, receiver, true, true, visitor);
624 break;
625 }
626 case EXTERNAL_INT16_ELEMENTS: {
627 IterateTypedArrayElements<ExternalInt16Array, int16_t>(
628 isolate, receiver, true, true, visitor);
629 break;
630 }
631 case INT16_ELEMENTS: {
632 IterateTypedArrayElements<FixedInt16Array, int16_t>(
633 isolate, receiver, true, true, visitor);
634 break;
635 }
636 case EXTERNAL_UINT16_ELEMENTS: {
637 IterateTypedArrayElements<ExternalUint16Array, uint16_t>(
638 isolate, receiver, true, true, visitor);
639 break;
640 }
641 case UINT16_ELEMENTS: {
642 IterateTypedArrayElements<FixedUint16Array, uint16_t>(
643 isolate, receiver, true, true, visitor);
644 break;
645 }
646 case EXTERNAL_INT32_ELEMENTS: {
647 IterateTypedArrayElements<ExternalInt32Array, int32_t>(
648 isolate, receiver, true, false, visitor);
649 break;
650 }
651 case INT32_ELEMENTS: {
652 IterateTypedArrayElements<FixedInt32Array, int32_t>(
653 isolate, receiver, true, false, visitor);
654 break;
655 }
656 case EXTERNAL_UINT32_ELEMENTS: {
657 IterateTypedArrayElements<ExternalUint32Array, uint32_t>(
658 isolate, receiver, true, false, visitor);
659 break;
660 }
661 case UINT32_ELEMENTS: {
662 IterateTypedArrayElements<FixedUint32Array, uint32_t>(
663 isolate, receiver, true, false, visitor);
664 break;
665 }
666 case EXTERNAL_FLOAT32_ELEMENTS: {
667 IterateTypedArrayElements<ExternalFloat32Array, float>(
668 isolate, receiver, false, false, visitor);
669 break;
670 }
671 case FLOAT32_ELEMENTS: {
672 IterateTypedArrayElements<FixedFloat32Array, float>(
673 isolate, receiver, false, false, visitor);
674 break;
675 }
676 case EXTERNAL_FLOAT64_ELEMENTS: {
677 IterateTypedArrayElements<ExternalFloat64Array, double>(
678 isolate, receiver, false, false, visitor);
679 break;
680 }
681 case FLOAT64_ELEMENTS: {
682 IterateTypedArrayElements<FixedFloat64Array, double>(
683 isolate, receiver, false, false, visitor);
684 break;
685 }
686 case SLOPPY_ARGUMENTS_ELEMENTS: {
687 ElementsAccessor* accessor = receiver->GetElementsAccessor();
688 for (uint32_t index = 0; index < length; index++) {
689 HandleScope loop_scope(isolate);
690 if (accessor->HasElement(receiver, receiver, index)) {
691 Handle<Object> element;
692 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
693 isolate, element, accessor->Get(receiver, receiver, index),
694 false);
695 visitor->visit(index, element);
696 }
697 }
698 break;
699 }
700 }
701 visitor->increase_index_offset(length);
702 return true;
703}
704
705
706static bool IsConcatSpreadable(Isolate* isolate, Handle<Object> obj) {
707 HandleScope handle_scope(isolate);
708 if (!obj->IsSpecObject()) return false;
709 if (obj->IsJSArray()) return true;
710 if (FLAG_harmony_arrays) {
711 Handle<Symbol> key(isolate->factory()->is_concat_spreadable_symbol());
712 Handle<Object> value;
713 MaybeHandle<Object> maybeValue =
714 i::Runtime::GetObjectProperty(isolate, obj, key);
715 if (maybeValue.ToHandle(&value)) {
716 return value->BooleanValue();
717 }
718 }
719 return false;
720}
721
722
723/**
724 * Array::concat implementation.
725 * See ECMAScript 262, 15.4.4.4.
726 * TODO(581): Fix non-compliance for very large concatenations and update to
727 * following the ECMAScript 5 specification.
728 */
729RUNTIME_FUNCTION(Runtime_ArrayConcat) {
730 HandleScope handle_scope(isolate);
731 DCHECK(args.length() == 1);
732
733 CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0);
734 int argument_count = static_cast<int>(arguments->length()->Number());
735 RUNTIME_ASSERT(arguments->HasFastObjectElements());
736 Handle<FixedArray> elements(FixedArray::cast(arguments->elements()));
737
738 // Pass 1: estimate the length and number of elements of the result.
739 // The actual length can be larger if any of the arguments have getters
740 // that mutate other arguments (but will otherwise be precise).
741 // The number of elements is precise if there are no inherited elements.
742
743 ElementsKind kind = FAST_SMI_ELEMENTS;
744
745 uint32_t estimate_result_length = 0;
746 uint32_t estimate_nof_elements = 0;
747 for (int i = 0; i < argument_count; i++) {
748 HandleScope loop_scope(isolate);
749 Handle<Object> obj(elements->get(i), isolate);
750 uint32_t length_estimate;
751 uint32_t element_estimate;
752 if (obj->IsJSArray()) {
753 Handle<JSArray> array(Handle<JSArray>::cast(obj));
754 length_estimate = static_cast<uint32_t>(array->length()->Number());
755 if (length_estimate != 0) {
756 ElementsKind array_kind =
757 GetPackedElementsKind(array->map()->elements_kind());
758 if (IsMoreGeneralElementsKindTransition(kind, array_kind)) {
759 kind = array_kind;
760 }
761 }
762 element_estimate = EstimateElementCount(array);
763 } else {
764 if (obj->IsHeapObject()) {
765 if (obj->IsNumber()) {
766 if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) {
767 kind = FAST_DOUBLE_ELEMENTS;
768 }
769 } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) {
770 kind = FAST_ELEMENTS;
771 }
772 }
773 length_estimate = 1;
774 element_estimate = 1;
775 }
776 // Avoid overflows by capping at kMaxElementCount.
777 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) {
778 estimate_result_length = JSObject::kMaxElementCount;
779 } else {
780 estimate_result_length += length_estimate;
781 }
782 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) {
783 estimate_nof_elements = JSObject::kMaxElementCount;
784 } else {
785 estimate_nof_elements += element_estimate;
786 }
787 }
788
789 // If estimated number of elements is more than half of length, a
790 // fixed array (fast case) is more time and space-efficient than a
791 // dictionary.
792 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length;
793
794 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) {
795 Handle<FixedArrayBase> storage =
796 isolate->factory()->NewFixedDoubleArray(estimate_result_length);
797 int j = 0;
798 bool failure = false;
799 if (estimate_result_length > 0) {
800 Handle<FixedDoubleArray> double_storage =
801 Handle<FixedDoubleArray>::cast(storage);
802 for (int i = 0; i < argument_count; i++) {
803 Handle<Object> obj(elements->get(i), isolate);
804 if (obj->IsSmi()) {
805 double_storage->set(j, Smi::cast(*obj)->value());
806 j++;
807 } else if (obj->IsNumber()) {
808 double_storage->set(j, obj->Number());
809 j++;
810 } else {
811 JSArray* array = JSArray::cast(*obj);
812 uint32_t length = static_cast<uint32_t>(array->length()->Number());
813 switch (array->map()->elements_kind()) {
814 case FAST_HOLEY_DOUBLE_ELEMENTS:
815 case FAST_DOUBLE_ELEMENTS: {
816 // Empty array is FixedArray but not FixedDoubleArray.
817 if (length == 0) break;
818 FixedDoubleArray* elements =
819 FixedDoubleArray::cast(array->elements());
820 for (uint32_t i = 0; i < length; i++) {
821 if (elements->is_the_hole(i)) {
822 // TODO(jkummerow/verwaest): We could be a bit more clever
823 // here: Check if there are no elements/getters on the
824 // prototype chain, and if so, allow creation of a holey
825 // result array.
826 // Same thing below (holey smi case).
827 failure = true;
828 break;
829 }
830 double double_value = elements->get_scalar(i);
831 double_storage->set(j, double_value);
832 j++;
833 }
834 break;
835 }
836 case FAST_HOLEY_SMI_ELEMENTS:
837 case FAST_SMI_ELEMENTS: {
838 FixedArray* elements(FixedArray::cast(array->elements()));
839 for (uint32_t i = 0; i < length; i++) {
840 Object* element = elements->get(i);
841 if (element->IsTheHole()) {
842 failure = true;
843 break;
844 }
845 int32_t int_value = Smi::cast(element)->value();
846 double_storage->set(j, int_value);
847 j++;
848 }
849 break;
850 }
851 case FAST_HOLEY_ELEMENTS:
852 case FAST_ELEMENTS:
853 DCHECK_EQ(0, length);
854 break;
855 default:
856 UNREACHABLE();
857 }
858 }
859 if (failure) break;
860 }
861 }
862 if (!failure) {
863 Handle<JSArray> array = isolate->factory()->NewJSArray(0);
864 Smi* length = Smi::FromInt(j);
865 Handle<Map> map;
866 map = JSObject::GetElementsTransitionMap(array, kind);
867 array->set_map(*map);
868 array->set_length(length);
869 array->set_elements(*storage);
870 return *array;
871 }
872 // In case of failure, fall through.
873 }
874
875 Handle<FixedArray> storage;
876 if (fast_case) {
877 // The backing storage array must have non-existing elements to preserve
878 // holes across concat operations.
879 storage =
880 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length);
881 } else {
882 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
883 uint32_t at_least_space_for =
884 estimate_nof_elements + (estimate_nof_elements >> 2);
885 storage = Handle<FixedArray>::cast(
886 SeededNumberDictionary::New(isolate, at_least_space_for));
887 }
888
889 ArrayConcatVisitor visitor(isolate, storage, fast_case);
890
891 for (int i = 0; i < argument_count; i++) {
892 Handle<Object> obj(elements->get(i), isolate);
893 bool spreadable = IsConcatSpreadable(isolate, obj);
894 if (isolate->has_pending_exception()) return isolate->heap()->exception();
895 if (spreadable) {
896 Handle<JSObject> object = Handle<JSObject>::cast(obj);
897 if (!IterateElements(isolate, object, &visitor)) {
898 return isolate->heap()->exception();
899 }
900 } else {
901 visitor.visit(0, obj);
902 visitor.increase_index_offset(1);
903 }
904 }
905
906 if (visitor.exceeds_array_limit()) {
907 THROW_NEW_ERROR_RETURN_FAILURE(
908 isolate,
909 NewRangeError("invalid_array_length", HandleVector<Object>(NULL, 0)));
910 }
911 return *visitor.ToArray();
912}
913
914
915// Moves all own elements of an object, that are below a limit, to positions
916// starting at zero. All undefined values are placed after non-undefined values,
917// and are followed by non-existing element. Does not change the length
918// property.
919// Returns the number of non-undefined elements collected.
920// Returns -1 if hole removal is not supported by this method.
921RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) {
922 HandleScope scope(isolate);
923 DCHECK(args.length() == 2);
924 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
925 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
926 return *JSObject::PrepareElementsForSort(object, limit);
927}
928
929
930// Move contents of argument 0 (an array) to argument 1 (an array)
931RUNTIME_FUNCTION(Runtime_MoveArrayContents) {
932 HandleScope scope(isolate);
933 DCHECK(args.length() == 2);
934 CONVERT_ARG_HANDLE_CHECKED(JSArray, from, 0);
935 CONVERT_ARG_HANDLE_CHECKED(JSArray, to, 1);
936 JSObject::ValidateElements(from);
937 JSObject::ValidateElements(to);
938
939 Handle<FixedArrayBase> new_elements(from->elements());
940 ElementsKind from_kind = from->GetElementsKind();
941 Handle<Map> new_map = JSObject::GetElementsTransitionMap(to, from_kind);
942 JSObject::SetMapAndElements(to, new_map, new_elements);
943 to->set_length(from->length());
944
945 JSObject::ResetElements(from);
946 from->set_length(Smi::FromInt(0));
947
948 JSObject::ValidateElements(to);
949 return *to;
950}
951
952
953// How many elements does this object/array have?
954RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements) {
955 HandleScope scope(isolate);
956 DCHECK(args.length() == 1);
957 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
958 Handle<FixedArrayBase> elements(array->elements(), isolate);
959 SealHandleScope shs(isolate);
960 if (elements->IsDictionary()) {
961 int result =
962 Handle<SeededNumberDictionary>::cast(elements)->NumberOfElements();
963 return Smi::FromInt(result);
964 } else {
965 DCHECK(array->length()->IsSmi());
966 // For packed elements, we know the exact number of elements
967 int length = elements->length();
968 ElementsKind kind = array->GetElementsKind();
969 if (IsFastPackedElementsKind(kind)) {
970 return Smi::FromInt(length);
971 }
972 // For holey elements, take samples from the buffer checking for holes
973 // to generate the estimate.
974 const int kNumberOfHoleCheckSamples = 97;
975 int increment = (length < kNumberOfHoleCheckSamples)
976 ? 1
977 : static_cast<int>(length / kNumberOfHoleCheckSamples);
978 ElementsAccessor* accessor = array->GetElementsAccessor();
979 int holes = 0;
980 for (int i = 0; i < length; i += increment) {
981 if (!accessor->HasElement(array, array, i, elements)) {
982 ++holes;
983 }
984 }
985 int estimate = static_cast<int>((kNumberOfHoleCheckSamples - holes) /
986 kNumberOfHoleCheckSamples * length);
987 return Smi::FromInt(estimate);
988 }
989}
990
991
992// Returns an array that tells you where in the [0, length) interval an array
993// might have elements. Can either return an array of keys (positive integers
994// or undefined) or a number representing the positive length of an interval
995// starting at index 0.
996// Intervals can span over some keys that are not in the object.
997RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
998 HandleScope scope(isolate);
999 DCHECK(args.length() == 2);
1000 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
1001 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
1002 if (array->elements()->IsDictionary()) {
1003 Handle<FixedArray> keys = isolate->factory()->empty_fixed_array();
1004 for (PrototypeIterator iter(isolate, array,
1005 PrototypeIterator::START_AT_RECEIVER);
1006 !iter.IsAtEnd(); iter.Advance()) {
1007 if (PrototypeIterator::GetCurrent(iter)->IsJSProxy() ||
1008 JSObject::cast(*PrototypeIterator::GetCurrent(iter))
1009 ->HasIndexedInterceptor()) {
1010 // Bail out if we find a proxy or interceptor, likely not worth
1011 // collecting keys in that case.
1012 return *isolate->factory()->NewNumberFromUint(length);
1013 }
1014 Handle<JSObject> current =
1015 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
1016 Handle<FixedArray> current_keys =
1017 isolate->factory()->NewFixedArray(current->NumberOfOwnElements(NONE));
1018 current->GetOwnElementKeys(*current_keys, NONE);
1019 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
1020 isolate, keys, FixedArray::UnionOfKeys(keys, current_keys));
1021 }
1022 // Erase any keys >= length.
1023 // TODO(adamk): Remove this step when the contract of %GetArrayKeys
1024 // is changed to let this happen on the JS side.
1025 for (int i = 0; i < keys->length(); i++) {
1026 if (NumberToUint32(keys->get(i)) >= length) keys->set_undefined(i);
1027 }
1028 return *isolate->factory()->NewJSArrayWithElements(keys);
1029 } else {
1030 RUNTIME_ASSERT(array->HasFastSmiOrObjectElements() ||
1031 array->HasFastDoubleElements());
1032 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
1033 return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
1034 }
1035}
1036
1037
1038static Object* ArrayConstructorCommon(Isolate* isolate,
1039 Handle<JSFunction> constructor,
1040 Handle<AllocationSite> site,
1041 Arguments* caller_args) {
1042 Factory* factory = isolate->factory();
1043
1044 bool holey = false;
1045 bool can_use_type_feedback = true;
1046 if (caller_args->length() == 1) {
1047 Handle<Object> argument_one = caller_args->at<Object>(0);
1048 if (argument_one->IsSmi()) {
1049 int value = Handle<Smi>::cast(argument_one)->value();
1050 if (value < 0 || value >= JSObject::kInitialMaxFastElementArray) {
1051 // the array is a dictionary in this case.
1052 can_use_type_feedback = false;
1053 } else if (value != 0) {
1054 holey = true;
1055 }
1056 } else {
1057 // Non-smi length argument produces a dictionary
1058 can_use_type_feedback = false;
1059 }
1060 }
1061
1062 Handle<JSArray> array;
1063 if (!site.is_null() && can_use_type_feedback) {
1064 ElementsKind to_kind = site->GetElementsKind();
1065 if (holey && !IsFastHoleyElementsKind(to_kind)) {
1066 to_kind = GetHoleyElementsKind(to_kind);
1067 // Update the allocation site info to reflect the advice alteration.
1068 site->SetElementsKind(to_kind);
1069 }
1070
1071 // We should allocate with an initial map that reflects the allocation site
1072 // advice. Therefore we use AllocateJSObjectFromMap instead of passing
1073 // the constructor.
1074 Handle<Map> initial_map(constructor->initial_map(), isolate);
1075 if (to_kind != initial_map->elements_kind()) {
1076 initial_map = Map::AsElementsKind(initial_map, to_kind);
1077 }
1078
1079 // If we don't care to track arrays of to_kind ElementsKind, then
1080 // don't emit a memento for them.
1081 Handle<AllocationSite> allocation_site;
1082 if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) {
1083 allocation_site = site;
1084 }
1085
1086 array = Handle<JSArray>::cast(factory->NewJSObjectFromMap(
1087 initial_map, NOT_TENURED, true, allocation_site));
1088 } else {
1089 array = Handle<JSArray>::cast(factory->NewJSObject(constructor));
1090
1091 // We might need to transition to holey
1092 ElementsKind kind = constructor->initial_map()->elements_kind();
1093 if (holey && !IsFastHoleyElementsKind(kind)) {
1094 kind = GetHoleyElementsKind(kind);
1095 JSObject::TransitionElementsKind(array, kind);
1096 }
1097 }
1098
1099 factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
1100
1101 ElementsKind old_kind = array->GetElementsKind();
1102 RETURN_FAILURE_ON_EXCEPTION(
1103 isolate, ArrayConstructInitializeElements(array, caller_args));
1104 if (!site.is_null() &&
1105 (old_kind != array->GetElementsKind() || !can_use_type_feedback)) {
1106 // The arguments passed in caused a transition. This kind of complexity
1107 // can't be dealt with in the inlined hydrogen array constructor case.
1108 // We must mark the allocationsite as un-inlinable.
1109 site->SetDoNotInlineCall();
1110 }
1111 return *array;
1112}
1113
1114
1115RUNTIME_FUNCTION(Runtime_ArrayConstructor) {
1116 HandleScope scope(isolate);
1117 // If we get 2 arguments then they are the stub parameters (constructor, type
1118 // info). If we get 4, then the first one is a pointer to the arguments
1119 // passed by the caller, and the last one is the length of the arguments
1120 // passed to the caller (redundant, but useful to check on the deoptimizer
1121 // with an assert).
1122 Arguments empty_args(0, NULL);
1123 bool no_caller_args = args.length() == 2;
1124 DCHECK(no_caller_args || args.length() == 4);
1125 int parameters_start = no_caller_args ? 0 : 1;
1126 Arguments* caller_args =
1127 no_caller_args ? &empty_args : reinterpret_cast<Arguments*>(args[0]);
1128 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
1129 CONVERT_ARG_HANDLE_CHECKED(Object, type_info, parameters_start + 1);
1130#ifdef DEBUG
1131 if (!no_caller_args) {
1132 CONVERT_SMI_ARG_CHECKED(arg_count, parameters_start + 2);
1133 DCHECK(arg_count == caller_args->length());
1134 }
1135#endif
1136
1137 Handle<AllocationSite> site;
1138 if (!type_info.is_null() &&
1139 *type_info != isolate->heap()->undefined_value()) {
1140 site = Handle<AllocationSite>::cast(type_info);
1141 DCHECK(!site->SitePointsToLiteral());
1142 }
1143
1144 return ArrayConstructorCommon(isolate, constructor, site, caller_args);
1145}
1146
1147
1148RUNTIME_FUNCTION(Runtime_InternalArrayConstructor) {
1149 HandleScope scope(isolate);
1150 Arguments empty_args(0, NULL);
1151 bool no_caller_args = args.length() == 1;
1152 DCHECK(no_caller_args || args.length() == 3);
1153 int parameters_start = no_caller_args ? 0 : 1;
1154 Arguments* caller_args =
1155 no_caller_args ? &empty_args : reinterpret_cast<Arguments*>(args[0]);
1156 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
1157#ifdef DEBUG
1158 if (!no_caller_args) {
1159 CONVERT_SMI_ARG_CHECKED(arg_count, parameters_start + 1);
1160 DCHECK(arg_count == caller_args->length());
1161 }
1162#endif
1163 return ArrayConstructorCommon(isolate, constructor,
1164 Handle<AllocationSite>::null(), caller_args);
1165}
1166
1167
1168RUNTIME_FUNCTION(Runtime_NormalizeElements) {
1169 HandleScope scope(isolate);
1170 DCHECK(args.length() == 1);
1171 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
1172 RUNTIME_ASSERT(!array->HasExternalArrayElements() &&
1173 !array->HasFixedTypedArrayElements());
1174 JSObject::NormalizeElements(array);
1175 return *array;
1176}
1177
1178
1179RUNTIME_FUNCTION(Runtime_HasComplexElements) {
1180 HandleScope scope(isolate);
1181 DCHECK(args.length() == 1);
1182 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
1183 for (PrototypeIterator iter(isolate, array,
1184 PrototypeIterator::START_AT_RECEIVER);
1185 !iter.IsAtEnd(); iter.Advance()) {
1186 if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
1187 return isolate->heap()->true_value();
1188 }
1189 Handle<JSObject> current =
1190 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
1191 if (current->HasIndexedInterceptor()) {
1192 return isolate->heap()->true_value();
1193 }
1194 if (!current->HasDictionaryElements()) continue;
1195 if (current->element_dictionary()->HasComplexElements()) {
1196 return isolate->heap()->true_value();
1197 }
1198 }
1199 return isolate->heap()->false_value();
1200}
1201
1202
1203// TODO(dcarney): remove this function when TurboFan supports it.
1204// Takes the object to be iterated over and the result of GetPropertyNamesFast
1205// Returns pair (cache_array, cache_type).
1206RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ForInInit) {
1207 SealHandleScope scope(isolate);
1208 DCHECK(args.length() == 2);
1209 // This simulates CONVERT_ARG_HANDLE_CHECKED for calls returning pairs.
1210 // Not worth creating a macro atm as this function should be removed.
1211 if (!args[0]->IsJSReceiver() || !args[1]->IsObject()) {
1212 Object* error = isolate->ThrowIllegalOperation();
1213 return MakePair(error, isolate->heap()->undefined_value());
1214 }
1215 Handle<JSReceiver> object = args.at<JSReceiver>(0);
1216 Handle<Object> cache_type = args.at<Object>(1);
1217 if (cache_type->IsMap()) {
1218 // Enum cache case.
1219 if (Map::EnumLengthBits::decode(Map::cast(*cache_type)->bit_field3()) ==
1220 0) {
1221 // 0 length enum.
1222 // Can't handle this case in the graph builder,
1223 // so transform it into the empty fixed array case.
1224 return MakePair(isolate->heap()->empty_fixed_array(), Smi::FromInt(1));
1225 }
1226 return MakePair(object->map()->instance_descriptors()->GetEnumCache(),
1227 *cache_type);
1228 } else {
1229 // FixedArray case.
1230 Smi* new_cache_type = Smi::FromInt(object->IsJSProxy() ? 0 : 1);
1231 return MakePair(*Handle<FixedArray>::cast(cache_type), new_cache_type);
1232 }
1233}
1234
1235
1236// TODO(dcarney): remove this function when TurboFan supports it.
1237RUNTIME_FUNCTION(Runtime_ForInCacheArrayLength) {
1238 SealHandleScope shs(isolate);
1239 DCHECK(args.length() == 2);
1240 CONVERT_ARG_HANDLE_CHECKED(Object, cache_type, 0);
1241 CONVERT_ARG_HANDLE_CHECKED(FixedArray, array, 1);
1242 int length = 0;
1243 if (cache_type->IsMap()) {
1244 length = Map::cast(*cache_type)->EnumLength();
1245 } else {
1246 DCHECK(cache_type->IsSmi());
1247 length = array->length();
1248 }
1249 return Smi::FromInt(length);
1250}
1251
1252
1253// TODO(dcarney): remove this function when TurboFan supports it.
1254// Takes (the object to be iterated over,
1255// cache_array from ForInInit,
1256// cache_type from ForInInit,
1257// the current index)
1258// Returns pair (array[index], needs_filtering).
1259RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ForInNext) {
1260 SealHandleScope scope(isolate);
1261 DCHECK(args.length() == 4);
1262 int32_t index;
1263 // This simulates CONVERT_ARG_HANDLE_CHECKED for calls returning pairs.
1264 // Not worth creating a macro atm as this function should be removed.
1265 if (!args[0]->IsJSReceiver() || !args[1]->IsFixedArray() ||
1266 !args[2]->IsObject() || !args[3]->ToInt32(&index)) {
1267 Object* error = isolate->ThrowIllegalOperation();
1268 return MakePair(error, isolate->heap()->undefined_value());
1269 }
1270 Handle<JSReceiver> object = args.at<JSReceiver>(0);
1271 Handle<FixedArray> array = args.at<FixedArray>(1);
1272 Handle<Object> cache_type = args.at<Object>(2);
1273 // Figure out first if a slow check is needed for this object.
1274 bool slow_check_needed = false;
1275 if (cache_type->IsMap()) {
1276 if (object->map() != Map::cast(*cache_type)) {
1277 // Object transitioned. Need slow check.
1278 slow_check_needed = true;
1279 }
1280 } else {
1281 // No slow check needed for proxies.
1282 slow_check_needed = Smi::cast(*cache_type)->value() == 1;
1283 }
1284 return MakePair(array->get(index),
1285 isolate->heap()->ToBoolean(slow_check_needed));
1286}
1287
1288
1289RUNTIME_FUNCTION(RuntimeReference_IsArray) {
1290 SealHandleScope shs(isolate);
1291 DCHECK(args.length() == 1);
1292 CONVERT_ARG_CHECKED(Object, obj, 0);
1293 return isolate->heap()->ToBoolean(obj->IsJSArray());
1294}
1295
1296
1297RUNTIME_FUNCTION(RuntimeReference_HasCachedArrayIndex) {
1298 SealHandleScope shs(isolate);
1299 DCHECK(args.length() == 1);
1300 return isolate->heap()->false_value();
1301}
1302
1303
1304RUNTIME_FUNCTION(RuntimeReference_GetCachedArrayIndex) {
1305 SealHandleScope shs(isolate);
1306 DCHECK(args.length() == 1);
1307 return isolate->heap()->undefined_value();
1308}
1309
1310
1311RUNTIME_FUNCTION(RuntimeReference_FastOneByteArrayJoin) {
1312 SealHandleScope shs(isolate);
1313 DCHECK(args.length() == 2);
1314 return isolate->heap()->undefined_value();
1315}
1316}
1317} // namespace v8::internal