Added first pass of verifier and supporting changes.
The verifier still needs to make a second pass through the code where it
checks the code flow. A TODO marks where it will be added.
Change-Id: I0abea5bad563776186df342d8132fb1ca8869652
diff --git a/src/class_linker.cc b/src/class_linker.cc
index e0461be..04a17cf 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1606,7 +1606,6 @@
Method* method = klass->GetVirtualMethod(i);
if (method->IsAbstract()) {
LG << "AbstractMethodError";
- method->code_off_ = 0xFFFFFFFF;
// TODO: throw AbstractMethodError
}
}
diff --git a/src/dex_file.h b/src/dex_file.h
index 76b9c2f..2dcb3b2 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -229,7 +229,7 @@
CatchHandlerIterator(const byte* handler_data) {
current_data_ = handler_data;
- remaining_count_ = DecodeUnsignedLeb128(¤t_data_);
+ remaining_count_ = DecodeSignedLeb128(¤t_data_);
// If remaining_count_ is non-positive, then it is the negative of
// the number of catch types, and the catches are followed by a
@@ -247,6 +247,10 @@
return handler_;
}
+ const byte* GetData() const {
+ return current_data_;
+ }
+
void Next() {
if (remaining_count_ > 0) {
handler_.type_idx_ = DecodeUnsignedLeb128(¤t_data_);
@@ -570,14 +574,14 @@
*last_idx = idx;
}
- const TryItem* dexGetTryItems(const CodeItem& code_item, uint32_t offset) const {
+ static const TryItem* dexGetTryItems(const CodeItem& code_item, uint32_t offset) {
const uint16_t* insns_end_ = &code_item.insns_[code_item.insns_size_];
return reinterpret_cast<const TryItem*>
(RoundUp(reinterpret_cast<uint32_t>(insns_end_), 4)) + offset;
}
// Get the base of the encoded data for the given DexCode.
- const byte* dexGetCatchHandlerData(const CodeItem& code_item, uint32_t offset) const {
+ static const byte* dexGetCatchHandlerData(const CodeItem& code_item, uint32_t offset) {
const byte* handler_data = reinterpret_cast<const byte*>
(dexGetTryItems(code_item, code_item.tries_size_));
return handler_data + offset;
@@ -586,7 +590,7 @@
// Find the handler associated with a given address, if any.
// Initializes the given iterator and returns true if a match is
// found. Returns end if there is no applicable handler.
- CatchHandlerIterator dexFindCatchHandler(const CodeItem& code_item, uint32_t address) const {
+ static CatchHandlerIterator dexFindCatchHandler(const CodeItem& code_item, uint32_t address) {
CatchHandlerItem handler;
handler.address_ = -1;
int32_t offset = -1;
@@ -619,9 +623,9 @@
return CatchHandlerIterator();
}
- int32_t dexFindCatchHandlerOffset0(const CodeItem &code_item,
- int32_t tries_size,
- uint32_t address) const {
+ static int32_t dexFindCatchHandlerOffset0(const CodeItem &code_item,
+ int32_t tries_size,
+ uint32_t address) {
// Note: Signed type is important for max and min.
int32_t min = 0;
int32_t max = tries_size - 1;
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index d753204..39348ed 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -5,7 +5,7 @@
namespace art {
const char* const Instruction::kInstructionNames[] = {
-#define INSTRUCTION_NAME(o, c, pname, f, r, i, a) pname,
+#define INSTRUCTION_NAME(o, c, pname, f, r, i, a, v) pname,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_NAME)
#undef DEX_INSTRUCTION_LIST
@@ -13,7 +13,7 @@
};
Instruction::InstructionFormat const Instruction::kInstructionFormats[] = {
-#define INSTRUCTION_FORMAT(o, c, p, format, r, i, a) format,
+#define INSTRUCTION_FORMAT(o, c, p, format, r, i, a, v) format,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_FORMAT)
#undef DEX_INSTRUCTION_LIST
@@ -21,25 +21,185 @@
};
int const Instruction::kInstructionFlags[] = {
-#define INSTRUCTION_FLAGS(o, c, p, f, r, i, flags) flags,
+#define INSTRUCTION_FLAGS(o, c, p, f, r, i, flags, v) flags,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_FLAGS)
#undef DEX_INSTRUCTION_LIST
#undef INSTRUCTION_FLAGS
};
+int const Instruction::kInstructionVerifyFlags[] = {
+#define INSTRUCTION_VERIFY_FLAGS(o, c, p, f, r, i, a, vflags) vflags,
+#include "dex_instruction_list.h"
+ DEX_INSTRUCTION_LIST(INSTRUCTION_VERIFY_FLAGS)
+#undef DEX_INSTRUCTION_LIST
+#undef INSTRUCTION_VERIFY_FLAGS
+};
+
+/*
+ * Handy macros for helping decode instructions.
+ */
+#define FETCH(_offset) (insns[(_offset)])
+#define FETCH_u4(_offset) (fetch_u4_impl((_offset), insns))
+#define INST_A(_insn) (((uint16_t)(_insn) >> 8) & 0x0f)
+#define INST_B(_insn) ((uint16_t)(_insn) >> 12)
+#define INST_AA(_insn) ((_insn) >> 8)
+
+/* Helper for FETCH_u4, above. */
+static inline uint32_t fetch_u4_impl(uint32_t offset, const uint16_t* insns) {
+ return insns[offset] | ((uint32_t) insns[offset+1] << 16);
+}
+
+void Instruction::Decode(uint32_t &vA, uint32_t &vB, uint64_t &vB_wide, uint32_t &vC, uint32_t arg[]) const {
+ const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
+ uint16_t insn = *insns;
+ int opcode = insn & 0xFF;
+
+ switch (Format()) {
+ case k10x: // op
+ /* nothing to do; copy the AA bits out for the verifier */
+ vA = INST_AA(insn);
+ break;
+ case k12x: // op vA, vB
+ vA = INST_A(insn);
+ vB = INST_B(insn);
+ break;
+ case k11n: // op vA, #+B
+ vA = INST_A(insn);
+ vB = (int32_t) (INST_B(insn) << 28) >> 28; // sign extend 4-bit value
+ break;
+ case k11x: // op vAA
+ vA = INST_AA(insn);
+ break;
+ case k10t: // op +AA
+ vA = (int8_t) INST_AA(insn); // sign-extend 8-bit value
+ break;
+ case k20t: // op +AAAA
+ vA = (int16_t) FETCH(1); // sign-extend 16-bit value
+ break;
+ case k21c: // op vAA, thing@BBBB
+ case k22x: // op vAA, vBBBB
+ vA = INST_AA(insn);
+ vB = FETCH(1);
+ break;
+ case k21s: // op vAA, #+BBBB
+ case k21t: // op vAA, +BBBB
+ vA = INST_AA(insn);
+ vB = (int16_t) FETCH(1); // sign-extend 16-bit value
+ break;
+ case k21h: // op vAA, #+BBBB0000[00000000]
+ vA = INST_AA(insn);
+ /*
+ * The value should be treated as right-zero-extended, but we don't
+ * actually do that here. Among other things, we don't know if it's
+ * the top bits of a 32- or 64-bit value.
+ */
+ vB = FETCH(1);
+ break;
+ case k23x: // op vAA, vBB, vCC
+ vA = INST_AA(insn);
+ vB = FETCH(1) & 0xff;
+ vC = FETCH(1) >> 8;
+ break;
+ case k22b: // op vAA, vBB, #+CC
+ vA = INST_AA(insn);
+ vB = FETCH(1) & 0xff;
+ vC = (int8_t) (FETCH(1) >> 8); // sign-extend 8-bit value
+ break;
+ case k22s: // op vA, vB, #+CCCC
+ case k22t: // op vA, vB, +CCCC
+ vA = INST_A(insn);
+ vB = INST_B(insn);
+ vC = (int16_t) FETCH(1); // sign-extend 16-bit value
+ break;
+ case k22c: // op vA, vB, thing@CCCC
+ vA = INST_A(insn);
+ vB = INST_B(insn);
+ vC = FETCH(1);
+ break;
+ case k30t: // op +AAAAAAAA
+ vA = FETCH_u4(1); // signed 32-bit value
+ break;
+ case k31t: // op vAA, +BBBBBBBB
+ case k31c: // op vAA, string@BBBBBBBB
+ vA = INST_AA(insn);
+ vB = FETCH_u4(1); // 32-bit value
+ break;
+ case k32x: // op vAAAA, vBBBB
+ vA = FETCH(1);
+ vB = FETCH(2);
+ break;
+ case k31i: // op vAA, #+BBBBBBBB
+ vA = INST_AA(insn);
+ vB = FETCH_u4(1); // signed 32-bit value
+ break;
+ case k35c: // op {vC, vD, vE, vF, vG}, thing@BBBB
+ {
+ /*
+ * Note that the fields mentioned in the spec don't appear in
+ * their "usual" positions here compared to most formats. This
+ * was done so that the field names for the argument count and
+ * reference index match between this format and the corresponding
+ * range formats (3rc and friends).
+ *
+ * Bottom line: The argument count is always in vA, and the
+ * method constant (or equivalent) is always in vB.
+ */
+ uint16_t regList;
+ int count;
+
+ vA = INST_B(insn); // This is labeled A in the spec.
+ vB = FETCH(1);
+ regList = FETCH(2);
+
+ count = vA;
+
+ /*
+ * Copy the argument registers into the arg[] array, and
+ * also copy the first argument (if any) into vC. (The
+ * DecodedInstruction structure doesn't have separate
+ * fields for {vD, vE, vF, vG}, so there's no need to make
+ * copies of those.) Note that cases 5..2 fall through.
+ */
+ switch (count) {
+ case 5: arg[4] = INST_A(insn);
+ case 4: arg[3] = (regList >> 12) & 0x0f;
+ case 3: arg[2] = (regList >> 8) & 0x0f;
+ case 2: arg[1] = (regList >> 4) & 0x0f;
+ case 1: vC = arg[0] = regList & 0x0f; break;
+ case 0: break; // Valid, but no need to do anything.
+ default:
+ LOG(ERROR) << "Invalid arg count in 35c (" << count << ")";
+ return;
+ }
+ }
+ break;
+ case k3rc: // op {vCCCC .. v(CCCC+AA-1)}, meth@BBBB
+ vA = INST_AA(insn);
+ vB = FETCH(1);
+ vC = FETCH(2);
+ break;
+ case k51l: // op vAA, #+BBBBBBBBBBBBBBBB
+ vA = INST_AA(insn);
+ vB_wide = FETCH_u4(1) | ((uint64_t) FETCH_u4(3) << 32);
+ break;
+ default:
+ LOG(ERROR) << "Can't decode unexpected format " << (int) Format() << " (op=" << opcode << ")";
+ return;
+ }
+}
+
size_t Instruction::Size() const {
const uint16_t* insns = reinterpret_cast<const uint16_t*>(this);
- size_t size = 0;
if (*insns == kPackedSwitchSignature) {
- size = 4 + insns[1] * 2;
+ return (4 + insns[1] * 2);
} else if (*insns == kSparseSwitchSignature) {
- size = 2 + insns[1] * 4;
+ return (2 + insns[1] * 4);
} else if (*insns == kArrayDataSignature) {
uint16_t element_size = insns[1];
uint32_t length = insns[2] | (((uint32_t)insns[3]) << 16);
// The plus 1 is to round up for odd size and width.
- return 4 + (element_size * length + 1) / 2;
+ return (4 + (element_size * length + 1) / 2);
} else {
switch (Format()) {
case k10x:
@@ -47,8 +207,7 @@
case k11n:
case k11x:
case k10t:
- size = 1;
- break;
+ return 1;
case k20t:
case k22x:
case k21t:
@@ -60,8 +219,7 @@
case k22t:
case k22s:
case k22c:
- size = 2;
- break;
+ return 2;
case k32x:
case k30t:
case k31t:
@@ -69,17 +227,14 @@
case k31c:
case k35c:
case k3rc:
- size = 3;
- break;
+ return 3;
case k51l:
- size = 5;
- break;
+ return 5;
default:
LOG(FATAL) << "Unreachable";
}
}
- size *= sizeof(uint16_t);
- return size;
+ return 0;
}
Instruction::Code Instruction::Opcode() const {
@@ -89,7 +244,7 @@
}
const Instruction* Instruction::Next() const {
- size_t current_size = Size();
+ size_t current_size = Size() * sizeof(uint16_t);
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(this);
return reinterpret_cast<const Instruction*>(ptr + current_size);
}
diff --git a/src/dex_instruction.h b/src/dex_instruction.h
index 617f818..8cf645d 100644
--- a/src/dex_instruction.h
+++ b/src/dex_instruction.h
@@ -19,7 +19,7 @@
};
enum Code {
-#define INSTRUCTION_ENUM(opcode, cname, p, f, r, i, a) cname = opcode,
+#define INSTRUCTION_ENUM(opcode, cname, p, f, r, i, a, v) cname = opcode,
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_ENUM)
#undef DEX_INSTRUCTION_LIST
@@ -63,6 +63,33 @@
// TODO: kUnconditional
};
+ enum VerifyFlag {
+ kVerifyNone = 0x00000,
+ kVerifyRegA = 0x00001,
+ kVerifyRegAWide = 0x00002,
+ kVerifyRegB = 0x00004,
+ kVerifyRegBField = 0x00008,
+ kVerifyRegBMethod = 0x00010,
+ kVerifyRegBNewInstance = 0x00020,
+ kVerifyRegBString = 0x00040,
+ kVerifyRegBType = 0x00080,
+ kVerifyRegBWide = 0x00100,
+ kVerifyRegC = 0x00200,
+ kVerifyRegCField = 0x00400,
+ kVerifyRegCNewArray = 0x00800,
+ kVerifyRegCType = 0x01000,
+ kVerifyRegCWide = 0x02000,
+ kVerifyArrayData = 0x04000,
+ kVerifyBranchTarget = 0x08000,
+ kVerifySwitchTargets = 0x10000,
+ kVerifyVarArg = 0x20000,
+ kVerifyVarArgRange = 0x40000,
+ kVerifyError = 0x80000,
+ };
+
+ // 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;
+
// Returns the size in bytes of this instruction.
size_t Size() const;
@@ -78,9 +105,9 @@
Code Opcode() const;
// Reads an instruction out of the stream at the specified address.
- static Instruction* At(byte* code) {
+ static const Instruction* At(const byte* code) {
CHECK(code != NULL);
- return reinterpret_cast<Instruction*>(code);
+ return reinterpret_cast<const Instruction*>(code);
}
// Returns the format of the current instruction.
@@ -93,6 +120,16 @@
return (kInstructionFlags[Opcode()] & kBranch) != 0;
}
+ // Returns true if this instruction is a switch.
+ bool IsSwitch() const {
+ return (kInstructionFlags[Opcode()] & kSwitch) != 0;
+ }
+
+ // Returns true if this instruction can throw.
+ bool IsThrow() const {
+ return (kInstructionFlags[Opcode()] & kThrow) != 0;
+ }
+
// Determine if the instruction is any of 'return' instructions.
bool IsReturn() const {
return (kInstructionFlags[Opcode()] & kReturn) != 0;
@@ -108,10 +145,30 @@
return (kInstructionFlags[Opcode()] & kInvoke) != 0;
}
+ int GetVerifyTypeArgumentA() const {
+ return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegA | kVerifyRegAWide));
+ }
+
+ int GetVerifyTypeArgumentB() const {
+ return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegB | kVerifyRegBField | kVerifyRegBMethod |
+ kVerifyRegBNewInstance | kVerifyRegBString | kVerifyRegBType | kVerifyRegBWide));
+ }
+
+ int GetVerifyTypeArgumentC() const {
+ return (kInstructionVerifyFlags[Opcode()] & (kVerifyRegC | kVerifyRegCField |
+ kVerifyRegCNewArray | kVerifyRegBType | kVerifyRegBWide));
+ }
+
+ int GetVerifyExtraFlags() const {
+ return (kInstructionVerifyFlags[Opcode()] & (kVerifyArrayData | kVerifyBranchTarget |
+ kVerifySwitchTargets | kVerifyVarArg | kVerifyVarArgRange | kVerifyError));
+ }
+
private:
static const char* const kInstructionNames[];
static InstructionFormat const kInstructionFormats[];
static int const kInstructionFlags[];
+ static int const kInstructionVerifyFlags[];
DISALLOW_IMPLICIT_CONSTRUCTORS(Instruction);
};
diff --git a/src/dex_instruction_list.h b/src/dex_instruction_list.h
index b2c1fa6..009573e 100644
--- a/src/dex_instruction_list.h
+++ b/src/dex_instruction_list.h
@@ -1,259 +1,285 @@
// Copyright 2011 Google Inc. All Rights Reserved.
#define DEX_INSTRUCTION_LIST(V) \
- V(0x00, NOP, "nop", k10x, false, kNone, kContinue) \
- V(0x01, MOVE, "move", k12x, true, kNone, kContinue) \
- V(0x02, MOVE_FROM16, "move/from16", k22x, true, kNone, kContinue) \
- V(0x03, MOVE_16, "move/16", k32x, true, kNone, kContinue) \
- V(0x04, MOVE_WIDE, "move-wide", k12x, true, kNone, kContinue) \
- V(0x05, MOVE_WIDE_FROM16, "move-wide/from16", k22x, true, kNone, kContinue) \
- V(0x06, MOVE_WIDE_16, "move-wide/16", k32x, true, kNone, kContinue) \
- V(0x07, MOVE_OBJECT, "move-object", k12x, true, kNone, kContinue) \
- V(0x08, MOVE_OBJECT_FROM16, "move-object/from16", k22x, true, kNone, kContinue) \
- V(0x09, MOVE_OBJECT_16, "move-object/16", k32x, true, kNone, kContinue) \
- V(0x0A, MOVE_RESULT, "move-result", k11x, true, kNone, kContinue) \
- V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, true, kNone, kContinue) \
- V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, true, kNone, kContinue) \
- V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, true, kNone, kContinue) \
- V(0x0E, RETURN_VOID, "return-void", k10x, false, kNone, kReturn) \
- V(0x0F, RETURN, "return", k11x, false, kNone, kReturn) \
- V(0x10, RETURN_WIDE, "return-wide", k11x, false, kNone, kReturn) \
- V(0x11, RETURN_OBJECT, "return-object", k11x, false, kNone, kReturn) \
- V(0x12, CONST_4, "const/4", k11n, true, kNone, kContinue) \
- V(0x13, CONST_16, "const/16", k21s, true, kNone, kContinue) \
- V(0x14, CONST, "const", k31i, true, kNone, kContinue) \
- V(0x15, CONST_HIGH16, "const/high16", k21h, true, kNone, kContinue) \
- V(0x16, CONST_WIDE_16, "const-wide/16", k21s, true, kNone, kContinue) \
- V(0x17, CONST_WIDE_32, "const-wide/32", k31i, true, kNone, kContinue) \
- V(0x18, CONST_WIDE, "const-wide", k51l, true, kNone, kContinue) \
- V(0x19, CONST_WIDE_HIGH16, "const-wide/high16", k21h, true, kNone, kContinue) \
- V(0x1A, CONST_STRING, "const-string", k21c, true, kStringRef, kContinue | kThrow) \
- V(0x1B, CONST_STRING_JUMBO, "const-string/jumbo", k31c, true, kStringRef, kContinue | kThrow) \
- V(0x1C, CONST_CLASS, "const-class", k21c, true, kTypeRef, kContinue | kThrow) \
- V(0x1D, MONITOR_ENTER, "monitor-enter", k11x, false, kNone, kContinue | kThrow) \
- V(0x1E, MONITOR_EXIT, "monitor-exit", k11x, false, kNone, kContinue | kThrow) \
- V(0x1F, CHECK_CAST, "check-cast", k21c, true, kTypeRef, kContinue | kThrow) \
- V(0x20, INSTANCE_OF, "instance-of", k22c, true, kTypeRef, kContinue | kThrow) \
- V(0x21, ARRAY_LENGTH, "array-length", k12x, true, kNone, kContinue | kThrow) \
- V(0x22, NEW_INSTANCE, "new-instance", k21c, true, kTypeRef, kContinue | kThrow) \
- V(0x23, NEW_ARRAY, "new-array", k22c, true, kTypeRef, kContinue | kThrow) \
- V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, false, kTypeRef, kContinue | kThrow) \
- V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, false, kTypeRef, kContinue | kThrow) \
- V(0x26, FILL_ARRAY_DATA, "fill-array-data", k31t, false, kNone, kContinue) \
- V(0x27, THROW, "throw", k11x, false, kNone, kThrow) \
- V(0x28, GOTO, "goto", k10t, false, kNone, kBranch) \
- V(0x29, GOTO_16, "goto/16", k20t, false, kNone, kBranch) \
- V(0x2A, GOTO_32, "goto/32", k30t, false, kNone, kBranch) \
- V(0x2B, PACKED_SWITCH, "packed-switch", k31t, false, kNone, kContinue | kSwitch) \
- V(0x2C, SPARSE_SWITCH, "sparse-switch", k31t, false, kNone, kContinue | kSwitch) \
- V(0x2D, CMPL_FLOAT, "cmpl-float", k23x, true, kNone, kContinue) \
- V(0x2E, CMPG_FLOAT, "cmpg-float", k23x, true, kNone, kContinue) \
- V(0x2F, CMPL_DOUBLE, "cmpl-double", k23x, true, kNone, kContinue) \
- V(0x30, CMPG_DOUBLE, "cmpg-double", k23x, true, kNone, kContinue) \
- V(0x31, CMP_LONG, "cmp-long", k23x, true, kNone, kContinue) \
- V(0x32, IF_EQ, "if-eq", k22t, false, kNone, kContinue | kBranch) \
- V(0x33, IF_NE, "if-ne", k22t, false, kNone, kContinue | kBranch) \
- V(0x34, IF_LT, "if-lt", k22t, false, kNone, kContinue | kBranch) \
- V(0x35, IF_GE, "if-ge", k22t, false, kNone, kContinue | kBranch) \
- V(0x36, IF_GT, "if-gt", k22t, false, kNone, kContinue | kBranch) \
- V(0x37, IF_LE, "if-le", k22t, false, kNone, kContinue | kBranch) \
- V(0x38, IF_EQZ, "if-eqz", k21t, false, kNone, kContinue | kBranch) \
- V(0x39, IF_NEZ, "if-nez", k21t, false, kNone, kContinue | kBranch) \
- V(0x3A, IF_LTZ, "if-ltz", k21t, false, kNone, kContinue | kBranch) \
- V(0x3B, IF_GEZ, "if-gez", k21t, false, kNone, kContinue | kBranch) \
- V(0x3C, IF_GTZ, "if-gtz", k21t, false, kNone, kContinue | kBranch) \
- V(0x3D, IF_LEZ, "if-lez", k21t, false, kNone, kContinue | kBranch) \
- V(0x3E, UNUSED_3E, "unused-3e", k10x, false, kUnknown, 0) \
- V(0x3F, UNUSED_3F, "unused-3f", k10x, false, kUnknown, 0) \
- V(0x40, UNUSED_40, "unused-40", k10x, false, kUnknown, 0) \
- V(0x41, UNUSED_41, "unused-41", k10x, false, kUnknown, 0) \
- V(0x42, UNUSED_42, "unused-42", k10x, false, kUnknown, 0) \
- V(0x43, UNUSED_43, "unused-43", k10x, false, kUnknown, 0) \
- V(0x44, AGET, "aget", k23x, true, kNone, kContinue | kThrow) \
- V(0x45, AGET_WIDE, "aget-wide", k23x, true, kNone, kContinue | kThrow) \
- V(0x46, AGET_OBJECT, "aget-object", k23x, true, kNone, kContinue | kThrow) \
- V(0x47, AGET_BOOLEAN, "aget-boolean", k23x, true, kNone, kContinue | kThrow) \
- V(0x48, AGET_BYTE, "aget-byte", k23x, true, kNone, kContinue | kThrow) \
- V(0x49, AGET_CHAR, "aget-char", k23x, true, kNone, kContinue | kThrow) \
- V(0x4A, AGET_SHORT, "aget-short", k23x, true, kNone, kContinue | kThrow) \
- V(0x4B, APUT, "aput", k23x, false, kNone, kContinue | kThrow) \
- V(0x4C, APUT_WIDE, "aput-wide", k23x, false, kNone, kContinue | kThrow) \
- V(0x4D, APUT_OBJECT, "aput-object", k23x, false, kNone, kContinue | kThrow) \
- V(0x4E, APUT_BOOLEAN, "aput-boolean", k23x, false, kNone, kContinue | kThrow) \
- V(0x4F, APUT_BYTE, "aput-byte", k23x, false, kNone, kContinue | kThrow) \
- V(0x50, APUT_CHAR, "aput-char", k23x, false, kNone, kContinue | kThrow) \
- V(0x51, APUT_SHORT, "aput-short", k23x, false, kNone, kContinue | kThrow) \
- V(0x52, IGET, "iget", k22c, true, kFieldRef, kContinue | kThrow) \
- V(0x53, IGET_WIDE, "iget-wide", k22c, true, kFieldRef, kContinue | kThrow) \
- V(0x54, IGET_OBJECT, "iget-object", k22c, true, kFieldRef, kContinue | kThrow) \
- V(0x55, IGET_BOOLEAN, "iget-boolean", k22c, true, kFieldRef, kContinue | kThrow) \
- V(0x56, IGET_BYTE, "iget-byte", k22c, true, kFieldRef, kContinue | kThrow) \
- V(0x57, IGET_CHAR, "iget-char", k22c, true, kFieldRef, kContinue | kThrow) \
- V(0x58, IGET_SHORT, "iget-short", k22c, true, kFieldRef, kContinue | kThrow) \
- V(0x59, IPUT, "iput", k22c, false, kFieldRef, kContinue | kThrow) \
- V(0x5A, IPUT_WIDE, "iput-wide", k22c, false, kFieldRef, kContinue | kThrow) \
- V(0x5B, IPUT_OBJECT, "iput-object", k22c, false, kFieldRef, kContinue | kThrow) \
- V(0x5C, IPUT_BOOLEAN, "iput-boolean", k22c, false, kFieldRef, kContinue | kThrow) \
- V(0x5D, IPUT_BYTE, "iput-byte", k22c, false, kFieldRef, kContinue | kThrow) \
- V(0x5E, IPUT_CHAR, "iput-char", k22c, false, kFieldRef, kContinue | kThrow) \
- V(0x5F, IPUT_SHORT, "iput-short", k22c, false, kFieldRef, kContinue | kThrow) \
- V(0x60, SGET, "sget", k21c, true, kFieldRef, kContinue | kThrow) \
- V(0x61, SGET_WIDE, "sget-wide", k21c, true, kFieldRef, kContinue | kThrow) \
- V(0x62, SGET_OBJECT, "sget-object", k21c, true, kFieldRef, kContinue | kThrow) \
- V(0x63, SGET_BOOLEAN, "sget-boolean", k21c, true, kFieldRef, kContinue | kThrow) \
- V(0x64, SGET_BYTE, "sget-byte", k21c, true, kFieldRef, kContinue | kThrow) \
- V(0x65, SGET_CHAR, "sget-char", k21c, true, kFieldRef, kContinue | kThrow) \
- V(0x66, SGET_SHORT, "sget-short", k21c, true, kFieldRef, kContinue | kThrow) \
- V(0x67, SPUT, "sput", k21c, false, kFieldRef, kContinue | kThrow) \
- V(0x68, SPUT_WIDE, "sput-wide", k21c, false, kFieldRef, kContinue | kThrow) \
- V(0x69, SPUT_OBJECT, "sput-object", k21c, false, kFieldRef, kContinue | kThrow) \
- V(0x6A, SPUT_BOOLEAN, "sput-boolean", k21c, false, kFieldRef, kContinue | kThrow) \
- V(0x6B, SPUT_BYTE, "sput-byte", k21c, false, kFieldRef, kContinue | kThrow) \
- V(0x6C, SPUT_CHAR, "sput-char", k21c, false, kFieldRef, kContinue | kThrow) \
- V(0x6D, SPUT_SHORT, "sput-short", k21c, false, kFieldRef, kContinue | kThrow) \
- V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x6F, INVOKE_SUPER, "invoke-super", k35c, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x71, INVOKE_STATIC, "invoke-static", k35c, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x73, UNUSED_73, "unused-73", k10x, false, kUnknown, 0) \
- V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x77, INVOKE_STATIC_RANGE, "invoke-static/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke) \
- V(0x79, UNUSED_79, "unused-79", k10x, false, kUnknown, 0) \
- V(0x7A, UNUSED_7A, "unused-7a", k10x, false, kUnknown, 0) \
- V(0x7B, NEG_INT, "neg-int", k12x, true, kNone, kContinue) \
- V(0x7C, NOT_INT, "not-int", k12x, true, kNone, kContinue) \
- V(0x7D, NEG_LONG, "neg-long", k12x, true, kNone, kContinue) \
- V(0x7E, NOT_LONG, "not-long", k12x, true, kNone, kContinue) \
- V(0x7F, NEG_FLOAT, "neg-float", k12x, true, kNone, kContinue) \
- V(0x80, NEG_DOUBLE, "neg-double", k12x, true, kNone, kContinue) \
- V(0x81, INT_TO_LONG, "int-to-long", k12x, true, kNone, kContinue) \
- V(0x82, INT_TO_FLOAT, "int-to-float", k12x, true, kNone, kContinue) \
- V(0x83, INT_TO_DOUBLE, "int-to-double", k12x, true, kNone, kContinue) \
- V(0x84, LONG_TO_INT, "long-to-int", k12x, true, kNone, kContinue) \
- V(0x85, LONG_TO_FLOAT, "long-to-float", k12x, true, kNone, kContinue) \
- V(0x86, LONG_TO_DOUBLE, "long-to-double", k12x, true, kNone, kContinue) \
- V(0x87, FLOAT_TO_INT, "float-to-int", k12x, true, kNone, kContinue) \
- V(0x88, FLOAT_TO_LONG, "float-to-long", k12x, true, kNone, kContinue) \
- V(0x89, FLOAT_TO_DOUBLE, "float-to-double", k12x, true, kNone, kContinue) \
- V(0x8A, DOUBLE_TO_INT, "double-to-int", k12x, true, kNone, kContinue) \
- V(0x8B, DOUBLE_TO_LONG, "double-to-long", k12x, true, kNone, kContinue) \
- V(0x8C, DOUBLE_TO_FLOAT, "double-to-float", k12x, true, kNone, kContinue) \
- V(0x8D, INT_TO_BYTE, "int-to-byte", k12x, true, kNone, kContinue) \
- V(0x8E, INT_TO_CHAR, "int-to-char", k12x, true, kNone, kContinue) \
- V(0x8F, INT_TO_SHORT, "int-to-short", k12x, true, kNone, kContinue) \
- V(0x90, ADD_INT, "add-int", k23x, true, kNone, kContinue) \
- V(0x91, SUB_INT, "sub-int", k23x, true, kNone, kContinue) \
- V(0x92, MUL_INT, "mul-int", k23x, true, kNone, kContinue) \
- V(0x93, DIV_INT, "div-int", k23x, true, kNone, kContinue | kThrow) \
- V(0x94, REM_INT, "rem-int", k23x, true, kNone, kContinue | kThrow) \
- V(0x95, AND_INT, "and-int", k23x, true, kNone, kContinue) \
- V(0x96, OR_INT, "or-int", k23x, true, kNone, kContinue) \
- V(0x97, XOR_INT, "xor-int", k23x, true, kNone, kContinue) \
- V(0x98, SHL_INT, "shl-int", k23x, true, kNone, kContinue) \
- V(0x99, SHR_INT, "shr-int", k23x, true, kNone, kContinue) \
- V(0x9A, USHR_INT, "ushr-int", k23x, true, kNone, kContinue) \
- V(0x9B, ADD_LONG, "add-long", k23x, true, kNone, kContinue) \
- V(0x9C, SUB_LONG, "sub-long", k23x, true, kNone, kContinue) \
- V(0x9D, MUL_LONG, "mul-long", k23x, true, kNone, kContinue) \
- V(0x9E, DIV_LONG, "div-long", k23x, true, kNone, kContinue | kThrow) \
- V(0x9F, REM_LONG, "rem-long", k23x, true, kNone, kContinue | kThrow) \
- V(0xA0, AND_LONG, "and-long", k23x, true, kNone, kContinue) \
- V(0xA1, OR_LONG, "or-long", k23x, true, kNone, kContinue) \
- V(0xA2, XOR_LONG, "xor-long", k23x, true, kNone, kContinue) \
- V(0xA3, SHL_LONG, "shl-long", k23x, true, kNone, kContinue) \
- V(0xA4, SHR_LONG, "shr-long", k23x, true, kNone, kContinue) \
- V(0xA5, USHR_LONG, "ushr-long", k23x, true, kNone, kContinue) \
- V(0xA6, ADD_FLOAT, "add-float", k23x, true, kNone, kContinue) \
- V(0xA7, SUB_FLOAT, "sub-float", k23x, true, kNone, kContinue) \
- V(0xA8, MUL_FLOAT, "mul-float", k23x, true, kNone, kContinue) \
- V(0xA9, DIV_FLOAT, "div-float", k23x, true, kNone, kContinue) \
- V(0xAA, REM_FLOAT, "rem-float", k23x, true, kNone, kContinue) \
- V(0xAB, ADD_DOUBLE, "add-double", k23x, true, kNone, kContinue) \
- V(0xAC, SUB_DOUBLE, "sub-double", k23x, true, kNone, kContinue) \
- V(0xAD, MUL_DOUBLE, "mul-double", k23x, true, kNone, kContinue) \
- V(0xAE, DIV_DOUBLE, "div-double", k23x, true, kNone, kContinue) \
- V(0xAF, REM_DOUBLE, "rem-double", k23x, true, kNone, kContinue) \
- V(0xB0, ADD_INT_2ADDR, "add-int/2addr", k12x, true, kNone, kContinue) \
- V(0xB1, SUB_INT_2ADDR, "sub-int/2addr", k12x, true, kNone, kContinue) \
- V(0xB2, MUL_INT_2ADDR, "mul-int/2addr", k12x, true, kNone, kContinue) \
- V(0xB3, DIV_INT_2ADDR, "div-int/2addr", k12x, true, kNone, kContinue | kThrow) \
- V(0xB4, REM_INT_2ADDR, "rem-int/2addr", k12x, true, kNone, kContinue | kThrow) \
- V(0xB5, AND_INT_2ADDR, "and-int/2addr", k12x, true, kNone, kContinue) \
- V(0xB6, OR_INT_2ADDR, "or-int/2addr", k12x, true, kNone, kContinue) \
- V(0xB7, XOR_INT_2ADDR, "xor-int/2addr", k12x, true, kNone, kContinue) \
- V(0xB8, SHL_INT_2ADDR, "shl-int/2addr", k12x, true, kNone, kContinue) \
- V(0xB9, SHR_INT_2ADDR, "shr-int/2addr", k12x, true, kNone, kContinue) \
- V(0xBA, USHR_INT_2ADDR, "ushr-int/2addr", k12x, true, kNone, kContinue) \
- V(0xBB, ADD_LONG_2ADDR, "add-long/2addr", k12x, true, kNone, kContinue) \
- V(0xBC, SUB_LONG_2ADDR, "sub-long/2addr", k12x, true, kNone, kContinue) \
- V(0xBD, MUL_LONG_2ADDR, "mul-long/2addr", k12x, true, kNone, kContinue) \
- V(0xBE, DIV_LONG_2ADDR, "div-long/2addr", k12x, true, kNone, kContinue | kThrow) \
- V(0xBF, REM_LONG_2ADDR, "rem-long/2addr", k12x, true, kNone, kContinue | kThrow) \
- V(0xC0, AND_LONG_2ADDR, "and-long/2addr", k12x, true, kNone, kContinue) \
- V(0xC1, OR_LONG_2ADDR, "or-long/2addr", k12x, true, kNone, kContinue) \
- V(0xC2, XOR_LONG_2ADDR, "xor-long/2addr", k12x, true, kNone, kContinue) \
- V(0xC3, SHL_LONG_2ADDR, "shl-long/2addr", k12x, true, kNone, kContinue) \
- V(0xC4, SHR_LONG_2ADDR, "shr-long/2addr", k12x, true, kNone, kContinue) \
- V(0xC5, USHR_LONG_2ADDR, "ushr-long/2addr", k12x, true, kNone, kContinue) \
- V(0xC6, ADD_FLOAT_2ADDR, "add-float/2addr", k12x, true, kNone, kContinue) \
- V(0xC7, SUB_FLOAT_2ADDR, "sub-float/2addr", k12x, true, kNone, kContinue) \
- V(0xC8, MUL_FLOAT_2ADDR, "mul-float/2addr", k12x, true, kNone, kContinue) \
- V(0xC9, DIV_FLOAT_2ADDR, "div-float/2addr", k12x, true, kNone, kContinue) \
- V(0xCA, REM_FLOAT_2ADDR, "rem-float/2addr", k12x, true, kNone, kContinue) \
- V(0xCB, ADD_DOUBLE_2ADDR, "add-double/2addr", k12x, true, kNone, kContinue) \
- V(0xCC, SUB_DOUBLE_2ADDR, "sub-double/2addr", k12x, true, kNone, kContinue) \
- V(0xCD, MUL_DOUBLE_2ADDR, "mul-double/2addr", k12x, true, kNone, kContinue) \
- V(0xCE, DIV_DOUBLE_2ADDR, "div-double/2addr", k12x, true, kNone, kContinue) \
- V(0xCF, REM_DOUBLE_2ADDR, "rem-double/2addr", k12x, true, kNone, kContinue) \
- V(0xD0, ADD_INT_LIT16, "add-int/lit16", k22s, true, kNone, kContinue) \
- V(0xD1, RSUB_INT, "rsub-int", k22s, true, kNone, kContinue) \
- V(0xD2, MUL_INT_LIT16, "mul-int/lit16", k22s, true, kNone, kContinue) \
- V(0xD3, DIV_INT_LIT16, "div-int/lit16", k22s, true, kNone, kContinue | kThrow) \
- V(0xD4, REM_INT_LIT16, "rem-int/lit16", k22s, true, kNone, kContinue | kThrow) \
- V(0xD5, AND_INT_LIT16, "and-int/lit16", k22s, true, kNone, kContinue) \
- V(0xD6, OR_INT_LIT16, "or-int/lit16", k22s, true, kNone, kContinue) \
- V(0xD7, XOR_INT_LIT16, "xor-int/lit16", k22s, true, kNone, kContinue) \
- V(0xD8, ADD_INT_LIT8, "add-int/lit8", k22b, true, kNone, kContinue) \
- V(0xD9, RSUB_INT_LIT8, "rsub-int/lit8", k22b, true, kNone, kContinue) \
- V(0xDA, MUL_INT_LIT8, "mul-int/lit8", k22b, true, kNone, kContinue) \
- V(0xDB, DIV_INT_LIT8, "div-int/lit8", k22b, true, kNone, kContinue | kThrow) \
- V(0xDC, REM_INT_LIT8, "rem-int/lit8", k22b, true, kNone, kContinue | kThrow) \
- V(0xDD, AND_INT_LIT8, "and-int/lit8", k22b, true, kNone, kContinue) \
- V(0xDE, OR_INT_LIT8, "or-int/lit8", k22b, true, kNone, kContinue) \
- V(0xDF, XOR_INT_LIT8, "xor-int/lit8", k22b, true, kNone, kContinue) \
- V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, true, kNone, kContinue) \
- V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, true, kNone, kContinue) \
- V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, true, kNone, kContinue) \
- V(0xE3, UNUSED_E3, "unused-e3", k10x, false, kUnknown, 0) \
- V(0xE4, UNUSED_E4, "unused-e4", k10x, false, kUnknown, 0) \
- V(0xE5, UNUSED_E5, "unused-e5", k10x, false, kUnknown, 0) \
- V(0xE6, UNUSED_E6, "unused-e6", k10x, false, kUnknown, 0) \
- V(0xE7, UNUSED_E7, "unused-e7", k10x, false, kUnknown, 0) \
- V(0xE8, UNUSED_E8, "unused-e8", k10x, false, kUnknown, 0) \
- V(0xE9, UNUSED_E9, "unused-e9", k10x, false, kUnknown, 0) \
- V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0) \
- V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0) \
- V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0) \
- V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0) \
- V(0xEE, UNUSED_EE, "unused-ee", k10x, false, kUnknown, 0) \
- V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0) \
- V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0) \
- V(0xF1, UNUSED_F1, "unused-f1", k10x, false, kUnknown, 0) \
- V(0xF2, UNUSED_F2, "unused-f2", k10x, false, kUnknown, 0) \
- V(0xF3, UNUSED_F3, "unused-f3", k10x, false, kUnknown, 0) \
- V(0xF4, UNUSED_F4, "unused-f4", k10x, false, kUnknown, 0) \
- V(0xF5, UNUSED_F5, "unused-f5", k10x, false, kUnknown, 0) \
- V(0xF6, UNUSED_F6, "unused-f6", k10x, false, kUnknown, 0) \
- V(0xF7, UNUSED_F7, "unused-f7", k10x, false, kUnknown, 0) \
- V(0xF8, UNUSED_F8, "unused-f8", k10x, false, kUnknown, 0) \
- V(0xF9, UNUSED_F9, "unused-f9", k10x, false, kUnknown, 0) \
- V(0xFA, UNUSED_FA, "unused-fa", k10x, false, kUnknown, 0) \
- V(0xFB, UNUSED_FB, "unused-fb", k10x, false, kUnknown, 0) \
- V(0xFC, UNUSED_FC, "unused-fc", k10x, false, kUnknown, 0) \
- V(0xFD, UNUSED_FD, "unused-fd", k10x, false, kUnknown, 0) \
- V(0xFE, UNUSED_FE, "unused-fe", k10x, false, kUnknown, 0) \
- V(0xFF, UNUSED_FF, "unused-ff", k10x, false, kUnknown, 0)
+ V(0x00, NOP, "nop", k10x, false, kNone, kContinue, kVerifyNone) \
+ V(0x01, MOVE, "move", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x02, MOVE_FROM16, "move/from16", k22x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x03, MOVE_16, "move/16", k32x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x04, MOVE_WIDE, "move-wide", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x05, MOVE_WIDE_FROM16, "move-wide/from16", k22x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x06, MOVE_WIDE_16, "move-wide/16", k32x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x07, MOVE_OBJECT, "move-object", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x08, MOVE_OBJECT_FROM16, "move-object/from16", k22x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x09, MOVE_OBJECT_16, "move-object/16", k32x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x0A, MOVE_RESULT, "move-result", k11x, true, kNone, kContinue, kVerifyRegA) \
+ V(0x0B, MOVE_RESULT_WIDE, "move-result-wide", k11x, true, kNone, kContinue, kVerifyRegAWide) \
+ V(0x0C, MOVE_RESULT_OBJECT, "move-result-object", k11x, true, kNone, kContinue, kVerifyRegA) \
+ V(0x0D, MOVE_EXCEPTION, "move-exception", k11x, true, kNone, kContinue, kVerifyRegA) \
+ V(0x0E, RETURN_VOID, "return-void", k10x, false, kNone, kReturn, kVerifyNone) \
+ V(0x0F, RETURN, "return", k11x, false, kNone, kReturn, kVerifyRegA) \
+ V(0x10, RETURN_WIDE, "return-wide", k11x, false, kNone, kReturn, kVerifyRegAWide) \
+ V(0x11, RETURN_OBJECT, "return-object", k11x, false, kNone, kReturn, kVerifyRegA) \
+ V(0x12, CONST_4, "const/4", k11n, true, kNone, kContinue, kVerifyRegA) \
+ V(0x13, CONST_16, "const/16", k21s, true, kNone, kContinue, kVerifyRegA) \
+ V(0x14, CONST, "const", k31i, true, kNone, kContinue, kVerifyRegA) \
+ V(0x15, CONST_HIGH16, "const/high16", k21h, true, kNone, kContinue, kVerifyRegA) \
+ V(0x16, CONST_WIDE_16, "const-wide/16", k21s, true, kNone, kContinue, kVerifyRegAWide) \
+ V(0x17, CONST_WIDE_32, "const-wide/32", k31i, true, kNone, kContinue, kVerifyRegAWide) \
+ V(0x18, CONST_WIDE, "const-wide", k51l, true, kNone, kContinue, kVerifyRegAWide) \
+ V(0x19, CONST_WIDE_HIGH16, "const-wide/high16", k21h, true, kNone, kContinue, kVerifyRegAWide) \
+ V(0x1A, CONST_STRING, "const-string", k21c, true, kStringRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBString) \
+ V(0x1B, CONST_STRING_JUMBO, "const-string/jumbo", k31c, true, kStringRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBString) \
+ V(0x1C, CONST_CLASS, "const-class", k21c, true, kTypeRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBType) \
+ V(0x1D, MONITOR_ENTER, "monitor-enter", k11x, false, kNone, kContinue | kThrow, kVerifyRegA) \
+ V(0x1E, MONITOR_EXIT, "monitor-exit", k11x, false, kNone, kContinue | kThrow, kVerifyRegA) \
+ V(0x1F, CHECK_CAST, "check-cast", k21c, true, kTypeRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBType) \
+ V(0x20, INSTANCE_OF, "instance-of", k22c, true, kTypeRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCType) \
+ V(0x21, ARRAY_LENGTH, "array-length", k12x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0x22, NEW_INSTANCE, "new-instance", k21c, true, kTypeRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBNewInstance) \
+ V(0x23, NEW_ARRAY, "new-array", k22c, true, kTypeRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCNewArray) \
+ V(0x24, FILLED_NEW_ARRAY, "filled-new-array", k35c, false, kTypeRef, kContinue | kThrow, kVerifyRegBType | kVerifyVarArg) \
+ V(0x25, FILLED_NEW_ARRAY_RANGE, "filled-new-array/range", k3rc, false, kTypeRef, kContinue | kThrow, kVerifyRegBType | kVerifyVarArgRange) \
+ V(0x26, FILL_ARRAY_DATA, "fill-array-data", k31t, false, kNone, kContinue, kVerifyRegA | kVerifyArrayData) \
+ V(0x27, THROW, "throw", k11x, false, kNone, kThrow, kVerifyRegA) \
+ V(0x28, GOTO, "goto", k10t, false, kNone, kBranch, kVerifyBranchTarget) \
+ V(0x29, GOTO_16, "goto/16", k20t, false, kNone, kBranch, kVerifyBranchTarget) \
+ V(0x2A, GOTO_32, "goto/32", k30t, false, kNone, kBranch, kVerifyBranchTarget) \
+ V(0x2B, PACKED_SWITCH, "packed-switch", k31t, false, kNone, kContinue | kSwitch, kVerifyRegA | kVerifySwitchTargets) \
+ V(0x2C, SPARSE_SWITCH, "sparse-switch", k31t, false, kNone, kContinue | kSwitch, kVerifyRegA | kVerifySwitchTargets) \
+ V(0x2D, CMPL_FLOAT, "cmpl-float", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x2E, CMPG_FLOAT, "cmpg-float", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x2F, CMPL_DOUBLE, "cmpl-double", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x30, CMPG_DOUBLE, "cmpg-double", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x31, CMP_LONG, "cmp-long", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x32, IF_EQ, "if-eq", k22t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x33, IF_NE, "if-ne", k22t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x34, IF_LT, "if-lt", k22t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x35, IF_GE, "if-ge", k22t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x36, IF_GT, "if-gt", k22t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x37, IF_LE, "if-le", k22t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyRegB | kVerifyBranchTarget) \
+ V(0x38, IF_EQZ, "if-eqz", k21t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x39, IF_NEZ, "if-nez", k21t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3A, IF_LTZ, "if-ltz", k21t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3B, IF_GEZ, "if-gez", k21t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3C, IF_GTZ, "if-gtz", k21t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3D, IF_LEZ, "if-lez", k21t, false, kNone, kContinue | kBranch, kVerifyRegA | kVerifyBranchTarget) \
+ V(0x3E, UNUSED_3E, "unused-3e", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x3F, UNUSED_3F, "unused-3f", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x40, UNUSED_40, "unused-40", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x41, UNUSED_41, "unused-41", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x42, UNUSED_42, "unused-42", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x43, UNUSED_43, "unused-43", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x44, AGET, "aget", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x45, AGET_WIDE, "aget-wide", k23x, true, kNone, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
+ V(0x46, AGET_OBJECT, "aget-object", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x47, AGET_BOOLEAN, "aget-boolean", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x48, AGET_BYTE, "aget-byte", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x49, AGET_CHAR, "aget-char", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4A, AGET_SHORT, "aget-short", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4B, APUT, "aput", k23x, false, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4C, APUT_WIDE, "aput-wide", k23x, false, kNone, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB | kVerifyRegC) \
+ V(0x4D, APUT_OBJECT, "aput-object", k23x, false, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4E, APUT_BOOLEAN, "aput-boolean", k23x, false, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x4F, APUT_BYTE, "aput-byte", k23x, false, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x50, APUT_CHAR, "aput-char", k23x, false, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x51, APUT_SHORT, "aput-short", k23x, false, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x52, IGET, "iget", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x53, IGET_WIDE, "iget-wide", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
+ V(0x54, IGET_OBJECT, "iget-object", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x55, IGET_BOOLEAN, "iget-boolean", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x56, IGET_BYTE, "iget-byte", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x57, IGET_CHAR, "iget-char", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x58, IGET_SHORT, "iget-short", k22c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x59, IPUT, "iput", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5A, IPUT_WIDE, "iput-wide", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegB | kVerifyRegCField) \
+ V(0x5B, IPUT_OBJECT, "iput-object", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5C, IPUT_BOOLEAN, "iput-boolean", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5D, IPUT_BYTE, "iput-byte", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5E, IPUT_CHAR, "iput-char", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x5F, IPUT_SHORT, "iput-short", k22c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegCField) \
+ V(0x60, SGET, "sget", k21c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x61, SGET_WIDE, "sget-wide", k21c, true, kFieldRef, kContinue | kThrow, kVerifyRegAWide | kVerifyRegBField) \
+ V(0x62, SGET_OBJECT, "sget-object", k21c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x63, SGET_BOOLEAN, "sget-boolean", k21c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x64, SGET_BYTE, "sget-byte", k21c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x65, SGET_CHAR, "sget-char", k21c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x66, SGET_SHORT, "sget-short", k21c, true, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x67, SPUT, "sput", k21c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x68, SPUT_WIDE, "sput-wide", k21c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x69, SPUT_OBJECT, "sput-object", k21c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x6A, SPUT_BOOLEAN, "sput-boolean", k21c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x6B, SPUT_BYTE, "sput-byte", k21c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x6C, SPUT_CHAR, "sput-char", k21c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x6D, SPUT_SHORT, "sput-short", k21c, false, kFieldRef, kContinue | kThrow, kVerifyRegA | kVerifyRegBField) \
+ V(0x6E, INVOKE_VIRTUAL, "invoke-virtual", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
+ V(0x6F, INVOKE_SUPER, "invoke-super", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
+ V(0x70, INVOKE_DIRECT, "invoke-direct", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
+ V(0x71, INVOKE_STATIC, "invoke-static", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
+ V(0x72, INVOKE_INTERFACE, "invoke-interface", k35c, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArg) \
+ V(0x73, UNUSED_73, "unused-73", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x74, INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
+ V(0x75, INVOKE_SUPER_RANGE, "invoke-super/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
+ V(0x76, INVOKE_DIRECT_RANGE, "invoke-direct/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
+ V(0x77, INVOKE_STATIC_RANGE, "invoke-static/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
+ V(0x78, INVOKE_INTERFACE_RANGE, "invoke-interface/range", k3rc, false, kMethodRef, kContinue | kThrow | kInvoke, kVerifyRegBMethod | kVerifyVarArgRange) \
+ V(0x79, UNUSED_79, "unused-79", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x7A, UNUSED_7A, "unused-7a", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0x7B, NEG_INT, "neg-int", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x7C, NOT_INT, "not-int", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x7D, NEG_LONG, "neg-long", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x7E, NOT_LONG, "not-long", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x7F, NEG_FLOAT, "neg-float", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x80, NEG_DOUBLE, "neg-double", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x81, INT_TO_LONG, "int-to-long", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegB) \
+ V(0x82, INT_TO_FLOAT, "int-to-float", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x83, INT_TO_DOUBLE, "int-to-double", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegB) \
+ V(0x84, LONG_TO_INT, "long-to-int", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegBWide) \
+ V(0x85, LONG_TO_FLOAT, "long-to-float", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegBWide) \
+ V(0x86, LONG_TO_DOUBLE, "long-to-double", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x87, FLOAT_TO_INT, "float-to-int", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x88, FLOAT_TO_LONG, "float-to-long", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegB) \
+ V(0x89, FLOAT_TO_DOUBLE, "float-to-double", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegB) \
+ V(0x8A, DOUBLE_TO_INT, "double-to-int", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegBWide) \
+ V(0x8B, DOUBLE_TO_LONG, "double-to-long", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0x8C, DOUBLE_TO_FLOAT, "double-to-float", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegBWide) \
+ V(0x8D, INT_TO_BYTE, "int-to-byte", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x8E, INT_TO_CHAR, "int-to-char", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x8F, INT_TO_SHORT, "int-to-short", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0x90, ADD_INT, "add-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x91, SUB_INT, "sub-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x92, MUL_INT, "mul-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x93, DIV_INT, "div-int", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x94, REM_INT, "rem-int", k23x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x95, AND_INT, "and-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x96, OR_INT, "or-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x97, XOR_INT, "xor-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x98, SHL_INT, "shl-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x99, SHR_INT, "shr-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x9A, USHR_INT, "ushr-int", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0x9B, ADD_LONG, "add-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9C, SUB_LONG, "sub-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9D, MUL_LONG, "mul-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9E, DIV_LONG, "div-long", k23x, true, kNone, kContinue | kThrow, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0x9F, REM_LONG, "rem-long", k23x, true, kNone, kContinue | kThrow, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA0, AND_LONG, "and-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA1, OR_LONG, "or-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA2, XOR_LONG, "xor-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xA3, SHL_LONG, "shl-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
+ V(0xA4, SHR_LONG, "shr-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
+ V(0xA5, USHR_LONG, "ushr-long", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegC) \
+ V(0xA6, ADD_FLOAT, "add-float", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xA7, SUB_FLOAT, "sub-float", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xA8, MUL_FLOAT, "mul-float", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xA9, DIV_FLOAT, "div-float", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xAA, REM_FLOAT, "rem-float", k23x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB | kVerifyRegC) \
+ V(0xAB, ADD_DOUBLE, "add-double", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAC, SUB_DOUBLE, "sub-double", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAD, MUL_DOUBLE, "mul-double", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAE, DIV_DOUBLE, "div-double", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xAF, REM_DOUBLE, "rem-double", k23x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide | kVerifyRegCWide) \
+ V(0xB0, ADD_INT_2ADDR, "add-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xB1, SUB_INT_2ADDR, "sub-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xB2, MUL_INT_2ADDR, "mul-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xB3, DIV_INT_2ADDR, "div-int/2addr", k12x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xB4, REM_INT_2ADDR, "rem-int/2addr", k12x, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xB5, AND_INT_2ADDR, "and-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xB6, OR_INT_2ADDR, "or-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xB7, XOR_INT_2ADDR, "xor-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xB8, SHL_INT_2ADDR, "shl-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xB9, SHR_INT_2ADDR, "shr-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xBA, USHR_INT_2ADDR, "ushr-int/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xBB, ADD_LONG_2ADDR, "add-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBC, SUB_LONG_2ADDR, "sub-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBD, MUL_LONG_2ADDR, "mul-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBE, DIV_LONG_2ADDR, "div-long/2addr", k12x, true, kNone, kContinue | kThrow, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xBF, REM_LONG_2ADDR, "rem-long/2addr", k12x, true, kNone, kContinue | kThrow, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC0, AND_LONG_2ADDR, "and-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC1, OR_LONG_2ADDR, "or-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC2, XOR_LONG_2ADDR, "xor-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xC3, SHL_LONG_2ADDR, "shl-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegB) \
+ V(0xC4, SHR_LONG_2ADDR, "shr-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegB) \
+ V(0xC5, USHR_LONG_2ADDR, "ushr-long/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegB) \
+ V(0xC6, ADD_FLOAT_2ADDR, "add-float/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xC7, SUB_FLOAT_2ADDR, "sub-float/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xC8, MUL_FLOAT_2ADDR, "mul-float/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xC9, DIV_FLOAT_2ADDR, "div-float/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xCA, REM_FLOAT_2ADDR, "rem-float/2addr", k12x, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xCB, ADD_DOUBLE_2ADDR, "add-double/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCC, SUB_DOUBLE_2ADDR, "sub-double/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCD, MUL_DOUBLE_2ADDR, "mul-double/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCE, DIV_DOUBLE_2ADDR, "div-double/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xCF, REM_DOUBLE_2ADDR, "rem-double/2addr", k12x, true, kNone, kContinue, kVerifyRegAWide | kVerifyRegBWide) \
+ V(0xD0, ADD_INT_LIT16, "add-int/lit16", k22s, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xD1, RSUB_INT, "rsub-int", k22s, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xD2, MUL_INT_LIT16, "mul-int/lit16", k22s, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xD3, DIV_INT_LIT16, "div-int/lit16", k22s, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xD4, REM_INT_LIT16, "rem-int/lit16", k22s, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xD5, AND_INT_LIT16, "and-int/lit16", k22s, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xD6, OR_INT_LIT16, "or-int/lit16", k22s, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xD7, XOR_INT_LIT16, "xor-int/lit16", k22s, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xD8, ADD_INT_LIT8, "add-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xD9, RSUB_INT_LIT8, "rsub-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xDA, MUL_INT_LIT8, "mul-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xDB, DIV_INT_LIT8, "div-int/lit8", k22b, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xDC, REM_INT_LIT8, "rem-int/lit8", k22b, true, kNone, kContinue | kThrow, kVerifyRegA | kVerifyRegB) \
+ V(0xDD, AND_INT_LIT8, "and-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xDE, OR_INT_LIT8, "or-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xDF, XOR_INT_LIT8, "xor-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xE0, SHL_INT_LIT8, "shl-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xE1, SHR_INT_LIT8, "shr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xE2, USHR_INT_LIT8, "ushr-int/lit8", k22b, true, kNone, kContinue, kVerifyRegA | kVerifyRegB) \
+ V(0xE3, UNUSED_E3, "unused-e3", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xE4, UNUSED_E4, "unused-e4", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xE5, UNUSED_E5, "unused-e5", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xE6, UNUSED_E6, "unused-e6", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xE7, UNUSED_E7, "unused-e7", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xE8, UNUSED_E8, "unused-e8", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xE9, UNUSED_E9, "unused-e9", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xEA, UNUSED_EA, "unused-ea", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xEB, UNUSED_EB, "unused-eb", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xEC, UNUSED_EC, "unused-ec", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xED, UNUSED_ED, "unused-ed", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xEE, UNUSED_EE, "unused-ee", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xEF, UNUSED_EF, "unused-ef", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF0, UNUSED_F0, "unused-f0", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF1, UNUSED_F1, "unused-f1", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF2, UNUSED_F2, "unused-f2", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF3, UNUSED_F3, "unused-f3", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF4, UNUSED_F4, "unused-f4", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF5, UNUSED_F5, "unused-f5", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF6, UNUSED_F6, "unused-f6", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF7, UNUSED_F7, "unused-f7", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF8, UNUSED_F8, "unused-f8", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xF9, UNUSED_F9, "unused-f9", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xFA, UNUSED_FA, "unused-fa", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xFB, UNUSED_FB, "unused-fb", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xFC, UNUSED_FC, "unused-fc", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xFD, UNUSED_FD, "unused-fd", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xFE, UNUSED_FE, "unused-fe", k10x, false, kUnknown, 0, kVerifyError) \
+ V(0xFF, UNUSED_FF, "unused-ff", k10x, false, kUnknown, 0, kVerifyError)
+
+#define DEX_INSTRUCTION_FORMAT_LIST(V) \
+ V(k10x) \
+ V(k12x) \
+ V(k11n) \
+ V(k11x) \
+ V(k10t) \
+ V(k20t) \
+ V(k22x) \
+ V(k21t) \
+ V(k21s) \
+ V(k21h) \
+ V(k21c) \
+ V(k23x) \
+ V(k22b) \
+ V(k22t) \
+ V(k22s) \
+ V(k22c) \
+ V(k32x) \
+ V(k30t) \
+ V(k31t) \
+ V(k31i) \
+ V(k31c) \
+ V(k35c) \
+ V(k3rc) \
+ V(k51l)
diff --git a/src/dex_instruction_visitor.h b/src/dex_instruction_visitor.h
index 73b4b3e..6a6bc3e 100644
--- a/src/dex_instruction_visitor.h
+++ b/src/dex_instruction_visitor.h
@@ -11,17 +11,17 @@
template<typename T>
class DexInstructionVisitor {
public:
- void Visit(uint16_t* code, size_t size) {
+ void Visit(const uint16_t* code, size_t size) {
T* derived = static_cast<T*>(this);
- byte* ptr = reinterpret_cast<byte*>(code);
- byte* end = ptr + size;
+ const byte* ptr = reinterpret_cast<const byte*>(code);
+ const byte* end = ptr + size;
while (ptr != end) {
- Instruction* inst = Instruction::At(ptr);
+ const Instruction* inst = Instruction::At(ptr);
switch (inst->Opcode()) {
-#define INSTRUCTION_CASE(o, cname, p, f, r, i, a) \
- case Instruction::cname: { \
- derived->Do_ ## cname(inst); \
- break; \
+#define INSTRUCTION_CASE(o, cname, p, f, r, i, a, v) \
+ case Instruction::cname: { \
+ derived->Do_ ## cname(inst); \
+ break; \
}
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_CASE)
@@ -30,17 +30,17 @@
default:
CHECK(true);
}
- ptr += inst->Size();
+ ptr += inst->Size() * sizeof(uint16_t);
CHECK_LE(ptr, end);
}
}
private:
// Specific handlers for each instruction.
-#define INSTRUCTION_VISITOR(o, cname, p, f, r, i, a) \
- void Do_ ## cname(Instruction* inst) { \
- T* derived = static_cast<T*>(this); \
- derived->Do_Default(inst); \
+#define INSTRUCTION_VISITOR(o, cname, p, f, r, i, a, v) \
+ void Do_ ## cname(const Instruction* inst) { \
+ T* derived = static_cast<T*>(this); \
+ derived->Do_Default(inst); \
}
#include "dex_instruction_list.h"
DEX_INSTRUCTION_LIST(INSTRUCTION_VISITOR)
@@ -48,11 +48,12 @@
#undef INSTRUCTION_VISITOR
// The default instruction handler.
- void Do_Default(Instruction* inst) {
+ void Do_Default(const Instruction* inst) {
return;
}
};
+
} // namespace art
#endif // ART_SRC_DEX_INSTRUCTION_VISITOR_H_
diff --git a/src/dex_instruction_visitor_test.cc b/src/dex_instruction_visitor_test.cc
index ff24e52..0edf160 100644
--- a/src/dex_instruction_visitor_test.cc
+++ b/src/dex_instruction_visitor_test.cc
@@ -20,34 +20,34 @@
CountVisitor() : count_(0) {}
- void Do_Default(Instruction* inst) {
+ void Do_Default(const Instruction* inst) {
++count_;
}
};
TEST(InstructionTest, Count) {
CountVisitor v0;
- uint16_t c0[] = {};
+ const uint16_t c0[] = {};
v0.Visit(c0, sizeof(c0));
EXPECT_EQ(0, v0.count_);
CountVisitor v1;
- uint16_t c1[] = { 0 };
+ const uint16_t c1[] = { 0 };
v1.Visit(c1, sizeof(c1));
EXPECT_EQ(1, v1.count_);
CountVisitor v2;
- uint16_t c2[] = { 0, 0 };
+ const uint16_t c2[] = { 0, 0 };
v2.Visit(c2, sizeof(c2));
EXPECT_EQ(2, v2.count_);
CountVisitor v3;
- uint16_t c3[] = { 0, 0, 0, };
+ const uint16_t c3[] = { 0, 0, 0, };
v3.Visit(c3, sizeof(c3));
EXPECT_EQ(3, v3.count_);
CountVisitor v4;
- uint16_t c4[] = { 0, 0, 0, 0 };
+ const uint16_t c4[] = { 0, 0, 0, 0 };
v4.Visit(c4, sizeof(c4));
EXPECT_EQ(4, v4.count_);
}
diff --git a/src/dex_verifier.cc b/src/dex_verifier.cc
index f55b20e..e0702b7 100644
--- a/src/dex_verifier.cc
+++ b/src/dex_verifier.cc
@@ -1,5 +1,9 @@
// Copyright 2011 Google Inc. All Rights Reserved.
+#include "class_linker.h"
+#include "dex_file.h"
+#include "dex_instruction.h"
+#include "dex_instruction_visitor.h"
#include "dex_verifier.h"
#include <iostream>
@@ -9,6 +13,620 @@
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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * Visited or changed?
+ */
+inline bool InsnIsVisitedOrChanged(const uint32_t insn_flags[], int addr) {
+ return (insn_flags[addr] & (DexVerify::kInsnFlagVisited |
+ DexVerify::kInsnFlagChanged)) != 0;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+}
+
+
+/*
+ * 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,
+ bool* pConditional, bool* selfOkay) {
+ const uint16_t* insns = code_item->insns_ + cur_offset;
+
+ switch (*insns & 0xff) {
+ case Instruction::GOTO:
+ *pOffset = ((int16_t) *insns) >> 8;
+ *pConditional = false;
+ *selfOkay = false;
+ break;
+ case Instruction::GOTO_32:
+ *pOffset = insns[1] | (((uint32_t) insns[2]) << 16);
+ *pConditional = false;
+ *selfOkay = true;
+ break;
+ case Instruction::GOTO_16:
+ *pOffset = (int16_t) insns[1];
+ *pConditional = false;
+ *selfOkay = false;
+ break;
+ case Instruction::IF_EQ:
+ case Instruction::IF_NE:
+ case Instruction::IF_LT:
+ case Instruction::IF_GE:
+ case Instruction::IF_GT:
+ case Instruction::IF_LE:
+ case Instruction::IF_EQZ:
+ case Instruction::IF_NEZ:
+ case Instruction::IF_LTZ:
+ case Instruction::IF_GEZ:
+ case Instruction::IF_GTZ:
+ case Instruction::IF_LEZ:
+ *pOffset = (int16_t) insns[1];
+ *pConditional = true;
+ *selfOkay = false;
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ 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,
+ 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* array_data;
+ int32_t array_data_offset;
+
+ assert(cur_offset < insn_count);
+
+ /* make sure the start of the array data table is in range */
+ array_data_offset = insns[1] | (((int32_t) insns[2]) << 16);
+ if ((int32_t) cur_offset + array_data_offset < 0 ||
+ cur_offset + array_data_offset + 2 >= insn_count)
+ {
+ LOG(ERROR) << "VFY: invalid array data start: at " << cur_offset
+ << ", data offset " << array_data_offset << ", count "
+ << insn_count;
+ return false;
+ }
+
+ /* offset to array data table is a relative branch-style offset */
+ array_data = insns + array_data_offset;
+
+ /* make sure the table is 32-bit aligned */
+ if ((((uint32_t) array_data) & 0x03) != 0) {
+ LOG(ERROR) << "VFY: unaligned array data table: at " << cur_offset
+ << ", data offset " << array_data_offset;
+ return false;
+ }
+
+ uint32_t value_width = array_data[1];
+ uint32_t value_count = *(uint32_t*) (&array_data[2]);
+ uint32_t table_size = 4 + (value_width * value_count + 1) / 2;
+
+ /* make sure the end of the switch is in range */
+ if (cur_offset + array_data_offset + table_size > insn_count) {
+ LOG(ERROR) << "VFY: invalid array data end: at " << cur_offset
+ << ", data offset " << array_data_offset << ", end "
+ << cur_offset + array_data_offset + table_size << ", count "
+ << insn_count;
+ return false;
+ }
+
+ 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) {
+ if (idx >= dex_file->GetHeader().type_ids_size_) {
+ LOG(ERROR) << "VFY: bad type index " << idx << " (max "
+ << dex_file->GetHeader().type_ids_size_ << ")";
+ return false;
+ }
+
+ const char* descriptor = dex_file->dexStringByTypeIdx(idx);
+ if (descriptor[0] != 'L') {
+ LOG(ERROR) << "VFY: can't call new-instance on type '"
+ << descriptor << "'";
+ return false;
+ }
+
+ 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) {
+ if (idx >= dex_file->GetHeader().type_ids_size_) {
+ LOG(ERROR) << "VFY: bad type index " << idx << " (max "
+ << dex_file->GetHeader().type_ids_size_ << ")";
+ return false;
+ }
+
+ int bracket_count = 0;
+ const char* descriptor = dex_file->dexStringByTypeIdx(idx);
+ const char* cp = descriptor;
+ while (*cp++ == '[')
+ bracket_count++;
+
+ if (bracket_count == 0) {
+ /* The given class must be an array type. */
+ LOG(ERROR) << "VFY: can't new-array class '" << descriptor
+ << "' (not an array)";
+ return false;
+ } else if (bracket_count > 255) {
+ /* It is illegal to create an array of more than 255 dimensions. */
+ LOG(ERROR) << "VFY: can't new-array class '" << descriptor
+ << "' (exceeds limit)";
+ return false;
+ }
+
+ 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) {
+ if (idx >= dex_file->GetHeader().type_ids_size_) {
+ LOG(ERROR) << "VFY: bad type index " << idx << " (max "
+ << dex_file->GetHeader().type_ids_size_ << ")";
+ return false;
+ }
+ 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) {
+ if (idx >= dex_file->GetHeader().field_ids_size_) {
+ LOG(ERROR) << "VFY: bad field index " << idx << " (max "
+ << dex_file->GetHeader().field_ids_size_ << ")";
+ return false;
+ }
+ 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) {
+ if (idx >= dex_file->GetHeader().method_ids_size_) {
+ LOG(ERROR) << "VFY: bad method index " << idx << " (max "
+ << dex_file->GetHeader().method_ids_size_ << ")";
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Ensure that the string index is in the valid range.
+ */
+static bool 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_ << ")";
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Ensure that the register index is valid for this code item.
+ */
+static bool 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_ << ")";
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Ensure that the wide register index is valid for this code item.
+ */
+static bool 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
+ << "+1 >= " << code_item->registers_size_ << ")";
+ return false;
+ }
+ 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[]) {
+ uint16_t registers_size = code_item->registers_size_;
+ uint32_t idx;
+
+ if (vA > 5) {
+ LOG(ERROR) << "VFY: invalid arg count (" << vA << ") in non-range invoke)";
+ return false;
+ }
+
+ for (idx = 0; idx < vA; idx++) {
+ if (arg[idx] > registers_size) {
+ LOG(ERROR) << "VFY: invalid reg index (" << arg[idx]
+ << ") in non-range invoke (> " << registers_size << ")";
+ return false;
+ }
+ }
+
+ 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,
+ uint32_t vA, uint32_t vC) {
+ uint16_t registers_size = code_item->registers_size_;
+
+ /*
+ * vA/vC are unsigned 8-bit/16-bit quantities for /range instructions,
+ * so there's no risk of integer overflow when adding them here.
+ */
+ if (vA + vC > registers_size) {
+ LOG(ERROR) << "VFY: invalid reg index " << vA << "+" << vC
+ << " in range invoke (> " << registers_size << ")";
+ return false;
+ }
+
+ 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) {
+ const uint32_t insn_count = code_item->insns_size_;
+ const uint16_t* insns = code_item->insns_ + cur_offset;
+ const uint16_t* switch_insns;
+ uint16_t expected_signature;
+ uint32_t switch_count, table_size;
+ int32_t switch_offset, keys_offset, targets_offset;
+ int32_t offset, abs_offset;
+ uint32_t targ;
+
+ /* make sure the start of the switch is in range */
+ switch_offset = insns[1] | ((int32_t) insns[2]) << 16;
+ if ((int32_t) cur_offset + switch_offset < 0 ||
+ cur_offset + switch_offset + 2 >= insn_count) {
+ LOG(ERROR) << "VFY: invalid switch start: at " << cur_offset
+ << ", switch offset " << switch_offset << ", count "
+ << insn_count;
+ return false;
+ }
+
+ /* offset to switch table is a relative branch-style offset */
+ switch_insns = insns + switch_offset;
+
+ /* make sure the table is 32-bit aligned */
+ if ((((uint32_t) switch_insns) & 0x03) != 0) {
+ LOG(ERROR) << "VFY: unaligned switch table: at " << cur_offset
+ << ", switch offset " << switch_offset;
+ return false;
+ }
+
+ switch_count = switch_insns[1];
+
+ if ((*insns & 0xff) == Instruction::PACKED_SWITCH) {
+ /* 0=sig, 1=count, 2/3=firstKey */
+ targets_offset = 4;
+ keys_offset = -1;
+ expected_signature = Instruction::kPackedSwitchSignature;
+ } else {
+ /* 0=sig, 1=count, 2..count*2 = keys */
+ keys_offset = 2;
+ targets_offset = 2 + 2 * switch_count;
+ expected_signature = Instruction::kSparseSwitchSignature;
+ }
+ 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 << ")";
+ return false;
+ }
+
+ /* make sure the end of the switch is in range */
+ if (cur_offset + switch_offset + table_size > (uint32_t) insn_count) {
+ LOG(ERROR) << "VFY: invalid switch end: at " << cur_offset
+ << ", switch offset " << switch_offset << ", end "
+ << cur_offset + switch_offset + table_size << ", count "
+ << insn_count;
+ return false;
+ }
+
+ /* for a sparse switch, verify the keys are in ascending order */
+ if (keys_offset > 0 && switch_count > 1) {
+ int32_t last_key;
+
+ last_key = switch_insns[keys_offset] |
+ (switch_insns[keys_offset + 1] << 16);
+ for (targ = 1; targ < switch_count; targ++) {
+ int32_t key = (int32_t) switch_insns[keys_offset + targ * 2] |
+ (int32_t) (switch_insns[keys_offset + targ * 2 + 1] << 16);
+ if (key <= last_key) {
+ LOG(ERROR) << "VFY: invalid packed switch: last key=" << last_key
+ << ", this=" << key;
+ return false;
+ }
+
+ last_key = key;
+ }
+ }
+
+ /* verify each switch target */
+ for (targ = 0; targ < switch_count; targ++) {
+ offset = (int32_t) switch_insns[targets_offset + targ * 2] |
+ (int32_t) (switch_insns[targets_offset + targ * 2 + 1] << 16);
+ abs_offset = cur_offset + offset;
+
+ 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 << "]";
+ return false;
+ }
+ InsnSetBranchTarget(insn_flags, abs_offset, true);
+ }
+
+ 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_;
+ int32_t offset, abs_offset;
+ bool isConditional, selfOkay;
+
+ if (!GetBranchOffset(code_item, insn_flags, cur_offset, &offset,
+ &isConditional, &selfOkay))
+ return false;
+
+ if (!selfOkay && offset == 0) {
+ LOG(ERROR) << "VFY: branch offset of zero not allowed at" << cur_offset;
+ return false;
+ }
+
+ /*
+ * Check for 32-bit overflow. This isn't strictly necessary if we can
+ * depend on the VM to have identical "wrap-around" behavior, but
+ * 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;
+ return false;
+ }
+ abs_offset = cur_offset + offset;
+ if (abs_offset < 0 || abs_offset >= insn_count ||
+ !InsnIsOpcode(insn_flags, abs_offset))
+ {
+ LOG(ERROR) << "VFY: invalid branch target " << offset << " (-> "
+ << abs_offset << ") at " << cur_offset;
+ return false;
+ }
+ InsnSetBranchTarget(insn_flags, abs_offset, true);
+
+ return true;
+}
+
+bool CheckInsnWidth(const uint16_t* insns, uint32_t insns_size,
+ uint32_t insn_flags[]) {
+ const byte* ptr = reinterpret_cast<const byte*>(insns);
+ const Instruction* inst = Instruction::At(ptr);
+
+ size_t width = 0;
+
+ while (width < insns_size) {
+ 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;
+ }
+
+ 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[]) {
+ 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, true);
+ }
+ }
+
+ /* 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, true);
+ }
+
+ handlers_ptr = iterator.GetData();
+ }
+
+ return true;
+}
+
+
+
bool DexVerify::VerifyClass(Class* klass) {
if (klass->IsVerified()) {
return true;
@@ -16,14 +634,16 @@
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();
+ 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();
+ LOG(ERROR) << "Verifier rejected class "
+ << klass->GetDescriptor()->ToModifiedUtf8();
return false;
}
}
@@ -31,7 +651,190 @@
}
bool DexVerify::VerifyMethod(Method* method) {
- return true; // TODO
+ 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->code_off_);
+
+ /*
+ * 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.
+ */
+ 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)) {
+ delete insn_flags;
+ return false;
+ }
+
+ /*
+ * Flag instructions guarded by a "try" block and check exception handlers.
+ */
+ if (!ScanTryCatchBlocks(code_item, insn_flags)) {
+ delete insn_flags;
+ return false;
+ }
+
+ /*
+ * Perform static instruction verification.
+ */
+ if (!VerifyInstructions(&dex_file, code_item, insn_flags)) {
+ delete insn_flags;
+ return false;
+ }
+
+ /*
+ * TODO: Code flow analysis
+ */
+
+ delete insn_flags;
+ return true;
}
+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();
+ }
+ return true;
+}
+
+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];
+
+ 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);
+ break;
+ case Instruction::kVerifyRegAWide:
+ result &= CheckWideRegisterIndex(code_item, vA);
+ break;
+ }
+
+ switch (argumentB) {
+ case Instruction::kVerifyRegB:
+ result &= CheckRegisterIndex(code_item, vB);
+ break;
+ case Instruction::kVerifyRegBField:
+ result &= CheckFieldIndex(dex_file, vB);
+ 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);
+ 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;
+ }
+
+ 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;
+ }
+
+ return result;
+};
+
} // namespace art
diff --git a/src/dex_verifier.h b/src/dex_verifier.h
index 03b2c96..29dc140 100644
--- a/src/dex_verifier.h
+++ b/src/dex_verifier.h
@@ -5,15 +5,33 @@
#include "macros.h"
#include "object.h"
+#include "dex_instruction.h"
namespace art {
class DexVerify {
public:
+ enum {
+ kInsnFlagWidthMask = 0x0000ffff,
+ kInsnFlagInTry = (1 << 16),
+ kInsnFlagBranchTarget = (1 << 17),
+ kInsnFlagGcPoint = (1 << 18),
+ kInsnFlagVisited = (1 << 30),
+ kInsnFlagChanged = (1 << 31),
+ };
+
static bool VerifyClass(Class* klass);
private:
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);
};
diff --git a/src/dex_verifier_test.cc b/src/dex_verifier_test.cc
new file mode 100644
index 0000000..f1d99e6
--- /dev/null
+++ b/src/dex_verifier_test.cc
@@ -0,0 +1,49 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "class_linker.h"
+#include "common_test.h"
+#include "compiler_test.h"
+#include "dex_file.h"
+#include "dex_verifier.h"
+#include "scoped_ptr.h"
+
+#include <stdio.h>
+#include "gtest/gtest.h"
+
+namespace art {
+
+class DexVerifierTest : public CommonTest {
+ protected:
+ void VerifyClass(ClassLoader* class_loader, const StringPiece& descriptor) {
+ ASSERT_TRUE(descriptor != NULL);
+ Class* klass = class_linker_->FindSystemClass(descriptor);
+
+ // Verify the class
+ ASSERT_TRUE(DexVerify::VerifyClass(klass));
+ }
+
+ void VerifyDexFile(const DexFile* dex, ClassLoader* class_loader) {
+ ASSERT_TRUE(dex != NULL);
+
+ // Verify all the classes defined in this file
+ for (size_t i = 0; i < dex->NumClassDefs(); i++) {
+ const DexFile::ClassDef& class_def = dex->GetClassDef(i);
+ const char* descriptor = dex->GetClassDescriptor(class_def);
+ VerifyClass(class_loader, descriptor);
+ }
+ }
+
+};
+
+TEST_F(DexVerifierTest, LibCore) {
+ VerifyDexFile(java_lang_dex_file_.get(), NULL);
+}
+
+TEST_F(DexVerifierTest, IntMath) {
+ const DexFile* dex = OpenDexFileBase64(kIntMathDex, "kIntMathDex");
+ PathClassLoader* class_loader = AllocPathClassLoader(dex);
+ Class* klass = class_linker_->FindClass("LIntMath;", class_loader);
+ ASSERT_TRUE(DexVerify::VerifyClass(klass));
+}
+
+} // namespace art