Index: runtime/vm/debugger.cc |
=================================================================== |
--- runtime/vm/debugger.cc (revision 31370) |
+++ runtime/vm/debugger.cc (working copy) |
@@ -50,17 +50,19 @@ |
SourceBreakpoint::SourceBreakpoint(intptr_t id, |
- const Function& func, |
+ const Script& script, |
intptr_t token_pos) |
: id_(id), |
- function_(func.raw()), |
+ script_(script.raw()), |
token_pos_(token_pos), |
- line_number_(-1), |
+ is_resolved_(false), |
is_enabled_(false), |
- next_(NULL) { |
- ASSERT(!func.IsNull()); |
- ASSERT((func.token_pos() <= token_pos_) && |
- (token_pos_ <= func.end_token_pos())); |
+ next_(NULL), |
+ function_(Function::null()), |
+ line_number_(-1) { |
+ ASSERT(id_ > 0); |
+ ASSERT(!script.IsNull()); |
+ ASSERT(token_pos_ >= 0); |
} |
@@ -76,51 +78,59 @@ |
} |
-RawScript* SourceBreakpoint::SourceCode() { |
- const Function& func = Function::Handle(function_); |
- return func.script(); |
+void SourceBreakpoint::SetResolved(const Function& func, intptr_t token_pos) { |
+ ASSERT(func.script() == script_); |
+ ASSERT((func.token_pos() <= token_pos) && |
+ (token_pos <= func.end_token_pos())); |
+ function_ = func.raw(); |
+ token_pos_ = token_pos; |
+ line_number_ = -1; // Recalcualte lazily. |
+ is_resolved_ = true; |
} |
+// TODO(hausner): Get rid of library parameter. A source breakpoint location |
+// does not imply a library, since the same source code can be included |
+// in more than one library, e.g. the text location of mixin functions. |
void SourceBreakpoint::GetCodeLocation( |
Library* lib, |
Script* script, |
- intptr_t* pos) const { |
- const Function& func = Function::Handle(function_); |
- const Class& cls = Class::Handle(func.origin()); |
- *lib = cls.library(); |
- *script = func.script(); |
- *pos = token_pos(); |
+ intptr_t* pos) { |
+ *script = this->script(); |
+ *pos = token_pos_; |
+ if (IsResolved()) { |
+ const Function& func = Function::Handle(function_); |
+ ASSERT(!func.IsNull()); |
+ const Class& cls = Class::Handle(func.origin()); |
+ *lib = cls.library(); |
+ } else { |
+ *lib = Library::null(); |
+ } |
} |
RawString* SourceBreakpoint::SourceUrl() { |
- const Script& script = Script::Handle(SourceCode()); |
- return script.url(); |
+ return Script::Handle(script()).url(); |
} |
intptr_t SourceBreakpoint::LineNumber() { |
// Compute line number lazily since it causes scanning of the script. |
if (line_number_ < 0) { |
- const Script& script = Script::Handle(SourceCode()); |
+ const Script& script = Script::Handle(this->script()); |
script.GetTokenLocation(token_pos_, &line_number_, NULL); |
} |
return line_number_; |
} |
-void SourceBreakpoint::set_function(const Function& func) { |
- function_ = func.raw(); |
-} |
- |
- |
void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) { |
+ visitor->VisitPointer(reinterpret_cast<RawObject**>(&script_)); |
visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_)); |
} |
-void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) const { |
+void SourceBreakpoint::PrintToJSONStream(JSONStream* stream) { |
Isolate* isolate = Isolate::Current(); |
JSONObject jsobj(stream); |
@@ -128,10 +138,8 @@ |
jsobj.AddProperty("id", id()); |
jsobj.AddProperty("enabled", IsEnabled()); |
+ jsobj.AddProperty("resolved", IsResolved()); |
- const Function& func = Function::Handle(function()); |
- jsobj.AddProperty("resolved", func.HasCode()); |
- |
Library& library = Library::Handle(isolate); |
Script& script = Script::Handle(isolate); |
intptr_t token_pos; |
@@ -139,7 +147,6 @@ |
{ |
JSONObject location(&jsobj, "location"); |
location.AddProperty("type", "Location"); |
- location.AddProperty("libId", library.index()); |
const String& url = String::Handle(script.url()); |
location.AddProperty("script", url.ToCString()); |
@@ -221,13 +228,30 @@ |
} |
+// Returns true if function contains the token position in the given script. |
+static bool FunctionContains(const Function& func, |
+ const Script& script, |
+ intptr_t token_pos) { |
+ if ((func.token_pos() <= token_pos) && (token_pos <= func.end_token_pos())) { |
+ // Check script equality second because it allocates |
+ // handles as a side effect. |
+ return func.script() == script.raw(); |
+ } |
+ return false; |
+} |
+ |
+ |
bool Debugger::HasBreakpoint(const Function& func) { |
if (!func.HasCode()) { |
// If the function is not compiled yet, just check whether there |
- // is a user-defined latent breakpoint. |
+ // is a user-defined breakpoint that falls into the token |
+ // range of the function. This may be a false positive: the breakpoint |
+ // might be inside a local closure. |
+ Script& script = Script::Handle(isolate_); |
SourceBreakpoint* sbpt = src_breakpoints_; |
while (sbpt != NULL) { |
- if (func.raw() == sbpt->function()) { |
+ script = sbpt->script(); |
+ if (FunctionContains(func, script, sbpt->token_pos())) { |
return true; |
} |
sbpt = sbpt->next_; |
@@ -1285,54 +1309,47 @@ |
} |
-// Given a function and a token position range, return the best fit |
-// token position to set a breakpoint. |
-// If multiple possible breakpoint positions are within the given range, |
-// the one with the lowest machine code address is picked. |
-// If no possible breakpoint location exists in the given range, the closest |
-// token position after the range is returned. |
+// Given a function and a token position, return the best fit |
+// token position to set a breakpoint. The best fit is the safe point |
+// with the lowest compiled code address that follows the requsted |
+// token position. |
intptr_t Debugger::ResolveBreakpointPos(const Function& func, |
- intptr_t first_token_pos, |
- intptr_t last_token_pos) { |
+ intptr_t requested_token_pos) { |
ASSERT(func.HasCode()); |
ASSERT(!func.HasOptimizedCode()); |
Code& code = Code::Handle(func.unoptimized_code()); |
ASSERT(!code.IsNull()); |
PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
intptr_t best_fit_index = -1; |
- intptr_t best_fit = INT_MAX; |
+ intptr_t best_fit_pos = INT_MAX; |
uword lowest_pc = kUwordMax; |
intptr_t lowest_pc_index = -1; |
for (intptr_t i = 0; i < desc.Length(); i++) { |
intptr_t desc_token_pos = desc.TokenPos(i); |
ASSERT(desc_token_pos >= 0); |
- if (desc_token_pos < first_token_pos) { |
- // This descriptor is before the given range. |
+ if (desc_token_pos < requested_token_pos) { |
+ // This descriptor is before the first acceptable token position. |
continue; |
} |
if (IsSafePoint(desc.DescriptorKind(i))) { |
- if ((desc_token_pos - first_token_pos) < best_fit) { |
- // So far, this descriptor has the closest token position to the |
- // beginning of the range. |
- best_fit = desc_token_pos - first_token_pos; |
- ASSERT(best_fit >= 0); |
+ if (desc_token_pos < best_fit_pos) { |
+ // So far, this descriptor has the lowest token position after |
+ // the first acceptable token position. |
+ best_fit_pos = desc_token_pos; |
best_fit_index = i; |
} |
- if ((first_token_pos <= desc_token_pos) && |
- (desc_token_pos <= last_token_pos) && |
- (desc.PC(i) < lowest_pc)) { |
- // This descriptor is within the token position range and so |
- // far has the lowest code address. |
+ if (desc.PC(i) < lowest_pc) { |
+ // This descriptor so far has the lowest code address. |
lowest_pc = desc.PC(i); |
lowest_pc_index = i; |
} |
} |
} |
if (lowest_pc_index >= 0) { |
- // We found the the pc descriptor within the given token range that |
- // has the lowest execution address. This is the first possible |
- // breakpoint on the line. We use this instead of the nearest |
- // PC descriptor measured in token index distance. |
+ // We found the pc descriptor that has the lowest execution address. |
+ // This is the first possible breakpoint after the requested token |
+ // position. We use this instead of the nearest PC descriptor |
+ // measured in token index distance. |
best_fit_index = lowest_pc_index; |
} |
if (best_fit_index >= 0) { |
@@ -1343,15 +1360,16 @@ |
void Debugger::MakeCodeBreakpointsAt(const Function& func, |
- intptr_t token_pos, |
SourceBreakpoint* bpt) { |
+ ASSERT((bpt != NULL) && bpt->IsResolved()); |
ASSERT(!func.HasOptimizedCode()); |
Code& code = Code::Handle(func.unoptimized_code()); |
ASSERT(!code.IsNull()); |
PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors()); |
for (intptr_t i = 0; i < desc.Length(); i++) { |
intptr_t desc_token_pos = desc.TokenPos(i); |
- if ((desc_token_pos == token_pos) && IsSafePoint(desc.DescriptorKind(i))) { |
+ if ((desc_token_pos == bpt->token_pos_) && |
+ IsSafePoint(desc.DescriptorKind(i))) { |
CodeBreakpoint* code_bpt = GetCodeBreakpoint(desc.PC(i)); |
if (code_bpt == NULL) { |
// No code breakpoint for this code exists; create one. |
@@ -1359,83 +1377,207 @@ |
RegisterCodeBreakpoint(code_bpt); |
} |
code_bpt->set_src_bpt(bpt); |
+ if (bpt->IsEnabled()) { |
+ code_bpt->Enable(); |
+ } |
} |
} |
} |
-SourceBreakpoint* Debugger::SetBreakpoint(const Function& target_function, |
- intptr_t first_token_pos, |
- intptr_t last_token_pos) { |
- if ((last_token_pos < target_function.token_pos()) || |
- (target_function.end_token_pos() < first_token_pos)) { |
- // The given token position is not within the target function. |
- return NULL; |
+void Debugger::FindEquivalentFunctions(const Script& script, |
+ intptr_t start_pos, |
+ intptr_t end_pos, |
+ GrowableObjectArray* function_list) { |
+ Class& cls = Class::Handle(isolate_); |
+ Array& functions = Array::Handle(isolate_); |
+ GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_); |
+ Function& function = Function::Handle(isolate_); |
+ |
+ const ClassTable& class_table = *isolate_->class_table(); |
+ const intptr_t num_classes = class_table.NumCids(); |
+ for (intptr_t i = 1; i < num_classes; i++) { |
+ if (class_table.HasValidClassAt(i)) { |
+ cls = class_table.At(i); |
+ // Note: we need to check the functions of this class even if |
+ // the class is defined in a differenct 'script'. There could |
+ // be mixin functions from the given script in this class. |
+ functions = cls.functions(); |
+ if (!functions.IsNull()) { |
+ const intptr_t num_functions = functions.Length(); |
+ for (intptr_t pos = 0; pos < num_functions; pos++) { |
+ function ^= functions.At(pos); |
+ ASSERT(!function.IsNull()); |
+ // Check token position first to avoid unnecessary calls |
+ // to script() which allocates handles. |
+ if ((function.token_pos() == start_pos) |
+ && (function.end_token_pos() == end_pos) |
+ && (function.script() == script.raw())) { |
+ function_list->Add(function); |
+ if (function.HasImplicitClosureFunction()) { |
+ function = function.ImplicitClosureFunction(); |
+ function_list->Add(function); |
+ } |
+ } |
+ } |
+ } |
+ closures = cls.closures(); |
+ if (!closures.IsNull()) { |
+ const intptr_t num_closures = closures.Length(); |
+ for (intptr_t pos = 0; pos < num_closures; pos++) { |
+ function ^= closures.At(pos); |
+ ASSERT(!function.IsNull()); |
+ if ((function.token_pos() == start_pos) |
+ && (function.end_token_pos() == end_pos) |
+ && (function.script() == script.raw())) { |
+ function_list->Add(function); |
+ if (function.HasImplicitClosureFunction()) { |
+ function = function.ImplicitClosureFunction(); |
+ function_list->Add(function); |
+ } |
+ } |
+ } |
+ } |
+ } |
} |
- intptr_t breakpoint_pos = -1; |
- Function& closure = Function::Handle(isolate_); |
- if (target_function.HasImplicitClosureFunction()) { |
- // There is a closurized version of this function. |
- closure = target_function.ImplicitClosureFunction(); |
+} |
+ |
+ |
+static void SelectBestFit(Function* best_fit, Function* func) { |
+ if (best_fit->IsNull()) { |
+ *best_fit = func->raw(); |
} |
- // Determine actual breakpoint location if the function or an |
- // implicit closure of the function has been compiled already. |
- if (target_function.HasCode()) { |
- DeoptimizeWorld(); |
- ASSERT(!target_function.HasOptimizedCode()); |
- breakpoint_pos = |
- ResolveBreakpointPos(target_function, first_token_pos, last_token_pos); |
- } else if (!closure.IsNull() && closure.HasCode()) { |
- DeoptimizeWorld(); |
- ASSERT(!closure.HasOptimizedCode()); |
- breakpoint_pos = |
- ResolveBreakpointPos(closure, first_token_pos, last_token_pos); |
- } else { |
- // This function has not been compiled yet. Set a pending |
- // breakpoint to be resolved later. |
- SourceBreakpoint* source_bpt = |
- GetSourceBreakpoint(target_function, first_token_pos); |
- if (source_bpt != NULL) { |
- // A pending source breakpoint for this uncompiled location |
- // already exists. |
- if (FLAG_verbose_debug) { |
- OS::Print("Pending breakpoint for uncompiled function" |
- " '%s' at line %" Pd " already exists\n", |
- target_function.ToFullyQualifiedCString(), |
- source_bpt->LineNumber()); |
+ if (func->token_pos() > best_fit->token_pos()) { |
+ if (func->end_token_pos() <= best_fit->end_token_pos()) { |
+ // func is contained within best_fit. Select it even if it |
+ // has not been compiled yet. |
+ *best_fit = func->raw(); |
+ if (func->HasImplicitClosureFunction()) { |
+ *func = func->ImplicitClosureFunction(); |
+ if (func->HasCode()) { |
+ *best_fit = func->raw(); |
+ } |
} |
- return source_bpt; |
} |
- source_bpt = |
- new SourceBreakpoint(nextId(), target_function, first_token_pos); |
- RegisterSourceBreakpoint(source_bpt); |
- if (FLAG_verbose_debug) { |
- OS::Print("Registering pending breakpoint for " |
- "uncompiled function '%s' at line %" Pd "\n", |
- target_function.ToFullyQualifiedCString(), |
- source_bpt->LineNumber()); |
+ } else if ((func->token_pos() == best_fit->token_pos()) |
+ && (func->end_token_pos() == best_fit->end_token_pos()) |
+ && func->HasCode()) { |
+ // If func covers the same range, it is considered a better fit if |
+ // it has been compiled. |
+ *best_fit = func->raw(); |
+ } |
+} |
+ |
+ |
+RawFunction* Debugger::FindBestFit(const Script& script, |
+ intptr_t token_pos) { |
+ Class& cls = Class::Handle(isolate_); |
+ Array& functions = Array::Handle(isolate_); |
+ GrowableObjectArray& closures = GrowableObjectArray::Handle(isolate_); |
+ Function& function = Function::Handle(isolate_); |
+ Function& best_fit = Function::Handle(isolate_); |
+ |
+ const ClassTable& class_table = *isolate_->class_table(); |
+ const intptr_t num_classes = class_table.NumCids(); |
+ for (intptr_t i = 1; i < num_classes; i++) { |
+ if (class_table.HasValidClassAt(i)) { |
+ cls = class_table.At(i); |
+ // Note: we need to check the functions of this class even if |
+ // the class is defined in a differenct 'script'. There could |
+ // be mixin functions from the given script in this class. |
+ functions = cls.functions(); |
+ if (!functions.IsNull()) { |
+ const intptr_t num_functions = functions.Length(); |
+ for (intptr_t pos = 0; pos < num_functions; pos++) { |
+ function ^= functions.At(pos); |
+ ASSERT(!function.IsNull()); |
+ if (FunctionContains(function, script, token_pos)) { |
+ SelectBestFit(&best_fit, &function); |
+ } |
+ } |
+ } |
+ |
+ closures = cls.closures(); |
+ if (!closures.IsNull()) { |
+ const intptr_t num_closures = closures.Length(); |
+ for (intptr_t pos = 0; pos < num_closures; pos++) { |
+ function ^= closures.At(pos); |
+ ASSERT(!function.IsNull()); |
+ if (FunctionContains(function, script, token_pos)) { |
+ SelectBestFit(&best_fit, &function); |
+ } |
+ } |
+ } |
} |
- source_bpt->Enable(); |
- return source_bpt; |
} |
- ASSERT(breakpoint_pos != -1); |
- SourceBreakpoint* source_bpt = |
- GetSourceBreakpoint(target_function, breakpoint_pos); |
- if (source_bpt != NULL) { |
- // A source breakpoint for this location already exists. |
- return source_bpt; |
+ return best_fit.raw(); |
+} |
+ |
+ |
+SourceBreakpoint* Debugger::SetBreakpoint(const Script& script, |
+ intptr_t token_pos) { |
+ Function& func = Function::Handle(isolate_); |
+ func = FindBestFit(script, token_pos); |
+ if (func.IsNull()) { |
+ return NULL; |
} |
- source_bpt = new SourceBreakpoint(nextId(), target_function, breakpoint_pos); |
- RegisterSourceBreakpoint(source_bpt); |
- if (target_function.HasCode()) { |
- MakeCodeBreakpointsAt(target_function, breakpoint_pos, source_bpt); |
+ if (!func.IsNull() && func.HasCode()) { |
+ // A function containing this breakpoint location has already |
+ // been compiled. We can resolve the breakpoint now. |
+ DeoptimizeWorld(); |
+ intptr_t breakpoint_pos = ResolveBreakpointPos(func, token_pos); |
+ if (breakpoint_pos >= 0) { |
+ SourceBreakpoint* bpt = GetSourceBreakpoint(script, breakpoint_pos); |
+ if (bpt != NULL) { |
+ // A source breakpoint for this location already exists. |
+ return bpt; |
+ } |
+ bpt = new SourceBreakpoint(nextId(), script, token_pos); |
+ bpt->SetResolved(func, breakpoint_pos); |
+ RegisterSourceBreakpoint(bpt); |
+ // There may be more than one function object for a given function |
+ // in source code. There may be implicit closure functions, and |
+ // there may be copies of mixin functions. Collect all functions whose |
+ // source code range matches exactly the best fit function we |
+ // found. |
+ GrowableObjectArray& functions = |
+ GrowableObjectArray::Handle(GrowableObjectArray::New()); |
+ FindEquivalentFunctions(script, |
+ func.token_pos(), |
+ func.end_token_pos(), |
+ &functions); |
+ const intptr_t num_functions = functions.Length(); |
+ // We must have found at least one function: func. |
+ ASSERT(num_functions > 0); |
+ // Create code breakpoints for all compiled functions we found. |
+ for (intptr_t i = 0; i < num_functions; i++) { |
+ func ^= functions.At(i); |
+ if (func.HasCode()) { |
+ MakeCodeBreakpointsAt(func, bpt); |
+ } |
+ } |
+ bpt->Enable(); |
+ SignalBpResolved(bpt); |
+ return bpt; |
+ } |
} |
- if (!closure.IsNull() && closure.HasCode()) { |
- MakeCodeBreakpointsAt(closure, breakpoint_pos, source_bpt); |
+ // There is no compiled function at this token position. |
+ // Register an unresolved breakpoint. |
+ if (FLAG_verbose_debug && !func.IsNull()) { |
+ intptr_t line_number; |
+ script.GetTokenLocation(token_pos, &line_number, NULL); |
+ OS::Print("Registering pending breakpoint for " |
+ "uncompiled function '%s' at line %" Pd "\n", |
+ func.ToFullyQualifiedCString(), |
+ line_number); |
} |
- source_bpt->Enable(); |
- SignalBpResolved(source_bpt); |
- return source_bpt; |
+ SourceBreakpoint* bpt = GetSourceBreakpoint(script, token_pos); |
+ if (bpt == NULL) { |
+ bpt = new SourceBreakpoint(nextId(), script, token_pos); |
+ } |
+ RegisterSourceBreakpoint(bpt); |
+ bpt->Enable(); |
+ return bpt; |
} |
@@ -1469,9 +1611,8 @@ |
SourceBreakpoint* Debugger::SetBreakpointAtEntry( |
const Function& target_function) { |
ASSERT(!target_function.IsNull()); |
- return SetBreakpoint(target_function, |
- target_function.token_pos(), |
- target_function.end_token_pos()); |
+ const Script& script = Script::Handle(target_function.script()); |
+ return SetBreakpoint(script, target_function.token_pos()); |
} |
@@ -1505,33 +1646,25 @@ |
} |
return NULL; |
} else if (last_token_idx < 0) { |
- // Line does not contain any tokens. first_token_index is the first |
- // token after the given line. We check whether that token is |
- // part of a function. |
- last_token_idx = first_token_idx; |
- } |
- |
- Function& func = Function::Handle(isolate_); |
- while (first_token_idx <= last_token_idx) { |
- func = lib.LookupFunctionInScript(script, first_token_idx); |
- if (!func.IsNull()) { |
- break; |
- } |
- first_token_idx++; |
- } |
- if (func.IsNull()) { |
+ // Line does not contain any tokens. |
if (FLAG_verbose_debug) { |
OS::Print("No executable code at line %" Pd " in '%s'\n", |
line_number, script_url.ToCString()); |
} |
return NULL; |
} |
- if (last_token_idx < 0) { |
- // The token at first_token_index is past the requested source line. |
- // Set the breakpoint at the closest position after that line. |
- last_token_idx = func.end_token_pos(); |
+ |
+ SourceBreakpoint* bpt = NULL; |
+ ASSERT(first_token_idx <= last_token_idx); |
+ while ((bpt == NULL) && (first_token_idx <= last_token_idx)) { |
+ bpt = SetBreakpoint(script, first_token_idx); |
+ first_token_idx++; |
} |
- return SetBreakpoint(func, first_token_idx, last_token_idx); |
+ if ((bpt == NULL) && FLAG_verbose_debug) { |
+ OS::Print("No executable code at line %" Pd " in '%s'\n", |
+ line_number, script_url.ToCString()); |
+ } |
+ return bpt; |
} |
@@ -1923,37 +2056,35 @@ |
} |
-static RawFunction* GetOriginalFunction(const Function& func) { |
- if (func.IsClosureFunction()) { |
- // Local functions (closures) in mixin functions do not have |
- // an original function they were cloned from. |
- // Note: this is a problem when a breakpoint is set in a mixed-in |
- // closure. The breakpoint is linked to the closure attached to the |
- // mixin application class, not the mixin class. When the same |
- // closure is compiled for another mixin application class, we |
- // don't find the breakpoint since we'll be looking in the |
- // mixin class. |
- return func.raw(); |
+// Return innermost closure contained in 'function' that contains |
+// the given token position. |
+RawFunction* Debugger::FindInnermostClosure(const Function& function, |
+ intptr_t token_pos) { |
+ const Class& owner = Class::Handle(isolate_, function.Owner()); |
+ if (owner.closures() == GrowableObjectArray::null()) { |
+ return Function::null(); |
} |
- const Class& origin_class = Class::Handle(func.origin()); |
- if (origin_class.is_patch()) { |
- // Patched functions from patch classes are removed from the |
- // function array of the patch class, so we will not find an |
- // original function object. |
- return func.raw(); |
- } |
- const Array& functions = Array::Handle(origin_class.functions()); |
- Object& orig_func = Object::Handle(); |
- for (intptr_t i = 0; i < functions.Length(); i++) { |
- orig_func = functions.At(i); |
- // Function names are symbols, so we can compare the raw pointers. |
- if (func.name() == Function::Cast(orig_func).name()) { |
- return Function::Cast(orig_func).raw(); |
+ // Note that we need to check that the closure is in the same |
+ // script as the outer function. We could have closures originating |
+ // in mixin classes whose source code is contained in a different |
+ // script. |
+ const Script& outer_origin = Script::Handle(isolate_, function.script()); |
+ const GrowableObjectArray& closures = |
+ GrowableObjectArray::Handle(isolate_, owner.closures()); |
+ const intptr_t num_closures = closures.Length(); |
+ Function& closure = Function::Handle(isolate_); |
+ Function& best_fit = Function::Handle(isolate_); |
+ for (intptr_t i = 0; i < num_closures; i++) { |
+ closure ^= closures.At(i); |
+ if ((function.token_pos() < closure.token_pos()) && |
+ (closure.end_token_pos() < function.end_token_pos()) && |
+ (closure.token_pos() <= token_pos) && |
+ (token_pos <= closure.end_token_pos()) && |
+ (closure.script() == outer_origin.raw())) { |
+ SelectBestFit(&best_fit, &closure); |
} |
} |
- // Uncommon case: not a mixin function. |
- ASSERT(!Class::Handle(func.Owner()).IsMixinApplication()); |
- return func.raw(); |
+ return best_fit.raw(); |
} |
@@ -1962,56 +2093,67 @@ |
// Return with minimal overhead if there are no breakpoints. |
return; |
} |
- Function& lookup_function = Function::Handle(func.raw()); |
- if (func.IsImplicitClosureFunction()) { |
- // If the newly compiled function is a an implicit closure (a closure that |
- // was formed by assigning a static or instance method to a function |
- // object), we need to use the closure's parent function to see whether |
- // there are any breakpoints. The parent function is the actual method on |
- // which the user sets breakpoints. |
- lookup_function = func.parent_function(); |
- ASSERT(!lookup_function.IsNull()); |
- } |
- if (lookup_function.Owner() != lookup_function.origin()) { |
- // This is a cloned function from a mixin class. If a breakpoint |
- // was set in this function, it is registered using the function |
- // of the origin class. |
- lookup_function = GetOriginalFunction(lookup_function); |
- } |
- SourceBreakpoint* bpt = src_breakpoints_; |
- while (bpt != NULL) { |
- if (lookup_function.raw() == bpt->function()) { |
- // Check if the breakpoint is inside a closure or local function |
- // within the newly compiled function. Note that we must look |
- // in the owner class of the function that just got compiled |
- // (i.e. func), not the owner class of the function we use to |
- // record the breakpoint (lookup_function). |
- Class& owner = Class::Handle(func.Owner()); |
- Function& closure = |
- Function::Handle(owner.LookupClosureFunction(bpt->token_pos())); |
- if (!closure.IsNull() && (closure.raw() != lookup_function.raw())) { |
+ // Iterate over all source breakpoints to check whether breakpoints |
+ // need to be set in the newly compiled function. |
+ Script& script = Script::Handle(isolate_); |
+ for (SourceBreakpoint* bpt = src_breakpoints_; |
+ bpt != NULL; |
+ bpt = bpt->next()) { |
+ script = bpt->script(); |
+ if (FunctionContains(func, script, bpt->token_pos())) { |
+ Function& inner_function = Function::Handle(isolate_); |
+ inner_function = FindInnermostClosure(func, bpt->token_pos()); |
+ if (!inner_function.IsNull()) { |
+ // The local function of a function we just compiled cannot |
+ // be compiled already. |
+ ASSERT(!inner_function.HasCode()); |
if (FLAG_verbose_debug) { |
- OS::Print("Resetting pending breakpoint to function %s\n", |
- closure.ToFullyQualifiedCString()); |
+ OS::Print("Pending BP remains unresolved in inner function '%s'\n", |
+ inner_function.ToFullyQualifiedCString()); |
} |
- bpt->set_function(closure); |
- } else { |
+ continue; |
+ } |
+ |
+ // TODO(hausner): What should we do if function is optimized? |
+ // Can we deoptimize the function? |
+ ASSERT(!func.HasOptimizedCode()); |
+ |
+ // There is no local function within func that contains the |
+ // breakpoint token position. Resolve the breakpoint if necessary |
+ // and set the code breakpoints. |
+ if (!bpt->IsResolved()) { |
+ // Resolve source breakpoint in the newly compiled function. |
+ intptr_t bp_pos = ResolveBreakpointPos(func, bpt->token_pos()); |
+ if (bp_pos < 0) { |
+ if (FLAG_verbose_debug) { |
+ OS::Print("Failed resolving breakpoint for function '%s'\n", |
+ String::Handle(func.name()).ToCString()); |
+ } |
+ continue; |
+ } |
+ intptr_t requested_pos = bpt->token_pos(); |
+ bpt->SetResolved(func, bp_pos); |
if (FLAG_verbose_debug) { |
- OS::Print("Enable pending breakpoint for function '%s'\n", |
- String::Handle(lookup_function.name()).ToCString()); |
+ OS::Print("Resolved BP %" Pd " to pos %" Pd ", line %" Pd ", " |
+ "function '%s' (requested pos %" Pd ")\n", |
+ bpt->id(), |
+ bpt->token_pos(), |
+ bpt->LineNumber(), |
+ func.ToFullyQualifiedCString(), |
+ requested_pos); |
} |
- const Script& script= Script::Handle(func.script()); |
- intptr_t first_pos, last_pos; |
- script.TokenRangeAtLine(bpt->LineNumber(), &first_pos, &last_pos); |
- intptr_t bp_pos = |
- ResolveBreakpointPos(func, bpt->token_pos(), last_pos); |
- bpt->set_token_pos(bp_pos); |
- MakeCodeBreakpointsAt(func, bp_pos, bpt); |
SignalBpResolved(bpt); |
} |
- bpt->Enable(); // Enables the code breakpoint as well. |
+ ASSERT(bpt->IsResolved()); |
+ if (FLAG_verbose_debug) { |
+ OS::Print("Setting breakpoint %" Pd " at line %" Pd " for %s '%s'\n", |
+ bpt->id(), |
+ bpt->LineNumber(), |
+ func.IsClosureFunction() ? "closure" : "function", |
+ String::Handle(func.name()).ToCString()); |
+ } |
+ MakeCodeBreakpointsAt(func, bpt); |
} |
- bpt = bpt->next(); |
} |
} |
@@ -2105,12 +2247,11 @@ |
} |
-SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func, |
+SourceBreakpoint* Debugger::GetSourceBreakpoint(const Script& script, |
intptr_t token_pos) { |
SourceBreakpoint* bpt = src_breakpoints_; |
while (bpt != NULL) { |
- if ((bpt->function() == func.raw()) && |
- (bpt->token_pos() == token_pos)) { |
+ if ((bpt->script_ == script.raw()) && (bpt->token_pos_ == token_pos)) { |
return bpt; |
} |
bpt = bpt->next(); |