OLD | NEW |
1 // Copyright 2010 the V8 project authors. All rights reserved. | 1 // Copyright 2010 the V8 project authors. All rights reserved. |
2 // Redistribution and use in source and binary forms, with or without | 2 // Redistribution and use in source and binary forms, with or without |
3 // modification, are permitted provided that the following conditions are | 3 // modification, are permitted provided that the following conditions are |
4 // met: | 4 // met: |
5 // | 5 // |
6 // * Redistributions of source code must retain the above copyright | 6 // * Redistributions of source code must retain the above copyright |
7 // notice, this list of conditions and the following disclaimer. | 7 // notice, this list of conditions and the following disclaimer. |
8 // * Redistributions in binary form must reproduce the above | 8 // * Redistributions in binary form must reproduce the above |
9 // copyright notice, this list of conditions and the following | 9 // copyright notice, this list of conditions and the following |
10 // disclaimer in the documentation and/or other materials provided | 10 // disclaimer in the documentation and/or other materials provided |
(...skipping 16 matching lines...) Expand all Loading... |
27 | 27 |
28 | 28 |
29 #include "v8.h" | 29 #include "v8.h" |
30 | 30 |
31 #include "liveedit.h" | 31 #include "liveedit.h" |
32 #include "compiler.h" | 32 #include "compiler.h" |
33 #include "oprofile-agent.h" | 33 #include "oprofile-agent.h" |
34 #include "scopes.h" | 34 #include "scopes.h" |
35 #include "global-handles.h" | 35 #include "global-handles.h" |
36 #include "debug.h" | 36 #include "debug.h" |
| 37 #include "memory.h" |
37 | 38 |
38 namespace v8 { | 39 namespace v8 { |
39 namespace internal { | 40 namespace internal { |
40 | 41 |
41 | 42 |
42 #ifdef ENABLE_DEBUGGER_SUPPORT | 43 #ifdef ENABLE_DEBUGGER_SUPPORT |
43 | 44 |
44 static void CompileScriptForTracker(Handle<Script> script) { | 45 static void CompileScriptForTracker(Handle<Script> script) { |
45 const bool is_eval = false; | 46 const bool is_eval = false; |
46 const bool is_global = true; | 47 const bool is_global = true; |
(...skipping 619 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
666 BreakPointInfo::cast(break_point_infos->get(i))); | 667 BreakPointInfo::cast(break_point_infos->get(i))); |
667 int new_position = TranslatePosition(info->source_position()->value(), | 668 int new_position = TranslatePosition(info->source_position()->value(), |
668 position_change_array); | 669 position_change_array); |
669 info->set_source_position(Smi::FromInt(new_position)); | 670 info->set_source_position(Smi::FromInt(new_position)); |
670 } | 671 } |
671 } | 672 } |
672 // TODO(635): Also patch breakpoint objects in JS. | 673 // TODO(635): Also patch breakpoint objects in JS. |
673 } | 674 } |
674 | 675 |
675 | 676 |
| 677 // Check an activation against list of functions. If there is a function |
| 678 // that matches, its status in result array is changed to status argument value. |
| 679 static bool CheckActivation(Handle<JSArray> shared_info_array, |
| 680 Handle<JSArray> result, StackFrame* frame, |
| 681 LiveEdit::FunctionPatchabilityStatus status) { |
| 682 if (!frame->is_java_script()) { |
| 683 return false; |
| 684 } |
| 685 int len = Smi::cast(shared_info_array->length())->value(); |
| 686 for (int i = 0; i < len; i++) { |
| 687 JSValue* wrapper = JSValue::cast(shared_info_array->GetElement(i)); |
| 688 Handle<SharedFunctionInfo> shared( |
| 689 SharedFunctionInfo::cast(wrapper->value())); |
| 690 |
| 691 if (frame->code() == shared->code()) { |
| 692 SetElement(result, i, Handle<Smi>(Smi::FromInt(status))); |
| 693 return true; |
| 694 } |
| 695 } |
| 696 return false; |
| 697 } |
| 698 |
| 699 |
| 700 // Iterates over handler chain and removes all elements that are inside |
| 701 // frames being dropped. |
| 702 static bool FixTryCatchHandler(StackFrame* top_frame, |
| 703 StackFrame* bottom_frame) { |
| 704 Address* pointer_address = |
| 705 &Memory::Address_at(Top::get_address_from_id(Top::k_handler_address)); |
| 706 |
| 707 while (*pointer_address < top_frame->sp()) { |
| 708 pointer_address = &Memory::Address_at(*pointer_address); |
| 709 } |
| 710 Address* above_frame_address = pointer_address; |
| 711 while (*pointer_address < bottom_frame->fp()) { |
| 712 pointer_address = &Memory::Address_at(*pointer_address); |
| 713 } |
| 714 bool change = *above_frame_address != *pointer_address; |
| 715 *above_frame_address = *pointer_address; |
| 716 return change; |
| 717 } |
| 718 |
| 719 |
| 720 // Removes specified range of frames from stack. There may be 1 or more |
| 721 // frames in range. Anyway the bottom frame is restarted rather than dropped, |
| 722 // and therefore has to be a JavaScript frame. |
| 723 // Returns error message or NULL. |
| 724 static const char* DropFrames(Vector<StackFrame*> frames, |
| 725 int top_frame_index, |
| 726 int bottom_js_frame_index) { |
| 727 StackFrame* pre_top_frame = frames[top_frame_index - 1]; |
| 728 StackFrame* top_frame = frames[top_frame_index]; |
| 729 StackFrame* bottom_js_frame = frames[bottom_js_frame_index]; |
| 730 |
| 731 ASSERT(bottom_js_frame->is_java_script()); |
| 732 |
| 733 // Check the nature of the top frame. |
| 734 if (pre_top_frame->code()->is_inline_cache_stub() && |
| 735 pre_top_frame->code()->ic_state() == DEBUG_BREAK) { |
| 736 // OK, we can drop inline cache calls. |
| 737 } else if (pre_top_frame->code() == |
| 738 Builtins::builtin(Builtins::FrameDropper_LiveEdit)) { |
| 739 // OK, we can drop our own code. |
| 740 } else if (pre_top_frame->code()->kind() == Code::STUB && |
| 741 pre_top_frame->code()->major_key()) { |
| 742 // Unit Test entry, it's fine, we support this case. |
| 743 } else { |
| 744 return "Unknown structure of stack above changing function"; |
| 745 } |
| 746 |
| 747 Address unused_stack_top = top_frame->sp(); |
| 748 Address unused_stack_bottom = bottom_js_frame->fp() |
| 749 - Debug::kFrameDropperFrameSize * kPointerSize // Size of the new frame. |
| 750 + kPointerSize; // Bigger address end is exclusive. |
| 751 |
| 752 if (unused_stack_top > unused_stack_bottom) { |
| 753 return "Not enough space for frame dropper frame"; |
| 754 } |
| 755 |
| 756 // Committing now. After this point we should return only NULL value. |
| 757 |
| 758 FixTryCatchHandler(pre_top_frame, bottom_js_frame); |
| 759 // Make sure FixTryCatchHandler is idempotent. |
| 760 ASSERT(!FixTryCatchHandler(pre_top_frame, bottom_js_frame)); |
| 761 |
| 762 Handle<Code> code(Builtins::builtin(Builtins::FrameDropper_LiveEdit)); |
| 763 top_frame->set_pc(code->entry()); |
| 764 pre_top_frame->SetCallerFp(bottom_js_frame->fp()); |
| 765 |
| 766 Debug::SetUpFrameDropperFrame(bottom_js_frame, code); |
| 767 |
| 768 for (Address a = unused_stack_top; |
| 769 a < unused_stack_bottom; |
| 770 a += kPointerSize) { |
| 771 Memory::Object_at(a) = Smi::FromInt(0); |
| 772 } |
| 773 |
| 774 return NULL; |
| 775 } |
| 776 |
| 777 |
| 778 static bool IsDropableFrame(StackFrame* frame) { |
| 779 return !frame->is_exit(); |
| 780 } |
| 781 |
| 782 // Fills result array with statuses of functions. Modifies the stack |
| 783 // removing all listed function if possible and if do_drop is true. |
| 784 static const char* DropActivationsInActiveThread( |
| 785 Handle<JSArray> shared_info_array, Handle<JSArray> result, bool do_drop) { |
| 786 |
| 787 ZoneScope scope(DELETE_ON_EXIT); |
| 788 Vector<StackFrame*> frames = CreateStackMap(); |
| 789 |
| 790 int array_len = Smi::cast(shared_info_array->length())->value(); |
| 791 |
| 792 int top_frame_index = -1; |
| 793 int frame_index = 0; |
| 794 for (; frame_index < frames.length(); frame_index++) { |
| 795 StackFrame* frame = frames[frame_index]; |
| 796 if (frame->id() == Debug::break_frame_id()) { |
| 797 top_frame_index = frame_index; |
| 798 break; |
| 799 } |
| 800 if (CheckActivation(shared_info_array, result, frame, |
| 801 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { |
| 802 // We are still above break_frame. It is not a target frame, |
| 803 // it is a problem. |
| 804 return "Debugger mark-up on stack is not found"; |
| 805 } |
| 806 } |
| 807 |
| 808 if (top_frame_index == -1) { |
| 809 // We haven't found break frame, but no function is blocking us anyway. |
| 810 return NULL; |
| 811 } |
| 812 |
| 813 bool target_frame_found = false; |
| 814 int bottom_js_frame_index = top_frame_index; |
| 815 bool c_code_found = false; |
| 816 |
| 817 for (; frame_index < frames.length(); frame_index++) { |
| 818 StackFrame* frame = frames[frame_index]; |
| 819 if (!IsDropableFrame(frame)) { |
| 820 c_code_found = true; |
| 821 break; |
| 822 } |
| 823 if (CheckActivation(shared_info_array, result, frame, |
| 824 LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { |
| 825 target_frame_found = true; |
| 826 bottom_js_frame_index = frame_index; |
| 827 } |
| 828 } |
| 829 |
| 830 if (c_code_found) { |
| 831 // There is a C frames on stack. Check that there are no target frames |
| 832 // below them. |
| 833 for (; frame_index < frames.length(); frame_index++) { |
| 834 StackFrame* frame = frames[frame_index]; |
| 835 if (frame->is_java_script()) { |
| 836 if (CheckActivation(shared_info_array, result, frame, |
| 837 LiveEdit::FUNCTION_BLOCKED_UNDER_NATIVE_CODE)) { |
| 838 // Cannot drop frame under C frames. |
| 839 return NULL; |
| 840 } |
| 841 } |
| 842 } |
| 843 } |
| 844 |
| 845 if (!do_drop) { |
| 846 // We are in check-only mode. |
| 847 return NULL; |
| 848 } |
| 849 |
| 850 if (!target_frame_found) { |
| 851 // Nothing to drop. |
| 852 return NULL; |
| 853 } |
| 854 |
| 855 const char* error_message = DropFrames(frames, top_frame_index, |
| 856 bottom_js_frame_index); |
| 857 |
| 858 if (error_message != NULL) { |
| 859 return error_message; |
| 860 } |
| 861 |
| 862 // Adjust break_frame after some frames has been dropped. |
| 863 StackFrame::Id new_id = StackFrame::NO_ID; |
| 864 for (int i = bottom_js_frame_index + 1; i < frames.length(); i++) { |
| 865 if (frames[i]->type() == StackFrame::JAVA_SCRIPT) { |
| 866 new_id = frames[i]->id(); |
| 867 break; |
| 868 } |
| 869 } |
| 870 Debug::FramesHaveBeenDropped(new_id); |
| 871 |
| 872 // Replace "blocked on active" with "replaced on active" status. |
| 873 for (int i = 0; i < array_len; i++) { |
| 874 if (result->GetElement(i) == |
| 875 Smi::FromInt(LiveEdit::FUNCTION_BLOCKED_ON_ACTIVE_STACK)) { |
| 876 result->SetElement(i, Smi::FromInt( |
| 877 LiveEdit::FUNCTION_REPLACED_ON_ACTIVE_STACK)); |
| 878 } |
| 879 } |
| 880 return NULL; |
| 881 } |
| 882 |
| 883 |
| 884 class InactiveThreadActivationsChecker : public ThreadVisitor { |
| 885 public: |
| 886 InactiveThreadActivationsChecker(Handle<JSArray> shared_info_array, |
| 887 Handle<JSArray> result) |
| 888 : shared_info_array_(shared_info_array), result_(result), |
| 889 has_blocked_functions_(false) { |
| 890 } |
| 891 void VisitThread(ThreadLocalTop* top) { |
| 892 for (StackFrameIterator it(top); !it.done(); it.Advance()) { |
| 893 has_blocked_functions_ |= CheckActivation( |
| 894 shared_info_array_, result_, it.frame(), |
| 895 LiveEdit::FUNCTION_BLOCKED_ON_OTHER_STACK); |
| 896 } |
| 897 } |
| 898 bool HasBlockedFunctions() { |
| 899 return has_blocked_functions_; |
| 900 } |
| 901 |
| 902 private: |
| 903 Handle<JSArray> shared_info_array_; |
| 904 Handle<JSArray> result_; |
| 905 bool has_blocked_functions_; |
| 906 }; |
| 907 |
| 908 |
| 909 Handle<JSArray> LiveEdit::CheckAndDropActivations( |
| 910 Handle<JSArray> shared_info_array, bool do_drop) { |
| 911 int len = Smi::cast(shared_info_array->length())->value(); |
| 912 |
| 913 Handle<JSArray> result = Factory::NewJSArray(len); |
| 914 |
| 915 // Fill the default values. |
| 916 for (int i = 0; i < len; i++) { |
| 917 SetElement(result, i, |
| 918 Handle<Smi>(Smi::FromInt(FUNCTION_AVAILABLE_FOR_PATCH))); |
| 919 } |
| 920 |
| 921 |
| 922 // First check inactive threads. Fail if some functions are blocked there. |
| 923 InactiveThreadActivationsChecker inactive_threads_checker(shared_info_array, |
| 924 result); |
| 925 ThreadManager::IterateThreads(&inactive_threads_checker); |
| 926 if (inactive_threads_checker.HasBlockedFunctions()) { |
| 927 return result; |
| 928 } |
| 929 |
| 930 // Try to drop activations from the current stack. |
| 931 const char* error_message = |
| 932 DropActivationsInActiveThread(shared_info_array, result, do_drop); |
| 933 if (error_message != NULL) { |
| 934 // Add error message as an array extra element. |
| 935 Vector<const char> vector_message(error_message, strlen(error_message)); |
| 936 Handle<String> str = Factory::NewStringFromAscii(vector_message); |
| 937 SetElement(result, len, str); |
| 938 } |
| 939 return result; |
| 940 } |
| 941 |
| 942 |
676 LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) { | 943 LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) { |
677 if (active_function_info_listener != NULL) { | 944 if (active_function_info_listener != NULL) { |
678 active_function_info_listener->FunctionStarted(fun); | 945 active_function_info_listener->FunctionStarted(fun); |
679 } | 946 } |
680 } | 947 } |
681 | 948 |
682 | 949 |
683 LiveEditFunctionTracker::~LiveEditFunctionTracker() { | 950 LiveEditFunctionTracker::~LiveEditFunctionTracker() { |
684 if (active_function_info_listener != NULL) { | 951 if (active_function_info_listener != NULL) { |
685 active_function_info_listener->FunctionDone(); | 952 active_function_info_listener->FunctionDone(); |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
728 | 995 |
729 bool LiveEditFunctionTracker::IsActive() { | 996 bool LiveEditFunctionTracker::IsActive() { |
730 return false; | 997 return false; |
731 } | 998 } |
732 | 999 |
733 #endif // ENABLE_DEBUGGER_SUPPORT | 1000 #endif // ENABLE_DEBUGGER_SUPPORT |
734 | 1001 |
735 | 1002 |
736 | 1003 |
737 } } // namespace v8::internal | 1004 } } // namespace v8::internal |
OLD | NEW |