Merge "Refactor debug info position visiting"
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index bda7108..233f61c 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -49,18 +49,12 @@
 
 static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
   std::vector<const char*> names;
+  DCHECK(mi->dex_file != nullptr);
   CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item, mi->dex_method_index);
   if (accessor.HasCodeItem()) {
-    DCHECK(mi->dex_file != nullptr);
-    const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset());
-    if (stream != nullptr) {
-      DecodeUnsignedLeb128(&stream);  // line.
-      uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
-      for (uint32_t i = 0; i < parameters_size; ++i) {
-        uint32_t id = DecodeUnsignedLeb128P1(&stream);
-        names.push_back(mi->dex_file->StringDataByIdx(dex::StringIndex(id)));
-      }
-    }
+    accessor.VisitParameterNames([&](const dex::StringIndex& id) {
+      names.push_back(mi->dex_file->StringDataByIdx(id));
+    });
   }
   return names;
 }
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 3d78943..0a13a92 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -34,11 +34,6 @@
 
 typedef std::vector<DexFile::PositionInfo> PositionInfos;
 
-static bool PositionInfoCallback(void* ctx, const DexFile::PositionInfo& entry) {
-  static_cast<PositionInfos*>(ctx)->push_back(entry);
-  return false;
-}
-
 template<typename ElfTypes>
 class ElfDebugLineWriter {
   using Elf_Addr = typename ElfTypes::Addr;
@@ -154,11 +149,14 @@
       Elf_Addr method_address = base_address + mi->code_address;
 
       PositionInfos dex2line_map;
-      DCHECK(mi->dex_file != nullptr);
       const DexFile* dex = mi->dex_file;
+      DCHECK(dex != nullptr);
       CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index);
-      const uint32_t debug_info_offset = accessor.DebugInfoOffset();
-      if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) {
+      if (!accessor.DecodeDebugPositionInfo(
+          [&](const DexFile::PositionInfo& entry) {
+            dex2line_map.push_back(entry);
+            return false;
+          })) {
         continue;
       }
 
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index e555d0d..034761d 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -466,22 +466,17 @@
 }
 
 ArenaBitVector* HInstructionBuilder::FindNativeDebugInfoLocations() {
-  // The callback gets called when the line number changes.
-  // In other words, it marks the start of new java statement.
-  struct Callback {
-    static bool Position(void* ctx, const DexFile::PositionInfo& entry) {
-      static_cast<ArenaBitVector*>(ctx)->SetBit(entry.address_);
-      return false;
-    }
-  };
   ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_,
                                                      code_item_accessor_.InsnsSizeInCodeUnits(),
                                                      /* expandable */ false,
                                                      kArenaAllocGraphBuilder);
   locations->ClearAllBits();
-  dex_file_->DecodeDebugPositionInfo(code_item_accessor_.DebugInfoOffset(),
-                                     Callback::Position,
-                                     locations);
+  // The visitor gets called when the line number changes.
+  // In other words, it marks the start of new java statement.
+  code_item_accessor_.DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
+    locations->SetBit(entry.address_);
+    return false;
+  });
   // Instruction-specific tweaks.
   for (const DexInstructionPcPair& inst : code_item_accessor_) {
     switch (inst->Opcode()) {
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index e9b6402..31bc6e3 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -751,14 +751,6 @@
 }
 
 /*
- * Callback for dumping each positions table entry.
- */
-static bool dumpPositionsCb(void* /*context*/, const DexFile::PositionInfo& entry) {
-  fprintf(gOutFile, "        0x%04x line=%d\n", entry.address_, entry.line_);
-  return false;
-}
-
-/*
  * Callback for dumping locals table entry.
  */
 static void dumpLocalsCb(void* /*context*/, const DexFile::LocalInfo& entry) {
@@ -1201,7 +1193,10 @@
   // Positions and locals table in the debug info.
   bool is_static = (flags & kAccStatic) != 0;
   fprintf(gOutFile, "      positions     : \n");
-  pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), dumpPositionsCb, nullptr);
+  accessor.DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
+    fprintf(gOutFile, "        0x%04x line=%d\n", entry.address_, entry.line_);
+    return false;
+  });
   fprintf(gOutFile, "      locals        : \n");
   accessor.DecodeDebugLocalInfo(is_static, idx, dumpLocalsCb, nullptr);
 }
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index 52d355b..148c0c7 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -1037,15 +1037,6 @@
 }
 
 /*
- * Callback for dumping each positions table entry.
- */
-static bool DumpPositionsCb(void* context, const DexFile::PositionInfo& entry) {
-  FILE* out_file = reinterpret_cast<FILE*>(context);
-  fprintf(out_file, "        0x%04x line=%d\n", entry.address_, entry.line_);
-  return false;
-}
-
-/*
  * Callback for dumping locals table entry.
  */
 static void DumpLocalsCb(void* context, const DexFile::LocalInfo& entry) {
@@ -1112,8 +1103,13 @@
                                      [this](uint32_t idx) {
                                        return StringDataByIdx(idx, this->header_);
                                      },
-                                     DumpPositionsCb,
-                                     out_file_);
+                                     [&](const DexFile::PositionInfo& entry) {
+                                       fprintf(out_file_,
+                                               "        0x%04x line=%d\n",
+                                               entry.address_,
+                                               entry.line_);
+                                        return false;
+                                     });
   }
   fprintf(out_file_, "      locals        : \n");
   if (debug_info != nullptr) {
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index e7eaf30..c2514a1 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -80,19 +80,6 @@
 }
 
 /*
- * Positions table callback; we just want to catch the number of the
- * first line in the method, which *should* correspond to the first
- * entry from the table.  (Could also use "min" here.)
- */
-static bool positionsCb(void* context, const DexFile::PositionInfo& entry) {
-  int* pFirstLine = reinterpret_cast<int *>(context);
-  if (*pFirstLine == -1) {
-    *pFirstLine = entry.line_;
-  }
-  return 0;
-}
-
-/*
  * Dumps a method.
  */
 static void dumpMethod(const DexFile* pDexFile,
@@ -123,9 +110,13 @@
     fileName = "(none)";
   }
 
-  // Find the first line.
-  int firstLine = -1;
-  pDexFile->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), positionsCb, &firstLine);
+  // We just want to catch the number of the first line in the method, which *should* correspond to
+  // the first entry from the table.
+  int first_line = -1;
+  accessor.DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
+    first_line = entry.line_;
+    return true;  // Early exit since we only want the first line.
+  });
 
   // Method signature.
   const Signature signature = pDexFile->GetMethodSignature(pMethodId);
@@ -134,7 +125,7 @@
   // Dump actual method information.
   fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n",
           insnsOff, accessor.InsnsSizeInCodeUnits() * 2,
-          className.get(), methodName, typeDesc, fileName, firstLine);
+          className.get(), methodName, typeDesc, fileName, first_line);
 
   free(typeDesc);
 }
diff --git a/libdexfile/dex/code_item_accessors-inl.h b/libdexfile/dex/code_item_accessors-inl.h
index c166f5f..c7e876e 100644
--- a/libdexfile/dex/code_item_accessors-inl.h
+++ b/libdexfile/dex/code_item_accessors-inl.h
@@ -199,6 +199,36 @@
                                          context);
 }
 
+template <typename Visitor>
+inline uint32_t CodeItemDebugInfoAccessor::VisitParameterNames(const Visitor& visitor) const {
+  const uint8_t* stream = dex_file_->GetDebugInfoStream(DebugInfoOffset());
+  return (stream != nullptr) ? DexFile::DecodeDebugInfoParameterNames(&stream, visitor) : 0u;
+}
+
+inline bool CodeItemDebugInfoAccessor::GetLineNumForPc(const uint32_t address,
+                                                       uint32_t* line_num) const {
+  return DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
+    // We know that this callback will be called in ascending address order, so keep going until we
+    // find a match or we've just gone past it.
+    if (entry.address_ > address) {
+      // The line number from the previous positions callback will be the final result.
+      return true;
+    }
+    *line_num = entry.line_;
+    return entry.address_ == address;
+  });
+}
+
+template <typename Visitor>
+inline bool CodeItemDebugInfoAccessor::DecodeDebugPositionInfo(const Visitor& visitor) const {
+  return dex_file_->DecodeDebugPositionInfo(
+      dex_file_->GetDebugInfoStream(DebugInfoOffset()),
+      [this](uint32_t idx) {
+        return dex_file_->StringDataByIdx(dex::StringIndex(idx));
+      },
+      visitor);
+}
+
 }  // namespace art
 
 #endif  // ART_LIBDEXFILE_DEX_CODE_ITEM_ACCESSORS_INL_H_
diff --git a/libdexfile/dex/code_item_accessors.h b/libdexfile/dex/code_item_accessors.h
index 695cc7b..c2aa23c 100644
--- a/libdexfile/dex/code_item_accessors.h
+++ b/libdexfile/dex/code_item_accessors.h
@@ -157,6 +157,16 @@
                             NewLocalCallback new_local,
                             void* context) const;
 
+  // Visit each parameter in the debug information. Returns the line number.
+  // The argument of the Visitor is dex::StringIndex.
+  template <typename Visitor>
+  uint32_t VisitParameterNames(const Visitor& visitor) const;
+
+  template <typename Visitor>
+  bool DecodeDebugPositionInfo(const Visitor& visitor) const;
+
+  bool GetLineNumForPc(const uint32_t pc, uint32_t* line_num) const;
+
  protected:
   ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index);
   ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
diff --git a/libdexfile/dex/dex_file-inl.h b/libdexfile/dex/dex_file-inl.h
index c512361..eca9ee9 100644
--- a/libdexfile/dex/dex_file-inl.h
+++ b/libdexfile/dex/dex_file-inl.h
@@ -22,6 +22,7 @@
 #include "base/casts.h"
 #include "base/leb128.h"
 #include "base/stringpiece.h"
+#include "base/utils.h"
 #include "class_iterator.h"
 #include "compact_dex_file.h"
 #include "dex_instruction_iterator.h"
@@ -401,19 +402,14 @@
 
 template<typename DexDebugNewPosition, typename IndexToStringData>
 bool DexFile::DecodeDebugPositionInfo(const uint8_t* stream,
-                                      IndexToStringData index_to_string_data,
-                                      DexDebugNewPosition position_functor,
-                                      void* context) {
+                                      const IndexToStringData& index_to_string_data,
+                                      const DexDebugNewPosition& position_functor) {
   if (stream == nullptr) {
     return false;
   }
 
-  PositionInfo entry = PositionInfo();
-  entry.line_ = DecodeUnsignedLeb128(&stream);
-  uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
-  for (uint32_t i = 0; i < parameters_size; ++i) {
-    DecodeUnsignedLeb128P1(&stream);  // Parameter name.
-  }
+  PositionInfo entry;
+  entry.line_ = DecodeDebugInfoParameterNames(&stream, VoidFunctor());
 
   for (;;)  {
     uint8_t opcode = *stream++;
@@ -456,7 +452,7 @@
         int adjopcode = opcode - DBG_FIRST_SPECIAL;
         entry.address_ += adjopcode / DBG_LINE_RANGE;
         entry.line_ += DBG_LINE_BASE + (adjopcode % DBG_LINE_RANGE);
-        if (position_functor(context, entry)) {
+        if (position_functor(entry)) {
           return true;  // early exit.
         }
         entry.prologue_end_ = false;
@@ -467,18 +463,6 @@
   }
 }
 
-template<typename DexDebugNewPosition>
-bool DexFile::DecodeDebugPositionInfo(uint32_t debug_info_offset,
-                                      DexDebugNewPosition position_functor,
-                                      void* context) const {
-  return DecodeDebugPositionInfo(GetDebugInfoStream(debug_info_offset),
-                                 [this](uint32_t idx) {
-                                   return StringDataByIdx(dex::StringIndex(idx));
-                                 },
-                                 position_functor,
-                                 context);
-}
-
 inline const CompactDexFile* DexFile::AsCompactDexFile() const {
   DCHECK(IsCompactDexFile());
   return down_cast<const CompactDexFile*>(this);
@@ -502,6 +486,18 @@
   return { ClassIterator(*this, 0u), ClassIterator(*this, NumClassDefs()) };
 }
 
+// Returns the line number
+template <typename Visitor>
+inline uint32_t DexFile::DecodeDebugInfoParameterNames(const uint8_t** debug_info,
+                                                       const Visitor& visitor) {
+  uint32_t line = DecodeUnsignedLeb128(debug_info);
+  const uint32_t parameters_size = DecodeUnsignedLeb128(debug_info);
+  for (uint32_t i = 0; i < parameters_size; ++i) {
+    visitor(dex::StringIndex(DecodeUnsignedLeb128P1(debug_info)));
+  }
+  return line;
+}
+
 }  // namespace art
 
 #endif  // ART_LIBDEXFILE_DEX_DEX_FILE_INL_H_
diff --git a/libdexfile/dex/dex_file.cc b/libdexfile/dex/dex_file.cc
index a3e6221..48f38ca 100644
--- a/libdexfile/dex/dex_file.cc
+++ b/libdexfile/dex/dex_file.cc
@@ -492,22 +492,6 @@
   return -1;
 }
 
-bool DexFile::LineNumForPcCb(void* raw_context, const PositionInfo& entry) {
-  LineNumFromPcContext* context = reinterpret_cast<LineNumFromPcContext*>(raw_context);
-
-  // We know that this callback will be called in
-  // ascending address order, so keep going until we find
-  // a match or we've just gone past it.
-  if (entry.address_ > context->address_) {
-    // The line number from the previous positions callback
-    // wil be the final result.
-    return true;
-  } else {
-    context->line_num_ = entry.line_;
-    return entry.address_ == context->address_;
-  }
-}
-
 // Read a signed integer.  "zwidth" is the zero-based byte count.
 int32_t DexFile::ReadSignedInt(const uint8_t* ptr, int zwidth) {
   int32_t val = 0;
diff --git a/libdexfile/dex/dex_file.h b/libdexfile/dex/dex_file.h
index fc218fb..3e4a481 100644
--- a/libdexfile/dex/dex_file.h
+++ b/libdexfile/dex/dex_file.h
@@ -782,8 +782,6 @@
   // Callback for "new locals table entry".
   typedef void (*DexDebugNewLocalCb)(void* context, const LocalInfo& entry);
 
-  static bool LineNumForPcCb(void* context, const PositionInfo& entry);
-
   const AnnotationsDirectoryItem* GetAnnotationsDirectory(const ClassDef& class_def) const {
     return DataPointer<AnnotationsDirectoryItem>(class_def.annotations_off_);
   }
@@ -865,15 +863,6 @@
     DBG_LINE_RANGE           = 15,
   };
 
-  struct LineNumFromPcContext {
-    LineNumFromPcContext(uint32_t address, uint32_t line_num)
-        : address_(address), line_num_(line_num) {}
-    uint32_t address_;
-    uint32_t line_num_;
-   private:
-    DISALLOW_COPY_AND_ASSIGN(LineNumFromPcContext);
-  };
-
   // Returns false if there is no debugging information or if it cannot be decoded.
   template<typename NewLocalCallback, typename IndexToStringData, typename TypeIndexToStringData>
   static bool DecodeDebugLocalInfo(const uint8_t* stream,
@@ -902,13 +891,8 @@
   // Returns false if there is no debugging information or if it cannot be decoded.
   template<typename DexDebugNewPosition, typename IndexToStringData>
   static bool DecodeDebugPositionInfo(const uint8_t* stream,
-                                      IndexToStringData index_to_string_data,
-                                      DexDebugNewPosition position_functor,
-                                      void* context);
-  template<typename DexDebugNewPosition>
-  bool DecodeDebugPositionInfo(uint32_t debug_info_offset,
-                               DexDebugNewPosition position_functor,
-                               void* context) const;
+                                      const IndexToStringData& index_to_string_data,
+                                      const DexDebugNewPosition& position_functor);
 
   const char* GetSourceFile(const ClassDef& class_def) const {
     if (!class_def.source_file_idx_.IsValid()) {
@@ -1016,7 +1000,11 @@
   // Iterate dex classes and remove hiddenapi flags in fields and methods.
   void UnhideApis() const;
 
-  inline IterationRange<ClassIterator> GetClasses() const;
+  IterationRange<ClassIterator> GetClasses() const;
+
+  template <typename Visitor>
+  static uint32_t DecodeDebugInfoParameterNames(const uint8_t** debug_info,
+                                                const Visitor& visitor);
 
  protected:
   // First Dex format version supporting default methods.
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 6764538..742a9fa 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -446,16 +446,6 @@
   return ERR(NONE);
 }
 
-using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
-
-static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
-  LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
-  jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
-                                       static_cast<jint>(entry.line_) };
-  context->push_back(jvmti_entry);
-  return false;  // Collect all, no early exit.
-}
-
 jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
                                           jmethodID method,
                                           jint* entry_count_ptr,
@@ -486,9 +476,11 @@
     DCHECK(accessor.HasCodeItem()) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
   }
 
-  LineNumberContext context;
-  bool success = dex_file->DecodeDebugPositionInfo(
-      accessor.DebugInfoOffset(), CollectLineNumbers, &context);
+  std::vector<jvmtiLineNumberEntry> context;
+  bool success = accessor.DecodeDebugPositionInfo([&](const art::DexFile::PositionInfo& entry) {
+    context.push_back({static_cast<jlocation>(entry.address_), static_cast<jint>(entry.line_)});
+    return false;
+  });
   if (!success) {
     return ERR(ABSENT_INFORMATION);
   }
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7103214..7b4fa6e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -1667,18 +1667,6 @@
 }
 
 void Dbg::OutputLineTable(JDWP::RefTypeId, JDWP::MethodId method_id, JDWP::ExpandBuf* pReply) {
-  struct DebugCallbackContext {
-    int numItems;
-    JDWP::ExpandBuf* pReply;
-
-    static bool Callback(void* context, const DexFile::PositionInfo& entry) {
-      DebugCallbackContext* pContext = reinterpret_cast<DebugCallbackContext*>(context);
-      expandBufAdd8BE(pContext->pReply, entry.address_);
-      expandBufAdd4BE(pContext->pReply, entry.line_);
-      pContext->numItems++;
-      return false;
-    }
-  };
   ArtMethod* m = FromMethodId(method_id);
   CodeItemDebugInfoAccessor accessor(m->DexInstructionDebugInfo());
   uint64_t start, end;
@@ -1699,17 +1687,15 @@
   size_t numLinesOffset = expandBufGetLength(pReply);
   expandBufAdd4BE(pReply, 0);
 
-  DebugCallbackContext context;
-  context.numItems = 0;
-  context.pReply = pReply;
+  int numItems = 0;
+  accessor.DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
+    expandBufAdd8BE(pReply, entry.address_);
+    expandBufAdd4BE(pReply, entry.line_);
+    numItems++;
+    return false;
+  });
 
-  if (accessor.HasCodeItem()) {
-    m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(),
-                                             DebugCallbackContext::Callback,
-                                             &context);
-  }
-
-  JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
+  JDWP::Set4BE(expandBufGetBuffer(pReply) + numLinesOffset, numItems);
 }
 
 void Dbg::OutputVariableTable(JDWP::RefTypeId, JDWP::MethodId method_id, bool with_generic,
@@ -3855,50 +3841,6 @@
   SingleStepStackVisitor visitor(thread);
   visitor.WalkStack();
 
-  // Find the dex_pc values that correspond to the current line, for line-based single-stepping.
-  struct DebugCallbackContext {
-    DebugCallbackContext(SingleStepControl* single_step_control_cb,
-                         int32_t line_number_cb, uint32_t num_insns_in_code_units)
-        : single_step_control_(single_step_control_cb), line_number_(line_number_cb),
-          num_insns_in_code_units_(num_insns_in_code_units), last_pc_valid(false), last_pc(0) {
-    }
-
-    static bool Callback(void* raw_context, const DexFile::PositionInfo& entry) {
-      DebugCallbackContext* context = reinterpret_cast<DebugCallbackContext*>(raw_context);
-      if (static_cast<int32_t>(entry.line_) == context->line_number_) {
-        if (!context->last_pc_valid) {
-          // Everything from this address until the next line change is ours.
-          context->last_pc = entry.address_;
-          context->last_pc_valid = true;
-        }
-        // Otherwise, if we're already in a valid range for this line,
-        // just keep going (shouldn't really happen)...
-      } else if (context->last_pc_valid) {  // and the line number is new
-        // Add everything from the last entry up until here to the set
-        for (uint32_t dex_pc = context->last_pc; dex_pc < entry.address_; ++dex_pc) {
-          context->single_step_control_->AddDexPc(dex_pc);
-        }
-        context->last_pc_valid = false;
-      }
-      return false;  // There may be multiple entries for any given line.
-    }
-
-    ~DebugCallbackContext() {
-      // If the line number was the last in the position table...
-      if (last_pc_valid) {
-        for (uint32_t dex_pc = last_pc; dex_pc < num_insns_in_code_units_; ++dex_pc) {
-          single_step_control_->AddDexPc(dex_pc);
-        }
-      }
-    }
-
-    SingleStepControl* const single_step_control_;
-    const int32_t line_number_;
-    const uint32_t num_insns_in_code_units_;
-    bool last_pc_valid;
-    uint32_t last_pc;
-  };
-
   // Allocate single step.
   SingleStepControl* single_step_control =
       new (std::nothrow) SingleStepControl(step_size, step_depth,
@@ -3914,10 +3856,33 @@
   // method on the stack (and no line number either).
   if (m != nullptr && !m->IsNative()) {
     CodeItemDebugInfoAccessor accessor(m->DexInstructionDebugInfo());
-    DebugCallbackContext context(single_step_control, line_number, accessor.InsnsSizeInCodeUnits());
-    m->GetDexFile()->DecodeDebugPositionInfo(accessor.DebugInfoOffset(),
-                                             DebugCallbackContext::Callback,
-                                             &context);
+    bool last_pc_valid = false;
+    uint32_t last_pc = 0u;
+    // Find the dex_pc values that correspond to the current line, for line-based single-stepping.
+    accessor.DecodeDebugPositionInfo([&](const DexFile::PositionInfo& entry) {
+      if (static_cast<int32_t>(entry.line_) == line_number) {
+        if (!last_pc_valid) {
+          // Everything from this address until the next line change is ours.
+          last_pc = entry.address_;
+          last_pc_valid = true;
+        }
+        // Otherwise, if we're already in a valid range for this line,
+        // just keep going (shouldn't really happen)...
+      } else if (last_pc_valid) {  // and the line number is new
+        // Add everything from the last entry up until here to the set
+        for (uint32_t dex_pc = last_pc; dex_pc < entry.address_; ++dex_pc) {
+          single_step_control->AddDexPc(dex_pc);
+        }
+        last_pc_valid = false;
+      }
+      return false;  // There may be multiple entries for any given line.
+    });
+    // If the line number was the last in the position table...
+    if (last_pc_valid) {
+      for (uint32_t dex_pc = last_pc; dex_pc < accessor.InsnsSizeInCodeUnits(); ++dex_pc) {
+        single_step_control->AddDexPc(dex_pc);
+      }
+    }
   }
 
   // Activate single-step in the thread.
diff --git a/runtime/dex/dex_file_annotations.cc b/runtime/dex/dex_file_annotations.cc
index b87bf8d..51cfd43 100644
--- a/runtime/dex/dex_file_annotations.cc
+++ b/runtime/dex/dex_file_annotations.cc
@@ -1578,9 +1578,9 @@
   DCHECK(accessor.HasCodeItem()) << method->PrettyMethod() << " " << dex_file->GetLocation();
 
   // A method with no line number info should return -1
-  DexFile::LineNumFromPcContext context(rel_pc, -1);
-  dex_file->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), DexFile::LineNumForPcCb, &context);
-  return context.line_num_;
+  uint32_t line_num = -1;
+  accessor.GetLineNumForPc(rel_pc, &line_num);
+  return line_num;
 }
 
 template<bool kTransactionActive>