Add support for the invoke-object-init/range opcode in ICS
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
index d9ebe13..5a79b9e 100644
--- a/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Analysis/MethodAnalyzer.java
@@ -1069,6 +1069,9 @@
             case INVOKE_DIRECT_EMPTY:
                 analyzeInvokeDirectEmpty(analyzedInstruction);
                 return true;
+            case INVOKE_OBJECT_INIT_RANGE:
+                analyzeInvokeObjectInitRange(analyzedInstruction);
+                return true;
             case IGET_QUICK:
             case IGET_WIDE_QUICK:
             case IGET_OBJECT_QUICK:
@@ -1568,6 +1571,7 @@
             case EXECUTE_INLINE:
             case EXECUTE_INLINE_RANGE:
             case INVOKE_DIRECT_EMPTY:
+            case INVOKE_OBJECT_INIT_RANGE:
             case IGET_QUICK:
             case IGET_WIDE_QUICK:
             case IGET_OBJECT_QUICK:
@@ -3470,6 +3474,17 @@
         analyzeInstruction(analyzedInstruction);
     }
 
+    private void analyzeInvokeObjectInitRange(AnalyzedInstruction analyzedInstruction) {
+        Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
+
+        Instruction3rc deodexedInstruction = new Instruction3rc(Opcode.INVOKE_DIRECT_RANGE,
+                (short)instruction.getRegCount(), instruction.getStartRegister(), instruction.getReferencedItem());
+
+        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+
+        analyzeInstruction(analyzedInstruction);
+    }
+
     private boolean analyzeIputIgetQuick(AnalyzedInstruction analyzedInstruction) {
         Instruction22cs instruction = (Instruction22cs)analyzedInstruction.instruction;
 
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java
index 4beace5..c2abc62 100644
--- a/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Format/Instruction3rc.java
@@ -117,7 +117,8 @@
             if (type.charAt(1) == 'J' || type.charAt(1) == 'D') {
                 throw new RuntimeException("The type cannot be an array of longs or doubles");
             }
-        } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value) {
+        } else if (opcode.value >= INVOKE_VIRTUAL_RANGE.value && opcode.value <= INVOKE_INTERFACE_RANGE.value ||
+                opcode == INVOKE_OBJECT_INIT_RANGE) {
             //check data for invoke-*/range opcodes
             MethodIdItem methodIdItem = (MethodIdItem) item;
             int parameterRegisterCount = methodIdItem.getPrototype().getParameterRegisterCount();
diff --git a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java
index 1c332e3..93bbdb7 100644
--- a/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java
+++ b/dexlib/src/main/java/org/jf/dexlib/Code/Opcode.java
@@ -267,6 +267,7 @@
     EXECUTE_INLINE((short)0xee, "execute-inline", ReferenceType.none,  Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
     EXECUTE_INLINE_RANGE((short)0xef, "execute-inline/range", ReferenceType.none,  Format.Format3rmi,  Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
     INVOKE_DIRECT_EMPTY((short)0xf0, "invoke-direct-empty", ReferenceType.method,  Format.Format35s, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", ReferenceType.method,  Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
     IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.none,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
     IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", ReferenceType.none,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
     IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", ReferenceType.none,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
@@ -351,13 +352,16 @@
         opcodesByName = new HashMap<Integer, Opcode>();
 
         for (Opcode opcode: Opcode.values()) {
-            if (((opcode.value >> 8) & 0xFF) == 0x00) {
-                opcodesByValue[opcode.value & 0xFF] = opcode;
-            } else {
-                assert ((opcode.value >> 8) & 0xFF) == 0xFF;
-                expandedOpcodesByValue[opcode.value & 0xFF] = opcode;
+            //INVOKE_DIRECT_EMPTY was changed to INVOKE_OBJECT_INIT_RANGE in ICS
+            if (opcode != INVOKE_DIRECT_EMPTY) {
+                if (((opcode.value >> 8) & 0xFF) == 0x00) {
+                    opcodesByValue[opcode.value & 0xFF] = opcode;
+                } else {
+                    assert ((opcode.value >> 8) & 0xFF) == 0xFF;
+                    expandedOpcodesByValue[opcode.value & 0xFF] = opcode;
+                }
+                opcodesByName.put(opcode.name.hashCode(), opcode);
             }
-            opcodesByName.put(opcode.name.hashCode(), opcode);
         }
     }
 
@@ -386,6 +390,18 @@
         }
     }
 
+    private static void addOpcodes(Opcode... toAdd) {
+        for (Opcode opcode: toAdd) {
+            if (((opcode.value >> 8) & 0xFF) == 0x00) {
+                opcodesByValue[opcode.value & 0xFF] = opcode;
+            } else {
+                assert ((opcode.value >> 8) & 0xFF) == 0xFF;
+                expandedOpcodesByValue[opcode.value & 0xFF] = opcode;
+            }
+            opcodesByName.put(opcode.name.hashCode(), opcode);
+        }
+    }
+
     /**
      * This will add/remove/replace various opcodes in the value/name maps as needed,
      * based on the idiosyncrasies of that api level
@@ -411,7 +427,9 @@
                     SGET_JUMBO, SGET_WIDE_JUMBO, SGET_OBJECT_JUMBO, SGET_BOOLEAN_JUMBO, SGET_BYTE_JUMBO,
                     SGET_CHAR_JUMBO, SGET_SHORT_JUMBO, SPUT_JUMBO, SPUT_WIDE_JUMBO, SPUT_OBJECT_JUMBO,
                     SPUT_BOOLEAN_JUMBO, SPUT_BYTE_JUMBO, SPUT_CHAR_JUMBO, SPUT_SHORT_JUMBO, INVOKE_VIRTUAL_JUMBO,
-                    INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO);
+                    INVOKE_SUPER_JUMBO, INVOKE_DIRECT_JUMBO, INVOKE_STATIC_JUMBO, INVOKE_INTERFACE_JUMBO,
+                    INVOKE_OBJECT_INIT_RANGE);
+            addOpcodes(INVOKE_DIRECT_EMPTY);
         }
     }
 
diff --git a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g
index 445c5ea..d1984b1 100644
--- a/smali/src/main/antlr3/org/jf/smali/smaliLexer.g
+++ b/smali/src/main/antlr3/org/jf/smali/smaliLexer.g
@@ -680,6 +680,9 @@
 	|	'invoke-static/range'
 	|	'invoke-interface/range';
 
+INSTRUCTION_FORMAT3rc_METHOD_ODEX
+	:	'invoke-object-init/range';
+
 INSTRUCTION_FORMAT3rc_TYPE
 	:	'filled-new-array/range';
 
diff --git a/smali/src/main/antlr3/org/jf/smali/smaliParser.g b/smali/src/main/antlr3/org/jf/smali/smaliParser.g
index 53b3bc9..3dc77e8 100644
--- a/smali/src/main/antlr3/org/jf/smali/smaliParser.g
+++ b/smali/src/main/antlr3/org/jf/smali/smaliParser.g
@@ -827,6 +827,11 @@
 	|	//e.g. invoke-virtual/range {v25..v26}, java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
 		INSTRUCTION_FORMAT3rc_METHOD OPEN_BRACE register_range CLOSE_BRACE COMMA fully_qualified_method {$size = Format.Format3rc.size;}
 		-> ^(I_STATEMENT_FORMAT3rc_METHOD[$start, "I_STATEMENT_FORMAT3rc_METHOD"] INSTRUCTION_FORMAT3rc_METHOD register_range fully_qualified_method)
+	|	//e.g. invoke-object-init/range {p0}, Ljava/lang/Object;-><init>()V
+		INSTRUCTION_FORMAT3rc_METHOD_ODEX OPEN_BRACE register_list CLOSE_BRACE COMMA fully_qualified_method
+		{
+			throwOdexedInstructionException(input, $INSTRUCTION_FORMAT3rc_METHOD_ODEX.text);
+		}
 	|	//e.g. filled-new-array/range {v0..v6}, I
 		INSTRUCTION_FORMAT3rc_TYPE OPEN_BRACE register_range CLOSE_BRACE COMMA nonvoid_type_descriptor {$size = Format.Format3rc.size;}
 		-> ^(I_STATEMENT_FORMAT3rc_TYPE[$start, "I_STATEMENT_FORMAT3rc_TYPE"] INSTRUCTION_FORMAT3rc_TYPE register_range nonvoid_type_descriptor)
diff --git a/smali/src/main/jflex/smaliLexer.flex b/smali/src/main/jflex/smaliLexer.flex
index c374292..ff72c3c 100644
--- a/smali/src/main/jflex/smaliLexer.flex
+++ b/smali/src/main/jflex/smaliLexer.flex
@@ -550,6 +550,10 @@
         return newToken(INSTRUCTION_FORMAT3rc_METHOD);
     }
 
+    "invoke-object-init/range" {
+        return newToken(INSTRUCTION_FORMAT3rc_METHOD_ODEX);
+    }
+
     "filled-new-array/range" {
         return newToken(INSTRUCTION_FORMAT3rc_TYPE);
     }
diff --git a/smali/src/test/resources/LexerTest/InstructionTest.smali b/smali/src/test/resources/LexerTest/InstructionTest.smali
index 778e3c3..d620793 100644
--- a/smali/src/test/resources/LexerTest/InstructionTest.smali
+++ b/smali/src/test/resources/LexerTest/InstructionTest.smali
@@ -210,6 +210,7 @@
 invoke-interface
 filled-new-array
 invoke-direct-empty
+invoke-object-init/range
 throw-verification-error
 execute-inline
 invoke-virtual-quick
diff --git a/smali/src/test/resources/LexerTest/InstructionTest.tokens b/smali/src/test/resources/LexerTest/InstructionTest.tokens
index 3388fa4..7b4860c 100644
--- a/smali/src/test/resources/LexerTest/InstructionTest.tokens
+++ b/smali/src/test/resources/LexerTest/InstructionTest.tokens
@@ -210,6 +210,7 @@
 INSTRUCTION_FORMAT35c_METHOD("invoke-interface")
 INSTRUCTION_FORMAT35c_TYPE("filled-new-array")
 INSTRUCTION_FORMAT35s_METHOD("invoke-direct-empty")
+INSTRUCTION_FORMAT3rc_METHOD_ODEX("invoke-object-init/range")
 INSTRUCTION_FORMAT20bc("throw-verification-error")
 INSTRUCTION_FORMAT35mi_METHOD("execute-inline")
 INSTRUCTION_FORMAT35ms_METHOD("invoke-virtual-quick")