Added second pass which does code flow checking to verifier.

Ran this through libcore and it finds no errors, but I still need to
create tests to make sure it catches errors when it should. Also, it's
still missing 2 pieces, replacement of failing opcodes and generation of
the register map.

Change-Id: I0f4c4c20751b5b030ca44c23e1d1c2e133404e0c
diff --git a/src/class_linker.cc b/src/class_linker.cc
index f312556..14f4883 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1193,7 +1193,7 @@
       CHECK(klass->GetStatus() == Class::kStatusResolved);
 
       klass->SetStatus(Class::kStatusVerifying);
-      if (!DexVerify::VerifyClass(klass)) {
+      if (!DexVerifier::VerifyClass(klass)) {
         LG << "Verification failed";  // TODO: ThrowVerifyError
         Object* exception = self->GetException();
         klass->SetVerifyErrorClass(exception->GetClass());
@@ -1592,6 +1592,8 @@
     for (size_t i = 0; i < count; ++i) {
       klass->GetVirtualMethodDuringLinking(i)->SetMethodIndex(i);
     }
+    // Link interface method tables
+    LinkInterfaceMethods(klass);
   } else {
     // Link virtual method tables
     LinkVirtualMethods(klass);
@@ -2103,7 +2105,9 @@
 
   const char* name = dex_file.dexStringById(method_id.name_idx_);
   std::string signature(dex_file.CreateMethodDescriptor(method_id.proto_idx_, NULL));
-  if (is_direct) {
+  if (klass->IsInterface()) {
+    resolved = klass->FindInterfaceMethod(name, signature);
+  } else if (is_direct) {
     resolved = klass->FindDirectMethod(name, signature);
   } else {
     resolved = klass->FindVirtualMethod(name, signature);
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index 8cf645d..3934d7d 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -87,6 +87,23 @@
     kVerifyError           = 0x80000,
   };
 
+  /*
+   * Holds the contents of a decoded instruction.
+   */
+  struct DecodedInstruction {
+    uint32_t vA_;
+    uint32_t vB_;
+    uint64_t vB_wide_;        /* for kFmt51l */
+    uint32_t vC_;
+    uint32_t arg_[5];         /* vC/D/E/F/G in invoke or filled-new-array */
+    Code     opcode_;
+
+    DecodedInstruction(const Instruction* inst) {
+      inst->Decode(vA_, vB_, vB_wide_, vC_, arg_);
+      opcode_ = inst->Opcode();
+    }
+  };
+
   // Decodes this instruction, populating its arguments.
   void Decode(uint32_t &vA, uint32_t &vB, uint64_t &vB_wide, uint32_t &vC, uint32_t arg[]) const;
 
@@ -115,6 +132,11 @@
     return kInstructionFormats[Opcode()];
   }
 
+  // Returns the flags for the current instruction.
+  int Flag() const {
+    return kInstructionFlags[Opcode()];
+  }
+
   // Returns true if this instruction is a branch.
   bool IsBranch() const {
     return (kInstructionFlags[Opcode()] & kBranch) != 0;
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index f257e78..86b119b 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -8,100 +8,448 @@
 #include "dex_file.h"
 #include "dex_instruction.h"
 #include "dex_instruction_visitor.h"
+#include "dex_verifier.h"
 #include "logging.h"
 #include "runtime.h"
 #include "stringpiece.h"
+#include "UniquePtr.h"
 
 namespace art {
 
-/*
- * Returns "true" if the flags indicate that this address holds the start
- * of an instruction.
- */
-inline bool InsnIsOpcode(const uint32_t insn_flags[], int addr) {
-  return (insn_flags[addr] & DexVerify::kInsnFlagWidthMask) != 0;
+#define k_  kRegTypeUnknown
+#define kU  kRegTypeUninit
+#define kX  kRegTypeConflict
+#define k0  kRegTypeZero
+#define k1  kRegTypeOne
+#define kZ  kRegTypeBoolean
+#define ky  kRegTypeConstPosByte
+#define kY  kRegTypeConstByte
+#define kh  kRegTypeConstPosShort
+#define kH  kRegTypeConstShort
+#define kc  kRegTypeConstChar
+#define ki  kRegTypeConstInteger
+#define kb  kRegTypePosByte
+#define kB  kRegTypeByte
+#define ks  kRegTypePosShort
+#define kS  kRegTypeShort
+#define kC  kRegTypeChar
+#define kI  kRegTypeInteger
+#define kF  kRegTypeFloat
+#define kN  kRegTypeConstLo
+#define kn  kRegTypeConstHi
+#define kJ  kRegTypeLongLo
+#define kj  kRegTypeLongHi
+#define kD  kRegTypeDoubleLo
+#define kd  kRegTypeDoubleHi
+
+const char DexVerifier::merge_table_[kRegTypeMAX][kRegTypeMAX] =
+  {
+    /* chk:  _  U  X  0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F  N  n  J  j  D  d */
+    { /*_*/ k_,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX },
+    { /*U*/ kX,kU,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX },
+    { /*X*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX },
+    { /*0*/ kX,kX,kX,k0,kZ,kZ,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*1*/ kX,kX,kX,kZ,k1,kZ,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*Z*/ kX,kX,kX,kZ,kZ,kZ,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*y*/ kX,kX,kX,ky,ky,ky,ky,kY,kh,kH,kc,ki,kb,kB,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*Y*/ kX,kX,kX,kY,kY,kY,kY,kY,kh,kH,kc,ki,kB,kB,kS,kS,kI,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*h*/ kX,kX,kX,kh,kh,kh,kh,kh,kh,kH,kc,ki,ks,kS,ks,kS,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*H*/ kX,kX,kX,kH,kH,kH,kH,kH,kH,kH,kc,ki,kS,kS,kS,kS,kI,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*c*/ kX,kX,kX,kc,kc,kc,kc,kc,kc,kc,kc,ki,kC,kI,kC,kI,kC,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*i*/ kX,kX,kX,ki,ki,ki,ki,ki,ki,ki,ki,ki,kI,kI,kI,kI,kI,kI,kF,kX,kX,kX,kX,kX,kX },
+    { /*b*/ kX,kX,kX,kb,kb,kb,kb,kB,ks,kS,kC,kI,kb,kB,ks,kS,kC,kI,kX,kX,kX,kX,kX,kX,kX },
+    { /*B*/ kX,kX,kX,kB,kB,kB,kB,kB,kS,kS,kI,kI,kB,kB,kS,kS,kI,kI,kX,kX,kX,kX,kX,kX,kX },
+    { /*s*/ kX,kX,kX,ks,ks,ks,ks,kS,ks,kS,kC,kI,ks,kS,ks,kS,kC,kI,kX,kX,kX,kX,kX,kX,kX },
+    { /*S*/ kX,kX,kX,kS,kS,kS,kS,kS,kS,kS,kI,kI,kS,kS,kS,kS,kI,kI,kX,kX,kX,kX,kX,kX,kX },
+    { /*C*/ kX,kX,kX,kC,kC,kC,kC,kI,kC,kI,kC,kI,kC,kI,kC,kI,kC,kI,kX,kX,kX,kX,kX,kX,kX },
+    { /*I*/ kX,kX,kX,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kI,kX,kX,kX,kX,kX,kX,kX },
+    { /*F*/ kX,kX,kX,kF,kF,kF,kF,kF,kF,kF,kF,kF,kX,kX,kX,kX,kX,kX,kF,kX,kX,kX,kX,kX,kX },
+    { /*N*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kN,kX,kJ,kX,kD,kX },
+    { /*n*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kn,kX,kj,kX,kd },
+    { /*J*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kJ,kX,kJ,kX,kX,kX },
+    { /*j*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kj,kX,kj,kX,kX },
+    { /*D*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kD,kX,kX,kX,kD,kX },
+    { /*d*/ kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kX,kd,kX,kX,kX,kd },
+  };
+
+#undef k_
+#undef kU
+#undef kX
+#undef k0
+#undef k1
+#undef kZ
+#undef ky
+#undef kY
+#undef kh
+#undef kH
+#undef kc
+#undef ki
+#undef kb
+#undef kB
+#undef ks
+#undef kS
+#undef kC
+#undef kI
+#undef kF
+#undef kN
+#undef kn
+#undef kJ
+#undef kj
+#undef kD
+#undef kd
+
+bool DexVerifier::VerifyClass(Class* klass) {
+  if (klass->IsVerified()) {
+    return true;
+  }
+  for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
+    Method* method = klass->GetDirectMethod(i);
+    if (!VerifyMethod(method)) {
+        LOG(ERROR) << "Verifier rejected class "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+      return false;
+    }
+  }
+  for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
+    Method* method = klass->GetVirtualMethod(i);
+    if (!VerifyMethod(method)) {
+        LOG(ERROR) << "Verifier rejected class "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+      return false;
+    }
+  }
+  return true;
 }
 
-/*
- * Extract the unsigned 16-bit instruction width from "flags".
- */
-inline int InsnGetWidth(const uint32_t insn_flags[], int addr) {
-  return insn_flags[addr] & DexVerify::kInsnFlagWidthMask;
+bool DexVerifier::VerifyMethod(Method* method) {
+  const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
+  const DexFile::CodeItem *code_item =
+      dex_file.GetCodeItem(method->GetCodeItemOffset());
+
+  /*
+   * Construct the verifier state container object.
+   */
+  VerifierData vdata(method, &dex_file, code_item);
+
+  /*
+   * If there aren't any instructions, make sure that's expected, then
+   * exit successfully.
+   */
+  if (code_item == NULL) {
+    if (!method->IsNative() && !method->IsAbstract()) {
+      LOG(ERROR) << "VFY: zero-length code in concrete non-native method";
+      return false;
+    }
+    return true;
+  }
+
+  /*
+   * Sanity-check the register counts.  ins + locals = registers, so make
+   * sure that ins <= registers.
+   */
+  if (code_item->ins_size_ > code_item->registers_size_) {
+    LOG(ERROR) << "VFY: bad register counts (ins=" << code_item->ins_size_
+               << " regs=" << code_item->registers_size_;
+    return false;
+  }
+
+  /*
+   * Allocate and initialize an array to hold instruction data.
+   */
+  UniquePtr<InsnFlags> insn_flags(new InsnFlags[code_item->insns_size_]());
+  vdata.insn_flags_ = insn_flags.get();
+
+  /*
+   * Run through the instructions and see if the width checks out.
+   */
+  if (!ComputeWidthsAndCountOps(&vdata)) {
+    return false;
+  }
+
+  /*
+   * Flag instructions guarded by a "try" block and check exception handlers.
+   */
+  if (!ScanTryCatchBlocks(&vdata)) {
+    return false;
+  }
+
+  /*
+   * Perform static instruction verification.
+   */
+  if (!VerifyInstructions(&vdata)) {
+    return false;
+  }
+
+  /*
+   * Perform code flow analysis.
+   */
+  if (!VerifyCodeFlow(&vdata)) {
+    return false;
+  }
+
+  return true;
 }
 
-/*
- * Changed?
- */
-inline bool InsnIsChanged(const uint32_t insn_flags[], int addr) {
-    return (insn_flags[addr] & DexVerify::kInsnFlagChanged) != 0;
-}
-inline void InsnSetChanged(uint32_t insn_flags[], int addr, bool changed) {
-  if (changed)
-    insn_flags[addr] |= DexVerify::kInsnFlagChanged;
-  else
-    insn_flags[addr] &= ~DexVerify::kInsnFlagChanged;
+bool DexVerifier::VerifyInstructions(VerifierData* vdata) {
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  InsnFlags* insn_flags = vdata->insn_flags_;
+  const byte* ptr = reinterpret_cast<const byte*>(code_item->insns_);
+  const Instruction* inst = Instruction::At(ptr);
+
+  /* Flag the start of the method as a branch target. */
+  InsnSetBranchTarget(insn_flags, 0);
+
+  uint32_t width = 0;
+  uint32_t insns_size = code_item->insns_size_;
+
+  while (width < insns_size) {
+    if (!VerifyInstruction(vdata, inst, width)) {
+      LOG(ERROR) << "VFY:  rejecting opcode 0x" << std::hex
+                 << (int) inst->Opcode() << " at 0x" << width << std::dec;
+      return false;
+    }
+
+    /* Flag instructions that are garbage collection points */
+    if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow() ||
+        inst->IsReturn()) {
+      InsnSetGcPoint(insn_flags, width);
+    }
+
+    width += inst->Size();
+    inst = inst->Next();
+  }
+  return true;
 }
 
-/*
- * Visited?
- */
-inline bool InsnIsVisited(const uint32_t insn_flags[], int addr) {
-    return (insn_flags[addr] & DexVerify::kInsnFlagVisited) != 0;
-}
-inline void InsnSetVisited(uint32_t insn_flags[], int addr, bool changed) {
-  if (changed)
-    insn_flags[addr] |= DexVerify::kInsnFlagVisited;
-  else
-    insn_flags[addr] &= ~DexVerify::kInsnFlagVisited;
+bool DexVerifier::VerifyInstruction(VerifierData* vdata,
+    const Instruction* inst, uint32_t code_offset) {
+  const DexFile* dex_file = vdata->dex_file_;
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  InsnFlags* insn_flags = vdata->insn_flags_;
+  Instruction::DecodedInstruction dec_insn(inst);
+  bool result = true;
+
+  int argumentA = inst->GetVerifyTypeArgumentA();
+  int argumentB = inst->GetVerifyTypeArgumentB();
+  int argumentC = inst->GetVerifyTypeArgumentC();
+  int extra_flags = inst->GetVerifyExtraFlags();
+
+  switch (argumentA) {
+    case Instruction::kVerifyRegA:
+      result &= CheckRegisterIndex(code_item, dec_insn.vA_);
+      break;
+    case Instruction::kVerifyRegAWide:
+      result &= CheckWideRegisterIndex(code_item, dec_insn.vA_);
+      break;
+  }
+
+  switch (argumentB) {
+    case Instruction::kVerifyRegB:
+      result &= CheckRegisterIndex(code_item, dec_insn.vB_);
+      break;
+    case Instruction::kVerifyRegBField:
+      result &= CheckFieldIndex(dex_file, dec_insn.vB_);
+      break;
+    case Instruction::kVerifyRegBMethod:
+      result &= CheckMethodIndex(dex_file, dec_insn.vB_);
+      break;
+    case Instruction::kVerifyRegBNewInstance:
+      result &= CheckNewInstance(dex_file, dec_insn.vB_);
+      break;
+    case Instruction::kVerifyRegBString:
+      result &= CheckStringIndex(dex_file, dec_insn.vB_);
+      break;
+    case Instruction::kVerifyRegBType:
+      result &= CheckTypeIndex(dex_file, dec_insn.vB_);
+      break;
+    case Instruction::kVerifyRegBWide:
+      result &= CheckWideRegisterIndex(code_item, dec_insn.vB_);
+      break;
+  }
+
+  switch (argumentC) {
+    case Instruction::kVerifyRegC:
+      result &= CheckRegisterIndex(code_item, dec_insn.vC_);
+      break;
+    case Instruction::kVerifyRegCField:
+      result &= CheckFieldIndex(dex_file, dec_insn.vC_);
+      break;
+    case Instruction::kVerifyRegCNewArray:
+      result &= CheckNewArray(dex_file, dec_insn.vC_);
+      break;
+    case Instruction::kVerifyRegCType:
+      result &= CheckTypeIndex(dex_file, dec_insn.vC_);
+      break;
+    case Instruction::kVerifyRegCWide:
+      result &= CheckWideRegisterIndex(code_item, dec_insn.vC_);
+      break;
+  }
+
+  switch (extra_flags) {
+    case Instruction::kVerifyArrayData:
+      result &= CheckArrayData(code_item, code_offset);
+      break;
+    case Instruction::kVerifyBranchTarget:
+      result &= CheckBranchTarget(code_item, insn_flags, code_offset);
+      break;
+    case Instruction::kVerifySwitchTargets:
+      result &= CheckSwitchTargets(code_item, insn_flags, code_offset);
+      break;
+    case Instruction::kVerifyVarArg:
+      result &= CheckVarArgRegs(code_item, dec_insn.vA_, dec_insn.arg_);
+      break;
+    case Instruction::kVerifyVarArgRange:
+      result &= CheckVarArgRangeRegs(code_item, dec_insn.vA_, dec_insn.vC_);
+      break;
+    case Instruction::kVerifyError:
+      LOG(ERROR) << "VFY: unexpected opcode " << std::hex
+                 << (int) dec_insn.opcode_ << std::dec;
+      result = false;
+      break;
+  }
+
+  return result;
 }
 
-/*
- * Visited or changed?
- */
-inline bool InsnIsVisitedOrChanged(const uint32_t insn_flags[], int addr) {
-  return (insn_flags[addr] & (DexVerify::kInsnFlagVisited |
-                              DexVerify::kInsnFlagChanged)) != 0;
-}
+bool DexVerifier::VerifyCodeFlow(VerifierData* vdata) {
+  Method* method = vdata->method_;
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  uint16_t registers_size = code_item->registers_size_;
+  uint32_t insns_size = code_item->insns_size_;
+  RegisterTable reg_table;
 
-/*
- * In a "try" block?
- */
-inline bool InsnIsInTry(const uint32_t insn_flags[], int addr) {
-  return (insn_flags[addr] & DexVerify::kInsnFlagInTry) != 0;
-}
-inline void InsnSetInTry(uint32_t insn_flags[], int addr, bool inTry) {
-  insn_flags[addr] |= DexVerify::kInsnFlagInTry;
-}
+  if (registers_size * insns_size > 4*1024*1024) {
+    LOG(ERROR) << "VFY: warning: method is huge (regs=" << registers_size
+               << " insns_size=" << insns_size << ")";
+  }
 
-/*
- * Instruction is a branch target or exception handler?
- */
-inline bool InsnIsBranchTarget(const uint32_t insn_flags[], int addr) {
-  return (insn_flags[addr] & DexVerify::kInsnFlagBranchTarget) != 0;
-}
-inline void InsnSetBranchTarget(uint32_t insn_flags[], int addr, bool isBranch)
-{
-  insn_flags[addr] |= DexVerify::kInsnFlagBranchTarget;
-}
+  /* Create and initialize register lists. */
+  if (!InitRegisterTable(vdata, &reg_table, kTrackRegsGcPoints)) {
+    return false;
+  }
 
-/*
- * Instruction is a GC point?
- */
-inline bool InsnIsGcPoint(const uint32_t insn_flags[], int addr) {
-  return (insn_flags[addr] & DexVerify::kInsnFlagGcPoint) != 0;
-}
-inline void InsnSetGcPoint(uint32_t insn_flags[], int addr, bool isGcPoint) {
-  insn_flags[addr] |= DexVerify::kInsnFlagGcPoint;
-}
+  vdata->register_lines_ = reg_table.register_lines_;
+
+  /* Allocate a map to hold the classes of uninitialized instances. */
+  UniquePtr<UninitInstanceMap> uninit_map(CreateUninitInstanceMap(vdata));
+  vdata->uninit_map_ = uninit_map.get();
+
+  /* Initialize register types of method arguments. */
+  if (!SetTypesFromSignature(vdata, reg_table.register_lines_[0].reg_types_)) {
+    LOG(ERROR) << "VFY: bad signature '"
+               << method->GetSignature()->ToModifiedUtf8() << "' for "
+               << method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+               << "." << method->GetName()->ToModifiedUtf8();
+    return false;
+  }
+
+  /* Perform code flow verification. */
+  if (!CodeFlowVerifyMethod(vdata, &reg_table)) {
+    return false;
+  }
+
+  /* TODO: Generate a register map. */
 
 
-/*
- * Extract the relative offset from a branch instruction.
- *
- * Returns "false" on failure (e.g. this isn't a branch instruction).
- */
-bool GetBranchOffset(const DexFile::CodeItem* code_item,
-    const uint32_t insn_flags[], int cur_offset, int32_t* pOffset,
+  return true;
+}
+
+bool DexVerifier::ComputeWidthsAndCountOps(VerifierData* vdata) {
+  const uint16_t* insns = vdata->code_item_->insns_;
+  uint32_t insns_size = vdata->code_item_->insns_size_;
+  InsnFlags* insn_flags = vdata->insn_flags_;
+  const byte* ptr = reinterpret_cast<const byte*>(insns);
+  const Instruction* inst = Instruction::At(ptr);
+  size_t new_instance_count = 0;
+  size_t monitor_enter_count = 0;
+  size_t width = 0;
+
+  while (width < insns_size) {
+    Instruction::Code opcode = inst->Opcode();
+    if (opcode == Instruction::NEW_INSTANCE) {
+      new_instance_count++;
+    } else if (opcode == Instruction::MONITOR_ENTER) {
+      monitor_enter_count++;
+    }
+
+    insn_flags[width] |= inst->Size();
+    width += inst->Size();
+    inst = inst->Next();
+  }
+
+  if (width != insns_size) {
+    LOG(ERROR) << "VFY: code did not end where expected (" << width << " vs. "
+               << insns_size << ")";
+    return false;
+  }
+
+  vdata->new_instance_count_ = new_instance_count;
+  vdata->monitor_enter_count_ = monitor_enter_count;
+  return true;
+}
+
+bool DexVerifier::ScanTryCatchBlocks(VerifierData* vdata) {
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  InsnFlags* insn_flags = vdata->insn_flags_;
+  uint32_t insns_size = code_item->insns_size_;
+  uint32_t tries_size = code_item->tries_size_;
+
+  if (tries_size == 0) {
+    return true;
+  }
+
+  const DexFile::TryItem* tries = DexFile::dexGetTryItems(*code_item, 0);
+
+  for (uint32_t idx = 0; idx < tries_size; idx++) {
+    const DexFile::TryItem* try_item = &tries[idx];
+    uint32_t start = try_item->start_addr_;
+    uint32_t end = start + try_item->insn_count_;
+
+    if ((start >= end) || (start >= insns_size) || (end > insns_size)) {
+      LOG(ERROR) << "VFY: bad exception entry: startAddr=" << start
+                 << " endAddr=" << end << " (size=" << insns_size << ")";
+      return false;
+    }
+
+    if (InsnGetWidth(insn_flags, start) == 0) {
+      LOG(ERROR) << "VFY: 'try' block starts inside an instruction ("
+                 << start << ")";
+      return false;
+    }
+
+    uint32_t addr;
+    for (addr = start; addr < end; addr += InsnGetWidth(insn_flags, addr)) {
+      InsnSetInTry(insn_flags, addr);
+    }
+  }
+
+  /* Iterate over each of the handlers to verify target addresses. */
+  const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item, 0);
+  uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+  for (uint32_t idx = 0; idx < handlers_size; idx++) {
+    DexFile::CatchHandlerIterator iterator(handlers_ptr);
+
+    for (; !iterator.HasNext(); iterator.Next()) {
+      uint32_t addr = iterator.Get().address_;
+      if (InsnGetWidth(insn_flags, addr) == 0) {
+        LOG(ERROR) << "VFY: exception handler starts at bad address ("
+                   << addr << ")";
+        return false;
+      }
+
+      InsnSetBranchTarget(insn_flags, addr);
+    }
+
+    handlers_ptr = iterator.GetData();
+  }
+
+  return true;
+}
+
+bool DexVerifier::GetBranchOffset(const DexFile::CodeItem* code_item,
+    const InsnFlags insn_flags[], uint32_t cur_offset, int32_t* pOffset,
     bool* pConditional, bool* selfOkay) {
   const uint16_t* insns = code_item->insns_ + cur_offset;
 
@@ -145,12 +493,7 @@
   return true;
 }
 
-
-/*
- * Verify an array data table.  "cur_offset" is the offset of the
- * fill-array-data instruction.
- */
-static bool CheckArrayData(const DexFile::CodeItem* code_item,
+bool DexVerifier::CheckArrayData(const DexFile::CodeItem* code_item,
     uint32_t cur_offset) {
   const uint32_t insn_count = code_item->insns_size_;
   const uint16_t* insns = code_item->insns_ + cur_offset;
@@ -196,13 +539,7 @@
   return true;
 }
 
-/*
- * Perform static checks on a "new-instance" instruction.  Specifically,
- * make sure the class reference isn't for an array class.
- *
- * We don't need the actual class, just a pointer to the class name.
- */
-static bool CheckNewInstance(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckNewInstance(const DexFile* dex_file, uint32_t idx) {
   if (idx >= dex_file->GetHeader().type_ids_size_) {
     LOG(ERROR) << "VFY: bad type index " << idx << " (max "
                << dex_file->GetHeader().type_ids_size_ << ")";
@@ -219,12 +556,7 @@
   return true;
 }
 
-/*
- * Perform static checks on a "new-array" instruction.  Specifically, make
- * sure they aren't creating an array of arrays that causes the number of
- * dimensions to exceed 255.
- */
-static bool CheckNewArray(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckNewArray(const DexFile* dex_file, uint32_t idx) {
   if (idx >= dex_file->GetHeader().type_ids_size_) {
     LOG(ERROR) << "VFY: bad type index " << idx << " (max "
                << dex_file->GetHeader().type_ids_size_ << ")";
@@ -252,11 +584,7 @@
   return true;
 }
 
-/*
- * Perform static checks on an instruction that takes a class constant.
- * Ensure that the class index is in the valid range.
- */
-static bool CheckTypeIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckTypeIndex(const DexFile* dex_file, uint32_t idx) {
   if (idx >= dex_file->GetHeader().type_ids_size_) {
     LOG(ERROR) << "VFY: bad type index " << idx << " (max "
                << dex_file->GetHeader().type_ids_size_ << ")";
@@ -265,11 +593,7 @@
   return true;
 }
 
-/*
- * Perform static checks on a field get or set instruction.  All we do
- * here is ensure that the field index is in the valid range.
- */
-static bool CheckFieldIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckFieldIndex(const DexFile* dex_file, uint32_t idx) {
   if (idx >= dex_file->GetHeader().field_ids_size_) {
     LOG(ERROR) << "VFY: bad field index " << idx << " (max "
                << dex_file->GetHeader().field_ids_size_ << ")";
@@ -278,11 +602,7 @@
   return true;
 }
 
-/*
- * Perform static checks on a method invocation instruction.  All we do
- * here is ensure that the method index is in the valid range.
- */
-static bool CheckMethodIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckMethodIndex(const DexFile* dex_file, uint32_t idx) {
   if (idx >= dex_file->GetHeader().method_ids_size_) {
     LOG(ERROR) << "VFY: bad method index " << idx << " (max "
                << dex_file->GetHeader().method_ids_size_ << ")";
@@ -291,10 +611,7 @@
   return true;
 }
 
-/*
- * Ensure that the string index is in the valid range.
- */
-static bool CheckStringIndex(const DexFile* dex_file, uint32_t idx) {
+bool DexVerifier::CheckStringIndex(const DexFile* dex_file, uint32_t idx) {
   if (idx >= dex_file->GetHeader().string_ids_size_) {
     LOG(ERROR) << "VFY: bad string index " << idx << " (max "
                << dex_file->GetHeader().string_ids_size_ << ")";
@@ -303,11 +620,8 @@
   return true;
 }
 
-/*
- * Ensure that the register index is valid for this code item.
- */
-static bool CheckRegisterIndex(const DexFile::CodeItem* code_item, uint32_t idx)
-{
+bool DexVerifier::CheckRegisterIndex(const DexFile::CodeItem* code_item,
+    uint32_t idx) {
   if (idx >= code_item->registers_size_) {
     LOG(ERROR) << "VFY: register index out of range (" << idx << " >= "
                << code_item->registers_size_ << ")";
@@ -316,10 +630,7 @@
   return true;
 }
 
-/*
- * Ensure that the wide register index is valid for this code item.
- */
-static bool CheckWideRegisterIndex(const DexFile::CodeItem* code_item,
+bool DexVerifier::CheckWideRegisterIndex(const DexFile::CodeItem* code_item,
     uint32_t idx) {
   if (idx + 1 >= code_item->registers_size_) {
     LOG(ERROR) << "VFY: wide register index out of range (" << idx
@@ -329,19 +640,8 @@
   return true;
 }
 
-/*
- * Check the register indices used in a "vararg" instruction, such as
- * invoke-virtual or filled-new-array.
- *
- * vA holds word count (0-5), args[] have values.
- *
- * There are some tests we don't do here, e.g. we don't try to verify
- * that invoking a method that takes a double is done with consecutive
- * registers.  This requires parsing the target method signature, which
- * we will be doing later on during the code flow analysis.
- */
-static bool CheckVarArgRegs(const DexFile::CodeItem* code_item, uint32_t vA,
-    uint32_t arg[]) {
+bool DexVerifier::CheckVarArgRegs(const DexFile::CodeItem* code_item,
+    uint32_t vA, uint32_t arg[]) {
   uint16_t registers_size = code_item->registers_size_;
   uint32_t idx;
 
@@ -361,13 +661,7 @@
   return true;
 }
 
-/*
- * Check the register indices used in a "vararg/range" instruction, such as
- * invoke-virtual/range or filled-new-array/range.
- *
- * vA holds word count, vC holds index of first reg.
- */
-static bool CheckVarArgRangeRegs(const DexFile::CodeItem* code_item,
+bool DexVerifier::CheckVarArgRangeRegs(const DexFile::CodeItem* code_item,
     uint32_t vA, uint32_t vC) {
   uint16_t registers_size = code_item->registers_size_;
 
@@ -384,13 +678,8 @@
   return true;
 }
 
-/*
- * Verify a switch table. "cur_offset" is the offset of the switch instruction.
- *
- * Updates "insnFlags", setting the "branch target" flag.
- */
-static bool CheckSwitchTargets(const DexFile::CodeItem* code_item,
-    uint32_t insn_flags[], uint32_t cur_offset) {
+bool DexVerifier::CheckSwitchTargets(const DexFile::CodeItem* code_item,
+    InsnFlags insn_flags[], uint32_t cur_offset) {
   const uint32_t insn_count = code_item->insns_size_;
   const uint16_t* insns = code_item->insns_ + cur_offset;
   const uint16_t* switch_insns;
@@ -436,8 +725,9 @@
   table_size = targets_offset + switch_count * 2;
 
   if (switch_insns[0] != expected_signature) {
-    LOG(ERROR) << "VFY: wrong signature for switch table (0x" << switch_insns[0]
-               << ", wanted 0x" << expected_signature << ")";
+    LOG(ERROR) << "VFY: wrong signature for switch table (0x" << std::hex
+               << switch_insns[0] << ", wanted 0x" << expected_signature << ")"
+               << std::dec;
     return false;
   }
 
@@ -478,30 +768,19 @@
     if (abs_offset < 0 || abs_offset >= (int32_t) insn_count ||
         !InsnIsOpcode(insn_flags, abs_offset)) {
       LOG(ERROR) << "VFY: invalid switch target " << offset << " (-> "
-                 << abs_offset << ") at " << cur_offset << "[" << targ << "]";
+                 << std::hex << abs_offset << ") at " << cur_offset << std::dec
+                 << "[" << targ << "]";
       return false;
     }
-    InsnSetBranchTarget(insn_flags, abs_offset, true);
+    InsnSetBranchTarget(insn_flags, abs_offset);
   }
 
   return true;
 }
 
-/*
- * Verify that the target of a branch instruction is valid.
- *
- * We don't expect code to jump directly into an exception handler, but
- * it's valid to do so as long as the target isn't a "move-exception"
- * instruction.  We verify that in a later stage.
- *
- * The VM spec doesn't forbid an instruction from branching to itself,
- * but the Dalvik spec declares that only certain instructions can do so.
- *
- * Updates "insnFlags", setting the "branch target" flag.
- */
-static bool CheckBranchTarget(const DexFile::CodeItem* code_item,
-    uint32_t insn_flags[], int cur_offset) {
-  const int insn_count = code_item->insns_size_;
+bool DexVerifier::CheckBranchTarget(const DexFile::CodeItem* code_item,
+    InsnFlags insn_flags[], uint32_t cur_offset) {
+  const uint32_t insn_count = code_item->insns_size_;
   int32_t offset, abs_offset;
   bool isConditional, selfOkay;
 
@@ -510,7 +789,8 @@
     return false;
 
   if (!selfOkay && offset == 0) {
-    LOG(ERROR) << "VFY: branch offset of zero not allowed at" << cur_offset;
+    LOG(ERROR) << "VFY: branch offset of zero not allowed at" << std::hex
+               << cur_offset << std::dec;
     return false;
   }
 
@@ -520,318 +800,4435 @@
    * it's unwise to depend on that.
    */
   if (((int64_t) cur_offset + (int64_t) offset) !=
-      (int64_t)(cur_offset + offset)) {
-    LOG(ERROR) << "VFY: branch target overflow " << cur_offset << " +"
-               << offset;
+      (int64_t) (cur_offset + offset)) {
+    LOG(ERROR) << "VFY: branch target overflow " << std::hex << cur_offset
+               << std::dec << " +" << offset;
     return false;
   }
   abs_offset = cur_offset + offset;
-  if (abs_offset < 0 || abs_offset >= insn_count ||
+  if (abs_offset < 0 || (uint32_t) abs_offset >= insn_count ||
       !InsnIsOpcode(insn_flags, abs_offset))
   {
     LOG(ERROR) << "VFY: invalid branch target " << offset << " (-> "
-               << abs_offset << ") at " << cur_offset;
+               << std::hex << abs_offset << ") at " << cur_offset << std::dec;
     return false;
   }
-  InsnSetBranchTarget(insn_flags, abs_offset, true);
+  InsnSetBranchTarget(insn_flags, abs_offset);
 
   return true;
 }
 
-bool CheckInsnWidth(const uint16_t* insns, uint32_t insns_size,
-    uint32_t insn_flags[]) {
-  const byte* ptr = reinterpret_cast<const byte*>(insns);
+bool DexVerifier::InitRegisterTable(VerifierData* vdata,
+    RegisterTable* reg_table, RegisterTrackingMode track_regs_for) {
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  InsnFlags* insn_flags = vdata->insn_flags_;
+  uint16_t registers_size = code_item->registers_size_;
+  uint32_t insns_size = code_item->insns_size_;
+  uint32_t i;
+
+  /*
+   * Every address gets a RegisterLine struct.  This is wasteful, but
+   * not so much that it's worth chasing through an extra level of
+   * indirection.
+   */
+  reg_table->insn_reg_count_plus_ = registers_size + kExtraRegs;
+  reg_table->register_lines_ = new RegisterLine[insns_size]();
+
+  assert(insns_size > 0);
+
+  bool track_monitors;
+  //if (gDvm.monitorVerification) {
+    //track_monitors = (vdata->monitor_enter_count_ != 0);
+  //} else {
+    track_monitors = false;
+  //}
+
+  /*
+   * Allocate entries in the sparse register line table.
+   *
+   * There is a RegisterLine associated with every address, but not
+   * every RegisterLine has non-NULL pointers to storage for its fields.
+   */
+  for (i = 0; i < insns_size; i++) {
+    bool interesting;
+
+    switch (track_regs_for) {
+      case kTrackRegsAll:
+        interesting = InsnIsOpcode(insn_flags, i);
+        break;
+      case kTrackRegsGcPoints:
+        interesting = InsnIsGcPoint(insn_flags, i) ||
+                      InsnIsBranchTarget(insn_flags, i);
+        break;
+      case kTrackRegsBranches:
+        interesting = InsnIsBranchTarget(insn_flags, i);
+        break;
+      default:
+        return false;
+    }
+
+    if (interesting) {
+      reg_table->register_lines_[i].Alloc(reg_table->insn_reg_count_plus_,
+          track_monitors);
+    }
+  }
+
+  /*
+   * Allocate space for our "temporary" register lines.
+   */
+  reg_table->work_line_.Alloc(reg_table->insn_reg_count_plus_, track_monitors);
+  reg_table->saved_line_.Alloc(reg_table->insn_reg_count_plus_, track_monitors);
+
+  return true;
+}
+
+DexVerifier::UninitInstanceMap* DexVerifier::CreateUninitInstanceMap(
+    VerifierData* vdata) {
+  Method* method = vdata->method_;
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  size_t new_instance_count = vdata->new_instance_count_;
+
+  if (IsInitMethod(method)) {
+    new_instance_count++;
+  }
+
+  /*
+   * Allocate the header and map as a single unit.
+   *
+   * TODO: consider having a static instance so we can avoid allocations.
+   * I don't think the verifier is guaranteed to be single-threaded when
+   * running in the VM (rather than dexopt), so that must be taken into
+   * account.
+   */
+  UninitInstanceMap* uninit_map = new UninitInstanceMap(new_instance_count);
+
+  size_t idx = 0;
+  if (IsInitMethod(method)) {
+    uninit_map->map_[idx++].addr_ = kUninitThisArgAddr;
+  }
+
+  /*
+   * Run through and find the new-instance instructions.
+   */
+  uint32_t addr = 0;
+  uint32_t insns_size = code_item->insns_size_;
+  const byte* ptr = reinterpret_cast<const byte*>(code_item->insns_);
   const Instruction* inst = Instruction::At(ptr);
+  while (addr < insns_size) {
+    Instruction::Code opcode = inst->Opcode();
+    if (opcode == Instruction::NEW_INSTANCE) {
+      uninit_map->map_[idx++].addr_ = addr;
+    }
 
-  size_t width = 0;
-
-  while (width < insns_size) {
-    insn_flags[width] |= inst->Size();
-    width += inst->Size();
+    addr += inst->Size();
     inst = inst->Next();
   }
 
-  if (width != insns_size) {
-    LOG(ERROR) << "VFY: code did not end where expected (" << width << " vs. "
-               << insns_size << ")";
+  assert(idx == new_instance_count);
+  return uninit_map;
+}
+
+bool DexVerifier::IsInitMethod(const Method* method) {
+  return (method->GetName()->Equals("<init>"));
+}
+
+Class* DexVerifier::LookupClassByDescriptor(const Method* method,
+    const char* descriptor, VerifyError* failure) {
+  /*
+   * The compiler occasionally puts references to nonexistent
+   * classes in signatures.  For example, if you have a non-static
+   * inner class with no constructor, the compiler provides
+   * a private <init> for you.  Constructing the class
+   * requires <init>(parent), but the outer class can't call
+   * that because the method is private.  So the compiler
+   * generates a package-scope <init>(parent,bogus) method that
+   * just calls the regular <init> (the "bogus" part being necessary
+   * to distinguish the signature of the synthetic method).
+   * Treating the bogus class as an instance of java.lang.Object
+   * allows the verifier to process the class successfully.
+   */
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  const ClassLoader* class_loader =
+      method->GetDeclaringClass()->GetClassLoader();
+  Class* klass = class_linker->FindClass(descriptor, class_loader);
+
+  if (klass == NULL) {
+    Thread::Current()->ClearException();
+    if (strchr(descriptor, '$') != NULL) {
+      LOG(INFO) << "VFY: unable to find class referenced in signature ("
+                << descriptor << ")";
+    } else {
+      LOG(ERROR) << "VFY: unable to find class referenced in signature ("
+                 << descriptor << ")";
+    }
+
+    /* Check if the descriptor is an array. */
+    if (descriptor[0] == '[') {
+      /*
+       * There should never be a problem loading primitive arrays.
+       */
+      if (descriptor[1] != 'L' && descriptor[1] != '[') {
+        LOG(ERROR) << "VFY: invalid char in signature in '" << descriptor
+                   << "'";
+        *failure = VERIFY_ERROR_GENERIC;
+      }
+
+      /*
+       * Try to continue with base array type.  This will let
+       * us pass basic stuff (e.g. get array len) that wouldn't
+       * fly with an Object.  This is NOT correct if the
+       * missing type is a primitive array, but we should never
+       * have a problem loading those.  (I'm not convinced this
+       * is correct or even useful.  Just use Object here?)
+       */
+      klass = class_linker->FindClass("[Ljava/lang/Object;", class_loader);
+    } else if (descriptor[0] == 'L') {
+      /*
+       * We are looking at a non-array reference descriptor;
+       * try to continue with base reference type.
+       */
+      klass = class_linker->FindSystemClass("Ljava/lang/Object;");
+    } else {
+      /* We are looking at a primitive type. */
+      LOG(ERROR) << "VFY: invalid char in signature in '" << descriptor << "'";
+      *failure = VERIFY_ERROR_GENERIC;
+    }
+
+    if (klass == NULL) {
+      *failure = VERIFY_ERROR_GENERIC;
+    }
+  }
+
+  if (klass->IsPrimitive()) {
+    LOG(ERROR) << "VFY: invalid use of primitive type '" << descriptor << "'";
+    *failure = VERIFY_ERROR_GENERIC;
+    klass = NULL;
+  }
+
+  return klass;
+}
+
+Class* DexVerifier::LookupSignatureClass(const Method* method, std::string sig,
+    VerifyError* failure) {
+  assert(sig[0] == 'L');
+  size_t end = sig.find(';');
+
+  if (end == std::string::npos) {
+    LOG(ERROR) << "VFY: bad signature component '" << sig << "' (missing ';')";
+    *failure = VERIFY_ERROR_GENERIC;
+    return NULL;
+  }
+
+  return LookupClassByDescriptor(method, sig.substr(0, end + 1).c_str(),
+      failure);
+}
+
+Class* DexVerifier::LookupSignatureArrayClass(const Method* method,
+    std::string sig, VerifyError* failure) {
+  assert(sig[0] == '[');
+  size_t end = 0;
+
+  while (sig[end] == '[')
+    end++;
+
+  if (sig[end] == 'L') {
+    end = sig.find(';');
+    if (end == std::string::npos) {
+      LOG(ERROR) << "VFY: bad signature component '" << sig
+                 << "' (missing ';')";
+      *failure = VERIFY_ERROR_GENERIC;
+      return NULL;
+    }
+  }
+
+  return LookupClassByDescriptor(method, sig.substr(0, end + 1).c_str(),
+      failure);
+}
+
+bool DexVerifier::SetTypesFromSignature(VerifierData* vdata, RegType* reg_types)
+{
+  Method* method = vdata->method_;
+  const DexFile* dex_file = vdata->dex_file_;
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  UninitInstanceMap* uninit_map = vdata->uninit_map_;
+
+  int arg_start = code_item->registers_size_ - code_item->ins_size_;
+  int expected_args = code_item->ins_size_;   /* long/double count as two */
+  int actual_args = 0;
+
+  assert(arg_start >= 0);      /* should have been verified earlier */
+
+  /*
+   * Include the "this" pointer.
+   */
+  if (!method->IsStatic()) {
+    /*
+     * If this is a constructor for a class other than java.lang.Object,
+     * mark the first ("this") argument as uninitialized.  This restricts
+     * field access until the superclass constructor is called.
+     */
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    Class* klass_object = class_linker->FindSystemClass("Ljava/lang/Object;");
+    if (IsInitMethod(method) && method->GetDeclaringClass() != klass_object) {
+      int idx = SetUninitInstance(uninit_map, kUninitThisArgAddr,
+          method->GetDeclaringClass());
+      assert(idx == 0);
+      reg_types[arg_start + actual_args] = RegTypeFromUninitIndex(idx);
+    } else {
+      reg_types[arg_start + actual_args] =
+          RegTypeFromClass(method->GetDeclaringClass());
+    }
+    actual_args++;
+  }
+
+  const DexFile::ProtoId& proto_id =
+      dex_file->GetProtoId(method->GetProtoIdx());
+  DexFile::ParameterIterator iterator(*dex_file, proto_id);
+  VerifyError failure = VERIFY_ERROR_NONE;
+
+  for (; iterator.HasNext(); iterator.Next()) {
+    const char* descriptor = iterator.GetDescriptor();
+
+    if (descriptor == NULL) {
+      break;
+    }
+
+    if (actual_args >= expected_args) {
+      LOG(ERROR) << "VFY: expected " << expected_args << " args, found more ("
+                 << descriptor << ")";
+      return false;
+    }
+
+    switch (*descriptor) {
+      case 'L':
+      case '[':
+        /*
+         * We assume that reference arguments are initialized.  The
+         * only way it could be otherwise (assuming the caller was
+         * verified) is if the current method is <init>, but in that
+         * case it's effectively considered initialized the instant
+         * we reach here (in the sense that we can return without
+         * doing anything or call virtual methods).
+         */
+        {
+          Class* klass =
+              LookupClassByDescriptor(method, descriptor, &failure);
+          if (failure != VERIFY_ERROR_NONE)
+            return false;
+          reg_types[arg_start + actual_args] = RegTypeFromClass(klass);
+        }
+        actual_args++;
+        break;
+      case 'Z':
+        reg_types[arg_start + actual_args] = kRegTypeBoolean;
+        actual_args++;
+        break;
+      case 'C':
+        reg_types[arg_start + actual_args] = kRegTypeChar;
+        actual_args++;
+        break;
+      case 'B':
+        reg_types[arg_start + actual_args] = kRegTypeByte;
+        actual_args++;
+        break;
+      case 'I':
+        reg_types[arg_start + actual_args] = kRegTypeInteger;
+        actual_args++;
+        break;
+      case 'S':
+        reg_types[arg_start + actual_args] = kRegTypeShort;
+        actual_args++;
+        break;
+      case 'F':
+        reg_types[arg_start + actual_args] = kRegTypeFloat;
+        actual_args++;
+        break;
+      case 'D':
+        reg_types[arg_start + actual_args] = kRegTypeDoubleLo;
+        reg_types[arg_start + actual_args +1] = kRegTypeDoubleHi;
+        actual_args += 2;
+        break;
+      case 'J':
+        reg_types[arg_start + actual_args] = kRegTypeLongLo;
+        reg_types[arg_start + actual_args +1] = kRegTypeLongHi;
+        actual_args += 2;
+        break;
+      default:
+        LOG(ERROR) << "VFY: unexpected signature type char '" << descriptor
+                   << "'";
+        return false;
+    }
+  }
+
+  if (actual_args != expected_args) {
+    LOG(ERROR) << "VFY: expected " << expected_args << " args, found "
+               << actual_args;
     return false;
   }
 
+  const char* descriptor = dex_file->GetReturnTypeDescriptor(proto_id);
+
+  /*
+   * Validate return type.  We don't do the type lookup; just want to make
+   * sure that it has the right format.  Only major difference from the
+   * method argument format is that 'V' is supported.
+   */
+  switch (*descriptor) {
+    case 'I':
+    case 'C':
+    case 'S':
+    case 'B':
+    case 'Z':
+    case 'V':
+    case 'F':
+    case 'D':
+    case 'J':
+      if (*(descriptor + 1) != '\0')
+        return false;
+      break;
+    case '[':
+      /* single/multi, object/primitive */
+      while (*++descriptor == '[')
+        ;
+      if (*descriptor == 'L') {
+        while (*++descriptor != ';' && *descriptor != '\0')
+          ;
+        if (*descriptor != ';')
+          return false;
+      } else {
+        if (*(descriptor+1) != '\0')
+          return false;
+      }
+      break;
+    case 'L':
+      /* could be more thorough here, but shouldn't be required */
+      while (*++descriptor != ';' && *descriptor != '\0')
+        ;
+      if (*descriptor != ';')
+        return false;
+      break;
+    default:
+      return false;
+  }
+
   return true;
 }
 
-/*
- * Set the "in try" flags for all instructions protected by "try" statements.
- * Also sets the "branch target" flags for exception handlers.
- *
- * Call this after widths have been set in "insn_flags".
- *
- * Returns "false" if something in the exception table looks fishy, but
- * we're expecting the exception table to be somewhat sane.
- */
-static bool ScanTryCatchBlocks(const DexFile::CodeItem* code_item,
-    uint32_t insn_flags[]) {
+int DexVerifier::SetUninitInstance(UninitInstanceMap* uninit_map, int addr,
+    Class* klass) {
+  int idx;
+  assert(klass != NULL);
+
+  /* TODO: binary search when numEntries > 8 */
+  for (idx = uninit_map->num_entries_ - 1; idx >= 0; idx--) {
+    if (uninit_map->map_[idx].addr_ == addr) {
+      if (uninit_map->map_[idx].klass_ != NULL &&
+          uninit_map->map_[idx].klass_ != klass) {
+        LOG(ERROR) << "VFY: addr " << addr << " already set to "
+                   << (int) uninit_map->map_[idx].klass_ << ", not setting to "
+                   << (int) klass;
+        return -1;          // already set to something else??
+      }
+      uninit_map->map_[idx].klass_ = klass;
+      return idx;
+    }
+  }
+
+  LOG(ERROR) << "VFY: addr " << addr << " not found in uninit map";
+  assert(false);      // shouldn't happen
+  return -1;
+}
+
+bool DexVerifier::CodeFlowVerifyMethod(VerifierData* vdata,
+    RegisterTable* reg_table) {
+  const Method* method = vdata->method_;
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  InsnFlags* insn_flags = vdata->insn_flags_;
+  const uint16_t* insns = code_item->insns_;
   uint32_t insns_size = code_item->insns_size_;
-  uint32_t tries_size = code_item->tries_size_;
+  size_t insn_idx, start_guess;
 
-  if (tries_size == 0) {
-    return true;
-  }
+  /* Begin by marking the first instruction as "changed". */
+  InsnSetChanged(insn_flags, 0, true);
 
-  const DexFile::TryItem* tries = DexFile::dexGetTryItems(*code_item, 0);
+  start_guess = 0;
 
-  for (uint32_t idx = 0; idx < tries_size; idx++) {
-    const DexFile::TryItem* try_item = &tries[idx];
-    uint32_t start = try_item->start_addr_;
-    uint32_t end = start + try_item->insn_count_;
+  /* Continue until no instructions are marked "changed". */
+  while (true) {
+    /*
+     * Find the first marked one.  Use "start_guess" as a way to find
+     * one quickly.
+     */
+    for (insn_idx = start_guess; insn_idx < insns_size; insn_idx++) {
+      if (InsnIsChanged(insn_flags, insn_idx))
+        break;
+    }
 
-    if ((start >= end) || (start >= insns_size) || (end > insns_size)) {
-      LOG(ERROR) << "VFY: bad exception entry: startAddr=" << start
-                 << " endAddr=" << end << " (size=" << insns_size << ")";
+    if (insn_idx == insns_size) {
+      if (start_guess != 0) {
+        /* try again, starting from the top */
+        start_guess = 0;
+        continue;
+      } else {
+        /* all flags are clear */
+        break;
+      }
+    }
+
+    /*
+     * We carry the working set of registers from instruction to
+     * instruction.  If this address can be the target of a branch
+     * (or throw) instruction, or if we're skipping around chasing
+     * "changed" flags, we need to load the set of registers from
+     * the table.
+     *
+     * Because we always prefer to continue on to the next instruction,
+     * we should never have a situation where we have a stray
+     * "changed" flag set on an instruction that isn't a branch target.
+     */
+    if (InsnIsBranchTarget(insn_flags, insn_idx)) {
+      RegisterLine* work_line = &reg_table->work_line_;
+      CopyLineFromTable(work_line, reg_table, insn_idx);
+    } else {
+#ifndef NDEBUG
+      /*
+       * Sanity check: retrieve the stored register line (assuming
+       * a full table) and make sure it actually matches.
+       */
+      RegisterLine* register_line = GetRegisterLine(reg_table, insn_idx);
+      if (register_line->reg_types_ != NULL && CompareLineToTable(reg_table,
+          insn_idx, &reg_table->work_line_) != 0) {
+        Class* klass = method->GetDeclaringClass();
+        LOG(ERROR) << "HUH? work_line diverged in "
+                   << klass->GetDescriptor()->ToModifiedUtf8() << "."
+                   << method->GetName()->ToModifiedUtf8() << " "
+                   << method->GetSignature()->ToModifiedUtf8();
+      }
+#endif
+    }
+
+    if (!CodeFlowVerifyInstruction(vdata, reg_table, insn_idx, &start_guess)) {
+      Class* klass = method->GetDeclaringClass();
+      LOG(ERROR) << "VFY: failure to verify "
+                 << klass->GetDescriptor()->ToModifiedUtf8() << "."
+                << method->GetName()->ToModifiedUtf8() << " "
+                << method->GetSignature()->ToModifiedUtf8();
       return false;
     }
 
-    if (InsnGetWidth(insn_flags, start) == 0) {
-      LOG(ERROR) << "VFY: 'try' block starts inside an instruction ("
-                 << start << ")";
-      return false;
-    }
+    /* Clear "changed" and mark as visited. */
+    InsnSetVisited(insn_flags, insn_idx, true);
+    InsnSetChanged(insn_flags, insn_idx, false);
+  }
 
-    uint32_t addr;
-    for (addr = start; addr < end; addr += InsnGetWidth(insn_flags, addr)) {
-      InsnSetInTry(insn_flags, addr, true);
+  if (DEAD_CODE_SCAN && ((method->GetAccessFlags() & kAccWritable) == 0)) {
+    /*
+     * Scan for dead code.  There's nothing "evil" about dead code
+     * (besides the wasted space), but it indicates a flaw somewhere
+     * down the line, possibly in the verifier.
+     *
+     * If we've substituted "always throw" instructions into the stream,
+     * we are almost certainly going to have some dead code.
+     */
+    int dead_start = -1;
+    for (insn_idx = 0; insn_idx < insns_size;
+         insn_idx += InsnGetWidth(insn_flags, insn_idx)) {
+      /*
+       * Switch-statement data doesn't get "visited" by scanner.  It
+       * may or may not be preceded by a padding NOP (for alignment).
+       */
+      if (insns[insn_idx] == Instruction::kPackedSwitchSignature ||
+          insns[insn_idx] == Instruction::kSparseSwitchSignature ||
+          insns[insn_idx] == Instruction::kArrayDataSignature ||
+          (insns[insn_idx] == Instruction::NOP &&
+           (insns[insn_idx + 1] == Instruction::kPackedSwitchSignature ||
+            insns[insn_idx + 1] == Instruction::kSparseSwitchSignature ||
+            insns[insn_idx + 1] == Instruction::kArrayDataSignature))) {
+        InsnSetVisited(insn_flags, insn_idx, true);
+      }
+
+      if (!InsnIsVisited(insn_flags, insn_idx)) {
+        if (dead_start < 0)
+          dead_start = insn_idx;
+      } else if (dead_start >= 0) {
+        Class* klass = method->GetDeclaringClass();
+        LOG(INFO) << "VFY: dead code 0x" << std::hex << dead_start << "-"
+                  << insn_idx - 1 << std::dec << " in "
+                  << klass->GetDescriptor()->ToModifiedUtf8() << "."
+                  << method->GetName()->ToModifiedUtf8() << " "
+                  << method->GetSignature()->ToModifiedUtf8();
+        dead_start = -1;
+      }
+    }
+    if (dead_start >= 0) {
+      Class* klass = method->GetDeclaringClass();
+      LOG(INFO) << "VFY: dead code 0x" << std::hex << dead_start << "-"
+                << insn_idx - 1 << std::dec << " in "
+                << klass->GetDescriptor()->ToModifiedUtf8() << "."
+                << method->GetName()->ToModifiedUtf8() << " "
+                << method->GetSignature()->ToModifiedUtf8();
     }
   }
 
-  /* Iterate over each of the handlers to verify target addresses. */
+  return true;
+}
+
+bool DexVerifier::CodeFlowVerifyInstruction(VerifierData* vdata,
+    RegisterTable* reg_table, uint32_t insn_idx, size_t* start_guess) {
+  const Method* method = vdata->method_;
+  Class* klass = method->GetDeclaringClass();
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  InsnFlags* insn_flags = vdata->insn_flags_;
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  UninitInstanceMap* uninit_map = vdata->uninit_map_;
+  const uint16_t* insns = code_item->insns_ + insn_idx;
+  uint32_t insns_size = code_item->insns_size_;
+  uint32_t registers_size = code_item->registers_size_;
+
+#ifdef VERIFIER_STATS
+  if (InsnIsVisited(insn_flags, insn_idx)) {
+    gDvm.verifierStats.instrsReexamined++;
+  } else {
+    gDvm.verifierStats.instrsExamined++;
+  }
+#endif
+
+  /*
+   * Once we finish decoding the instruction, we need to figure out where
+   * we can go from here.  There are three possible ways to transfer
+   * control to another statement:
+   *
+   * (1) Continue to the next instruction.  Applies to all but
+   *     unconditional branches, method returns, and exception throws.
+   * (2) Branch to one or more possible locations.  Applies to branches
+   *     and switch statements.
+   * (3) Exception handlers.  Applies to any instruction that can
+   *     throw an exception that is handled by an encompassing "try"
+   *     block.
+   *
+   * We can also return, in which case there is no successor instruction
+   * from this point.
+   *
+   * The behavior can be determined from the OpcodeFlags.
+   */
+  RegisterLine* work_line = &reg_table->work_line_;
+  const DexFile* dex_file = vdata->dex_file_;
+  const byte* ptr = reinterpret_cast<const byte*>(insns);
+  const Instruction* inst = Instruction::At(ptr);
+  Instruction::DecodedInstruction dec_insn(inst);
+  int opcode_flag = inst->Flag();
+
+  Class* res_class;
+  int32_t branch_target = 0;
+  RegType tmp_type;
+  bool just_set_result = false;
+  VerifyError failure = VERIFY_ERROR_NONE;
+
+  /*
+   * Make a copy of the previous register state.  If the instruction
+   * can throw an exception, we will copy/merge this into the "catch"
+   * address rather than work_line, because we don't want the result
+   * from the "successful" code path (e.g. a check-cast that "improves"
+   * a type) to be visible to the exception handler.
+   */
+  if ((opcode_flag & Instruction::kThrow) != 0 &&
+      InsnIsInTry(insn_flags, insn_idx)) {
+    CopyRegisterLine(&reg_table->saved_line_, work_line,
+        reg_table->insn_reg_count_plus_);
+  } else {
+#ifndef NDEBUG
+    memset(reg_table->saved_line_.reg_types_, 0xdd,
+        reg_table->insn_reg_count_plus_ * sizeof(RegType));
+#endif
+  }
+
+  switch (dec_insn.opcode_) {
+    case Instruction::NOP:
+      /*
+       * A "pure" NOP has no effect on anything.  Data tables start with
+       * a signature that looks like a NOP; if we see one of these in
+       * the course of executing code then we have a problem.
+       */
+      if (dec_insn.vA_ != 0) {
+        LOG(ERROR) << "VFY: encountered data table in instruction stream";
+        failure = VERIFY_ERROR_GENERIC;
+      }
+      break;
+
+    case Instruction::MOVE:
+    case Instruction::MOVE_FROM16:
+    case Instruction::MOVE_16:
+      CopyRegister1(work_line, dec_insn.vA_, dec_insn.vB_, kTypeCategory1nr,
+          &failure);
+      break;
+    case Instruction::MOVE_WIDE:
+    case Instruction::MOVE_WIDE_FROM16:
+    case Instruction::MOVE_WIDE_16:
+      CopyRegister2(work_line, dec_insn.vA_, dec_insn.vB_, &failure);
+      break;
+    case Instruction::MOVE_OBJECT:
+    case Instruction::MOVE_OBJECT_FROM16:
+    case Instruction::MOVE_OBJECT_16:
+      CopyRegister1(work_line, dec_insn.vA_, dec_insn.vB_, kTypeCategoryRef,
+          &failure);
+      break;
+
+    /*
+     * The move-result instructions copy data out of a "pseudo-register"
+     * with the results from the last method invocation.  In practice we
+     * might want to hold the result in an actual CPU register, so the
+     * Dalvik spec requires that these only appear immediately after an
+     * invoke or filled-new-array.
+     *
+     * These calls invalidate the "result" register.  (This is now
+     * redundant with the reset done below, but it can make the debug info
+     * easier to read in some cases.)
+     */
+    case Instruction::MOVE_RESULT:
+      CopyResultRegister1(work_line, registers_size, dec_insn.vA_,
+          kTypeCategory1nr, &failure);
+      break;
+    case Instruction::MOVE_RESULT_WIDE:
+      CopyResultRegister2(work_line, registers_size, dec_insn.vA_, &failure);
+      break;
+    case Instruction::MOVE_RESULT_OBJECT:
+      CopyResultRegister1(work_line, registers_size, dec_insn.vA_,
+          kTypeCategoryRef, &failure);
+      break;
+
+    case Instruction::MOVE_EXCEPTION:
+      /*
+       * This statement can only appear as the first instruction in an
+       * exception handler (though not all exception handlers need to
+       * have one of these).  We verify that as part of extracting the
+       * exception type from the catch block list.
+       *
+       * "res_class" will hold the closest common superclass of all
+       * exceptions that can be handled here.
+       */
+      res_class = GetCaughtExceptionType(vdata, insn_idx, &failure);
+      if (res_class == NULL) {
+        assert(failure != VERIFY_ERROR_NONE);
+      } else {
+        SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(res_class));
+      }
+      break;
+
+    case Instruction::RETURN_VOID:
+      if (!CheckConstructorReturn(method, work_line, registers_size)) {
+        failure = VERIFY_ERROR_GENERIC;
+      } else if (GetMethodReturnType(dex_file, method) != kRegTypeUnknown) {
+        LOG(ERROR) << "VFY: return-void not expected";
+        failure = VERIFY_ERROR_GENERIC;
+      }
+      break;
+    case Instruction::RETURN:
+      if (!CheckConstructorReturn(method, work_line, registers_size)) {
+        failure = VERIFY_ERROR_GENERIC;
+      } else {
+        /* check the method signature */
+        RegType return_type = GetMethodReturnType(dex_file, method);
+        CheckTypeCategory(return_type, kTypeCategory1nr, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          LOG(ERROR) << "VFY: return-1nr not expected";
+
+        /*
+         * compiler may generate synthetic functions that write byte
+         * values into boolean fields. Also, it may use integer values
+         * for boolean, byte, short, and character return types.
+         */
+        RegType src_type = GetRegisterType(work_line, dec_insn.vA_);
+        if ((return_type == kRegTypeBoolean && src_type == kRegTypeByte) ||
+           ((return_type == kRegTypeBoolean || return_type == kRegTypeByte ||
+             return_type == kRegTypeShort || return_type == kRegTypeChar) &&
+             src_type == kRegTypeInteger))
+          return_type = src_type;
+
+        /* check the register contents */
+        VerifyRegisterType(work_line, dec_insn.vA_, return_type, &failure);
+        if (failure != VERIFY_ERROR_NONE) {
+          LOG(ERROR) << "VFY: return-1nr on invalid register v" << dec_insn.vA_;
+        }
+      }
+      break;
+    case Instruction::RETURN_WIDE:
+      if (!CheckConstructorReturn(method, work_line, registers_size)) {
+        failure = VERIFY_ERROR_GENERIC;
+      } else {
+        RegType return_type;
+
+        /* check the method signature */
+        return_type = GetMethodReturnType(dex_file, method);
+        CheckTypeCategory(return_type, kTypeCategory2, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          LOG(ERROR) << "VFY: return-wide not expected";
+
+        /* check the register contents */
+        VerifyRegisterType(work_line, dec_insn.vA_, return_type, &failure);
+        if (failure != VERIFY_ERROR_NONE) {
+          LOG(ERROR) << "VFY: return-wide on invalid register pair v"
+                     << dec_insn.vA_;
+        }
+      }
+      break;
+    case Instruction::RETURN_OBJECT:
+      if (!CheckConstructorReturn(method, work_line, registers_size)) {
+        failure = VERIFY_ERROR_GENERIC;
+      } else {
+        RegType return_type = GetMethodReturnType(dex_file, method);
+        CheckTypeCategory(return_type, kTypeCategoryRef, &failure);
+        if (failure != VERIFY_ERROR_NONE) {
+          LOG(ERROR) << "VFY: return-object not expected";
+          break;
+        }
+
+        /* return_type is the *expected* return type, not register value */
+        assert(return_type != kRegTypeZero);
+        assert(!RegTypeIsUninitReference(return_type));
+
+        /*
+         * Verify that the reference in vAA is an instance of the type
+         * in "return_type".  The Zero type is allowed here.  If the
+         * method is declared to return an interface, then any
+         * initialized reference is acceptable.
+         *
+         * Note GetClassFromRegister fails if the register holds an
+         * uninitialized reference, so we do not allow them to be
+         * returned.
+         */
+        Class* decl_class = RegTypeInitializedReferenceToClass(return_type);
+        res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        if (res_class != NULL) {
+          if (!decl_class->IsInterface() &&
+              //!res_class->InstanceOf(decl_class)) {
+              !decl_class->IsAssignableFrom(res_class)) {
+            LOG(ERROR) << "VFY: returning " << std::hex
+                       << res_class->GetDescriptor()->ToModifiedUtf8()
+                       << " (cl=0x" << (int) res_class->GetClassLoader()
+                       << "), declared "
+                       << decl_class->GetDescriptor()->ToModifiedUtf8()
+                       << " (cl=0x" << (int) decl_class->GetClassLoader()
+                       << ")" << std::dec;
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+        }
+      }
+      break;
+
+    case Instruction::CONST_4:
+    case Instruction::CONST_16:
+    case Instruction::CONST:
+      /* could be boolean, int, float, or a null reference */
+      SetRegisterType(work_line, dec_insn.vA_,
+          DetermineCat1Const((int32_t) dec_insn.vB_));
+      break;
+    case Instruction::CONST_HIGH16:
+      /* could be boolean, int, float, or a null reference */
+      SetRegisterType(work_line, dec_insn.vA_,
+          DetermineCat1Const((int32_t) dec_insn.vB_ << 16));
+      break;
+    case Instruction::CONST_WIDE_16:
+    case Instruction::CONST_WIDE_32:
+    case Instruction::CONST_WIDE:
+    case Instruction::CONST_WIDE_HIGH16:
+      /* could be long or double; resolved upon use */
+      SetRegisterType(work_line, dec_insn.vA_, kRegTypeConstLo);
+      break;
+    case Instruction::CONST_STRING:
+    case Instruction::CONST_STRING_JUMBO:
+      SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(
+          class_linker->FindSystemClass("Ljava/lang/String;")));
+      break;
+    case Instruction::CONST_CLASS:
+      /* make sure we can resolve the class; access check is important */
+      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      if (res_class == NULL) {
+        const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+        LOG(ERROR) << "VFY: unable to resolve const-class " << dec_insn.vB_
+                   << " (" << bad_class_desc << ") in "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+        assert(failure != VERIFY_ERROR_GENERIC);
+      } else {
+        SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(
+            class_linker->FindSystemClass("Ljava/lang/Class;")));
+      }
+      break;
+
+    case Instruction::MONITOR_ENTER:
+      HandleMonitorEnter(work_line, dec_insn.vA_, insn_idx, &failure);
+      break;
+    case Instruction::MONITOR_EXIT:
+      /*
+       * monitor-exit instructions are odd.  They can throw exceptions,
+       * but when they do they act as if they succeeded and the PC is
+       * pointing to the following instruction.  (This behavior goes back
+       * to the need to handle asynchronous exceptions, a now-deprecated
+       * feature that Dalvik doesn't support.)
+       *
+       * In practice we don't need to worry about this.  The only
+       * exceptions that can be thrown from monitor-exit are for a
+       * null reference and -exit without a matching -enter.  If the
+       * structured locking checks are working, the former would have
+       * failed on the -enter instruction, and the latter is impossible.
+       *
+       * This is fortunate, because issue 3221411 prevents us from
+       * chasing the "can throw" path when monitor verification is
+       * enabled.  If we can fully verify the locking we can ignore
+       * some catch blocks (which will show up as "dead" code when
+       * we skip them here); if we can't, then the code path could be
+       * "live" so we still need to check it.
+       */
+      if (work_line->monitor_entries_ != NULL)
+        opcode_flag &= ~Instruction::kThrow;
+      HandleMonitorExit(work_line, dec_insn.vA_, insn_idx, &failure);
+      break;
+
+    case Instruction::CHECK_CAST:
+      /*
+       * If this instruction succeeds, we will promote register vA to
+       * the type in vB.  (This could be a demotion -- not expected, so
+       * we don't try to address it.)
+       *
+       * If it fails, an exception is thrown, which we deal with later
+       * by ignoring the update to dec_insn.vA_ when branching to a handler.
+       */
+      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      if (res_class == NULL) {
+        const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+        LOG(ERROR) << "VFY: unable to resolve check-cast " << dec_insn.vB_
+                   << " (" << bad_class_desc << ") in "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+        assert(failure != VERIFY_ERROR_GENERIC);
+      } else {
+        RegType orig_type;
+
+        orig_type = GetRegisterType(work_line, dec_insn.vA_);
+        if (!RegTypeIsReference(orig_type)) {
+          LOG(ERROR) << "VFY: check-cast on non-reference in v" << dec_insn.vA_;
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+        SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(res_class));
+      }
+      break;
+    case Instruction::INSTANCE_OF:
+      /* make sure we're checking a reference type */
+      tmp_type = GetRegisterType(work_line, dec_insn.vB_);
+      if (!RegTypeIsReference(tmp_type)) {
+        LOG(ERROR) << "VFY: vB not a reference (" << tmp_type << ")";
+        failure = VERIFY_ERROR_GENERIC;
+        break;
+      }
+
+      /* make sure we can resolve the class; access check is important */
+      res_class = class_linker->ResolveType(*dex_file, dec_insn.vC_, klass);
+      if (res_class == NULL) {
+        const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vC_);
+        LOG(ERROR) << "VFY: unable to resolve instanceof " << dec_insn.vC_
+                   << " (" << bad_class_desc << ") in "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+        assert(failure != VERIFY_ERROR_GENERIC);
+      } else {
+        /* result is boolean */
+        SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+      }
+      break;
+
+    case Instruction::ARRAY_LENGTH:
+      res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+      if (res_class != NULL && !res_class->IsArrayClass()) {
+        LOG(ERROR) << "VFY: array-length on non-array";
+        failure = VERIFY_ERROR_GENERIC;
+        break;
+      }
+      SetRegisterType(work_line, dec_insn.vA_, kRegTypeInteger);
+      break;
+
+    case Instruction::NEW_INSTANCE:
+      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      if (res_class == NULL) {
+        const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+        LOG(ERROR) << "VFY: unable to resolve new-instance " << dec_insn.vB_
+                   << " (" << bad_class_desc << ") in "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+        assert(failure != VERIFY_ERROR_GENERIC);
+      } else {
+        RegType uninit_type;
+
+        /* can't create an instance of an interface or abstract class */
+        if (res_class->IsAbstract() || res_class->IsInterface()) {
+          LOG(ERROR) << "VFY: new-instance on interface or abstract class"
+                     << res_class->GetDescriptor()->ToModifiedUtf8();
+          failure = VERIFY_ERROR_INSTANTIATION;
+          break;
+        }
+
+        /* add resolved class to uninit map if not already there */
+        int uidx = SetUninitInstance(uninit_map, insn_idx, res_class);
+        assert(uidx >= 0);
+        uninit_type = RegTypeFromUninitIndex(uidx);
+
+        /*
+         * Any registers holding previous allocations from this address
+         * that have not yet been initialized must be marked invalid.
+         */
+        MarkUninitRefsAsInvalid(work_line, registers_size, uninit_map,
+            uninit_type);
+
+        /* add the new uninitialized reference to the register ste */
+        SetRegisterType(work_line, dec_insn.vA_, uninit_type);
+      }
+      break;
+   case Instruction::NEW_ARRAY:
+      res_class = class_linker->ResolveType(*dex_file, dec_insn.vC_, klass);
+      if (res_class == NULL) {
+        const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vC_);
+        LOG(ERROR) << "VFY: unable to resolve new-array " << dec_insn.vC_
+                   << " (" << bad_class_desc << ") in "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+        assert(failure != VERIFY_ERROR_GENERIC);
+      } else if (!res_class->IsArrayClass()) {
+        LOG(ERROR) << "VFY: new-array on non-array class";
+        failure = VERIFY_ERROR_GENERIC;
+      } else {
+        /* make sure "size" register is valid type */
+        VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeInteger, &failure);
+        /* set register type to array class */
+        SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(res_class));
+      }
+      break;
+    case Instruction::FILLED_NEW_ARRAY:
+    case Instruction::FILLED_NEW_ARRAY_RANGE:
+      res_class = class_linker->ResolveType(*dex_file, dec_insn.vB_, klass);
+      if (res_class == NULL) {
+        const char* bad_class_desc = dex_file->dexStringByTypeIdx(dec_insn.vB_);
+        LOG(ERROR) << "VFY: unable to resolve filled-array " << dec_insn.vB_
+                   << " (" << bad_class_desc << ") in "
+                   << klass->GetDescriptor()->ToModifiedUtf8();
+        assert(failure != VERIFY_ERROR_GENERIC);
+      } else if (!res_class->IsArrayClass()) {
+        LOG(ERROR) << "VFY: filled-new-array on non-array class";
+        failure = VERIFY_ERROR_GENERIC;
+      } else {
+        bool is_range = (dec_insn.opcode_ ==
+            Instruction::FILLED_NEW_ARRAY_RANGE);
+
+        /* check the arguments to the instruction */
+        VerifyFilledNewArrayRegs(method, work_line, &dec_insn, res_class,
+            is_range, &failure);
+        /* filled-array result goes into "result" register */
+        SetResultRegisterType(work_line, registers_size,
+            RegTypeFromClass(res_class));
+        just_set_result = true;
+      }
+      break;
+
+    case Instruction::CMPL_FLOAT:
+    case Instruction::CMPG_FLOAT:
+      VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeFloat, &failure);
+      VerifyRegisterType(work_line, dec_insn.vC_, kRegTypeFloat, &failure);
+      SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+      break;
+    case Instruction::CMPL_DOUBLE:
+    case Instruction::CMPG_DOUBLE:
+      VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeDoubleLo, &failure);
+      VerifyRegisterType(work_line, dec_insn.vC_, kRegTypeDoubleLo, &failure);
+      SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+      break;
+    case Instruction::CMP_LONG:
+      VerifyRegisterType(work_line, dec_insn.vB_, kRegTypeLongLo, &failure);
+      VerifyRegisterType(work_line, dec_insn.vC_, kRegTypeLongLo, &failure);
+      SetRegisterType(work_line, dec_insn.vA_, kRegTypeBoolean);
+      break;
+
+    case Instruction::THROW:
+      res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+      if (failure == VERIFY_ERROR_NONE && res_class != NULL) {
+        Class* throwable_class =
+            class_linker->FindSystemClass("Ljava/lang/Throwable;");
+        if (!throwable_class->IsAssignableFrom(res_class)) {
+          LOG(ERROR) << "VFY: thrown class "
+                     << res_class->GetDescriptor()->ToModifiedUtf8()
+                     << " not instanceof Throwable",
+          failure = VERIFY_ERROR_GENERIC;
+        }
+      }
+      break;
+
+    case Instruction::GOTO:
+    case Instruction::GOTO_16:
+    case Instruction::GOTO_32:
+      /* no effect on or use of registers */
+      break;
+
+    case Instruction::PACKED_SWITCH:
+    case Instruction::SPARSE_SWITCH:
+      /* verify that vAA is an integer, or can be converted to one */
+      VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeInteger, &failure);
+      break;
+
+    case Instruction::FILL_ARRAY_DATA:
+      {
+        RegType value_type;
+        const uint16_t *array_data;
+        uint16_t elem_width;
+
+        /* Similar to the verification done for APUT */
+        res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /* res_class can be null if the reg type is Zero */
+        if (res_class == NULL)
+          break;
+
+        Class::PrimitiveType prim_type =
+            res_class->GetComponentType()->GetPrimitiveType();
+        if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+            prim_type == Class::kPrimNot || prim_type == Class::kPrimVoid) {
+          LOG(ERROR) << "VFY: invalid fill-array-data on " <<
+                res_class->GetDescriptor()->ToModifiedUtf8();
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        value_type = PrimitiveTypeToRegType(prim_type);
+        assert(value_type != kRegTypeUnknown);
+
+        /*
+         * Now verify if the element width in the table matches the element
+         * width declared in the array
+         */
+        array_data = insns + (insns[1] | (((int32_t) insns[2]) << 16));
+        if (array_data[0] != Instruction::kArrayDataSignature) {
+          LOG(ERROR) << "VFY: invalid magic for array-data";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        switch (prim_type) {
+          case Class::kPrimBoolean:
+          case Class::kPrimByte:
+            elem_width = 1;
+            break;
+          case Class::kPrimChar:
+          case Class::kPrimShort:
+            elem_width = 2;
+            break;
+          case Class::kPrimFloat:
+          case Class::kPrimInt:
+            elem_width = 4;
+            break;
+          case Class::kPrimDouble:
+          case Class::kPrimLong:
+            elem_width = 8;
+            break;
+          default:
+            elem_width = 0;
+            break;
+        }
+
+        /*
+         * Since we don't compress the data in Dex, expect to see equal
+         * width of data stored in the table and expected from the array
+         * class.
+         */
+        if (array_data[1] != elem_width) {
+          LOG(ERROR) << "VFY: array-data size mismatch (" << array_data[1]
+                     << " vs " << elem_width << ")";
+          failure = VERIFY_ERROR_GENERIC;
+        }
+      }
+      break;
+
+    case Instruction::IF_EQ:
+    case Instruction::IF_NE:
+      {
+        RegType type1, type2;
+
+        type1 = GetRegisterType(work_line, dec_insn.vA_);
+        type2 = GetRegisterType(work_line, dec_insn.vB_);
+
+        /* both references? */
+        if (RegTypeIsReference(type1) && RegTypeIsReference(type2))
+          break;
+
+        /* both category-1nr? */
+        CheckTypeCategory(type1, kTypeCategory1nr, &failure);
+        CheckTypeCategory(type2, kTypeCategory1nr, &failure);
+        if (failure != VERIFY_ERROR_NONE) {
+          LOG(ERROR) << "VFY: args to if-eq/if-ne must both be refs or cat1";
+          break;
+        }
+      }
+      break;
+    case Instruction::IF_LT:
+    case Instruction::IF_GE:
+    case Instruction::IF_GT:
+    case Instruction::IF_LE:
+      tmp_type = GetRegisterType(work_line, dec_insn.vA_);
+      CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+      if (failure != VERIFY_ERROR_NONE) {
+        LOG(ERROR) << "VFY: args to 'if' must be cat-1nr";
+        break;
+      }
+      tmp_type = GetRegisterType(work_line, dec_insn.vB_);
+      CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+      if (failure != VERIFY_ERROR_NONE) {
+        LOG(ERROR) << "VFY: args to 'if' must be cat-1nr";
+        break;
+      }
+      break;
+    case Instruction::IF_EQZ:
+    case Instruction::IF_NEZ:
+      tmp_type = GetRegisterType(work_line, dec_insn.vA_);
+      if (RegTypeIsReference(tmp_type))
+        break;
+      CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        LOG(ERROR) << "VFY: expected cat-1 arg to if";
+      break;
+    case Instruction::IF_LTZ:
+    case Instruction::IF_GEZ:
+    case Instruction::IF_GTZ:
+    case Instruction::IF_LEZ:
+      tmp_type = GetRegisterType(work_line, dec_insn.vA_);
+      CheckTypeCategory(tmp_type, kTypeCategory1nr, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        LOG(ERROR) << "VFY: expected cat-1 arg to if";
+      break;
+
+    case Instruction::AGET:
+      tmp_type = kRegTypeConstInteger;
+      goto aget_1nr_common;
+    case Instruction::AGET_BOOLEAN:
+      tmp_type = kRegTypeBoolean;
+      goto aget_1nr_common;
+    case Instruction::AGET_BYTE:
+      tmp_type = kRegTypeByte;
+      goto aget_1nr_common;
+    case Instruction::AGET_CHAR:
+      tmp_type = kRegTypeChar;
+      goto aget_1nr_common;
+    case Instruction::AGET_SHORT:
+      tmp_type = kRegTypeShort;
+      goto aget_1nr_common;
+aget_1nr_common:
+      {
+        RegType src_type, index_type;
+
+        index_type = GetRegisterType(work_line, dec_insn.vC_);
+        CheckArrayIndexType(method, index_type, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        if (res_class != NULL) {
+          /* verify the class */
+          Class::PrimitiveType prim_type =
+              res_class->GetComponentType()->GetPrimitiveType();
+          if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+              prim_type == Class::kPrimNot) {
+            LOG(ERROR) << "VFY: invalid aget-1nr target "
+                       << res_class->GetDescriptor()->ToModifiedUtf8();
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          /* make sure array type matches instruction */
+          src_type = PrimitiveTypeToRegType(prim_type);
+
+          /* differentiate between float and int */
+          if (src_type == kRegTypeFloat || src_type == kRegTypeInteger)
+            tmp_type = src_type;
+
+          if (tmp_type != src_type) {
+            LOG(ERROR) << "VFY: invalid aget-1nr, array type=" << src_type
+                       << " with inst type=" << tmp_type << " (on "
+                       << res_class->GetDescriptor()->ToModifiedUtf8() << ")";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+        }
+        SetRegisterType(work_line, dec_insn.vA_, tmp_type);
+      }
+      break;
+
+    case Instruction::AGET_WIDE:
+      {
+        RegType dst_type, index_type;
+
+        index_type = GetRegisterType(work_line, dec_insn.vC_);
+        CheckArrayIndexType(method, index_type, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        if (res_class != NULL) {
+          /* verify the class */
+          Class::PrimitiveType prim_type =
+              res_class->GetComponentType()->GetPrimitiveType();
+          if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+              prim_type == Class::kPrimNot) {
+            LOG(ERROR) << "VFY: invalid aget-wide target "
+                       << res_class->GetDescriptor()->ToModifiedUtf8();
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          /* try to refine "dst_type" */
+          switch (prim_type) {
+            case Class::kPrimLong:
+              dst_type = kRegTypeLongLo;
+              break;
+            case Class::kPrimDouble:
+              dst_type = kRegTypeDoubleLo;
+              break;
+            default:
+              LOG(ERROR) << "VFY: invalid aget-wide on "
+                         << res_class->GetDescriptor()->ToModifiedUtf8();
+              dst_type = kRegTypeUnknown;
+              failure = VERIFY_ERROR_GENERIC;
+              break;
+          }
+        } else {
+          /*
+           * Null array ref; this code path will fail at runtime.  We
+           * know this is either long or double, so label it const.
+           */
+          dst_type = kRegTypeConstLo;
+        }
+        SetRegisterType(work_line, dec_insn.vA_, dst_type);
+      }
+      break;
+
+    case Instruction::AGET_OBJECT:
+      {
+        RegType dst_type, index_type;
+
+        index_type = GetRegisterType(work_line, dec_insn.vC_);
+        CheckArrayIndexType(method, index_type, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /* get the class of the array we're pulling an object from */
+        res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        if (res_class != NULL) {
+          Class* element_class;
+
+          assert(res_class != NULL);
+          if (!res_class->IsArrayClass()) {
+            LOG(ERROR) << "VFY: aget-object on non-array class";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+          assert(res_class->GetComponentType() != NULL);
+
+          /*
+           * Find the element class.  res_class->GetComponentType() indicates
+           * the basic type, which won't be what we want for a
+           * multi-dimensional array.
+           */
+          if (res_class->GetDescriptor()->CharAt(1) == '[') {
+            assert(res_class->GetArrayRank() > 1);
+            std::string descriptor =
+                res_class->GetDescriptor()->ToModifiedUtf8();
+            element_class = class_linker->FindClass(descriptor.c_str() + 1,
+                           res_class->GetClassLoader());
+          } else if (res_class->GetDescriptor()->CharAt(1) == 'L') {
+            assert(res_class->GetArrayRank() == 1);
+            element_class = res_class->GetComponentType();
+          } else {
+            LOG(ERROR) << "VFY: aget-object on non-ref array class ("
+                       << res_class->GetDescriptor()->ToModifiedUtf8() << ")";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          dst_type = RegTypeFromClass(element_class);
+        } else {
+          /*
+           * The array reference is NULL, so the current code path will
+           * throw an exception.  For proper merging with later code
+           * paths, and correct handling of "if-eqz" tests on the
+           * result of the array get, we want to treat this as a null
+           * reference.
+           */
+          dst_type = kRegTypeZero;
+        }
+      SetRegisterType(work_line, dec_insn.vA_, dst_type);
+      }
+      break;
+    case Instruction::APUT:
+      tmp_type = kRegTypeInteger;
+      goto aput_1nr_common;
+    case Instruction::APUT_BOOLEAN:
+      tmp_type = kRegTypeBoolean;
+      goto aput_1nr_common;
+    case Instruction::APUT_BYTE:
+      tmp_type = kRegTypeByte;
+      goto aput_1nr_common;
+    case Instruction::APUT_CHAR:
+      tmp_type = kRegTypeChar;
+      goto aput_1nr_common;
+    case Instruction::APUT_SHORT:
+      tmp_type = kRegTypeShort;
+      goto aput_1nr_common;
+aput_1nr_common:
+      {
+        RegType src_type, dst_type, index_type;
+
+        index_type = GetRegisterType(work_line, dec_insn.vC_);
+        CheckArrayIndexType(method, index_type, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /* res_class can be null if the reg type is Zero */
+        if (res_class == NULL)
+          break;
+
+        Class::PrimitiveType prim_type =
+            res_class->GetComponentType()->GetPrimitiveType();
+        if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+            prim_type == Class::kPrimNot) {
+          LOG(ERROR) << "VFY: invalid aput-1nr on "
+                     << res_class->GetDescriptor()->ToModifiedUtf8();
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        /* verify that instruction matches array */
+        dst_type = PrimitiveTypeToRegType(prim_type);
+
+        /* correct if float */
+        if (dst_type == kRegTypeFloat)
+          tmp_type = kRegTypeFloat;
+
+        /* make sure the source register has the correct type */
+        src_type = GetRegisterType(work_line, dec_insn.vA_);
+        if (!CanConvertTo1nr(src_type, tmp_type)) {
+          LOG(ERROR) << "VFY: invalid reg type " << src_type
+                     << " on aput instr (need " << tmp_type << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        VerifyRegisterType(work_line, dec_insn.vA_, dst_type, &failure);
+
+        if (failure != VERIFY_ERROR_NONE || dst_type == kRegTypeUnknown ||
+          tmp_type != dst_type) {
+          LOG(ERROR) << "VFY: invalid aput-1nr on "
+                     << res_class->GetDescriptor()->ToModifiedUtf8()
+                     << " (inst=" << tmp_type << " dst=" << dst_type << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+      }
+      break;
+    case Instruction::APUT_WIDE:
+      tmp_type = GetRegisterType(work_line, dec_insn.vC_);
+      CheckArrayIndexType(method, tmp_type, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+
+      res_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+      if (res_class != NULL) {
+        Class::PrimitiveType prim_type =
+            res_class->GetComponentType()->GetPrimitiveType();
+        /* verify the class and try to refine "dst_type" */
+        if (!res_class->IsArrayClass() || res_class->GetArrayRank() != 1 ||
+            prim_type == Class::kPrimNot)
+        {
+          LOG(ERROR) << "VFY: invalid aput-wide on "
+                     << res_class->GetDescriptor()->ToModifiedUtf8();
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        switch (prim_type) {
+          case Class::kPrimLong:
+            VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeLongLo,
+                &failure);
+            break;
+          case Class::kPrimDouble:
+            VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeDoubleLo,
+                &failure);
+            break;
+          default:
+            LOG(ERROR) << "VFY: invalid aput-wide on "
+                       << res_class->GetDescriptor()->ToModifiedUtf8();
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+      }
+      break;
+    case Instruction::APUT_OBJECT:
+      tmp_type = GetRegisterType(work_line, dec_insn.vC_);
+      CheckArrayIndexType(method, tmp_type, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+
+      /* get the ref we're storing; Zero is okay, Uninit is not */
+      res_class = GetClassFromRegister(work_line, dec_insn.vA_, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+      if (res_class != NULL) {
+        Class* array_class;
+        Class* element_class;
+
+        /*
+         * Get the array class.  If the array ref is null, we won't
+         * have type information (and we'll crash at runtime with a
+         * null pointer exception).
+         */
+        array_class = GetClassFromRegister(work_line, dec_insn.vB_, &failure);
+
+        if (array_class != NULL) {
+          /* see if the array holds a compatible type */
+          if (!array_class->IsArrayClass()) {
+            LOG(ERROR) << "VFY: invalid aput-object on "
+                       << array_class->GetDescriptor()->ToModifiedUtf8();
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          /*
+           * Find the element class.  res_class->GetComponentType() indicates
+           * the basic type, which won't be what we want for a
+           * multi-dimensional array.
+           *
+           * All we want to check here is that the element type is a
+           * reference class.  We *don't* check instanceof here, because
+           * you can still put a String into a String[] after the latter
+           * has been cast to an Object[].
+           */
+          if (array_class->GetDescriptor()->CharAt(1) == '[') {
+            assert(array_class->GetArrayRank() > 1);
+            std::string descriptor =
+                array_class->GetDescriptor()->ToModifiedUtf8();
+            element_class = class_linker->FindClass(descriptor.c_str() + 1,
+                           array_class->GetClassLoader());
+          } else {
+            assert(array_class->GetArrayRank() == 1);
+            element_class = array_class->GetComponentType();
+          }
+          if (element_class->GetPrimitiveType() != Class::kPrimNot) {
+            LOG(ERROR) << "VFY: invalid aput-object of "
+                       << res_class->GetDescriptor()->ToModifiedUtf8()
+                       << " into "
+                       << array_class->GetDescriptor()->ToModifiedUtf8();
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+        }
+      }
+      break;
+
+    case Instruction::IGET:
+      tmp_type = kRegTypeInteger;
+      goto iget_1nr_common;
+    case Instruction::IGET_BOOLEAN:
+      tmp_type = kRegTypeBoolean;
+      goto iget_1nr_common;
+    case Instruction::IGET_BYTE:
+      tmp_type = kRegTypeByte;
+      goto iget_1nr_common;
+    case Instruction::IGET_CHAR:
+      tmp_type = kRegTypeChar;
+      goto iget_1nr_common;
+    case Instruction::IGET_SHORT:
+      tmp_type = kRegTypeShort;
+      goto iget_1nr_common;
+iget_1nr_common:
+      {
+        Field* inst_field;
+        RegType obj_type, field_type;
+
+        obj_type = GetRegisterType(work_line, dec_insn.vB_);
+        inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /* make sure the field's type is compatible with expectation */
+        field_type =
+            PrimitiveTypeToRegType(inst_field->GetType()->GetPrimitiveType());
+
+        /* correct if float */
+        if (field_type == kRegTypeFloat)
+          tmp_type = kRegTypeFloat;
+
+        if (field_type == kRegTypeUnknown || tmp_type != field_type) {
+          Class* inst_field_class = inst_field->GetDeclaringClass();
+          LOG(ERROR) << "VFY: invalid iget-1nr of "
+                     << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+                     << "." << inst_field->GetName()->ToModifiedUtf8()
+                     << " (inst=" << tmp_type << " field=" << field_type << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        SetRegisterType(work_line, dec_insn.vA_, tmp_type);
+      }
+      break;
+    case Instruction::IGET_WIDE:
+      {
+        RegType dst_type;
+        Field* inst_field;
+        RegType obj_type;
+
+        obj_type = GetRegisterType(work_line, dec_insn.vB_);
+        inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+        Class* inst_field_class = inst_field->GetDeclaringClass();
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        /* check the type, which should be prim */
+        switch (inst_field->GetType()->GetPrimitiveType()) {
+          case Class::kPrimDouble:
+            dst_type = kRegTypeDoubleLo;
+            break;
+          case Class::kPrimLong:
+            dst_type = kRegTypeLongLo;
+            break;
+          default:
+            LOG(ERROR) << "VFY: invalid iget-wide of "
+                       << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+                       << "." << inst_field->GetName()->ToModifiedUtf8();
+            dst_type = kRegTypeUnknown;
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (failure == VERIFY_ERROR_NONE) {
+          SetRegisterType(work_line, dec_insn.vA_, dst_type);
+        }
+      }
+      break;
+    case Instruction::IGET_OBJECT:
+      {
+        Class* field_class;
+        Field* inst_field;
+        RegType obj_type;
+
+        obj_type = GetRegisterType(work_line, dec_insn.vB_);
+        inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        field_class = inst_field->GetType();
+        if (field_class == NULL) {
+          /* class not found or primitive type */
+          LOG(ERROR) << "VFY: unable to recover field class from "
+                     << inst_field->GetName()->ToModifiedUtf8();
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+        if (failure == VERIFY_ERROR_NONE) {
+          assert(!field_class->IsPrimitive());
+          SetRegisterType(work_line, dec_insn.vA_,
+              RegTypeFromClass(field_class));
+        }
+      }
+      break;
+    case Instruction::IPUT:
+      tmp_type = kRegTypeInteger;
+      goto iput_1nr_common;
+    case Instruction::IPUT_BOOLEAN:
+      tmp_type = kRegTypeBoolean;
+      goto iput_1nr_common;
+    case Instruction::IPUT_BYTE:
+      tmp_type = kRegTypeByte;
+      goto iput_1nr_common;
+    case Instruction::IPUT_CHAR:
+      tmp_type = kRegTypeChar;
+      goto iput_1nr_common;
+    case Instruction::IPUT_SHORT:
+      tmp_type = kRegTypeShort;
+      goto iput_1nr_common;
+iput_1nr_common:
+      {
+        RegType src_type, field_type, obj_type;
+        Field* inst_field;
+
+        obj_type = GetRegisterType(work_line, dec_insn.vB_);
+        inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        CheckFinalFieldAccess(method, inst_field, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /* get type of field we're storing into */
+        field_type =
+            PrimitiveTypeToRegType(inst_field->GetType()->GetPrimitiveType());
+        src_type = GetRegisterType(work_line, dec_insn.vA_);
+
+        /* correct if float */
+        if (field_type == kRegTypeFloat)
+          tmp_type = kRegTypeFloat;
+
+        /*
+         * compiler can generate synthetic functions that write byte values
+         * into boolean fields.
+         */
+        if (tmp_type == kRegTypeBoolean && src_type == kRegTypeByte)
+          tmp_type = kRegTypeByte;
+        if (field_type == kRegTypeBoolean && src_type == kRegTypeByte)
+          field_type = kRegTypeByte;
+
+        /* make sure the source register has the correct type */
+        if (!CanConvertTo1nr(src_type, tmp_type)) {
+          LOG(ERROR) << "VFY: invalid reg type " << src_type
+                     << " on iput instr (need " << tmp_type << ")",
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        VerifyRegisterType(work_line, dec_insn.vA_, field_type, &failure);
+
+        if (failure != VERIFY_ERROR_NONE || field_type == kRegTypeUnknown ||
+            tmp_type != field_type) {
+          Class* inst_field_class = inst_field->GetDeclaringClass();
+          LOG(ERROR) << "VFY: invalid iput-1nr of "
+                     << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+                     << "." << inst_field->GetName()->ToModifiedUtf8()
+                     << " (inst=" << tmp_type << " field=" << field_type << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+      }
+      break;
+    case Instruction::IPUT_WIDE:
+      Field* inst_field;
+      RegType obj_type;
+
+      obj_type = GetRegisterType(work_line, dec_insn.vB_);
+      inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+      CheckFinalFieldAccess(method, inst_field, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+
+      /* check the type, which should be prim */
+      switch (inst_field->GetType()->GetPrimitiveType()) {
+        case Class::kPrimDouble:
+          VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeDoubleLo,
+              &failure);
+          break;
+        case Class::kPrimLong:
+          VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeLongLo, &failure);
+          break;
+        default:
+          LOG(ERROR) << "VFY: invalid iput-wide of "
+                     << inst_field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+                     << "." << inst_field->GetName()->ToModifiedUtf8();
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+      break;
+    case Instruction::IPUT_OBJECT:
+      {
+        Class* field_class;
+        Class* value_class;
+        Field* inst_field;
+        RegType obj_type, value_type;
+
+        obj_type = GetRegisterType(work_line, dec_insn.vB_);
+        inst_field = GetInstField(vdata, obj_type, dec_insn.vC_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        CheckFinalFieldAccess(method, inst_field, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        field_class = inst_field->GetType();
+        if (field_class == NULL) {
+          LOG(ERROR) << "VFY: unable to recover field class from '"
+                     << inst_field->GetName()->ToModifiedUtf8() << "'";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        value_type = GetRegisterType(work_line, dec_insn.vA_);
+        if (!RegTypeIsReference(value_type)) {
+          LOG(ERROR) << "VFY: storing non-ref v" << dec_insn.vA_
+                     << " into ref field '"
+                     << inst_field->GetName()->ToModifiedUtf8() << "' ("
+                     << field_class->GetDescriptor()->ToModifiedUtf8() << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+        if (value_type != kRegTypeZero) {
+          value_class = RegTypeInitializedReferenceToClass(value_type);
+          if (value_class == NULL) {
+            LOG(ERROR) << "VFY: storing uninit ref v" << dec_insn.vA_
+                       << " into ref field";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+          /* allow if field is any interface or field is base class */
+          if (!field_class->IsInterface() &&
+              !field_class->IsAssignableFrom(value_class)) {
+            Class* inst_field_class = inst_field->GetDeclaringClass();
+            LOG(ERROR) << "VFY: storing type '"
+                       << value_class->GetDescriptor()->ToModifiedUtf8()
+                       << "' into field type '"
+                       << field_class->GetDescriptor()->ToModifiedUtf8()
+                       << "' ("
+                       << inst_field_class->GetDescriptor()->ToModifiedUtf8()
+                       << "." << inst_field->GetName()->ToModifiedUtf8() << ")";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+        }
+      }
+      break;
+
+    case Instruction::SGET:
+      tmp_type = kRegTypeInteger;
+      goto sget_1nr_common;
+    case Instruction::SGET_BOOLEAN:
+      tmp_type = kRegTypeBoolean;
+      goto sget_1nr_common;
+    case Instruction::SGET_BYTE:
+      tmp_type = kRegTypeByte;
+      goto sget_1nr_common;
+    case Instruction::SGET_CHAR:
+      tmp_type = kRegTypeChar;
+      goto sget_1nr_common;
+    case Instruction::SGET_SHORT:
+      tmp_type = kRegTypeShort;
+      goto sget_1nr_common;
+sget_1nr_common:
+      {
+        Field* static_field;
+        RegType field_type;
+
+        static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /*
+         * Make sure the field's type is compatible with expectation.
+         * We can get ourselves into trouble if we mix & match loads
+         * and stores with different widths, so rather than just checking
+         * "CanConvertTo1nr" we require that the field types have equal
+         * widths.
+         */
+        field_type =
+            PrimitiveTypeToRegType(static_field->GetType()->GetPrimitiveType());
+
+        /* correct if float */
+        if (field_type == kRegTypeFloat)
+          tmp_type = kRegTypeFloat;
+
+        if (tmp_type != field_type) {
+          Class* static_field_class = static_field->GetDeclaringClass();
+          LOG(ERROR) << "VFY: invalid sget-1nr of "
+                     << static_field_class->GetDescriptor()->ToModifiedUtf8()
+                     << "." << static_field->GetName()->ToModifiedUtf8()
+                     << " (inst=" << tmp_type << " actual=" << field_type
+                     << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        SetRegisterType(work_line, dec_insn.vA_, tmp_type);
+      }
+      break;
+    case Instruction::SGET_WIDE:
+      {
+        Field* static_field;
+        RegType dst_type;
+
+        static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+        Class* static_field_class = static_field->GetDeclaringClass();
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        /* check the type, which should be prim */
+        switch (static_field->GetType()->GetPrimitiveType()) {
+          case Class::kPrimDouble:
+            dst_type = kRegTypeDoubleLo;
+            break;
+          case Class::kPrimLong:
+            dst_type = kRegTypeLongLo;
+            break;
+          default:
+            LOG(ERROR) << "VFY: invalid sget-wide of "
+                       << static_field_class->GetDescriptor()->ToModifiedUtf8()
+                       << "." << static_field->GetName()->ToModifiedUtf8();
+            dst_type = kRegTypeUnknown;
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+        }
+        if (failure == VERIFY_ERROR_NONE) {
+          SetRegisterType(work_line, dec_insn.vA_, dst_type);
+        }
+      }
+      break;
+    case Instruction::SGET_OBJECT:
+      {
+        Field* static_field;
+        Class* field_class;
+
+        static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        field_class = static_field->GetType();
+        if (field_class == NULL) {
+          LOG(ERROR) << "VFY: unable to recover field class from '"
+                     << static_field->GetName()->ToModifiedUtf8() << "'";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+        if (field_class->IsPrimitive()) {
+          LOG(ERROR) << "VFY: attempt to get prim field with sget-object";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+      SetRegisterType(work_line, dec_insn.vA_, RegTypeFromClass(field_class));
+      }
+      break;
+    case Instruction::SPUT:
+      tmp_type = kRegTypeInteger;
+      goto sput_1nr_common;
+    case Instruction::SPUT_BOOLEAN:
+      tmp_type = kRegTypeBoolean;
+      goto sput_1nr_common;
+    case Instruction::SPUT_BYTE:
+      tmp_type = kRegTypeByte;
+      goto sput_1nr_common;
+    case Instruction::SPUT_CHAR:
+      tmp_type = kRegTypeChar;
+      goto sput_1nr_common;
+    case Instruction::SPUT_SHORT:
+      tmp_type = kRegTypeShort;
+      goto sput_1nr_common;
+sput_1nr_common:
+      {
+        RegType src_type, field_type;
+        Field* static_field;
+
+        static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        CheckFinalFieldAccess(method, static_field, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /*
+         * Get type of field we're storing into.  We know that the
+         * contents of the register match the instruction, but we also
+         * need to ensure that the instruction matches the field type.
+         * Using e.g. sput-short to write into a 32-bit integer field
+         * can lead to trouble if we do 16-bit writes.
+         */
+        field_type =
+            PrimitiveTypeToRegType(static_field->GetType()->GetPrimitiveType());
+        src_type = GetRegisterType(work_line, dec_insn.vA_);
+
+        /* correct if float */
+        if (field_type == kRegTypeFloat)
+          tmp_type = kRegTypeFloat;
+
+        /*
+         * compiler can generate synthetic functions that write byte values
+         * into boolean fields.
+         */
+        if (tmp_type == kRegTypeBoolean && src_type == kRegTypeByte)
+          tmp_type = kRegTypeByte;
+        if (field_type == kRegTypeBoolean && src_type == kRegTypeByte)
+          field_type = kRegTypeByte;
+
+        /* make sure the source register has the correct type */
+        if (!CanConvertTo1nr(src_type, tmp_type)) {
+          LOG(ERROR) << "VFY: invalid reg type " << src_type
+                     << " on sput instr (need " << tmp_type << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        VerifyRegisterType(work_line, dec_insn.vA_, field_type, &failure);
+
+        if (failure != VERIFY_ERROR_NONE || field_type == kRegTypeUnknown ||
+            tmp_type != field_type) {
+          Class* static_field_class = static_field->GetDeclaringClass();
+          LOG(ERROR) << "VFY: invalid sput-1nr of "
+                     << static_field_class->GetDescriptor()->ToModifiedUtf8()
+                     << "." << static_field->GetName()->ToModifiedUtf8()
+                     << " (inst=" << tmp_type << " actual=" << field_type
+                     << ")";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+      }
+      break;
+    case Instruction::SPUT_WIDE:
+      Field* static_field;
+
+      static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+      CheckFinalFieldAccess(method, static_field, &failure);
+      if (failure != VERIFY_ERROR_NONE)
+        break;
+
+      /* check the type, which should be prim */
+      switch (static_field->GetType()->GetPrimitiveType()) {
+        case Class::kPrimDouble:
+          VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeDoubleLo,
+              &failure);
+          break;
+        case Class::kPrimLong:
+          VerifyRegisterType(work_line, dec_insn.vA_, kRegTypeLongLo, &failure);
+          break;
+        default:
+          LOG(ERROR) << "VFY: invalid sput-wide of "
+                     << static_field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+                     << "." << static_field->GetName()->ToModifiedUtf8();
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+      }
+      break;
+    case Instruction::SPUT_OBJECT:
+      {
+        Class* field_class;
+        Class* value_class;
+        Field* static_field;
+        RegType value_type;
+
+        static_field = GetStaticField(vdata, dec_insn.vB_, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+        CheckFinalFieldAccess(method, static_field, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        field_class = static_field->GetType();
+        if (field_class == NULL) {
+          LOG(ERROR) << "VFY: unable to recover field class from '"
+                     << static_field->GetName()->ToModifiedUtf8() << "'";
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+
+        value_type = GetRegisterType(work_line, dec_insn.vA_);
+        if (!RegTypeIsReference(value_type)) {
+          LOG(ERROR) << "VFY: storing non-ref v" << dec_insn.vA_
+                     << " into ref field '"
+                     << static_field->GetName()->ToModifiedUtf8() << "' ("
+                     << field_class->GetDescriptor()->ToModifiedUtf8() << ")",
+          failure = VERIFY_ERROR_GENERIC;
+          break;
+        }
+        if (value_type != kRegTypeZero) {
+          value_class = RegTypeInitializedReferenceToClass(value_type);
+          if (value_class == NULL) {
+            LOG(ERROR) << "VFY: storing uninit ref v" << dec_insn.vA_
+                       << " into ref field";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+          /* allow if field is any interface or field is base class */
+          if (!field_class->IsInterface() &&
+              !field_class->IsAssignableFrom(value_class)) {
+            Class* static_field_class = static_field->GetDeclaringClass();
+            LOG(ERROR) << "VFY: storing type '"
+                       << value_class->GetDescriptor()->ToModifiedUtf8()
+                       << "' into field type '"
+                       << field_class->GetDescriptor()->ToModifiedUtf8()
+                       << "' ("
+                       << static_field_class->GetDescriptor()->ToModifiedUtf8()
+                       << "." << static_field->GetName()->ToModifiedUtf8()
+                       << ")",
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+        }
+      }
+      break;
+
+    case Instruction::INVOKE_VIRTUAL:
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+    case Instruction::INVOKE_SUPER:
+    case Instruction::INVOKE_SUPER_RANGE:
+      {
+        Method* called_method;
+        RegType return_type;
+        bool is_range;
+        bool is_super;
+
+        is_range =  (dec_insn.opcode_ == Instruction::INVOKE_VIRTUAL_RANGE ||
+                     dec_insn.opcode_ == Instruction::INVOKE_SUPER_RANGE);
+        is_super =  (dec_insn.opcode_ == Instruction::INVOKE_SUPER ||
+                     dec_insn.opcode_ == Instruction::INVOKE_SUPER_RANGE);
+
+        called_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+            &dec_insn, METHOD_VIRTUAL, is_range, is_super, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+            break;
+        return_type = GetMethodReturnType(dex_file, called_method);
+        SetResultRegisterType(work_line, registers_size, return_type);
+        just_set_result = true;
+      }
+      break;
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_DIRECT_RANGE:
+      {
+        RegType return_type;
+        Method* called_method;
+        bool is_range;
+
+        is_range = (dec_insn.opcode_ == Instruction::INVOKE_DIRECT_RANGE);
+        called_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+            &dec_insn, METHOD_DIRECT, is_range, false, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        /*
+         * Some additional checks when calling <init>.  We know from
+         * the invocation arg check that the "this" argument is an
+         * instance of called_method->klass.  Now we further restrict
+         * that to require that called_method->klass is the same as
+         * this->klass or this->super, allowing the latter only if
+         * the "this" argument is the same as the "this" argument to
+         * this method (which implies that we're in <init> ourselves).
+         */
+        if (IsInitMethod(called_method)) {
+          RegType this_type;
+          this_type = GetInvocationThis(work_line, &dec_insn, &failure);
+          if (failure != VERIFY_ERROR_NONE)
+            break;
+
+          /* no null refs allowed (?) */
+          if (this_type == kRegTypeZero) {
+            LOG(ERROR) << "VFY: unable to initialize null ref";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          Class* this_class;
+
+          this_class = RegTypeReferenceToClass(this_type, uninit_map);
+          assert(this_class != NULL);
+
+          /* must be in same class or in superclass */
+          if (called_method->GetDeclaringClass() == this_class->GetSuperClass())
+          {
+            if (this_class != method->GetDeclaringClass()) {
+              LOG(ERROR) << "VFY: invoke-direct <init> on super only "
+                         << "allowed for 'this' in <init>";
+              failure = VERIFY_ERROR_GENERIC;
+              break;
+            }
+          }  else if (called_method->GetDeclaringClass() != this_class) {
+            LOG(ERROR) << "VFY: invoke-direct <init> must be on current "
+                       << "class or super";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          /* arg must be an uninitialized reference */
+          if (!RegTypeIsUninitReference(this_type)) {
+            LOG(ERROR) << "VFY: can only initialize the uninitialized";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          /*
+           * Replace the uninitialized reference with an initialized
+           * one, and clear the entry in the uninit map.  We need to
+           * do this for all registers that have the same object
+           * instance in them, not just the "this" register.
+           */
+          MarkRefsAsInitialized(work_line, registers_size, uninit_map,
+              this_type, &failure);
+          if (failure != VERIFY_ERROR_NONE)
+            break;
+          }
+        return_type = GetMethodReturnType(dex_file, called_method);
+        SetResultRegisterType(work_line, registers_size, return_type);
+        just_set_result = true;
+      }
+      break;
+    case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_STATIC_RANGE:
+      {
+        RegType return_type;
+        Method* called_method;
+        bool is_range;
+
+        is_range = (dec_insn.opcode_ == Instruction::INVOKE_STATIC_RANGE);
+        called_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+            &dec_insn, METHOD_STATIC, is_range, false, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        return_type = GetMethodReturnType(dex_file, called_method);
+        SetResultRegisterType(work_line, registers_size, return_type);
+        just_set_result = true;
+      }
+      break;
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_INTERFACE_RANGE:
+      {
+        RegType /*this_type,*/ return_type;
+        Method* abs_method;
+        bool is_range;
+
+        is_range =  (dec_insn.opcode_ == Instruction::INVOKE_INTERFACE_RANGE);
+        abs_method = VerifyInvocationArgs(vdata, work_line, registers_size,
+            &dec_insn, METHOD_INTERFACE, is_range, false, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+#if 0   /* can't do this here, fails on dalvik test 052-verifier-fun */
+        /*
+         * Get the type of the "this" arg, which should always be an
+         * interface class.  Because we don't do a full merge on
+         * interface classes, this might have reduced to Object.
+         */
+        this_type = GetInvocationThis(work_line, &dec_insn, &failure);
+        if (failure != VERIFY_ERROR_NONE)
+          break;
+
+        if (this_type == kRegTypeZero) {
+          /* null pointer always passes (and always fails at runtime) */
+        } else {
+          Class* this_class;
+
+          this_class = RegTypeInitializedReferenceToClass(this_type);
+          if (this_class == NULL) {
+            LOG(ERROR) << "VFY: interface call on uninitialized";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+
+          /*
+           * Either "this_class" needs to be the interface class that
+           * defined abs_method, or abs_method's class needs to be one
+           * of the interfaces implemented by "this_class".  (Or, if
+           * we couldn't complete the merge, this will be Object.)
+           */
+          if (this_class != abs_method->GetDeclaringClass() &&
+              this_class != class_linker->FindSystemClass("Ljava/lang/Object;") &&
+              !this_class->Implements(abs_method->GetDeclaringClass())) {
+            LOG(ERROR) << "VFY: unable to match abs_method '"
+                       << abs_method->GetName()->ToModifiedUtf8() << "' with "
+                       << this_class->GetDescriptor()->ToModifiedUtf8()
+                       << " interfaces";
+            failure = VERIFY_ERROR_GENERIC;
+            break;
+          }
+        }
+#endif
+
+        /*
+         * We don't have an object instance, so we can't find the
+         * concrete method.  However, all of the type information is
+         * in the abstract method, so we're good.
+         */
+        return_type = GetMethodReturnType(dex_file, abs_method);
+        SetResultRegisterType(work_line, registers_size, return_type);
+        just_set_result = true;
+      }
+      break;
+
+    case Instruction::NEG_INT:
+    case Instruction::NOT_INT:
+      CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+          &failure);
+      break;
+    case Instruction::NEG_LONG:
+    case Instruction::NOT_LONG:
+      CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo, &failure);
+      break;
+    case Instruction::NEG_FLOAT:
+      CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeFloat, &failure);
+      break;
+    case Instruction::NEG_DOUBLE:
+      CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeDoubleLo,
+          &failure);
+      break;
+    case Instruction::INT_TO_LONG:
+      CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeInteger,
+          &failure);
+      break;
+    case Instruction::INT_TO_FLOAT:
+      CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeInteger, &failure);
+      break;
+    case Instruction::INT_TO_DOUBLE:
+      CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeInteger,
+          &failure);
+      break;
+    case Instruction::LONG_TO_INT:
+      CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeLongLo,
+          &failure);
+      break;
+    case Instruction::LONG_TO_FLOAT:
+      CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeLongLo, &failure);
+      break;
+    case Instruction::LONG_TO_DOUBLE:
+      CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeLongLo,
+          &failure);
+      break;
+    case Instruction::FLOAT_TO_INT:
+      CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeFloat, &failure);
+      break;
+    case Instruction::FLOAT_TO_LONG:
+      CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeFloat, &failure);
+      break;
+    case Instruction::FLOAT_TO_DOUBLE:
+      CheckUnop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeFloat,
+          &failure);
+      break;
+    case Instruction::DOUBLE_TO_INT:
+      CheckUnop(work_line, &dec_insn, kRegTypeInteger, kRegTypeDoubleLo,
+          &failure);
+      break;
+    case Instruction::DOUBLE_TO_LONG:
+      CheckUnop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeDoubleLo,
+          &failure);
+      break;
+    case Instruction::DOUBLE_TO_FLOAT:
+      CheckUnop(work_line, &dec_insn, kRegTypeFloat, kRegTypeDoubleLo,
+          &failure);
+      break;
+    case Instruction::INT_TO_BYTE:
+      CheckUnop(work_line, &dec_insn, kRegTypeByte, kRegTypeInteger, &failure);
+      break;
+    case Instruction::INT_TO_CHAR:
+      CheckUnop(work_line, &dec_insn, kRegTypeChar, kRegTypeInteger, &failure);
+      break;
+    case Instruction::INT_TO_SHORT:
+      CheckUnop(work_line, &dec_insn, kRegTypeShort, kRegTypeInteger, &failure);
+      break;
+
+    case Instruction::ADD_INT:
+    case Instruction::SUB_INT:
+    case Instruction::MUL_INT:
+    case Instruction::REM_INT:
+    case Instruction::DIV_INT:
+    case Instruction::SHL_INT:
+    case Instruction::SHR_INT:
+    case Instruction::USHR_INT:
+      CheckBinop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+          kRegTypeInteger, false, &failure);
+      break;
+    case Instruction::AND_INT:
+    case Instruction::OR_INT:
+    case Instruction::XOR_INT:
+      CheckBinop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+          kRegTypeInteger, true, &failure);
+      break;
+    case Instruction::ADD_LONG:
+    case Instruction::SUB_LONG:
+    case Instruction::MUL_LONG:
+    case Instruction::DIV_LONG:
+    case Instruction::REM_LONG:
+    case Instruction::AND_LONG:
+    case Instruction::OR_LONG:
+    case Instruction::XOR_LONG:
+      CheckBinop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+          kRegTypeLongLo, false, &failure);
+      break;
+    case Instruction::SHL_LONG:
+    case Instruction::SHR_LONG:
+    case Instruction::USHR_LONG:
+      /* shift distance is Int, making these different from other binops */
+      CheckBinop(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+          kRegTypeInteger, false, &failure);
+      break;
+    case Instruction::ADD_FLOAT:
+    case Instruction::SUB_FLOAT:
+    case Instruction::MUL_FLOAT:
+    case Instruction::DIV_FLOAT:
+    case Instruction::REM_FLOAT:
+      CheckBinop(work_line, &dec_insn, kRegTypeFloat, kRegTypeFloat,
+          kRegTypeFloat, false, &failure);
+      break;
+    case Instruction::ADD_DOUBLE:
+    case Instruction::SUB_DOUBLE:
+    case Instruction::MUL_DOUBLE:
+    case Instruction::DIV_DOUBLE:
+    case Instruction::REM_DOUBLE:
+      CheckBinop(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeDoubleLo,
+          kRegTypeDoubleLo, false, &failure);
+      break;
+    case Instruction::ADD_INT_2ADDR:
+    case Instruction::SUB_INT_2ADDR:
+    case Instruction::MUL_INT_2ADDR:
+    case Instruction::REM_INT_2ADDR:
+    case Instruction::SHL_INT_2ADDR:
+    case Instruction::SHR_INT_2ADDR:
+    case Instruction::USHR_INT_2ADDR:
+      CheckBinop2addr(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+          kRegTypeInteger, false, &failure);
+      break;
+    case Instruction::AND_INT_2ADDR:
+    case Instruction::OR_INT_2ADDR:
+    case Instruction::XOR_INT_2ADDR:
+      CheckBinop2addr(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+          kRegTypeInteger, true, &failure);
+      break;
+    case Instruction::DIV_INT_2ADDR:
+      CheckBinop2addr(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger,
+          kRegTypeInteger, false, &failure);
+      break;
+    case Instruction::ADD_LONG_2ADDR:
+    case Instruction::SUB_LONG_2ADDR:
+    case Instruction::MUL_LONG_2ADDR:
+    case Instruction::DIV_LONG_2ADDR:
+    case Instruction::REM_LONG_2ADDR:
+    case Instruction::AND_LONG_2ADDR:
+    case Instruction::OR_LONG_2ADDR:
+    case Instruction::XOR_LONG_2ADDR:
+      CheckBinop2addr(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+          kRegTypeLongLo, false, &failure);
+      break;
+    case Instruction::SHL_LONG_2ADDR:
+    case Instruction::SHR_LONG_2ADDR:
+    case Instruction::USHR_LONG_2ADDR:
+      CheckBinop2addr(work_line, &dec_insn, kRegTypeLongLo, kRegTypeLongLo,
+          kRegTypeInteger, false, &failure);
+      break;
+    case Instruction::ADD_FLOAT_2ADDR:
+    case Instruction::SUB_FLOAT_2ADDR:
+    case Instruction::MUL_FLOAT_2ADDR:
+    case Instruction::DIV_FLOAT_2ADDR:
+    case Instruction::REM_FLOAT_2ADDR:
+      CheckBinop2addr(work_line, &dec_insn, kRegTypeFloat, kRegTypeFloat,
+          kRegTypeFloat, false, &failure);
+      break;
+    case Instruction::ADD_DOUBLE_2ADDR:
+    case Instruction::SUB_DOUBLE_2ADDR:
+    case Instruction::MUL_DOUBLE_2ADDR:
+    case Instruction::DIV_DOUBLE_2ADDR:
+    case Instruction::REM_DOUBLE_2ADDR:
+      CheckBinop2addr(work_line, &dec_insn, kRegTypeDoubleLo, kRegTypeDoubleLo,
+          kRegTypeDoubleLo, false, &failure);
+      break;
+    case Instruction::ADD_INT_LIT16:
+    case Instruction::RSUB_INT:
+    case Instruction::MUL_INT_LIT16:
+    case Instruction::DIV_INT_LIT16:
+    case Instruction::REM_INT_LIT16:
+      CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, false,
+          &failure);
+      break;
+    case Instruction::AND_INT_LIT16:
+    case Instruction::OR_INT_LIT16:
+    case Instruction::XOR_INT_LIT16:
+      CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, true,
+          &failure);
+      break;
+    case Instruction::ADD_INT_LIT8:
+    case Instruction::RSUB_INT_LIT8:
+    case Instruction::MUL_INT_LIT8:
+    case Instruction::DIV_INT_LIT8:
+    case Instruction::REM_INT_LIT8:
+    case Instruction::SHL_INT_LIT8:
+      CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, false,
+          &failure);
+      break;
+    case Instruction::SHR_INT_LIT8:
+      tmp_type = AdjustForRightShift(work_line, dec_insn.vB_, dec_insn.vC_,
+          false, &failure);
+      CheckLitop(work_line, &dec_insn, tmp_type, kRegTypeInteger, false,
+          &failure);
+      break;
+    case Instruction::USHR_INT_LIT8:
+      tmp_type = AdjustForRightShift(work_line, dec_insn.vB_, dec_insn.vC_,
+          true, &failure);
+      CheckLitop(work_line, &dec_insn, tmp_type, kRegTypeInteger, false,
+          &failure);
+      break;
+    case Instruction::AND_INT_LIT8:
+    case Instruction::OR_INT_LIT8:
+    case Instruction::XOR_INT_LIT8:
+      CheckLitop(work_line, &dec_insn, kRegTypeInteger, kRegTypeInteger, true,
+          &failure);
+      break;
+
+    /*
+     * This falls into the general category of "optimized" instructions,
+     * which don't generally appear during verification.  Because it's
+     * inserted in the course of verification, we can expect to see it here.
+     */
+    //case Instruction::THROW_VERIFICATION_ERROR:
+    case Instruction::UNUSED_ED:
+      break;
+
+    /*
+     * Verifying "quickened" instructions is tricky, because we have
+     * discarded the original field/method information.  The byte offsets
+     * and vtable indices only have meaning in the context of an object
+     * instance.
+     *
+     * If a piece of code declares a local reference variable, assigns
+     * null to it, and then issues a virtual method call on it, we
+     * cannot evaluate the method call during verification.  This situation
+     * isn't hard to handle, since we know the call will always result in an
+     * NPE, and the arguments and return value don't matter.  Any code that
+     * depends on the result of the method call is inaccessible, so the
+     * fact that we can't fully verify anything that comes after the bad
+     * call is not a problem.
+     *
+     * We must also consider the case of multiple code paths, only some of
+     * which involve a null reference.  We can completely verify the method
+     * if we sidestep the results of executing with a null reference.
+     * For example, if on the first pass through the code we try to do a
+     * virtual method invocation through a null ref, we have to skip the
+     * method checks and have the method return a "wildcard" type (which
+     * merges with anything to become that other thing).  The move-result
+     * will tell us if it's a reference, single-word numeric, or double-word
+     * value.  We continue to perform the verification, and at the end of
+     * the function any invocations that were never fully exercised are
+     * marked as null-only.
+     *
+     * We would do something similar for the field accesses.  The field's
+     * type, once known, can be used to recover the width of short integers.
+     * If the object reference was null, the field-get returns the "wildcard"
+     * type, which is acceptable for any operation.
+     */
+    case Instruction::UNUSED_EE:
+    case Instruction::UNUSED_EF:
+    case Instruction::UNUSED_F2:
+    case Instruction::UNUSED_F3:
+    case Instruction::UNUSED_F4:
+    case Instruction::UNUSED_F5:
+    case Instruction::UNUSED_F6:
+    case Instruction::UNUSED_F7:
+    case Instruction::UNUSED_F8:
+    case Instruction::UNUSED_F9:
+    case Instruction::UNUSED_FA:
+    case Instruction::UNUSED_FB:
+    //case Instruction::EXECUTE_INLINE:
+    //case Instruction::EXECUTE_INLINE_RANGE:
+    //case Instruction::IGET_QUICK:
+    //case Instruction::IGET_WIDE_QUICK:
+    //case Instruction::IGET_OBJECT_QUICK:
+    //case Instruction::IPUT_QUICK:
+    //case Instruction::IPUT_WIDE_QUICK:
+    //case Instruction::IPUT_OBJECT_QUICK:
+    //case Instruction::INVOKE_VIRTUAL_QUICK:
+    //case Instruction::INVOKE_VIRTUAL_QUICK_RANGE:
+    //case Instruction::INVOKE_SUPER_QUICK:
+    //case Instruction::INVOKE_SUPER_QUICK_RANGE:
+      /* fall through to failure */
+
+    /*
+     * These instructions are equivalent (from the verifier's point of view)
+     * to the original form.  The change was made for correctness rather
+     * than improved performance (except for invoke-object-init, which
+     * provides both).  The substitution takes place after verification
+     * completes, though, so we don't expect to see them here.
+     */
+    case Instruction::UNUSED_F0:
+    case Instruction::UNUSED_F1:
+    case Instruction::UNUSED_E3:
+    case Instruction::UNUSED_E8:
+    case Instruction::UNUSED_E7:
+    case Instruction::UNUSED_E4:
+    case Instruction::UNUSED_E9:
+    case Instruction::UNUSED_FC:
+    case Instruction::UNUSED_E5:
+    case Instruction::UNUSED_EA:
+    case Instruction::UNUSED_FD:
+    case Instruction::UNUSED_E6:
+    case Instruction::UNUSED_EB:
+    case Instruction::UNUSED_FE:
+    //case Instruction::INVOKE_OBJECT_INIT_RANGE:
+    //case Instruction::RETURN_VOID_BARRIER:
+    //case Instruction::IGET_VOLATILE:
+    //case Instruction::IGET_WIDE_VOLATILE:
+    //case Instruction::IGET_OBJECT_VOLATILE:
+    //case Instruction::IPUT_VOLATILE:
+    //case Instruction::IPUT_WIDE_VOLATILE:
+    //case Instruction::IPUT_OBJECT_VOLATILE:
+    //case Instruction::SGET_VOLATILE:
+    //case Instruction::SGET_WIDE_VOLATILE:
+    //case Instruction::SGET_OBJECT_VOLATILE:
+    //case Instruction::SPUT_VOLATILE:
+    //case Instruction::SPUT_WIDE_VOLATILE:
+    //case Instruction::SPUT_OBJECT_VOLATILE:
+      /* fall through to failure */
+
+    /* These should never appear during verification. */
+    case Instruction::UNUSED_3E:
+    case Instruction::UNUSED_3F:
+    case Instruction::UNUSED_40:
+    case Instruction::UNUSED_41:
+    case Instruction::UNUSED_42:
+    case Instruction::UNUSED_43:
+    case Instruction::UNUSED_73:
+    case Instruction::UNUSED_79:
+    case Instruction::UNUSED_7A:
+    case Instruction::UNUSED_EC:
+    case Instruction::UNUSED_FF:
+    //case Instruction::BREAKPOINT:
+    //case Instruction::DISPATCH_FF:
+      failure = VERIFY_ERROR_GENERIC;
+      break;
+
+    /*
+     * DO NOT add a "default" clause here.  Without it the compiler will
+     * complain if an instruction is missing (which is desirable).
+     */
+    }
+
+  if (failure != VERIFY_ERROR_NONE) {
+    //if (failure == VERIFY_ERROR_GENERIC || gDvm.optimizing)
+    if (failure == VERIFY_ERROR_GENERIC) {
+      /* immediate failure, reject class */
+      LOG(ERROR) << "VFY:  rejecting opcode 0x" << std::hex
+                 << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
+      return false;
+    } else {
+      /* replace opcode and continue on */
+      LOG(ERROR) << "VFY: replacing opcode 0x" << std::hex
+                 << (int) dec_insn.opcode_ << " at 0x" << insn_idx << std::dec;
+      if (!ReplaceFailingInstruction(code_item, insn_flags, insn_idx, failure))
+      {
+        LOG(ERROR) << "VFY:  rejecting opcode 0x" << std::hex
+                   << (int) dec_insn.opcode_ << " at 0x" << insn_idx
+                   << std::dec;
+        return false;
+      }
+      /* IMPORTANT: method->insns may have been changed */
+      insns = code_item->insns_ + insn_idx;
+
+      /* continue on as if we just handled a throw-verification-error */
+      failure = VERIFY_ERROR_NONE;
+      opcode_flag = Instruction::kThrow;
+    }
+  }
+
+  /*
+   * If we didn't just set the result register, clear it out.  This
+   * ensures that you can only use "move-result" immediately after the
+   * result is set.  (We could check this statically, but it's not
+   * expensive and it makes our debugging output cleaner.)
+   */
+  if (!just_set_result) {
+    int reg = RESULT_REGISTER(registers_size);
+    SetRegisterType(work_line, reg, kRegTypeUnknown);
+    SetRegisterType(work_line, reg + 1, kRegTypeUnknown);
+  }
+
+  /*
+   * Handle "continue".  Tag the next consecutive instruction.
+   */
+  if ((opcode_flag & Instruction::kContinue) != 0) {
+    size_t insn_width = InsnGetWidth(insn_flags, insn_idx);
+    if (insn_idx + insn_width >= insns_size) {
+      LOG(ERROR) << "VFY: execution can walk off end of code area (from 0x"
+                 << std::hex << insn_idx << std::dec << ")";
+      return false;
+    }
+
+    /*
+     * The only way to get to a move-exception instruction is to get
+     * thrown there.  Make sure the next instruction isn't one.
+     */
+    if (!CheckMoveException(code_item->insns_, insn_idx + insn_width))
+      return false;
+
+    if (GetRegisterLine(reg_table, insn_idx + insn_width)->reg_types_ != NULL) {
+      /*
+       * Merge registers into what we have for the next instruction,
+       * and set the "changed" flag if needed.
+       */
+      if (!UpdateRegisters(insn_flags, reg_table, insn_idx + insn_width,
+          work_line))
+        return false;
+    } else {
+      /*
+       * We're not recording register data for the next instruction,
+       * so we don't know what the prior state was.  We have to
+       * assume that something has changed and re-evaluate it.
+       */
+      InsnSetChanged(insn_flags, insn_idx + insn_width, true);
+    }
+  }
+
+  /*
+   * Handle "branch".  Tag the branch target.
+   *
+   * NOTE: instructions like Instruction::EQZ provide information about the
+   * state of the register when the branch is taken or not taken.  For example,
+   * somebody could get a reference field, check it for zero, and if the
+   * branch is taken immediately store that register in a boolean field
+   * since the value is known to be zero.  We do not currently account for
+   * that, and will reject the code.
+   *
+   * TODO: avoid re-fetching the branch target
+   */
+  if ((opcode_flag & Instruction::kBranch) != 0) {
+    bool isConditional, selfOkay;
+
+    if (!GetBranchOffset(code_item, insn_flags, insn_idx, &branch_target,
+        &isConditional, &selfOkay)) {
+      /* should never happen after static verification */
+      LOG(ERROR) << "VFY: bad branch at 0x" << std::hex << insn_idx << std::dec;
+      return false;
+    }
+    assert(isConditional || (opcode_flag & Instruction::kContinue) == 0);
+    assert(!isConditional || (opcode_flag & Instruction::kContinue) != 0);
+
+    if (!CheckMoveException(code_item->insns_, insn_idx + branch_target))
+      return false;
+
+    /* update branch target, set "changed" if appropriate */
+    if (!UpdateRegisters(insn_flags, reg_table, insn_idx + branch_target,
+        work_line))
+      return false;
+  }
+
+  /*
+   * Handle "switch".  Tag all possible branch targets.
+   *
+   * We've already verified that the table is structurally sound, so we
+   * just need to walk through and tag the targets.
+   */
+  if ((opcode_flag & Instruction::kSwitch) != 0) {
+    int offset_to_switch = insns[1] | (((int32_t) insns[2]) << 16);
+    const uint16_t* switch_insns = insns + offset_to_switch;
+    int switch_count = switch_insns[1];
+    int offset_to_targets, targ;
+
+    if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
+      /* 0 = sig, 1 = count, 2/3 = first key */
+      offset_to_targets = 4;
+    } else {
+      /* 0 = sig, 1 = count, 2..count * 2 = keys */
+      assert((*insns & 0xff) == Instruction::SPARSE_SWITCH);
+      offset_to_targets = 2 + 2 * switch_count;
+    }
+
+    /* verify each switch target */
+    for (targ = 0; targ < switch_count; targ++) {
+      int offset;
+      uint32_t abs_offset;
+
+      /* offsets are 32-bit, and only partly endian-swapped */
+      offset = switch_insns[offset_to_targets + targ * 2] |
+         (((int32_t) switch_insns[offset_to_targets + targ * 2 + 1]) << 16);
+      abs_offset = insn_idx + offset;
+
+      assert(abs_offset < insns_size);
+
+      if (!CheckMoveException(code_item->insns_, abs_offset))
+        return false;
+
+      if (!UpdateRegisters(insn_flags, reg_table, abs_offset, work_line))
+        return false;
+    }
+  }
+
+  /*
+   * Handle instructions that can throw and that are sitting in a
+   * "try" block.  (If they're not in a "try" block when they throw,
+   * control transfers out of the method.)
+   */
+  if ((opcode_flag & Instruction::kThrow) != 0 &&
+      InsnIsInTry(insn_flags, insn_idx)) {
+    bool has_catch_all = false;
+    DexFile::CatchHandlerIterator iterator = DexFile::dexFindCatchHandler(
+        *code_item, insn_idx);
+
+    for (; !iterator.HasNext(); iterator.Next()) {
+      if (iterator.Get().type_idx_ == DexFile::kDexNoIndex)
+        has_catch_all = true;
+
+      /*
+       * Merge registers into the "catch" block.  We want to
+       * use the "savedRegs" rather than "work_regs", because
+       * at runtime the exception will be thrown before the
+       * instruction modifies any registers.
+       */
+      if (!UpdateRegisters(insn_flags, reg_table, iterator.Get().address_,
+          &reg_table->saved_line_))
+        return false;
+    }
+
+    /*
+     * If the monitor stack depth is nonzero, there must be a "catch all"
+     * handler for this instruction.  This does apply to monitor-exit
+     * because of async exception handling.
+     */
+    if (work_line->monitor_stack_top_ != 0 && !has_catch_all) {
+      /*
+       * The state in work_line reflects the post-execution state.
+       * If the current instruction is a monitor-enter and the monitor
+       * stack was empty, we don't need a catch-all (if it throws,
+       * it will do so before grabbing the lock).
+       */
+      if (!(dec_insn.opcode_ == Instruction::MONITOR_ENTER &&
+          work_line->monitor_stack_top_ == 1))
+      {
+        LOG(ERROR) << "VFY: no catch-all for instruction at 0x" << std::hex
+                   << insn_idx << std::dec;
+        return false;
+      }
+    }
+  }
+
+  /*
+   * If we're returning from the method, make sure our monitor stack is empty.
+   */
+  if ((opcode_flag & Instruction::kReturn) != 0 &&
+      work_line->monitor_stack_top_ != 0) {
+    LOG(ERROR) << "VFY: return with stack depth="
+               << work_line->monitor_stack_top_ << " at 0x" << std::hex
+               << insn_idx << std::dec;
+    return false;
+  }
+
+  /*
+   * Update start_guess.  Advance to the next instruction of that's
+   * possible, otherwise use the branch target if one was found.  If
+   * neither of those exists we're in a return or throw; leave start_guess
+   * alone and let the caller sort it out.
+   */
+  if ((opcode_flag & Instruction::kContinue) != 0) {
+    *start_guess = insn_idx + InsnGetWidth(insn_flags, insn_idx);
+  } else if ((opcode_flag & Instruction::kBranch) != 0) {
+    /* we're still okay if branch_target is zero */
+    *start_guess = insn_idx + branch_target;
+  }
+
+  assert(*start_guess < insns_size &&
+      InsnGetWidth(insn_flags, *start_guess) != 0);
+
+  return true;
+}
+
+bool DexVerifier::ReplaceFailingInstruction(const DexFile::CodeItem* code_item,
+    InsnFlags* insn_flags, int insn_idx, VerifyError failure) {
+  const uint16_t* insns = code_item->insns_ + insn_idx;
+  const byte* ptr = reinterpret_cast<const byte*>(insns);
+  const Instruction* inst = Instruction::At(ptr);
+  Instruction::Code opcode = inst->Opcode();
+  VerifyErrorRefType ref_type;
+
+  /*
+   * Generate the new instruction out of the old.
+   *
+   * First, make sure this is an instruction we're expecting to stomp on.
+   */
+  switch (opcode) {
+    case Instruction::CONST_CLASS:            // insn[1] == class ref, 2 bytes
+    case Instruction::CHECK_CAST:
+    case Instruction::INSTANCE_OF:
+    case Instruction::NEW_INSTANCE:
+    case Instruction::NEW_ARRAY:
+    case Instruction::FILLED_NEW_ARRAY:       // insn[1] == class ref, 3 bytes
+    case Instruction::FILLED_NEW_ARRAY_RANGE:
+      ref_type = VERIFY_ERROR_REF_CLASS;
+      break;
+
+    case Instruction::IGET:                   // insn[1] == field ref, 2 bytes
+    case Instruction::IGET_BOOLEAN:
+    case Instruction::IGET_BYTE:
+    case Instruction::IGET_CHAR:
+    case Instruction::IGET_SHORT:
+    case Instruction::IGET_WIDE:
+    case Instruction::IGET_OBJECT:
+    case Instruction::IPUT:
+    case Instruction::IPUT_BOOLEAN:
+    case Instruction::IPUT_BYTE:
+    case Instruction::IPUT_CHAR:
+    case Instruction::IPUT_SHORT:
+    case Instruction::IPUT_WIDE:
+    case Instruction::IPUT_OBJECT:
+    case Instruction::SGET:
+    case Instruction::SGET_BOOLEAN:
+    case Instruction::SGET_BYTE:
+    case Instruction::SGET_CHAR:
+    case Instruction::SGET_SHORT:
+    case Instruction::SGET_WIDE:
+    case Instruction::SGET_OBJECT:
+    case Instruction::SPUT:
+    case Instruction::SPUT_BOOLEAN:
+    case Instruction::SPUT_BYTE:
+    case Instruction::SPUT_CHAR:
+    case Instruction::SPUT_SHORT:
+    case Instruction::SPUT_WIDE:
+    case Instruction::SPUT_OBJECT:
+      ref_type = VERIFY_ERROR_REF_FIELD;
+      break;
+
+    case Instruction::INVOKE_VIRTUAL:         // insn[1] == method ref, 3 bytes
+    case Instruction::INVOKE_VIRTUAL_RANGE:
+    case Instruction::INVOKE_SUPER:
+    case Instruction::INVOKE_SUPER_RANGE:
+    case Instruction::INVOKE_DIRECT:
+    case Instruction::INVOKE_DIRECT_RANGE:
+    case Instruction::INVOKE_STATIC:
+    case Instruction::INVOKE_STATIC_RANGE:
+    case Instruction::INVOKE_INTERFACE:
+    case Instruction::INVOKE_INTERFACE_RANGE:
+      ref_type = VERIFY_ERROR_REF_METHOD;
+      break;
+
+    default:
+      /* could handle this in a generic way, but this is probably safer */
+      LOG(ERROR) << "GLITCH: verifier asked to replace opcode 0x" << std::hex
+                 << (int) opcode << std::dec;
+      return false;
+  }
+
+  assert(inst->IsThrow());
+
+  /* write a NOP over the third code unit, if necessary */
+  int width = InsnGetWidth(insn_flags, insn_idx);
+  switch (width) {
+    case 2:
+      /* nothing to do */
+      break;
+    case 3:
+      // TODO: Add this functionality
+      //UpdateCodeUnit(method, insns + 2, Instruction::NOP);
+      break;
+    default:
+      /* whoops */
+      LOG(FATAL) << "ERROR: stomped a " << width
+                 << "-unit instruction with a verifier error";
+  }
+
+  /* encode the opcode, with the failure code in the high byte */
+  // TODO: REPLACE FAILING OPCODES
+  //assert(width == 2 || width == 3);
+  //uint16_t new_val = Instruction::THROW_VERIFICATION_ERROR |
+      //(failure << 8) | (ref_type << (8 + kVerifyErrorRefTypeShift));
+  //UpdateCodeUnit(method, insns, new_val);
+
+  return true;
+}
+
+/* Handle a monitor-enter instruction. */
+void DexVerifier::HandleMonitorEnter(RegisterLine* work_line, uint32_t reg_idx,
+    uint32_t insn_idx, VerifyError* failure) {
+  if (!RegTypeIsReference(GetRegisterType(work_line, reg_idx))) {
+    LOG(ERROR) << "VFY: monitor-enter on non-object";
+    *failure = VERIFY_ERROR_GENERIC;
+    return;
+  }
+
+  if (work_line->monitor_entries_ == NULL) {
+    return;
+  }
+
+  if (work_line->monitor_stack_top_ == kMaxMonitorStackDepth) {
+    LOG(ERROR) << "VFY: monitor-enter stack overflow (" << kMaxMonitorStackDepth
+               << ")";
+    *failure = VERIFY_ERROR_GENERIC;
+    return;
+  }
+
+  /*
+   * Push an entry on the stack, and set a bit in the register flags to
+   * indicate that it's associated with this register.
+   */
+  work_line->monitor_entries_[reg_idx] |= 1 << work_line->monitor_stack_top_;
+  work_line->monitor_stack_[work_line->monitor_stack_top_++] = insn_idx;
+}
+
+/* Handle a monitor-exit instruction. */
+void DexVerifier::HandleMonitorExit(RegisterLine* work_line, uint32_t reg_idx,
+    uint32_t insn_idx, VerifyError* failure) {
+  if (!RegTypeIsReference(GetRegisterType(work_line, reg_idx))) {
+    LOG(ERROR) << "VFY: monitor-exit on non-object";
+    *failure = VERIFY_ERROR_GENERIC;
+    return;
+  }
+
+  if (work_line->monitor_entries_ == NULL) {
+    return;
+  }
+
+  if (work_line->monitor_stack_top_ == 0) {
+    LOG(ERROR) << "VFY: monitor-exit stack underflow";
+    *failure = VERIFY_ERROR_GENERIC;
+    return;
+  }
+
+  /*
+   * Confirm that the entry at the top of the stack is associated with
+   * the register.  Pop the top entry off.
+   */
+  work_line->monitor_stack_top_--;
+#ifdef BUG_3215458_FIXED
+  /*
+   * TODO: This code can safely be enabled if know we are working on
+   * a dex file of format version 036 or later. (That is, we'll need to
+   * add a check for the version number.)
+   */
+  if ((work_line->monitor_entries_[reg_idx] &
+      (1 << work_line->monitor_stack_top_)) == 0) {
+    LOG(ERROR) << "VFY: monitor-exit bit " << work_line->monitor_stack_top_
+               << " not set: addr=0x" << std::hex << insn_idx << std::dec
+               << " (bits[" << reg_idx << "]=" << std::hex
+               << work_line->monitor_entries_[reg_idx] << std::dec << ")";
+    *failure = VERIFY_ERROR_GENERIC;
+    return;
+  }
+#endif
+  work_line->monitor_stack_[work_line->monitor_stack_top_] = 0;
+
+  /* Clear the bit from the register flags. */
+  work_line->monitor_entries_[reg_idx] &= ~(1 << work_line->monitor_stack_top_);
+}
+
+Field* DexVerifier::GetInstField(VerifierData* vdata, RegType obj_type,
+    int field_idx, VerifyError* failure) {
+  Method* method = vdata->method_;
+  const DexFile* dex_file = vdata->dex_file_;
+  UninitInstanceMap* uninit_map = vdata->uninit_map_;
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+  const ClassLoader* class_loader =
+      method->GetDeclaringClass()->GetClassLoader();
+  Field* field = NULL;
+  Class* obj_class;
+  bool must_be_local = false;
+
+  if (!RegTypeIsReference(obj_type)) {
+    LOG(ERROR) << "VFY: attempt to access field in non-reference type "
+               << obj_type;
+    *failure = VERIFY_ERROR_GENERIC;
+    return field;
+  }
+
+  field = class_linker->ResolveField(*dex_file, field_idx, dex_cache,
+      class_loader, false);
+  if (field == NULL) {
+    LOG(ERROR) << "VFY: unable to resolve instance field " << field_idx;
+    return field;
+  }
+
+  if (obj_type == kRegTypeZero)
+    return field;
+
+  /*
+   * Access to fields in uninitialized objects is allowed if this is
+   * the <init> method for the object and the field in question is
+   * declared by this class.
+   */
+  obj_class = RegTypeReferenceToClass(obj_type, uninit_map);
+  assert(obj_class != NULL);
+  if (RegTypeIsUninitReference(obj_type)) {
+    if (!IsInitMethod(method) || method->GetDeclaringClass() != obj_class) {
+      LOG(ERROR) << "VFY: attempt to access field via uninitialized ref";
+      *failure = VERIFY_ERROR_GENERIC;
+      return field;
+    }
+    must_be_local = true;
+  }
+
+  //if (!obj_class->InstanceOf(field->GetDeclaringClass())) {
+  if (!field->GetDeclaringClass()->IsAssignableFrom(obj_class)) {
+    LOG(ERROR) << "VFY: invalid field access (field "
+               << field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+               << "." << field->GetName()->ToModifiedUtf8() << ", through "
+               << obj_class->GetDescriptor()->ToModifiedUtf8() << " ref)";
+    *failure = VERIFY_ERROR_NO_FIELD;
+    return field;
+  }
+
+  if (must_be_local) {
+    bool found = false;
+    /* for uninit ref, make sure it's defined by this class, not super */
+    for (uint32_t i = 0; i < obj_class->NumInstanceFields(); i++) {
+      found |= (field == obj_class->GetInstanceField(i));
+    }
+    if (!found) {
+      LOG(ERROR) << "VFY: invalid constructor field access (field "
+                 << field->GetName()->ToModifiedUtf8() << " in "
+                 << obj_class->GetDescriptor()->ToModifiedUtf8() << ")";
+      *failure = VERIFY_ERROR_GENERIC;
+      return field;
+    }
+  }
+
+  return field;
+}
+
+Field* DexVerifier::GetStaticField(VerifierData* vdata, int field_idx,
+    VerifyError* failure) {
+  Method* method = vdata->method_;
+  const DexFile* dex_file = vdata->dex_file_;
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+  const ClassLoader* class_loader =
+      method->GetDeclaringClass()->GetClassLoader();
+  Field* field;
+
+  field = class_linker->ResolveField(*dex_file, field_idx, dex_cache,
+      class_loader, true);
+  if (field == NULL) {
+    //const DexFile::FieldId field_id = dex_file->GetFieldId(field_idx);
+
+    //LOG(ERROR) << "VFY: unable to resolve static field " << field_idx << " ("
+               //<< dex_file->GetFieldName(field_id) << ") in "
+               //<< dex_file->GetFieldClassDescriptor(field_id);
+    LOG(ERROR) << "VFY: unable to resolve static field";
+  }
+
+  return field;
+}
+
+Class* DexVerifier::GetCaughtExceptionType(VerifierData* vdata, int insn_idx,
+    VerifyError* failure) {
+  const DexFile* dex_file = vdata->dex_file_;
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  Method* method = vdata->method_;
+  Class* common_super = NULL;
+  uint32_t handlers_size;
   const byte* handlers_ptr = DexFile::dexGetCatchHandlerData(*code_item, 0);
-  uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
-  for (uint32_t idx = 0; idx < handlers_size; idx++) {
+
+  if (code_item->tries_size_ != 0) {
+    handlers_size = DecodeUnsignedLeb128(&handlers_ptr);
+  } else {
+    handlers_size = 0;
+  }
+
+  for (uint32_t i = 0; i < handlers_size; i++) {
     DexFile::CatchHandlerIterator iterator(handlers_ptr);
 
     for (; !iterator.HasNext(); iterator.Next()) {
-      uint32_t addr = iterator.Get().address_;
-      if (InsnGetWidth(insn_flags, addr) == 0) {
-        LOG(ERROR) << "VFY: exception handler starts at bad address ("
-                   << addr << ")";
-        return false;
-      }
+      DexFile::CatchHandlerItem handler = iterator.Get();
+      if (handler.address_ == (uint32_t) insn_idx) {
+        ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+        Class* klass;
 
-      InsnSetBranchTarget(insn_flags, addr, true);
+        if (handler.type_idx_ == DexFile::kDexNoIndex) {
+          klass = class_linker->FindSystemClass("Ljava/lang/Throwable;");
+        } else {
+          klass = class_linker->ResolveType(*dex_file, handler.type_idx_,
+              method->GetDeclaringClass());
+        }
+
+        if (klass == NULL) {
+          LOG(ERROR) << "VFY: unable to resolve exception class "
+                     << handler.type_idx_ << " ("
+                     << dex_file->dexStringByTypeIdx(handler.type_idx_) << ")";
+          /* TODO: do we want to keep going?  If we don't fail
+           * this we run the risk of having a non-Throwable
+           * introduced at runtime.  However, that won't pass
+           * an instanceof test, so is essentially harmless.
+           */
+        } else {
+          if (common_super == NULL)
+            common_super = klass;
+          else
+            common_super = FindCommonSuperclass(klass, common_super);
+        }
+      }
     }
 
     handlers_ptr = iterator.GetData();
   }
 
-  return true;
+  if (common_super == NULL) {
+    /* no catch blocks, or no catches with classes we can find */
+    LOG(ERROR) << "VFY: unable to find exception handler at addr 0x" << std::hex
+               << insn_idx << std::dec;
+    *failure = VERIFY_ERROR_GENERIC;
+  }
+
+  return common_super;
 }
 
-
-
-bool DexVerify::VerifyClass(Class* klass) {
-  if (klass->IsVerified()) {
-    return true;
-  }
-  for (size_t i = 0; i < klass->NumDirectMethods(); ++i) {
-    Method* method = klass->GetDirectMethod(i);
-    if (!VerifyMethod(method)) {
-        LOG(ERROR) << "Verifier rejected class "
-                   << klass->GetDescriptor()->ToModifiedUtf8();
-      return false;
-    }
-  }
-  for (size_t i = 0; i < klass->NumVirtualMethods(); ++i) {
-    Method* method = klass->GetVirtualMethod(i);
-    if (!VerifyMethod(method)) {
-        LOG(ERROR) << "Verifier rejected class "
-                   << klass->GetDescriptor()->ToModifiedUtf8();
-      return false;
-    }
-  }
-  return true;
+DexVerifier::RegType DexVerifier::GetMethodReturnType(const DexFile* dex_file,
+    const Method* method) {
+  Class* klass = method->GetReturnType();
+  if (klass->IsPrimitive())
+    return PrimitiveTypeToRegType(klass->GetPrimitiveType());
+  else
+    return RegTypeFromClass(klass);
 }
 
-bool DexVerify::VerifyMethod(Method* method) {
-  const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
-  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
-  const DexFile& dex_file = class_linker->FindDexFile(dex_cache);
-  const DexFile::CodeItem *code_item = dex_file.GetCodeItem(method->GetCodeItemOffset());
+Class* DexVerifier::GetClassFromRegister(const RegisterLine* register_line,
+    uint32_t vsrc, VerifyError* failure) {
+  /* get the element type of the array held in vsrc */
+  RegType type = GetRegisterType(register_line, vsrc);
 
-  /*
-   * If there aren't any instructions, make sure that's expected, then
-   * exit successfully.
-   */
-  if (code_item == NULL) {
-    if (!method->IsNative() && !method->IsAbstract()) {
-      LOG(ERROR) << "VFY: zero-length code in concrete non-native method";
-      return false;
-    }
-    return true;
+  /* if "always zero", we allow it to fail at runtime */
+  if (type == kRegTypeZero)
+    return NULL;
+
+  if (!RegTypeIsReference(type)) {
+    LOG(ERROR) << "VFY: tried to get class from non-ref register v" << vsrc
+               << " (type=" << type << ")",
+    *failure = VERIFY_ERROR_GENERIC;
+    return NULL;
+  }
+  if (RegTypeIsUninitReference(type)) {
+    LOG(ERROR) << "VFY: register " << vsrc << " holds uninitialized reference";
+    *failure = VERIFY_ERROR_GENERIC;
+    return NULL;
   }
 
-  /*
-   * Sanity-check the register counts.  ins + locals = registers, so make
-   * sure that ins <= registers.
-   */
-  if (code_item->ins_size_ > code_item->registers_size_) {
-    LOG(ERROR) << "VFY: bad register counts (ins=" << code_item->ins_size_
-               << " regs=" << code_item->registers_size_;
-    return false;
-  }
-
-  /*
-   * Allocate and initialize an array to hold instruction data.
-   */
-  UniquePtr<uint32_t[]> insn_flags(new uint32_t[code_item->insns_size_]());
-
-  /*
-   * Run through the instructions and see if the width checks out.
-   */
-  if (!CheckInsnWidth(code_item->insns_, code_item->insns_size_, insn_flags.get())) {
-    return false;
-  }
-
-  /*
-   * Flag instructions guarded by a "try" block and check exception handlers.
-   */
-  if (!ScanTryCatchBlocks(code_item, insn_flags.get())) {
-    return false;
-  }
-
-  /*
-   * Perform static instruction verification.
-   */
-  if (!VerifyInstructions(&dex_file, code_item, insn_flags.get())) {
-    return false;
-  }
-
-  /*
-   * TODO: Code flow analysis
-   */
-
-  return true;
+  return RegTypeInitializedReferenceToClass(type);
 }
 
-bool DexVerify::VerifyInstructions(const DexFile* dex_file,
-    const DexFile::CodeItem* code_item, uint32_t insn_flags[]) {
-  const byte* ptr = reinterpret_cast<const byte*>(code_item->insns_);
-  const Instruction* inst = Instruction::At(ptr);
-
-  /* Flag the start of the method as a branch target. */
-  InsnSetBranchTarget(insn_flags, 0, true);
-
-  uint32_t width = 0;
-  uint32_t insns_size = code_item->insns_size_;
-
-  while (width < insns_size) {
-    if (!VerifyInstruction(dex_file, inst, width, code_item, insn_flags)) {
-      LOG(ERROR) << "VFY:  rejecting opcode 0x" << std::hex
-                 << (int) inst->Opcode() << " at 0x" << width << std::dec;
-      return false;
-    }
-
-    /* Flag instructions that are garbage collection points */
-    if (inst->IsBranch() || inst->IsSwitch() || inst->IsThrow() ||
-        inst->IsReturn()) {
-      InsnSetGcPoint(insn_flags, width, true);
-    }
-
-    width += inst->Size();
-    inst = inst->Next();
+DexVerifier::RegType DexVerifier::GetInvocationThis(
+    const RegisterLine* register_line,
+    const Instruction::DecodedInstruction* dec_insn, VerifyError* failure) {
+  if (dec_insn->vA_ < 1) {
+    LOG(ERROR) << "VFY: invoke lacks 'this'";
+    *failure = VERIFY_ERROR_GENERIC;
+    return kRegTypeUnknown;
   }
-  return true;
+
+  /* get the element type of the array held in vsrc */
+  RegType this_type = GetRegisterType(register_line, dec_insn->vC_);
+  if (!RegTypeIsReference(this_type)) {
+    LOG(ERROR) << "VFY: tried to get class from non-ref register v"
+               << dec_insn->vC_ << " (type=" << this_type << ")";
+    *failure = VERIFY_ERROR_GENERIC;
+    return kRegTypeUnknown;
+  }
+
+  return this_type;
 }
 
-bool DexVerify::VerifyInstruction(const DexFile* dex_file,
-    const Instruction* inst, uint32_t code_offset,
-    const DexFile::CodeItem* code_item, uint32_t insn_flags[]) {
-  Instruction::Code opcode = inst->Opcode();
-  bool result = true;
-  uint32_t vA, vB, vC;
-  uint64_t vB_wide;
-  uint32_t arg[5];
+void DexVerifier::SetRegisterType(RegisterLine* register_line, uint32_t vdst,
+    RegType new_type) {
+  RegType* insn_regs = register_line->reg_types_;
 
-  inst->Decode(vA, vB, vB_wide, vC, arg);
-
-  int argumentA = inst->GetVerifyTypeArgumentA();
-  int argumentB = inst->GetVerifyTypeArgumentB();
-  int argumentC = inst->GetVerifyTypeArgumentC();
-  int extra_flags = inst->GetVerifyExtraFlags();
-
-  switch (argumentA) {
-    case Instruction::kVerifyRegA:
-      result &= CheckRegisterIndex(code_item, vA);
+  switch (new_type) {
+    case kRegTypeUnknown:
+    case kRegTypeBoolean:
+    case kRegTypeOne:
+    case kRegTypeConstByte:
+    case kRegTypeConstPosByte:
+    case kRegTypeConstShort:
+    case kRegTypeConstPosShort:
+    case kRegTypeConstChar:
+    case kRegTypeConstInteger:
+    case kRegTypeByte:
+    case kRegTypePosByte:
+    case kRegTypeShort:
+    case kRegTypePosShort:
+    case kRegTypeChar:
+    case kRegTypeInteger:
+    case kRegTypeFloat:
+    case kRegTypeZero:
+    case kRegTypeUninit:
+      insn_regs[vdst] = new_type;
       break;
-    case Instruction::kVerifyRegAWide:
-      result &= CheckWideRegisterIndex(code_item, vA);
+    case kRegTypeConstLo:
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+      insn_regs[vdst] = new_type;
+      insn_regs[vdst + 1] = new_type + 1;
+      break;
+    case kRegTypeConstHi:
+    case kRegTypeLongHi:
+    case kRegTypeDoubleHi:
+      /* should never set these explicitly */
+      LOG(FATAL) << "BUG: explicit set of high register type";
+      break;
+
+    default:
+      /* can't switch for ref types, so we check explicitly */
+      if (RegTypeIsReference(new_type)) {
+        insn_regs[vdst] = new_type;
+
+        /*
+         * In most circumstances we won't see a reference to a primitive
+         * class here (e.g. "D"), since that would mean the object in the
+         * register is actually a primitive type.  It can happen as the
+         * result of an assumed-successful check-cast instruction in
+         * which the second argument refers to a primitive class.  (In
+         * practice, such an instruction will always throw an exception.)
+         *
+         * This is not an issue for instructions like const-class, where
+         * the object in the register is a java.lang.Class instance.
+         */
+        break;
+      }
+      /* bad type - fall through */
+
+    case kRegTypeConflict:      // should only be set during a merge
+      LOG(FATAL) << "BUG: set register to unknown type " << new_type;
       break;
   }
 
-  switch (argumentB) {
-    case Instruction::kVerifyRegB:
-      result &= CheckRegisterIndex(code_item, vB);
+  /*
+   * Clear the monitor entry bits for this register.
+   */
+  if (register_line->monitor_entries_ != NULL)
+    register_line->monitor_entries_[vdst] = 0;
+}
+
+void DexVerifier::VerifyRegisterType(RegisterLine* register_line, uint32_t vsrc,
+    RegType check_type, VerifyError* failure) {
+  const RegType* insn_regs = register_line->reg_types_;
+  RegType src_type = insn_regs[vsrc];
+
+  switch (check_type) {
+    case kRegTypeFloat:
+    case kRegTypeBoolean:
+    case kRegTypePosByte:
+    case kRegTypeByte:
+    case kRegTypePosShort:
+    case kRegTypeShort:
+    case kRegTypeChar:
+    case kRegTypeInteger:
+      if (!CanConvertTo1nr(src_type, check_type)) {
+        LOG(ERROR) << "VFY: register1 v" << vsrc << " type " << src_type
+                   << ", wanted " << check_type;
+        *failure = VERIFY_ERROR_GENERIC;
+      }
+      /* Update type if result is float */
+      if (check_type == kRegTypeFloat) {
+        SetRegisterType(register_line, vsrc, check_type);
+      } else {
+        /* Update const type to actual type after use */
+        SetRegisterType(register_line, vsrc, ConstTypeToRegType(src_type));
+      }
       break;
-    case Instruction::kVerifyRegBField:
-      result &= CheckFieldIndex(dex_file, vB);
+    case kRegTypeLongLo:
+    case kRegTypeDoubleLo:
+      if (insn_regs[vsrc + 1] != src_type + 1) {
+        LOG(ERROR) << "VFY: register2 v" << vsrc << "-" << vsrc + 1
+                   << " values " << insn_regs[vsrc] << ","
+                   << insn_regs[vsrc + 1];
+        *failure = VERIFY_ERROR_GENERIC;
+      } else if (!CanConvertTo2(src_type, check_type)) {
+        LOG(ERROR) << "VFY: register2 v" << vsrc << " type " << src_type
+                   << ", wanted " << check_type;
+        *failure = VERIFY_ERROR_GENERIC;
+      }
+      /* Update type if source is from const */
+      if (src_type == kRegTypeConstLo) {
+        SetRegisterType(register_line, vsrc, check_type);
+      }
       break;
-    case Instruction::kVerifyRegBMethod:
-      result &= CheckMethodIndex(dex_file, vB);
-      break;
-    case Instruction::kVerifyRegBNewInstance:
-      result &= CheckNewInstance(dex_file, vB);
-      break;
-    case Instruction::kVerifyRegBString:
-      result &= CheckStringIndex(dex_file, vB);
-      break;
-    case Instruction::kVerifyRegBType:
-      result &= CheckTypeIndex(dex_file, vB);
-      break;
-    case Instruction::kVerifyRegBWide:
-      result &= CheckWideRegisterIndex(code_item, vB);
+    case kRegTypeConstLo:
+    case kRegTypeConstHi:
+    case kRegTypeLongHi:
+    case kRegTypeDoubleHi:
+    case kRegTypeZero:
+    case kRegTypeOne:
+    case kRegTypeUnknown:
+    case kRegTypeConflict:
+      /* should never be checking for these explicitly */
+      assert(false);
+      *failure = VERIFY_ERROR_GENERIC;
+      return;
+    case kRegTypeUninit:
+    default:
+      /* make sure check_type is initialized reference */
+      if (!RegTypeIsReference(check_type)) {
+        LOG(ERROR) << "VFY: unexpected check type " << check_type;
+        assert(false);
+        *failure = VERIFY_ERROR_GENERIC;
+        break;
+      }
+      if (RegTypeIsUninitReference(check_type)) {
+        LOG(ERROR) << "VFY: uninitialized ref not expected as reg check";
+        *failure = VERIFY_ERROR_GENERIC;
+        break;
+      }
+      /* make sure src_type is initialized reference or always-NULL */
+      if (!RegTypeIsReference(src_type)) {
+        LOG(ERROR) << "VFY: register1 v" << vsrc << " type " << src_type
+                   << ", wanted ref";
+        *failure = VERIFY_ERROR_GENERIC;
+        break;
+      }
+      if (RegTypeIsUninitReference(src_type)) {
+        LOG(ERROR) << "VFY: register1 v" << vsrc << " holds uninitialized ref";
+        *failure = VERIFY_ERROR_GENERIC;
+        break;
+      }
+      /* if the register isn't Zero, make sure it's an instance of check */
+      if (src_type != kRegTypeZero) {
+        Class* src_class = RegTypeInitializedReferenceToClass(src_type);
+        Class* check_class = RegTypeInitializedReferenceToClass(check_type);
+        assert(src_class != NULL);
+        assert(check_class != NULL);
+
+        if (!check_class->IsAssignableFrom(src_class)) {
+          LOG(ERROR) << "VFY: " << src_class->GetDescriptor()->ToModifiedUtf8()
+                     << " is not instance of "
+                     << check_class->GetDescriptor()->ToModifiedUtf8();
+          *failure = VERIFY_ERROR_GENERIC;
+        }
+      }
       break;
   }
+}
 
-  switch (argumentC) {
-    case Instruction::kVerifyRegC:
-      result &= CheckRegisterIndex(code_item, vC);
-      break;
-    case Instruction::kVerifyRegCField:
-      result &= CheckFieldIndex(dex_file, vC);
-      break;
-    case Instruction::kVerifyRegCNewArray:
-      result &= CheckNewArray(dex_file, vC);
-      break;
-    case Instruction::kVerifyRegCType:
-      result &= CheckTypeIndex(dex_file, vC);
-      break;
-    case Instruction::kVerifyRegCWide:
-      result &= CheckWideRegisterIndex(code_item, vC);
-      break;
+void DexVerifier::SetResultRegisterType(RegisterLine* register_line,
+    const int insn_reg_count, RegType new_type) {
+  SetRegisterType(register_line, RESULT_REGISTER(insn_reg_count), new_type);
+}
+
+void DexVerifier::MarkRefsAsInitialized(RegisterLine* register_line,
+    int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type,
+    VerifyError* failure) {
+  RegType* insn_regs = register_line->reg_types_;
+  Class* klass = GetUninitInstance(uninit_map,
+      RegTypeToUninitIndex(uninit_type));
+
+  if (klass == NULL) {
+    LOG(ERROR) << "VFY: unable to find type=" << std::hex << uninit_type
+               << std::dec << " (idx=" << RegTypeToUninitIndex(uninit_type)
+               << ")";
+    *failure = VERIFY_ERROR_GENERIC;
+    return;
   }
 
-  switch (extra_flags) {
-    case Instruction::kVerifyArrayData:
-      result &= CheckArrayData(code_item, code_offset);
-      break;
-    case Instruction::kVerifyBranchTarget:
-      result &= CheckBranchTarget(code_item, insn_flags, code_offset);
-      break;
-    case Instruction::kVerifySwitchTargets:
-      result &= CheckSwitchTargets(code_item, insn_flags, code_offset);
-      break;
-    case Instruction::kVerifyVarArg:
-      result &= CheckVarArgRegs(code_item, vA, arg);
-      break;
-    case Instruction::kVerifyVarArgRange:
-      result &= CheckVarArgRangeRegs(code_item, vA, vC);
-      break;
-    case Instruction::kVerifyError:
-      LOG(ERROR) << "VFY: unexpected opcode " << (int) opcode;
-      result = false;
-      break;
+  RegType init_type = RegTypeFromClass(klass);
+  int changed = 0;
+  for (int i = 0; i < insn_reg_count; i++) {
+    if (insn_regs[i] == uninit_type) {
+      insn_regs[i] = init_type;
+      changed++;
+    }
+  }
+  assert(changed > 0);
+
+  return;
+}
+
+void DexVerifier::MarkUninitRefsAsInvalid(RegisterLine* register_line,
+    int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type) {
+  RegType* insn_regs = register_line->reg_types_;
+
+  for (int i = 0; i < insn_reg_count; i++) {
+    if (insn_regs[i] == uninit_type) {
+      insn_regs[i] = kRegTypeConflict;
+      if (register_line->monitor_entries_ != NULL)
+        register_line->monitor_entries_[i] = 0;
+    }
+  }
+}
+
+void DexVerifier::CopyRegister1(RegisterLine* register_line, uint32_t vdst,
+    uint32_t vsrc, TypeCategory cat, VerifyError* failure) {
+  assert(cat == kTypeCategory1nr || cat == kTypeCategoryRef);
+  RegType type = GetRegisterType(register_line, vsrc);
+  CheckTypeCategory(type, cat, failure);
+  if (*failure != VERIFY_ERROR_NONE) {
+    LOG(ERROR) << "VFY: copy1 v" << vdst << "<-v" << vsrc << " type=" << type
+               << " cat=" << (int) cat;
+  } else {
+    SetRegisterType(register_line, vdst, type);
+    if (cat == kTypeCategoryRef && register_line->monitor_entries_ != NULL) {
+      register_line->monitor_entries_[vdst] =
+          register_line->monitor_entries_[vsrc];
+    }
+  }
+}
+
+void DexVerifier::CopyRegister2(RegisterLine* register_line, uint32_t vdst,
+    uint32_t vsrc, VerifyError* failure) {
+  RegType type_l = GetRegisterType(register_line, vsrc);
+  RegType type_h = GetRegisterType(register_line, vsrc + 1);
+
+  CheckTypeCategory(type_l, kTypeCategory2, failure);
+  CheckWidePair(type_l, type_h, failure);
+  if (*failure != VERIFY_ERROR_NONE) {
+    LOG(ERROR) << "VFY: copy2 v" << vdst << "<-v" << vsrc << " type=" << type_l
+               << "/" << type_h;
+  } else {
+    SetRegisterType(register_line, vdst, type_l);
+  }
+}
+
+void DexVerifier::CopyResultRegister1(RegisterLine* register_line,
+    const int insn_reg_count, uint32_t vdst, TypeCategory cat,
+    VerifyError* failure) {
+  assert(vdst < (uint32_t) insn_reg_count);
+
+  uint32_t vsrc = RESULT_REGISTER(insn_reg_count);
+  RegType type = GetRegisterType(register_line, vsrc);
+  CheckTypeCategory(type, cat, failure);
+  if (*failure != VERIFY_ERROR_NONE) {
+    LOG(ERROR) << "VFY: copyRes1 v" << vdst << "<-v" << vsrc << " cat="
+               << (int) cat << " type=" << type;
+  } else {
+    SetRegisterType(register_line, vdst, type);
+    SetRegisterType(register_line, vsrc, kRegTypeUnknown);
+  }
+}
+
+/*
+ * Implement "move-result-wide".  Copy the category-2 value from the result
+ * register to another register, and reset the result register.
+ */
+void DexVerifier::CopyResultRegister2(RegisterLine* register_line,
+    const int insn_reg_count, uint32_t vdst, VerifyError* failure) {
+  assert(vdst < (uint32_t) insn_reg_count);
+
+  uint32_t vsrc = RESULT_REGISTER(insn_reg_count);
+  RegType type_l = GetRegisterType(register_line, vsrc);
+  RegType type_h = GetRegisterType(register_line, vsrc + 1);
+  CheckTypeCategory(type_l, kTypeCategory2, failure);
+  CheckWidePair(type_l, type_h, failure);
+  if (*failure != VERIFY_ERROR_NONE) {
+    LOG(ERROR) << "VFY: copyRes2 v" << vdst << "<-v" << vsrc << " type="
+               << type_l << "/" << type_h;
+  } else {
+    SetRegisterType(register_line, vdst, type_l);
+    SetRegisterType(register_line, vsrc, kRegTypeUnknown);
+    SetRegisterType(register_line, vsrc + 1, kRegTypeUnknown);
+  }
+}
+
+int DexVerifier::GetClassDepth(Class* klass) {
+  int depth = 0;
+  while (klass->GetSuperClass() != NULL) {
+    klass = klass->GetSuperClass();
+    depth++;
+  }
+  return depth;
+}
+
+Class* DexVerifier::DigForSuperclass(Class* c1, Class* c2) {
+  int depth1, depth2;
+
+  depth1 = GetClassDepth(c1);
+  depth2 = GetClassDepth(c2);
+
+  /* pull the deepest one up */
+  if (depth1 > depth2) {
+    while (depth1 > depth2) {
+      c1 = c1->GetSuperClass();
+      depth1--;
+    }
+  } else {
+    while (depth2 > depth1) {
+      c2 = c2->GetSuperClass();
+      depth2--;
+    }
   }
 
+  /* walk up in lock-step */
+  while (c1 != c2) {
+    c1 = c1->GetSuperClass();
+    c2 = c2->GetSuperClass();
+
+    assert(c1 != NULL && c2 != NULL);
+  }
+
+  return c1;
+}
+
+Class* DexVerifier::FindCommonArraySuperclass(Class* c1, Class* c2) {
+  Class* array_class = NULL;
+  Class* common_elem;
+  int array_dim1, array_dim2;
+  int i, num_dims;
+  bool has_primitive = false;
+
+  array_dim1 = c1->GetArrayRank();
+  array_dim2 = c2->GetArrayRank();
+  assert(c1->GetArrayRank() > 0);
+  assert(c2->GetArrayRank() > 0);
+
+  if (c1->GetComponentType()->IsPrimitive()) {
+    array_dim1--;
+    has_primitive = true;
+  }
+  if (c2->GetComponentType()->IsPrimitive()) {
+    array_dim2--;
+    has_primitive = true;
+  }
+
+  if (!has_primitive && array_dim1 == array_dim2) {
+    /*
+     * Two arrays of reference types with equal dimensions.  Try to
+     * find a good match.
+     */
+    common_elem = FindCommonSuperclass(c1->GetComponentType(),
+        c2->GetComponentType());
+    num_dims = array_dim1;
+  } else {
+    /*
+     * Mismatched array depths and/or array(s) of primitives.  We want
+     * Object, or an Object array with appropriate dimensions.
+     *
+     * We initialize array_class to Object here, because it's possible
+     * for us to set num_dims=0.
+     */
+    if (array_dim1 < array_dim2)
+      num_dims = array_dim1;
+    else
+      num_dims = array_dim2;
+    array_class = common_elem = c1->GetSuperClass();  // == java.lang.Object
+  }
+
+  /*
+   * Find an appropriately-dimensioned array class.  This is easiest
+   * to do iteratively, using the array class found by the current round
+   * as the element type for the next round.
+   */
+  for (i = 0; i < num_dims; i++) {
+    ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+    const ClassLoader* class_loader = c1->GetClassLoader();
+    std::string descriptor = "[" +
+        common_elem->GetDescriptor()->ToModifiedUtf8();
+    array_class = class_linker->FindClass(descriptor.c_str(), class_loader);
+    common_elem = array_class;
+  }
+  assert(array_class != NULL);
+
+  return array_class;
+}
+
+Class* DexVerifier::FindCommonSuperclass(Class* c1, Class* c2) {
+  assert(!c1->IsPrimitive() && !c2->IsPrimitive());
+
+  if (c1 == c2)
+    return c1;
+
+  if (c1->IsInterface() && c2->Implements(c1)) {
+    return c1;
+  }
+  if (c2->IsInterface() && c1->Implements(c2)) {
+    return c2;
+  }
+  if (c1->IsArrayClass() && c2->IsArrayClass()) {
+    return FindCommonArraySuperclass(c1, c2);
+  }
+
+  return DigForSuperclass(c1, c2);
+}
+
+DexVerifier::RegType DexVerifier::MergeTypes(RegType type1, RegType type2,
+    bool* changed) {
+  RegType result;
+
+  /*
+   * Check for trivial case so we don't have to hit memory.
+   */
+  if (type1 == type2)
+    return type1;
+
+  /*
+   * Use the table if we can, and reject any attempts to merge something
+   * from the table with a reference type.
+   *
+   * Uninitialized references are composed of the enum ORed with an
+   * index value.  The uninitialized table entry at index zero *will*
+   * show up as a simple kRegTypeUninit value.  Since this cannot be
+   * merged with anything but itself, the rules do the right thing.
+   */
+  if (type1 < kRegTypeMAX) {
+    if (type2 < kRegTypeMAX) {
+      result = merge_table_[type1][type2];
+    } else {
+      /* simple + reference == conflict, usually */
+      if (type1 == kRegTypeZero)
+        result = type2;
+      else
+        result = kRegTypeConflict;
+    }
+  } else {
+    if (type2 < kRegTypeMAX) {
+      /* reference + simple == conflict, usually */
+      if (type2 == kRegTypeZero)
+        result = type1;
+      else
+        result = kRegTypeConflict;
+    } else {
+      /* merging two references */
+      if (RegTypeIsUninitReference(type1) ||
+          RegTypeIsUninitReference(type2))
+      {
+        /* can't merge uninit with anything but self */
+        result = kRegTypeConflict;
+      } else {
+        Class* klass1 = RegTypeInitializedReferenceToClass(type1);
+        Class* klass2 = RegTypeInitializedReferenceToClass(type2);
+        Class* merged_class = FindCommonSuperclass(klass1, klass2);
+        assert(merged_class != NULL);
+        result = RegTypeFromClass(merged_class);
+      }
+    }
+  }
+
+  if (result != type1)
+    *changed = true;
   return result;
-};
+}
+
+DexVerifier::MonitorEntries DexVerifier::MergeMonitorEntries(
+    MonitorEntries ents1, MonitorEntries ents2, bool* changed) {
+  MonitorEntries result = ents1 & ents2;
+  if (result != ents1)
+    *changed = true;
+  return result;
+}
+
+bool DexVerifier::UpdateRegisters(InsnFlags* insn_flags,
+    RegisterTable* reg_table, int next_insn, const RegisterLine* work_line) {
+  const size_t insn_reg_count_plus = reg_table->insn_reg_count_plus_;
+  assert(work_line != NULL);
+  const RegType* work_regs = work_line->reg_types_;
+
+  if (!InsnIsVisitedOrChanged(insn_flags, next_insn)) {
+    /*
+     * We haven't processed this instruction before, and we haven't
+     * touched the registers here, so there's nothing to "merge".  Copy
+     * the registers over and mark it as changed.  (This is the only
+     * way a register can transition out of "unknown", so this is not
+     * just an optimization.)
+     */
+    CopyLineToTable(reg_table, next_insn, work_line);
+    InsnSetChanged(insn_flags, next_insn, true);
+  } else {
+    /* Merge registers, set Changed only if different */
+    RegisterLine* target_line = GetRegisterLine(reg_table, next_insn);
+    RegType* target_regs = target_line->reg_types_;
+    MonitorEntries* work_mon_ents = work_line->monitor_entries_;
+    MonitorEntries* target_mon_ents = target_line->monitor_entries_;
+    bool changed = false;
+    unsigned int idx;
+
+    assert(target_regs != NULL);
+    if (target_mon_ents != NULL) {
+      /* Monitor stacks must be identical. */
+      if (target_line->monitor_stack_top_ != work_line->monitor_stack_top_) {
+        LOG(ERROR) << "VFY: mismatched stack depth "
+                   << target_line->monitor_stack_top_ << " vs. "
+                   << work_line->monitor_stack_top_ << " at 0x"
+                   << std::hex << next_insn << std::dec;
+        return false;
+      }
+      if (memcmp(target_line->monitor_stack_, work_line->monitor_stack_,
+                 target_line->monitor_stack_top_ * sizeof(uint32_t)) != 0) {
+         LOG(ERROR) << "VFY: mismatched monitor stacks at 0x" << std::hex
+                    << next_insn << std::dec;
+         return false;
+      }
+    }
+
+    for (idx = 0; idx < insn_reg_count_plus; idx++) {
+      target_regs[idx] = MergeTypes(target_regs[idx], work_regs[idx], &changed);
+
+      if (target_mon_ents != NULL) {
+        target_mon_ents[idx] = MergeMonitorEntries(target_mon_ents[idx],
+             work_mon_ents[idx], &changed);
+      }
+    }
+
+    if (changed) {
+      InsnSetChanged(insn_flags, next_insn, true);
+    }
+  }
+
+  return true;
+}
+
+bool DexVerifier::CanConvertTo1nr(RegType src_type, RegType check_type) {
+  static const char conv_tab[kRegType1nrEND - kRegType1nrSTART + 1]
+                            [kRegType1nrEND - kRegType1nrSTART + 1] =
+  {
+    /* chk: 0  1  Z  y  Y  h  H  c  i  b  B  s  S  C  I  F */
+    { /*0*/ 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+    { /*1*/ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+    { /*Z*/ 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 },
+    { /*y*/ 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
+    { /*Y*/ 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1 },
+    { /*h*/ 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1 },
+    { /*H*/ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1 },
+    { /*c*/ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1 },
+    { /*i*/ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1 },
+    { /*b*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0 },
+    { /*B*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0 },
+    { /*s*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 },
+    { /*S*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
+    { /*C*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
+    { /*I*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 },
+    { /*F*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
+  };
+
+  assert(check_type >= kRegType1nrSTART && check_type <= kRegType1nrEND);
+
+  if (src_type >= kRegType1nrSTART && src_type <= kRegType1nrEND)
+    return (bool) conv_tab[src_type - kRegType1nrSTART]
+                          [check_type - kRegType1nrSTART];
+
+  return false;
+}
+
+bool DexVerifier::CanConvertTo2(RegType src_type, RegType check_type) {
+  return ((src_type == kRegTypeConstLo || src_type == check_type) &&
+          (check_type == kRegTypeLongLo || check_type == kRegTypeDoubleLo));
+}
+
+DexVerifier::RegType DexVerifier::PrimitiveTypeToRegType(
+    Class::PrimitiveType prim_type) {
+  switch (prim_type) {
+    case Class::kPrimBoolean: return kRegTypeBoolean;
+    case Class::kPrimByte:    return kRegTypeByte;
+    case Class::kPrimShort:   return kRegTypeShort;
+    case Class::kPrimChar:    return kRegTypeChar;
+    case Class::kPrimInt:     return kRegTypeInteger;
+    case Class::kPrimLong:    return kRegTypeLongLo;
+    case Class::kPrimFloat:   return kRegTypeFloat;
+    case Class::kPrimDouble:  return kRegTypeDoubleLo;
+    case Class::kPrimVoid:
+    default: {
+      return kRegTypeUnknown;
+    }
+  }
+}
+
+DexVerifier::RegType DexVerifier::ConstTypeToRegType(RegType const_type) {
+  switch (const_type) {
+    case kRegTypeConstPosByte: return kRegTypePosByte;
+    case kRegTypeConstByte: return kRegTypeByte;
+    case kRegTypeConstPosShort: return kRegTypePosShort;
+    case kRegTypeConstShort: return kRegTypeShort;
+    case kRegTypeConstChar: return kRegTypeChar;
+    case kRegTypeConstInteger: return kRegTypeInteger;
+    default: {
+      return const_type;
+    }
+  }
+}
+
+char DexVerifier::DetermineCat1Const(int32_t value) {
+  if (value < -32768)
+    return kRegTypeConstInteger;
+  else if (value < -128)
+    return kRegTypeConstShort;
+  else if (value < 0)
+    return kRegTypeConstByte;
+  else if (value == 0)
+    return kRegTypeZero;
+  else if (value == 1)
+    return kRegTypeOne;
+  else if (value < 128)
+    return kRegTypeConstPosByte;
+  else if (value < 32768)
+    return kRegTypeConstPosShort;
+  else if (value < 65536)
+    return kRegTypeConstChar;
+  else
+    return kRegTypeConstInteger;
+}
+
+void DexVerifier::CheckFinalFieldAccess(const Method* method,
+    const Field* field, VerifyError* failure) {
+  if (!field->IsFinal())
+    return;
+
+  /* make sure we're in the same class */
+  if (method->GetDeclaringClass() != field->GetDeclaringClass()) {
+    LOG(ERROR) << "VFY: can't modify final field "
+               << field->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+               << "." << field->GetName()->ToModifiedUtf8();
+    *failure = VERIFY_ERROR_ACCESS_FIELD;
+    return;
+  }
+}
+
+void DexVerifier::CheckArrayIndexType(const Method* method, RegType reg_type,
+    VerifyError* failure) {
+  if (*failure == VERIFY_ERROR_NONE) {
+    /*
+     * The 1nr types are interchangeable at this level.  We could
+     * do something special if we can definitively identify it as a
+     * float, but there's no real value in doing so.
+     */
+    CheckTypeCategory(reg_type, kTypeCategory1nr, failure);
+    if (*failure != VERIFY_ERROR_NONE) {
+      LOG(ERROR) << "Invalid reg type for array index (" << reg_type << ")";
+    }
+  }
+}
+
+bool DexVerifier::CheckConstructorReturn(const Method* method,
+    const RegisterLine* register_line, const int insn_reg_count) {
+  const RegType* insn_regs = register_line->reg_types_;
+
+  if (!IsInitMethod(method))
+    return true;
+
+  RegType uninit_this = RegTypeFromUninitIndex(kUninitThisArgSlot);
+
+  for (int i = 0; i < insn_reg_count; i++) {
+    if (insn_regs[i] == uninit_this) {
+      LOG(ERROR) << "VFY: <init> returning without calling superclass init";
+      return false;
+    }
+  }
+  return true;
+}
+
+bool DexVerifier::CheckMoveException(const uint16_t* insns, int insn_idx) {
+  if ((insns[insn_idx] & 0xff) == Instruction::MOVE_EXCEPTION) {
+    LOG(ERROR) << "VFY: invalid use of move-exception";
+    return false;
+  }
+  return true;
+}
+
+void DexVerifier::CheckTypeCategory(RegType type, TypeCategory cat,
+    VerifyError* failure) {
+  switch (cat) {
+    case kTypeCategory1nr:
+      switch (type) {
+        case kRegTypeZero:
+        case kRegTypeOne:
+        case kRegTypeBoolean:
+        case kRegTypeConstPosByte:
+        case kRegTypeConstByte:
+        case kRegTypeConstPosShort:
+        case kRegTypeConstShort:
+        case kRegTypeConstChar:
+        case kRegTypeConstInteger:
+        case kRegTypePosByte:
+        case kRegTypeByte:
+        case kRegTypePosShort:
+        case kRegTypeShort:
+        case kRegTypeChar:
+        case kRegTypeInteger:
+        case kRegTypeFloat:
+          break;
+        default:
+          *failure = VERIFY_ERROR_GENERIC;
+          break;
+      }
+      break;
+    case kTypeCategory2:
+      switch (type) {
+        case kRegTypeConstLo:
+        case kRegTypeLongLo:
+        case kRegTypeDoubleLo:
+          break;
+        default:
+          *failure = VERIFY_ERROR_GENERIC;
+         break;
+      }
+      break;
+    case kTypeCategoryRef:
+      if (type != kRegTypeZero && !RegTypeIsReference(type))
+        *failure = VERIFY_ERROR_GENERIC;
+      break;
+    default:
+      assert(false);
+      *failure = VERIFY_ERROR_GENERIC;
+      break;
+  }
+}
+
+void DexVerifier::CheckWidePair(RegType type_l, RegType type_h,
+    VerifyError* failure) {
+  if ((type_h != type_l + 1))
+    *failure = VERIFY_ERROR_GENERIC;
+}
+
+void DexVerifier::CheckUnop(RegisterLine* register_line,
+    Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+    RegType src_type, VerifyError* failure) {
+  VerifyRegisterType(register_line, dec_insn->vB_, src_type, failure);
+  SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+bool DexVerifier::UpcastBooleanOp(RegisterLine* register_line, uint32_t reg1,
+    uint32_t reg2) {
+  RegType type1, type2;
+
+  type1 = GetRegisterType(register_line, reg1);
+  type2 = GetRegisterType(register_line, reg2);
+
+  if ((type1 == kRegTypeBoolean || type1 == kRegTypeZero || type1 == kRegTypeOne) &&
+      (type2 == kRegTypeBoolean || type2 == kRegTypeZero || type2 == kRegTypeOne)) {
+    return true;
+  }
+  return false;
+}
+
+void DexVerifier::CheckLitop(RegisterLine* register_line,
+    Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+    RegType src_type, bool check_boolean_op, VerifyError* failure) {
+  VerifyRegisterType(register_line, dec_insn->vB_, src_type, failure);
+
+  if ((*failure == VERIFY_ERROR_NONE) && check_boolean_op) {
+    assert(dst_type == kRegTypeInteger);
+
+    /* check vB with the call, then check the constant manually */
+    if (UpcastBooleanOp(register_line, dec_insn->vB_, dec_insn->vB_)
+        && (dec_insn->vC_ == 0 || dec_insn->vC_ == 1)) {
+      dst_type = kRegTypeBoolean;
+    }
+  }
+
+  SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+void DexVerifier::CheckBinop(RegisterLine* register_line,
+    Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+    RegType src_type1, RegType src_type2, bool check_boolean_op,
+    VerifyError* failure) {
+  VerifyRegisterType(register_line, dec_insn->vB_, src_type1, failure);
+  VerifyRegisterType(register_line, dec_insn->vC_, src_type2, failure);
+
+  if ((*failure == VERIFY_ERROR_NONE) && check_boolean_op) {
+    assert(dst_type == kRegTypeInteger);
+    if (UpcastBooleanOp(register_line, dec_insn->vB_, dec_insn->vC_))
+      dst_type = kRegTypeBoolean;
+  }
+
+  SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+void DexVerifier::CheckBinop2addr(RegisterLine* register_line,
+    Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+    RegType src_type1, RegType src_type2, bool check_boolean_op,
+    VerifyError* failure) {
+  VerifyRegisterType(register_line, dec_insn->vA_, src_type1, failure);
+  VerifyRegisterType(register_line, dec_insn->vB_, src_type2, failure);
+
+  if ((*failure == VERIFY_ERROR_NONE) && check_boolean_op) {
+    assert(dst_type == kRegTypeInteger);
+    if (UpcastBooleanOp(register_line, dec_insn->vA_, dec_insn->vB_))
+      dst_type = kRegTypeBoolean;
+  }
+
+  SetRegisterType(register_line, dec_insn->vA_, dst_type);
+}
+
+DexVerifier::RegType DexVerifier::AdjustForRightShift(
+    RegisterLine* register_line, int reg, unsigned int shift_count,
+    bool is_unsigned_shift, VerifyError* failure) {
+  RegType src_type = GetRegisterType(register_line, reg);
+  RegType new_type;
+
+  /* convert const derived types to their actual types */
+  src_type = ConstTypeToRegType(src_type);
+
+  /* no-op */
+  if (shift_count == 0)
+    return src_type;
+
+  /* safe defaults */
+  if (is_unsigned_shift)
+    new_type = kRegTypeInteger;
+  else
+    new_type = src_type;
+
+  if (shift_count >= 32) {
+    LOG(ERROR) << "Got unexpectedly large shift count " << shift_count;
+    /* fail? */
+    return new_type;
+  }
+
+  switch (src_type) {
+    case kRegTypeInteger:               /* 32-bit signed value */
+      if (is_unsigned_shift) {
+        if (shift_count > 24)
+          new_type = kRegTypePosByte;
+        else if (shift_count >= 16)
+          new_type = kRegTypeChar;
+      } else {
+        if (shift_count >= 24)
+          new_type = kRegTypeByte;
+        else if (shift_count >= 16)
+          new_type = kRegTypeShort;
+      }
+      break;
+    case kRegTypeShort:                 /* 16-bit signed value */
+      if (is_unsigned_shift) {
+        /* default (kRegTypeInteger) is correct */
+      } else {
+        if (shift_count >= 8)
+          new_type = kRegTypeByte;
+      }
+      break;
+    case kRegTypePosShort:              /* 15-bit unsigned value */
+      if (shift_count >= 8)
+        new_type = kRegTypePosByte;
+      break;
+    case kRegTypeChar:                  /* 16-bit unsigned value */
+      if (shift_count > 8)
+        new_type = kRegTypePosByte;
+      break;
+    case kRegTypeByte:                  /* 8-bit signed value */
+      /* defaults (u=kRegTypeInteger / s=src_type) are correct */
+      break;
+    case kRegTypePosByte:               /* 7-bit unsigned value */
+      /* always use new_type=src_type */
+      new_type = src_type;
+      break;
+    case kRegTypeZero:                  /* 1-bit unsigned value */
+    case kRegTypeOne:
+    case kRegTypeBoolean:
+      /* unnecessary? */
+      new_type = kRegTypeZero;
+      break;
+    default:
+      /* long, double, references; shouldn't be here! */
+      assert(false);
+      break;
+  }
+
+  return new_type;
+}
+
+void DexVerifier::VerifyFilledNewArrayRegs(const Method* method,
+    RegisterLine* register_line,
+    const Instruction::DecodedInstruction* dec_insn, Class* res_class,
+    bool is_range, VerifyError* failure) {
+  uint32_t arg_count = dec_insn->vA_;
+  RegType expected_type;
+  Class::PrimitiveType elem_type;
+  unsigned int ui;
+
+  assert(res_class->IsArrayClass());
+  elem_type = res_class->GetComponentType()->GetPrimitiveType();
+  if (elem_type == Class::kPrimNot) {
+    expected_type = RegTypeFromClass(res_class->GetComponentType());
+  } else {
+    expected_type = PrimitiveTypeToRegType(elem_type);
+  }
+
+  /*
+   * Verify each register.  If "arg_count" is bad, VerifyRegisterType()
+   * will run off the end of the list and fail.  It's legal, if silly,
+   * for arg_count to be zero.
+   */
+  for (ui = 0; ui < arg_count; ui++) {
+    uint32_t get_reg;
+
+    if (is_range)
+      get_reg = dec_insn->vC_ + ui;
+    else
+      get_reg = dec_insn->arg_[ui];
+
+    VerifyRegisterType(register_line, get_reg, expected_type, failure);
+    if (*failure != VERIFY_ERROR_NONE) {
+      LOG(ERROR) << "VFY: filled-new-array arg " << ui << "(" << get_reg
+                 << ") not valid";
+      return;
+    }
+  }
+}
+
+bool DexVerifier::IsCorrectInvokeKind(MethodType method_type,
+    Method* res_method) {
+  switch (method_type) {
+    case METHOD_DIRECT:
+      return res_method->IsDirect();
+    case METHOD_STATIC:
+      return res_method->IsStatic();
+    case METHOD_VIRTUAL:
+    case METHOD_INTERFACE:
+      return !res_method->IsDirect();
+    default:
+      return false;
+  }
+}
+
+Method* DexVerifier::VerifyInvocationArgs(VerifierData* vdata,
+    RegisterLine* register_line, const int insn_reg_count,
+    const Instruction::DecodedInstruction* dec_insn, MethodType method_type,
+    bool is_range, bool is_super, VerifyError* failure) {
+  Method* method = vdata->method_;
+  const DexFile* dex_file = vdata->dex_file_;
+  const DexFile::CodeItem* code_item = vdata->code_item_;
+  UninitInstanceMap* uninit_map = vdata->uninit_map_;
+  ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+  DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache();
+  const ClassLoader* class_loader =
+      method->GetDeclaringClass()->GetClassLoader();
+
+  Method* res_method;
+  std::string sig;
+  size_t sig_offset;
+  int expected_args;
+  int actual_args;
+
+  /*
+   * Resolve the method.  This could be an abstract or concrete method
+   * depending on what sort of call we're making.
+   */
+  res_method = class_linker->ResolveMethod(*dex_file, dec_insn->vB_, dex_cache,
+      class_loader, (method_type == METHOD_DIRECT || method_type == METHOD_STATIC));
+
+  /* Scan all implemented interfaces for the method */
+  if (method_type == METHOD_INTERFACE && res_method == NULL) {
+
+  }
+
+  if (res_method == NULL) {
+    /* failed; print a meaningful failure message */
+    //const DexFile::MethodId method_id = dex_file->GetMethodId(dec_insn->vB_);
+    //const char* method_name = dex_file->dexStringById(method_id.name_idx_);
+    //const char* class_name = dex_file->dexStringByTypeIdx(method_id.class_idx_);
+
+    //LOG(ERROR) << "VFY: unable to resolve " << (int) method_type << " method "
+               //<< dec_insn->vB_ << ": " << class_name << "." << method_name;
+    LOG(ERROR) << "VFY: unable to resolve called method";
+    *failure = VERIFY_ERROR_NO_METHOD;
+    return NULL;
+  }
+  Class* res_class = res_method->GetDeclaringClass();
+
+  /*
+   * Only time you can explicitly call a method starting with '<' is when
+   * making a "direct" invocation on "<init>".  There are additional
+   * restrictions but we don't enforce them here.
+   */
+  if (res_method->GetName()->Equals("<init>")) {
+    if (method_type != METHOD_DIRECT || !IsInitMethod(res_method)) {
+      LOG(ERROR) << "VFY: invalid call to "
+                 << res_class->GetDescriptor()->ToModifiedUtf8()
+                 << "." << res_method->GetName();
+      goto bad_sig;
+    }
+  }
+
+  /*
+   * See if the method type implied by the invoke instruction matches the
+   * access flags for the target method.
+   */
+  if (!IsCorrectInvokeKind(method_type, res_method)) {
+    LOG(ERROR) << "VFY: invoke type does not match method type of "
+               << res_class->GetDescriptor()->ToModifiedUtf8()
+               << "." << res_method->GetName()->ToModifiedUtf8();
+
+    *failure = VERIFY_ERROR_GENERIC;
+    return NULL;
+  }
+
+  /*
+   * If we're using invoke-super(method), make sure that the executing
+   * method's class' superclass has a vtable entry for the target method.
+   */
+  if (is_super) {
+    assert(method_type == METHOD_VIRTUAL);
+    Class* super = method->GetDeclaringClass()->GetSuperClass();
+    if (super == NULL || res_method->GetMethodIndex() > super->GetVTable()->GetLength()) {
+      if (super == NULL) {
+        LOG(ERROR) << "VFY: invalid invoke-super from "
+                   << method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+                   << "." << method->GetName()->ToModifiedUtf8() << " to super -."
+                   << res_method->GetName()->ToModifiedUtf8()
+                   << " " << res_method->GetSignature()->ToModifiedUtf8();
+      } else {
+        LOG(ERROR) << "VFY: invalid invoke-super from "
+                   << method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+                   << "." << method->GetName()->ToModifiedUtf8() << " to super "
+                   << super->GetDescriptor()->ToModifiedUtf8()
+                   << "." << res_method->GetName()->ToModifiedUtf8()
+                   << " " << res_method->GetSignature()->ToModifiedUtf8();
+      }
+      *failure = VERIFY_ERROR_NO_METHOD;
+      return NULL;
+    }
+  }
+
+  /*
+   * We use vAA as our expected arg count, rather than res_method->insSize,
+   * because we need to match the call to the signature.  Also, we might
+   * might be calling through an abstract method definition (which doesn't
+   * have register count values).
+   */
+  expected_args = dec_insn->vA_;
+  actual_args = 0;
+
+  /* caught by static verifier */
+  assert(is_range || expected_args <= 5);
+
+  if (expected_args > code_item->outs_size_) {
+    LOG(ERROR) << "VFY: invalid arg count (" << expected_args
+               << ") exceeds outsSize (" << code_item->outs_size_ << ")";
+    *failure = VERIFY_ERROR_GENERIC;
+    return NULL;
+  }
+
+  sig = res_method->GetSignature()->ToModifiedUtf8();
+  if (sig[0] != '(') {
+    LOG(ERROR) << "VFY: descriptor doesn't start with '(': " << sig;
+    goto bad_sig;
+  }
+
+  /*
+   * Check the "this" argument, which must be an instance of the class
+   * that declared the method.  For an interface class, we don't do the
+   * full interface merge, so we can't do a rigorous check here (which
+   * is okay since we have to do it at runtime).
+   */
+  if (!res_method->IsStatic()) {
+    Class* actual_this_ref;
+    RegType actual_arg_type;
+
+    actual_arg_type = GetInvocationThis(register_line, dec_insn, failure);
+    if (*failure != VERIFY_ERROR_NONE)
+      return NULL;
+
+    if (RegTypeIsUninitReference(actual_arg_type) &&
+        !res_method->GetName()->Equals("<init>")) {
+      LOG(ERROR) << "VFY: 'this' arg must be initialized";
+      *failure = VERIFY_ERROR_GENERIC;
+      return NULL;
+    }
+    if (method_type != METHOD_INTERFACE && actual_arg_type != kRegTypeZero) {
+      actual_this_ref = RegTypeReferenceToClass(actual_arg_type, uninit_map);
+      if (!res_method->GetDeclaringClass()->IsAssignableFrom(actual_this_ref)) {
+        LOG(ERROR) << "VFY: 'this' arg '"
+                   << actual_this_ref->GetDescriptor()->ToModifiedUtf8()
+                   << "' not instance of '"
+                   << res_method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+                   << "'";
+        *failure = VERIFY_ERROR_GENERIC;
+        return NULL;
+      }
+    }
+    actual_args++;
+  }
+
+  /*
+   * Process the target method's signature.  This signature may or may not
+   * have been verified, so we can't assume it's properly formed.
+   */
+  for (sig_offset = 1; sig_offset < sig.size(); sig_offset++) {
+  //while (*sig != '\0' && *sig != ')') {
+    if (sig[sig_offset] == ')')
+      break;
+
+    if (actual_args >= expected_args) {
+      LOG(ERROR) << "VFY: expected " << expected_args << " args, found more ("
+                 << sig.substr(sig_offset) << ")";
+      goto bad_sig;
+    }
+
+    uint32_t get_reg;
+    if (is_range)
+      get_reg = dec_insn->vC_ + actual_args;
+    else
+      get_reg = dec_insn->arg_[actual_args];
+
+    switch (sig[sig_offset]) {
+      case 'L':
+        {
+          Class* klass = LookupSignatureClass(method, sig.substr(sig_offset),
+              failure);
+          if (*failure != VERIFY_ERROR_NONE)
+            goto bad_sig;
+          VerifyRegisterType(register_line, get_reg, RegTypeFromClass(klass),
+              failure);
+          if (*failure != VERIFY_ERROR_NONE) {
+            LOG(ERROR) << "VFY: bad arg " << actual_args << " (into "
+                       << klass->GetDescriptor()->ToModifiedUtf8() << ")";
+            goto bad_sig;
+          }
+          sig_offset += sig.substr(sig_offset).find(';');
+        }
+        actual_args++;
+        break;
+      case '[':
+        {
+          Class* klass = LookupSignatureArrayClass(method,
+              sig.substr(sig_offset), failure);
+          if (*failure != VERIFY_ERROR_NONE)
+            goto bad_sig;
+          VerifyRegisterType(register_line, get_reg, RegTypeFromClass(klass),
+              failure);
+          if (*failure != VERIFY_ERROR_NONE) {
+            LOG(ERROR) << "VFY: bad arg " << actual_args << " (into "
+                       << klass->GetDescriptor()->ToModifiedUtf8() << ")";
+            goto bad_sig;
+          }
+          while (sig[sig_offset] == '[')
+            sig_offset++;
+          if (sig[sig_offset] == 'L')
+            sig_offset += sig.substr(sig_offset).find(';');
+        }
+        actual_args++;
+        break;
+      case 'Z':
+        VerifyRegisterType(register_line, get_reg, kRegTypeBoolean, failure);
+        actual_args++;
+        break;
+      case 'C':
+        VerifyRegisterType(register_line, get_reg, kRegTypeChar, failure);
+        actual_args++;
+        break;
+      case 'B':
+        VerifyRegisterType(register_line, get_reg, kRegTypeByte, failure);
+        actual_args++;
+        break;
+      case 'I':
+        VerifyRegisterType(register_line, get_reg, kRegTypeInteger, failure);
+        actual_args++;
+        break;
+      case 'S':
+        VerifyRegisterType(register_line, get_reg, kRegTypeShort, failure);
+        actual_args++;
+        break;
+      case 'F':
+        VerifyRegisterType(register_line, get_reg, kRegTypeFloat, failure);
+        actual_args++;
+        break;
+      case 'D':
+        VerifyRegisterType(register_line, get_reg, kRegTypeDoubleLo, failure);
+        actual_args += 2;
+        break;
+      case 'J':
+        VerifyRegisterType(register_line, get_reg, kRegTypeLongLo, failure);
+        actual_args += 2;
+        break;
+      default:
+        LOG(ERROR) << "VFY: invocation target: bad signature type char '"
+                   << sig << "'";
+        goto bad_sig;
+    }
+  }
+  if (sig[sig_offset] != ')') {
+    LOG(ERROR) << "VFY: invocation target: bad signature '"
+               << res_method->GetSignature()->ToModifiedUtf8() << "'";
+    goto bad_sig;
+  }
+
+  if (actual_args != expected_args) {
+    LOG(ERROR) << "VFY: expected " << expected_args << " args, found "
+               << actual_args;
+    goto bad_sig;
+  }
+
+  return res_method;
+
+bad_sig:
+  if (res_method != NULL) {
+    LOG(ERROR) << "VFY:  rejecting call to "
+               << res_method->GetDeclaringClass()->GetDescriptor()->ToModifiedUtf8()
+               << "." << res_method->GetName()->ToModifiedUtf8() << " "
+               << res_method->GetSignature()->ToModifiedUtf8();
+  }
+
+  if (*failure == VERIFY_ERROR_NONE)
+    *failure = VERIFY_ERROR_GENERIC;
+  return NULL;
+}
 
 }  // namespace art
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index e909b35..b6fbd46 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -10,31 +10,1488 @@
 
 namespace art {
 
-class DexVerify {
+#define kMaxMonitorStackDepth   (sizeof(MonitorEntries) * 8)
+
+/*
+ * Set this to enable dead code scanning.  This is not required, but it's
+ * very useful when testing changes to the verifier (to make sure we're not
+ * skipping over stuff). The only reason not to do it is that it slightly
+ * increases the time required to perform verification.
+ */
+#ifndef NDEBUG
+# define DEAD_CODE_SCAN  true
+#else
+# define DEAD_CODE_SCAN  false
+#endif
+
+/*
+ * We need an extra "pseudo register" to hold the return type briefly.  It
+ * can be category 1 or 2, so we need two slots.
+ */
+#define kExtraRegs  2
+#define RESULT_REGISTER(_insnRegCount)  (_insnRegCount)
+
+class DexVerifier {
  public:
-  enum {
-    kInsnFlagWidthMask = 0x0000ffff,
-    kInsnFlagInTry = (1 << 16),
+  /*
+   * RegType holds information about the type of data held in a register.
+   * For most types it's a simple enum.  For reference types it holds a
+   * pointer to the ClassObject, and for uninitialized references it holds
+   * an index into the UninitInstanceMap.
+   */
+  typedef uint32_t RegType;
+
+  /*
+   * A bit vector indicating which entries in the monitor stack are
+   * associated with this register.  The low bit corresponds to the stack's
+   * bottom-most entry.
+   */
+  typedef uint32_t MonitorEntries;
+
+  /*
+   * InsnFlags is a 32-bit integer with the following layout:
+   *   0-15  instruction length (or 0 if this address doesn't hold an opcode)
+   *  16-31  single bit flags:
+   *    InTry: in "try" block; exceptions thrown here may be caught locally
+   *    BranchTarget: other instructions can branch to this instruction
+   *    GcPoint: this instruction is a GC safe point
+   *    Visited: verifier has examined this instruction at least once
+   *    Changed: set/cleared as bytecode verifier runs
+   */
+  typedef uint32_t InsnFlags;
+
+  enum InsnFlag {
+    kInsnFlagWidthMask    = 0x0000ffff,
+    kInsnFlagInTry        = (1 << 16),
     kInsnFlagBranchTarget = (1 << 17),
-    kInsnFlagGcPoint = (1 << 18),
-    kInsnFlagVisited = (1 << 30),
-    kInsnFlagChanged = (1 << 31),
+    kInsnFlagGcPoint      = (1 << 18),
+    kInsnFlagVisited      = (1 << 30),
+    kInsnFlagChanged      = (1 << 31),
   };
 
+  /*
+   * "Direct" and "virtual" methods are stored independently.  The type of call
+   * used to invoke the method determines which list we search, and whether
+   * we travel up into superclasses.
+   *
+   * (<clinit>, <init>, and methods declared "private" or "static" are stored
+   * in the "direct" list.  All others are stored in the "virtual" list.)
+   */
+  enum MethodType {
+    METHOD_UNKNOWN  = 0,
+    METHOD_DIRECT,      // <init>, private
+    METHOD_STATIC,      // static
+    METHOD_VIRTUAL,     // virtual, super
+    METHOD_INTERFACE    // interface
+  };
+
+  /*
+   * We don't need to store the register data for many instructions, because
+   * we either only need it at branch points (for verification) or GC points
+   * and branches (for verification + type-precise register analysis).
+   */
+  enum RegisterTrackingMode {
+    kTrackRegsBranches,
+    kTrackRegsGcPoints,
+    kTrackRegsAll,
+  };
+
+  /*
+   * Enumeration for register type values.  The "hi" piece of a 64-bit value
+   * MUST immediately follow the "lo" piece in the enumeration, so we can check
+   * that hi==lo+1.
+   *
+   * Assignment of constants:
+   *   [-MAXINT,-32768)   : integer
+   *   [-32768,-128)      : short
+   *   [-128,0)           : byte
+   *   0                  : zero
+   *   1                  : one
+   *   [2,128)            : posbyte
+   *   [128,32768)        : posshort
+   *   [32768,65536)      : char
+   *   [65536,MAXINT]     : integer
+   *
+   * Allowed "implicit" widening conversions:
+   *   zero -> boolean, posbyte, byte, posshort, short, char, integer, ref (null)
+   *   one -> boolean, posbyte, byte, posshort, short, char, integer
+   *   boolean -> posbyte, byte, posshort, short, char, integer
+   *   posbyte -> posshort, short, integer, char
+   *   byte -> short, integer
+   *   posshort -> integer, char
+   *   short -> integer
+   *   char -> integer
+   *
+   * In addition, all of the above can convert to "float".
+   *
+   * We're more careful with integer values than the spec requires.  The
+   * motivation is to restrict byte/char/short to the correct range of values.
+   * For example, if a method takes a byte argument, we don't want to allow
+   * the code to load the constant "1024" and pass it in.
+   */
+  enum {
+    kRegTypeUnknown = 0,    /* initial state; use value=0 so calloc works */
+    kRegTypeUninit = 1,     /* MUST be odd to distinguish from pointer */
+    kRegTypeConflict,       /* merge clash makes this reg's type unknowable */
+
+    /*
+     * Category-1nr types.  The order of these is chiseled into a couple
+     * of tables, so don't add, remove, or reorder if you can avoid it.
+     */
+#define kRegType1nrSTART    kRegTypeZero
+    kRegTypeZero,           /* 32-bit 0, could be Boolean, Int, Float, or Ref */
+    kRegTypeOne,            /* 32-bit 1, could be Boolean, Int, Float */
+    kRegTypeBoolean,        /* must be 0 or 1 */
+    kRegTypeConstPosByte,   /* const derived byte, known positive */
+    kRegTypeConstByte,      /* const derived byte */
+    kRegTypeConstPosShort,  /* const derived short, known positive */
+    kRegTypeConstShort,     /* const derived short */
+    kRegTypeConstChar,      /* const derived char */
+    kRegTypeConstInteger,   /* const derived integer */
+    kRegTypePosByte,        /* byte, known positive (can become char) */
+    kRegTypeByte,
+    kRegTypePosShort,       /* short, known positive (can become char) */
+    kRegTypeShort,
+    kRegTypeChar,
+    kRegTypeInteger,
+    kRegTypeFloat,
+#define kRegType1nrEND      kRegTypeFloat
+    kRegTypeConstLo,        /* const derived wide, lower half */
+    kRegTypeConstHi,        /* const derived wide, upper half */
+    kRegTypeLongLo,         /* lower-numbered register; endian-independent */
+    kRegTypeLongHi,
+    kRegTypeDoubleLo,
+    kRegTypeDoubleHi,
+
+    /*
+     * Enumeration max; this is used with "full" (32-bit) RegType values.
+     *
+     * Anything larger than this is a ClassObject or uninit ref.  Mask off
+     * all but the low 8 bits; if you're left with kRegTypeUninit, pull
+     * the uninit index out of the high 24.  Because kRegTypeUninit has an
+     * odd value, there is no risk of a particular ClassObject pointer bit
+     * pattern being confused for it (assuming our class object allocator
+     * uses word alignment).
+     */
+    kRegTypeMAX
+  };
+#define kRegTypeUninitMask  0xff
+#define kRegTypeUninitShift 8
+
+  /*
+   * Register type categories, for type checking.
+   *
+   * The spec says category 1 includes boolean, byte, char, short, int, float,
+   * reference, and returnAddress.  Category 2 includes long and double.
+   *
+   * We treat object references separately, so we have "category1nr".  We
+   * don't support jsr/ret, so there is no "returnAddress" type.
+   */
+  enum TypeCategory {
+    kTypeCategoryUnknown = 0,
+    kTypeCategory1nr = 1,         // boolean, byte, char, short, int, float
+    kTypeCategory2 = 2,           // long, double
+    kTypeCategoryRef = 3,         // object reference
+  };
+
+  /* An enumeration of problems that can turn up during verification. */
+  enum VerifyError {
+    VERIFY_ERROR_NONE = 0,      /* no error; must be zero */
+    VERIFY_ERROR_GENERIC,       /* VerifyError */
+
+    VERIFY_ERROR_NO_CLASS,      /* NoClassDefFoundError */
+    VERIFY_ERROR_NO_FIELD,      /* NoSuchFieldError */
+    VERIFY_ERROR_NO_METHOD,     /* NoSuchMethodError */
+    VERIFY_ERROR_ACCESS_CLASS,  /* IllegalAccessError */
+    VERIFY_ERROR_ACCESS_FIELD,  /* IllegalAccessError */
+    VERIFY_ERROR_ACCESS_METHOD, /* IllegalAccessError */
+    VERIFY_ERROR_CLASS_CHANGE,  /* IncompatibleClassChangeError */
+    VERIFY_ERROR_INSTANTIATION, /* InstantiationError */
+  };
+
+  /*
+   * Identifies the type of reference in the instruction that generated the
+   * verify error (e.g. VERIFY_ERROR_ACCESS_CLASS could come from a method,
+   * field, or class reference).
+   *
+   * This must fit in two bits.
+   */
+  enum VerifyErrorRefType {
+    VERIFY_ERROR_REF_CLASS  = 0,
+    VERIFY_ERROR_REF_FIELD  = 1,
+    VERIFY_ERROR_REF_METHOD = 2,
+  };
+#define kVerifyErrorRefTypeShift 6
+
+  /*
+   * During verification, we associate one of these with every "interesting"
+   * instruction.  We track the status of all registers, and (if the method
+   * has any monitor-enter instructions) maintain a stack of entered monitors
+   * (identified by code unit offset).
+   *
+   * If live-precise register maps are enabled, the "liveRegs" vector will
+   * be populated.  Unlike the other lists of registers here, we do not
+   * track the liveness of the method result register (which is not visible
+   * to the GC).
+   */
+  struct RegisterLine {
+    RegType*        reg_types_;
+    MonitorEntries* monitor_entries_;
+    uint32_t*       monitor_stack_;
+    uint32_t        monitor_stack_top_;
+
+    /* Default constructor. */
+    RegisterLine() {
+      reg_types_ = NULL;
+      monitor_entries_ = NULL;
+      monitor_stack_ = NULL;
+      monitor_stack_top_ = 0;
+    }
+
+    /* Default destructor. */
+    ~RegisterLine() {
+      delete reg_types_;
+      delete monitor_entries_;
+      delete monitor_stack_;
+    }
+
+    /* Allocate space for the fields. */
+    void Alloc(size_t size, bool track_monitors) {
+      reg_types_ = new RegType[size]();
+      if (track_monitors) {
+        monitor_entries_ = new MonitorEntries[size];
+        monitor_stack_ = new uint32_t[kMaxMonitorStackDepth];
+      }
+    }
+  };
+
+  /* Big fat collection of register data. */
+  struct RegisterTable {
+    /*
+     * Array of RegisterLine structs, one per address in the method.  We only
+     * set the pointers for certain addresses, based on instruction widths
+     * and what we're trying to accomplish.
+     */
+    RegisterLine* register_lines_;
+
+    /*
+     * Number of registers we track for each instruction.  This is equal
+     * to the method's declared "registersSize" plus kExtraRegs (2).
+     */
+    size_t      insn_reg_count_plus_;
+
+    /* Storage for a register line we're currently working on. */
+    RegisterLine work_line_;
+
+    /* Storage for a register line we're saving for later. */
+    RegisterLine saved_line_;
+
+    /* Default constructor. */
+    RegisterTable() {
+      register_lines_ = NULL;
+      insn_reg_count_plus_ = 0;
+    }
+
+    /* Default destructor. */
+    ~RegisterTable() {
+      delete [] register_lines_;
+    }
+  };
+
+  /* Entries in the UninitInstanceMap. */
+  struct UninitInstanceMapEntry {
+    /* Code offset, or -1 for method arg ("this"). */
+    int addr_;
+
+    /* Class created at this address. */
+    Class* klass_;
+  };
+
+  /*
+   * Table that maps uninitialized instances to classes, based on the
+   * address of the new-instance instruction.  One per method.
+   */
+  struct UninitInstanceMap {
+    int num_entries_;
+    UninitInstanceMapEntry* map_;
+
+    /* Basic constructor */
+    UninitInstanceMap(int num_entries) {
+      num_entries_ = num_entries;
+      map_ = new UninitInstanceMapEntry[num_entries]();
+    }
+
+    /* Default destructor */
+    ~UninitInstanceMap() {
+      delete map_;
+    }
+  };
+  #define kUninitThisArgAddr  (-1)
+  #define kUninitThisArgSlot  0
+
+  /* Various bits of data used by the verifier and register map generator. */
+  struct VerifierData {
+    /* The method we're working on. */
+    Method* method_;
+
+    /* The dex file containing the method. */
+    const DexFile* dex_file_;
+
+    /* The code item containing the code for the method. */
+    const DexFile::CodeItem* code_item_;
+
+    /* Instruction widths and flags, one entry per code unit. */
+    InsnFlags* insn_flags_;
+
+    /*
+     * Uninitialized instance map, used for tracking the movement of
+     * objects that have been allocated but not initialized.
+     */
+    UninitInstanceMap* uninit_map_;
+
+    /*
+     * Array of RegisterLine structs, one entry per code unit.  We only need
+     * entries for code units that hold the start of an "interesting"
+     * instruction.  For register map generation, we're only interested
+     * in GC points.
+     */
+    RegisterLine* register_lines_;
+
+    /* The number of occurrences of specific opcodes. */
+    size_t new_instance_count_;
+    size_t monitor_enter_count_;
+
+    /* Basic constructor. */
+    VerifierData(Method* method, const DexFile* dex_file,
+        const DexFile::CodeItem* code_item)
+        : method_(method), dex_file_(dex_file), code_item_(code_item),
+          insn_flags_(NULL), uninit_map_(NULL), register_lines_(NULL),
+          new_instance_count_(0), monitor_enter_count_(0) { }
+  };
+
+  /*
+   * Merge result table for primitive values.  The table is symmetric along
+   * the diagonal.
+   *
+   * Note that 32-bit int/float do not merge into 64-bit long/double.  This
+   * is a register merge, not a widening conversion.  Only the "implicit"
+   * widening within a category, e.g. byte to short, is allowed.
+   *
+   * Dalvik does not draw a distinction between int and float, but we enforce
+   * that once a value is used as int, it can't be used as float, and vice
+   * versa. We do not allow free exchange between 32-bit int/float and 64-bit
+   * long/double.
+   *
+   * Note that Uninit+Uninit=Uninit.  This holds true because we only
+   * use this when the RegType value is exactly equal to kRegTypeUninit, which
+   * can only happen for the zeroeth entry in the table.
+   *
+   * "Unknown" never merges with anything known.  The only time a register
+   * transitions from "unknown" to "known" is when we're executing code
+   * for the first time, and we handle that with a simple copy.
+   */
+  static const char merge_table_[kRegTypeMAX][kRegTypeMAX];
+
+  /*
+   * Returns "true" if the flags indicate that this address holds the start
+   * of an instruction.
+   */
+  static inline bool InsnIsOpcode(const InsnFlags insn_flags[], int addr) {
+    return (insn_flags[addr] & kInsnFlagWidthMask) != 0;
+  }
+
+  /* Extract the unsigned 16-bit instruction width from "flags". */
+  static inline int InsnGetWidth(const InsnFlags insn_flags[], int addr) {
+    return insn_flags[addr] & kInsnFlagWidthMask;
+  }
+
+  /* Utilities to check and set kInsnFlagChanged. */
+  static inline bool InsnIsChanged(const InsnFlags insn_flags[], int addr) {
+    return (insn_flags[addr] & kInsnFlagChanged) != 0;
+  }
+  static inline void InsnSetChanged(InsnFlags insn_flags[], int addr,
+      bool changed) {
+    if (changed)
+      insn_flags[addr] |= kInsnFlagChanged;
+    else
+      insn_flags[addr] &= ~kInsnFlagChanged;
+  }
+
+  /* Utilities to check and set kInsnFlagVisited. */
+  static inline bool InsnIsVisited(const InsnFlags insn_flags[], int addr) {
+      return (insn_flags[addr] & kInsnFlagVisited) != 0;
+  }
+  static inline void InsnSetVisited(InsnFlags insn_flags[], int addr,
+      bool visited) {
+    if (visited)
+      insn_flags[addr] |= kInsnFlagVisited;
+    else
+      insn_flags[addr] &= ~kInsnFlagVisited;
+  }
+
+  static inline bool InsnIsVisitedOrChanged(const InsnFlags insn_flags[],
+      int addr) {
+    return (insn_flags[addr] & (kInsnFlagVisited |
+                                kInsnFlagChanged)) != 0;
+  }
+
+  /* Utilities to check and set kInsnFlagInTry. */
+  static inline bool InsnIsInTry(const InsnFlags insn_flags[], int addr) {
+    return (insn_flags[addr] & kInsnFlagInTry) != 0;
+  }
+  static inline void InsnSetInTry(InsnFlags insn_flags[], int addr) {
+    insn_flags[addr] |= kInsnFlagInTry;
+  }
+
+  /* Utilities to check and set kInsnFlagBranchTarget. */
+  static inline bool InsnIsBranchTarget(const InsnFlags insn_flags[], int addr)
+  {
+    return (insn_flags[addr] & kInsnFlagBranchTarget) != 0;
+  }
+  static inline void InsnSetBranchTarget(InsnFlags insn_flags[], int addr) {
+    insn_flags[addr] |= kInsnFlagBranchTarget;
+  }
+
+  /* Utilities to check and set kInsnFlagGcPoint. */
+  static inline bool InsnIsGcPoint(const InsnFlags insn_flags[], int addr) {
+    return (insn_flags[addr] & kInsnFlagGcPoint) != 0;
+  }
+  static inline void InsnSetGcPoint(InsnFlags insn_flags[], int addr) {
+    insn_flags[addr] |= kInsnFlagGcPoint;
+  }
+
+  /* Get the class object at the specified index. */
+  static inline Class* GetUninitInstance(const UninitInstanceMap* uninit_map,
+      int idx) {
+    assert(idx >= 0 && idx < uninit_map->num_entries_);
+    return uninit_map->map_[idx].klass_;
+  }
+
+  /* Determine if "type" is actually an object reference (init/uninit/zero) */
+  static inline bool RegTypeIsReference(RegType type) {
+    return (type > kRegTypeMAX || type == kRegTypeUninit ||
+            type == kRegTypeZero);
+  }
+
+  /* Determine if "type" is an uninitialized object reference */
+  static inline bool RegTypeIsUninitReference(RegType type) {
+    return ((type & kRegTypeUninitMask) == kRegTypeUninit);
+  }
+
+  /*
+   * Convert the initialized reference "type" to a Class pointer
+   * (does not expect uninit ref types or "zero").
+   */
+  static Class* RegTypeInitializedReferenceToClass(RegType type) {
+    assert(RegTypeIsReference(type) && type != kRegTypeZero);
+    if ((type & 0x01) == 0) {
+      return (Class*) type;
+    } else {
+      LOG(ERROR) << "VFY: attempted to use uninitialized reference";
+      return NULL;
+    }
+  }
+
+  /* Extract the index into the uninitialized instance map table. */
+  static inline int RegTypeToUninitIndex(RegType type) {
+    assert(RegTypeIsUninitReference(type));
+    return (type & ~kRegTypeUninitMask) >> kRegTypeUninitShift;
+  }
+
+  /* Convert the reference "type" to a Class pointer. */
+  static Class* RegTypeReferenceToClass(RegType type,
+      const UninitInstanceMap* uninit_map) {
+    assert(RegTypeIsReference(type) && type != kRegTypeZero);
+    if (RegTypeIsUninitReference(type)) {
+      assert(uninit_map != NULL);
+      return GetUninitInstance(uninit_map, RegTypeToUninitIndex(type));
+    } else {
+        return (Class*) type;
+    }
+  }
+
+  /* Convert the ClassObject pointer to an (initialized) register type. */
+  static inline RegType RegTypeFromClass(Class* klass) {
+    return (uint32_t) klass;
+  }
+
+  /* Return the RegType for the uninitialized reference in slot "uidx". */
+  static inline RegType RegTypeFromUninitIndex(int uidx) {
+    return (uint32_t) (kRegTypeUninit | (uidx << kRegTypeUninitShift));
+  }
+
+  /* Verify a class. Returns "true" on success. */
   static bool VerifyClass(Class* klass);
 
  private:
+  /*
+   * Perform verification on a single method.
+   *
+   * We do this in three passes:
+   *  (1) Walk through all code units, determining instruction locations,
+   *      widths, and other characteristics.
+   *  (2) Walk through all code units, performing static checks on
+   *      operands.
+   *  (3) Iterate through the method, checking type safety and looking
+   *      for code flow problems.
+   *
+   * Some checks may be bypassed depending on the verification mode.  We can't
+   * turn this stuff off completely if we want to do "exact" GC.
+   *
+   * Confirmed here:
+   * - code array must not be empty
+   * Confirmed by ComputeWidthsAndCountOps():
+   * - opcode of first instruction begins at index 0
+   * - only documented instructions may appear
+   * - each instruction follows the last
+   * - last byte of last instruction is at (code_length-1)
+   */
   static bool VerifyMethod(Method* method);
-  static bool VerifyInstructions(const DexFile* dex_file,
-                                 const DexFile::CodeItem* code_item,
-                                 uint32_t insn_flags[]);
-  static bool VerifyInstruction(const DexFile* dex_file,
-                                const Instruction* inst,
-                                uint32_t code_offset,
-                                const DexFile::CodeItem* code_item,
-                                uint32_t insn_flags[]);
 
-  DISALLOW_COPY_AND_ASSIGN(DexVerify);
+  /*
+   * Perform static verification on all instructions in a method.
+   *
+   * Walks through instructions in a method calling VerifyInstruction on each.
+   */
+  static bool VerifyInstructions(VerifierData* vdata);
+
+  /*
+   * Perform static verification on an instruction.
+   *
+   * As a side effect, this sets the "branch target" flags in InsnFlags.
+   *
+   * "(CF)" items are handled during code-flow analysis.
+   *
+   * v3 4.10.1
+   * - target of each jump and branch instruction must be valid
+   * - targets of switch statements must be valid
+   * - operands referencing constant pool entries must be valid
+   * - (CF) operands of getfield, putfield, getstatic, putstatic must be valid
+   * - (CF) operands of method invocation instructions must be valid
+   * - (CF) only invoke-direct can call a method starting with '<'
+   * - (CF) <clinit> must never be called explicitly
+   * - operands of instanceof, checkcast, new (and variants) must be valid
+   * - new-array[-type] limited to 255 dimensions
+   * - can't use "new" on an array class
+   * - (?) limit dimensions in multi-array creation
+   * - local variable load/store register values must be in valid range
+   *
+   * v3 4.11.1.2
+   * - branches must be within the bounds of the code array
+   * - targets of all control-flow instructions are the start of an instruction
+   * - register accesses fall within range of allocated registers
+   * - (N/A) access to constant pool must be of appropriate type
+   * - code does not end in the middle of an instruction
+   * - execution cannot fall off the end of the code
+   * - (earlier) for each exception handler, the "try" area must begin and
+   *   end at the start of an instruction (end can be at the end of the code)
+   * - (earlier) for each exception handler, the handler must start at a valid
+   *   instruction
+   */
+  static bool VerifyInstruction(VerifierData* vdata,
+      const Instruction* inst, uint32_t code_offset);
+
+  /* Perform detailed code-flow analysis on a single method. */
+  static bool VerifyCodeFlow(VerifierData* vdata);
+
+  /*
+   * Compute the width of the instruction at each address in the instruction
+   * stream, and store it in vdata->insn_flags.  Addresses that are in the
+   * middle of an instruction, or that are part of switch table data, are not
+   * touched (so the caller should probably initialize "insn_flags" to zero).
+   *
+   * The "new_instance_count_" and "monitor_enter_count_" fields in vdata are
+   * also set.
+   *
+   * Performs some static checks, notably:
+   * - opcode of first instruction begins at index 0
+   * - only documented instructions may appear
+   * - each instruction follows the last
+   * - last byte of last instruction is at (code_length-1)
+   *
+   * Logs an error and returns "false" on failure.
+   */
+  static bool ComputeWidthsAndCountOps(VerifierData* vdata);
+
+  /*
+   * Set the "in try" flags for all instructions protected by "try" statements.
+   * Also sets the "branch target" flags for exception handlers.
+   *
+   * Call this after widths have been set in "insn_flags".
+   *
+   * Returns "false" if something in the exception table looks fishy, but
+   * we're expecting the exception table to be somewhat sane.
+   */
+  static bool ScanTryCatchBlocks(VerifierData* vdata);
+
+  /*
+   * Extract the relative offset from a branch instruction.
+   *
+   * Returns "false" on failure (e.g. this isn't a branch instruction).
+   */
+  static bool GetBranchOffset(const DexFile::CodeItem* code_item,
+      const InsnFlags insn_flags[], uint32_t cur_offset, int32_t* pOffset,
+      bool* pConditional, bool* selfOkay);
+
+  /*
+   * Verify an array data table.  "cur_offset" is the offset of the
+   * fill-array-data instruction.
+   */
+  static bool CheckArrayData(const DexFile::CodeItem* code_item,
+      uint32_t cur_offset);
+
+  /*
+   * Perform static checks on a "new-instance" instruction.  Specifically,
+   * make sure the class reference isn't for an array class.
+   *
+   * We don't need the actual class, just a pointer to the class name.
+   */
+  static bool CheckNewInstance(const DexFile* dex_file, uint32_t idx);
+
+  /*
+   * Perform static checks on a "new-array" instruction.  Specifically, make
+   * sure they aren't creating an array of arrays that causes the number of
+   * dimensions to exceed 255.
+   */
+  static bool CheckNewArray(const DexFile* dex_file, uint32_t idx);
+
+  /*
+   * Perform static checks on an instruction that takes a class constant.
+   * Ensure that the class index is in the valid range.
+   */
+  static bool CheckTypeIndex(const DexFile* dex_file, uint32_t idx);
+
+  /*
+   * Perform static checks on a field get or set instruction.  All we do
+   * here is ensure that the field index is in the valid range.
+   */
+  static bool CheckFieldIndex(const DexFile* dex_file, uint32_t idx);
+
+  /*
+   * Perform static checks on a method invocation instruction.  All we do
+   * here is ensure that the method index is in the valid range.
+   */
+  static bool CheckMethodIndex(const DexFile* dex_file, uint32_t idx);
+
+  /* Ensure that the string index is in the valid range. */
+  static bool CheckStringIndex(const DexFile* dex_file, uint32_t idx);
+
+  /* Ensure that the register index is valid for this code item. */
+  static bool CheckRegisterIndex(const DexFile::CodeItem* code_item,
+      uint32_t idx);
+
+  /* Ensure that the wide register index is valid for this code item. */
+  static bool CheckWideRegisterIndex(const DexFile::CodeItem* code_item,
+      uint32_t idx);
+
+  /*
+   * Check the register indices used in a "vararg" instruction, such as
+   * invoke-virtual or filled-new-array.
+   *
+   * vA holds word count (0-5), args[] have values.
+   *
+   * There are some tests we don't do here, e.g. we don't try to verify
+   * that invoking a method that takes a double is done with consecutive
+   * registers.  This requires parsing the target method signature, which
+   * we will be doing later on during the code flow analysis.
+   */
+  static bool CheckVarArgRegs(const DexFile::CodeItem* code_item, uint32_t vA,
+      uint32_t arg[]);
+
+  /*
+   * Check the register indices used in a "vararg/range" instruction, such as
+   * invoke-virtual/range or filled-new-array/range.
+   *
+   * vA holds word count, vC holds index of first reg.
+   */
+  static bool CheckVarArgRangeRegs(const DexFile::CodeItem* code_item,
+      uint32_t vA, uint32_t vC);
+
+  /*
+   * Verify a switch table. "cur_offset" is the offset of the switch
+   * instruction.
+   *
+   * Updates "insnFlags", setting the "branch target" flag.
+   */
+  static bool CheckSwitchTargets(const DexFile::CodeItem* code_item,
+      InsnFlags insn_flags[], uint32_t cur_offset);
+
+  /*
+   * Verify that the target of a branch instruction is valid.
+   *
+   * We don't expect code to jump directly into an exception handler, but
+   * it's valid to do so as long as the target isn't a "move-exception"
+   * instruction.  We verify that in a later stage.
+   *
+   * The dex format forbids certain instructions from branching to itself.
+   *
+   * Updates "insnFlags", setting the "branch target" flag.
+   */
+  static bool CheckBranchTarget(const DexFile::CodeItem* code_item,
+      InsnFlags insn_flags[], uint32_t cur_offset);
+
+  /*
+   * Initialize the RegisterTable.
+   *
+   * Every instruction address can have a different set of information about
+   * what's in which register, but for verification purposes we only need to
+   * store it at branch target addresses (because we merge into that).
+   *
+   * By zeroing out the regType storage we are effectively initializing the
+   * register information to kRegTypeUnknown.
+   *
+   * We jump through some hoops here to minimize the total number of
+   * allocations we have to perform per method verified.
+   */
+  static bool InitRegisterTable(VerifierData* vdata, RegisterTable* reg_table,
+      RegisterTrackingMode track_regs_for);
+
+  /* Get the register line for the given instruction in the current method. */
+  static inline RegisterLine* GetRegisterLine(const RegisterTable* reg_table,
+      int insn_idx) {
+    return &reg_table->register_lines_[insn_idx];
+  }
+
+  /* Copy a register line. */
+  static inline void CopyRegisterLine(RegisterLine* dst,
+      const RegisterLine* src, size_t num_regs) {
+    memcpy(dst->reg_types_, src->reg_types_, num_regs * sizeof(RegType));
+
+    assert((src->monitor_entries_ == NULL && dst->monitor_entries_ == NULL) ||
+           (src->monitor_entries_ != NULL && dst->monitor_entries_ != NULL));
+    if (dst->monitor_entries_ != NULL) {
+      assert(dst->monitor_stack_ != NULL);
+      memcpy(dst->monitor_entries_, src->monitor_entries_,
+          num_regs * sizeof(MonitorEntries));
+      memcpy(dst->monitor_stack_, src->monitor_stack_,
+          kMaxMonitorStackDepth * sizeof(uint32_t));
+      dst->monitor_stack_top_ = src->monitor_stack_top_;
+    }
+  }
+
+  /* Copy a register line into the table. */
+  static inline void CopyLineToTable(RegisterTable* reg_table, int insn_idx,
+      const RegisterLine* src) {
+    RegisterLine* dst = GetRegisterLine(reg_table, insn_idx);
+    assert(dst->reg_types_ != NULL);
+    CopyRegisterLine(dst, src, reg_table->insn_reg_count_plus_);
+  }
+
+  /* Copy a register line out of the table. */
+  static inline void CopyLineFromTable(RegisterLine* dst,
+      const RegisterTable* reg_table, int insn_idx) {
+    RegisterLine* src = GetRegisterLine(reg_table, insn_idx);
+    assert(src->reg_types_ != NULL);
+    CopyRegisterLine(dst, src, reg_table->insn_reg_count_plus_);
+  }
+
+#ifndef NDEBUG
+  /*
+   * Compare two register lines.  Returns 0 if they match.
+   *
+   * Using this for a sort is unwise, since the value can change based on
+   * machine endianness.
+   */
+  static inline int CompareLineToTable(const RegisterTable* reg_table,
+      int insn_idx, const RegisterLine* line2) {
+    const RegisterLine* line1 = GetRegisterLine(reg_table, insn_idx);
+    if (line1->monitor_entries_ != NULL) {
+      int result;
+
+      if (line2->monitor_entries_ == NULL)
+        return 1;
+      result = memcmp(line1->monitor_entries_, line2->monitor_entries_,
+          reg_table->insn_reg_count_plus_ * sizeof(MonitorEntries));
+      if (result != 0) {
+        LOG(ERROR) << "monitor_entries_ mismatch";
+        return result;
+      }
+      result = line1->monitor_stack_top_ - line2->monitor_stack_top_;
+      if (result != 0) {
+        LOG(ERROR) << "monitor_stack_top_ mismatch";
+        return result;
+      }
+      result = memcmp(line1->monitor_stack_, line2->monitor_stack_,
+            line1->monitor_stack_top_);
+      if (result != 0) {
+        LOG(ERROR) << "monitor_stack_ mismatch";
+        return result;
+      }
+    }
+    return memcmp(line1->reg_types_, line2->reg_types_,
+        reg_table->insn_reg_count_plus_ * sizeof(RegType));
+  }
+#endif
+
+  /*
+   * Create a new uninitialized instance map.
+   *
+   * The map is allocated and populated with address entries.  The addresses
+   * appear in ascending order to allow binary searching.
+   *
+   * Very few methods have 10 or more new-instance instructions; the
+   * majority have 0 or 1.  Occasionally a static initializer will have 200+.
+   *
+   * TODO: merge this into the static pass or initRegisterTable; want to
+   * avoid walking through the instructions yet again just to set up this table
+   */
+  static UninitInstanceMap* CreateUninitInstanceMap(VerifierData* vdata);
+
+  /* Returns true if this method is a constructor. */
+  static bool IsInitMethod(const Method* method);
+
+  /*
+   * Look up a class reference given as a simple string descriptor.
+   *
+   * If we can't find it, return a generic substitute when possible.
+   */
+  static Class* LookupClassByDescriptor(const Method* method,
+      const char* descriptor, VerifyError* failure);
+
+  /*
+   * Look up a class reference in a signature.  Could be an arg or the
+   * return value.
+   *
+   * Advances "*sig" to the last character in the signature (that is, to
+   * the ';').
+   *
+   * NOTE: this is also expected to verify the signature.
+   */
+  static Class* LookupSignatureClass(const Method* method, std::string sig,
+      VerifyError* failure);
+
+  /*
+   * Look up an array class reference in a signature.  Could be an arg or the
+   * return value.
+   *
+   * Advances "*sig" to the last character in the signature.
+   *
+   * NOTE: this is also expected to verify the signature.
+   */
+  static Class* LookupSignatureArrayClass(const Method* method,
+      std::string sig, VerifyError* failure);
+
+  /*
+   * Set the register types for the first instruction in the method based on
+   * the method signature.
+   *
+   * This has the side-effect of validating the signature.
+   *
+   * Returns "true" on success.
+   */
+  static bool SetTypesFromSignature(VerifierData* vdata, RegType* reg_types);
+
+  /*
+   * Set the class object associated with the instruction at "addr".
+   *
+   * Returns the map slot index, or -1 if the address isn't listed in the map
+   * (shouldn't happen) or if a class is already associated with the address
+   * (bad bytecode).
+   *
+   * Entries, once set, do not change -- a given address can only allocate
+   * one type of object.
+   */
+  static int SetUninitInstance(UninitInstanceMap* uninit_map, int addr,
+      Class* klass);
+
+  /*
+   * Perform code flow on a method.
+   *
+   * The basic strategy is as outlined in v3 4.11.1.2: set the "changed" bit
+   * on the first instruction, process it (setting additional "changed" bits),
+   * and repeat until there are no more.
+   *
+   * v3 4.11.1.1
+   * - (N/A) operand stack is always the same size
+   * - operand stack [registers] contain the correct types of values
+   * - local variables [registers] contain the correct types of values
+   * - methods are invoked with the appropriate arguments
+   * - fields are assigned using values of appropriate types
+   * - opcodes have the correct type values in operand registers
+   * - there is never an uninitialized class instance in a local variable in
+   *   code protected by an exception handler (operand stack is okay, because
+   *   the operand stack is discarded when an exception is thrown) [can't
+   *   know what's a local var w/o the debug info -- should fall out of
+   *   register typing]
+   *
+   * v3 4.11.1.2
+   * - execution cannot fall off the end of the code
+   *
+   * (We also do many of the items described in the "static checks" sections,
+   * because it's easier to do them here.)
+   *
+   * We need an array of RegType values, one per register, for every
+   * instruction.  If the method uses monitor-enter, we need extra data
+   * for every register, and a stack for every "interesting" instruction.
+   * In theory this could become quite large -- up to several megabytes for
+   * a monster function.
+   *
+   * NOTE:
+   * The spec forbids backward branches when there's an uninitialized reference
+   * in a register.  The idea is to prevent something like this:
+   *   loop:
+   *     move r1, r0
+   *     new-instance r0, MyClass
+   *     ...
+   *     if-eq rN, loop  // once
+   *   initialize r0
+   *
+   * This leaves us with two different instances, both allocated by the
+   * same instruction, but only one is initialized.  The scheme outlined in
+   * v3 4.11.1.4 wouldn't catch this, so they work around it by preventing
+   * backward branches.  We achieve identical results without restricting
+   * code reordering by specifying that you can't execute the new-instance
+   * instruction if a register contains an uninitialized instance created
+   * by that same instrutcion.
+   */
+  static bool CodeFlowVerifyMethod(VerifierData* vdata,
+      RegisterTable* reg_table);
+
+  /*
+   * Perform verification for a single instruction.
+   *
+   * This requires fully decoding the instruction to determine the effect
+   * it has on registers.
+   *
+   * Finds zero or more following instructions and sets the "changed" flag
+   * if execution at that point needs to be (re-)evaluated.  Register changes
+   * are merged into "reg_types_" at the target addresses.  Does not set or
+   * clear any other flags in "insn_flags".
+   */
+  static bool CodeFlowVerifyInstruction(VerifierData* vdata,
+      RegisterTable* reg_table, uint32_t insn_idx, size_t* start_guess);
+
+  /*
+   * Replace an instruction with "throw-verification-error".  This allows us to
+   * defer error reporting until the code path is first used.
+   *
+   * This is expected to be called during "just in time" verification, not
+   * from within dexopt.  (Verification failures in dexopt will result in
+   * postponement of verification to first use of the class.)
+   *
+   * The throw-verification-error instruction requires two code units.  Some
+   * of the replaced instructions require three; the third code unit will
+   * receive a "nop".  The instruction's length will be left unchanged
+   * in "insn_flags".
+   *
+   * The VM postpones setting of debugger breakpoints in unverified classes,
+   * so there should be no clashes with the debugger.
+   *
+   * Returns "true" on success.
+   */
+  static bool ReplaceFailingInstruction(const DexFile::CodeItem* code_item,
+      InsnFlags* insn_flags, int insn_idx, VerifyError failure);
+
+  /* Handle a monitor-enter instruction. */
+  static void HandleMonitorEnter(RegisterLine* work_line, uint32_t reg_idx,
+      uint32_t insn_idx, VerifyError* failure);
+
+  /* Handle a monitor-exit instruction. */
+  static void HandleMonitorExit(RegisterLine* work_line, uint32_t reg_idx,
+      uint32_t insn_idx, VerifyError* failure);
+
+  /*
+   * Look up an instance field, specified by "field_idx", that is going to be
+   * accessed in object "obj_type".  This resolves the field and then verifies
+   * that the class containing the field is an instance of the reference in
+   * "obj_type".
+   *
+   * It is possible for "obj_type" to be kRegTypeZero, meaning that we might
+   * have a null reference.  This is a runtime problem, so we allow it,
+   * skipping some of the type checks.
+   *
+   * In general, "obj_type" must be an initialized reference.  However, we
+   * allow it to be uninitialized if this is an "<init>" method and the field
+   * is declared within the "obj_type" class.
+   *
+   * Returns a Field on success, returns NULL and sets "*failure" on failure.
+   */
+  static Field* GetInstField(VerifierData* vdata, RegType obj_type,
+      int field_idx, VerifyError* failure);
+
+  /*
+   * Look up a static field.
+   *
+   * Returns a StaticField on success, returns NULL and sets "*failure"
+   * on failure.
+   */
+  static Field* GetStaticField(VerifierData* vdata, int field_idx,
+      VerifyError* failure);
+  /*
+   * For the "move-exception" instruction at "insn_idx", which must be at an
+   * exception handler address, determine the first common superclass of
+   * all exceptions that can land here.  (For javac output, we're probably
+   * looking at multiple spans of bytecode covered by one "try" that lands
+   * at an exception-specific "catch", but in general the handler could be
+   * shared for multiple exceptions.)
+   *
+   * Returns NULL if no matching exception handler can be found, or if the
+   * exception is not a subclass of Throwable.
+   */
+  static Class* GetCaughtExceptionType(VerifierData* vdata, int insn_idx,
+      VerifyError* failure);
+
+  /*
+   * Get the type of register N.
+   *
+   * The register index was validated during the static pass, so we don't
+   * need to check it here.
+   */
+  static inline RegType GetRegisterType(const RegisterLine* register_line,
+      uint32_t vsrc) {
+    return register_line->reg_types_[vsrc];
+  }
+
+  /*
+   * Return the register type for the method.  We can't just use the
+   * already-computed DalvikJniReturnType, because if it's a reference type
+   * we need to do the class lookup.
+   *
+   * Returned references are assumed to be initialized.
+   *
+   * Returns kRegTypeUnknown for "void".
+   */
+  static RegType GetMethodReturnType(const DexFile* dex_file,
+      const Method* method);
+
+  /*
+   * Get the value from a register, and cast it to a Class.  Sets
+   * "*failure" if something fails.
+   *
+   * This fails if the register holds an uninitialized class.
+   *
+   * If the register holds kRegTypeZero, this returns a NULL pointer.
+   */
+  static Class* GetClassFromRegister(const RegisterLine* register_line,
+      uint32_t vsrc, VerifyError* failure);
+
+  /*
+   * Get the "this" pointer from a non-static method invocation.  This
+   * returns the RegType so the caller can decide whether it needs the
+   * reference to be initialized or not.  (Can also return kRegTypeZero
+   * if the reference can only be zero at this point.)
+   *
+   * The argument count is in vA, and the first argument is in vC, for both
+   * "simple" and "range" versions.  We just need to make sure vA is >= 1
+   * and then return vC.
+   */
+  static RegType GetInvocationThis(const RegisterLine* register_line,
+      const Instruction::DecodedInstruction* dec_insn, VerifyError* failure);
+
+  /*
+   * Set the type of register N, verifying that the register is valid.  If
+   * "new_type" is the "Lo" part of a 64-bit value, register N+1 will be
+   * set to "new_type+1".
+   *
+   * The register index was validated during the static pass, so we don't
+   * need to check it here.
+   *
+   * TODO: clear mon stack bits
+   */
+  static void SetRegisterType(RegisterLine* register_line, uint32_t vdst,
+      RegType new_type);
+
+  /*
+   * Verify that the contents of the specified register have the specified
+   * type (or can be converted to it through an implicit widening conversion).
+   *
+   * This will modify the type of the source register if it was originally
+   * derived from a constant to prevent mixing of int/float and long/double.
+   *
+   * If "vsrc" is a reference, both it and the "vsrc" register must be
+   * initialized ("vsrc" may be Zero).  This will verify that the value in
+   * the register is an instance of check_type, or if check_type is an
+   * interface, verify that the register implements check_type.
+   */
+  static void VerifyRegisterType(RegisterLine* register_line, uint32_t vsrc,
+      RegType check_type, VerifyError* failure);
+
+  /* Set the type of the "result" register. */
+  static void SetResultRegisterType(RegisterLine* register_line,
+      const int insn_reg_count, RegType new_type);
+
+  /*
+   * Update all registers holding "uninit_type" to instead hold the
+   * corresponding initialized reference type.  This is called when an
+   * appropriate <init> method is invoked -- all copies of the reference
+   * must be marked as initialized.
+   */
+  static void MarkRefsAsInitialized(RegisterLine* register_line,
+      int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type,
+      VerifyError* failure);
+
+  /*
+   * Implement category-1 "move" instructions.  Copy a 32-bit value from
+   * "vsrc" to "vdst".
+   */
+  static void CopyRegister1(RegisterLine* register_line, uint32_t vdst,
+      uint32_t vsrc, TypeCategory cat, VerifyError* failure);
+
+  /*
+   * Implement category-2 "move" instructions.  Copy a 64-bit value from
+   * "vsrc" to "vdst".  This copies both halves of the register.
+   */
+  static void CopyRegister2(RegisterLine* register_line, uint32_t vdst,
+      uint32_t vsrc, VerifyError* failure);
+
+  /*
+   * Implement "move-result".  Copy the category-1 value from the result
+   * register to another register, and reset the result register.
+   */
+  static void CopyResultRegister1(RegisterLine* register_line,
+      const int insn_reg_count, uint32_t vdst, TypeCategory cat,
+      VerifyError* failure);
+
+  /*
+   * Implement "move-result-wide".  Copy the category-2 value from the result
+   * register to another register, and reset the result register.
+   */
+  static void CopyResultRegister2(RegisterLine* register_line,
+      const int insn_reg_count, uint32_t vdst, VerifyError* failure);
+
+  /*
+   * Compute the "class depth" of a class.  This is the distance from the
+   * class to the top of the tree, chasing superclass links.  java.lang.Object
+   * has a class depth of 0.
+   */
+  static int GetClassDepth(Class* klass);
+
+  /*
+   * Given two classes, walk up the superclass tree to find a common
+   * ancestor.  (Called from findCommonSuperclass().)
+   *
+   * TODO: consider caching the class depth in the class object so we don't
+   * have to search for it here.
+   */
+  static Class* DigForSuperclass(Class* c1, Class* c2);
+
+  /*
+   * Merge two array classes.  We can't use the general "walk up to the
+   * superclass" merge because the superclass of an array is always Object.
+   * We want String[] + Integer[] = Object[].  This works for higher dimensions
+   * as well, e.g. String[][] + Integer[][] = Object[][].
+   *
+   * If Foo1 and Foo2 are subclasses of Foo, Foo1[] + Foo2[] = Foo[].
+   *
+   * If Class implements Type, Class[] + Type[] = Type[].
+   *
+   * If the dimensions don't match, we want to convert to an array of Object
+   * with the least dimension, e.g. String[][] + String[][][][] = Object[][].
+   *
+   * Arrays of primitive types effectively have one less dimension when
+   * merging.  int[] + float[] = Object, int[] + String[] = Object,
+   * int[][] + float[][] = Object[], int[][] + String[] = Object[].  (The
+   * only time this function doesn't return an array class is when one of
+   * the arguments is a 1-dimensional primitive array.)
+   *
+   * This gets a little awkward because we may have to ask the VM to create
+   * a new array type with the appropriate element and dimensions.  However, we
+   * shouldn't be doing this often.
+   */
+  static Class* FindCommonArraySuperclass(Class* c1, Class* c2);
+
+  /*
+   * Find the first common superclass of the two classes.  We're not
+   * interested in common interfaces.
+   *
+   * The easiest way to do this for concrete classes is to compute the "class
+   * depth" of each, move up toward the root of the deepest one until they're
+   * at the same depth, then walk both up to the root until they match.
+   *
+   * If both classes are arrays, we need to merge based on array depth and
+   * element type.
+   *
+   * If one class is an interface, we check to see if the other class/interface
+   * (or one of its predecessors) implements the interface.  If so, we return
+   * the interface; otherwise, we return Object.
+   *
+   * NOTE: we continue the tradition of "lazy interface handling".  To wit,
+   * suppose we have three classes:
+   *   One implements Fancy, Free
+   *   Two implements Fancy, Free
+   *   Three implements Free
+   * where Fancy and Free are unrelated interfaces.  The code requires us
+   * to merge One into Two.  Ideally we'd use a common interface, which
+   * gives us a choice between Fancy and Free, and no guidance on which to
+   * use.  If we use Free, we'll be okay when Three gets merged in, but if
+   * we choose Fancy, we're hosed.  The "ideal" solution is to create a
+   * set of common interfaces and carry that around, merging further references
+   * into it.  This is a pain.  The easy solution is to simply boil them
+   * down to Objects and let the runtime invokeinterface call fail, which
+   * is what we do.
+   */
+  static Class* FindCommonSuperclass(Class* c1, Class* c2);
+
+  /*
+   * Merge two RegType values.
+   *
+   * Sets "*changed" to "true" if the result doesn't match "type1".
+   */
+  static RegType MergeTypes(RegType type1, RegType type2, bool* changed);
+
+  /*
+   * Merge the bits that indicate which monitor entry addresses on the stack
+   * are associated with this register.
+   *
+   * The merge is a simple bitwise AND.
+   *
+   * Sets "*pChanged" to "true" if the result doesn't match "ents1".
+   */
+  static MonitorEntries MergeMonitorEntries(MonitorEntries ents1,
+      MonitorEntries ents2, bool* changed);
+
+  /*
+   * We're creating a new instance of class C at address A.  Any registers
+   * holding instances previously created at address A must be initialized
+   * by now.  If not, we mark them as "conflict" to prevent them from being
+   * used (otherwise, MarkRefsAsInitialized would mark the old ones and the
+   * new ones at the same time).
+   */
+  static void MarkUninitRefsAsInvalid(RegisterLine* register_line,
+      int insn_reg_count, UninitInstanceMap* uninit_map, RegType uninit_type);
+
+  /*
+   * Control can transfer to "next_insn".
+   *
+   * Merge the registers from "work_line" into "reg_table" at "next_insn", and
+   * set the "changed" flag on the target address if any of the registers
+   * has changed.
+   *
+   * Returns "false" if we detect mismatched monitor stacks.
+   */
+  static bool UpdateRegisters(InsnFlags* insn_flags, RegisterTable* reg_table,
+      int next_insn, const RegisterLine* work_line);
+
+  /*
+   * Determine whether we can convert "src_type" to "check_type", where
+   * "check_type" is one of the category-1 non-reference types.
+   *
+   * Constant derived types may become floats, but other values may not.
+   */
+  static bool CanConvertTo1nr(RegType src_type, RegType check_type);
+
+  /* Determine whether the category-2 types are compatible. */
+  static bool CanConvertTo2(RegType src_type, RegType check_type);
+
+  /* Convert a VM PrimitiveType enum value to the equivalent RegType value. */
+  static RegType PrimitiveTypeToRegType(Class::PrimitiveType prim_type);
+
+  /*
+   * Convert a const derived RegType to the equivalent non-const RegType value.
+   * Does nothing if the argument type isn't const derived.
+   */
+  static RegType ConstTypeToRegType(RegType const_type);
+
+  /*
+   * Given a 32-bit constant, return the most-restricted RegType enum entry
+   * that can hold the value. The types used here indicate the value came
+   * from a const instruction, and may not correctly represent the real type
+   * of the value. Upon use, a constant derived type is updated with the
+   * type from the use, which will be unambiguous.
+   */
+  static char DetermineCat1Const(int32_t value);
+
+  /*
+   * If "field" is marked "final", make sure this is the either <clinit>
+   * or <init> as appropriate.
+   *
+   * Sets "*failure" on failure.
+   */
+  static void CheckFinalFieldAccess(const Method* method, const Field* field,
+      VerifyError* failure);
+
+  /*
+   * Make sure that the register type is suitable for use as an array index.
+   *
+   * Sets "*failure" if not.
+   */
+  static void CheckArrayIndexType(const Method* method, RegType reg_type,
+      VerifyError* failure);
+
+  /*
+   * Check constraints on constructor return.  Specifically, make sure that
+   * the "this" argument got initialized.
+   *
+   * The "this" argument to <init> uses code offset kUninitThisArgAddr, which
+   * puts it at the start of the list in slot 0.  If we see a register with
+   * an uninitialized slot 0 reference, we know it somehow didn't get
+   * initialized.
+   *
+   * Returns "true" if all is well.
+   */
+  static bool CheckConstructorReturn(const Method* method,
+      const RegisterLine* register_line, const int insn_reg_count);
+
+  /*
+   * Verify that the target instruction is not "move-exception".  It's important
+   * that the only way to execute a move-exception is as the first instruction
+   * of an exception handler.
+   *
+   * Returns "true" if all is well, "false" if the target instruction is
+   * move-exception.
+   */
+  static bool CheckMoveException(const uint16_t* insns, int insn_idx);
+
+  /*
+   * See if "type" matches "cat".  All we're really looking for here is that
+   * we're not mixing and matching 32-bit and 64-bit quantities, and we're
+   * not mixing references with numerics.  (For example, the arguments to
+   * "a < b" could be integers of different sizes, but they must both be
+   * integers.  Dalvik is less specific about int vs. float, so we treat them
+   * as equivalent here.)
+   *
+   * For category 2 values, "type" must be the "low" half of the value.
+   *
+   * Sets "*failure" if something looks wrong.
+   */
+  static void CheckTypeCategory(RegType type, TypeCategory cat,
+      VerifyError* failure);
+
+  /*
+   * For a category 2 register pair, verify that "type_h" is the appropriate
+   * high part for "type_l".
+   *
+   * Does not verify that "type_l" is in fact the low part of a 64-bit
+   * register pair.
+   */
+  static void CheckWidePair(RegType type_l, RegType type_h,
+      VerifyError* failure);
+
+  /*
+   * Verify types for a simple two-register instruction (e.g. "neg-int").
+   * "dst_type" is stored into vA, and "src_type" is verified against vB.
+   */
+  static void CheckUnop(RegisterLine* register_line,
+      Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+      RegType src_type, VerifyError* failure);
+
+  /*
+   * Verify types for a simple three-register instruction (e.g. "add-int").
+   * "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified
+   * against vB/vC.
+   */
+  static void CheckBinop(RegisterLine* register_line,
+      Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+      RegType src_type1, RegType src_type2, bool check_boolean_op,
+      VerifyError* failure);
+
+  /*
+   * Verify types for a binary "2addr" operation.  "src_type1"/"src_type2"
+   * are verified against vA/vB, then "dst_type" is stored into vA.
+   */
+  static void CheckBinop2addr(RegisterLine* register_line,
+      Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+      RegType src_type1, RegType src_type2, bool check_boolean_op,
+      VerifyError* failure);
+
+  /*
+   * Treat right-shifting as a narrowing conversion when possible.
+   *
+   * For example, right-shifting an int 24 times results in a value that can
+   * be treated as a byte.
+   *
+   * Things get interesting when contemplating sign extension.  Right-
+   * shifting an integer by 16 yields a value that can be represented in a
+   * "short" but not a "char", but an unsigned right shift by 16 yields a
+   * value that belongs in a char rather than a short.  (Consider what would
+   * happen if the result of the shift were cast to a char or short and then
+   * cast back to an int.  If sign extension, or the lack thereof, causes
+   * a change in the 32-bit representation, then the conversion was lossy.)
+   *
+   * A signed right shift by 17 on an integer results in a short.  An unsigned
+   * right shfit by 17 on an integer results in a posshort, which can be
+   * assigned to a short or a char.
+   *
+   * An unsigned right shift on a short can actually expand the result into
+   * a 32-bit integer.  For example, 0xfffff123 >>> 8 becomes 0x00fffff1,
+   * which can't be represented in anything smaller than an int.
+   *
+   * javac does not generate code that takes advantage of this, but some
+   * of the code optimizers do.  It's generally a peephole optimization
+   * that replaces a particular sequence, e.g. (bipush 24, ishr, i2b) is
+   * replaced by (bipush 24, ishr).  Knowing that shifting a short 8 times
+   * to the right yields a byte is really more than we need to handle the
+   * code that's out there, but support is not much more complex than just
+   * handling integer.
+   *
+   * Right-shifting never yields a boolean value.
+   *
+   * Returns the new register type.
+   */
+  static RegType AdjustForRightShift(RegisterLine* register_line, int reg,
+      unsigned int shift_count, bool is_unsigned_shift, VerifyError* failure);
+
+  /*
+   * We're performing an operation like "and-int/2addr" that can be
+   * performed on booleans as well as integers.  We get no indication of
+   * boolean-ness, but we can infer it from the types of the arguments.
+   *
+   * Assumes we've already validated reg1/reg2.
+   *
+   * TODO: consider generalizing this.  The key principle is that the
+   * result of a bitwise operation can only be as wide as the widest of
+   * the operands.  You can safely AND/OR/XOR two chars together and know
+   * you still have a char, so it's reasonable for the compiler or "dx"
+   * to skip the int-to-char instruction.  (We need to do this for boolean
+   * because there is no int-to-boolean operation.)
+   *
+   * Returns true if both args are Boolean, Zero, or One.
+   */
+  static bool UpcastBooleanOp(RegisterLine* register_line, uint32_t reg1,
+      uint32_t reg2);
+
+  /*
+   * Verify types for A two-register instruction with a literal constant
+   * (e.g. "add-int/lit8").  "dst_type" is stored into vA, and "src_type" is
+   * verified against vB.
+   *
+   * If "check_boolean_op" is set, we use the constant value in vC.
+   */
+  static void CheckLitop(RegisterLine* register_line,
+      Instruction::DecodedInstruction* dec_insn, RegType dst_type,
+      RegType src_type, bool check_boolean_op, VerifyError* failure);
+
+  /*
+   * Verify that the arguments in a filled-new-array instruction are valid.
+   *
+   * "res_class" is the class refered to by dec_insn->vB_.
+   */
+  static void VerifyFilledNewArrayRegs(const Method* method,
+      RegisterLine* register_line,
+      const Instruction::DecodedInstruction* dec_insn, Class* res_class,
+      bool is_range, VerifyError* failure);
+
+  /* See if the method matches the MethodType. */
+  static bool IsCorrectInvokeKind(MethodType method_type, Method* res_method);
+
+  /*
+   * Verify the arguments to a method.  We're executing in "method", making
+   * a call to the method reference in vB.
+   *
+   * If this is a "direct" invoke, we allow calls to <init>.  For calls to
+   * <init>, the first argument may be an uninitialized reference.  Otherwise,
+   * calls to anything starting with '<' will be rejected, as will any
+   * uninitialized reference arguments.
+   *
+   * For non-static method calls, this will verify that the method call is
+   * appropriate for the "this" argument.
+   *
+   * The method reference is in vBBBB.  The "is_range" parameter determines
+   * whether we use 0-4 "args" values or a range of registers defined by
+   * vAA and vCCCC.
+   *
+   * Widening conversions on integers and references are allowed, but
+   * narrowing conversions are not.
+   *
+   * Returns the resolved method on success, NULL on failure (with *failure
+   * set appropriately).
+   */
+  static Method* VerifyInvocationArgs(VerifierData* vdata,
+      RegisterLine* register_line, const int insn_reg_count,
+      const Instruction::DecodedInstruction* dec_insn, MethodType method_type,
+      bool is_range, bool is_super, VerifyError* failure);
+
+  DISALLOW_COPY_AND_ASSIGN(DexVerifier);
 };
 
 }  // namespace art
diff --git a/src/dex_verifier_test.cc b/src/dex_verifier_test.cc
index e6d8096..39e14c4 100644
--- a/src/dex_verifier_test.cc
+++ b/src/dex_verifier_test.cc
@@ -18,7 +18,7 @@
     Class* klass = class_linker_->FindSystemClass(descriptor);
 
     // Verify the class
-    ASSERT_TRUE(DexVerify::VerifyClass(klass));
+    ASSERT_TRUE(DexVerifier::VerifyClass(klass));
   }
 
   void VerifyDexFile(const DexFile* dex, ClassLoader* class_loader) {
@@ -41,7 +41,7 @@
 TEST_F(DexVerifierTest, IntMath) {
   const ClassLoader* class_loader = LoadDex("IntMath");
   Class* klass = class_linker_->FindClass("LIntMath;", class_loader);
-  ASSERT_TRUE(DexVerify::VerifyClass(klass));
+  ASSERT_TRUE(DexVerifier::VerifyClass(klass));
 }
 
 }  // namespace art
diff --git a/src/object.cc b/src/object.cc
index 4384a24..edc5bdd 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -774,6 +774,24 @@
   return NULL;
 }
 
+Method* Class::FindInterfaceMethod(const StringPiece& name,
+                                   const StringPiece& signature) {
+  // Check the current class before checking the interfaces.
+  Method* method = FindVirtualMethod(name, signature);
+  if (method != NULL) {
+    return method;
+  }
+
+  InterfaceEntry* iftable = GetIFTable();
+  for (size_t i = 0; i < GetIFTableCount(); i++) {
+    method = iftable[i].GetInterface()->FindVirtualMethod(name, signature);
+    if (method != NULL) {
+      return method;
+    }
+  }
+  return NULL;
+}
+
 Method* Class::FindDeclaredDirectMethod(const StringPiece& name,
                                         const StringPiece& signature) {
   for (size_t i = 0; i < NumDirectMethods(); ++i) {
diff --git a/src/object.h b/src/object.h
index 050f571..9106472 100644
--- a/src/object.h
+++ b/src/object.h
@@ -82,6 +82,7 @@
 
 static const uint32_t kAccConstructor = 0x00010000;  // method (Dalvik only)
 static const uint32_t kAccDeclaredSynchronized = 0x00020000;  // method (Dalvik only)
+static const uint32_t kAccWritable = 0x80000000; // method (Dalvik only)
 
 static const uint32_t kAccClassFlagsMask = (kAccPublic
                                             | kAccFinal
@@ -501,6 +502,10 @@
     return (GetAccessFlags() & kAccStatic) != 0;
   }
 
+  bool IsFinal() const {
+    return (access_flags_ & kAccFinal) != 0;
+  }
+
   uint32_t GetTypeIdx() const;
 
   void SetTypeIdx(uint32_t type_idx);
@@ -1666,6 +1671,9 @@
   // method for this class.
   Method* FindVirtualMethodForInterface(Method* method);
 
+  Method* FindInterfaceMethod(const StringPiece& name,
+                              const StringPiece& descriptor);
+
   Method* FindVirtualMethodForVirtualOrInterface(Method* method) {
     if (method->GetDeclaringClass()->IsInterface()) {
       return FindVirtualMethodForInterface(method);
@@ -1931,8 +1939,9 @@
                              new_source_file, false);
   }
 
- private:
   bool Implements(const Class* klass) const;
+
+ private:
   bool IsArrayAssignableFromArray(const Class* klass) const;
   bool IsAssignableFromArray(const Class* klass) const;
   bool IsSubClass(const Class* klass) const;
@@ -1977,7 +1986,7 @@
   // is 2.  Otherwise 0.
   int32_t array_rank_;
 
-  // primitive type index, or PRIM_NOT (-1); set for generated prim classes
+  // primitive type index, or kPrimNot (0); set for generated prim classes
   PrimitiveType primitive_type_;
 
   // The superclass, or NULL if this is java.lang.Object or a