Merge "cpplint: Cleanup errors"
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index a94dbe9..cc452fc 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -112,9 +112,10 @@
 
 void DexCompiler::Compile() {
   DCHECK_EQ(dex_to_dex_compilation_level_, DexToDexCompilationLevel::kOptimize);
-  for (CodeItemIterator it(*unit_.GetCodeItem()); !it.Done(); it.Advance()) {
-    Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
-    const uint32_t dex_pc = it.CurrentDexPc();
+  IterationRange<DexInstructionIterator> instructions = unit_.GetCodeItem()->Instructions();
+  for (DexInstructionIterator it = instructions.begin(); it != instructions.end(); ++it) {
+    const uint32_t dex_pc = it.DexPc();
+    Instruction* inst = const_cast<Instruction*>(&it.Inst());
     switch (inst->Opcode()) {
       case Instruction::RETURN_VOID:
         CompileReturnVoid(inst, dex_pc);
@@ -125,7 +126,7 @@
         if (inst->Opcode() == Instruction::NOP) {
           // We turned the CHECK_CAST into two NOPs, avoid visiting the second NOP twice since this
           // would add 2 quickening info entries.
-          it.Advance();
+          ++it;
         }
         break;
 
@@ -362,8 +363,8 @@
     if (kIsDebugBuild) {
       // Double check that the counts line up with the size of the quicken info.
       size_t quicken_count = 0;
-      for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) {
-        if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
+      for (const DexInstructionPcPair& pair : code_item->Instructions()) {
+        if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) {
           ++quicken_count;
         }
       }
diff --git a/compiler/optimizing/block_builder.cc b/compiler/optimizing/block_builder.cc
index d7def77..595dd4d 100644
--- a/compiler/optimizing/block_builder.cc
+++ b/compiler/optimizing/block_builder.cc
@@ -76,9 +76,10 @@
 
   // Iterate over all instructions and find branching instructions. Create blocks for
   // the locations these instructions branch to.
-  for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
-    uint32_t dex_pc = it.CurrentDexPc();
-    const Instruction& instruction = it.CurrentInstruction();
+  IterationRange<DexInstructionIterator> instructions = code_item_.Instructions();
+  for (const DexInstructionPcPair& pair : instructions) {
+    const uint32_t dex_pc = pair.DexPc();
+    const Instruction& instruction = pair.Inst();
 
     if (instruction.IsBranch()) {
       number_of_branches_++;
@@ -105,13 +106,13 @@
     }
 
     if (instruction.CanFlowThrough()) {
-      if (it.IsLast()) {
+      DexInstructionIterator next(std::next(DexInstructionIterator(pair)));
+      if (next == instructions.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.
         return false;
-      } else {
-        MaybeCreateBlockAt(dex_pc + it.CurrentInstruction().SizeInCodeUnits());
       }
+      MaybeCreateBlockAt(next.DexPc());
     }
   }
 
@@ -126,8 +127,9 @@
   bool is_throwing_block = false;
   // Calculate the qucikening index here instead of CreateBranchTargets since it's easier to
   // calculate in dex_pc order.
-  for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
-    uint32_t dex_pc = it.CurrentDexPc();
+  for (const DexInstructionPcPair& pair : code_item_.Instructions()) {
+    const uint32_t dex_pc = pair.DexPc();
+    const Instruction& instruction = pair.Inst();
 
     // Check if this dex_pc address starts a new basic block.
     HBasicBlock* next_block = GetBlockAt(dex_pc);
@@ -144,7 +146,7 @@
       graph_->AddBlock(block);
     }
     // Make sure to increment this before the continues.
-    if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
+    if (QuickenInfoTable::NeedsIndexForInstruction(&instruction)) {
       ++quicken_index;
     }
 
@@ -153,8 +155,6 @@
       continue;
     }
 
-    const Instruction& instruction = it.CurrentInstruction();
-
     if (!is_throwing_block && IsThrowingDexInstruction(instruction)) {
       DCHECK(!ContainsElement(throwing_blocks_, block));
       is_throwing_block = true;
@@ -185,9 +185,9 @@
       continue;
     }
 
+    // Go to the next instruction in case we read dex PC below.
     if (instruction.CanFlowThrough()) {
-      uint32_t next_dex_pc = dex_pc + instruction.SizeInCodeUnits();
-      block->AddSuccessor(GetBlockAt(next_dex_pc));
+      block->AddSuccessor(GetBlockAt(std::next(DexInstructionIterator(pair)).DexPc()));
     }
 
     // The basic block ends here. Do not add any more instructions.
@@ -229,7 +229,7 @@
     }
   }
 
-  const Instruction& first = GetDexInstructionAt(code_item_, catch_block->GetDexPc());
+  const Instruction& first = code_item_.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.
diff --git a/compiler/optimizing/escape.cc b/compiler/optimizing/escape.cc
index 9df5bf1..0a92703 100644
--- a/compiler/optimizing/escape.cc
+++ b/compiler/optimizing/escape.cc
@@ -36,6 +36,12 @@
   *is_singleton = true;
   *is_singleton_and_not_returned = true;
   *is_singleton_and_not_deopt_visible = true;
+
+  if (reference->IsNewInstance() && reference->AsNewInstance()->IsFinalizable()) {
+    // Finalizable reference is treated as being returned in the end.
+    *is_singleton_and_not_returned = false;
+  }
+
   // Visit all uses to determine if this reference can escape into the heap,
   // a method call, an alias, etc.
   for (const HUseListNode<HInstruction*>& use : reference->GetUses()) {
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index 0f0be20..8e9b818 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -321,19 +321,19 @@
       quicken_index = block_builder_->GetQuickenIndex(block_dex_pc);
     }
 
-    for (CodeItemIterator it(code_item_, block_dex_pc); !it.Done(); it.Advance()) {
+    for (const DexInstructionPcPair& pair : code_item_.Instructions(block_dex_pc)) {
       if (current_block_ == nullptr) {
         // The previous instruction ended this block.
         break;
       }
 
-      uint32_t dex_pc = it.CurrentDexPc();
+      const uint32_t dex_pc = pair.DexPc();
       if (dex_pc != block_dex_pc && FindBlockStartingAt(dex_pc) != nullptr) {
         // This dex_pc starts a new basic block.
         break;
       }
 
-      if (current_block_->IsTryBlock() && IsThrowingDexInstruction(it.CurrentInstruction())) {
+      if (current_block_->IsTryBlock() && IsThrowingDexInstruction(pair.Inst())) {
         PropagateLocalsToCatchBlocks();
       }
 
@@ -341,11 +341,11 @@
         AppendInstruction(new (allocator_) HNativeDebugInfo(dex_pc));
       }
 
-      if (!ProcessDexInstruction(it.CurrentInstruction(), dex_pc, quicken_index)) {
+      if (!ProcessDexInstruction(pair.Inst(), dex_pc, quicken_index)) {
         return false;
       }
 
-      if (QuickenInfoTable::NeedsIndexForInstruction(&it.CurrentInstruction())) {
+      if (QuickenInfoTable::NeedsIndexForInstruction(&pair.Inst())) {
         ++quicken_index;
       }
     }
@@ -382,16 +382,15 @@
   dex_file_->DecodeDebugPositionInfo(&code_item_, Callback::Position, locations);
   // Instruction-specific tweaks.
   IterationRange<DexInstructionIterator> instructions = code_item_.Instructions();
-  for (DexInstructionIterator it = instructions.begin(); it != instructions.end(); ++it) {
-    switch (it->Opcode()) {
+  for (const DexInstructionPcPair& inst : instructions) {
+    switch (inst->Opcode()) {
       case Instruction::MOVE_EXCEPTION: {
         // Stop in native debugger after the exception has been moved.
         // The compiler also expects the move at the start of basic block so
         // we do not want to interfere by inserting native-debug-info before it.
-        locations->ClearBit(it.DexPc());
-        DexInstructionIterator next = it;
-        ++next;
-        DCHECK(next != it);
+        locations->ClearBit(inst.DexPc());
+        DexInstructionIterator next = std::next(DexInstructionIterator(inst));
+        DCHECK(next.DexPc() != inst.DexPc());
         if (next != instructions.end()) {
           locations->SetBit(next.DexPc());
         }
diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc
index 7dff696..a16fdc2 100644
--- a/compiler/optimizing/load_store_elimination.cc
+++ b/compiler/optimizing/load_store_elimination.cc
@@ -183,7 +183,6 @@
             !location->IsValueKilledByLoopSideEffects()) {
           // A removable singleton's field that's not stored into inside a loop is
           // invariant throughout the loop. Nothing to do.
-          DCHECK(ref_info->IsSingletonAndRemovable());
         } else {
           // heap value is killed by loop side effects (stored into directly, or
           // due to aliasing). Or the heap value may be needed after method return
@@ -386,7 +385,7 @@
     } else if (index != nullptr && ref_info->HasIndexAliasing()) {
       // For array element, don't eliminate stores if the index can be aliased.
     } else if (ref_info->IsSingleton()) {
-      // Store into a field of a singleton. The value cannot be killed due to
+      // Store into a field/element of a singleton. The value cannot be killed due to
       // aliasing/invocation. It can be redundant since future loads can
       // directly get the value set by this instruction. The value can still be killed due to
       // merging or loop side effects. Stores whose values are killed due to merging/loop side
@@ -394,24 +393,18 @@
       // Stores whose values may be needed after method return or deoptimization
       // are also removed from possibly_removed_stores_ when that is detected.
       possibly_redundant = true;
-      HNewInstance* new_instance = ref_info->GetReference()->AsNewInstance();
-      if (new_instance != nullptr && new_instance->IsFinalizable()) {
-        // Finalizable objects escape globally. Need to keep the store.
-        possibly_redundant = false;
-      } else {
-        HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
-        if (loop_info != nullptr) {
-          // instruction is a store in the loop so the loop must does write.
-          DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
+      HLoopInformation* loop_info = instruction->GetBlock()->GetLoopInformation();
+      if (loop_info != nullptr) {
+        // instruction is a store in the loop so the loop must does write.
+        DCHECK(side_effects_.GetLoopEffects(loop_info->GetHeader()).DoesAnyWrite());
 
-          if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
-            DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
-            // Keep the store since its value may be needed at the loop header.
-            possibly_redundant = false;
-          } else {
-            // The singleton is created inside the loop. Value stored to it isn't needed at
-            // the loop header. This is true for outer loops also.
-          }
+        if (loop_info->IsDefinedOutOfTheLoop(original_ref)) {
+          DCHECK(original_ref->GetBlock()->Dominates(loop_info->GetPreHeader()));
+          // Keep the store since its value may be needed at the loop header.
+          possibly_redundant = false;
+        } else {
+          // The singleton is created inside the loop. Value stored to it isn't needed at
+          // the loop header. This is true for outer loops also.
         }
       }
     }
@@ -575,9 +568,8 @@
       // new_instance isn't used for field accesses. No need to process it.
       return;
     }
-    if (ref_info->IsSingletonAndRemovable() &&
-        !new_instance->IsFinalizable() &&
-        !new_instance->NeedsChecks()) {
+    if (ref_info->IsSingletonAndRemovable() && !new_instance->NeedsChecks()) {
+      DCHECK(!new_instance->IsFinalizable());
       singleton_new_instances_.push_back(new_instance);
     }
     ScopedArenaVector<HInstruction*>& heap_values =
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index a1f825b..afca26d 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -940,8 +940,8 @@
                class_it.HasNext();
                class_it.Next()) {
             if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
-              for (CodeItemIterator it(*class_it.GetMethodCodeItem()); !it.Done(); it.Advance()) {
-                Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
+              for (const DexInstructionPcPair& inst :
+                  class_it.GetMethodCodeItem()->Instructions()) {
                 ASSERT_FALSE(inst->IsQuickened());
               }
             }
diff --git a/openjdkjvmti/Android.bp b/openjdkjvmti/Android.bp
index c6090ef..aef4dfc 100644
--- a/openjdkjvmti/Android.bp
+++ b/openjdkjvmti/Android.bp
@@ -35,6 +35,7 @@
         "ti_class_definition.cc",
         "ti_class_loader.cc",
         "ti_dump.cc",
+        "ti_extension.cc",
         "ti_field.cc",
         "ti_heap.cc",
         "ti_jni.cc",
diff --git a/openjdkjvmti/OpenjdkJvmTi.cc b/openjdkjvmti/OpenjdkJvmTi.cc
index 5f726b1..4814924 100644
--- a/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/openjdkjvmti/OpenjdkJvmTi.cc
@@ -52,6 +52,7 @@
 #include "ti_breakpoint.h"
 #include "ti_class.h"
 #include "ti_dump.h"
+#include "ti_extension.h"
 #include "ti_field.h"
 #include "ti_heap.h"
 #include "ti_jni.h"
@@ -1060,216 +1061,24 @@
     ENSURE_VALID_ENV(env);
     ENSURE_NON_NULL(extension_count_ptr);
     ENSURE_NON_NULL(extensions);
-
-    std::vector<jvmtiExtensionFunctionInfo> ext_vector;
-
-    // Holders for allocated values.
-    std::vector<JvmtiUniquePtr<char[]>> char_buffers;
-    std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers;
-    std::vector<JvmtiUniquePtr<jvmtiError[]>> error_buffers;
-
-    // Add a helper struct that takes an arbitrary const char*. add_extension will use Allocate
-    // appropriately.
-    struct CParamInfo {
-      const char* name;
-      jvmtiParamKind kind;
-      jvmtiParamTypes base_type;
-      jboolean null_ok;
-    };
-
-    auto add_extension = [&](jvmtiExtensionFunction func,
-                             const char* id,
-                             const char* short_description,
-                             jint param_count,
-                             const std::vector<CParamInfo>& params,
-                             jint error_count,
-                             const std::vector<jvmtiError>& errors) {
-      jvmtiExtensionFunctionInfo func_info;
-      jvmtiError error;
-
-      func_info.func = func;
-
-      JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error);
-      if (id_ptr == nullptr) {
-        return error;
-      }
-      func_info.id = id_ptr.get();
-      char_buffers.push_back(std::move(id_ptr));
-
-      JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error);
-      if (descr == nullptr) {
-        return error;
-      }
-      func_info.short_description = descr.get();
-      char_buffers.push_back(std::move(descr));
-
-      func_info.param_count = param_count;
-      if (param_count > 0) {
-        JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr =
-            AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, param_count, &error);
-        if (params_ptr == nullptr) {
-          return error;
-        }
-        func_info.params = params_ptr.get();
-        param_buffers.push_back(std::move(params_ptr));
-
-        for (jint i = 0; i != param_count; ++i) {
-          JvmtiUniquePtr<char[]> param_name = CopyString(env, params[i].name, &error);
-          if (param_name == nullptr) {
-            return error;
-          }
-          func_info.params[i].name = param_name.get();
-          char_buffers.push_back(std::move(param_name));
-
-          func_info.params[i].kind = params[i].kind;
-          func_info.params[i].base_type = params[i].base_type;
-          func_info.params[i].null_ok = params[i].null_ok;
-        }
-      } else {
-        func_info.params = nullptr;
-      }
-
-      func_info.error_count = error_count;
-      if (error_count > 0) {
-        JvmtiUniquePtr<jvmtiError[]> errors_ptr =
-            AllocJvmtiUniquePtr<jvmtiError[]>(env, error_count, &error);
-        if (errors_ptr == nullptr) {
-          return error;
-        }
-        func_info.errors = errors_ptr.get();
-        error_buffers.push_back(std::move(errors_ptr));
-
-        for (jint i = 0; i != error_count; ++i) {
-          func_info.errors[i] = errors[i];
-        }
-      } else {
-        func_info.errors = nullptr;
-      }
-
-      ext_vector.push_back(func_info);
-
-      return ERR(NONE);
-    };
-
-    jvmtiError error;
-
-    // Heap extensions.
-    error = add_extension(
-        reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetObjectHeapId),
-        "com.android.art.heap.get_object_heap_id",
-        "Retrieve the heap id of the the object tagged with the given argument. An "
-            "arbitrary object is chosen if multiple objects exist with the same tag.",
-        2,
-        {                                                          // NOLINT [whitespace/braces] [4]
-            { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false},
-            { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false}
-        },
-        1,
-        { JVMTI_ERROR_NOT_FOUND });
-    if (error != ERR(NONE)) {
-      return error;
-    }
-
-    error = add_extension(
-        reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName),
-        "com.android.art.heap.get_heap_name",
-        "Retrieve the name of the heap with the given id.",
-        2,
-        {                                                          // NOLINT [whitespace/braces] [4]
-            { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
-            { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false}
-        },
-        1,
-        { JVMTI_ERROR_ILLEGAL_ARGUMENT });
-    if (error != ERR(NONE)) {
-      return error;
-    }
-
-    error = add_extension(
-        reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::IterateThroughHeapExt),
-        "com.android.art.heap.iterate_through_heap_ext",
-        "Iterate through a heap. This is equivalent to the standard IterateThroughHeap function,"
-        " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks"
-        " structure is reused, with the callbacks field overloaded to a signature of "
-        "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).",
-        4,
-        {                                                          // NOLINT [whitespace/braces] [4]
-            { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
-            { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true},
-            { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false},
-            { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true}
-        },
-        3,
-        {                                                          // NOLINT [whitespace/braces] [4]
-            JVMTI_ERROR_MUST_POSSESS_CAPABILITY,
-            JVMTI_ERROR_INVALID_CLASS,
-            JVMTI_ERROR_NULL_POINTER
-        });
-    if (error != ERR(NONE)) {
-      return error;
-    }
-
-    error = add_extension(
-        reinterpret_cast<jvmtiExtensionFunction>(AllocUtil::GetGlobalJvmtiAllocationState),
-        "com.android.art.alloc.get_global_jvmti_allocation_state",
-        "Returns the total amount of memory currently allocated by all jvmtiEnvs through the"
-        " 'Allocate' jvmti function. This does not include any memory that has been deallocated"
-        " through the 'Deallocate' function. This number is approximate and might not correspond"
-        " exactly to the sum of the sizes of all not freed allocations.",
-        1,
-        {                                                          // NOLINT [whitespace/braces] [4]
-            { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false},
-        },
-        1,
-        { ERR(NULL_POINTER) });
-    if (error != ERR(NONE)) {
-      return error;
-    }
-
-    // Copy into output buffer.
-
-    *extension_count_ptr = ext_vector.size();
-    JvmtiUniquePtr<jvmtiExtensionFunctionInfo[]> out_data =
-        AllocJvmtiUniquePtr<jvmtiExtensionFunctionInfo[]>(env, ext_vector.size(), &error);
-    if (out_data == nullptr) {
-      return error;
-    }
-    memcpy(out_data.get(),
-           ext_vector.data(),
-           ext_vector.size() * sizeof(jvmtiExtensionFunctionInfo));
-    *extensions = out_data.release();
-
-    // Release all the buffer holders, we're OK now.
-    for (auto& holder : char_buffers) {
-      holder.release();
-    }
-    for (auto& holder : param_buffers) {
-      holder.release();
-    }
-    for (auto& holder : error_buffers) {
-      holder.release();
-    }
-
-    return ERR(NONE);
+    return ExtensionUtil::GetExtensionFunctions(env, extension_count_ptr, extensions);
   }
 
   static jvmtiError GetExtensionEvents(jvmtiEnv* env,
                                        jint* extension_count_ptr,
                                        jvmtiExtensionEventInfo** extensions) {
     ENSURE_VALID_ENV(env);
-    // We do not have any extension events.
-    *extension_count_ptr = 0;
-    *extensions = nullptr;
-
-    return ERR(NONE);
+    ENSURE_NON_NULL(extension_count_ptr);
+    ENSURE_NON_NULL(extensions);
+    return ExtensionUtil::GetExtensionEvents(env, extension_count_ptr, extensions);
   }
 
   static jvmtiError SetExtensionEventCallback(jvmtiEnv* env,
-                                              jint extension_event_index ATTRIBUTE_UNUSED,
-                                              jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) {
+                                              jint extension_event_index,
+                                              jvmtiExtensionEvent callback) {
     ENSURE_VALID_ENV(env);
     // We do not have any extension events, so any call is illegal.
-    return ERR(ILLEGAL_ARGUMENT);
+    return ExtensionUtil::SetExtensionEventCallback(env, extension_event_index, callback);
   }
 
   static jvmtiError GetPotentialCapabilities(jvmtiEnv* env, jvmtiCapabilities* capabilities_ptr) {
diff --git a/openjdkjvmti/ti_extension.cc b/openjdkjvmti/ti_extension.cc
new file mode 100644
index 0000000..fbed964
--- /dev/null
+++ b/openjdkjvmti/ti_extension.cc
@@ -0,0 +1,249 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <vector>
+
+#include "ti_extension.h"
+
+#include "art_jvmti.h"
+#include "ti_allocator.h"
+#include "ti_heap.h"
+
+namespace openjdkjvmti {
+
+struct CParamInfo {
+  const char* name;
+  jvmtiParamKind kind;
+  jvmtiParamTypes base_type;
+  jboolean null_ok;
+
+  jvmtiParamInfo ToParamInfo(jvmtiEnv* env,
+                             /*out*/std::vector<JvmtiUniquePtr<char[]>>* char_buffers,
+                             /*out*/jvmtiError* err) const {
+    JvmtiUniquePtr<char[]> param_name = CopyString(env, name, err);
+    char* name_ptr = param_name.get();
+    char_buffers->push_back(std::move(param_name));
+    return jvmtiParamInfo { name_ptr, kind, base_type, null_ok }; // NOLINT [whitespace/braces] [4]
+  }
+};
+
+jvmtiError ExtensionUtil::GetExtensionFunctions(jvmtiEnv* env,
+                                                jint* extension_count_ptr,
+                                                jvmtiExtensionFunctionInfo** extensions) {
+  if (extension_count_ptr == nullptr || extensions == nullptr) {
+    return ERR(NULL_POINTER);
+  }
+
+  std::vector<jvmtiExtensionFunctionInfo> ext_vector;
+
+  // Holders for allocated values.
+  std::vector<JvmtiUniquePtr<char[]>> char_buffers;
+  std::vector<JvmtiUniquePtr<jvmtiParamInfo[]>> param_buffers;
+  std::vector<JvmtiUniquePtr<jvmtiError[]>> error_buffers;
+
+  auto add_extension = [&](jvmtiExtensionFunction func,
+                           const char* id,
+                           const char* short_description,
+                           const std::vector<CParamInfo>& params,
+                           const std::vector<jvmtiError>& errors) {
+    jvmtiExtensionFunctionInfo func_info;
+    jvmtiError error;
+
+    func_info.func = func;
+
+    JvmtiUniquePtr<char[]> id_ptr = CopyString(env, id, &error);
+    if (id_ptr == nullptr) {
+      return error;
+    }
+    func_info.id = id_ptr.get();
+    char_buffers.push_back(std::move(id_ptr));
+
+    JvmtiUniquePtr<char[]> descr = CopyString(env, short_description, &error);
+    if (descr == nullptr) {
+      return error;
+    }
+    func_info.short_description = descr.get();
+    char_buffers.push_back(std::move(descr));
+
+    func_info.param_count = params.size();
+    if (!params.empty()) {
+      JvmtiUniquePtr<jvmtiParamInfo[]> params_ptr =
+          AllocJvmtiUniquePtr<jvmtiParamInfo[]>(env, params.size(), &error);
+      if (params_ptr == nullptr) {
+        return error;
+      }
+      func_info.params = params_ptr.get();
+      param_buffers.push_back(std::move(params_ptr));
+
+      for (jint i = 0; i != func_info.param_count; ++i) {
+        func_info.params[i] = params[i].ToParamInfo(env, &char_buffers, &error);
+        if (error != OK) {
+          return error;
+        }
+      }
+    } else {
+      func_info.params = nullptr;
+    }
+
+    func_info.error_count = errors.size();
+    if (!errors.empty()) {
+      JvmtiUniquePtr<jvmtiError[]> errors_ptr =
+          AllocJvmtiUniquePtr<jvmtiError[]>(env, errors.size(), &error);
+      if (errors_ptr == nullptr) {
+        return error;
+      }
+      func_info.errors = errors_ptr.get();
+      error_buffers.push_back(std::move(errors_ptr));
+
+      for (jint i = 0; i != func_info.error_count; ++i) {
+        func_info.errors[i] = errors[i];
+      }
+    } else {
+      func_info.errors = nullptr;
+    }
+
+    ext_vector.push_back(func_info);
+
+    return ERR(NONE);
+  };
+
+  jvmtiError error;
+
+  // Heap extensions.
+  error = add_extension(
+      reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetObjectHeapId),
+      "com.android.art.heap.get_object_heap_id",
+      "Retrieve the heap id of the the object tagged with the given argument. An "
+          "arbitrary object is chosen if multiple objects exist with the same tag.",
+      {                                                          // NOLINT [whitespace/braces] [4]
+          { "tag", JVMTI_KIND_IN, JVMTI_TYPE_JLONG, false},
+          { "heap_id", JVMTI_KIND_OUT, JVMTI_TYPE_JINT, false}
+      },
+      { JVMTI_ERROR_NOT_FOUND });
+  if (error != ERR(NONE)) {
+    return error;
+  }
+
+  error = add_extension(
+      reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::GetHeapName),
+      "com.android.art.heap.get_heap_name",
+      "Retrieve the name of the heap with the given id.",
+      {                                                          // NOLINT [whitespace/braces] [4]
+          { "heap_id", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
+          { "heap_name", JVMTI_KIND_ALLOC_BUF, JVMTI_TYPE_CCHAR, false}
+      },
+      { JVMTI_ERROR_ILLEGAL_ARGUMENT });
+  if (error != ERR(NONE)) {
+    return error;
+  }
+
+  error = add_extension(
+      reinterpret_cast<jvmtiExtensionFunction>(HeapExtensions::IterateThroughHeapExt),
+      "com.android.art.heap.iterate_through_heap_ext",
+      "Iterate through a heap. This is equivalent to the standard IterateThroughHeap function,"
+      " except for additionally passing the heap id of the current object. The jvmtiHeapCallbacks"
+      " structure is reused, with the callbacks field overloaded to a signature of "
+      "jint (*)(jlong, jlong, jlong*, jint length, void*, jint).",
+      {                                                          // NOLINT [whitespace/braces] [4]
+          { "heap_filter", JVMTI_KIND_IN, JVMTI_TYPE_JINT, false},
+          { "klass", JVMTI_KIND_IN, JVMTI_TYPE_JCLASS, true},
+          { "callbacks", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, false},
+          { "user_data", JVMTI_KIND_IN_PTR, JVMTI_TYPE_CVOID, true}
+      },
+      {                                                          // NOLINT [whitespace/braces] [4]
+          ERR(MUST_POSSESS_CAPABILITY),
+          ERR(INVALID_CLASS),
+          ERR(NULL_POINTER),
+      });
+  if (error != ERR(NONE)) {
+    return error;
+  }
+
+  error = add_extension(
+      reinterpret_cast<jvmtiExtensionFunction>(AllocUtil::GetGlobalJvmtiAllocationState),
+      "com.android.art.alloc.get_global_jvmti_allocation_state",
+      "Returns the total amount of memory currently allocated by all jvmtiEnvs through the"
+      " 'Allocate' jvmti function. This does not include any memory that has been deallocated"
+      " through the 'Deallocate' function. This number is approximate and might not correspond"
+      " exactly to the sum of the sizes of all not freed allocations.",
+      {                                                          // NOLINT [whitespace/braces] [4]
+          { "currently_allocated", JVMTI_KIND_OUT, JVMTI_TYPE_JLONG, false},
+      },
+      { ERR(NULL_POINTER) });
+  if (error != ERR(NONE)) {
+    return error;
+  }
+
+  // Copy into output buffer.
+
+  *extension_count_ptr = ext_vector.size();
+  JvmtiUniquePtr<jvmtiExtensionFunctionInfo[]> out_data =
+      AllocJvmtiUniquePtr<jvmtiExtensionFunctionInfo[]>(env, ext_vector.size(), &error);
+  if (out_data == nullptr) {
+    return error;
+  }
+  memcpy(out_data.get(),
+          ext_vector.data(),
+          ext_vector.size() * sizeof(jvmtiExtensionFunctionInfo));
+  *extensions = out_data.release();
+
+  // Release all the buffer holders, we're OK now.
+  for (auto& holder : char_buffers) {
+    holder.release();
+  }
+  for (auto& holder : param_buffers) {
+    holder.release();
+  }
+  for (auto& holder : error_buffers) {
+    holder.release();
+  }
+
+  return OK;
+}
+
+
+jvmtiError ExtensionUtil::GetExtensionEvents(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                             jint* extension_count_ptr,
+                                             jvmtiExtensionEventInfo** extensions) {
+  // We don't have any extension events at the moment.
+  *extension_count_ptr = 0;
+  *extensions = nullptr;
+  return OK;
+}
+
+jvmtiError ExtensionUtil::SetExtensionEventCallback(jvmtiEnv* env ATTRIBUTE_UNUSED,
+                                                    jint extension_event_index ATTRIBUTE_UNUSED,
+                                                    jvmtiExtensionEvent callback ATTRIBUTE_UNUSED) {
+  // We do not have any extension events, so any call is illegal.
+  return ERR(ILLEGAL_ARGUMENT);
+}
+
+}  // namespace openjdkjvmti
diff --git a/openjdkjvmti/ti_extension.h b/openjdkjvmti/ti_extension.h
new file mode 100644
index 0000000..d705ba7
--- /dev/null
+++ b/openjdkjvmti/ti_extension.h
@@ -0,0 +1,57 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h.  The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_OPENJDKJVMTI_TI_EXTENSION_H_
+#define ART_OPENJDKJVMTI_TI_EXTENSION_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ExtensionUtil {
+ public:
+  static jvmtiError GetExtensionFunctions(jvmtiEnv* env,
+                                          jint* extension_count_ptr,
+                                          jvmtiExtensionFunctionInfo** extensions);
+
+  static jvmtiError GetExtensionEvents(jvmtiEnv* env,
+                                       jint* extension_count_ptr,
+                                       jvmtiExtensionEventInfo** extensions);
+
+  static jvmtiError SetExtensionEventCallback(jvmtiEnv* env,
+                                              jint extension_event_index,
+                                              jvmtiExtensionEvent callback);
+};
+
+}  // namespace openjdkjvmti
+
+#endif  // ART_OPENJDKJVMTI_TI_EXTENSION_H_
diff --git a/profman/profman.cc b/profman/profman.cc
index ae07784..1c50645 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -725,15 +725,15 @@
     const DexFile::CodeItem* code_item = dex_file->GetCodeItem(offset);
 
     bool found_invoke = false;
-    for (CodeItemIterator it(*code_item); !it.Done(); it.Advance()) {
-      if (it.CurrentInstruction().Opcode() == Instruction::INVOKE_VIRTUAL) {
+    for (const DexInstructionPcPair& inst : code_item->Instructions()) {
+      if (inst->Opcode() == Instruction::INVOKE_VIRTUAL) {
         if (found_invoke) {
           LOG(ERROR) << "Multiple invoke INVOKE_VIRTUAL found: "
                      << dex_file->PrettyMethod(method_index);
           return false;
         }
         found_invoke = true;
-        *dex_pc = it.CurrentDexPc();
+        *dex_pc = inst.DexPc();
       }
     }
     if (!found_invoke) {
diff --git a/runtime/bytecode_utils.h b/runtime/bytecode_utils.h
index b6a3c03..815e0ba 100644
--- a/runtime/bytecode_utils.h
+++ b/runtime/bytecode_utils.h
@@ -24,35 +24,6 @@
 
 namespace art {
 
-class CodeItemIterator : public ValueObject {
- public:
-  explicit CodeItemIterator(const DexFile::CodeItem& code_item) : CodeItemIterator(code_item, 0u) {}
-  CodeItemIterator(const DexFile::CodeItem& code_item, uint32_t start_dex_pc)
-      : code_ptr_(code_item.insns_ + start_dex_pc),
-        code_end_(code_item.insns_ + code_item.insns_size_in_code_units_),
-        dex_pc_(start_dex_pc) {}
-
-  bool Done() const { return code_ptr_ >= code_end_; }
-  bool IsLast() const { return code_ptr_ + CurrentInstruction().SizeInCodeUnits() >= code_end_; }
-
-  const Instruction& CurrentInstruction() const { return *Instruction::At(code_ptr_); }
-  uint32_t CurrentDexPc() const { return dex_pc_; }
-
-  void Advance() {
-    DCHECK(!Done());
-    size_t instruction_size = CurrentInstruction().SizeInCodeUnits();
-    code_ptr_ += instruction_size;
-    dex_pc_ += instruction_size;
-  }
-
- private:
-  const uint16_t* code_ptr_;
-  const uint16_t* const code_end_;
-  uint32_t dex_pc_;
-
-  DISALLOW_COPY_AND_ASSIGN(CodeItemIterator);
-};
-
 class DexSwitchTable : public ValueObject {
  public:
   DexSwitchTable(const Instruction& instruction, uint32_t dex_pc)
@@ -164,10 +135,6 @@
   size_t index_;
 };
 
-inline const Instruction& GetDexInstructionAt(const DexFile::CodeItem& code_item, uint32_t dex_pc) {
-  return CodeItemIterator(code_item, dex_pc).CurrentInstruction();
-}
-
 inline bool IsThrowingDexInstruction(const Instruction& instruction) {
   // Special-case MONITOR_EXIT which is a throwing instruction but the verifier
   // guarantees that it will never throw. This is necessary to avoid rejecting
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index f239edc..5c9b258 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -301,8 +301,9 @@
 
   // Raw code_item.
   struct CodeItem {
-    IterationRange<DexInstructionIterator> Instructions() const {
-      return { DexInstructionIterator(insns_, 0u),
+    IterationRange<DexInstructionIterator> Instructions(uint32_t start_dex_pc = 0u) const {
+      DCHECK_LE(start_dex_pc, insns_size_in_code_units_);
+      return { DexInstructionIterator(insns_, start_dex_pc),
                DexInstructionIterator(insns_, insns_size_in_code_units_) };
     }
 
diff --git a/runtime/dex_instruction_iterator.h b/runtime/dex_instruction_iterator.h
index f77908c..be583a2 100644
--- a/runtime/dex_instruction_iterator.h
+++ b/runtime/dex_instruction_iterator.h
@@ -123,6 +123,9 @@
   explicit DexInstructionIterator(const uint16_t* inst, uint32_t dex_pc)
       : DexInstructionIteratorBase(Instruction::At(inst), dex_pc) {}
 
+  explicit DexInstructionIterator(const DexInstructionPcPair& pair)
+      : DexInstructionIterator(pair.Instructions(), pair.DexPc()) {}
+
   // Value after modification.
   DexInstructionIterator& operator++() {
     data_.dex_pc_ += Inst().SizeInCodeUnits();
diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc
index 908405b..a5ebade 100644
--- a/runtime/dex_to_dex_decompiler.cc
+++ b/runtime/dex_to_dex_decompiler.cc
@@ -89,8 +89,8 @@
   // because the RETURN_VOID quickening is not encoded in the quickening data. Because
   // unquickening is a rare need and not performance sensitive, it is not worth the
   // added storage to also add the RETURN_VOID quickening in the quickened data.
-  for (CodeItemIterator it(code_item_); !it.Done(); it.Advance()) {
-    Instruction* inst = const_cast<Instruction*>(&it.CurrentInstruction());
+  for (const DexInstructionPcPair& pair : code_item_.Instructions()) {
+    Instruction* inst = const_cast<Instruction*>(&pair.Inst());
 
     switch (inst->Opcode()) {
       case Instruction::RETURN_VOID_NO_BARRIER:
diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java
index 7ae873a..30d4970 100644
--- a/test/530-checker-lse/src/Main.java
+++ b/test/530-checker-lse/src/Main.java
@@ -62,11 +62,12 @@
 
 class Finalizable {
   static boolean sVisited = false;
-  static final int VALUE = 0xbeef;
+  static final int VALUE1 = 0xbeef;
+  static final int VALUE2 = 0xcafe;
   int i;
 
   protected void finalize() {
-    if (i != VALUE) {
+    if (i != VALUE1) {
       System.out.println("Where is the beef?");
     }
     sVisited = true;
@@ -620,15 +621,18 @@
   /// CHECK-START: void Main.testFinalizable() load_store_elimination (before)
   /// CHECK: NewInstance
   /// CHECK: InstanceFieldSet
+  /// CHECK: InstanceFieldSet
 
   /// CHECK-START: void Main.testFinalizable() load_store_elimination (after)
   /// CHECK: NewInstance
   /// CHECK: InstanceFieldSet
+  /// CHECK-NOT: InstanceFieldSet
 
-  // Allocations and stores into finalizable objects cannot be eliminated.
+  // Allocations of finalizable objects cannot be eliminated.
   static void testFinalizable() {
     Finalizable finalizable = new Finalizable();
-    finalizable.i = Finalizable.VALUE;
+    finalizable.i = Finalizable.VALUE2;
+    finalizable.i = Finalizable.VALUE1;
   }
 
   static java.lang.ref.WeakReference<Object> getWeakReference() {
diff --git a/test/983-source-transform-verify/source_transform.cc b/test/983-source-transform-verify/source_transform.cc
index ef67ace..ca3f88b 100644
--- a/test/983-source-transform-verify/source_transform.cc
+++ b/test/983-source-transform-verify/source_transform.cc
@@ -89,13 +89,13 @@
       if (!it.IsAtMethod() || it.GetMethodCodeItem() == nullptr) {
         continue;
       }
-      for (CodeItemIterator code_it(*it.GetMethodCodeItem()); !code_it.Done(); code_it.Advance()) {
-        const Instruction& inst = code_it.CurrentInstruction();
+      for (const DexInstructionPcPair& pair : it.GetMethodCodeItem()->Instructions()) {
+        const Instruction& inst = pair.Inst();
         int forbiden_flags = (Instruction::kVerifyError | Instruction::kVerifyRuntimeOnly);
         if (inst.Opcode() == Instruction::RETURN_VOID_NO_BARRIER ||
             (inst.GetVerifyExtraFlags() & forbiden_flags) != 0) {
           std::cout << "Unexpected instruction found in " << dex->PrettyMethod(it.GetMemberIndex())
-                    << " [Dex PC: 0x" << std::hex << code_it.CurrentDexPc() << std::dec << "] : "
+                    << " [Dex PC: 0x" << std::hex << pair.DexPc() << std::dec << "] : "
                     << inst.DumpString(dex.get()) << std::endl;
           continue;
         }
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 753fc39..31f43fc 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -545,9 +545,17 @@
     bpath="${bpath}:${framework}/bouncycastle${bpath_suffix}.jar"
     # Pass down the bootclasspath
     FLAGS="${FLAGS} -Xbootclasspath:${bpath}"
-    # Add 5 minutes to give some time to generate the boot image.
-    TIME_OUT_VALUE=$((${TIME_OUT_VALUE} + 300))
-    DALVIKVM_BOOT_OPT="-Ximage:/system/non-existant/core.art"
+    # Disable image dex2oat - this will forbid the runtime to patch or compile an image.
+    FLAGS="${FLAGS} -Xnoimage-dex2oat"
+
+    # We'll abuse a second flag here to test different behavior. If --relocate, use the
+    # existing image - relocation will fail as patching is disallowed. If --no-relocate,
+    # pass a non-existent image - compilation will fail as dex2oat is disallowed.
+    if [ "${RELOCATE}" = "y" ] ; then
+      DALVIKVM_BOOT_OPT="-Ximage:${BOOT_IMAGE}"
+    else
+      DALVIKVM_BOOT_OPT="-Ximage:/system/non-existant/core.art"
+    fi
 else
     DALVIKVM_BOOT_OPT="-Ximage:${BOOT_IMAGE}"
 fi
@@ -935,6 +943,10 @@
           # All tests would log the error of failing dex2oat/patchoat. Be silent here and only
           # log fatal events.
           export ANDROID_LOG_TAGS='*:s'
+      elif [ "$HAVE_IMAGE" = "n" ]; then
+          # All tests would log the error of missing image. Be silent here and only log fatal
+          # events.
+          export ANDROID_LOG_TAGS='*:s'
       else
           # We are interested in LOG(ERROR) output.
           export ANDROID_LOG_TAGS='*:e'
diff --git a/test/knownfailures.json b/test/knownfailures.json
index 47b2f22..c6b6574 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -384,6 +384,12 @@
         "variant": "jvmti-stress & jit | redefine-stress & jit"
     },
     {
+        "test_patterns": ["616-cha"],
+        "description": ["The test assumes a boot image exists."],
+        "bug": "http://b/34193647",
+        "variant": "no-image"
+    },
+    {
         "tests": [ "663-odd-dex-size",
                    "663-odd-dex-size2",
                    "663-odd-dex-size3",