| Index: runtime/vm/debugger.cc
|
| diff --git a/runtime/vm/debugger.cc b/runtime/vm/debugger.cc
|
| index 96966d92ae1c690d3c5604c088c7a414dd6ea5cf..6c0a2d069274e26e82b17c12807065496abd41e4 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());
|
| // 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,
|
| +// - 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;
|
|
|