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; |