Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(901)

Side by Side Diff: runtime/vm/debugger.cc

Issue 2904793002: Allow setting breakpoints in literal function initializers of fields. (Closed)
Patch Set: Address comments on patch set 3. Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/debugger_test.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/debugger.h" 5 #include "vm/debugger.h"
6 6
7 #include "include/dart_api.h" 7 #include "include/dart_api.h"
8 8
9 #include "platform/address_sanitizer.h" 9 #include "platform/address_sanitizer.h"
10 10
11 #include "vm/code_patcher.h" 11 #include "vm/code_patcher.h"
12 #include "vm/compiler.h" 12 #include "vm/compiler.h"
13 #include "vm/dart_entry.h" 13 #include "vm/dart_entry.h"
14 #include "vm/deopt_instructions.h" 14 #include "vm/deopt_instructions.h"
15 #include "vm/flags.h" 15 #include "vm/flags.h"
16 #include "vm/globals.h" 16 #include "vm/globals.h"
17 #include "vm/json_stream.h" 17 #include "vm/json_stream.h"
18 #include "vm/longjump.h" 18 #include "vm/longjump.h"
19 #include "vm/message_handler.h" 19 #include "vm/message_handler.h"
20 #include "vm/object.h" 20 #include "vm/object.h"
21 #include "vm/object_store.h" 21 #include "vm/object_store.h"
22 #include "vm/os.h" 22 #include "vm/os.h"
23 #include "vm/parser.h"
23 #include "vm/port.h" 24 #include "vm/port.h"
24 #include "vm/runtime_entry.h" 25 #include "vm/runtime_entry.h"
25 #include "vm/service.h" 26 #include "vm/service.h"
26 #include "vm/service_event.h" 27 #include "vm/service_event.h"
27 #include "vm/service_isolate.h" 28 #include "vm/service_isolate.h"
28 #include "vm/stack_frame.h" 29 #include "vm/stack_frame.h"
29 #include "vm/stack_trace.h" 30 #include "vm/stack_trace.h"
30 #include "vm/stub_code.h" 31 #include "vm/stub_code.h"
31 #include "vm/symbols.h" 32 #include "vm/symbols.h"
32 #include "vm/thread_interrupter.h" 33 #include "vm/thread_interrupter.h"
(...skipping 2614 matching lines...) Expand 10 before | Expand all | Expand 10 after
2647 } 2648 }
2648 } 2649 }
2649 } 2650 }
2650 2651
2651 2652
2652 static bool IsTokenPosWithinFunction(const Function& func, TokenPosition pos) { 2653 static bool IsTokenPosWithinFunction(const Function& func, TokenPosition pos) {
2653 return (func.token_pos() <= pos && pos <= func.end_token_pos()); 2654 return (func.token_pos() <= pos && pos <= func.end_token_pos());
2654 } 2655 }
2655 2656
2656 2657
2657 RawFunction* Debugger::FindBestFit(const Script& script, 2658 // Returns true if a best fit is found. A best fit can either be a function
2658 TokenPosition token_pos) { 2659 // or a field. If it is a function, then the best fit function is returned
2660 // in |best_fit|. If a best fit is a field, it means that a latent
2661 // breakpoint can be set in the range |token_pos| to |last_token_pos|.
2662 bool Debugger::FindBestFit(const Script& script,
2663 TokenPosition token_pos,
2664 TokenPosition last_token_pos,
2665 Function* best_fit) {
2659 Zone* zone = Thread::Current()->zone(); 2666 Zone* zone = Thread::Current()->zone();
2660 Class& cls = Class::Handle(zone); 2667 Class& cls = Class::Handle(zone);
2661 Array& functions = Array::Handle(zone);
2662 const GrowableObjectArray& closures = GrowableObjectArray::Handle( 2668 const GrowableObjectArray& closures = GrowableObjectArray::Handle(
2663 zone, isolate_->object_store()->closure_functions()); 2669 zone, isolate_->object_store()->closure_functions());
2670 Array& functions = Array::Handle(zone);
2664 Function& function = Function::Handle(zone); 2671 Function& function = Function::Handle(zone);
2665 Function& best_fit = Function::Handle(zone); 2672 Array& fields = Array::Handle(zone);
2673 Field& field = Field::Handle(zone);
2666 Error& error = Error::Handle(zone); 2674 Error& error = Error::Handle(zone);
2667 2675
2668 const intptr_t num_closures = closures.Length(); 2676 const intptr_t num_closures = closures.Length();
2669 for (intptr_t i = 0; i < num_closures; i++) { 2677 for (intptr_t i = 0; i < num_closures; i++) {
2670 function ^= closures.At(i); 2678 function ^= closures.At(i);
2671 if (function.script() != script.raw()) { 2679 if (function.script() != script.raw()) {
2672 continue; 2680 continue;
2673 } 2681 }
2674 if (IsTokenPosWithinFunction(function, token_pos)) { 2682 if (IsTokenPosWithinFunction(function, token_pos)) {
2675 // Select the inner most closure. 2683 // Select the inner most closure.
2676 SelectBestFit(&best_fit, &function); 2684 SelectBestFit(best_fit, &function);
2677 } 2685 }
2678 } 2686 }
2679 if (!best_fit.IsNull()) { 2687 if (!best_fit->IsNull()) {
2680 // The inner most closure found will be the best fit. Going 2688 // The inner most closure found will be the best fit. Going
2681 // over class functions below will not help in any further 2689 // over class functions below will not help in any further
2682 // narrowing. 2690 // narrowing.
2683 return best_fit.raw(); 2691 return true;
2684 } 2692 }
2685 2693
2686 const ClassTable& class_table = *isolate_->class_table(); 2694 const ClassTable& class_table = *isolate_->class_table();
2687 const intptr_t num_classes = class_table.NumCids(); 2695 const intptr_t num_classes = class_table.NumCids();
2688 for (intptr_t i = 1; i < num_classes; i++) { 2696 for (intptr_t i = 1; i < num_classes; i++) {
2689 if (class_table.HasValidClassAt(i)) { 2697 if (!class_table.HasValidClassAt(i)) {
2690 cls = class_table.At(i); 2698 continue;
2691 if (cls.script() != script.raw()) { 2699 }
2692 continue; 2700 cls = class_table.At(i);
2701 if (cls.script() != script.raw()) {
2702 continue;
2703 }
2704 // Parse class definition if not done yet.
2705 error = cls.EnsureIsFinalized(Thread::Current());
2706 if (!error.IsNull()) {
2707 // Ignore functions in this class.
2708 // TODO(hausner): Should we propagate this error? How?
2709 // EnsureIsFinalized only returns an error object if there
2710 // is no longjump base on the stack.
2711 continue;
2712 }
2713 functions = cls.functions();
2714 if (!functions.IsNull()) {
2715 const intptr_t num_functions = functions.Length();
2716 for (intptr_t pos = 0; pos < num_functions; pos++) {
2717 function ^= functions.At(pos);
2718 ASSERT(!function.IsNull());
2719 if (IsTokenPosWithinFunction(function, token_pos)) {
2720 // Closures and inner functions within a class method are not
2721 // present in the functions of a class. Hence, we can return
2722 // right away as looking through other functions of a class
2723 // will not narrow down to any inner function/closure.
2724 *best_fit = function.raw();
2725 return true;
2726 }
2693 } 2727 }
2694 // Parse class definition if not done yet. 2728 }
2695 error = cls.EnsureIsFinalized(Thread::Current()); 2729 // If none of the functions in the class contain token_pos, then we
2696 if (!error.IsNull()) { 2730 // check if it falls within a function literal initializer of a field
2697 // Ignore functions in this class. 2731 // that has not been initialized yet. If the field (and hence the
2698 // TODO(hausner): Should we propagate this error? How? 2732 // function literal initializer) has already been initialized, then
2699 // EnsureIsFinalized only returns an error object if there 2733 // it would have been found above in the object store as a closure.
2700 // is no longjump base on the stack. 2734 fields = cls.fields();
2701 continue; 2735 if (!fields.IsNull()) {
2702 } 2736 const intptr_t num_fields = fields.Length();
2703 functions = cls.functions(); 2737 for (intptr_t pos = 0; pos < num_fields; pos++) {
2704 if (!functions.IsNull()) { 2738 TokenPosition start;
2705 const intptr_t num_functions = functions.Length(); 2739 TokenPosition end;
2706 for (intptr_t pos = 0; pos < num_functions; pos++) { 2740 field ^= fields.At(pos);
2707 function ^= functions.At(pos); 2741 ASSERT(!field.IsNull());
2708 ASSERT(!function.IsNull()); 2742 if (Parser::FieldHasFunctionLiteralInitializer(field, &start, &end)) {
2709 if (IsTokenPosWithinFunction(function, token_pos)) { 2743 if ((start <= token_pos && token_pos <= end) ||
2710 // Closures and inner functions within a class method are not 2744 (token_pos <= start && start <= last_token_pos)) {
2711 // present in the functions of a class. Hence, we can return 2745 return true;
2712 // right away as looking through other functions of a class
2713 // will not narrow down to any inner function/closure.
2714 return function.raw();
2715 } 2746 }
2716 } 2747 }
2717 } 2748 }
2718 } 2749 }
2719 } 2750 }
2720 return Function::null(); 2751 return false;
2721 } 2752 }
2722 2753
2723 2754
2724 BreakpointLocation* Debugger::SetBreakpoint(const Script& script, 2755 BreakpointLocation* Debugger::SetBreakpoint(const Script& script,
2725 TokenPosition token_pos, 2756 TokenPosition token_pos,
2726 TokenPosition last_token_pos, 2757 TokenPosition last_token_pos,
2727 intptr_t requested_line, 2758 intptr_t requested_line,
2728 intptr_t requested_column) { 2759 intptr_t requested_column) {
2729 Function& func = Function::Handle(); 2760 Function& func = Function::Handle();
2730 func = FindBestFit(script, token_pos); 2761 if (!FindBestFit(script, token_pos, last_token_pos, &func)) {
2731 if (func.IsNull()) {
2732 return NULL; 2762 return NULL;
2733 } 2763 }
2734 // There may be more than one function object for a given function 2764 if (!func.IsNull()) {
2735 // in source code. There may be implicit closure functions, and 2765 // There may be more than one function object for a given function
2736 // there may be copies of mixin functions. Collect all compiled 2766 // in source code. There may be implicit closure functions, and
2737 // functions whose source code range matches exactly the best fit 2767 // there may be copies of mixin functions. Collect all compiled
2738 // function we found. 2768 // functions whose source code range matches exactly the best fit
2739 GrowableObjectArray& functions = 2769 // function we found.
2740 GrowableObjectArray::Handle(GrowableObjectArray::New()); 2770 GrowableObjectArray& functions =
2741 FindCompiledFunctions(script, func.token_pos(), func.end_token_pos(), 2771 GrowableObjectArray::Handle(GrowableObjectArray::New());
2742 &functions); 2772 FindCompiledFunctions(script, func.token_pos(), func.end_token_pos(),
2773 &functions);
2743 2774
2744 if (functions.Length() > 0) { 2775 if (functions.Length() > 0) {
2745 // One or more function object containing this breakpoint location 2776 // One or more function object containing this breakpoint location
2746 // have already been compiled. We can resolve the breakpoint now. 2777 // have already been compiled. We can resolve the breakpoint now.
2747 DeoptimizeWorld(); 2778 DeoptimizeWorld();
2748 func ^= functions.At(0); 2779 func ^= functions.At(0);
2749 TokenPosition breakpoint_pos = 2780 TokenPosition breakpoint_pos = ResolveBreakpointPos(
2750 ResolveBreakpointPos(func, token_pos, last_token_pos, requested_column); 2781 func, token_pos, last_token_pos, requested_column);
2751 if (breakpoint_pos.IsReal()) { 2782 if (breakpoint_pos.IsReal()) {
2752 BreakpointLocation* bpt = 2783 BreakpointLocation* bpt =
2753 GetBreakpointLocation(script, breakpoint_pos, requested_column); 2784 GetBreakpointLocation(script, breakpoint_pos, requested_column);
2754 if (bpt != NULL) { 2785 if (bpt != NULL) {
2755 // A source breakpoint for this location already exists. 2786 // A source breakpoint for this location already exists.
2787 return bpt;
2788 }
2789 bpt = new BreakpointLocation(script, token_pos, last_token_pos,
2790 requested_line, requested_column);
2791 bpt->SetResolved(func, breakpoint_pos);
2792 RegisterBreakpointLocation(bpt);
2793
2794 // Create code breakpoints for all compiled functions we found.
2795 const intptr_t num_functions = functions.Length();
2796 for (intptr_t i = 0; i < num_functions; i++) {
2797 func ^= functions.At(i);
2798 ASSERT(func.HasCode());
2799 MakeCodeBreakpointAt(func, bpt);
2800 }
2801 if (FLAG_verbose_debug) {
2802 intptr_t line_number;
2803 intptr_t column_number;
2804 script.GetTokenLocation(breakpoint_pos, &line_number, &column_number);
2805 OS::Print(
2806 "Resolved BP for "
2807 "function '%s' at line %" Pd " col %" Pd "\n",
2808 func.ToFullyQualifiedCString(), line_number, column_number);
2809 }
2756 return bpt; 2810 return bpt;
2757 } 2811 }
2758 bpt = new BreakpointLocation(script, token_pos, last_token_pos,
2759 requested_line, requested_column);
2760 bpt->SetResolved(func, breakpoint_pos);
2761 RegisterBreakpointLocation(bpt);
2762
2763 // Create code breakpoints for all compiled functions we found.
2764 const intptr_t num_functions = functions.Length();
2765 for (intptr_t i = 0; i < num_functions; i++) {
2766 func ^= functions.At(i);
2767 ASSERT(func.HasCode());
2768 MakeCodeBreakpointAt(func, bpt);
2769 }
2770 if (FLAG_verbose_debug) {
2771 intptr_t line_number;
2772 intptr_t column_number;
2773 script.GetTokenLocation(breakpoint_pos, &line_number, &column_number);
2774 OS::Print(
2775 "Resolved BP for "
2776 "function '%s' at line %" Pd " col %" Pd "\n",
2777 func.ToFullyQualifiedCString(), line_number, column_number);
2778 }
2779 return bpt;
2780 } 2812 }
2781 } 2813 }
2782 // There is no compiled function at this token position. 2814 // There is either an uncompiled function, or an uncompiled function literal
2783 // Register an unresolved breakpoint. 2815 // initializer of a field at |token_pos|. Hence, Register an unresolved
2784 if (FLAG_verbose_debug && !func.IsNull()) { 2816 // breakpoint.
2817 if (FLAG_verbose_debug) {
2785 intptr_t line_number; 2818 intptr_t line_number;
2786 intptr_t column_number; 2819 intptr_t column_number;
2787 script.GetTokenLocation(token_pos, &line_number, &column_number); 2820 script.GetTokenLocation(token_pos, &line_number, &column_number);
2788 OS::Print( 2821 if (func.IsNull()) {
2789 "Registering pending breakpoint for " 2822 OS::Print(
2790 "uncompiled function '%s' at line %" Pd " col %" Pd "\n", 2823 "Registering pending breakpoint for "
2791 func.ToFullyQualifiedCString(), line_number, column_number); 2824 "an uncompiled function literal at line %" Pd " col %" Pd "\n",
2825 line_number, column_number);
2826 } else {
2827 OS::Print(
2828 "Registering pending breakpoint for "
2829 "uncompiled function '%s' at line %" Pd " col %" Pd "\n",
2830 func.ToFullyQualifiedCString(), line_number, column_number);
2831 }
2792 } 2832 }
2793 BreakpointLocation* bpt = 2833 BreakpointLocation* bpt =
2794 GetBreakpointLocation(script, token_pos, requested_column); 2834 GetBreakpointLocation(script, token_pos, requested_column);
2795 if (bpt == NULL) { 2835 if (bpt == NULL) {
2796 bpt = new BreakpointLocation(script, token_pos, last_token_pos, 2836 bpt = new BreakpointLocation(script, token_pos, last_token_pos,
2797 requested_line, requested_column); 2837 requested_line, requested_column);
2798 RegisterBreakpointLocation(bpt); 2838 RegisterBreakpointLocation(bpt);
2799 } 2839 }
2800 return bpt; 2840 return bpt;
2801 } 2841 }
(...skipping 1639 matching lines...) Expand 10 before | Expand all | Expand 10 after
4441 4481
4442 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { 4482 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) {
4443 ASSERT(bpt->next() == NULL); 4483 ASSERT(bpt->next() == NULL);
4444 bpt->set_next(code_breakpoints_); 4484 bpt->set_next(code_breakpoints_);
4445 code_breakpoints_ = bpt; 4485 code_breakpoints_ = bpt;
4446 } 4486 }
4447 4487
4448 #endif // !PRODUCT 4488 #endif // !PRODUCT
4449 4489
4450 } // namespace dart 4490 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/debugger_test.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698