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

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

Issue 2904793002: Allow setting breakpoints in literal function initializers of fields. (Closed)
Patch Set: Address comments 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
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());
2664 Function& function = Function::Handle(zone); 2670 Function& function = Function::Handle(zone);
2665 Function& best_fit = Function::Handle(zone); 2671 Field& field = Field::Handle(zone);
2666 Error& error = Error::Handle(zone); 2672 Error& error = Error::Handle(zone);
2667 2673
2668 const intptr_t num_closures = closures.Length(); 2674 const intptr_t num_closures = closures.Length();
2669 for (intptr_t i = 0; i < num_closures; i++) { 2675 for (intptr_t i = 0; i < num_closures; i++) {
2670 function ^= closures.At(i); 2676 function ^= closures.At(i);
2671 if (function.script() != script.raw()) { 2677 if (function.script() != script.raw()) {
2672 continue; 2678 continue;
2673 } 2679 }
2674 if (IsTokenPosWithinFunction(function, token_pos)) { 2680 if (IsTokenPosWithinFunction(function, token_pos)) {
2675 // Select the inner most closure. 2681 // Select the inner most closure.
2676 SelectBestFit(&best_fit, &function); 2682 SelectBestFit(best_fit, &function);
2677 } 2683 }
2678 } 2684 }
2679 if (!best_fit.IsNull()) { 2685 if (!best_fit->IsNull()) {
2680 // The inner most closure found will be the best fit. Going 2686 // The inner most closure found will be the best fit. Going
2681 // over class functions below will not help in any further 2687 // over class functions below will not help in any further
2682 // narrowing. 2688 // narrowing.
2683 return best_fit.raw(); 2689 return true;
2684 } 2690 }
2685 2691
2686 const ClassTable& class_table = *isolate_->class_table(); 2692 const ClassTable& class_table = *isolate_->class_table();
2687 const intptr_t num_classes = class_table.NumCids(); 2693 const intptr_t num_classes = class_table.NumCids();
2688 for (intptr_t i = 1; i < num_classes; i++) { 2694 for (intptr_t i = 1; i < num_classes; i++) {
2689 if (class_table.HasValidClassAt(i)) { 2695 if (!class_table.HasValidClassAt(i)) {
2690 cls = class_table.At(i); 2696 continue;
2691 if (cls.script() != script.raw()) { 2697 }
2692 continue; 2698 cls = class_table.At(i);
2699 if (cls.script() != script.raw()) {
2700 continue;
2701 }
2702 // Parse class definition if not done yet.
2703 error = cls.EnsureIsFinalized(Thread::Current());
2704 if (!error.IsNull()) {
2705 // Ignore functions in this class.
2706 // TODO(hausner): Should we propagate this error? How?
2707 // EnsureIsFinalized only returns an error object if there
2708 // is no longjump base on the stack.
2709 continue;
2710 }
2711 const Array& functions = Array::Handle(zone, cls.functions());
siva 2017/05/26 17:18:40 Since this is in a loop it is better to hoist this
sivachandra 2017/05/26 20:27:35 Done.
2712 if (!functions.IsNull()) {
2713 const intptr_t num_functions = functions.Length();
2714 for (intptr_t pos = 0; pos < num_functions; pos++) {
2715 function ^= functions.At(pos);
2716 ASSERT(!function.IsNull());
2717 if (IsTokenPosWithinFunction(function, token_pos)) {
2718 // Closures and inner functions within a class method are not
2719 // present in the functions of a class. Hence, we can return
2720 // right away as looking through other functions of a class
2721 // will not narrow down to any inner function/closure.
2722 *best_fit = function.raw();
2723 return true;
2724 }
2693 } 2725 }
2694 // Parse class definition if not done yet. 2726 }
2695 error = cls.EnsureIsFinalized(Thread::Current()); 2727 // If none of the functions in the class contain token_pos, then we
2696 if (!error.IsNull()) { 2728 // check if it falls within a function literal initializer of field.
siva 2017/05/26 17:18:40 // check if it falls within a function literal ini
sivachandra 2017/05/26 20:27:34 Improved as you suggested, and also added a little
2697 // Ignore functions in this class. 2729 const Array& fields = Array::Handle(zone, cls.fields());
siva 2017/05/26 17:18:40 Ditto comment about hoisting this handle creation
sivachandra 2017/05/26 20:27:34 Done.
2698 // TODO(hausner): Should we propagate this error? How? 2730 if (!fields.IsNull()) {
2699 // EnsureIsFinalized only returns an error object if there 2731 const intptr_t num_fields = fields.Length();
2700 // is no longjump base on the stack. 2732 for (intptr_t pos = 0; pos < num_fields; pos++) {
2701 continue; 2733 TokenPosition start;
2702 } 2734 TokenPosition end;
2703 functions = cls.functions(); 2735 field ^= fields.At(pos);
2704 if (!functions.IsNull()) { 2736 ASSERT(!field.IsNull());
2705 const intptr_t num_functions = functions.Length(); 2737 if (Parser::FieldHasFunctionLiteralInitializer(field, &start, &end)) {
2706 for (intptr_t pos = 0; pos < num_functions; pos++) { 2738 if ((start <= token_pos && token_pos <= end) ||
2707 function ^= functions.At(pos); 2739 (token_pos <= start && start <= last_token_pos)) {
2708 ASSERT(!function.IsNull()); 2740 return true;
2709 if (IsTokenPosWithinFunction(function, token_pos)) {
2710 // Closures and inner functions within a class method are not
2711 // present in the functions of a class. Hence, we can return
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 } 2741 }
2716 } 2742 }
2717 } 2743 }
2718 } 2744 }
2719 } 2745 }
2720 return Function::null(); 2746 return false;
2721 } 2747 }
2722 2748
2723 2749
2724 BreakpointLocation* Debugger::SetBreakpoint(const Script& script, 2750 BreakpointLocation* Debugger::SetBreakpoint(const Script& script,
2725 TokenPosition token_pos, 2751 TokenPosition token_pos,
2726 TokenPosition last_token_pos, 2752 TokenPosition last_token_pos,
2727 intptr_t requested_line, 2753 intptr_t requested_line,
2728 intptr_t requested_column) { 2754 intptr_t requested_column) {
2729 Function& func = Function::Handle(); 2755 Function& func = Function::Handle();
2730 func = FindBestFit(script, token_pos); 2756 if (!FindBestFit(script, token_pos, last_token_pos, &func)) {
2731 if (func.IsNull()) {
2732 return NULL; 2757 return NULL;
2733 } 2758 }
2734 // There may be more than one function object for a given function 2759 if (!func.IsNull()) {
2735 // in source code. There may be implicit closure functions, and 2760 // There may be more than one function object for a given function
2736 // there may be copies of mixin functions. Collect all compiled 2761 // in source code. There may be implicit closure functions, and
2737 // functions whose source code range matches exactly the best fit 2762 // there may be copies of mixin functions. Collect all compiled
2738 // function we found. 2763 // functions whose source code range matches exactly the best fit
2739 GrowableObjectArray& functions = 2764 // function we found.
2740 GrowableObjectArray::Handle(GrowableObjectArray::New()); 2765 GrowableObjectArray& functions =
2741 FindCompiledFunctions(script, func.token_pos(), func.end_token_pos(), 2766 GrowableObjectArray::Handle(GrowableObjectArray::New());
2742 &functions); 2767 FindCompiledFunctions(script, func.token_pos(), func.end_token_pos(),
2768 &functions);
2743 2769
2744 if (functions.Length() > 0) { 2770 if (functions.Length() > 0) {
2745 // One or more function object containing this breakpoint location 2771 // One or more function object containing this breakpoint location
2746 // have already been compiled. We can resolve the breakpoint now. 2772 // have already been compiled. We can resolve the breakpoint now.
2747 DeoptimizeWorld(); 2773 DeoptimizeWorld();
2748 func ^= functions.At(0); 2774 func ^= functions.At(0);
2749 TokenPosition breakpoint_pos = 2775 TokenPosition breakpoint_pos = ResolveBreakpointPos(
2750 ResolveBreakpointPos(func, token_pos, last_token_pos, requested_column); 2776 func, token_pos, last_token_pos, requested_column);
2751 if (breakpoint_pos.IsReal()) { 2777 if (breakpoint_pos.IsReal()) {
2752 BreakpointLocation* bpt = 2778 BreakpointLocation* bpt =
2753 GetBreakpointLocation(script, breakpoint_pos, requested_column); 2779 GetBreakpointLocation(script, breakpoint_pos, requested_column);
2754 if (bpt != NULL) { 2780 if (bpt != NULL) {
2755 // A source breakpoint for this location already exists. 2781 // A source breakpoint for this location already exists.
2782 return bpt;
2783 }
2784 bpt = new BreakpointLocation(script, token_pos, last_token_pos,
2785 requested_line, requested_column);
2786 bpt->SetResolved(func, breakpoint_pos);
2787 RegisterBreakpointLocation(bpt);
2788
2789 // Create code breakpoints for all compiled functions we found.
2790 const intptr_t num_functions = functions.Length();
2791 for (intptr_t i = 0; i < num_functions; i++) {
2792 func ^= functions.At(i);
2793 ASSERT(func.HasCode());
2794 MakeCodeBreakpointAt(func, bpt);
2795 }
2796 if (FLAG_verbose_debug) {
2797 intptr_t line_number;
2798 intptr_t column_number;
2799 script.GetTokenLocation(breakpoint_pos, &line_number, &column_number);
2800 OS::Print(
2801 "Resolved BP for "
2802 "function '%s' at line %" Pd " col %" Pd "\n",
2803 func.ToFullyQualifiedCString(), line_number, column_number);
2804 }
2756 return bpt; 2805 return bpt;
2757 } 2806 }
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 } 2807 }
2781 } 2808 }
2782 // There is no compiled function at this token position. 2809 // There is either an uncompiled function, or an uncompiled function literal
2783 // Register an unresolved breakpoint. 2810 // initializer of a field at |token_pos|. Hence, Register an unresolved
2784 if (FLAG_verbose_debug && !func.IsNull()) { 2811 // breakpoint.
2812 if (FLAG_verbose_debug) {
2785 intptr_t line_number; 2813 intptr_t line_number;
2786 intptr_t column_number; 2814 intptr_t column_number;
2787 script.GetTokenLocation(token_pos, &line_number, &column_number); 2815 script.GetTokenLocation(token_pos, &line_number, &column_number);
2788 OS::Print( 2816 if (func.IsNull()) {
2789 "Registering pending breakpoint for " 2817 OS::Print(
2790 "uncompiled function '%s' at line %" Pd " col %" Pd "\n", 2818 "Registering pending breakpoint for "
2791 func.ToFullyQualifiedCString(), line_number, column_number); 2819 "an uncompiled function literal at line %" Pd " col %" Pd "\n",
2820 line_number, column_number);
2821 } else {
2822 OS::Print(
2823 "Registering pending breakpoint for "
2824 "uncompiled function '%s' at line %" Pd " col %" Pd "\n",
2825 func.ToFullyQualifiedCString(), line_number, column_number);
2826 }
2792 } 2827 }
2793 BreakpointLocation* bpt = 2828 BreakpointLocation* bpt =
2794 GetBreakpointLocation(script, token_pos, requested_column); 2829 GetBreakpointLocation(script, token_pos, requested_column);
2795 if (bpt == NULL) { 2830 if (bpt == NULL) {
2796 bpt = new BreakpointLocation(script, token_pos, last_token_pos, 2831 bpt = new BreakpointLocation(script, token_pos, last_token_pos,
2797 requested_line, requested_column); 2832 requested_line, requested_column);
2798 RegisterBreakpointLocation(bpt); 2833 RegisterBreakpointLocation(bpt);
2799 } 2834 }
2800 return bpt; 2835 return bpt;
2801 } 2836 }
(...skipping 1639 matching lines...) Expand 10 before | Expand all | Expand 10 after
4441 4476
4442 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) { 4477 void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) {
4443 ASSERT(bpt->next() == NULL); 4478 ASSERT(bpt->next() == NULL);
4444 bpt->set_next(code_breakpoints_); 4479 bpt->set_next(code_breakpoints_);
4445 code_breakpoints_ = bpt; 4480 code_breakpoints_ = bpt;
4446 } 4481 }
4447 4482
4448 #endif // !PRODUCT 4483 #endif // !PRODUCT
4449 4484
4450 } // namespace dart 4485 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/debugger.h ('k') | runtime/vm/debugger_test.cc » ('j') | runtime/vm/debugger_test.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698