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