Chromium Code Reviews| Index: runtime/vm/debugger.cc |
| diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc |
| index 0cd16d852896199b9843d5d2687776c0c9e4dfb1..8629277f9f918b9c43955caac23173597c46de53 100644 |
| --- a/runtime/vm/debugger.cc |
| +++ b/runtime/vm/debugger.cc |
| @@ -65,7 +65,9 @@ class RemoteObjectCache : public ZoneAllocated { |
| // Create an unresolved breakpoint in given token range and script. |
| BreakpointLocation::BreakpointLocation(const Script& script, |
| intptr_t token_pos, |
| - intptr_t end_token_pos) |
| + intptr_t end_token_pos, |
| + intptr_t requested_line_number, |
| + intptr_t requested_column_number) |
| : script_(script.raw()), |
| url_(script.url()), |
| token_pos_(token_pos), |
| @@ -73,15 +75,19 @@ BreakpointLocation::BreakpointLocation(const Script& script, |
| is_resolved_(false), |
| next_(NULL), |
| conditions_(NULL), |
| + requested_line_number_(requested_line_number), |
| + requested_column_number_(requested_column_number), |
| function_(Function::null()), |
| - line_number_(-1) { |
| + line_number_(-1), |
| + column_number_(-1) { |
| ASSERT(!script.IsNull()); |
| ASSERT(token_pos_ >= 0); |
| } |
| // Create a latent breakpoint at given url and line number. |
| BreakpointLocation::BreakpointLocation(const String& url, |
| - intptr_t line_number) |
| + intptr_t requested_line_number, |
| + intptr_t requested_column_number) |
| : script_(Script::null()), |
| url_(url.raw()), |
| token_pos_(-1), |
| @@ -89,9 +95,12 @@ BreakpointLocation::BreakpointLocation(const String& url, |
| is_resolved_(false), |
| next_(NULL), |
| conditions_(NULL), |
| + requested_line_number_(requested_line_number), |
| + requested_column_number_(requested_column_number), |
| function_(Function::null()), |
| - line_number_(line_number) { |
| - ASSERT(line_number_ >= 0); |
| + line_number_(-1), |
| + column_number_(-1) { |
| + ASSERT(requested_line_number_ >= 0); |
| } |
| @@ -119,7 +128,6 @@ void BreakpointLocation::SetResolved(const Function& func, intptr_t token_pos) { |
| function_ = func.raw(); |
| token_pos_ = token_pos; |
| end_token_pos_ = token_pos; |
| - line_number_ = -1; // Recalculate lazily. |
| is_resolved_ = true; |
| } |
| @@ -129,7 +137,7 @@ void BreakpointLocation::SetResolved(const Function& func, intptr_t token_pos) { |
| // in more than one library, e.g. the text location of mixin functions. |
| void BreakpointLocation::GetCodeLocation(Library* lib, |
| Script* script, |
| - intptr_t* pos) { |
| + intptr_t* pos) const { |
| if (IsLatent()) { |
| *lib = Library::null(); |
| *script = Script::null(); |
| @@ -150,8 +158,7 @@ void BreakpointLocation::GetCodeLocation(Library* lib, |
| intptr_t BreakpointLocation::LineNumber() { |
| - // Latent breakpoints must have a requested line number >= 0. |
| - ASSERT(!IsLatent() || line_number_ >= 0); |
| + ASSERT(IsResolved()); |
|
hausner
2015/09/04 22:45:16
So far, LineNumber() could be invoked before the b
turnidge
2015/09/08 18:41:02
Thanks. I have fixed Dart_GetBreakpointLine().
|
| // Compute line number lazily since it causes scanning of the script. |
| if (line_number_ < 0) { |
| const Script& script = Script::Handle(this->script()); |
| @@ -161,6 +168,17 @@ intptr_t BreakpointLocation::LineNumber() { |
| } |
| +intptr_t BreakpointLocation::ColumnNumber() { |
| + ASSERT(IsResolved()); |
| + // Compute column number lazily since it causes scanning of the script. |
| + if (column_number_ < 0) { |
| + const Script& script = Script::Handle(this->script()); |
| + script.GetTokenLocation(token_pos_, &line_number_, &column_number_); |
| + } |
| + return column_number_; |
| +} |
| + |
| + |
| void Breakpoint::set_bpt_location(BreakpointLocation* new_bpt_location) { |
| ASSERT(bpt_location_->IsLatent()); // Only reason to move. |
| bpt_location_ = new_bpt_location; |
| @@ -186,20 +204,17 @@ void BreakpointLocation::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
| void Breakpoint::PrintJSON(JSONStream* stream) { |
| - Isolate* isolate = Isolate::Current(); |
| - |
| JSONObject jsobj(stream); |
| jsobj.AddProperty("type", "Breakpoint"); |
| jsobj.AddFixedServiceId("breakpoints/%" Pd "", id()); |
| jsobj.AddProperty("breakpointNumber", id()); |
| jsobj.AddProperty("resolved", bpt_location_->IsResolved()); |
| - |
| - Library& library = Library::Handle(isolate); |
| - Script& script = Script::Handle(isolate); |
| - intptr_t token_pos; |
| - bpt_location_->GetCodeLocation(&library, &script, &token_pos); |
| - jsobj.AddLocation(script, token_pos); |
| + if (bpt_location_->IsResolved()) { |
| + jsobj.AddLocation(bpt_location_); |
| + } else { |
| + jsobj.AddUnresolvedLocation(bpt_location_); |
| + } |
| } |
| @@ -1602,13 +1617,66 @@ static intptr_t LastTokenOnLine(const TokenStream& tokens, intptr_t pos) { |
| } |
| -// Given a function and a token range, return the best fit |
| -// token position to set a breakpoint. The best fit is the safe point |
| -// in the line closest to the beginning of the token range, and within |
| -// that line, the safe point with the lowest compiled code address. |
| +// Returns the best fit token position for a breakpoint. |
| +// |
| +// Takes a range of tokens [requested_token_pos, last_token_pos] and |
| +// an optional column (requested_column). The range of tokens usually |
| +// represents one line of the program text, but can represent a larger |
| +// range on recursive calls. |
| +// |
| +// The best fit is found in two passes. |
| +// |
| +// The first pass finds a candidate token which: |
| +// |
| +// - is a safepoint, |
| +// - has the lowest column number compatible with the requested column |
| +// if a column has been specified, |
| +// and: |
| +// - has the lowest token position number which satisfies the above. |
| +// |
| +// When we consider a column number, we look for the closed token |
| +// which intersects the desired column. For example: |
| +// |
| +// 1 2 3 |
| +// 12345678901234567890 0 |
| +// |
| +// var x = function(function(y)); |
| +// ^ |
| +// |
| +// If we request a breakpoint at column 14, the lowest column number |
| +// compatible with that would for column 11 (beginning of the |
| +// 'function' token) in the example above. |
| +// |
| +// Once this candidate token from the first pass is found, we then |
| +// have a second pass which considers only those tokens on the same |
| +// line as the candidate token. |
| +// |
| +// The second pass finds a best fit token which: |
| +// |
| +// - is a safepoint, |
|
hausner
2015/09/04 22:45:16
nit: this is implied by the fact that you only loo
turnidge
2015/09/08 18:41:02
I'm not sure I really get the nit. The comment do
hausner
2015/09/08 19:38:34
Yes, you are right. I falsely thought the second p
|
| +// - has the same column number as the candidate token (perhaps |
| +// more than one token has the same column number), |
| +// and: |
| +// - has the lowest code address in the generated code. |
| +// |
| +// We prefer the lowest compiled code address, because this tends to |
| +// select the first subexpression on a line. For example in a line |
| +// with nested function calls f(g(x)), the call to g() will have a |
| +// lower compiled code address than the call to f(). |
| +// |
| +// If no best fit token can be found, the search is expanded, |
| +// searching through the rest of the current function by calling this |
| +// function recursively. |
| +// |
| +// TODO(turnidge): Given that we usually call this function with a |
| +// token range restricted to a single line, this could be a one-pass |
| +// algorithm, which would be simpler. I believe that it only needs |
| +// two passes to support the recursive try-the-whole-function case. |
| +// Rewrite this later, once there are more tests in place. |
| intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| intptr_t requested_token_pos, |
| - intptr_t last_token_pos) { |
| + intptr_t last_token_pos, |
| + intptr_t requested_column) { |
| ASSERT(func.HasCode()); |
| ASSERT(!func.HasOptimizedCode()); |
| @@ -1619,6 +1687,7 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| last_token_pos = func.end_token_pos(); |
| } |
| + Script& script = Script::Handle(func.script()); |
| Code& code = Code::Handle(func.unoptimized_code()); |
| ASSERT(!code.IsNull()); |
| PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
| @@ -1626,22 +1695,47 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| // First pass: find the safe point which is closest to the beginning |
| // of the given token range. |
| intptr_t best_fit_pos = INT_MAX; |
| + intptr_t best_column = INT_MAX; |
| PcDescriptors::Iterator iter(desc, kSafepointKind); |
| while (iter.MoveNext()) { |
| - const intptr_t desc_token_pos = iter.TokenPos(); |
| - if ((desc_token_pos != Scanner::kNoSourcePos) && |
| - (desc_token_pos < best_fit_pos) && |
| - (desc_token_pos >= requested_token_pos) && |
| - (desc_token_pos <= last_token_pos)) { |
| - best_fit_pos = desc_token_pos; |
| - } |
| - } |
| - // Second pass (if we found a safe point in the first pass): |
| - // For all token positions on the same line, select the one |
| - // with the lowest compiled code address. E.g., in a line with |
| - // the nested function calls f(g(x)), the call g() will have a lower |
| - // compiled code address but is not the lowest token position in the |
| - // line. |
| + const intptr_t pos = iter.TokenPos(); |
| + if ((pos == Scanner::kNoSourcePos) || |
| + (pos < requested_token_pos) || |
| + (pos > last_token_pos)) { |
| + // Token is not in the target range. |
| + continue; |
| + } |
| + |
| + intptr_t token_start_column = -1; |
| + if (requested_column >= 0) { |
| + intptr_t ignored = -1; |
| + intptr_t token_len = -1; |
| + // TODO(turnidge): GetTokenLocation is a very expensive |
| + // operation, and this code will blow up when we are setting |
| + // column breakpoints on, for example, a large, single-line |
| + // program. Consider rewriting this code so that it only scans |
| + // the program code once and caches the token positions and |
| + // lengths. |
| + script.GetTokenLocation(pos, &ignored, &token_start_column, &token_len); |
| + intptr_t token_end_column = token_start_column + token_len - 1; |
| + if ((token_end_column < requested_column) || |
| + (token_start_column > best_column)) { |
| + // Prefer the token with the lowest column number compatible |
| + // with the requested column. |
| + continue; |
| + } |
| + } |
| + |
| + // Prefer the lowest (first) token pos. |
| + if (pos < best_fit_pos) { |
| + best_fit_pos = pos; |
| + best_column = token_start_column; |
| + } |
| + } |
| + |
| + // Second pass (if we found a safe point in the first pass). Find |
| + // the token on the line which is at the best fit column (if column |
| + // was specified) and has the lowest code address. |
| if (best_fit_pos != INT_MAX) { |
| const Script& script = Script::Handle(func.script()); |
| const TokenStream& tokens = TokenStream::Handle(script.tokens()); |
| @@ -1651,9 +1745,26 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| PcDescriptors::Iterator iter(desc, kSafepointKind); |
| while (iter.MoveNext()) { |
| const intptr_t pos = iter.TokenPos(); |
| - if ((pos != Scanner::kNoSourcePos) && |
| - (begin_pos <= pos) && (pos <= end_of_line_pos) && |
| - (iter.PcOffset() < lowest_pc_offset)) { |
| + if ((pos == Scanner::kNoSourcePos) || |
| + (pos < begin_pos) || |
| + (pos > end_of_line_pos)) { |
| + // Token is not on same line as best fit. |
| + continue; |
| + } |
| + |
| + if (requested_column >= 0) { |
| + intptr_t ignored = -1; |
| + intptr_t token_start_column = -1; |
| + // We look for other tokens at the best column in case there |
| + // is more than one token at the same column offset. |
| + script.GetTokenLocation(pos, &ignored, &token_start_column); |
| + if (token_start_column != best_column) { |
| + continue; |
| + } |
| + } |
| + |
| + // Prefer the lowest pc offset. |
| + if (iter.PcOffset() < lowest_pc_offset) { |
| lowest_pc_offset = iter.PcOffset(); |
| best_fit_pos = pos; |
| } |
| @@ -1661,10 +1772,13 @@ intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
| return best_fit_pos; |
| } |
| - // We didn't find a safe point in the given token range. Try and find |
| - // a safe point in the remaining source code of the function. |
| + // We didn't find a safe point in the given token range. Try and |
| + // find a safe point in the remaining source code of the function. |
| + // Since we have moved to the next line of the function, we no |
| + // longer are requesting a specific column number. |
| if (last_token_pos < func.end_token_pos()) { |
| - return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos()); |
| + return ResolveBreakpointPos(func, last_token_pos, func.end_token_pos(), |
| + -1 /* no column */); |
| } |
| return -1; |
| } |
| @@ -1857,7 +1971,9 @@ RawFunction* Debugger::FindBestFit(const Script& script, |
| BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| intptr_t token_pos, |
| - intptr_t last_token_pos) { |
| + intptr_t last_token_pos, |
| + intptr_t requested_line, |
| + intptr_t requested_column) { |
| Function& func = Function::Handle(isolate_); |
| func = FindBestFit(script, token_pos); |
| if (func.IsNull()) { |
| @@ -1881,14 +1997,16 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| DeoptimizeWorld(); |
| func ^= functions.At(0); |
| intptr_t breakpoint_pos = |
| - ResolveBreakpointPos(func, token_pos, last_token_pos); |
| + ResolveBreakpointPos(func, token_pos, last_token_pos, requested_column); |
| if (breakpoint_pos >= 0) { |
| - BreakpointLocation* bpt = GetBreakpointLocation(script, breakpoint_pos); |
| + BreakpointLocation* bpt = |
| + GetBreakpointLocation(script, breakpoint_pos, requested_column); |
| if (bpt != NULL) { |
| // A source breakpoint for this location already exists. |
| return bpt; |
| } |
| - bpt = new BreakpointLocation(script, token_pos, last_token_pos); |
| + bpt = new BreakpointLocation(script, token_pos, last_token_pos, |
| + requested_line, requested_column); |
| bpt->SetResolved(func, breakpoint_pos); |
| RegisterBreakpointLocation(bpt); |
| @@ -1901,11 +2019,12 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| } |
| if (FLAG_verbose_debug) { |
| intptr_t line_number; |
| - script.GetTokenLocation(breakpoint_pos, &line_number, NULL); |
| + intptr_t column_number; |
| + script.GetTokenLocation(breakpoint_pos, &line_number, &column_number); |
| OS::Print("Resolved BP for " |
| - "function '%s' at line %" Pd "\n", |
| + "function '%s' at line %" Pd " col %" Pd "\n", |
| func.ToFullyQualifiedCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| return bpt; |
| } |
| @@ -1914,15 +2033,18 @@ BreakpointLocation* Debugger::SetBreakpoint(const Script& script, |
| // Register an unresolved breakpoint. |
| if (FLAG_verbose_debug && !func.IsNull()) { |
| intptr_t line_number; |
| - script.GetTokenLocation(token_pos, &line_number, NULL); |
| + intptr_t column_number; |
| + script.GetTokenLocation(token_pos, &line_number, &column_number); |
| OS::Print("Registering pending breakpoint for " |
| - "uncompiled function '%s' at line %" Pd "\n", |
| + "uncompiled function '%s' at line %" Pd " col %" Pd "\n", |
| func.ToFullyQualifiedCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| - BreakpointLocation* bpt = GetBreakpointLocation(script, token_pos); |
| + BreakpointLocation* bpt = |
| + GetBreakpointLocation(script, token_pos, requested_column); |
| if (bpt == NULL) { |
| - bpt = new BreakpointLocation(script, token_pos, last_token_pos); |
| + bpt = new BreakpointLocation(script, token_pos, last_token_pos, |
| + requested_line, requested_column); |
| RegisterBreakpointLocation(bpt); |
| } |
| return bpt; |
| @@ -1969,7 +2091,8 @@ Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function, |
| BreakpointLocation* bpt_location = |
| SetBreakpoint(script, |
| target_function.token_pos(), |
| - target_function.end_token_pos()); |
| + target_function.end_token_pos(), |
| + -1, -1 /* no requested line/col */); |
| if (single_shot) { |
| return bpt_location->AddSingleShot(this); |
| } else { |
| @@ -1986,7 +2109,8 @@ Breakpoint* Debugger::SetBreakpointAtActivation(const Instance& closure) { |
| const Script& script = Script::Handle(func.script()); |
| BreakpointLocation* bpt_location = SetBreakpoint(script, |
| func.token_pos(), |
| - func.end_token_pos()); |
| + func.end_token_pos(), |
| + -1, -1 /* no line/col */); |
| return bpt_location->AddPerClosure(this, closure); |
| } |
| @@ -2016,7 +2140,21 @@ Breakpoint* Debugger::BreakpointAtActivation(const Instance& closure) { |
| Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
| intptr_t line_number) { |
| - BreakpointLocation* loc = BreakpointLocationAtLine(script_url, line_number); |
| + BreakpointLocation* loc = |
| + BreakpointLocationAtLineCol(script_url, line_number, -1 /* no column */); |
| + if (loc != NULL) { |
| + return loc->AddRepeated(this); |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| +Breakpoint* Debugger::SetBreakpointAtLineCol(const String& script_url, |
| + intptr_t line_number, |
| + intptr_t column_number) { |
| + BreakpointLocation* loc = BreakpointLocationAtLineCol(script_url, |
| + line_number, |
| + column_number); |
| if (loc != NULL) { |
| return loc->AddRepeated(this); |
| } |
| @@ -2024,8 +2162,10 @@ Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url, |
| } |
| -BreakpointLocation* Debugger::BreakpointLocationAtLine(const String& script_url, |
| - intptr_t line_number) { |
| +BreakpointLocation* Debugger::BreakpointLocationAtLineCol( |
| + const String& script_url, |
| + intptr_t line_number, |
| + intptr_t column_number) { |
| Library& lib = Library::Handle(isolate_); |
| Script& script = Script::Handle(isolate_); |
| const GrowableObjectArray& libs = |
| @@ -2043,11 +2183,13 @@ BreakpointLocation* Debugger::BreakpointLocationAtLine(const String& script_url, |
| // No script found with given url. Create a latent breakpoint which |
| // will be set if the url is loaded later. |
| BreakpointLocation* latent_bpt = GetLatentBreakpoint(script_url, |
| - line_number); |
| + line_number, |
| + column_number); |
| if (FLAG_verbose_debug) { |
| - OS::Print("Set latent breakpoint in url '%s' at line %" Pd "\n", |
| + OS::Print("Set latent breakpoint in url '%s' at " |
| + "line %" Pd " col %" Pd "\n", |
| script_url.ToCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| return latent_bpt; |
| } |
| @@ -2079,7 +2221,8 @@ BreakpointLocation* Debugger::BreakpointLocationAtLine(const String& script_url, |
| BreakpointLocation* bpt = NULL; |
| ASSERT(first_token_idx <= last_token_idx); |
| while ((bpt == NULL) && (first_token_idx <= last_token_idx)) { |
| - bpt = SetBreakpoint(script, first_token_idx, last_token_idx); |
| + bpt = SetBreakpoint(script, first_token_idx, last_token_idx, |
| + line_number, column_number); |
| first_token_idx++; |
| } |
| if ((bpt == NULL) && FLAG_verbose_debug) { |
| @@ -2680,7 +2823,8 @@ void Debugger::NotifyCompilation(const Function& func) { |
| if (!loc->IsResolved()) { |
| // Resolve source breakpoint in the newly compiled function. |
| intptr_t bp_pos = |
| - ResolveBreakpointPos(func, loc->token_pos(), loc->end_token_pos()); |
| + ResolveBreakpointPos(func, loc->token_pos(), loc->end_token_pos(), |
| + loc->requested_column_number()); |
| if (bp_pos < 0) { |
| if (FLAG_verbose_debug) { |
| OS::Print("Failed resolving breakpoint for function '%s'\n", |
| @@ -2694,14 +2838,18 @@ void Debugger::NotifyCompilation(const Function& func) { |
| Breakpoint* bpt = loc->breakpoints(); |
| while (bpt != NULL) { |
| if (FLAG_verbose_debug) { |
| - OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", " |
| - "function '%s' (requested range %" Pd "-%" Pd ")\n", |
| + OS::Print("Resolved BP %" Pd " to pos %" Pd ", " |
| + "line %" Pd " col %" Pd ", " |
| + "function '%s' (requested range %" Pd "-%" Pd ", " |
| + "requested col %" Pd ")\n", |
| bpt->id(), |
| loc->token_pos(), |
| loc->LineNumber(), |
| + loc->ColumnNumber(), |
| func.ToFullyQualifiedCString(), |
| requested_pos, |
| - requested_end_pos); |
| + requested_end_pos, |
| + loc->requested_column_number()); |
| } |
| SignalBpResolved(bpt); |
| SendServiceBreakpointEvent(ServiceEvent::kBreakpointResolved, bpt); |
| @@ -2712,9 +2860,11 @@ void Debugger::NotifyCompilation(const Function& func) { |
| if (FLAG_verbose_debug) { |
| Breakpoint* bpt = loc->breakpoints(); |
| while (bpt != NULL) { |
| - OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n", |
| + OS::Print("Setting breakpoint %" Pd " at line %" Pd " col %" Pd "" |
| + " for %s '%s'\n", |
| bpt->id(), |
| loc->LineNumber(), |
| + loc->ColumnNumber(), |
| func.IsClosureFunction() ? "closure" : "function", |
| String::Handle(func.name()).ToCString()); |
| bpt = bpt->next(); |
| @@ -2757,7 +2907,8 @@ void Debugger::NotifyDoneLoading() { |
| } |
| // Now find the token range at the requested line and make a |
| // new unresolved source breakpoint. |
| - intptr_t line_number = matched_loc->LineNumber(); |
| + intptr_t line_number = matched_loc->requested_line_number(); |
| + intptr_t column_number = matched_loc->requested_column_number(); |
| ASSERT(line_number >= 0); |
| intptr_t first_token_pos, last_token_pos; |
| script.TokenRangeAtLine(line_number, &first_token_pos, &last_token_pos); |
| @@ -2784,15 +2935,15 @@ void Debugger::NotifyDoneLoading() { |
| // If there is one, assert in debug build but silently drop |
| // the latent breakpoint in release build. |
| BreakpointLocation* existing_loc = |
| - GetBreakpointLocation(script, first_token_pos); |
| + GetBreakpointLocation(script, first_token_pos, column_number); |
| ASSERT(existing_loc == NULL); |
| if (existing_loc == NULL) { |
| // Create and register a new source breakpoint for the |
| // latent breakpoint. |
| BreakpointLocation* unresolved_loc = |
| new BreakpointLocation(script, |
| - first_token_pos, |
| - last_token_pos); |
| + first_token_pos, last_token_pos, |
| + line_number, column_number); |
| RegisterBreakpointLocation(unresolved_loc); |
| // Move breakpoints over. |
| @@ -2803,10 +2954,10 @@ void Debugger::NotifyDoneLoading() { |
| bpt->set_bpt_location(unresolved_loc); |
| if (FLAG_verbose_debug) { |
| OS::Print("Converted latent breakpoint " |
| - "%" Pd " in '%s' at line %" Pd "\n", |
| + "%" Pd " in '%s' at line %" Pd " col %" Pd "\n", |
| bpt->id(), |
| url.ToCString(), |
| - line_number); |
| + line_number, column_number); |
| } |
| bpt = bpt->next(); |
| } |
| @@ -2970,10 +3121,13 @@ void Debugger::RemoveInternalBreakpoints() { |
| BreakpointLocation* Debugger::GetBreakpointLocation(const Script& script, |
| - intptr_t token_pos) { |
| + intptr_t token_pos, |
| + intptr_t requested_column) { |
| BreakpointLocation* bpt = breakpoint_locations_; |
| while (bpt != NULL) { |
| - if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) { |
| + if ((bpt->script_ == script.raw()) && |
| + (bpt->token_pos_ == token_pos) && |
| + (bpt->requested_column_number_ == requested_column)) { |
| return bpt; |
| } |
| bpt = bpt->next(); |
| @@ -2999,18 +3153,21 @@ Breakpoint* Debugger::GetBreakpointById(intptr_t id) { |
| BreakpointLocation* Debugger::GetLatentBreakpoint(const String& url, |
| - intptr_t line) { |
| + intptr_t line, |
| + intptr_t column) { |
| BreakpointLocation* bpt = latent_locations_; |
| String& bpt_url = String::Handle(isolate_); |
| while (bpt != NULL) { |
| bpt_url = bpt->url(); |
| - if (bpt_url.Equals(url) && (bpt->LineNumber() == line)) { |
| + if (bpt_url.Equals(url) && |
| + (bpt->requested_line_number() == line) && |
| + (bpt->requested_column_number() == column)) { |
| return bpt; |
| } |
| bpt = bpt->next(); |
| } |
| - // No breakpint for this url and line requested. Allocate new one. |
| - bpt = new BreakpointLocation(url, line); |
| + // No breakpoint for this location requested. Allocate new one. |
| + bpt = new BreakpointLocation(url, line, column); |
| bpt->set_next(latent_locations_); |
| latent_locations_ = bpt; |
| return bpt; |