Make CodeItem fields private

Make code item fields private and use accessors. Added a hand full of
friend classes to reduce the size of the change.

Changed default to be nullable and removed CreateNullable.
CreateNullable was a bad API since it defaulted to the unsafe, may
add a CreateNonNullable if it's important for performance.

Motivation:
Have a different layout for code items in cdex.

Bug: 63756964
Test: test-art-host-gtest
Test: test/testrunner/testrunner.py --host
Test: art/tools/run-jdwp-tests.sh '--mode=host' '--variant=X32' --debug

Change-Id: I42bc7435e20358682075cb6de52713b595f95bf9
diff --git a/compiler/compiler.cc b/compiler/compiler.cc
index bb614ae..47f44ff 100644
--- a/compiler/compiler.cc
+++ b/compiler/compiler.cc
@@ -19,6 +19,7 @@
 #include <android-base/logging.h>
 
 #include "base/macros.h"
+#include "code_item_accessors-inl.h"
 #include "driver/compiler_driver.h"
 #include "optimizing/optimizing_compiler.h"
 #include "utils.h"
@@ -46,15 +47,16 @@
    * Dalvik uses 16-bit uints for instruction and register counts.  We'll limit to a quarter
    * of that, which also guarantees we cannot overflow our 16-bit internal Quick SSA name space.
    */
-  if (code_item.insns_size_in_code_units_ >= UINT16_MAX / 4) {
+  CodeItemDataAccessor accessor(&dex_file, &code_item);
+  if (accessor.InsnsSizeInCodeUnits() >= UINT16_MAX / 4) {
     LOG(INFO) << "Method exceeds compiler instruction limit: "
-              << code_item.insns_size_in_code_units_
+              << accessor.InsnsSizeInCodeUnits()
               << " in " << dex_file.PrettyMethod(method_idx);
     return true;
   }
-  if (code_item.registers_size_ >= UINT16_MAX / 4) {
+  if (accessor.RegistersSize() >= UINT16_MAX / 4) {
     LOG(INFO) << "Method exceeds compiler virtual register limit: "
-              << code_item.registers_size_ << " in " << dex_file.PrettyMethod(method_idx);
+              << accessor.RegistersSize() << " in " << dex_file.PrettyMethod(method_idx);
     return true;
   }
   return false;
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 107ed48..0e11e32 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -260,14 +260,10 @@
 
       // Write local variables.
       LocalInfos local_infos;
-      if (dex->DecodeDebugLocalInfo(accessor.RegistersSize(),
-                                    accessor.InsSize(),
-                                    accessor.InsnsSizeInCodeUnits(),
-                                    accessor.DebugInfoOffset(),
-                                    is_static,
-                                    mi->dex_method_index,
-                                    LocalInfoCallback,
-                                    &local_infos)) {
+      if (accessor.DecodeDebugLocalInfo(is_static,
+                                        mi->dex_method_index,
+                                        LocalInfoCallback,
+                                        &local_infos)) {
         for (const DexFile::LocalInfo& var : local_infos) {
           if (var.reg_ < accessor.RegistersSize() - accessor.InsSize()) {
             info_.StartTag(DW_TAG_variable);
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
index 1d609af..34c2919 100644
--- a/compiler/debug/elf_debug_loc_writer.h
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -149,11 +149,12 @@
     DCHECK_LT(stack_map_index, dex_register_maps.size());
     DexRegisterMap dex_register_map = dex_register_maps[stack_map_index];
     DCHECK(dex_register_map.IsValid());
+    CodeItemDataAccessor accessor(method_info->dex_file, method_info->code_item);
     reg_lo = dex_register_map.GetDexRegisterLocation(
-        vreg, method_info->code_item->registers_size_, code_info, encoding);
+        vreg, accessor.RegistersSize(), code_info, encoding);
     if (is64bitValue) {
       reg_hi = dex_register_map.GetDexRegisterLocation(
-          vreg + 1, method_info->code_item->registers_size_, code_info, encoding);
+          vreg + 1, accessor.RegistersSize(), code_info, encoding);
     }
 
     // Add location entry for this address range.
diff --git a/compiler/dex/inline_method_analyser.cc b/compiler/dex/inline_method_analyser.cc
index b409eb2..80677b9 100644
--- a/compiler/dex/inline_method_analyser.cc
+++ b/compiler/dex/inline_method_analyser.cc
@@ -141,8 +141,11 @@
 ArtMethod* GetTargetConstructor(ArtMethod* method, const Instruction* invoke_direct)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   DCHECK_EQ(invoke_direct->Opcode(), Instruction::INVOKE_DIRECT);
-  DCHECK_EQ(invoke_direct->VRegC_35c(),
-            method->GetCodeItem()->registers_size_ - method->GetCodeItem()->ins_size_);
+  if (kIsDebugBuild) {
+    CodeItemDataAccessor accessor(method);
+    DCHECK_EQ(invoke_direct->VRegC_35c(),
+              accessor.RegistersSize() - accessor.InsSize());
+  }
   uint32_t method_index = invoke_direct->VRegB_35c();
   ArtMethod* target_method = Runtime::Current()->GetClassLinker()->LookupResolvedMethod(
       method_index, method->GetDexCache(), method->GetClassLoader());
@@ -323,7 +326,7 @@
       if (target_method->GetDeclaringClass()->IsObjectClass()) {
         DCHECK_EQ(CodeItemDataAccessor(target_method).begin()->Opcode(), Instruction::RETURN_VOID);
       } else {
-        CodeItemDataAccessor target_code_item = CodeItemDataAccessor::CreateNullable(target_method);
+        CodeItemDataAccessor target_code_item(target_method);
         if (!target_code_item.HasCodeItem()) {
           return false;  // Native constructor?
         }
@@ -427,7 +430,7 @@
     InlineMethodAnalyser::IPutVariant(Instruction::IPUT_SHORT), "iget/iput_short variant");
 
 bool InlineMethodAnalyser::AnalyseMethodCode(ArtMethod* method, InlineMethod* result) {
-  CodeItemDataAccessor code_item = CodeItemDataAccessor::CreateNullable(method);
+  CodeItemDataAccessor code_item(method);
   if (!code_item.HasCodeItem()) {
     // Native or abstract.
     return false;
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 0631c0f..68f963e 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -949,14 +949,14 @@
       ArtMethod* method,
       std::set<std::pair<dex::TypeIndex, const DexFile*>>* exceptions_to_resolve)
       REQUIRES_SHARED(Locks::mutator_lock_) {
-    const DexFile::CodeItem* code_item = method->GetCodeItem();
-    if (code_item == nullptr) {
+    if (method->GetCodeItem() == nullptr) {
       return;  // native or abstract method
     }
-    if (code_item->tries_size_ == 0) {
+    CodeItemDataAccessor accessor(method);
+    if (accessor.TriesSize() == 0) {
       return;  // nothing to process
     }
-    const uint8_t* encoded_catch_handler_list = DexFile::GetCatchHandlerData(*code_item, 0);
+    const uint8_t* encoded_catch_handler_list = accessor.GetCatchHandlerData();
     size_t num_encoded_catch_handlers = DecodeUnsignedLeb128(&encoded_catch_handler_list);
     for (size_t i = 0; i < num_encoded_catch_handlers; i++) {
       int32_t encoded_catch_handler_size = DecodeSignedLeb128(&encoded_catch_handler_list);
diff --git a/compiler/exception_test.cc b/compiler/exception_test.cc
index 897b50b..4dbef0d 100644
--- a/compiler/exception_test.cc
+++ b/compiler/exception_test.cc
@@ -20,6 +20,7 @@
 #include "base/callee_save_type.h"
 #include "base/enums.h"
 #include "class_linker.h"
+#include "code_item_accessors-inl.h"
 #include "common_runtime_test.h"
 #include "dex_file-inl.h"
 #include "dex_file.h"
@@ -129,11 +130,12 @@
 TEST_F(ExceptionTest, FindCatchHandler) {
   ScopedObjectAccess soa(Thread::Current());
   const DexFile::CodeItem* code_item = dex_->GetCodeItem(method_f_->GetCodeItemOffset());
+  CodeItemDataAccessor accessor(dex_, code_item);
 
   ASSERT_TRUE(code_item != nullptr);
 
-  ASSERT_EQ(2u, code_item->tries_size_);
-  ASSERT_NE(0u, code_item->insns_size_in_code_units_);
+  ASSERT_EQ(2u, accessor.TriesSize());
+  ASSERT_NE(0u, accessor.InsnsSizeInCodeUnits());
 
   const DexFile::TryItem *t0, *t1;
   t0 = dex_->GetTryItems(*code_item, 0);
diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc
index 58f591b..c505efa 100644
--- a/compiler/optimizing/block_builder.cc
+++ b/compiler/optimizing/block_builder.cc
@@ -18,10 +18,31 @@
 
 #include "base/logging.h"  // FOR VLOG.
 #include "bytecode_utils.h"
+#include "code_item_accessors-inl.h"
 #include "quicken_info.h"
 
 namespace art {
 
+HBasicBlockBuilder::HBasicBlockBuilder(HGraph* graph,
+                                       const DexFile* const dex_file,
+                                       const CodeItemDebugInfoAccessor& accessor,
+                                       ScopedArenaAllocator* local_allocator)
+    : allocator_(graph->GetAllocator()),
+      graph_(graph),
+      dex_file_(dex_file),
+      code_item_accessor_(accessor),
+      local_allocator_(local_allocator),
+      branch_targets_(code_item_accessor_.HasCodeItem()
+                          ? code_item_accessor_.InsnsSizeInCodeUnits()
+                          : /* fake dex_pc=0 for intrinsic graph */ 1u,
+                      nullptr,
+                      local_allocator->Adapter(kArenaAllocGraphBuilder)),
+      throwing_blocks_(kDefaultNumberOfThrowingBlocks,
+                       local_allocator->Adapter(kArenaAllocGraphBuilder)),
+      number_of_branches_(0u),
+      quicken_index_for_dex_pc_(std::less<uint32_t>(),
+                                local_allocator->Adapter(kArenaAllocGraphBuilder)) {}
+
 HBasicBlock* HBasicBlockBuilder::MaybeCreateBlockAt(uint32_t dex_pc) {
   return MaybeCreateBlockAt(dex_pc, dex_pc);
 }
@@ -41,20 +62,19 @@
   // Create the first block for the dex instructions, single successor of the entry block.
   MaybeCreateBlockAt(0u);
 
-  if (code_item_->tries_size_ != 0) {
+  if (code_item_accessor_.TriesSize() != 0) {
     // Create branch targets at the start/end of the TryItem range. These are
     // places where the program might fall through into/out of the a block and
     // where TryBoundary instructions will be inserted later. Other edges which
     // enter/exit the try blocks are a result of branches/switches.
-    for (size_t idx = 0; idx < code_item_->tries_size_; ++idx) {
-      const DexFile::TryItem* try_item = DexFile::GetTryItems(*code_item_, idx);
-      uint32_t dex_pc_start = try_item->start_addr_;
-      uint32_t dex_pc_end = dex_pc_start + try_item->insn_count_;
+    for (const DexFile::TryItem& try_item : code_item_accessor_.TryItems()) {
+      uint32_t dex_pc_start = try_item.start_addr_;
+      uint32_t dex_pc_end = dex_pc_start + try_item.insn_count_;
       MaybeCreateBlockAt(dex_pc_start);
-      if (dex_pc_end < code_item_->insns_size_in_code_units_) {
+      if (dex_pc_end < code_item_accessor_.InsnsSizeInCodeUnits()) {
         // TODO: Do not create block if the last instruction cannot fall through.
         MaybeCreateBlockAt(dex_pc_end);
-      } else if (dex_pc_end == code_item_->insns_size_in_code_units_) {
+      } else if (dex_pc_end == code_item_accessor_.InsnsSizeInCodeUnits()) {
         // The TryItem spans until the very end of the CodeItem and therefore
         // cannot have any code afterwards.
       } else {
@@ -65,7 +85,7 @@
     }
 
     // Create branch targets for exception handlers.
-    const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
+    const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData();
     uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
     for (uint32_t idx = 0; idx < handlers_size; ++idx) {
       CatchHandlerIterator iterator(handlers_ptr);
@@ -78,8 +98,7 @@
 
   // Iterate over all instructions and find branching instructions. Create blocks for
   // the locations these instructions branch to.
-  IterationRange<DexInstructionIterator> instructions = code_item_->Instructions();
-  for (const DexInstructionPcPair& pair : instructions) {
+  for (const DexInstructionPcPair& pair : code_item_accessor_) {
     const uint32_t dex_pc = pair.DexPc();
     const Instruction& instruction = pair.Inst();
 
@@ -109,7 +128,7 @@
 
     if (instruction.CanFlowThrough()) {
       DexInstructionIterator next(std::next(DexInstructionIterator(pair)));
-      if (next == instructions.end()) {
+      if (next == code_item_accessor_.end()) {
         // In the normal case we should never hit this but someone can artificially forge a dex
         // file to fall-through out the method code. In this case we bail out compilation.
         VLOG(compiler) << "Not compiled: Fall-through beyond the CodeItem";
@@ -130,7 +149,7 @@
   bool is_throwing_block = false;
   // Calculate the qucikening index here instead of CreateBranchTargets since it's easier to
   // calculate in dex_pc order.
-  for (const DexInstructionPcPair& pair : code_item_->Instructions()) {
+  for (const DexInstructionPcPair& pair : code_item_accessor_) {
     const uint32_t dex_pc = pair.DexPc();
     const Instruction& instruction = pair.Inst();
 
@@ -213,10 +232,12 @@
 // successors matches the order in which runtime exception delivery searches
 // for a handler.
 static void LinkToCatchBlocks(HTryBoundary* try_boundary,
-                              const DexFile::CodeItem& code_item,
+                              const CodeItemDataAccessor& accessor,
                               const DexFile::TryItem* try_item,
                               const ScopedArenaSafeMap<uint32_t, HBasicBlock*>& catch_blocks) {
-  for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) {
+  for (CatchHandlerIterator it(accessor.GetCatchHandlerData(try_item->handler_off_));
+      it.HasNext();
+      it.Next()) {
     try_boundary->AddExceptionHandler(catch_blocks.Get(it.GetHandlerAddress()));
   }
 }
@@ -232,7 +253,7 @@
     }
   }
 
-  const Instruction& first = code_item_->InstructionAt(catch_block->GetDexPc());
+  const Instruction& first = code_item_accessor_.InstructionAt(catch_block->GetDexPc());
   if (first.Opcode() == Instruction::MOVE_EXCEPTION) {
     // Verifier guarantees that if a catch block begins with MOVE_EXCEPTION then
     // it has no live normal predecessors.
@@ -250,7 +271,7 @@
 }
 
 void HBasicBlockBuilder::InsertTryBoundaryBlocks() {
-  if (code_item_->tries_size_ == 0) {
+  if (code_item_accessor_.TriesSize() == 0) {
     return;
   }
 
@@ -272,12 +293,10 @@
     // loop for synchronized blocks.
     if (ContainsElement(throwing_blocks_, block)) {
       // Try to find a TryItem covering the block.
-      const int32_t try_item_idx = DexFile::FindTryItem(DexFile::GetTryItems(*code_item_, 0u),
-                                                        code_item_->tries_size_,
-                                                        block->GetDexPc());
-      if (try_item_idx != -1) {
+      const DexFile::TryItem* try_item = code_item_accessor_.FindTryItem(block->GetDexPc());
+      if (try_item != nullptr) {
         // Block throwing and in a TryItem. Store the try block information.
-        try_block_info.Put(block->GetBlockId(), DexFile::GetTryItems(*code_item_, try_item_idx));
+        try_block_info.Put(block->GetBlockId(), try_item);
       }
     }
   }
@@ -288,7 +307,7 @@
 
   // Iterate over catch blocks, create artifical landing pads if necessary to
   // simplify the CFG, and set metadata.
-  const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item_, 0);
+  const uint8_t* handlers_ptr = code_item_accessor_.GetCatchHandlerData();
   uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
   for (uint32_t idx = 0; idx < handlers_size; ++idx) {
     CatchHandlerIterator iterator(handlers_ptr);
@@ -336,7 +355,7 @@
         HTryBoundary* try_entry = new (allocator_) HTryBoundary(
             HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc());
         try_block->CreateImmediateDominator()->AddInstruction(try_entry);
-        LinkToCatchBlocks(try_entry, *code_item_, try_item, catch_blocks);
+        LinkToCatchBlocks(try_entry, code_item_accessor_, try_item, catch_blocks);
         break;
       }
     }
@@ -364,13 +383,13 @@
       HTryBoundary* try_exit =
           new (allocator_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc());
       graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit);
-      LinkToCatchBlocks(try_exit, *code_item_, try_item, catch_blocks);
+      LinkToCatchBlocks(try_exit, code_item_accessor_, try_item, catch_blocks);
     }
   }
 }
 
 bool HBasicBlockBuilder::Build() {
-  DCHECK(code_item_ != nullptr);
+  DCHECK(code_item_accessor_.HasCodeItem());
   DCHECK(graph_->GetBlocks().empty());
 
   graph_->SetEntryBlock(new (allocator_) HBasicBlock(graph_, kNoDexPc));
@@ -388,7 +407,7 @@
 }
 
 void HBasicBlockBuilder::BuildIntrinsic() {
-  DCHECK(code_item_ == nullptr);
+  DCHECK(!code_item_accessor_.HasCodeItem());
   DCHECK(graph_->GetBlocks().empty());
 
   // Create blocks.
diff --git a/compiler/optimizing/block_builder.h b/compiler/optimizing/block_builder.h
index 7d0f56d..e68b95c 100644
--- a/compiler/optimizing/block_builder.h
+++ b/compiler/optimizing/block_builder.h
@@ -19,6 +19,7 @@
 
 #include "base/scoped_arena_allocator.h"
 #include "base/scoped_arena_containers.h"
+#include "code_item_accessors.h"
 #include "dex_file.h"
 #include "nodes.h"
 
@@ -28,22 +29,8 @@
  public:
   HBasicBlockBuilder(HGraph* graph,
                      const DexFile* const dex_file,
-                     const DexFile::CodeItem* code_item,
-                     ScopedArenaAllocator* local_allocator)
-      : allocator_(graph->GetAllocator()),
-        graph_(graph),
-        dex_file_(dex_file),
-        code_item_(code_item),
-        local_allocator_(local_allocator),
-        branch_targets_(code_item != nullptr ? code_item->insns_size_in_code_units_
-                                             : /* fake dex_pc=0 for intrinsic graph */ 1u,
-                        nullptr,
-                        local_allocator->Adapter(kArenaAllocGraphBuilder)),
-        throwing_blocks_(kDefaultNumberOfThrowingBlocks,
-                         local_allocator->Adapter(kArenaAllocGraphBuilder)),
-        number_of_branches_(0u),
-        quicken_index_for_dex_pc_(std::less<uint32_t>(),
-                                  local_allocator->Adapter(kArenaAllocGraphBuilder)) {}
+                     const CodeItemDebugInfoAccessor& accessor,
+                     ScopedArenaAllocator* local_allocator);
 
   // Creates basic blocks in `graph_` at branch target dex_pc positions of the
   // `code_item_`. Blocks are connected but left unpopulated with instructions.
@@ -83,7 +70,7 @@
   HGraph* const graph_;
 
   const DexFile* const dex_file_;
-  const DexFile::CodeItem* const code_item_;  // null for intrinsic graph.
+  CodeItemDataAccessor code_item_accessor_;  // null code item for intrinsic graph.
 
   ScopedArenaAllocator* const local_allocator_;
   ScopedArenaVector<HBasicBlock*> branch_targets_;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index d73ef1f..af537dd 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -37,7 +37,7 @@
 namespace art {
 
 HGraphBuilder::HGraphBuilder(HGraph* graph,
-                             const DexFile::CodeItem* code_item,
+                             const CodeItemDebugInfoAccessor& accessor,
                              const DexCompilationUnit* dex_compilation_unit,
                              const DexCompilationUnit* outer_compilation_unit,
                              CompilerDriver* driver,
@@ -47,7 +47,7 @@
                              VariableSizedHandleScope* handles)
     : graph_(graph),
       dex_file_(&graph->GetDexFile()),
-      code_item_(code_item),
+      code_item_accessor_(accessor),
       dex_compilation_unit_(dex_compilation_unit),
       outer_compilation_unit_(outer_compilation_unit),
       compiler_driver_(driver),
@@ -57,6 +57,23 @@
       handles_(handles),
       return_type_(DataType::FromShorty(dex_compilation_unit_->GetShorty()[0])) {}
 
+HGraphBuilder::HGraphBuilder(HGraph* graph,
+                             const DexCompilationUnit* dex_compilation_unit,
+                             const CodeItemDebugInfoAccessor& accessor,
+                             VariableSizedHandleScope* handles,
+                             DataType::Type return_type)
+    : graph_(graph),
+      dex_file_(&graph->GetDexFile()),
+      code_item_accessor_(accessor),
+      dex_compilation_unit_(dex_compilation_unit),
+      outer_compilation_unit_(nullptr),
+      compiler_driver_(nullptr),
+      code_generator_(nullptr),
+      compilation_stats_(nullptr),
+      interpreter_metadata_(nullptr),
+      handles_(handles),
+      return_type_(return_type) {}
+
 bool HGraphBuilder::SkipCompilation(size_t number_of_branches) {
   if (compiler_driver_ == nullptr) {
     // Note that the compiler driver is null when unit testing.
@@ -69,20 +86,20 @@
     return false;
   }
 
-  if (compiler_options.IsHugeMethod(code_item_->insns_size_in_code_units_)) {
+  const uint32_t code_units = code_item_accessor_.InsnsSizeInCodeUnits();
+  if (compiler_options.IsHugeMethod(code_units)) {
     VLOG(compiler) << "Skip compilation of huge method "
                    << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex())
-                   << ": " << code_item_->insns_size_in_code_units_ << " code units";
+                   << ": " << code_units << " code units";
     MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledHugeMethod);
     return true;
   }
 
   // If it's large and contains no branches, it's likely to be machine generated initialization.
-  if (compiler_options.IsLargeMethod(code_item_->insns_size_in_code_units_)
-      && (number_of_branches == 0)) {
+  if (compiler_options.IsLargeMethod(code_units) && (number_of_branches == 0)) {
     VLOG(compiler) << "Skip compilation of large method with no branch "
                    << dex_file_->PrettyMethod(dex_compilation_unit_->GetDexMethodIndex())
-                   << ": " << code_item_->insns_size_in_code_units_ << " code units";
+                   << ": " << code_units << " code units";
     MaybeRecordStat(compilation_stats_, MethodCompilationStat::kNotCompiledLargeMethodNoBranches);
     return true;
   }
@@ -91,17 +108,17 @@
 }
 
 GraphAnalysisResult HGraphBuilder::BuildGraph() {
-  DCHECK(code_item_ != nullptr);
+  DCHECK(code_item_accessor_.HasCodeItem());
   DCHECK(graph_->GetBlocks().empty());
 
-  graph_->SetNumberOfVRegs(code_item_->registers_size_);
-  graph_->SetNumberOfInVRegs(code_item_->ins_size_);
-  graph_->SetMaximumNumberOfOutVRegs(code_item_->outs_size_);
-  graph_->SetHasTryCatch(code_item_->tries_size_ != 0);
+  graph_->SetNumberOfVRegs(code_item_accessor_.RegistersSize());
+  graph_->SetNumberOfInVRegs(code_item_accessor_.InsSize());
+  graph_->SetMaximumNumberOfOutVRegs(code_item_accessor_.OutsSize());
+  graph_->SetHasTryCatch(code_item_accessor_.TriesSize() != 0);
 
   // Use ScopedArenaAllocator for all local allocations.
   ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
-  HBasicBlockBuilder block_builder(graph_, dex_file_, code_item_, &local_allocator);
+  HBasicBlockBuilder block_builder(graph_, dex_file_, code_item_accessor_, &local_allocator);
   SsaBuilder ssa_builder(graph_,
                          dex_compilation_unit_->GetClassLoader(),
                          dex_compilation_unit_->GetDexCache(),
@@ -111,7 +128,7 @@
                                           &block_builder,
                                           &ssa_builder,
                                           dex_file_,
-                                          code_item_,
+                                          code_item_accessor_,
                                           return_type_,
                                           dex_compilation_unit_,
                                           outer_compilation_unit_,
@@ -150,7 +167,7 @@
 }
 
 void HGraphBuilder::BuildIntrinsicGraph(ArtMethod* method) {
-  DCHECK(code_item_ == nullptr);
+  DCHECK(!code_item_accessor_.HasCodeItem());
   DCHECK(graph_->GetBlocks().empty());
 
   // Determine the number of arguments and associated vregs.
@@ -170,7 +187,10 @@
 
   // Use ScopedArenaAllocator for all local allocations.
   ScopedArenaAllocator local_allocator(graph_->GetArenaStack());
-  HBasicBlockBuilder block_builder(graph_, dex_file_, /* code_item */ nullptr, &local_allocator);
+  HBasicBlockBuilder block_builder(graph_,
+                                   dex_file_,
+                                   CodeItemDebugInfoAccessor(),
+                                   &local_allocator);
   SsaBuilder ssa_builder(graph_,
                          dex_compilation_unit_->GetClassLoader(),
                          dex_compilation_unit_->GetDexCache(),
@@ -180,7 +200,7 @@
                                           &block_builder,
                                           &ssa_builder,
                                           dex_file_,
-                                          /* code_item */ nullptr,
+                                          CodeItemDebugInfoAccessor(),
                                           return_type_,
                                           dex_compilation_unit_,
                                           outer_compilation_unit_,
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 0bb3a05..c40e0b4 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -18,6 +18,7 @@
 #define ART_COMPILER_OPTIMIZING_BUILDER_H_
 
 #include "base/arena_object.h"
+#include "code_item_accessors.h"
 #include "dex_file-inl.h"
 #include "dex_file.h"
 #include "driver/compiler_driver.h"
@@ -33,7 +34,7 @@
 class HGraphBuilder : public ValueObject {
  public:
   HGraphBuilder(HGraph* graph,
-                const DexFile::CodeItem* code_item,
+                const CodeItemDebugInfoAccessor& accessor,
                 const DexCompilationUnit* dex_compilation_unit,
                 const DexCompilationUnit* outer_compilation_unit,
                 CompilerDriver* driver,
@@ -45,20 +46,9 @@
   // Only for unit testing.
   HGraphBuilder(HGraph* graph,
                 const DexCompilationUnit* dex_compilation_unit,
-                const DexFile::CodeItem& code_item,
+                const CodeItemDebugInfoAccessor& accessor,
                 VariableSizedHandleScope* handles,
-                DataType::Type return_type = DataType::Type::kInt32)
-      : graph_(graph),
-        dex_file_(&graph->GetDexFile()),
-        code_item_(&code_item),
-        dex_compilation_unit_(dex_compilation_unit),
-        outer_compilation_unit_(nullptr),
-        compiler_driver_(nullptr),
-        code_generator_(nullptr),
-        compilation_stats_(nullptr),
-        interpreter_metadata_(nullptr),
-        handles_(handles),
-        return_type_(return_type) {}
+                DataType::Type return_type = DataType::Type::kInt32);
 
   GraphAnalysisResult BuildGraph();
   void BuildIntrinsicGraph(ArtMethod* method);
@@ -70,7 +60,7 @@
 
   HGraph* const graph_;
   const DexFile* const dex_file_;
-  const DexFile::CodeItem* const code_item_;  // null for intrinsic graph.
+  const CodeItemDebugInfoAccessor code_item_accessor_;  // null for intrinsic graph.
 
   // The compilation unit of the current method being compiled. Note that
   // it can be an inlined method.
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 8750910..7a66d80 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1381,26 +1381,26 @@
 
   bool same_dex_file = IsSameDexFile(*outer_compilation_unit_.GetDexFile(), *method->GetDexFile());
 
-  const DexFile::CodeItem* code_item = method->GetCodeItem();
+  CodeItemDataAccessor accessor(method);
 
-  if (code_item == nullptr) {
+  if (!accessor.HasCodeItem()) {
     LOG_FAIL_NO_STAT()
         << "Method " << method->PrettyMethod() << " is not inlined because it is native";
     return false;
   }
 
   size_t inline_max_code_units = compiler_driver_->GetCompilerOptions().GetInlineMaxCodeUnits();
-  if (code_item->insns_size_in_code_units_ > inline_max_code_units) {
+  if (accessor.InsnsSizeInCodeUnits() > inline_max_code_units) {
     LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedCodeItem)
         << "Method " << method->PrettyMethod()
         << " is not inlined because its code item is too big: "
-        << code_item->insns_size_in_code_units_
+        << accessor.InsnsSizeInCodeUnits()
         << " > "
         << inline_max_code_units;
     return false;
   }
 
-  if (code_item->tries_size_ != 0) {
+  if (accessor.TriesSize() != 0) {
     LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatch)
         << "Method " << method->PrettyMethod() << " is not inlined because of try block";
     return false;
@@ -1660,6 +1660,7 @@
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
   const DexFile& callee_dex_file = *resolved_method->GetDexFile();
   uint32_t method_index = resolved_method->GetDexMethodIndex();
+  CodeItemDebugInfoAccessor code_item_accessor(&callee_dex_file, code_item);
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(),
                                                             caller_compilation_unit_.GetDexCache(),
@@ -1714,7 +1715,7 @@
     }
   }
   HGraphBuilder builder(callee_graph,
-                        code_item,
+                        code_item_accessor,
                         &dex_compilation_unit,
                         &outer_compilation_unit_,
                         compiler_driver_,
@@ -1967,6 +1968,7 @@
     return;
   }
 
+  CodeItemDataAccessor accessor(&callee_graph->GetDexFile(), code_item);
   HInliner inliner(callee_graph,
                    outermost_graph_,
                    codegen_,
@@ -1975,7 +1977,7 @@
                    compiler_driver_,
                    handles_,
                    inline_stats_,
-                   total_number_of_dex_registers_ + code_item->registers_size_,
+                   total_number_of_dex_registers_ + accessor.RegistersSize(),
                    total_number_of_instructions_ + number_of_instructions,
                    this,
                    depth_ + 1);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index e36d91f..3f5923f 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -39,6 +39,44 @@
 
 namespace art {
 
+HInstructionBuilder::HInstructionBuilder(HGraph* graph,
+                                         HBasicBlockBuilder* block_builder,
+                                         SsaBuilder* ssa_builder,
+                                         const DexFile* dex_file,
+                                         const CodeItemDebugInfoAccessor& accessor,
+                                         DataType::Type return_type,
+                                         const DexCompilationUnit* dex_compilation_unit,
+                                         const DexCompilationUnit* outer_compilation_unit,
+                                         CompilerDriver* compiler_driver,
+                                         CodeGenerator* code_generator,
+                                         const uint8_t* interpreter_metadata,
+                                         OptimizingCompilerStats* compiler_stats,
+                                         VariableSizedHandleScope* handles,
+                                         ScopedArenaAllocator* local_allocator)
+    : allocator_(graph->GetAllocator()),
+      graph_(graph),
+      handles_(handles),
+      dex_file_(dex_file),
+      code_item_accessor_(accessor),
+      return_type_(return_type),
+      block_builder_(block_builder),
+      ssa_builder_(ssa_builder),
+      compiler_driver_(compiler_driver),
+      code_generator_(code_generator),
+      dex_compilation_unit_(dex_compilation_unit),
+      outer_compilation_unit_(outer_compilation_unit),
+      quicken_info_(interpreter_metadata),
+      compilation_stats_(compiler_stats),
+      local_allocator_(local_allocator),
+      locals_for_(local_allocator->Adapter(kArenaAllocGraphBuilder)),
+      current_block_(nullptr),
+      current_locals_(nullptr),
+      latest_result_(nullptr),
+      current_this_parameter_(nullptr),
+      loop_headers_(local_allocator->Adapter(kArenaAllocGraphBuilder)) {
+  loop_headers_.reserve(kDefaultNumberOfLoops);
+}
+
 HBasicBlock* HInstructionBuilder::FindBlockStartingAt(uint32_t dex_pc) const {
   return block_builder_->GetBlockAt(dex_pc);
 }
@@ -273,7 +311,7 @@
 }
 
 bool HInstructionBuilder::Build() {
-  DCHECK(code_item_ != nullptr);
+  DCHECK(code_item_accessor_.HasCodeItem());
   locals_for_.resize(
       graph_->GetBlocks().size(),
       ScopedArenaVector<HInstruction*>(local_allocator_->Adapter(kArenaAllocGraphBuilder)));
@@ -323,7 +361,7 @@
       quicken_index = block_builder_->GetQuickenIndex(block_dex_pc);
     }
 
-    for (const DexInstructionPcPair& pair : code_item_->Instructions(block_dex_pc)) {
+    for (const DexInstructionPcPair& pair : code_item_accessor_.InstructionsFrom(block_dex_pc)) {
       if (current_block_ == nullptr) {
         // The previous instruction ended this block.
         break;
@@ -367,7 +405,7 @@
 }
 
 void HInstructionBuilder::BuildIntrinsic(ArtMethod* method) {
-  DCHECK(code_item_ == nullptr);
+  DCHECK(!code_item_accessor_.HasCodeItem());
   DCHECK(method->IsIntrinsic());
 
   locals_for_.resize(
@@ -442,15 +480,16 @@
       return false;
     }
   };
-  CodeItemDebugInfoAccessor accessor(dex_file_, code_item_);
   ArenaBitVector* locations = ArenaBitVector::Create(local_allocator_,
-                                                     accessor.InsnsSizeInCodeUnits(),
+                                                     code_item_accessor_.InsnsSizeInCodeUnits(),
                                                      /* expandable */ false,
                                                      kArenaAllocGraphBuilder);
   locations->ClearAllBits();
-  dex_file_->DecodeDebugPositionInfo(accessor.DebugInfoOffset(), Callback::Position, locations);
+  dex_file_->DecodeDebugPositionInfo(code_item_accessor_.DebugInfoOffset(),
+                                     Callback::Position,
+                                     locations);
   // Instruction-specific tweaks.
-  for (const DexInstructionPcPair& inst : accessor) {
+  for (const DexInstructionPcPair& inst : code_item_accessor_) {
     switch (inst->Opcode()) {
       case Instruction::MOVE_EXCEPTION: {
         // Stop in native debugger after the exception has been moved.
@@ -459,7 +498,7 @@
         locations->ClearBit(inst.DexPc());
         DexInstructionIterator next = std::next(DexInstructionIterator(inst));
         DCHECK(next.DexPc() != inst.DexPc());
-        if (next != accessor.end()) {
+        if (next != code_item_accessor_.end()) {
           locations->SetBit(next.DexPc());
         }
         break;
@@ -1706,7 +1745,8 @@
 
   int32_t payload_offset = instruction.VRegB_31t() + dex_pc;
   const Instruction::ArrayDataPayload* payload =
-      reinterpret_cast<const Instruction::ArrayDataPayload*>(code_item_->insns_ + payload_offset);
+      reinterpret_cast<const Instruction::ArrayDataPayload*>(
+          code_item_accessor_.Insns() + payload_offset);
   const uint8_t* data = payload->data;
   uint32_t element_count = payload->element_count;
 
diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h
index 0500d40..b4e3051 100644
--- a/compiler/optimizing/instruction_builder.h
+++ b/compiler/optimizing/instruction_builder.h
@@ -19,6 +19,7 @@
 
 #include "base/scoped_arena_allocator.h"
 #include "base/scoped_arena_containers.h"
+#include "code_item_accessors.h"
 #include "data_type.h"
 #include "dex_file.h"
 #include "dex_file_types.h"
@@ -50,7 +51,7 @@
                       HBasicBlockBuilder* block_builder,
                       SsaBuilder* ssa_builder,
                       const DexFile* dex_file,
-                      const DexFile::CodeItem* code_item,
+                      const CodeItemDebugInfoAccessor& accessor,
                       DataType::Type return_type,
                       const DexCompilationUnit* dex_compilation_unit,
                       const DexCompilationUnit* outer_compilation_unit,
@@ -59,30 +60,7 @@
                       const uint8_t* interpreter_metadata,
                       OptimizingCompilerStats* compiler_stats,
                       VariableSizedHandleScope* handles,
-                      ScopedArenaAllocator* local_allocator)
-      : allocator_(graph->GetAllocator()),
-        graph_(graph),
-        handles_(handles),
-        dex_file_(dex_file),
-        code_item_(code_item),
-        return_type_(return_type),
-        block_builder_(block_builder),
-        ssa_builder_(ssa_builder),
-        compiler_driver_(compiler_driver),
-        code_generator_(code_generator),
-        dex_compilation_unit_(dex_compilation_unit),
-        outer_compilation_unit_(outer_compilation_unit),
-        quicken_info_(interpreter_metadata),
-        compilation_stats_(compiler_stats),
-        local_allocator_(local_allocator),
-        locals_for_(local_allocator->Adapter(kArenaAllocGraphBuilder)),
-        current_block_(nullptr),
-        current_locals_(nullptr),
-        latest_result_(nullptr),
-        current_this_parameter_(nullptr),
-        loop_headers_(local_allocator->Adapter(kArenaAllocGraphBuilder)) {
-    loop_headers_.reserve(kDefaultNumberOfLoops);
-  }
+                      ScopedArenaAllocator* local_allocator);
 
   bool Build();
   void BuildIntrinsic(ArtMethod* method);
@@ -329,7 +307,7 @@
 
   // The dex file where the method being compiled is, and the bytecode data.
   const DexFile* const dex_file_;
-  const DexFile::CodeItem* const code_item_;  // null for intrinsic graph.
+  const CodeItemDebugInfoAccessor code_item_accessor_;  // null for intrinsic graph.
 
   // The return type of the method being compiled.
   const DataType::Type return_type_;
diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc
index 7149d93..d8ac696 100644
--- a/compiler/optimizing/optimization.cc
+++ b/compiler/optimizing/optimization.cc
@@ -35,6 +35,7 @@
 
 #include "bounds_check_elimination.h"
 #include "cha_guard_optimization.h"
+#include "code_item_accessors-inl.h"
 #include "code_sinking.h"
 #include "constant_folding.h"
 #include "constructor_fence_redundancy_elimination.h"
@@ -241,7 +242,8 @@
         opt = new (allocator) HDeadCodeElimination(graph, stats, name);
         break;
       case OptimizationPass::kInliner: {
-        size_t number_of_dex_registers = dex_compilation_unit.GetCodeItem()->registers_size_;
+        CodeItemDataAccessor accessor(dex_compilation_unit.GetDexFile(),
+                                      dex_compilation_unit.GetCodeItem());
         opt = new (allocator) HInliner(graph,                   // outer_graph
                                        graph,                   // outermost_graph
                                        codegen,
@@ -250,7 +252,7 @@
                                        driver,
                                        handles,
                                        stats,
-                                       number_of_dex_registers,
+                                       accessor.RegistersSize(),
                                        /* total_number_of_instructions */ 0,
                                        /* parent */ nullptr,
                                        /* depth */ 0,
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 24b1a12..9d04dd8 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -766,11 +766,13 @@
   static constexpr size_t kSpaceFilterOptimizingThreshold = 128;
   const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions();
   if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace)
-      && (code_item->insns_size_in_code_units_ > kSpaceFilterOptimizingThreshold)) {
+      && (CodeItemInstructionAccessor(&dex_file, code_item).InsnsSizeInCodeUnits() >
+          kSpaceFilterOptimizingThreshold)) {
     MaybeRecordStat(compilation_stats_.get(), MethodCompilationStat::kNotCompiledSpaceFilter);
     return nullptr;
   }
 
+  CodeItemDebugInfoAccessor code_item_accessor(&dex_file, code_item);
   HGraph* graph = new (allocator) HGraph(
       allocator,
       arena_stack,
@@ -814,7 +816,7 @@
     VLOG(compiler) << "Building " << pass_observer.GetMethodName();
     PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer);
     HGraphBuilder builder(graph,
-                          code_item,
+                          code_item_accessor,
                           &dex_compilation_unit,
                           &dex_compilation_unit,
                           compiler_driver,
@@ -932,7 +934,7 @@
     VLOG(compiler) << "Building intrinsic graph " << pass_observer.GetMethodName();
     PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer);
     HGraphBuilder builder(graph,
-                          /* code_item */ nullptr,
+                          CodeItemDebugInfoAccessor(),  // Null code item.
                           &dex_compilation_unit,
                           &dex_compilation_unit,
                           compiler_driver,
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 158c252..7d05262 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -19,6 +19,7 @@
 
 #include "base/scoped_arena_allocator.h"
 #include "builder.h"
+#include "code_item_accessors-inl.h"
 #include "common_compiler_test.h"
 #include "dex_file.h"
 #include "dex_instruction.h"
@@ -145,7 +146,8 @@
               /* access_flags */ 0u,
               /* verified_method */ nullptr,
               handles_->NewHandle<mirror::DexCache>(nullptr));
-      HGraphBuilder builder(graph, dex_compilation_unit, *code_item, handles_.get(), return_type);
+      CodeItemDebugInfoAccessor accessor(&graph->GetDexFile(), code_item);
+      HGraphBuilder builder(graph, dex_compilation_unit, accessor, handles_.get(), return_type);
       bool graph_built = (builder.BuildGraph() == kAnalysisSuccess);
       return graph_built ? graph : nullptr;
     }
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index f64c89a..730d4b9 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -735,7 +735,7 @@
  * Dumps the catches table associated with the code.
  */
 static void dumpCatches(const DexFile* pDexFile, const DexFile::CodeItem* pCode) {
-  const u4 triesSize = pCode->tries_size_;
+  const u4 triesSize = CodeItemDataAccessor(pDexFile, pCode).TriesSize();
 
   // No catch table.
   if (triesSize == 0) {
diff --git a/dexdump/dexdump_cfg.cc b/dexdump/dexdump_cfg.cc
index 23ecf93..dd57a11 100644
--- a/dexdump/dexdump_cfg.cc
+++ b/dexdump/dexdump_cfg.cc
@@ -25,6 +25,7 @@
 #include <set>
 #include <sstream>
 
+#include "code_item_accessors-no_art-inl.h"
 #include "dex_file-inl.h"
 #include "dex_instruction-inl.h"
 
@@ -37,17 +38,17 @@
   os << "digraph {\n";
   os << "  # /* " << dex_file->PrettyMethod(dex_method_idx, true) << " */\n";
 
+  CodeItemInstructionAccessor accessor(dex_file, code_item);
+
   std::set<uint32_t> dex_pc_is_branch_target;
   {
     // Go and populate.
-    const Instruction* inst = Instruction::At(code_item->insns_);
-    for (uint32_t dex_pc = 0;
-         dex_pc < code_item->insns_size_in_code_units_;
-         dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
+    for (const DexInstructionPcPair& pair : accessor) {
+      const Instruction* inst = &pair.Inst();
       if (inst->IsBranch()) {
-        dex_pc_is_branch_target.insert(dex_pc + inst->GetTargetOffset());
+        dex_pc_is_branch_target.insert(pair.DexPc() + inst->GetTargetOffset());
       } else if (inst->IsSwitch()) {
-        const uint16_t* insns = code_item->insns_ + dex_pc;
+        const uint16_t* insns = reinterpret_cast<const uint16_t*>(inst);
         int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
         const uint16_t* switch_insns = insns + switch_offset;
         uint32_t switch_count = switch_insns[1];
@@ -63,7 +64,7 @@
           int32_t offset =
               static_cast<int32_t>(switch_insns[targets_offset + targ * 2]) |
               static_cast<int32_t>(switch_insns[targets_offset + targ * 2 + 1] << 16);
-          dex_pc_is_branch_target.insert(dex_pc + offset);
+          dex_pc_is_branch_target.insert(pair.DexPc() + offset);
         }
       }
     }
@@ -74,12 +75,10 @@
   std::map<uint32_t, uint32_t> dex_pc_to_incl_id;  // This has entries for all dex pcs.
 
   {
-    const Instruction* inst = Instruction::At(code_item->insns_);
     bool first_in_block = true;
     bool force_new_block = false;
-    for (uint32_t dex_pc = 0;
-         dex_pc < code_item->insns_size_in_code_units_;
-         dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
+    for (const DexInstructionPcPair& pair : accessor) {
+      const uint32_t dex_pc = pair.DexPc();
       if (dex_pc == 0 ||
           (dex_pc_is_branch_target.find(dex_pc) != dex_pc_is_branch_target.end()) ||
           force_new_block) {
@@ -108,7 +107,7 @@
       // Dump the instruction. Need to escape '"', '<', '>', '{' and '}'.
       os << "<" << "p" << dex_pc << ">";
       os << " 0x" << std::hex << dex_pc << std::dec << ": ";
-      std::string inst_str = inst->DumpString(dex_file);
+      std::string inst_str = pair.Inst().DumpString(dex_file);
       size_t cur_start = 0;  // It's OK to start at zero, instruction dumps don't start with chars
                              // we need to escape.
       while (cur_start != std::string::npos) {
@@ -137,7 +136,7 @@
 
       // Force a new block for some fall-throughs and some instructions that terminate the "local"
       // control flow.
-      force_new_block = inst->IsSwitch() || inst->IsBasicBlockEnd();
+      force_new_block = pair.Inst().IsSwitch() || pair.Inst().IsBasicBlockEnd();
     }
     // Close last node.
     if (dex_pc_to_node_id.size() > 0) {
@@ -162,10 +161,9 @@
       uint32_t last_node_id = std::numeric_limits<uint32_t>::max();
       uint32_t old_dex_pc = 0;
       uint32_t block_start_dex_pc = std::numeric_limits<uint32_t>::max();
-      const Instruction* inst = Instruction::At(code_item->insns_);
-      for (uint32_t dex_pc = 0;
-          dex_pc < code_item->insns_size_in_code_units_;
-          old_dex_pc = dex_pc, dex_pc += inst->SizeInCodeUnits(), inst = inst->Next()) {
+      for (const DexInstructionPcPair& pair : accessor) {
+        const Instruction* inst = &pair.Inst();
+        const uint32_t dex_pc = pair.DexPc();
         {
           auto it = dex_pc_to_node_id.find(dex_pc);
           if (it != dex_pc_to_node_id.end()) {
@@ -222,7 +220,7 @@
           }
         } else if (inst->IsSwitch()) {
           // TODO: Iterate through all switch targets.
-          const uint16_t* insns = code_item->insns_ + dex_pc;
+          const uint16_t* insns = reinterpret_cast<const uint16_t*>(inst);
           /* make sure the start of the switch is in range */
           int32_t switch_offset = insns[1] | (static_cast<int32_t>(insns[2]) << 16);
           /* offset to switch table is a relative branch-style offset */
@@ -272,6 +270,7 @@
           // No fall-through.
           last_node_id = std::numeric_limits<uint32_t>::max();
         }
+        old_dex_pc = pair.DexPc();
       }
       // Finish up the last block, if it had common exceptions.
       if (!exception_targets.empty()) {
@@ -293,7 +292,7 @@
     // TODO
     // Exception edges. If this is not the first instruction in the block
     for (uint32_t dex_pc : blocks_with_detailed_exceptions) {
-      const Instruction* inst = Instruction::At(&code_item->insns_[dex_pc]);
+      const Instruction* inst = &accessor.InstructionAt(dex_pc);
       uint32_t this_node_id = dex_pc_to_incl_id.find(dex_pc)->second;
       while (true) {
         CatchHandlerIterator catch_it(*code_item, dex_pc);
@@ -322,7 +321,7 @@
         // Loop update. Have a break-out if the next instruction is a branch target and thus in
         // another block.
         dex_pc += inst->SizeInCodeUnits();
-        if (dex_pc >= code_item->insns_size_in_code_units_) {
+        if (dex_pc >= accessor.InsnsSizeInCodeUnits()) {
           break;
         }
         if (dex_pc_to_node_id.find(dex_pc) != dex_pc_to_node_id.end()) {
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 90df2d7..a163bd9 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -21,6 +21,8 @@
  */
 
 #include "dex_ir.h"
+
+#include "code_item_accessors-inl.h"
 #include "dex_instruction-inl.h"
 #include "dex_ir_builder.h"
 
@@ -564,13 +566,14 @@
 
 CodeItem* Collections::CreateCodeItem(const DexFile& dex_file,
                                       const DexFile::CodeItem& disk_code_item, uint32_t offset) {
-  uint16_t registers_size = disk_code_item.registers_size_;
-  uint16_t ins_size = disk_code_item.ins_size_;
-  uint16_t outs_size = disk_code_item.outs_size_;
-  uint32_t tries_size = disk_code_item.tries_size_;
+  CodeItemDebugInfoAccessor accessor(&dex_file, &disk_code_item);
+  const uint16_t registers_size = accessor.RegistersSize();
+  const uint16_t ins_size = accessor.InsSize();
+  const uint16_t outs_size = accessor.OutsSize();
+  const uint32_t tries_size = accessor.TriesSize();
 
   // TODO: Calculate the size of the debug info.
-  uint32_t debug_info_offset = dex_file.GetDebugInfoOffset(&disk_code_item);
+  const uint32_t debug_info_offset = accessor.DebugInfoOffset();
   const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(debug_info_offset);
   DebugInfoItem* debug_info = nullptr;
   if (debug_info_stream != nullptr) {
@@ -584,20 +587,19 @@
     }
   }
 
-  uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
+  uint32_t insns_size = accessor.InsnsSizeInCodeUnits();
   uint16_t* insns = new uint16_t[insns_size];
-  memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t));
+  memcpy(insns, accessor.Insns(), insns_size * sizeof(uint16_t));
 
   TryItemVector* tries = nullptr;
   CatchHandlerVector* handler_list = nullptr;
   if (tries_size > 0) {
     tries = new TryItemVector();
     handler_list = new CatchHandlerVector();
-    for (uint32_t i = 0; i < tries_size; ++i) {
-      const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i);
-      uint32_t start_addr = disk_try_item->start_addr_;
-      uint16_t insn_count = disk_try_item->insn_count_;
-      uint16_t handler_off = disk_try_item->handler_off_;
+    for (const DexFile::TryItem& disk_try_item : accessor.TryItems()) {
+      uint32_t start_addr = disk_try_item.start_addr_;
+      uint16_t insn_count = disk_try_item.insn_count_;
+      uint16_t handler_off = disk_try_item.handler_off_;
       const CatchHandler* handlers = nullptr;
       for (std::unique_ptr<const CatchHandler>& existing_handlers : *handler_list) {
         if (handler_off == existing_handlers->GetListOffset()) {
@@ -608,7 +610,7 @@
       if (handlers == nullptr) {
         bool catch_all = false;
         TypeAddrPairVector* addr_pairs = new TypeAddrPairVector();
-        for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) {
+        for (CatchHandlerIterator it(disk_code_item, disk_try_item); it.HasNext(); it.Next()) {
           const dex::TypeIndex type_index = it.GetHandlerTypeIndex();
           const TypeId* type_id = GetTypeIdOrNullPtr(type_index.index_);
           catch_all |= type_id == nullptr;
@@ -622,7 +624,7 @@
       tries->push_back(std::unique_ptr<const TryItem>(try_item));
     }
     // Manually walk catch handlers list and add any missing handlers unreferenced by try items.
-    const uint8_t* handlers_base = DexFile::GetCatchHandlerData(disk_code_item, 0);
+    const uint8_t* handlers_base = accessor.GetCatchHandlerData();
     const uint8_t* handlers_data = handlers_base;
     uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_data);
     while (handlers_size > handler_list->size()) {
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 1a1d8cc..b2e19a8 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1261,8 +1261,7 @@
                      bool* addr_found) {
     bool success = true;
 
-    CodeItemDataAccessor code_item_accessor(CodeItemDataAccessor::CreateNullable(&dex_file,
-                                                                                 code_item));
+    CodeItemDataAccessor code_item_accessor(&dex_file, code_item);
 
     // TODO: Support regex
     std::string method_name = dex_file.GetMethodName(dex_file.GetMethodId(dex_method_idx));
diff --git a/openjdkjvmti/ti_breakpoint.cc b/openjdkjvmti/ti_breakpoint.cc
index 8e5b56e..7634fa3 100644
--- a/openjdkjvmti/ti_breakpoint.cc
+++ b/openjdkjvmti/ti_breakpoint.cc
@@ -98,7 +98,7 @@
   art::ScopedObjectAccess soa(art::Thread::Current());
   art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
   if (location < 0 || static_cast<uint32_t>(location) >=
-      art_method->GetCodeItem()->insns_size_in_code_units_) {
+      art_method->DexInstructions().InsnsSizeInCodeUnits()) {
     return ERR(INVALID_LOCATION);
   }
   DeoptManager::Get()->AddMethodBreakpoint(art_method);
diff --git a/openjdkjvmti/ti_method.cc b/openjdkjvmti/ti_method.cc
index 4444853..947ba79 100644
--- a/openjdkjvmti/ti_method.cc
+++ b/openjdkjvmti/ti_method.cc
@@ -123,19 +123,19 @@
   }
 
   art::ScopedObjectAccess soa(art::Thread::Current());
-  const art::DexFile::CodeItem* code_item = art_method->GetCodeItem();
-  if (code_item == nullptr) {
+  art::CodeItemInstructionAccessor accessor(art_method);
+  if (!accessor.HasCodeItem()) {
     *size_ptr = 0;
     *bytecode_ptr = nullptr;
     return OK;
   }
   // 2 bytes per instruction for dex code.
-  *size_ptr = code_item->insns_size_in_code_units_ * 2;
+  *size_ptr = accessor.InsnsSizeInCodeUnits() * 2;
   jvmtiError err = env->Allocate(*size_ptr, bytecode_ptr);
   if (err != OK) {
     return err;
   }
-  memcpy(*bytecode_ptr, code_item->insns_, *size_ptr);
+  memcpy(*bytecode_ptr, accessor.Insns(), *size_ptr);
   return OK;
 }
 
@@ -168,7 +168,7 @@
   }
 
   DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
-  *size_ptr = art_method->GetCodeItem()->ins_size_;
+  *size_ptr = art::CodeItemDataAccessor(art_method).InsSize();
 
   return ERR(NONE);
 }
@@ -266,14 +266,10 @@
   };
 
   LocalVariableContext context(env);
-  if (!dex_file->DecodeDebugLocalInfo(accessor.RegistersSize(),
-                                      accessor.InsSize(),
-                                      accessor.InsnsSizeInCodeUnits(),
-                                      accessor.DebugInfoOffset(),
-                                      art_method->IsStatic(),
-                                      art_method->GetDexMethodIndex(),
-                                      LocalVariableContext::Callback,
-                                      &context)) {
+  if (!accessor.DecodeDebugLocalInfo(art_method->IsStatic(),
+                                     art_method->GetDexMethodIndex(),
+                                     LocalVariableContext::Callback,
+                                     &context)) {
     // Something went wrong with decoding the debug information. It might as well not be there.
     return ERR(ABSENT_INFORMATION);
   } else {
@@ -305,7 +301,7 @@
   }
 
   DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
-  *max_ptr = art_method->GetCodeItem()->registers_size_;
+  *max_ptr = art::CodeItemDataAccessor(art_method).RegistersSize();
 
   return ERR(NONE);
 }
@@ -420,7 +416,7 @@
 
   DCHECK_NE(art_method->GetCodeItemOffset(), 0u);
   *start_location_ptr = 0;
-  *end_location_ptr = art_method->GetCodeItem()->insns_size_in_code_units_ - 1;
+  *end_location_ptr = art_method->DexInstructions().InsnsSizeInCodeUnits() - 1;
 
   return ERR(NONE);
 }
@@ -571,7 +567,7 @@
       // TODO It might be useful to fake up support for get at least on proxy frames.
       result_ = ERR(OPAQUE_FRAME);
       return;
-    } else if (method->GetCodeItem()->registers_size_ <= slot_) {
+    } else if (art::CodeItemDataAccessor(method).RegistersSize() <= slot_) {
       result_ = ERR(INVALID_SLOT);
       return;
     }
diff --git a/openjdkjvmti/ti_stack.cc b/openjdkjvmti/ti_stack.cc
index b43eaa0..17b4243 100644
--- a/openjdkjvmti/ti_stack.cc
+++ b/openjdkjvmti/ti_stack.cc
@@ -44,6 +44,7 @@
 #include "base/bit_utils.h"
 #include "base/enums.h"
 #include "base/mutex.h"
+#include "code_item_accessors-inl.h"
 #include "dex_file.h"
 #include "dex_file_annotations.h"
 #include "dex_file_types.h"
@@ -1044,7 +1045,7 @@
     if (shadow_frame == nullptr) {
       needs_instrument = true;
       const size_t frame_id = visitor.GetFrameId();
-      const uint16_t num_regs = method->GetCodeItem()->registers_size_;
+      const uint16_t num_regs = art::CodeItemDataAccessor(method).RegistersSize();
       shadow_frame = target->FindOrCreateDebuggerShadowFrame(frame_id,
                                                              num_regs,
                                                              method,
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index 869394c..c6c4f4a 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -459,16 +459,8 @@
   }
 }
 
-inline IterationRange<DexInstructionIterator> ArtMethod::DexInstructions() {
-  CodeItemInstructionAccessor accessor(this);
-  return { accessor.begin(),
-           accessor.end() };
-}
-
-inline IterationRange<DexInstructionIterator> ArtMethod::NullableDexInstructions() {
-  CodeItemInstructionAccessor accessor(CodeItemInstructionAccessor::CreateNullable(this));
-  return { accessor.begin(),
-           accessor.end() };
+inline CodeItemInstructionAccessor ArtMethod::DexInstructions() {
+  return CodeItemInstructionAccessor(this);
 }
 
 }  // namespace art
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 9005120..7ddaa7e 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -297,9 +297,8 @@
     }
   }
   if (found_dex_pc != dex::kDexNoIndex) {
-    const Instruction* first_catch_instr =
-        Instruction::At(&code_item->insns_[found_dex_pc]);
-    *has_no_move_exception = (first_catch_instr->Opcode() != Instruction::MOVE_EXCEPTION);
+    const Instruction& first_catch_instr = DexInstructions().InstructionAt(found_dex_pc);
+    *has_no_move_exception = (first_catch_instr.Opcode() != Instruction::MOVE_EXCEPTION);
   }
   // Put the exception back.
   if (exception != nullptr) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index f433223..ab90a10 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -27,6 +27,7 @@
 #include "base/iteration_range.h"
 #include "base/macros.h"
 #include "base/runtime_debug.h"
+#include "code_item_accessors.h"
 #include "dex_file.h"
 #include "dex_instruction_iterator.h"
 #include "gc_root.h"
@@ -716,13 +717,9 @@
             "ptr_sized_fields_.entry_point_from_quick_compiled_code_");
   }
 
-  // Returns the dex instructions of the code item for the art method. Must not be called on null
-  // code items.
-  ALWAYS_INLINE IterationRange<DexInstructionIterator> DexInstructions()
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  // Handles a null code item by returning iterators that have a null address.
-  ALWAYS_INLINE IterationRange<DexInstructionIterator> NullableDexInstructions()
+  // Returns the dex instructions of the code item for the art method. Returns an empty array for
+  // the null code item case.
+  ALWAYS_INLINE CodeItemInstructionAccessor DexInstructions()
       REQUIRES_SHARED(Locks::mutator_lock_);
 
  protected:
diff --git a/runtime/check_reference_map_visitor.h b/runtime/check_reference_map_visitor.h
index d9e8915..c4a613a 100644
--- a/runtime/check_reference_map_visitor.h
+++ b/runtime/check_reference_map_visitor.h
@@ -18,6 +18,7 @@
 #define ART_RUNTIME_CHECK_REFERENCE_MAP_VISITOR_H_
 
 #include "art_method-inl.h"
+#include "code_item_accessors-inl.h"
 #include "dex_file_types.h"
 #include "oat_quick_method_header.h"
 #include "scoped_thread_state_change-inl.h"
@@ -66,14 +67,15 @@
     CodeInfo code_info = GetCurrentOatQuickMethodHeader()->GetOptimizedCodeInfo();
     CodeInfoEncoding encoding = code_info.ExtractEncoding();
     StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
-    uint16_t number_of_dex_registers = m->GetCodeItem()->registers_size_;
+    CodeItemDataAccessor accessor(m);
+    uint16_t number_of_dex_registers = accessor.RegistersSize();
     DexRegisterMap dex_register_map =
         code_info.GetDexRegisterMapOf(stack_map, encoding, number_of_dex_registers);
     uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map);
     BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map);
     for (int i = 0; i < number_of_references; ++i) {
       int reg = registers[i];
-      CHECK(reg < m->GetCodeItem()->registers_size_);
+      CHECK_LT(reg, accessor.RegistersSize());
       DexRegisterLocation location = dex_register_map.GetDexRegisterLocation(
           reg, number_of_dex_registers, code_info, encoding);
       switch (location.GetKind()) {
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 55fa632..45b7618 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4317,15 +4317,14 @@
 
 void ClassLinker::ResolveMethodExceptionHandlerTypes(ArtMethod* method) {
   // similar to DexVerifier::ScanTryCatchBlocks and dex2oat's ResolveExceptionsForMethod.
-  const DexFile::CodeItem* code_item =
-      method->GetDexFile()->GetCodeItem(method->GetCodeItemOffset());
-  if (code_item == nullptr) {
+  CodeItemDataAccessor accessor(method);
+  if (!accessor.HasCodeItem()) {
     return;  // native or abstract method
   }
-  if (code_item->tries_size_ == 0) {
+  if (accessor.TriesSize() == 0) {
     return;  // nothing to process
   }
-  const uint8_t* handlers_ptr = DexFile::GetCatchHandlerData(*code_item, 0);
+  const uint8_t* handlers_ptr = accessor.GetCatchHandlerData(0);
   uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
   for (uint32_t idx = 0; idx < handlers_size; idx++) {
     CatchHandlerIterator iterator(handlers_ptr);
diff --git a/runtime/code_item_accessors-inl.h b/runtime/code_item_accessors-inl.h
index 1ba3c55..f63ea69 100644
--- a/runtime/code_item_accessors-inl.h
+++ b/runtime/code_item_accessors-inl.h
@@ -30,27 +30,9 @@
 inline CodeItemInstructionAccessor::CodeItemInstructionAccessor(ArtMethod* method)
     : CodeItemInstructionAccessor(method->GetDexFile(), method->GetCodeItem()) {}
 
-inline CodeItemInstructionAccessor CodeItemInstructionAccessor::CreateNullable(
-    ArtMethod* method) {
-  DCHECK(method != nullptr);
-  CodeItemInstructionAccessor ret;
-  const DexFile::CodeItem* code_item = method->GetCodeItem();
-  if (code_item != nullptr) {
-    ret.Init(method->GetDexFile(), code_item);
-  } else {
-    DCHECK(!ret.HasCodeItem()) << "Should be null initialized";
-  }
-  return ret;
-}
-
 inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method)
     : CodeItemDataAccessor(method->GetDexFile(), method->GetCodeItem()) {}
 
-inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(ArtMethod* method) {
-  DCHECK(method != nullptr);
-  return CreateNullable(method->GetDexFile(), method->GetCodeItem());
-}
-
 inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method)
     : CodeItemDebugInfoAccessor(method->GetDexFile(), method->GetCodeItem()) {}
 
@@ -59,7 +41,7 @@
   if (code_item == nullptr) {
     return;
   }
-  Init(dex_file, code_item, OatFile::GetDebugInfoOffset(*dex_file, code_item));
+  Init(dex_file, code_item, OatFile::GetDebugInfoOffset(*dex_file, code_item->debug_info_off_));
 }
 
 }  // namespace art
diff --git a/runtime/code_item_accessors-no_art-inl.h b/runtime/code_item_accessors-no_art-inl.h
index 96321b5..ebdd78b 100644
--- a/runtime/code_item_accessors-no_art-inl.h
+++ b/runtime/code_item_accessors-no_art-inl.h
@@ -24,7 +24,6 @@
 #include "standard_dex_file.h"
 
 // The no ART version is used by binaries that don't include the whole runtime.
-
 namespace art {
 
 inline void CodeItemInstructionAccessor::Init(const CompactDexFile::CodeItem& code_item) {
@@ -39,13 +38,14 @@
 
 inline void CodeItemInstructionAccessor::Init(const DexFile* dex_file,
                                               const DexFile::CodeItem* code_item) {
-  DCHECK(dex_file != nullptr);
-  DCHECK(code_item != nullptr);
-  if (dex_file->IsCompactDexFile()) {
-    Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
-  } else {
-    DCHECK(dex_file->IsStandardDexFile());
-    Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+  if (code_item != nullptr) {
+    DCHECK(dex_file != nullptr);
+    if (dex_file->IsCompactDexFile()) {
+      Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+    } else {
+      DCHECK(dex_file->IsStandardDexFile());
+      Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+    }
   }
 }
 
@@ -63,6 +63,14 @@
   return DexInstructionIterator(insns_, insns_size_in_code_units_);
 }
 
+inline IterationRange<DexInstructionIterator> CodeItemInstructionAccessor::InstructionsFrom(
+    uint32_t start_dex_pc) const {
+  DCHECK_LT(start_dex_pc, InsnsSizeInCodeUnits());
+  return {
+      DexInstructionIterator(insns_, start_dex_pc),
+      DexInstructionIterator(insns_, insns_size_in_code_units_) };
+}
+
 inline void CodeItemDataAccessor::Init(const CompactDexFile::CodeItem& code_item) {
   CodeItemInstructionAccessor::Init(code_item);
   registers_size_ = code_item.registers_size_;
@@ -81,13 +89,14 @@
 
 inline void CodeItemDataAccessor::Init(const DexFile* dex_file,
                                        const DexFile::CodeItem* code_item) {
-  DCHECK(dex_file != nullptr);
-  DCHECK(code_item != nullptr);
-  if (dex_file->IsCompactDexFile()) {
-    CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
-  } else {
-    DCHECK(dex_file->IsStandardDexFile());
-    CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+  if (code_item != nullptr) {
+    DCHECK(dex_file != nullptr);
+    if (dex_file->IsCompactDexFile()) {
+      CodeItemDataAccessor::Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+    } else {
+      DCHECK(dex_file->IsStandardDexFile());
+      CodeItemDataAccessor::Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
+    }
   }
 }
 
@@ -96,18 +105,6 @@
   Init(dex_file, code_item);
 }
 
-inline CodeItemDataAccessor CodeItemDataAccessor::CreateNullable(
-    const DexFile* dex_file,
-    const DexFile::CodeItem* code_item) {
-  CodeItemDataAccessor ret;
-  if (code_item != nullptr) {
-    ret.Init(dex_file, code_item);
-  } else {
-    DCHECK(!ret.HasCodeItem()) << "Should be null initialized";
-  }
-  return ret;
-}
-
 inline IterationRange<const DexFile::TryItem*> CodeItemDataAccessor::TryItems() const {
   const DexFile::TryItem* try_items = DexFile::GetTryItems(end(), 0u);
   return {
@@ -163,7 +160,6 @@
                                          context);
 }
 
-
 }  // namespace art
 
 #endif  // ART_RUNTIME_CODE_ITEM_ACCESSORS_NO_ART_INL_H_
diff --git a/runtime/code_item_accessors.h b/runtime/code_item_accessors.h
index 9f40114..4cbe7a1 100644
--- a/runtime/code_item_accessors.h
+++ b/runtime/code_item_accessors.h
@@ -42,6 +42,8 @@
 
   ALWAYS_INLINE DexInstructionIterator end() const;
 
+  IterationRange<DexInstructionIterator> InstructionsFrom(uint32_t start_dex_pc) const;
+
   uint32_t InsnsSizeInCodeUnits() const {
     return insns_size_in_code_units_;
   }
@@ -52,6 +54,7 @@
 
   // Return the instruction for a dex pc.
   const Instruction& InstructionAt(uint32_t dex_pc) const {
+    DCHECK_LT(dex_pc, InsnsSizeInCodeUnits());
     return *Instruction::At(insns_ + dex_pc);
   }
 
@@ -60,10 +63,6 @@
     return Insns() != nullptr;
   }
 
-  // CreateNullable allows ArtMethods that have a null code item.
-  ALWAYS_INLINE static CodeItemInstructionAccessor CreateNullable(ArtMethod* method)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
  protected:
   CodeItemInstructionAccessor() = default;
 
@@ -109,14 +108,6 @@
 
   const DexFile::TryItem* FindTryItem(uint32_t try_dex_pc) const;
 
-  // CreateNullable allows ArtMethods that have a null code item.
-  ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(ArtMethod* method)
-      REQUIRES_SHARED(Locks::mutator_lock_);
-
-  ALWAYS_INLINE static CodeItemDataAccessor CreateNullable(
-      const DexFile* dex_file,
-      const DexFile::CodeItem* code_item);
-
  protected:
   CodeItemDataAccessor() = default;
 
diff --git a/runtime/common_dex_operations.h b/runtime/common_dex_operations.h
index 267735f..8988702 100644
--- a/runtime/common_dex_operations.h
+++ b/runtime/common_dex_operations.h
@@ -23,6 +23,7 @@
 #include "base/macros.h"
 #include "base/mutex.h"
 #include "class_linker.h"
+#include "code_item_accessors.h"
 #include "handle_scope-inl.h"
 #include "instrumentation.h"
 #include "interpreter/shadow_frame.h"
@@ -52,7 +53,7 @@
 }  // namespace interpreter
 
 inline void PerformCall(Thread* self,
-                        const DexFile::CodeItem* code_item,
+                        const CodeItemDataAccessor& accessor,
                         ArtMethod* caller_method,
                         const size_t first_dest_reg,
                         ShadowFrame* callee_frame,
@@ -61,13 +62,13 @@
     REQUIRES_SHARED(Locks::mutator_lock_) {
   if (LIKELY(Runtime::Current()->IsStarted())) {
     if (use_interpreter_entrypoint) {
-      interpreter::ArtInterpreterToInterpreterBridge(self, code_item, callee_frame, result);
+      interpreter::ArtInterpreterToInterpreterBridge(self, accessor, callee_frame, result);
     } else {
       interpreter::ArtInterpreterToCompiledCodeBridge(
           self, caller_method, callee_frame, first_dest_reg, result);
     }
   } else {
-    interpreter::UnstartedRuntime::Invoke(self, code_item, callee_frame, result, first_dest_reg);
+    interpreter::UnstartedRuntime::Invoke(self, accessor, callee_frame, result, first_dest_reg);
   }
 }
 
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 575d18e..707885c 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -551,43 +551,43 @@
 void ThrowNullPointerExceptionFromDexPC(bool check_address, uintptr_t addr) {
   uint32_t throw_dex_pc;
   ArtMethod* method = Thread::Current()->GetCurrentMethod(&throw_dex_pc);
-  const DexFile::CodeItem* code = method->GetCodeItem();
-  CHECK_LT(throw_dex_pc, code->insns_size_in_code_units_);
-  const Instruction* instr = Instruction::At(&code->insns_[throw_dex_pc]);
-  if (check_address && !IsValidImplicitCheck(addr, *instr)) {
+  CodeItemInstructionAccessor accessor(method);
+  CHECK_LT(throw_dex_pc, accessor.InsnsSizeInCodeUnits());
+  const Instruction& instr = accessor.InstructionAt(throw_dex_pc);
+  if (check_address && !IsValidImplicitCheck(addr, instr)) {
     const DexFile* dex_file = method->GetDeclaringClass()->GetDexCache()->GetDexFile();
     LOG(FATAL) << "Invalid address for an implicit NullPointerException check: "
                << "0x" << std::hex << addr << std::dec
                << ", at "
-               << instr->DumpString(dex_file)
+               << instr.DumpString(dex_file)
                << " in "
                << method->PrettyMethod();
   }
 
-  switch (instr->Opcode()) {
+  switch (instr.Opcode()) {
     case Instruction::INVOKE_DIRECT:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kDirect);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kDirect);
       break;
     case Instruction::INVOKE_DIRECT_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kDirect);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kDirect);
       break;
     case Instruction::INVOKE_VIRTUAL:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kVirtual);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kVirtual);
       break;
     case Instruction::INVOKE_VIRTUAL_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kVirtual);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kVirtual);
       break;
     case Instruction::INVOKE_INTERFACE:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_35c(), kInterface);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_35c(), kInterface);
       break;
     case Instruction::INVOKE_INTERFACE_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_3rc(), kInterface);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_3rc(), kInterface);
       break;
     case Instruction::INVOKE_POLYMORPHIC:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_45cc(), kVirtual);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_45cc(), kVirtual);
       break;
     case Instruction::INVOKE_POLYMORPHIC_RANGE:
-      ThrowNullPointerExceptionForMethodAccess(instr->VRegB_4rcc(), kVirtual);
+      ThrowNullPointerExceptionForMethodAccess(instr.VRegB_4rcc(), kVirtual);
       break;
     case Instruction::INVOKE_VIRTUAL_QUICK:
     case Instruction::INVOKE_VIRTUAL_RANGE_QUICK: {
@@ -612,7 +612,7 @@
     case Instruction::IGET_CHAR:
     case Instruction::IGET_SHORT: {
       ArtField* field =
-          Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false);
+          Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false);
       ThrowNullPointerExceptionForFieldAccess(field, true /* read */);
       break;
     }
@@ -644,7 +644,7 @@
     case Instruction::IPUT_CHAR:
     case Instruction::IPUT_SHORT: {
       ArtField* field =
-          Runtime::Current()->GetClassLinker()->ResolveField(instr->VRegC_22c(), method, false);
+          Runtime::Current()->GetClassLinker()->ResolveField(instr.VRegC_22c(), method, false);
       ThrowNullPointerExceptionForFieldAccess(field, false /* write */);
       break;
     }
@@ -707,7 +707,7 @@
       const DexFile* dex_file =
           method->GetDeclaringClass()->GetDexCache()->GetDexFile();
       LOG(FATAL) << "NullPointerException at an unexpected instruction: "
-                 << instr->DumpString(dex_file)
+                 << instr.DumpString(dex_file)
                  << " in "
                  << method->PrettyMethod();
       break;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index c85c233..2be00f5 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -278,11 +278,8 @@
   }
 
  private:
-  static bool IsReturn(ArtMethod* method, uint32_t dex_pc)
-      REQUIRES_SHARED(Locks::mutator_lock_) {
-    const DexFile::CodeItem* code_item = method->GetCodeItem();
-    const Instruction* instruction = Instruction::At(&code_item->insns_[dex_pc]);
-    return instruction->IsReturn();
+  static bool IsReturn(ArtMethod* method, uint32_t dex_pc) REQUIRES_SHARED(Locks::mutator_lock_) {
+    return method->DexInstructions().InstructionAt(dex_pc).IsReturn();
   }
 
   static bool IsListeningToDexPcMoved() REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -1535,15 +1532,15 @@
  */
 static uint16_t MangleSlot(uint16_t slot, ArtMethod* m)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  const DexFile::CodeItem* code_item = m->GetCodeItem();
-  if (code_item == nullptr) {
+  CodeItemDataAccessor accessor(m);
+  if (!accessor.HasCodeItem()) {
     // We should not get here for a method without code (native, proxy or abstract). Log it and
     // return the slot as is since all registers are arguments.
     LOG(WARNING) << "Trying to mangle slot for method without code " << m->PrettyMethod();
     return slot;
   }
-  uint16_t ins_size = code_item->ins_size_;
-  uint16_t locals_size = code_item->registers_size_ - ins_size;
+  uint16_t ins_size = accessor.InsSize();
+  uint16_t locals_size = accessor.RegistersSize() - ins_size;
   if (slot >= locals_size) {
     return slot - locals_size;
   } else {
@@ -1566,8 +1563,8 @@
  */
 static uint16_t DemangleSlot(uint16_t slot, ArtMethod* m, JDWP::JdwpError* error)
     REQUIRES_SHARED(Locks::mutator_lock_) {
-  const DexFile::CodeItem* code_item = m->GetCodeItem();
-  if (code_item == nullptr) {
+  CodeItemDataAccessor accessor(m);
+  if (!accessor.HasCodeItem()) {
     // We should not get here for a method without code (native, proxy or abstract). Log it and
     // return the slot as is since all registers are arguments.
     LOG(WARNING) << "Trying to demangle slot for method without code "
@@ -1578,9 +1575,9 @@
       return slot;
     }
   } else {
-    if (slot < code_item->registers_size_) {
-      uint16_t ins_size = code_item->ins_size_;
-      uint16_t locals_size = code_item->registers_size_ - ins_size;
+    if (slot < accessor.RegistersSize()) {
+      uint16_t ins_size = accessor.InsSize();
+      uint16_t locals_size = accessor.RegistersSize() - ins_size;
       *error = JDWP::ERR_NONE;
       return (slot < ins_size) ? slot + locals_size : slot - ins_size;
     }
@@ -1793,9 +1790,9 @@
   if (m == nullptr) {
     return JDWP::ERR_INVALID_METHODID;
   }
-  const DexFile::CodeItem* code_item = m->GetCodeItem();
-  size_t byte_count = code_item->insns_size_in_code_units_ * 2;
-  const uint8_t* begin = reinterpret_cast<const uint8_t*>(code_item->insns_);
+  CodeItemDataAccessor accessor(m);
+  size_t byte_count = accessor.InsnsSizeInCodeUnits() * 2;
+  const uint8_t* begin = reinterpret_cast<const uint8_t*>(accessor.Insns());
   const uint8_t* end = begin + byte_count;
   for (const uint8_t* p = begin; p != end; ++p) {
     bytecodes->push_back(*p);
@@ -2978,9 +2975,8 @@
   Handle<mirror::Throwable> pending_exception(hs.NewHandle(self->GetException()));
   self->ClearException();
   if (kIsDebugBuild && pending_exception != nullptr) {
-    const DexFile::CodeItem* code_item = location.method->GetCodeItem();
-    const Instruction* instr = Instruction::At(&code_item->insns_[location.dex_pc]);
-    CHECK_EQ(Instruction::MOVE_EXCEPTION, instr->Opcode());
+    const Instruction& instr = location.method->DexInstructions().InstructionAt(location.dex_pc);
+    CHECK_EQ(Instruction::MOVE_EXCEPTION, instr.Opcode());
   }
 
   gJdwpState->PostLocationEvent(&location, this_object, event_flags, return_value);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 76110f2..f3a88f7 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -320,6 +320,7 @@
       debug_info_off_ = new_offset;
     }
 
+   private:
     uint16_t registers_size_;            // the number of registers used by this code
                                          //   (locals + parameters)
     uint16_t ins_size_;                  // the number of words of incoming arguments to the method
@@ -340,6 +341,14 @@
     uint16_t insns_[1];                  // actual array of bytecode.
 
    private:
+    ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
+    friend class CatchHandlerIterator;
+    friend class CodeItemDataAccessor;
+    friend class CodeItemDebugInfoAccessor;
+    friend class CodeItemInstructionAccessor;
+    friend class DexFile;  // TODO: Remove this one when it's cleaned up.
+    friend class DexFileVerifier;
+    friend class VdexFile;  // TODO: Remove this one when it's cleaned up.
     DISALLOW_COPY_AND_ASSIGN(CodeItem);
   };
 
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 6905504..87eec57 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -22,6 +22,7 @@
 
 #include "base/stl_util.h"
 #include "base/unix_file/fd_file.h"
+#include "code_item_accessors-inl.h"
 #include "common_runtime_test.h"
 #include "dex_file-inl.h"
 #include "dex_file_loader.h"
@@ -730,15 +731,8 @@
       kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true);
   const DexFile::ClassDef& class_def = raw->GetClassDef(0);
   const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1));
-  uint32_t debug_info_offset = raw->GetDebugInfoOffset(code_item);
-  ASSERT_TRUE(raw->DecodeDebugLocalInfo(code_item->registers_size_,
-                                        code_item->ins_size_,
-                                        code_item->insns_size_in_code_units_,
-                                        debug_info_offset,
-                                        true,
-                                        1,
-                                        Callback,
-                                        nullptr));
+  CodeItemDebugInfoAccessor accessor(raw.get(), code_item);
+  ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr));
 }
 
 }  // namespace art
diff --git a/runtime/dex_file_tracking_registrar.cc b/runtime/dex_file_tracking_registrar.cc
index 4de4376..c157ddc 100644
--- a/runtime/dex_file_tracking_registrar.cc
+++ b/runtime/dex_file_tracking_registrar.cc
@@ -30,6 +30,7 @@
 #endif
 #include "base/memory_tool.h"
 
+#include "code_item_accessors-inl.h"
 #include "dex_file-inl.h"
 
 namespace art {
@@ -184,9 +185,12 @@
         if (code_item != nullptr) {
           const void* code_item_begin = reinterpret_cast<const void*>(code_item);
           size_t code_item_start = reinterpret_cast<size_t>(code_item);
-          size_t code_item_start_end = reinterpret_cast<size_t>(&code_item->insns_[1]);
+          CodeItemInstructionAccessor accessor(dex_file_, code_item);
+          size_t code_item_start_end = reinterpret_cast<size_t>(accessor.Insns());
           size_t code_item_start_size = code_item_start_end - code_item_start;
-          range_values_.push_back(std::make_tuple(code_item_begin, code_item_start_size, should_poison));
+          range_values_.push_back(std::make_tuple(code_item_begin,
+                                                  code_item_start_size,
+                                                  should_poison));
         }
         cdit.Next();
       }
@@ -204,9 +208,10 @@
       while (cdit.HasNextMethod()) {
         const DexFile::CodeItem* code_item = cdit.GetMethodCodeItem();
         if (code_item != nullptr) {
-          const void* insns_begin = reinterpret_cast<const void*>(&code_item->insns_);
+          CodeItemInstructionAccessor accessor(dex_file_, code_item);
+          const void* insns_begin = reinterpret_cast<const void*>(accessor.Insns());
           // Member insns_size_in_code_units_ is in 2-byte units
-          size_t insns_size = code_item->insns_size_in_code_units_ * 2;
+          size_t insns_size = accessor.InsnsSizeInCodeUnits() * 2;
           range_values_.push_back(std::make_tuple(insns_begin, insns_size, should_poison));
         }
         cdit.Next();
diff --git a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
index d4bc1c7..d22f180 100644
--- a/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_fillarray_entrypoints.cc
@@ -28,7 +28,7 @@
                                               ArtMethod* method, Thread* self)
     REQUIRES_SHARED(Locks::mutator_lock_) {
   ScopedQuickEntrypointChecks sqec(self);
-  const uint16_t* const insns = method->GetCodeItem()->insns_;
+  const uint16_t* const insns = method->DexInstructions().Insns();
   const Instruction::ArrayDataPayload* payload =
       reinterpret_cast<const Instruction::ArrayDataPayload*>(insns + payload_offset);
   bool success = FillArrayData(array, payload);
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index ca5b799..a4d14ec 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -784,8 +784,8 @@
   DCHECK(!method->IsNative()) << method->PrettyMethod();
   uint32_t shorty_len = 0;
   ArtMethod* non_proxy_method = method->GetInterfaceMethodIfProxy(kRuntimePointerSize);
-  const DexFile::CodeItem* code_item = non_proxy_method->GetCodeItem();
-  DCHECK(code_item != nullptr) << method->PrettyMethod();
+  DCHECK(non_proxy_method->GetCodeItem() != nullptr) << method->PrettyMethod();
+  CodeItemDataAccessor accessor(non_proxy_method);
   const char* shorty = non_proxy_method->GetShorty(&shorty_len);
 
   JValue result;
@@ -795,12 +795,12 @@
   } else {
     const char* old_cause = self->StartAssertNoThreadSuspension(
         "Building interpreter shadow frame");
-    uint16_t num_regs = code_item->registers_size_;
+    uint16_t num_regs = accessor.RegistersSize();
     // No last shadow coming from quick.
     ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
         CREATE_SHADOW_FRAME(num_regs, /* link */ nullptr, method, /* dex pc */ 0);
     ShadowFrame* shadow_frame = shadow_frame_unique_ptr.get();
-    size_t first_arg_reg = code_item->registers_size_ - code_item->ins_size_;
+    size_t first_arg_reg = accessor.RegistersSize() - accessor.InsSize();
     BuildQuickShadowFrameVisitor shadow_frame_builder(sp, method->IsStatic(), shorty, shorty_len,
                                                       shadow_frame, first_arg_reg);
     shadow_frame_builder.VisitArguments();
@@ -823,7 +823,7 @@
       }
     }
 
-    result = interpreter::EnterInterpreterFromEntryPoint(self, code_item, shadow_frame);
+    result = interpreter::EnterInterpreterFromEntryPoint(self, accessor, shadow_frame);
   }
 
   // Pop transition.
@@ -1121,10 +1121,9 @@
     // code.
     if (!found_stack_map || kIsDebugBuild) {
       uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
-      const DexFile::CodeItem* code;
-      code = caller->GetCodeItem();
-      CHECK_LT(dex_pc, code->insns_size_in_code_units_);
-      const Instruction& instr = code->InstructionAt(dex_pc);
+      CodeItemInstructionAccessor accessor(caller);
+      CHECK_LT(dex_pc, accessor.InsnsSizeInCodeUnits());
+      const Instruction& instr = accessor.InstructionAt(dex_pc);
       Instruction::Code instr_code = instr.Opcode();
       bool is_range;
       switch (instr_code) {
@@ -2450,9 +2449,7 @@
     // Fetch the dex_method_idx of the target interface method from the caller.
     uint32_t dex_method_idx;
     uint32_t dex_pc = QuickArgumentVisitor::GetCallingDexPc(sp);
-    const DexFile::CodeItem* code_item = caller_method->GetCodeItem();
-    DCHECK_LT(dex_pc, code_item->insns_size_in_code_units_);
-    const Instruction& instr = code_item->InstructionAt(dex_pc);
+    const Instruction& instr = caller_method->DexInstructions().InstructionAt(dex_pc);
     Instruction::Code instr_code = instr.Opcode();
     DCHECK(instr_code == Instruction::INVOKE_INTERFACE ||
            instr_code == Instruction::INVOKE_INTERFACE_RANGE)
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 49f2021..a4ee21d 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -1246,18 +1246,17 @@
         shorty = m->GetInterfaceMethodIfProxy(kRuntimePointerSize)->GetShorty()[0];
         return false;
       }
-      const DexFile::CodeItem* code_item = m->GetCodeItem();
-      const Instruction* instr = Instruction::At(&code_item->insns_[GetDexPc()]);
-      if (instr->IsInvoke()) {
+      const Instruction& instr = m->DexInstructions().InstructionAt(GetDexPc());
+      if (instr.IsInvoke()) {
         const DexFile* dex_file = m->GetDexFile();
-        if (interpreter::IsStringInit(dex_file, instr->VRegB())) {
+        if (interpreter::IsStringInit(dex_file, instr.VRegB())) {
           // Invoking string init constructor is turned into invoking
           // StringFactory.newStringFromChars() which returns a string.
           shorty = 'L';
           return false;
         }
         // A regular invoke, use callee's shorty.
-        uint32_t method_idx = instr->VRegB();
+        uint32_t method_idx = instr.VRegB();
         shorty = dex_file->GetMethodShorty(method_idx)[0];
       }
       // Stop stack walking since we've seen a Java frame.
diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc
index 01b7d4e..9b81819 100644
--- a/runtime/interpreter/interpreter.cc
+++ b/runtime/interpreter/interpreter.cc
@@ -240,7 +240,7 @@
 
 static inline JValue Execute(
     Thread* self,
-    const DexFile::CodeItem* code_item,
+    const CodeItemDataAccessor& accessor,
     ShadowFrame& shadow_frame,
     JValue result_register,
     bool stay_in_interpreter = false) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -254,11 +254,13 @@
     ArtMethod *method = shadow_frame.GetMethod();
 
     if (UNLIKELY(instrumentation->HasMethodEntryListeners())) {
-      instrumentation->MethodEnterEvent(self, shadow_frame.GetThisObject(code_item->ins_size_),
-                                        method, 0);
+      instrumentation->MethodEnterEvent(self,
+                                        shadow_frame.GetThisObject(accessor.InsSize()),
+                                        method,
+                                        0);
       if (UNLIKELY(self->IsExceptionPending())) {
         instrumentation->MethodUnwindEvent(self,
-                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetThisObject(accessor.InsSize()),
                                            method,
                                            0);
         return JValue();
@@ -277,7 +279,7 @@
           // Calculate the offset of the first input reg. The input registers are in the high regs.
           // It's ok to access the code item here since JIT code will have been touched by the
           // interpreter and compiler already.
-          uint16_t arg_offset = code_item->registers_size_ - code_item->ins_size_;
+          uint16_t arg_offset = accessor.RegistersSize() - accessor.InsSize();
           ArtInterpreterToCompiledCodeBridge(self, nullptr, &shadow_frame, arg_offset, &result);
           // Push the shadow frame back as the caller will expect it.
           self->PushShadowFrame(&shadow_frame);
@@ -302,27 +304,27 @@
     if (kInterpreterImplKind == kMterpImplKind) {
       if (transaction_active) {
         // No Mterp variant - just use the switch interpreter.
-        return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<false, true>(self, accessor, shadow_frame, result_register,
                                               false);
       } else if (UNLIKELY(!Runtime::Current()->IsStarted())) {
-        return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
                                                false);
       } else {
         while (true) {
           // Mterp does not support all instrumentation/debugging.
           if (MterpShouldSwitchInterpreters() != 0) {
-            return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
+            return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
                                                    false);
           }
           bool returned = ExecuteMterpImpl(self,
-                                           code_item->insns_,
+                                           accessor.Insns(),
                                            &shadow_frame,
                                            &result_register);
           if (returned) {
             return result_register;
           } else {
             // Mterp didn't like that instruction.  Single-step it with the reference interpreter.
-            result_register = ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame,
+            result_register = ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame,
                                                               result_register, true);
             if (shadow_frame.GetDexPC() == dex::kDexNoIndex) {
               // Single-stepped a return or an exception not handled locally.  Return to caller.
@@ -334,10 +336,10 @@
     } else {
       DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
       if (transaction_active) {
-        return ExecuteSwitchImpl<false, true>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<false, true>(self, accessor, shadow_frame, result_register,
                                               false);
       } else {
-        return ExecuteSwitchImpl<false, false>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<false, false>(self, accessor, shadow_frame, result_register,
                                                false);
       }
     }
@@ -346,19 +348,19 @@
     if (kInterpreterImplKind == kMterpImplKind) {
       // No access check variants for Mterp.  Just use the switch version.
       if (transaction_active) {
-        return ExecuteSwitchImpl<true, true>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<true, true>(self, accessor, shadow_frame, result_register,
                                              false);
       } else {
-        return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<true, false>(self, accessor, shadow_frame, result_register,
                                               false);
       }
     } else {
       DCHECK_EQ(kInterpreterImplKind, kSwitchImplKind);
       if (transaction_active) {
-        return ExecuteSwitchImpl<true, true>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<true, true>(self, accessor, shadow_frame, result_register,
                                              false);
       } else {
-        return ExecuteSwitchImpl<true, false>(self, code_item, shadow_frame, result_register,
+        return ExecuteSwitchImpl<true, false>(self, accessor, shadow_frame, result_register,
                                               false);
       }
     }
@@ -387,12 +389,12 @@
   }
 
   const char* old_cause = self->StartAssertNoThreadSuspension("EnterInterpreterFromInvoke");
-  const DexFile::CodeItem* code_item = method->GetCodeItem();
+  CodeItemDataAccessor accessor(method);
   uint16_t num_regs;
   uint16_t num_ins;
-  if (code_item != nullptr) {
-    num_regs =  code_item->registers_size_;
-    num_ins = code_item->ins_size_;
+  if (accessor.HasCodeItem()) {
+    num_regs =  accessor.RegistersSize();
+    num_ins = accessor.InsSize();
   } else if (!method->IsInvokable()) {
     self->EndAssertNoThreadSuspension(old_cause);
     method->ThrowInvocationTimeError();
@@ -454,7 +456,7 @@
     }
   }
   if (LIKELY(!method->IsNative())) {
-    JValue r = Execute(self, code_item, *shadow_frame, JValue(), stay_in_interpreter);
+    JValue r = Execute(self, accessor, *shadow_frame, JValue(), stay_in_interpreter);
     if (result != nullptr) {
       *result = r;
     }
@@ -497,7 +499,7 @@
     DCHECK(!shadow_frame->GetMethod()->MustCountLocks());
 
     self->SetTopOfShadowStack(shadow_frame);
-    const DexFile::CodeItem* code_item = shadow_frame->GetMethod()->GetCodeItem();
+    CodeItemDataAccessor accessor(shadow_frame->GetMethod());
     const uint32_t dex_pc = shadow_frame->GetDexPC();
     uint32_t new_dex_pc = dex_pc;
     if (UNLIKELY(self->IsExceptionPending())) {
@@ -510,7 +512,7 @@
           self, *shadow_frame, instrumentation) ? shadow_frame->GetDexPC() : dex::kDexNoIndex;
     } else if (!from_code) {
       // Deoptimization is not called from code directly.
-      const Instruction* instr = Instruction::At(&code_item->insns_[dex_pc]);
+      const Instruction* instr = &accessor.InstructionAt(dex_pc);
       if (deopt_method_type == DeoptimizationMethodType::kKeepDexPc) {
         DCHECK(first);
         // Need to re-execute the dex instruction.
@@ -566,7 +568,7 @@
     }
     if (new_dex_pc != dex::kDexNoIndex) {
       shadow_frame->SetDexPC(new_dex_pc);
-      value = Execute(self, code_item, *shadow_frame, value);
+      value = Execute(self, accessor, *shadow_frame, value);
     }
     ShadowFrame* old_frame = shadow_frame;
     shadow_frame = shadow_frame->GetLink();
@@ -580,7 +582,7 @@
   ret_val->SetJ(value.GetJ());
 }
 
-JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item,
+JValue EnterInterpreterFromEntryPoint(Thread* self, const CodeItemDataAccessor& accessor,
                                       ShadowFrame* shadow_frame) {
   DCHECK_EQ(self, Thread::Current());
   bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
@@ -593,11 +595,11 @@
   if (jit != nullptr) {
     jit->NotifyCompiledCodeToInterpreterTransition(self, shadow_frame->GetMethod());
   }
-  return Execute(self, code_item, *shadow_frame, JValue());
+  return Execute(self, accessor, *shadow_frame, JValue());
 }
 
 void ArtInterpreterToInterpreterBridge(Thread* self,
-                                       const DexFile::CodeItem* code_item,
+                                       const CodeItemDataAccessor& accessor,
                                        ShadowFrame* shadow_frame,
                                        JValue* result) {
   bool implicit_check = !Runtime::Current()->ExplicitStackOverflowChecks();
@@ -626,7 +628,7 @@
   }
 
   if (LIKELY(!shadow_frame->GetMethod()->IsNative())) {
-    result->SetJ(Execute(self, code_item, *shadow_frame, JValue()).GetJ());
+    result->SetJ(Execute(self, accessor, *shadow_frame, JValue()).GetJ());
   } else {
     // We don't expect to be asked to interpret native code (which is entered via a JNI compiler
     // generated stub) except during testing and image writing.
diff --git a/runtime/interpreter/interpreter.h b/runtime/interpreter/interpreter.h
index df8568e..f9d7774 100644
--- a/runtime/interpreter/interpreter.h
+++ b/runtime/interpreter/interpreter.h
@@ -27,6 +27,7 @@
 }  // namespace mirror
 
 class ArtMethod;
+class CodeItemDataAccessor;
 union JValue;
 class ShadowFrame;
 class Thread;
@@ -52,12 +53,15 @@
                                            DeoptimizationMethodType method_type)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-extern JValue EnterInterpreterFromEntryPoint(Thread* self, const DexFile::CodeItem* code_item,
+extern JValue EnterInterpreterFromEntryPoint(Thread* self,
+                                             const CodeItemDataAccessor& accessor,
                                              ShadowFrame* shadow_frame)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
-void ArtInterpreterToInterpreterBridge(Thread* self, const DexFile::CodeItem* code_item,
-                                       ShadowFrame* shadow_frame, JValue* result)
+void ArtInterpreterToInterpreterBridge(Thread* self,
+                                       const CodeItemDataAccessor& accessor,
+                                       ShadowFrame* shadow_frame,
+                                       JValue* result)
     REQUIRES_SHARED(Locks::mutator_lock_);
 
 // One-time sanity check.
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 122d1a8..deed16b 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -1320,7 +1320,7 @@
   }
 
   // Compute method information.
-  const DexFile::CodeItem* code_item = called_method->GetCodeItem();
+  CodeItemDataAccessor accessor(called_method);
   // Number of registers for the callee's call frame.
   uint16_t num_regs;
   // Test whether to use the interpreter or compiler entrypoint, and save that result to pass to
@@ -1334,7 +1334,7 @@
       ClassLinker::ShouldUseInterpreterEntrypoint(
           called_method,
           called_method->GetEntryPointFromQuickCompiledCode());
-  if (LIKELY(code_item != nullptr)) {
+  if (LIKELY(accessor.HasCodeItem())) {
     // When transitioning to compiled code, space only needs to be reserved for the input registers.
     // The rest of the frame gets discarded. This also prevents accessing the called method's code
     // item, saving memory by keeping code items of compiled code untouched.
@@ -1342,8 +1342,8 @@
       DCHECK(!Runtime::Current()->IsAotCompiler()) << "Compiler should use interpreter entrypoint";
       num_regs = number_of_inputs;
     } else {
-      num_regs = code_item->registers_size_;
-      DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, code_item->ins_size_);
+      num_regs = accessor.RegistersSize();
+      DCHECK_EQ(string_init ? number_of_inputs - 1 : number_of_inputs, accessor.InsSize());
     }
   } else {
     DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
@@ -1367,7 +1367,7 @@
     DCHECK_GT(num_regs, 0u);  // As the method is an instance method, there should be at least 1.
 
     // The new StringFactory call is static and has one fewer argument.
-    if (code_item == nullptr) {
+    if (!accessor.HasCodeItem()) {
       DCHECK(called_method->IsNative() || called_method->IsProxyMethod());
       num_regs--;
     }  // else ... don't need to change num_regs since it comes up from the string_init's code item
@@ -1499,7 +1499,7 @@
   }
 
   PerformCall(self,
-              code_item,
+              accessor,
               shadow_frame.GetMethod(),
               first_dest_reg,
               new_shadow_frame,
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 094f086..81c1e1d 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -67,7 +67,7 @@
   {                                                                                             \
     if (UNLIKELY(instrumentation->HasDexPcListeners()) &&                                       \
         UNLIKELY(!DoDexPcMoveEvent(self,                                                        \
-                                   code_item,                                                   \
+                                   accessor,                                                    \
                                    shadow_frame,                                                \
                                    dex_pc,                                                      \
                                    instrumentation,                                             \
@@ -125,7 +125,7 @@
 // jvmti-agents while handling breakpoint or single step events. We had to move this into its own
 // function because it was making ExecuteSwitchImpl have too large a stack.
 NO_INLINE static bool DoDexPcMoveEvent(Thread* self,
-                                       const DexFile::CodeItem* code_item,
+                                       const CodeItemDataAccessor& accessor,
                                        const ShadowFrame& shadow_frame,
                                        uint32_t dex_pc,
                                        const instrumentation::Instrumentation* instrumentation,
@@ -139,7 +139,7 @@
       hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot()));
   self->ClearException();
   instrumentation->DexPcMovedEvent(self,
-                                   shadow_frame.GetThisObject(code_item->ins_size_),
+                                   shadow_frame.GetThisObject(accessor.InsSize()),
                                    shadow_frame.GetMethod(),
                                    dex_pc);
   if (UNLIKELY(self->IsExceptionPending())) {
@@ -188,7 +188,7 @@
 }
 
 template<bool do_access_check, bool transaction_active>
-JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
+JValue ExecuteSwitchImpl(Thread* self, const CodeItemDataAccessor& accessor,
                          ShadowFrame& shadow_frame, JValue result_register,
                          bool interpret_one_instruction) {
   constexpr bool do_assignability_check = do_access_check;
@@ -200,10 +200,10 @@
 
   uint32_t dex_pc = shadow_frame.GetDexPC();
   const auto* const instrumentation = Runtime::Current()->GetInstrumentation();
-  const uint16_t* const insns = code_item->insns_;
+  ArtMethod* method = shadow_frame.GetMethod();
+  const uint16_t* const insns = accessor.Insns();
   const Instruction* inst = Instruction::At(insns + dex_pc);
   uint16_t inst_data;
-  ArtMethod* method = shadow_frame.GetMethod();
   jit::Jit* jit = Runtime::Current()->GetJit();
 
   do {
@@ -303,7 +303,7 @@
                      !SendMethodExitEvents(self,
                                            instrumentation,
                                            shadow_frame,
-                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetThisObject(accessor.InsSize()),
                                            shadow_frame.GetMethod(),
                                            inst->GetDexPc(insns),
                                            result))) {
@@ -325,7 +325,7 @@
                      !SendMethodExitEvents(self,
                                            instrumentation,
                                            shadow_frame,
-                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetThisObject(accessor.InsSize()),
                                            shadow_frame.GetMethod(),
                                            inst->GetDexPc(insns),
                                            result))) {
@@ -348,7 +348,7 @@
                      !SendMethodExitEvents(self,
                                            instrumentation,
                                            shadow_frame,
-                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetThisObject(accessor.InsSize()),
                                            shadow_frame.GetMethod(),
                                            inst->GetDexPc(insns),
                                            result))) {
@@ -370,7 +370,7 @@
                      !SendMethodExitEvents(self,
                                            instrumentation,
                                            shadow_frame,
-                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetThisObject(accessor.InsSize()),
                                            shadow_frame.GetMethod(),
                                            inst->GetDexPc(insns),
                                            result))) {
@@ -412,7 +412,7 @@
                      !SendMethodExitEvents(self,
                                            instrumentation,
                                            shadow_frame,
-                                           shadow_frame.GetThisObject(code_item->ins_size_),
+                                           shadow_frame.GetThisObject(accessor.InsSize()),
                                            shadow_frame.GetMethod(),
                                            inst->GetDexPc(insns),
                                            result))) {
@@ -2484,19 +2484,19 @@
 
 // Explicit definitions of ExecuteSwitchImpl.
 template HOT_ATTR
-JValue ExecuteSwitchImpl<true, false>(Thread* self, const DexFile::CodeItem* code_item,
+JValue ExecuteSwitchImpl<true, false>(Thread* self, const CodeItemDataAccessor& accessor,
                                       ShadowFrame& shadow_frame, JValue result_register,
                                       bool interpret_one_instruction);
 template HOT_ATTR
-JValue ExecuteSwitchImpl<false, false>(Thread* self, const DexFile::CodeItem* code_item,
+JValue ExecuteSwitchImpl<false, false>(Thread* self, const CodeItemDataAccessor& accessor,
                                        ShadowFrame& shadow_frame, JValue result_register,
                                        bool interpret_one_instruction);
 template
-JValue ExecuteSwitchImpl<true, true>(Thread* self, const DexFile::CodeItem* code_item,
+JValue ExecuteSwitchImpl<true, true>(Thread* self, const CodeItemDataAccessor& accessor,
                                      ShadowFrame& shadow_frame, JValue result_register,
                                      bool interpret_one_instruction);
 template
-JValue ExecuteSwitchImpl<false, true>(Thread* self, const DexFile::CodeItem* code_item,
+JValue ExecuteSwitchImpl<false, true>(Thread* self, const CodeItemDataAccessor& accessor,
                                       ShadowFrame& shadow_frame, JValue result_register,
                                       bool interpret_one_instruction);
 
diff --git a/runtime/interpreter/interpreter_switch_impl.h b/runtime/interpreter/interpreter_switch_impl.h
index 267df2e..aa0f854 100644
--- a/runtime/interpreter/interpreter_switch_impl.h
+++ b/runtime/interpreter/interpreter_switch_impl.h
@@ -25,6 +25,7 @@
 
 namespace art {
 
+class CodeItemDataAccessor;
 class ShadowFrame;
 class Thread;
 
@@ -32,7 +33,7 @@
 
 template<bool do_access_check, bool transaction_active>
 JValue ExecuteSwitchImpl(Thread* self,
-                         const DexFile::CodeItem* code_item,
+                         const CodeItemDataAccessor& accessor,
                          ShadowFrame& shadow_frame,
                          JValue result_register,
                          bool interpret_one_instruction) REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/interpreter/shadow_frame.cc b/runtime/interpreter/shadow_frame.cc
index ab154cf..fe7e3e0 100644
--- a/runtime/interpreter/shadow_frame.cc
+++ b/runtime/interpreter/shadow_frame.cc
@@ -27,9 +27,9 @@
   } else if (m->IsNative()) {
     return GetVRegReference(0);
   } else {
-    const DexFile::CodeItem* code_item = m->GetCodeItem();
-    CHECK(code_item != nullptr) << ArtMethod::PrettyMethod(m);
-    uint16_t reg = code_item->registers_size_ - code_item->ins_size_;
+    CHECK(m->GetCodeItem() != nullptr) << ArtMethod::PrettyMethod(m);
+    CodeItemDataAccessor accessor(m);
+    uint16_t reg = accessor.RegistersSize() - accessor.InsSize();
     return GetVRegReference(reg);
   }
 }
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index dece830..d1436fa 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -1910,7 +1910,7 @@
   tables_initialized_ = true;
 }
 
-void UnstartedRuntime::Invoke(Thread* self, const DexFile::CodeItem* code_item,
+void UnstartedRuntime::Invoke(Thread* self, const CodeItemDataAccessor& accessor,
                               ShadowFrame* shadow_frame, JValue* result, size_t arg_offset) {
   // In a runtime that's not started we intercept certain methods to avoid complicated dependency
   // problems in core libraries.
@@ -1930,7 +1930,7 @@
     self->PopShadowFrame();
   } else {
     // Not special, continue with regular interpreter execution.
-    ArtInterpreterToInterpreterBridge(self, code_item, shadow_frame, result);
+    ArtInterpreterToInterpreterBridge(self, accessor, shadow_frame, result);
   }
 }
 
diff --git a/runtime/interpreter/unstarted_runtime.h b/runtime/interpreter/unstarted_runtime.h
index bc9ead8..2e86dea 100644
--- a/runtime/interpreter/unstarted_runtime.h
+++ b/runtime/interpreter/unstarted_runtime.h
@@ -25,6 +25,7 @@
 namespace art {
 
 class ArtMethod;
+class CodeItemDataAccessor;
 class Thread;
 class ShadowFrame;
 
@@ -48,7 +49,7 @@
   static void Initialize();
 
   static void Invoke(Thread* self,
-                     const DexFile::CodeItem* code_item,
+                     const CodeItemDataAccessor& accessor,
                      ShadowFrame* shadow_frame,
                      JValue* result,
                      size_t arg_offset)
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 278bc57..12bf79d 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -467,7 +467,8 @@
   // Fetch some data before looking up for an OSR method. We don't want thread
   // suspension once we hold an OSR method, as the JIT code cache could delete the OSR
   // method while we are being suspended.
-  const size_t number_of_vregs = method->GetCodeItem()->registers_size_;
+  CodeItemDataAccessor accessor(method);
+  const size_t number_of_vregs = accessor.RegistersSize();
   const char* shorty = method->GetShorty();
   std::string method_name(VLOG_IS_ON(jit) ? method->PrettyMethod() : "");
   void** memory = nullptr;
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 8eb31c1..88f30a8 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -408,7 +408,7 @@
                                             const InstructionOperands* const operands,
                                             JValue* result) REQUIRES_SHARED(Locks::mutator_lock_) {
   // Compute method information.
-  const DexFile::CodeItem* code_item = called_method->GetCodeItem();
+  CodeItemDataAccessor accessor(called_method);
 
   // Number of registers for the callee's call frame. Note that for non-exact
   // invokes, we always derive this information from the callee method. We
@@ -419,10 +419,10 @@
   uint16_t num_regs;
   size_t num_input_regs;
   size_t first_dest_reg;
-  if (LIKELY(code_item != nullptr)) {
-    num_regs = code_item->registers_size_;
-    first_dest_reg = num_regs - code_item->ins_size_;
-    num_input_regs = code_item->ins_size_;
+  if (LIKELY(accessor.HasCodeItem())) {
+    num_regs = accessor.RegistersSize();
+    first_dest_reg = num_regs - accessor.InsSize();
+    num_input_regs = accessor.InsSize();
     // Parameter registers go at the end of the shadow frame.
     DCHECK_NE(first_dest_reg, (size_t)-1);
   } else {
@@ -496,7 +496,7 @@
   bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
       called_method, called_method->GetEntryPointFromQuickCompiledCode());
   PerformCall(self,
-              code_item,
+              accessor,
               shadow_frame.GetMethod(),
               first_dest_reg,
               new_shadow_frame,
@@ -550,10 +550,9 @@
   // - One for the only method argument (an EmulatedStackFrame).
   static constexpr size_t kNumRegsForTransform = 2;
 
-  const DexFile::CodeItem* code_item = called_method->GetCodeItem();
-  DCHECK(code_item != nullptr);
-  DCHECK_EQ(kNumRegsForTransform, code_item->registers_size_);
-  DCHECK_EQ(kNumRegsForTransform, code_item->ins_size_);
+  CodeItemDataAccessor accessor(called_method);
+  DCHECK_EQ(kNumRegsForTransform, accessor.RegistersSize());
+  DCHECK_EQ(kNumRegsForTransform, accessor.InsSize());
 
   ShadowFrameAllocaUniquePtr shadow_frame_unique_ptr =
       CREATE_SHADOW_FRAME(kNumRegsForTransform, &shadow_frame, called_method, /* dex pc */ 0);
@@ -589,7 +588,7 @@
   bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
       called_method, called_method->GetEntryPointFromQuickCompiledCode());
   PerformCall(self,
-              code_item,
+              accessor,
               shadow_frame.GetMethod(),
               0 /* first destination register */,
               new_shadow_frame,
@@ -1035,14 +1034,14 @@
   }
 
   // Compute method information.
-  const DexFile::CodeItem* code_item = called_method->GetCodeItem();
+  CodeItemDataAccessor accessor(called_method);
   uint16_t num_regs;
   size_t num_input_regs;
   size_t first_dest_reg;
-  if (LIKELY(code_item != nullptr)) {
-    num_regs = code_item->registers_size_;
-    first_dest_reg = num_regs - code_item->ins_size_;
-    num_input_regs = code_item->ins_size_;
+  if (LIKELY(accessor.HasCodeItem())) {
+    num_regs = accessor.RegistersSize();
+    first_dest_reg = num_regs - accessor.InsSize();
+    num_input_regs = accessor.InsSize();
     // Parameter registers go at the end of the shadow frame.
     DCHECK_NE(first_dest_reg, (size_t)-1);
   } else {
@@ -1066,7 +1065,7 @@
   bool use_interpreter_entrypoint = ClassLinker::ShouldUseInterpreterEntrypoint(
       called_method, called_method->GetEntryPointFromQuickCompiledCode());
   PerformCall(self,
-              code_item,
+              accessor,
               shadow_frame.GetMethod(),
               first_dest_reg,
               new_shadow_frame,
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index cfef9c7..542692f 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1377,9 +1377,9 @@
   }
 
   // Is there any reason to believe there's any synchronization in this method?
-  const DexFile::CodeItem* code_item = m->GetCodeItem();
-  CHECK(code_item != nullptr) << m->PrettyMethod();
-  if (code_item->tries_size_ == 0) {
+  CHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod();
+  CodeItemDataAccessor accessor(m);
+  if (accessor.TriesSize() == 0) {
     return;  // No "tries" implies no synchronization, so no held locks to report.
   }
 
@@ -1399,11 +1399,10 @@
   for (verifier::MethodVerifier::DexLockInfo& dex_lock_info : monitor_enter_dex_pcs) {
     // As a debug check, check that dex PC corresponds to a monitor-enter.
     if (kIsDebugBuild) {
-      const Instruction* monitor_enter_instruction =
-          Instruction::At(&code_item->insns_[dex_lock_info.dex_pc]);
-      CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER)
+      const Instruction& monitor_enter_instruction = accessor.InstructionAt(dex_lock_info.dex_pc);
+      CHECK_EQ(monitor_enter_instruction.Opcode(), Instruction::MONITOR_ENTER)
           << "expected monitor-enter @" << dex_lock_info.dex_pc << "; was "
-          << reinterpret_cast<const void*>(monitor_enter_instruction);
+          << reinterpret_cast<const void*>(&monitor_enter_instruction);
     }
 
     // Iterate through the set of dex registers, as the compiler may not have held all of them
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 32f8df7..f437db2 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1500,14 +1500,10 @@
   }
 }
 
-uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file,
-                                     const DexFile::CodeItem* code_item) {
-  if (code_item == nullptr) {
-    return 0;
-  }
-  const uint32_t debug_info_off = code_item->debug_info_off_;
+uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off) {
   // Note that although the specification says that 0 should be used if there
   // is no debug information, some applications incorrectly use 0xFFFFFFFF.
+  // The following check also handles debug_info_off == 0.
   if (debug_info_off < dex_file.Size() || debug_info_off == 0xFFFFFFFF) {
     return debug_info_off;
   }
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 36a4d7b..1fb17a4 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -114,9 +114,9 @@
                                const char* abs_dex_location,
                                std::string* error_msg);
 
-  // Return the debug info offset of the code item `item` located in `dex_file`.
-  static uint32_t GetDebugInfoOffset(const DexFile& dex_file,
-                                     const DexFile::CodeItem* item);
+  // Return the actual debug info offset for an offset that might be actually pointing to
+  // dequickening info. The returned debug info offset is the one originally in the the dex file.
+  static uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off);
 
   virtual ~OatFile();
 
diff --git a/runtime/quick_exception_handler.cc b/runtime/quick_exception_handler.cc
index a7771ab..06b94c3 100644
--- a/runtime/quick_exception_handler.cc
+++ b/runtime/quick_exception_handler.cc
@@ -222,7 +222,8 @@
     self_->DumpStack(LOG_STREAM(INFO) << "Setting catch phis: ");
   }
 
-  const size_t number_of_vregs = handler_method_->GetCodeItem()->registers_size_;
+  CodeItemDataAccessor accessor(handler_method_);
+  const size_t number_of_vregs = accessor.RegistersSize();
   CodeInfo code_info = handler_method_header_->GetOptimizedCodeInfo();
   CodeInfoEncoding encoding = code_info.ExtractEncoding();
 
@@ -359,7 +360,8 @@
       const size_t frame_id = GetFrameId();
       ShadowFrame* new_frame = GetThread()->FindDebuggerShadowFrame(frame_id);
       const bool* updated_vregs;
-      const size_t num_regs = method->GetCodeItem()->registers_size_;
+      CodeItemDataAccessor accessor(method);
+      const size_t num_regs = accessor.RegistersSize();
       if (new_frame == nullptr) {
         new_frame = ShadowFrame::CreateDeoptimizedFrame(num_regs, nullptr, method, GetDexPc());
         updated_vregs = nullptr;
@@ -406,7 +408,8 @@
     uintptr_t native_pc_offset = method_header->NativeQuickPcOffset(GetCurrentQuickFramePc());
     CodeInfoEncoding encoding = code_info.ExtractEncoding();
     StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset, encoding);
-    const size_t number_of_vregs = m->GetCodeItem()->registers_size_;
+    CodeItemDataAccessor accessor(m);
+    const size_t number_of_vregs = accessor.RegistersSize();
     uint32_t register_mask = code_info.GetRegisterMaskOf(encoding, stack_map);
     BitMemoryRegion stack_mask = code_info.GetStackMaskOf(encoding, stack_map);
     DexRegisterMap vreg_map = IsInInlinedFrame()
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 5ad1f7c..bbea48b 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -154,13 +154,13 @@
       return cur_shadow_frame_->GetVRegReference(0);
     }
   } else {
-    const DexFile::CodeItem* code_item = m->GetCodeItem();
-    if (code_item == nullptr) {
+    CodeItemDataAccessor accessor(m);
+    if (!accessor.HasCodeItem()) {
       UNIMPLEMENTED(ERROR) << "Failed to determine this object of abstract or proxy method: "
           << ArtMethod::PrettyMethod(m);
       return nullptr;
     } else {
-      uint16_t reg = code_item->registers_size_ - code_item->ins_size_;
+      uint16_t reg = accessor.RegistersSize() - accessor.InsSize();
       uint32_t value = 0;
       bool success = GetVReg(m, reg, kReferenceVReg, &value);
       // We currently always guarantee the `this` object is live throughout the method.
@@ -223,11 +223,11 @@
 bool StackVisitor::GetVRegFromOptimizedCode(ArtMethod* m, uint16_t vreg, VRegKind kind,
                                             uint32_t* val) const {
   DCHECK_EQ(m, GetMethod());
-  const DexFile::CodeItem* code_item = m->GetCodeItem();
-  DCHECK(code_item != nullptr) << m->PrettyMethod();  // Can't be null or how would we compile
-                                                      // its instructions?
-  uint16_t number_of_dex_registers = code_item->registers_size_;
-  DCHECK_LT(vreg, code_item->registers_size_);
+  // Can't be null or how would we compile its instructions?
+  DCHECK(m->GetCodeItem() != nullptr) << m->PrettyMethod();
+  CodeItemDataAccessor accessor(m);
+  uint16_t number_of_dex_registers = accessor.RegistersSize();
+  DCHECK_LT(vreg, number_of_dex_registers);
   const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
   CodeInfo code_info = method_header->GetOptimizedCodeInfo();
   CodeInfoEncoding encoding = code_info.ExtractEncoding();
@@ -395,8 +395,8 @@
                            uint16_t vreg,
                            uint32_t new_value,
                            VRegKind kind) {
-  const DexFile::CodeItem* code_item = m->GetCodeItem();
-  if (code_item == nullptr) {
+  CodeItemDataAccessor accessor(m);
+  if (!accessor.HasCodeItem()) {
     return false;
   }
   ShadowFrame* shadow_frame = GetCurrentShadowFrame();
@@ -404,7 +404,7 @@
     // This is a compiled frame: we must prepare and update a shadow frame that will
     // be executed by the interpreter after deoptimization of the stack.
     const size_t frame_id = GetFrameId();
-    const uint16_t num_regs = code_item->registers_size_;
+    const uint16_t num_regs = accessor.RegistersSize();
     shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc());
     CHECK(shadow_frame != nullptr);
     // Remember the vreg has been set for debugging and must not be overwritten by the
@@ -432,15 +432,15 @@
     LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi;
     UNREACHABLE();
   }
-  const DexFile::CodeItem* code_item = m->GetCodeItem();
-  if (code_item == nullptr) {
+  CodeItemDataAccessor accessor(m);
+  if (!accessor.HasCodeItem()) {
     return false;
   }
   ShadowFrame* shadow_frame = GetCurrentShadowFrame();
   if (shadow_frame == nullptr) {
     // This is a compiled frame: we must prepare for deoptimization (see SetVRegFromDebugger).
     const size_t frame_id = GetFrameId();
-    const uint16_t num_regs = code_item->registers_size_;
+    const uint16_t num_regs = accessor.RegistersSize();
     shadow_frame = thread_->FindOrCreateDebuggerShadowFrame(frame_id, num_regs, m, GetDexPc());
     CHECK(shadow_frame != nullptr);
     // Remember the vreg pair has been set for debugging and must not be overwritten by the
diff --git a/runtime/thread.cc b/runtime/thread.cc
index b86e56b..6babd75 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -3425,7 +3425,7 @@
                        const CodeInfoEncoding& _encoding,
                        const StackMap& map,
                        RootVisitor& _visitor)
-          : number_of_dex_registers(method->GetCodeItem()->registers_size_),
+          : number_of_dex_registers(CodeItemDataAccessor(method).RegistersSize()),
             code_info(_code_info),
             encoding(_encoding),
             dex_register_map(code_info.GetDexRegisterMapOf(map,
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 4ff49ed..be6915f 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -350,13 +350,13 @@
   }
 }
 
-static bool IsLargeMethod(const DexFile::CodeItem* const code_item) {
-  if (code_item == nullptr) {
+static bool IsLargeMethod(const CodeItemDataAccessor& accessor) {
+  if (!accessor.HasCodeItem()) {
     return false;
   }
 
-  uint16_t registers_size = code_item->registers_size_;
-  uint32_t insns_size = code_item->insns_size_in_code_units_;
+  uint16_t registers_size = accessor.RegistersSize();
+  uint32_t insns_size = accessor.InsnsSizeInCodeUnits();
 
   return registers_size * insns_size > 4*1024*1024;
 }
@@ -494,7 +494,7 @@
     if (duration_ns > MsToNs(100)) {
       LOG(WARNING) << "Verification of " << dex_file->PrettyMethod(method_idx)
                    << " took " << PrettyDuration(duration_ns)
-                   << (IsLargeMethod(code_item) ? " (large method)" : "");
+                   << (IsLargeMethod(verifier.CodeItem()) ? " (large method)" : "");
     }
   }
   result.types = verifier.encountered_failure_types_;
@@ -567,7 +567,7 @@
       dex_cache_(dex_cache),
       class_loader_(class_loader),
       class_def_(class_def),
-      code_item_accessor_(CodeItemDataAccessor::CreateNullable(dex_file, code_item)),
+      code_item_accessor_(dex_file, code_item),
       declaring_class_(nullptr),
       interesting_dex_pc_(-1),
       monitor_enter_dex_pcs_(nullptr),
diff --git a/test/466-get-live-vreg/get_live_vreg_jni.cc b/test/466-get-live-vreg/get_live_vreg_jni.cc
index 6cea673..24792f1 100644
--- a/test/466-get-live-vreg/get_live_vreg_jni.cc
+++ b/test/466-get-live-vreg/get_live_vreg_jni.cc
@@ -16,6 +16,7 @@
 
 #include "arch/context.h"
 #include "art_method-inl.h"
+#include "code_item_accessors-inl.h"
 #include "jni.h"
 #include "oat_quick_method_header.h"
 #include "scoped_thread_state_change-inl.h"
@@ -41,7 +42,7 @@
       CHECK(GetVReg(m, 0, kIntVReg, &value));
       CHECK_EQ(value, 42u);
     } else if (m_name.compare("$opt$noinline$testIntervalHole") == 0) {
-      uint32_t number_of_dex_registers = m->GetCodeItem()->registers_size_;
+      uint32_t number_of_dex_registers = CodeItemDataAccessor(m).RegistersSize();
       uint32_t dex_register_of_first_parameter = number_of_dex_registers - 2;
       found_method_ = true;
       uint32_t value = 0;