| 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/builtins.h" | 5 #include "src/builtins.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 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 83 called_function(); | 83 called_function(); |
| 84 } | 84 } |
| 85 #endif | 85 #endif |
| 86 | 86 |
| 87 | 87 |
| 88 #define DEF_ARG_TYPE(name, spec) \ | 88 #define DEF_ARG_TYPE(name, spec) \ |
| 89 typedef BuiltinArguments<spec> name##ArgumentsType; | 89 typedef BuiltinArguments<spec> name##ArgumentsType; |
| 90 BUILTIN_LIST_C(DEF_ARG_TYPE) | 90 BUILTIN_LIST_C(DEF_ARG_TYPE) |
| 91 #undef DEF_ARG_TYPE | 91 #undef DEF_ARG_TYPE |
| 92 | 92 |
| 93 } // namespace | |
| 94 | 93 |
| 95 // ---------------------------------------------------------------------------- | 94 // ---------------------------------------------------------------------------- |
| 96 // Support macro for defining builtins in C++. | 95 // Support macro for defining builtins in C++. |
| 97 // ---------------------------------------------------------------------------- | 96 // ---------------------------------------------------------------------------- |
| 98 // | 97 // |
| 99 // A builtin function is defined by writing: | 98 // A builtin function is defined by writing: |
| 100 // | 99 // |
| 101 // BUILTIN(name) { | 100 // BUILTIN(name) { |
| 102 // ... | 101 // ... |
| 103 // } | 102 // } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 154 // has been specialized for each architecture so if any one of them | 153 // has been specialized for each architecture so if any one of them |
| 155 // changes this code has to be changed as well. | 154 // changes this code has to be changed as well. |
| 156 const int kMarkerOffset = StandardFrameConstants::kMarkerOffset; | 155 const int kMarkerOffset = StandardFrameConstants::kMarkerOffset; |
| 157 const Smi* kConstructMarker = Smi::FromInt(StackFrame::CONSTRUCT); | 156 const Smi* kConstructMarker = Smi::FromInt(StackFrame::CONSTRUCT); |
| 158 Object* marker = Memory::Object_at(caller_fp + kMarkerOffset); | 157 Object* marker = Memory::Object_at(caller_fp + kMarkerOffset); |
| 159 bool result = (marker == kConstructMarker); | 158 bool result = (marker == kConstructMarker); |
| 160 DCHECK_EQ(result, reference_result); | 159 DCHECK_EQ(result, reference_result); |
| 161 return result; | 160 return result; |
| 162 } | 161 } |
| 163 #endif | 162 #endif |
| 163 } // namespace |
| 164 | 164 |
| 165 | 165 |
| 166 // ---------------------------------------------------------------------------- | 166 // ---------------------------------------------------------------------------- |
| 167 | 167 |
| 168 BUILTIN(Illegal) { | 168 BUILTIN(Illegal) { |
| 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 namespace { |
| 180 |
| 181 bool ClampedToInteger(Object* object, int* out) { |
| 182 // This is an extended version of ECMA-262 9.4, but additionally |
| 183 // clamps values to [kMinInt, kMaxInt] |
| 184 if (object->IsSmi()) { |
| 185 *out = Smi::cast(object)->value(); |
| 186 return true; |
| 187 } else if (object->IsHeapNumber()) { |
| 188 *out = FastD2IChecked(HeapNumber::cast(object)->value()); |
| 189 return true; |
| 190 } else if (object->IsUndefined()) { |
| 191 *out = 0; |
| 192 return true; |
| 193 } else if (object->IsBoolean()) { |
| 194 *out = (Oddball::cast(object)->kind() == Oddball::kTrue) ? 1 : 0; |
| 195 return true; |
| 196 } |
| 197 return false; |
| 198 } |
| 199 |
| 200 |
| 179 static void MoveDoubleElements(FixedDoubleArray* dst, int dst_index, | 201 static void MoveDoubleElements(FixedDoubleArray* dst, int dst_index, |
| 180 FixedDoubleArray* src, int src_index, int len) { | 202 FixedDoubleArray* src, int src_index, int len) { |
| 181 if (len == 0) return; | 203 if (len == 0) return; |
| 182 MemMove(dst->data_start() + dst_index, src->data_start() + src_index, | 204 MemMove(dst->data_start() + dst_index, src->data_start() + src_index, |
| 183 len * kDoubleSize); | 205 len * kDoubleSize); |
| 184 } | 206 } |
| 185 | 207 |
| 186 | 208 |
| 187 static bool ArrayPrototypeHasNoElements(PrototypeIterator* iter) { | 209 static bool ArrayPrototypeHasNoElements(PrototypeIterator* iter) { |
| 188 DisallowHeapAllocation no_gc; | 210 DisallowHeapAllocation no_gc; |
| (...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 297 isolate, result, | 319 isolate, result, |
| 298 Execution::Call(isolate, | 320 Execution::Call(isolate, |
| 299 function, | 321 function, |
| 300 args.receiver(), | 322 args.receiver(), |
| 301 argc, | 323 argc, |
| 302 argv.start())); | 324 argv.start())); |
| 303 return *result; | 325 return *result; |
| 304 } | 326 } |
| 305 | 327 |
| 306 | 328 |
| 329 } // namespace |
| 330 |
| 331 |
| 307 BUILTIN(ArrayPush) { | 332 BUILTIN(ArrayPush) { |
| 308 HandleScope scope(isolate); | 333 HandleScope scope(isolate); |
| 309 Handle<Object> receiver = args.receiver(); | 334 Handle<Object> receiver = args.receiver(); |
| 310 MaybeHandle<FixedArrayBase> maybe_elms_obj = | 335 MaybeHandle<FixedArrayBase> maybe_elms_obj = |
| 311 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1); | 336 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 1); |
| 312 Handle<FixedArrayBase> elms_obj; | 337 Handle<FixedArrayBase> elms_obj; |
| 313 if (!maybe_elms_obj.ToHandle(&elms_obj)) { | 338 if (!maybe_elms_obj.ToHandle(&elms_obj)) { |
| 314 return CallJsIntrinsic(isolate, isolate->array_push(), args); | 339 return CallJsIntrinsic(isolate, isolate->array_push(), args); |
| 315 } | 340 } |
| 316 // Fast Elements Path | 341 // Fast Elements Path |
| (...skipping 290 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 607 | 632 |
| 608 ElementsAccessor* accessor = object->GetElementsAccessor(); | 633 ElementsAccessor* accessor = object->GetElementsAccessor(); |
| 609 accessor->CopyElements( | 634 accessor->CopyElements( |
| 610 elms, k, kind, handle(result_array->elements(), isolate), 0, result_len); | 635 elms, k, kind, handle(result_array->elements(), isolate), 0, result_len); |
| 611 return *result_array; | 636 return *result_array; |
| 612 } | 637 } |
| 613 | 638 |
| 614 | 639 |
| 615 BUILTIN(ArraySplice) { | 640 BUILTIN(ArraySplice) { |
| 616 HandleScope scope(isolate); | 641 HandleScope scope(isolate); |
| 617 Heap* heap = isolate->heap(); | |
| 618 Handle<Object> receiver = args.receiver(); | 642 Handle<Object> receiver = args.receiver(); |
| 619 MaybeHandle<FixedArrayBase> maybe_elms_obj = | 643 MaybeHandle<FixedArrayBase> maybe_elms_obj = |
| 620 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3); | 644 EnsureJSArrayWithWritableFastElements(isolate, receiver, &args, 3); |
| 621 Handle<FixedArrayBase> elms_obj; | 645 Handle<FixedArrayBase> elms_obj; |
| 622 if (!maybe_elms_obj.ToHandle(&elms_obj)) { | 646 if (!maybe_elms_obj.ToHandle(&elms_obj)) { |
| 623 return CallJsIntrinsic(isolate, isolate->array_splice(), args); | 647 return CallJsIntrinsic(isolate, isolate->array_splice(), args); |
| 624 } | 648 } |
| 625 Handle<JSArray> array = Handle<JSArray>::cast(receiver); | 649 Handle<JSArray> array = Handle<JSArray>::cast(receiver); |
| 626 DCHECK(!array->map()->is_observed()); | 650 DCHECK(!array->map()->is_observed()); |
| 627 | 651 |
| 628 int len = Smi::cast(array->length())->value(); | 652 int argument_count = args.length() - 1; |
| 629 | |
| 630 int n_arguments = args.length() - 1; | |
| 631 | |
| 632 int relative_start = 0; | 653 int relative_start = 0; |
| 633 if (n_arguments > 0) { | 654 if (argument_count > 0) { |
| 634 DisallowHeapAllocation no_gc; | 655 DisallowHeapAllocation no_gc; |
| 635 Object* arg1 = args[1]; | 656 if (!ClampedToInteger(args[1], &relative_start)) { |
| 636 if (arg1->IsSmi()) { | |
| 637 relative_start = Smi::cast(arg1)->value(); | |
| 638 } else if (arg1->IsHeapNumber()) { | |
| 639 double start = HeapNumber::cast(arg1)->value(); | |
| 640 if (start < kMinInt || start > kMaxInt) { | |
| 641 AllowHeapAllocation allow_allocation; | |
| 642 return CallJsIntrinsic(isolate, isolate->array_splice(), args); | |
| 643 } | |
| 644 relative_start = std::isnan(start) ? 0 : static_cast<int>(start); | |
| 645 } else if (!arg1->IsUndefined()) { | |
| 646 AllowHeapAllocation allow_allocation; | 657 AllowHeapAllocation allow_allocation; |
| 647 return CallJsIntrinsic(isolate, isolate->array_splice(), args); | 658 return CallJsIntrinsic(isolate, isolate->array_splice(), args); |
| 648 } | 659 } |
| 649 } | 660 } |
| 661 int len = Smi::cast(array->length())->value(); |
| 662 // clip relative start to [0, len] |
| 650 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0) | 663 int actual_start = (relative_start < 0) ? Max(len + relative_start, 0) |
| 651 : Min(relative_start, len); | 664 : Min(relative_start, len); |
| 652 | 665 |
| 653 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is | |
| 654 // given as a request to delete all the elements from the start. | |
| 655 // And it differs from the case of undefined delete count. | |
| 656 // This does not follow ECMA-262, but we do the same for | |
| 657 // compatibility. | |
| 658 int actual_delete_count; | 666 int actual_delete_count; |
| 659 if (n_arguments == 1) { | 667 if (argument_count == 1) { |
| 668 // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is |
| 669 // given as a request to delete all the elements from the start. |
| 670 // And it differs from the case of undefined delete count. |
| 671 // This does not follow ECMA-262, but we do the same for compatibility. |
| 660 DCHECK(len - actual_start >= 0); | 672 DCHECK(len - actual_start >= 0); |
| 661 actual_delete_count = len - actual_start; | 673 actual_delete_count = len - actual_start; |
| 662 } else { | 674 } else { |
| 663 int value = 0; // ToInteger(undefined) == 0 | 675 int delete_count = 0; |
| 664 if (n_arguments > 1) { | 676 DisallowHeapAllocation no_gc; |
| 665 DisallowHeapAllocation no_gc; | 677 if (argument_count > 1) { |
| 666 Object* arg2 = args[2]; | 678 if (!ClampedToInteger(args[2], &delete_count)) { |
| 667 if (arg2->IsSmi()) { | |
| 668 value = Smi::cast(arg2)->value(); | |
| 669 } else { | |
| 670 AllowHeapAllocation allow_allocation; | 679 AllowHeapAllocation allow_allocation; |
| 671 return CallJsIntrinsic(isolate, isolate->array_splice(), args); | 680 return CallJsIntrinsic(isolate, isolate->array_splice(), args); |
| 672 } | 681 } |
| 673 } | 682 } |
| 674 actual_delete_count = Min(Max(value, 0), len - actual_start); | 683 actual_delete_count = Min(Max(delete_count, 0), len - actual_start); |
| 675 } | 684 } |
| 676 | 685 |
| 677 ElementsKind elements_kind = array->GetElementsKind(); | 686 int add_count = (argument_count > 1) ? (argument_count - 2) : 0; |
| 678 | 687 int new_length = len - actual_delete_count + add_count; |
| 679 int item_count = (n_arguments > 1) ? (n_arguments - 2) : 0; | |
| 680 int new_length = len - actual_delete_count + item_count; | |
| 681 | |
| 682 // For double mode we do not support changing the length. | |
| 683 if (new_length > len && IsFastDoubleElementsKind(elements_kind)) { | |
| 684 return CallJsIntrinsic(isolate, isolate->array_splice(), args); | |
| 685 } | |
| 686 | 688 |
| 687 if (new_length != len && JSArray::HasReadOnlyLength(array)) { | 689 if (new_length != len && JSArray::HasReadOnlyLength(array)) { |
| 688 AllowHeapAllocation allow_allocation; | 690 AllowHeapAllocation allow_allocation; |
| 689 return CallJsIntrinsic(isolate, isolate->array_splice(), args); | 691 return CallJsIntrinsic(isolate, isolate->array_splice(), args); |
| 690 } | 692 } |
| 691 | 693 ElementsAccessor* accessor = array->GetElementsAccessor(); |
| 692 if (new_length == 0) { | 694 Handle<JSArray> result = accessor->Splice( |
| 693 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements( | 695 array, elms_obj, actual_start, actual_delete_count, args, add_count); |
| 694 elms_obj, elements_kind, actual_delete_count); | 696 return *result; |
| 695 array->set_elements(heap->empty_fixed_array()); | |
| 696 array->set_length(Smi::FromInt(0)); | |
| 697 return *result; | |
| 698 } | |
| 699 | |
| 700 Handle<JSArray> result_array = | |
| 701 isolate->factory()->NewJSArray(elements_kind, | |
| 702 actual_delete_count, | |
| 703 actual_delete_count); | |
| 704 | |
| 705 if (actual_delete_count > 0) { | |
| 706 DisallowHeapAllocation no_gc; | |
| 707 ElementsAccessor* accessor = array->GetElementsAccessor(); | |
| 708 accessor->CopyElements( | |
| 709 elms_obj, actual_start, elements_kind, | |
| 710 handle(result_array->elements(), isolate), 0, actual_delete_count); | |
| 711 } | |
| 712 | |
| 713 bool elms_changed = false; | |
| 714 if (item_count < actual_delete_count) { | |
| 715 // Shrink the array. | |
| 716 const bool trim_array = !heap->lo_space()->Contains(*elms_obj) && | |
| 717 ((actual_start + item_count) < | |
| 718 (len - actual_delete_count - actual_start)); | |
| 719 if (trim_array) { | |
| 720 const int delta = actual_delete_count - item_count; | |
| 721 | |
| 722 if (elms_obj->IsFixedDoubleArray()) { | |
| 723 Handle<FixedDoubleArray> elms = | |
| 724 Handle<FixedDoubleArray>::cast(elms_obj); | |
| 725 MoveDoubleElements(*elms, delta, *elms, 0, actual_start); | |
| 726 } else { | |
| 727 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); | |
| 728 DisallowHeapAllocation no_gc; | |
| 729 heap->MoveElements(*elms, delta, 0, actual_start); | |
| 730 } | |
| 731 | |
| 732 if (heap->CanMoveObjectStart(*elms_obj)) { | |
| 733 // On the fast path we move the start of the object in memory. | |
| 734 elms_obj = handle(heap->LeftTrimFixedArray(*elms_obj, delta)); | |
| 735 } else { | |
| 736 // This is the slow path. We are going to move the elements to the left | |
| 737 // by copying them. For trimmed values we store the hole. | |
| 738 if (elms_obj->IsFixedDoubleArray()) { | |
| 739 Handle<FixedDoubleArray> elms = | |
| 740 Handle<FixedDoubleArray>::cast(elms_obj); | |
| 741 MoveDoubleElements(*elms, 0, *elms, delta, len - delta); | |
| 742 elms->FillWithHoles(len - delta, len); | |
| 743 } else { | |
| 744 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); | |
| 745 DisallowHeapAllocation no_gc; | |
| 746 heap->MoveElements(*elms, 0, delta, len - delta); | |
| 747 elms->FillWithHoles(len - delta, len); | |
| 748 } | |
| 749 } | |
| 750 elms_changed = true; | |
| 751 } else { | |
| 752 if (elms_obj->IsFixedDoubleArray()) { | |
| 753 Handle<FixedDoubleArray> elms = | |
| 754 Handle<FixedDoubleArray>::cast(elms_obj); | |
| 755 MoveDoubleElements(*elms, actual_start + item_count, | |
| 756 *elms, actual_start + actual_delete_count, | |
| 757 (len - actual_delete_count - actual_start)); | |
| 758 elms->FillWithHoles(new_length, len); | |
| 759 } else { | |
| 760 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); | |
| 761 DisallowHeapAllocation no_gc; | |
| 762 heap->MoveElements(*elms, actual_start + item_count, | |
| 763 actual_start + actual_delete_count, | |
| 764 (len - actual_delete_count - actual_start)); | |
| 765 elms->FillWithHoles(new_length, len); | |
| 766 } | |
| 767 } | |
| 768 } else if (item_count > actual_delete_count) { | |
| 769 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); | |
| 770 // Currently fixed arrays cannot grow too big, so | |
| 771 // we should never hit this case. | |
| 772 DCHECK((item_count - actual_delete_count) <= (Smi::kMaxValue - len)); | |
| 773 | |
| 774 // Check if array need to grow. | |
| 775 if (new_length > elms->length()) { | |
| 776 // New backing storage is needed. | |
| 777 int capacity = new_length + (new_length >> 1) + 16; | |
| 778 Handle<FixedArray> new_elms = | |
| 779 isolate->factory()->NewUninitializedFixedArray(capacity); | |
| 780 | |
| 781 DisallowHeapAllocation no_gc; | |
| 782 | |
| 783 ElementsKind kind = array->GetElementsKind(); | |
| 784 ElementsAccessor* accessor = array->GetElementsAccessor(); | |
| 785 if (actual_start > 0) { | |
| 786 // Copy the part before actual_start as is. | |
| 787 accessor->CopyElements( | |
| 788 elms, 0, kind, new_elms, 0, actual_start); | |
| 789 } | |
| 790 accessor->CopyElements( | |
| 791 elms, actual_start + actual_delete_count, kind, | |
| 792 new_elms, actual_start + item_count, | |
| 793 ElementsAccessor::kCopyToEndAndInitializeToHole); | |
| 794 | |
| 795 elms_obj = new_elms; | |
| 796 elms_changed = true; | |
| 797 } else { | |
| 798 DisallowHeapAllocation no_gc; | |
| 799 heap->MoveElements(*elms, actual_start + item_count, | |
| 800 actual_start + actual_delete_count, | |
| 801 (len - actual_delete_count - actual_start)); | |
| 802 } | |
| 803 } | |
| 804 | |
| 805 if (IsFastDoubleElementsKind(elements_kind)) { | |
| 806 Handle<FixedDoubleArray> elms = Handle<FixedDoubleArray>::cast(elms_obj); | |
| 807 for (int k = actual_start; k < actual_start + item_count; k++) { | |
| 808 Object* arg = args[3 + k - actual_start]; | |
| 809 if (arg->IsSmi()) { | |
| 810 elms->set(k, Smi::cast(arg)->value()); | |
| 811 } else { | |
| 812 elms->set(k, HeapNumber::cast(arg)->value()); | |
| 813 } | |
| 814 } | |
| 815 } else { | |
| 816 Handle<FixedArray> elms = Handle<FixedArray>::cast(elms_obj); | |
| 817 DisallowHeapAllocation no_gc; | |
| 818 WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc); | |
| 819 for (int k = actual_start; k < actual_start + item_count; k++) { | |
| 820 elms->set(k, args[3 + k - actual_start], mode); | |
| 821 } | |
| 822 } | |
| 823 | |
| 824 if (elms_changed) { | |
| 825 array->set_elements(*elms_obj); | |
| 826 } | |
| 827 // Set the length. | |
| 828 array->set_length(Smi::FromInt(new_length)); | |
| 829 | |
| 830 return *result_array; | |
| 831 } | 697 } |
| 832 | 698 |
| 833 | 699 |
| 834 BUILTIN(ArrayConcat) { | 700 BUILTIN(ArrayConcat) { |
| 835 HandleScope scope(isolate); | 701 HandleScope scope(isolate); |
| 836 | 702 |
| 837 int n_arguments = args.length(); | 703 int n_arguments = args.length(); |
| 838 int result_len = 0; | 704 int result_len = 0; |
| 839 ElementsKind elements_kind = GetInitialFastElementsKind(); | 705 ElementsKind elements_kind = GetInitialFastElementsKind(); |
| 840 bool has_double = false; | 706 bool has_double = false; |
| (...skipping 686 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1527 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) | 1393 BUILTIN_LIST_C(DEFINE_BUILTIN_ACCESSOR_C) |
| 1528 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) | 1394 BUILTIN_LIST_A(DEFINE_BUILTIN_ACCESSOR_A) |
| 1529 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) | 1395 BUILTIN_LIST_H(DEFINE_BUILTIN_ACCESSOR_H) |
| 1530 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) | 1396 BUILTIN_LIST_DEBUG_A(DEFINE_BUILTIN_ACCESSOR_A) |
| 1531 #undef DEFINE_BUILTIN_ACCESSOR_C | 1397 #undef DEFINE_BUILTIN_ACCESSOR_C |
| 1532 #undef DEFINE_BUILTIN_ACCESSOR_A | 1398 #undef DEFINE_BUILTIN_ACCESSOR_A |
| 1533 | 1399 |
| 1534 | 1400 |
| 1535 } // namespace internal | 1401 } // namespace internal |
| 1536 } // namespace v8 | 1402 } // namespace v8 |
| OLD | NEW |