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) { | |
mvstanton
2015/08/20 10:45:33
I like this method much better, thanks for address
| |
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 | |
179 static void MoveDoubleElements(FixedDoubleArray* dst, int dst_index, | 200 static void MoveDoubleElements(FixedDoubleArray* dst, int dst_index, |
180 FixedDoubleArray* src, int src_index, int len) { | 201 FixedDoubleArray* src, int src_index, int len) { |
181 if (len == 0) return; | 202 if (len == 0) return; |
182 MemMove(dst->data_start() + dst_index, src->data_start() + src_index, | 203 MemMove(dst->data_start() + dst_index, src->data_start() + src_index, |
183 len * kDoubleSize); | 204 len * kDoubleSize); |
184 } | 205 } |
185 | 206 |
186 | 207 |
187 static bool ArrayPrototypeHasNoElements(PrototypeIterator* iter) { | 208 static bool ArrayPrototypeHasNoElements(PrototypeIterator* iter) { |
188 DisallowHeapAllocation no_gc; | 209 DisallowHeapAllocation no_gc; |
(...skipping 425 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
614 | 635 |
615 ElementsAccessor* accessor = object->GetElementsAccessor(); | 636 ElementsAccessor* accessor = object->GetElementsAccessor(); |
616 accessor->CopyElements( | 637 accessor->CopyElements( |
617 elms, k, kind, handle(result_array->elements(), isolate), 0, result_len); | 638 elms, k, kind, handle(result_array->elements(), isolate), 0, result_len); |
618 return *result_array; | 639 return *result_array; |
619 } | 640 } |
620 | 641 |
621 | 642 |
622 BUILTIN(ArraySplice) { | 643 BUILTIN(ArraySplice) { |
623 HandleScope scope(isolate); | 644 HandleScope scope(isolate); |
624 Heap* heap = isolate->heap(); | |
625 Handle<Object> receiver = args.receiver(); | 645 Handle<Object> receiver = args.receiver(); |
626 MaybeHandle<FixedArrayBase> maybe_elms_obj = | 646 MaybeHandle<FixedArrayBase> maybe_elms_obj = |
627 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3); | 647 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3); |
628 Handle<FixedArrayBase> elms_obj; | 648 Handle<FixedArrayBase> elms_obj; |
629 if (!maybe_elms_obj.ToHandle(&elms_obj)) { | 649 if (!maybe_elms_obj.ToHandle(&elms_obj)) { |
630 return CallJsBuiltin(isolate, "$arraySplice", args); | 650 return CallJsBuiltin(isolate, "$arraySplice", args); |
631 } | 651 } |
632 Handle<JSArray> array = Handle<JSArray>::cast(receiver); | 652 Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
633 DCHECK(!array->map()->is_observed()); | 653 DCHECK(!array->map()->is_observed()); |
634 | 654 |
635 int len = Smi::cast(array->length())->value(); | 655 int argument_count = args.length() - 1; |
636 | |
637 int n_arguments = args.length() - 1; | |
638 | |
639 int relative_start = 0; | 656 int relative_start = 0; |
640 if (n_arguments > 0) { | 657 if (argument_count > 0) { |
641 DisallowHeapAllocation no_gc; | 658 DisallowHeapAllocation no_gc; |
642 Object* arg1 = args[1]; | 659 if (!ClampedToInteger(args[1], &relative_start)) { |
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()) { | |
653 AllowHeapAllocation allow_allocation; | 660 AllowHeapAllocation allow_allocation; |
654 return CallJsBuiltin(isolate, "$arraySplice", args); | 661 return CallJsBuiltin(isolate, "$arraySplice", args); |
655 } | 662 } |
656 } | 663 } |
664 int len = Smi::cast(array->length())->value(); | |
665 // clip relative start to [0, len] | |
657 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0) | 666 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0) |
658 : Min(relative_start, len); | 667 : Min(relative_start, len); |
659 | 668 |
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. | |
665 int actual_delete_count; | 669 int actual_delete_count; |
666 if (n_arguments == 1) { | 670 if (argument_count == 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. | |
667 DCHECK(len - actual_start >= 0); | 675 DCHECK(len - actual_start >= 0); |
668 actual_delete_count = len - actual_start; | 676 actual_delete_count = len - actual_start; |
669 } else { | 677 } else { |
670 int value = 0; // ToInteger(undefined) == 0 | 678 int delete_count = 0; |
671 if (n_arguments > 1) { | 679 DisallowHeapAllocation no_gc; |
672 DisallowHeapAllocation no_gc; | 680 if (argument_count > 1) { |
673 Object* arg2 = args[2]; | 681 if (!ClampedToInteger(args[2], &delete_count)) { |
674 if (arg2->IsSmi()) { | |
675 value = Smi::cast(arg2)->value(); | |
676 } else { | |
677 AllowHeapAllocation allow_allocation; | 682 AllowHeapAllocation allow_allocation; |
678 return CallJsBuiltin(isolate, "$arraySplice", args); | 683 return CallJsBuiltin(isolate, "$arraySplice", args); |
679 } | 684 } |
680 } | 685 } |
681 actual_delete_count = Min(Max(value, 0), len - actual_start); | 686 actual_delete_count = Min(Max(delete_count, 0), len - actual_start); |
682 } | 687 } |
683 | 688 |
684 ElementsKind elements_kind = array->GetElementsKind(); | 689 int add_count = (argument_count > 1) ? (argument_count - 2) : 0; |
685 | 690 int new_length = len - actual_delete_count + add_count; |
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 } | |
693 | 691 |
694 if (new_length != len && JSArray::HasReadOnlyLength(array)) { | 692 if (new_length != len && JSArray::HasReadOnlyLength(array)) { |
695 AllowHeapAllocation allow_allocation; | 693 AllowHeapAllocation allow_allocation; |
696 return CallJsBuiltin(isolate, "$arraySplice", args); | 694 return CallJsBuiltin(isolate, "$arraySplice", args); |
697 } | 695 } |
698 | 696 ElementsAccessor* accessor = array->GetElementsAccessor(); |
699 if (new_length == 0) { | 697 Handle<JSArray> result = accessor->Splice( |
700 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements( | 698 array, elms_obj, actual_start, actual_delete_count, args, add_count); |
701 elms_obj, elements_kind, actual_delete_count); | 699 return *result; |
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; | |
838 } | 700 } |
839 | 701 |
840 | 702 |
841 BUILTIN(ArrayConcat) { | 703 BUILTIN(ArrayConcat) { |
842 HandleScope scope(isolate); | 704 HandleScope scope(isolate); |
843 | 705 |
844 int n_arguments = args.length(); | 706 int n_arguments = args.length(); |
845 int result_len = 0; | 707 int result_len = 0; |
846 ElementsKind elements_kind = GetInitialFastElementsKind(); | 708 ElementsKind elements_kind = GetInitialFastElementsKind(); |
847 bool has_double = false; | 709 bool has_double = false; |
(...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1534 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) | 1396 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) |
1535 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) | 1397 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) |
1536 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) | 1398 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) |
1537 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) | 1399 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) |
1538 #undef DEFINE_BUILTIN_ACCESSOR_C | 1400 #undef DEFINE_BUILTIN_ACCESSOR_C |
1539 #undef DEFINE_BUILTIN_ACCESSOR_A | 1401 #undef DEFINE_BUILTIN_ACCESSOR_A |
1540 | 1402 |
1541 | 1403 |
1542 } // namespace internal | 1404 } // namespace internal |
1543 } // namespace v8 | 1405 } // namespace v8 |
OLD | NEW |