| Index: runtime/vm/debugger.cc
 | 
| ===================================================================
 | 
| --- runtime/vm/debugger.cc	(revision 4881)
 | 
| +++ runtime/vm/debugger.cc	(working copy)
 | 
| @@ -25,57 +25,65 @@
 | 
|  static const bool verbose = false;
 | 
|  
 | 
|  
 | 
| -Breakpoint::Breakpoint(const Function& func, intptr_t pc_desc_index)
 | 
| +SourceBreakpoint::SourceBreakpoint(const Function& func, intptr_t token_index)
 | 
|      : function_(func.raw()),
 | 
| -      pc_desc_index_(pc_desc_index),
 | 
| -      pc_(0),
 | 
| +      token_index_(token_index),
 | 
|        line_number_(-1),
 | 
| -      is_temporary_(false),
 | 
| -      is_patched_(false),
 | 
| +      is_enabled_(false),
 | 
|        next_(NULL) {
 | 
| -  ASSERT(!func.HasOptimizedCode());
 | 
| -  Code& code = Code::Handle(func.unoptimized_code());
 | 
| -  ASSERT(!code.IsNull());  // Function must be compiled.
 | 
| -  PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
 | 
| -  ASSERT(pc_desc_index < desc.Length());
 | 
| -  this->token_index_ = desc.TokenIndex(pc_desc_index);
 | 
| -  ASSERT(this->token_index_ > 0);
 | 
| -  this->pc_ = desc.PC(pc_desc_index);
 | 
| -  ASSERT(this->pc_ != 0);
 | 
| -  this->breakpoint_kind_ = desc.DescriptorKind(pc_desc_index);
 | 
| +  ASSERT(!func.IsNull());
 | 
| +  ASSERT((func.token_index() <= token_index_) &&
 | 
| +         (token_index_ < func.end_token_index()));
 | 
|  }
 | 
|  
 | 
|  
 | 
| -RawScript* Breakpoint::SourceCode() {
 | 
| -  const Function& func = Function::Handle(this->function_);
 | 
| +void SourceBreakpoint::Enable() {
 | 
| +  is_enabled_ = true;
 | 
| +  Isolate::Current()->debugger()->SyncBreakpoint(this);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +void SourceBreakpoint::Disable() {
 | 
| +  is_enabled_ = false;
 | 
| +  Isolate::Current()->debugger()->SyncBreakpoint(this);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +RawScript* SourceBreakpoint::SourceCode() {
 | 
| +  const Function& func = Function::Handle(function_);
 | 
|    const Class& cls = Class::Handle(func.owner());
 | 
|    return cls.script();
 | 
|  }
 | 
|  
 | 
|  
 | 
| -RawString* Breakpoint::SourceUrl() {
 | 
| -  const Script& script = Script::Handle(this->SourceCode());
 | 
| +RawString* SourceBreakpoint::SourceUrl() {
 | 
| +  const Script& script = Script::Handle(SourceCode());
 | 
|    return script.url();
 | 
|  }
 | 
|  
 | 
|  
 | 
| -intptr_t Breakpoint::LineNumber() {
 | 
| +intptr_t SourceBreakpoint::LineNumber() {
 | 
|    // Compute line number lazily since it causes scanning of the script.
 | 
| -  if (this->line_number_ < 0) {
 | 
| -    const Script& script = Script::Handle(this->SourceCode());
 | 
| +  if (line_number_ < 0) {
 | 
| +    const Script& script = Script::Handle(SourceCode());
 | 
|      intptr_t ignore_column;
 | 
| -    script.GetTokenLocation(this->token_index_,
 | 
| -                            &this->line_number_, &ignore_column);
 | 
| +    script.GetTokenLocation(token_index_, &line_number_, &ignore_column);
 | 
|    }
 | 
| -  return this->line_number_;
 | 
| +  return line_number_;
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void Breakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
 | 
| +void SourceBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
 | 
|    visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
 | 
|  }
 | 
|  
 | 
|  
 | 
| +
 | 
| +void CodeBreakpoint::VisitObjectPointers(ObjectPointerVisitor* visitor) {
 | 
| +  visitor->VisitPointer(reinterpret_cast<RawObject**>(&function_));
 | 
| +}
 | 
| +
 | 
| +
 | 
|  ActivationFrame::ActivationFrame(uword pc, uword fp, uword sp)
 | 
|      : pc_(pc), fp_(fp), sp_(sp),
 | 
|        function_(Function::ZoneHandle()),
 | 
| @@ -281,12 +289,63 @@
 | 
|  
 | 
|  
 | 
|  void StackTrace::AddActivation(ActivationFrame* frame) {
 | 
| -  this->trace_.Add(frame);
 | 
| +  trace_.Add(frame);
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void Breakpoint::PatchCode() {
 | 
| -  ASSERT(!is_patched_);
 | 
| +CodeBreakpoint::CodeBreakpoint(const Function& func, intptr_t pc_desc_index)
 | 
| +    : function_(func.raw()),
 | 
| +      pc_desc_index_(pc_desc_index),
 | 
| +      pc_(0),
 | 
| +      line_number_(-1),
 | 
| +      is_enabled_(false),
 | 
| +      src_bpt_(NULL),
 | 
| +      next_(NULL) {
 | 
| +  ASSERT(!func.HasOptimizedCode());
 | 
| +  Code& code = Code::Handle(func.unoptimized_code());
 | 
| +  ASSERT(!code.IsNull());  // Function must be compiled.
 | 
| +  PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
 | 
| +  ASSERT(pc_desc_index < desc.Length());
 | 
| +  token_index_ = desc.TokenIndex(pc_desc_index);
 | 
| +  ASSERT(token_index_ > 0);
 | 
| +  pc_ = desc.PC(pc_desc_index);
 | 
| +  ASSERT(pc_ != 0);
 | 
| +  breakpoint_kind_ = desc.DescriptorKind(pc_desc_index);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +CodeBreakpoint::~CodeBreakpoint() {
 | 
| +  // Make sure we don't leave patched code behind.
 | 
| +  ASSERT(!IsEnabled());
 | 
| +}
 | 
| +
 | 
| +
 | 
| +RawScript* CodeBreakpoint::SourceCode() {
 | 
| +  const Function& func = Function::Handle(function_);
 | 
| +  const Class& cls = Class::Handle(func.owner());
 | 
| +  return cls.script();
 | 
| +}
 | 
| +
 | 
| +
 | 
| +RawString* CodeBreakpoint::SourceUrl() {
 | 
| +  const Script& script = Script::Handle(SourceCode());
 | 
| +  return script.url();
 | 
| +}
 | 
| +
 | 
| +
 | 
| +intptr_t CodeBreakpoint::LineNumber() {
 | 
| +  // Compute line number lazily since it causes scanning of the script.
 | 
| +  if (line_number_ < 0) {
 | 
| +    const Script& script = Script::Handle(SourceCode());
 | 
| +    intptr_t ignore_column;
 | 
| +    script.GetTokenLocation(token_index_, &line_number_, &ignore_column);
 | 
| +  }
 | 
| +  return line_number_;
 | 
| +}
 | 
| +
 | 
| +
 | 
| +void CodeBreakpoint::PatchCode() {
 | 
| +  ASSERT(!is_enabled_);
 | 
|    switch (breakpoint_kind_) {
 | 
|      case PcDescriptors::kIcCall: {
 | 
|        int num_args, num_named_args;
 | 
| @@ -310,12 +369,12 @@
 | 
|      default:
 | 
|        UNREACHABLE();
 | 
|    }
 | 
| -  is_patched_ = true;
 | 
| +  is_enabled_ = true;
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void Breakpoint::RestoreCode() {
 | 
| -  ASSERT(is_patched_);
 | 
| +void CodeBreakpoint::RestoreCode() {
 | 
| +  ASSERT(is_enabled_);
 | 
|    switch (breakpoint_kind_) {
 | 
|      case PcDescriptors::kIcCall:
 | 
|        CodePatcher::PatchInstanceCallAt(pc_, saved_bytes_.target_address_);
 | 
| @@ -329,22 +388,23 @@
 | 
|      default:
 | 
|        UNREACHABLE();
 | 
|    }
 | 
| -  is_patched_ = false;
 | 
| +  is_enabled_ = false;
 | 
|  }
 | 
|  
 | 
| -void Breakpoint::SetActive(bool value) {
 | 
| -  if (value && !is_patched_) {
 | 
| +
 | 
| +void CodeBreakpoint::Enable() {
 | 
| +  if (!is_enabled_) {
 | 
|      PatchCode();
 | 
| -    return;
 | 
|    }
 | 
| -  if (!value && is_patched_) {
 | 
| -    RestoreCode();
 | 
| -  }
 | 
| +  ASSERT(is_enabled_);
 | 
|  }
 | 
|  
 | 
|  
 | 
| -bool Breakpoint::IsActive() {
 | 
| -  return is_patched_;
 | 
| +void CodeBreakpoint::Disable() {
 | 
| +  if (is_enabled_) {
 | 
| +    RestoreCode();
 | 
| +  }
 | 
| +  ASSERT(!is_enabled_);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -352,23 +412,30 @@
 | 
|      : isolate_(NULL),
 | 
|        initialized_(false),
 | 
|        bp_handler_(NULL),
 | 
| -      breakpoints_(NULL),
 | 
| +      src_breakpoints_(NULL),
 | 
| +      code_breakpoints_(NULL),
 | 
|        resume_action_(kContinue) {
 | 
|  }
 | 
|  
 | 
|  
 | 
|  Debugger::~Debugger() {
 | 
| -  ASSERT(breakpoints_ == NULL);
 | 
| +  ASSERT(src_breakpoints_ == NULL);
 | 
| +  ASSERT(code_breakpoints_ == NULL);
 | 
|  }
 | 
|  
 | 
|  
 | 
|  void Debugger::Shutdown() {
 | 
| -  while (breakpoints_ != NULL) {
 | 
| -    Breakpoint* bpt = breakpoints_;
 | 
| -    breakpoints_ = breakpoints_->next();
 | 
| -    UnsetBreakpoint(bpt);
 | 
| +  while (src_breakpoints_ != NULL) {
 | 
| +    SourceBreakpoint* bpt = src_breakpoints_;
 | 
| +    src_breakpoints_ = src_breakpoints_->next();
 | 
|      delete bpt;
 | 
|    }
 | 
| +  while (code_breakpoints_ != NULL) {
 | 
| +    CodeBreakpoint* bpt = code_breakpoints_;
 | 
| +    code_breakpoints_ = code_breakpoints_->next();
 | 
| +    bpt->Disable();
 | 
| +    delete bpt;
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -378,7 +445,7 @@
 | 
|    // This is probably not conservative enough (we could set the first
 | 
|    // breakpoint after optimized code has already been produced).
 | 
|    // Long-term, we need to be able to de-optimize code.
 | 
| -  return breakpoints_ != NULL;
 | 
| +  return (src_breakpoints_ != NULL) || (code_breakpoints_ != NULL);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -430,7 +497,7 @@
 | 
|    ASSERT(!code.IsNull());
 | 
|    PcDescriptors& desc = PcDescriptors::Handle(code.pc_descriptors());
 | 
|    for (int i = 0; i < desc.Length(); i++) {
 | 
| -    Breakpoint* bpt = GetBreakpoint(desc.PC(i));
 | 
| +    CodeBreakpoint* bpt = GetCodeBreakpoint(desc.PC(i));
 | 
|      if (bpt != NULL) {
 | 
|        // There is already a breakpoint for this address. Leave it alone.
 | 
|        continue;
 | 
| @@ -439,57 +506,50 @@
 | 
|      if ((kind == PcDescriptors::kIcCall) ||
 | 
|          (kind == PcDescriptors::kFuncCall) ||
 | 
|          (kind == PcDescriptors::kReturn)) {
 | 
| -      bpt = new Breakpoint(target_function, i);
 | 
| -      bpt->set_temporary(true);
 | 
| -      bpt->PatchCode();
 | 
| -      RegisterBreakpoint(bpt);
 | 
| +      bpt = new CodeBreakpoint(target_function, i);
 | 
| +      RegisterCodeBreakpoint(bpt);
 | 
| +      bpt->Enable();
 | 
|      }
 | 
|    }
 | 
|  }
 | 
|  
 | 
|  
 | 
| -// TODO(hausner): Distinguish between newly created breakpoints and
 | 
| -// returning a breakpoint that already exists?
 | 
| -Breakpoint* Debugger::SetBreakpoint(const Function& target_function,
 | 
| -                                    intptr_t token_index,
 | 
| -                                    Error* error) {
 | 
| -  if ((token_index < target_function.token_index()) ||
 | 
| -      (target_function.end_token_index() <= token_index)) {
 | 
| -    // The given token position is not within the target function.
 | 
| -    return NULL;
 | 
| -  }
 | 
| -  if (!target_function.HasCode()) {
 | 
| -    *error = Compiler::CompileFunction(target_function);
 | 
| -    if (!error->IsNull()) {
 | 
| -      return NULL;
 | 
| -    }
 | 
| -  }
 | 
| -  ASSERT(!target_function.HasOptimizedCode());
 | 
| -  Code& code = Code::Handle(target_function.unoptimized_code());
 | 
| +CodeBreakpoint* Debugger::MakeCodeBreakpoint(SourceBreakpoint* src_bpt) {
 | 
| +  Function& func = Function::Handle(src_bpt->function());
 | 
| +  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 requested_token_index = src_bpt->token_index();
 | 
|    for (int i = 0; i < desc.Length(); i++) {
 | 
| -    if (desc.TokenIndex(i) < token_index) {
 | 
| +    if (desc.TokenIndex(i) < requested_token_index) {
 | 
|        continue;
 | 
|      }
 | 
| -    Breakpoint* bpt = GetBreakpoint(desc.PC(i));
 | 
| +    CodeBreakpoint* bpt = GetCodeBreakpoint(desc.PC(i));
 | 
| +    // We should only ever have one code breakpoint at the same address.
 | 
| +    // If we find an existing breakpoint, it must be an internal one which
 | 
| +    // is used for stepping.
 | 
|      if (bpt != NULL) {
 | 
| -      // Found existing breakpoint.
 | 
| +      ASSERT(bpt->src_bpt() == NULL);
 | 
| +      bpt->set_src_bpt(src_bpt);
 | 
|        return bpt;
 | 
|      }
 | 
| +
 | 
|      PcDescriptors::Kind kind = desc.DescriptorKind(i);
 | 
|      if ((kind == PcDescriptors::kIcCall) ||
 | 
|          (kind == PcDescriptors::kFuncCall) ||
 | 
|          (kind == PcDescriptors::kReturn)) {
 | 
| -      bpt = new Breakpoint(target_function, i);
 | 
| -      bpt->PatchCode();
 | 
| -      RegisterBreakpoint(bpt);
 | 
| +      bpt = new CodeBreakpoint(func, i);
 | 
| +      bpt->set_src_bpt(src_bpt);
 | 
|        if (verbose) {
 | 
| -        OS::Print("Setting breakpoint at '%s' line %d  (PC %p)\n",
 | 
| +        OS::Print("Setting breakpoint in function '%s' (%s:%d)  (PC %p)\n",
 | 
| +                  String::Handle(func.name()).ToCString(),
 | 
|                    String::Handle(bpt->SourceUrl()).ToCString(),
 | 
|                    bpt->LineNumber(),
 | 
|                    bpt->pc());
 | 
|        }
 | 
| +      RegisterCodeBreakpoint(bpt);
 | 
|        return bpt;
 | 
|      }
 | 
|    }
 | 
| @@ -497,21 +557,67 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void Debugger::UnsetBreakpoint(Breakpoint* bpt) {
 | 
| -  bpt->SetActive(false);
 | 
| +SourceBreakpoint* Debugger::SetBreakpoint(const Function& target_function,
 | 
| +                                          intptr_t token_index) {
 | 
| +  if ((token_index < target_function.token_index()) ||
 | 
| +      (target_function.end_token_index() <= token_index)) {
 | 
| +    // The given token position is not within the target function.
 | 
| +    return NULL;
 | 
| +  }
 | 
| +  SourceBreakpoint* bpt = GetSourceBreakpoint(target_function, token_index);
 | 
| +  if (bpt != NULL) {
 | 
| +    // A breakpoint for this location already exists, return it.
 | 
| +    return bpt;
 | 
| +  }
 | 
| +  bpt = new SourceBreakpoint(target_function, token_index);
 | 
| +  RegisterSourceBreakpoint(bpt);
 | 
| +  if (verbose && !target_function.HasCode()) {
 | 
| +    OS::Print("Registering breakpoint for uncompiled function '%s'"
 | 
| +              " (%s:%d)\n",
 | 
| +              String::Handle(target_function.name()).ToCString(),
 | 
| +              String::Handle(bpt->SourceUrl()).ToCString(),
 | 
| +              bpt->LineNumber());
 | 
| +  }
 | 
| +
 | 
| +  if (target_function.HasCode()) {
 | 
| +    CodeBreakpoint* cbpt = MakeCodeBreakpoint(bpt);
 | 
| +    if (cbpt == NULL) {
 | 
| +      if (verbose) {
 | 
| +        OS::Print("Failed to set breakpoint at '%s' line %d\n",
 | 
| +                  String::Handle(bpt->SourceUrl()).ToCString(),
 | 
| +                  bpt->LineNumber());
 | 
| +      }
 | 
| +    }
 | 
| +  }
 | 
| +  bpt->Enable();
 | 
| +  return bpt;
 | 
|  }
 | 
|  
 | 
|  
 | 
| -Breakpoint* Debugger::SetBreakpointAtEntry(const Function& target_function,
 | 
| -                                           Error* error) {
 | 
| +void Debugger::SyncBreakpoint(SourceBreakpoint* bpt) {
 | 
| +  CodeBreakpoint* cbpt = code_breakpoints_;
 | 
| +  while (cbpt != NULL) {
 | 
| +    if (bpt == cbpt->src_bpt()) {
 | 
| +      if (bpt->IsEnabled()) {
 | 
| +        cbpt->Enable();
 | 
| +      } else {
 | 
| +        cbpt->Disable();
 | 
| +      }
 | 
| +    }
 | 
| +    cbpt = cbpt->next();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +SourceBreakpoint* Debugger::SetBreakpointAtEntry(
 | 
| +      const Function& target_function) {
 | 
|    ASSERT(!target_function.IsNull());
 | 
| -  return SetBreakpoint(target_function, target_function.token_index(), error);
 | 
| +  return SetBreakpoint(target_function, target_function.token_index());
 | 
|  }
 | 
|  
 | 
|  
 | 
| -Breakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
 | 
| -                                          intptr_t line_number,
 | 
| -                                          Error* error) {
 | 
| +SourceBreakpoint* Debugger::SetBreakpointAtLine(const String& script_url,
 | 
| +                                          intptr_t line_number) {
 | 
|    Library& lib = Library::Handle();
 | 
|    Script& script = Script::Handle();
 | 
|    lib = isolate_->object_store()->registered_libraries();
 | 
| @@ -523,19 +629,31 @@
 | 
|      lib = lib.next_registered();
 | 
|    }
 | 
|    if (script.IsNull()) {
 | 
| +    if (verbose) {
 | 
| +      OS::Print("Failed to find script with url '%s'\n",
 | 
| +                script_url.ToCString());
 | 
| +    }
 | 
|      return NULL;
 | 
|    }
 | 
|    intptr_t token_index_at_line = script.TokenIndexAtLine(line_number);
 | 
|    if (token_index_at_line < 0) {
 | 
|      // Script does not contain the given line number.
 | 
| +    if (verbose) {
 | 
| +      OS::Print("Script '%s' does not contain line number %d\n",
 | 
| +                script_url.ToCString(), line_number);
 | 
| +    }
 | 
|      return NULL;
 | 
|    }
 | 
|    const Function& func =
 | 
|        Function::Handle(lib.LookupFunctionInScript(script, token_index_at_line));
 | 
|    if (func.IsNull()) {
 | 
| +    if (verbose) {
 | 
| +      OS::Print("No executable code at line %d in '%s'\n",
 | 
| +                line_number, script_url.ToCString());
 | 
| +    }
 | 
|      return NULL;
 | 
|    }
 | 
| -  return SetBreakpoint(func, token_index_at_line, error);
 | 
| +  return SetBreakpoint(func, token_index_at_line);
 | 
|  }
 | 
|  
 | 
|  
 | 
| @@ -641,15 +759,20 @@
 | 
|  
 | 
|  void Debugger::VisitObjectPointers(ObjectPointerVisitor* visitor) {
 | 
|    ASSERT(visitor != NULL);
 | 
| -  Breakpoint* bpt = this->breakpoints_;
 | 
| +  SourceBreakpoint* bpt = src_breakpoints_;
 | 
|    while (bpt != NULL) {
 | 
|      bpt->VisitObjectPointers(visitor);
 | 
|      bpt = bpt->next();
 | 
|    }
 | 
| +  CodeBreakpoint* cbpt = code_breakpoints_;
 | 
| +  while (cbpt != NULL) {
 | 
| +    cbpt->VisitObjectPointers(visitor);
 | 
| +    cbpt = cbpt->next();
 | 
| +  }
 | 
|  }
 | 
|  
 | 
|  
 | 
| -static void DefaultBreakpointHandler(Breakpoint* bpt, StackTrace* stack) {
 | 
| +static void DefaultBreakpointHandler(SourceBreakpoint* bpt, StackTrace* stack) {
 | 
|    String& var_name = String::Handle();
 | 
|    Instance& value = Instance::Handle();
 | 
|    for (intptr_t i = 0; i < stack->Length(); i++) {
 | 
| @@ -680,11 +803,11 @@
 | 
|    DartFrameIterator iterator;
 | 
|    DartFrame* frame = iterator.NextFrame();
 | 
|    ASSERT(frame != NULL);
 | 
| -  Breakpoint* bpt = GetBreakpoint(frame->pc());
 | 
| +  CodeBreakpoint* bpt = GetCodeBreakpoint(frame->pc());
 | 
|    ASSERT(bpt != NULL);
 | 
|    if (verbose) {
 | 
|      OS::Print(">>> %s breakpoint at %s:%d (Address %p)\n",
 | 
| -        bpt->is_temporary() ? "hit temp" : "hit user",
 | 
| +        bpt->IsInternal() ? "hit internal" : "hit user",
 | 
|          bpt ? String::Handle(bpt->SourceUrl()).ToCString() : "?",
 | 
|          bpt ? bpt->LineNumber() : 0,
 | 
|          frame->pc());
 | 
| @@ -701,11 +824,12 @@
 | 
|  
 | 
|    resume_action_ = kContinue;
 | 
|    if (bp_handler_ != NULL) {
 | 
| -    (*bp_handler_)(bpt, stack_trace);
 | 
| +    SourceBreakpoint* src_bpt = bpt->src_bpt();
 | 
| +    (*bp_handler_)(src_bpt, stack_trace);
 | 
|    }
 | 
|  
 | 
|    if (resume_action_ == kContinue) {
 | 
| -    RemoveTemporaryBreakpoints();
 | 
| +    RemoveInternalBreakpoints();
 | 
|    } else if (resume_action_ == kStepOver) {
 | 
|      Function& func = Function::Handle(bpt->function());
 | 
|      if (bpt->breakpoint_kind_ == PcDescriptors::kReturn) {
 | 
| @@ -713,12 +837,12 @@
 | 
|        if (stack_trace->Length() > 1) {
 | 
|          ActivationFrame* caller = stack_trace->ActivationFrameAt(1);
 | 
|          func = caller->DartFunction().raw();
 | 
| -        RemoveTemporaryBreakpoints();
 | 
| +        RemoveInternalBreakpoints();
 | 
|        }
 | 
|      }
 | 
|      InstrumentForStepping(func);
 | 
|    } else if (resume_action_ == kStepInto) {
 | 
| -    RemoveTemporaryBreakpoints();
 | 
| +    RemoveInternalBreakpoints();
 | 
|      if (bpt->breakpoint_kind_ == PcDescriptors::kIcCall) {
 | 
|        int num_args, num_named_args;
 | 
|        uword target;
 | 
| @@ -748,8 +872,8 @@
 | 
|      }
 | 
|    } else {
 | 
|      ASSERT(resume_action_ == kStepOut);
 | 
| -    // Set temporary breakpoints in the caller.
 | 
| -    RemoveTemporaryBreakpoints();
 | 
| +    // Set stepping breakpoints in the caller.
 | 
| +    RemoveInternalBreakpoints();
 | 
|      if (stack_trace->Length() > 1) {
 | 
|        ActivationFrame* caller = stack_trace->ActivationFrameAt(1);
 | 
|        InstrumentForStepping(caller->DartFunction());
 | 
| @@ -768,9 +892,26 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -Breakpoint* Debugger::GetBreakpoint(uword breakpoint_address) {
 | 
| -  Breakpoint* bpt = this->breakpoints_;
 | 
| +// TODO(hausner): handle closure functions.
 | 
| +void Debugger::NotifyCompilation(const Function& func) {
 | 
| +  SourceBreakpoint* bpt = src_breakpoints_;
 | 
|    while (bpt != NULL) {
 | 
| +    if (func.raw() == bpt->function()) {
 | 
| +      if (verbose) {
 | 
| +        OS::Print("Enable latent breakpoint for function '%s'\n",
 | 
| +                  String::Handle(func.name()).ToCString());
 | 
| +      }
 | 
| +      CodeBreakpoint* cbpt = MakeCodeBreakpoint(bpt);
 | 
| +      bpt->Enable();
 | 
| +    }
 | 
| +    bpt = bpt->next();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +
 | 
| +CodeBreakpoint* Debugger::GetCodeBreakpoint(uword breakpoint_address) {
 | 
| +  CodeBreakpoint* bpt = code_breakpoints_;
 | 
| +  while (bpt != NULL) {
 | 
|      if (bpt->pc() == breakpoint_address) {
 | 
|        return bpt;
 | 
|      }
 | 
| @@ -780,18 +921,21 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void Debugger::RemoveBreakpoint(Breakpoint* bpt) {
 | 
| -  ASSERT(breakpoints_ != NULL);
 | 
| -  Breakpoint* prev_bpt = NULL;
 | 
| -  Breakpoint* curr_bpt = breakpoints_;
 | 
| +// Remove and delete the source breakpoint bpt and its associated
 | 
| +// code breakpoints.
 | 
| +void Debugger::RemoveBreakpoint(SourceBreakpoint* bpt) {
 | 
| +  ASSERT(src_breakpoints_ != NULL);
 | 
| +  SourceBreakpoint* prev_bpt = NULL;
 | 
| +  SourceBreakpoint* curr_bpt = src_breakpoints_;
 | 
|    while (curr_bpt != NULL) {
 | 
|      if (bpt == curr_bpt) {
 | 
|        if (prev_bpt == NULL) {
 | 
| -        breakpoints_ = breakpoints_->next();
 | 
| +        src_breakpoints_ = src_breakpoints_->next();
 | 
|        } else {
 | 
|          prev_bpt->set_next(curr_bpt->next());
 | 
|        }
 | 
| -      UnsetBreakpoint(bpt);
 | 
| +      // Remove the code breakpoints associated with the source breakpoint.
 | 
| +      RemoveCodeBreakpoints(bpt);
 | 
|        delete bpt;
 | 
|        return;
 | 
|      }
 | 
| @@ -802,19 +946,21 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void Debugger::RemoveTemporaryBreakpoints() {
 | 
| -  Breakpoint* prev_bpt = NULL;
 | 
| -  Breakpoint* curr_bpt = breakpoints_;
 | 
| +// Remove and delete the code breakpoints that are associated with given
 | 
| +// source breakpoint bpt. If bpt is null, remove the internal breakpoints.
 | 
| +void Debugger::RemoveCodeBreakpoints(SourceBreakpoint* src_bpt) {
 | 
| +  CodeBreakpoint* prev_bpt = NULL;
 | 
| +  CodeBreakpoint* curr_bpt = code_breakpoints_;
 | 
|    while (curr_bpt != NULL) {
 | 
| -    if (curr_bpt->is_temporary()) {
 | 
| +    if (curr_bpt->src_bpt() == src_bpt) {
 | 
|        if (prev_bpt == NULL) {
 | 
| -        breakpoints_ = breakpoints_->next();
 | 
| +        code_breakpoints_ = code_breakpoints_->next();
 | 
|        } else {
 | 
|          prev_bpt->set_next(curr_bpt->next());
 | 
|        }
 | 
| -      Breakpoint* temp_bpt = curr_bpt;
 | 
| +      CodeBreakpoint* temp_bpt = curr_bpt;
 | 
|        curr_bpt = curr_bpt->next();
 | 
| -      UnsetBreakpoint(temp_bpt);
 | 
| +      temp_bpt->Disable();
 | 
|        delete temp_bpt;
 | 
|      } else {
 | 
|        prev_bpt = curr_bpt;
 | 
| @@ -824,9 +970,16 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -Breakpoint* Debugger::GetBreakpointByFunction(const Function& func,
 | 
| -                                              intptr_t token_index) {
 | 
| -  Breakpoint* bpt = this->breakpoints_;
 | 
| +// Remove and delete all breakpoints that are not associated with a
 | 
| +// user-defined source breakpoint.
 | 
| +void Debugger::RemoveInternalBreakpoints() {
 | 
| +  RemoveCodeBreakpoints(NULL);
 | 
| +}
 | 
| +
 | 
| +
 | 
| +SourceBreakpoint* Debugger::GetSourceBreakpoint(const Function& func,
 | 
| +                                                intptr_t token_index) {
 | 
| +  SourceBreakpoint* bpt = src_breakpoints_;
 | 
|    while (bpt != NULL) {
 | 
|      if ((bpt->function() == func.raw()) &&
 | 
|          (bpt->token_index() == token_index)) {
 | 
| @@ -838,11 +991,17 @@
 | 
|  }
 | 
|  
 | 
|  
 | 
| -void Debugger::RegisterBreakpoint(Breakpoint* bpt) {
 | 
| +void Debugger::RegisterSourceBreakpoint(SourceBreakpoint* bpt) {
 | 
|    ASSERT(bpt->next() == NULL);
 | 
| -  bpt->set_next(this->breakpoints_);
 | 
| -  this->breakpoints_ = bpt;
 | 
| +  bpt->set_next(src_breakpoints_);
 | 
| +  src_breakpoints_ = bpt;
 | 
|  }
 | 
|  
 | 
|  
 | 
| +void Debugger::RegisterCodeBreakpoint(CodeBreakpoint* bpt) {
 | 
| +  ASSERT(bpt->next() == NULL);
 | 
| +  bpt->set_next(code_breakpoints_);
 | 
| +  code_breakpoints_ = bpt;
 | 
| +}
 | 
| +
 | 
|  }  // namespace dart
 | 
| 
 |