OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "src/v8.h" | 5 #include "src/v8.h" |
6 | 6 |
7 #include "src/api.h" | 7 #include "src/api.h" |
8 #include "src/api-natives.h" | 8 #include "src/api-natives.h" |
9 #include "src/arguments.h" | 9 #include "src/arguments.h" |
10 #include "src/base/once.h" | 10 #include "src/base/once.h" |
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 UNREACHABLE(); | 169 UNREACHABLE(); |
170 return isolate->heap()->undefined_value(); // Make compiler happy. | 170 return isolate->heap()->undefined_value(); // Make compiler happy. |
171 } | 171 } |
172 | 172 |
173 | 173 |
174 BUILTIN(EmptyFunction) { | 174 BUILTIN(EmptyFunction) { |
175 return isolate->heap()->undefined_value(); | 175 return isolate->heap()->undefined_value(); |
176 } | 176 } |
177 | 177 |
178 | 178 |
179 // TODO(cbruni): check if this is a suitable method on Object | |
180 bool ClampedToInteger(Object* object, int* out) { | |
181 // This is an extended version of ECMA-262 9.4, but additionally | |
182 // clamps values to [kMinInt, kMaxInt] | |
183 if (object->IsSmi()) { | |
184 *out = Smi::cast(object)->value(); | |
185 return true; | |
186 } else if (object->IsHeapNumber()) { | |
187 *out = FastD2IChecked(HeapNumber::cast(object)->value()); | |
188 return true; | |
189 } else if (object->IsUndefined()) { | |
190 *out = 0; | |
191 return true; | |
192 } else if (object->IsBoolean()) { | |
193 *out = (Oddball::cast(object)->kind() == Oddball::kTrue) ? 1 : 0; | |
194 return true; | |
195 } | |
196 return false; | |
197 } | |
198 | |
199 | |
200 static void MoveDoubleElements(FixedDoubleArray* dst, int dst_index, | 179 static void MoveDoubleElements(FixedDoubleArray* dst, int dst_index, |
201 FixedDoubleArray* src, int src_index, int len) { | 180 FixedDoubleArray* src, int src_index, int len) { |
202 if (len == 0) return; | 181 if (len == 0) return; |
203 MemMove(dst->data_start() + dst_index, src->data_start() + src_index, | 182 MemMove(dst->data_start() + dst_index, src->data_start() + src_index, |
204 len * kDoubleSize); | 183 len * kDoubleSize); |
205 } | 184 } |
206 | 185 |
207 | 186 |
208 static bool ArrayPrototypeHasNoElements(PrototypeIterator* iter) { | 187 static bool ArrayPrototypeHasNoElements(PrototypeIterator* iter) { |
209 DisallowHeapAllocation no_gc; | 188 DisallowHeapAllocation no_gc; |
(...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
635 | 614 |
636 ElementsAccessor* accessor = object->GetElementsAccessor(); | 615 ElementsAccessor* accessor = object->GetElementsAccessor(); |
637 accessor->CopyElements( | 616 accessor->CopyElements( |
638 elms, k, kind, handle(result_array->elements(), isolate), 0, result_len); | 617 elms, k, kind, handle(result_array->elements(), isolate), 0, result_len); |
639 return *result_array; | 618 return *result_array; |
640 } | 619 } |
641 | 620 |
642 | 621 |
643 BUILTIN(ArraySplice) { | 622 BUILTIN(ArraySplice) { |
644 HandleScope scope(isolate); | 623 HandleScope scope(isolate); |
| 624 Heap* heap = isolate->heap(); |
645 Handle<Object> receiver = args.receiver(); | 625 Handle<Object> receiver = args.receiver(); |
646 MaybeHandle<FixedArrayBase> maybe_elms_obj = | 626 MaybeHandle<FixedArrayBase> maybe_elms_obj = |
647 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3); | 627 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3); |
648 Handle<FixedArrayBase> elms_obj; | 628 Handle<FixedArrayBase> elms_obj; |
649 if (!maybe_elms_obj.ToHandle(&elms_obj)) { | 629 if (!maybe_elms_obj.ToHandle(&elms_obj)) { |
650 return CallJsBuiltin(isolate, "$arraySplice", args); | 630 return CallJsBuiltin(isolate, "$arraySplice", args); |
651 } | 631 } |
652 Handle<JSArray> array = Handle<JSArray>::cast(receiver); | 632 Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
653 DCHECK(!array->map()->is_observed()); | 633 DCHECK(!array->map()->is_observed()); |
654 | 634 |
655 int argument_count = args.length() - 1; | 635 int len = Smi::cast(array->length())->value(); |
| 636 |
| 637 int n_arguments = args.length() - 1; |
| 638 |
656 int relative_start = 0; | 639 int relative_start = 0; |
657 if (argument_count > 0) { | 640 if (n_arguments > 0) { |
658 DisallowHeapAllocation no_gc; | 641 DisallowHeapAllocation no_gc; |
659 if (!ClampedToInteger(args[1], &relative_start)) { | 642 Object* arg1 = args[1]; |
| 643 if (arg1->IsSmi()) { |
| 644 relative_start = Smi::cast(arg1)->value(); |
| 645 } else if (arg1->IsHeapNumber()) { |
| 646 double start = HeapNumber::cast(arg1)->value(); |
| 647 if (start < kMinInt || start > kMaxInt) { |
| 648 AllowHeapAllocation allow_allocation; |
| 649 return CallJsBuiltin(isolate, "$arraySplice", args); |
| 650 } |
| 651 relative_start = std::isnan(start) ? 0 : static_cast<int>(start); |
| 652 } else if (!arg1->IsUndefined()) { |
660 AllowHeapAllocation allow_allocation; | 653 AllowHeapAllocation allow_allocation; |
661 return CallJsBuiltin(isolate, "$arraySplice", args); | 654 return CallJsBuiltin(isolate, "$arraySplice", args); |
662 } | 655 } |
663 } | 656 } |
664 int len = Smi::cast(array->length())->value(); | |
665 // clip relative start to [0, len] | |
666 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0) | 657 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0) |
667 : Min(relative_start, len); | 658 : Min(relative_start, len); |
668 | 659 |
| 660 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is |
| 661 // given as a request to delete all the elements from the start. |
| 662 // And it differs from the case of undefined delete count. |
| 663 // This does not follow ECMA-262, but we do the same for |
| 664 // compatibility. |
669 int actual_delete_count; | 665 int actual_delete_count; |
670 if (argument_count == 1) { | 666 if (n_arguments == 1) { |
671 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is | |
672 // given as a request to delete all the elements from the start. | |
673 // And it differs from the case of undefined delete count. | |
674 // This does not follow ECMA-262, but we do the same for compatibility. | |
675 DCHECK(len - actual_start >= 0); | 667 DCHECK(len - actual_start >= 0); |
676 actual_delete_count = len - actual_start; | 668 actual_delete_count = len - actual_start; |
677 } else { | 669 } else { |
678 int delete_count = 0; | 670 int value = 0; // ToInteger(undefined) == 0 |
679 DisallowHeapAllocation no_gc; | 671 if (n_arguments > 1) { |
680 if (argument_count > 1) { | 672 DisallowHeapAllocation no_gc; |
681 if (!ClampedToInteger(args[2], &delete_count)) { | 673 Object* arg2 = args[2]; |
| 674 if (arg2->IsSmi()) { |
| 675 value = Smi::cast(arg2)->value(); |
| 676 } else { |
682 AllowHeapAllocation allow_allocation; | 677 AllowHeapAllocation allow_allocation; |
683 return CallJsBuiltin(isolate, "$arraySplice", args); | 678 return CallJsBuiltin(isolate, "$arraySplice", args); |
684 } | 679 } |
685 } | 680 } |
686 actual_delete_count = Min(Max(delete_count, 0), len - actual_start); | 681 actual_delete_count = Min(Max(value, 0), len - actual_start); |
687 } | 682 } |
688 | 683 |
689 int add_count = (argument_count > 1) ? (argument_count - 2) : 0; | 684 ElementsKind elements_kind = array->GetElementsKind(); |
690 int new_length = len - actual_delete_count + add_count; | 685 |
| 686 int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0; |
| 687 int new_length = len - actual_delete_count + item_count; |
| 688 |
| 689 // For double mode we do not support changing the length. |
| 690 if (new_length > len && IsFastDoubleElementsKind(elements_kind)) { |
| 691 return CallJsBuiltin(isolate, "$arraySplice", args); |
| 692 } |
691 | 693 |
692 if (new_length != len && JSArray::HasReadOnlyLength(array)) { | 694 if (new_length != len && JSArray::HasReadOnlyLength(array)) { |
693 AllowHeapAllocation allow_allocation; | 695 AllowHeapAllocation allow_allocation; |
694 return CallJsBuiltin(isolate, "$arraySplice", args); | 696 return CallJsBuiltin(isolate, "$arraySplice", args); |
695 } | 697 } |
696 ElementsAccessor* accessor = array->GetElementsAccessor(); | 698 |
697 Handle<JSArray> result = accessor->Splice( | 699 if (new_length == 0) { |
698 array, elms_obj, actual_start, actual_delete_count, args, add_count); | 700 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements( |
699 return *result; | 701 elms_obj, elements_kind, actual_delete_count); |
| 702 array->set_elements(heap->empty_fixed_array()); |
| 703 array->set_length(Smi::FromInt(0)); |
| 704 return *result; |
| 705 } |
| 706 |
| 707 Handle<JSArray> result_array = |
| 708 isolate->factory()->NewJSArray(elements_kind, |
| 709 actual_delete_count, |
| 710 actual_delete_count); |
| 711 |
| 712 if (actual_delete_count > 0) { |
| 713 DisallowHeapAllocation no_gc; |
| 714 ElementsAccessor* accessor = array->GetElementsAccessor(); |
| 715 accessor->CopyElements( |
| 716 elms_obj, actual_start, elements_kind, |
| 717 handle(result_array->elements(), isolate), 0, actual_delete_count); |
| 718 } |
| 719 |
| 720 bool elms_changed = false; |
| 721 if (item_count < actual_delete_count) { |
| 722 // Shrink the array. |
| 723 const bool trim_array = !heap->lo_space()->Contains(*elms_obj) && |
| 724 ((actual_start + item_count) < |
| 725 (len - actual_delete_count - actual_start)); |
| 726 if (trim_array) { |
| 727 const int delta = actual_delete_count - item_count; |
| 728 |
| 729 if (elms_obj->IsFixedDoubleArray()) { |
| 730 Handle<FixedDoubleArray> elms = |
| 731 Handle<FixedDoubleArray>::cast(elms_obj); |
| 732 MoveDoubleElements(*elms, delta, *elms, 0, actual_start); |
| 733 } else { |
| 734 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); |
| 735 DisallowHeapAllocation no_gc; |
| 736 heap->MoveElements(*elms, delta, 0, actual_start); |
| 737 } |
| 738 |
| 739 if (heap->CanMoveObjectStart(*elms_obj)) { |
| 740 // On the fast path we move the start of the object in memory. |
| 741 elms_obj = handle(heap->LeftTrimFixedArray(*elms_obj, delta)); |
| 742 } else { |
| 743 // This is the slow path. We are going to move the elements to the left |
| 744 // by copying them. For trimmed values we store the hole. |
| 745 if (elms_obj->IsFixedDoubleArray()) { |
| 746 Handle<FixedDoubleArray> elms = |
| 747 Handle<FixedDoubleArray>::cast(elms_obj); |
| 748 MoveDoubleElements(*elms, 0, *elms, delta, len - delta); |
| 749 elms->FillWithHoles(len - delta, len); |
| 750 } else { |
| 751 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); |
| 752 DisallowHeapAllocation no_gc; |
| 753 heap->MoveElements(*elms, 0, delta, len - delta); |
| 754 elms->FillWithHoles(len - delta, len); |
| 755 } |
| 756 } |
| 757 elms_changed = true; |
| 758 } else { |
| 759 if (elms_obj->IsFixedDoubleArray()) { |
| 760 Handle<FixedDoubleArray> elms = |
| 761 Handle<FixedDoubleArray>::cast(elms_obj); |
| 762 MoveDoubleElements(*elms, actual_start + item_count, |
| 763 *elms, actual_start + actual_delete_count, |
| 764 (len - actual_delete_count - actual_start)); |
| 765 elms->FillWithHoles(new_length, len); |
| 766 } else { |
| 767 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); |
| 768 DisallowHeapAllocation no_gc; |
| 769 heap->MoveElements(*elms, actual_start + item_count, |
| 770 actual_start + actual_delete_count, |
| 771 (len - actual_delete_count - actual_start)); |
| 772 elms->FillWithHoles(new_length, len); |
| 773 } |
| 774 } |
| 775 } else if (item_count > actual_delete_count) { |
| 776 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); |
| 777 // Currently fixed arrays cannot grow too big, so |
| 778 // we should never hit this case. |
| 779 DCHECK((item_count - actual_delete_count) <= (Smi::kMaxValue - len)); |
| 780 |
| 781 // Check if array need to grow. |
| 782 if (new_length > elms->length()) { |
| 783 // New backing storage is needed. |
| 784 int capacity = new_length + (new_length >> 1) + 16; |
| 785 Handle<FixedArray> new_elms = |
| 786 isolate->factory()->NewUninitializedFixedArray(capacity); |
| 787 |
| 788 DisallowHeapAllocation no_gc; |
| 789 |
| 790 ElementsKind kind = array->GetElementsKind(); |
| 791 ElementsAccessor* accessor = array->GetElementsAccessor(); |
| 792 if (actual_start > 0) { |
| 793 // Copy the part before actual_start as is. |
| 794 accessor->CopyElements( |
| 795 elms, 0, kind, new_elms, 0, actual_start); |
| 796 } |
| 797 accessor->CopyElements( |
| 798 elms, actual_start + actual_delete_count, kind, |
| 799 new_elms, actual_start + item_count, |
| 800 ElementsAccessor::kCopyToEndAndInitializeToHole); |
| 801 |
| 802 elms_obj = new_elms; |
| 803 elms_changed = true; |
| 804 } else { |
| 805 DisallowHeapAllocation no_gc; |
| 806 heap->MoveElements(*elms, actual_start + item_count, |
| 807 actual_start + actual_delete_count, |
| 808 (len - actual_delete_count - actual_start)); |
| 809 } |
| 810 } |
| 811 |
| 812 if (IsFastDoubleElementsKind(elements_kind)) { |
| 813 Handle<FixedDoubleArray> elms = Handle<FixedDoubleArray>::cast(elms_obj); |
| 814 for (int k = actual_start; k < actual_start + item_count; k++) { |
| 815 Object* arg = args[3 + k - actual_start]; |
| 816 if (arg->IsSmi()) { |
| 817 elms->set(k, Smi::cast(arg)->value()); |
| 818 } else { |
| 819 elms->set(k, HeapNumber::cast(arg)->value()); |
| 820 } |
| 821 } |
| 822 } else { |
| 823 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); |
| 824 DisallowHeapAllocation no_gc; |
| 825 WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); |
| 826 for (int k = actual_start; k < actual_start + item_count; k++) { |
| 827 elms->set(k, args[3 + k - actual_start], mode); |
| 828 } |
| 829 } |
| 830 |
| 831 if (elms_changed) { |
| 832 array->set_elements(*elms_obj); |
| 833 } |
| 834 // Set the length. |
| 835 array->set_length(Smi::FromInt(new_length)); |
| 836 |
| 837 return *result_array; |
700 } | 838 } |
701 | 839 |
702 | 840 |
703 BUILTIN(ArrayConcat) { | 841 BUILTIN(ArrayConcat) { |
704 HandleScope scope(isolate); | 842 HandleScope scope(isolate); |
705 | 843 |
706 int n_arguments = args.length(); | 844 int n_arguments = args.length(); |
707 int result_len = 0; | 845 int result_len = 0; |
708 ElementsKind elements_kind = GetInitialFastElementsKind(); | 846 ElementsKind elements_kind = GetInitialFastElementsKind(); |
709 bool has_double = false; | 847 bool has_double = false; |
(...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1396 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) | 1534 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) |
1397 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) | 1535 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) |
1398 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) | 1536 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) |
1399 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) | 1537 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) |
1400 #undef DEFINE_BUILTIN_ACCESSOR_C | 1538 #undef DEFINE_BUILTIN_ACCESSOR_C |
1401 #undef DEFINE_BUILTIN_ACCESSOR_A | 1539 #undef DEFINE_BUILTIN_ACCESSOR_A |
1402 | 1540 |
1403 | 1541 |
1404 } // namespace internal | 1542 } // namespace internal |
1405 } // namespace v8 | 1543 } // namespace v8 |
OLD | NEW |