Merge "Added support for version 51 class files in dx"
diff --git a/dx/src/com/android/dx/cf/code/ByteOps.java b/dx/src/com/android/dx/cf/code/ByteOps.java
index 1376008..850346a 100644
--- a/dx/src/com/android/dx/cf/code/ByteOps.java
+++ b/dx/src/com/android/dx/cf/code/ByteOps.java
@@ -210,6 +210,7 @@
     public static final int INVOKESPECIAL = 0xb7;
     public static final int INVOKESTATIC = 0xb8;
     public static final int INVOKEINTERFACE = 0xb9;
+    public static final int INVOKEDYNAMIC = 0xba;
     public static final int NEW = 0xbb;
     public static final int NEWARRAY = 0xbc;
     public static final int ANEWARRAY = 0xbd;
diff --git a/dx/src/com/android/dx/cf/code/BytecodeArray.java b/dx/src/com/android/dx/cf/code/BytecodeArray.java
index f4ea007..424b971 100644
--- a/dx/src/com/android/dx/cf/code/BytecodeArray.java
+++ b/dx/src/com/android/dx/cf/code/BytecodeArray.java
@@ -16,6 +16,7 @@
 
 package com.android.dx.cf.code;
 
+import com.android.dx.cf.iface.ParseException;
 import com.android.dx.rop.cst.Constant;
 import com.android.dx.rop.cst.ConstantPool;
 import com.android.dx.rop.cst.CstDouble;
@@ -772,6 +773,9 @@
                                           count | (expectZero << 8));
                     return 5;
                 }
+                case ByteOps.INVOKEDYNAMIC: {
+                  throw new ParseException("invokedynamic not supported");
+                }
                 case ByteOps.NEWARRAY: {
                     return parseNewarray(offset, visitor);
                 }
diff --git a/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
index 7ec2fba..036c880 100644
--- a/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
+++ b/dx/src/com/android/dx/cf/cst/ConstantPoolParser.java
@@ -27,6 +27,9 @@
 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_NameAndType;
 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_String;
 import static com.android.dx.cf.cst.ConstantTags.CONSTANT_Utf8;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_MethodHandle;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_MethodType;
+import static com.android.dx.cf.cst.ConstantTags.CONSTANT_InvokeDynamic;
 import com.android.dx.cf.iface.ParseException;
 import com.android.dx.cf.iface.ParseObserver;
 import com.android.dx.rop.cst.Constant;
@@ -184,41 +187,51 @@
         for (int i = 1; i < offsets.length; i += lastCategory) {
             offsets[i] = at;
             int tag = bytes.getUnsignedByte(at);
-            switch (tag) {
-                case CONSTANT_Integer:
-                case CONSTANT_Float:
-                case CONSTANT_Fieldref:
-                case CONSTANT_Methodref:
-                case CONSTANT_InterfaceMethodref:
-                case CONSTANT_NameAndType: {
-                    lastCategory = 1;
-                    at += 5;
-                    break;
+            try {
+                switch (tag) {
+                    case CONSTANT_Integer:
+                    case CONSTANT_Float:
+                    case CONSTANT_Fieldref:
+                    case CONSTANT_Methodref:
+                    case CONSTANT_InterfaceMethodref:
+                    case CONSTANT_NameAndType: {
+                        lastCategory = 1;
+                        at += 5;
+                        break;
+                    }
+                    case CONSTANT_Long:
+                    case CONSTANT_Double: {
+                        lastCategory = 2;
+                        at += 9;
+                        break;
+                    }
+                    case CONSTANT_Class:
+                    case CONSTANT_String: {
+                        lastCategory = 1;
+                        at += 3;
+                        break;
+                    }
+                    case CONSTANT_Utf8: {
+                        lastCategory = 1;
+                        at += bytes.getUnsignedShort(at + 1) + 3;
+                        break;
+                    }
+                    case CONSTANT_MethodHandle: {
+                        throw new ParseException("MethodHandle not supported");
+                    }
+                    case CONSTANT_MethodType: {
+                        throw new ParseException("MethodType not supported");
+                    }
+                    case CONSTANT_InvokeDynamic: {
+                        throw new ParseException("InvokeDynamic not supported");
+                    }
+                    default: {
+                        throw new ParseException("unknown tag byte: " + Hex.u1(tag));
+                    }
                 }
-                case CONSTANT_Long:
-                case CONSTANT_Double: {
-                    lastCategory = 2;
-                    at += 9;
-                    break;
-                }
-                case CONSTANT_Class:
-                case CONSTANT_String: {
-                    lastCategory = 1;
-                    at += 3;
-                    break;
-                }
-                case CONSTANT_Utf8: {
-                    lastCategory = 1;
-                    at += bytes.getUnsignedShort(at + 1) + 3;
-                    break;
-                }
-                default: {
-                    ParseException ex =
-                        new ParseException("unknown tag byte: " + Hex.u1(tag));
-                    ex.addContext("...while preparsing cst " + Hex.u2(i) +
-                                  " at offset " + Hex.u4(at));
-                    throw ex;
-                }
+            } catch (ParseException ex) {
+                ex.addContext("...while preparsing cst " + Hex.u2(i) + " at offset " + Hex.u4(at));
+                throw ex;
             }
         }
 
@@ -313,6 +326,18 @@
                     cst = new CstNat(name, descriptor);
                     break;
                 }
+                case CONSTANT_MethodHandle: {
+                    throw new ParseException("MethodHandle not supported");
+                }
+                case CONSTANT_MethodType: {
+                    throw new ParseException("MethodType not supported");
+                }
+                case CONSTANT_InvokeDynamic: {
+                    throw new ParseException("InvokeDynamic not supported");
+                }
+                default: {
+                    throw new ParseException("unknown tag byte: " + Hex.u1(tag));
+                }
             }
         } catch (ParseException ex) {
             ex.addContext("...while parsing cst " + Hex.u2(idx) +
diff --git a/dx/src/com/android/dx/cf/cst/ConstantTags.java b/dx/src/com/android/dx/cf/cst/ConstantTags.java
index 9febbdf..56ef4d7 100644
--- a/dx/src/com/android/dx/cf/cst/ConstantTags.java
+++ b/dx/src/com/android/dx/cf/cst/ConstantTags.java
@@ -52,4 +52,13 @@
 
     /** tag for a {@code CONSTANT_NameAndType_info} */
     int CONSTANT_NameAndType = 12;
+
+    /** tag for a {@code CONSTANT_MethodHandle} */
+    int CONSTANT_MethodHandle = 15;
+
+    /** tag for a {@code CONSTANT_MethodType} */
+    int CONSTANT_MethodType = 16;
+
+    /** tag for a {@code CONSTANT_InvokeDynamic} */
+    int CONSTANT_InvokeDynamic = 18;
 }
diff --git a/dx/src/com/android/dx/cf/direct/DirectClassFile.java b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
index a7bb073..2af2efe 100644
--- a/dx/src/com/android/dx/cf/direct/DirectClassFile.java
+++ b/dx/src/com/android/dx/cf/direct/DirectClassFile.java
@@ -50,6 +50,7 @@
      * See http://en.wikipedia.org/wiki/Java_class_file for an up-to-date
      * list of version numbers. Currently known (taken from that table) are:
      *
+     *     J2SE 7.0 = 51 (0x33 hex),
      *     J2SE 6.0 = 50 (0x32 hex),
      *     J2SE 5.0 = 49 (0x31 hex),
      *     JDK 1.4 = 48 (0x30 hex),
@@ -68,7 +69,7 @@
      *
      * Note: if you change this, please change "java.class.version" in System.java.
      */
-    private static final int CLASS_FILE_MAX_MAJOR_VERSION = 50;
+    private static final int CLASS_FILE_MAX_MAJOR_VERSION = 51;
 
     /** maximum {@code .class} file minor version */
     private static final int CLASS_FILE_MAX_MINOR_VERSION = 0;
diff --git a/dx/tests/003-magic-version-access/class-version-49.0.txt b/dx/tests/003-magic-version-access/class-version-49.0.txt
index 0b30fcd..9b9a27f 100644
--- a/dx/tests/003-magic-version-access/class-version-49.0.txt
+++ b/dx/tests/003-magic-version-access/class-version-49.0.txt
@@ -1,5 +1,5 @@
 #
-# classfile with the highest valid version, 49.0 (0x31.0x00)
+# classfile with the valid version 49.0 (0x31.0x00)
 #
 
 cafe babe  # magic
diff --git a/dx/tests/003-magic-version-access/class-version-49.1.txt b/dx/tests/003-magic-version-access/class-version-49.1.txt
index 9eb477c..69c0ab1 100644
--- a/dx/tests/003-magic-version-access/class-version-49.1.txt
+++ b/dx/tests/003-magic-version-access/class-version-49.1.txt
@@ -1,6 +1,5 @@
 #
-# classfile with a minor version 1 higher than the highest valid
-# version.  49.1 (0x31.0x01)
+# classfile with the valid version 49.1 (0x31.0x01)
 #
 
 cafe babe  # magic
diff --git a/dx/tests/003-magic-version-access/class-version-49.65535.txt b/dx/tests/003-magic-version-access/class-version-49.65535.txt
index 668631b..42970aa 100644
--- a/dx/tests/003-magic-version-access/class-version-49.65535.txt
+++ b/dx/tests/003-magic-version-access/class-version-49.65535.txt
@@ -1,6 +1,5 @@
 #
-# classfile with an invalid version, with the same major version
-# as the highest valid version.  49.65535 (0x31.0xffff)
+# classfile with the valid version 49.65535 (0x31.0xffff)
 #
 
 cafe babe  # magic
diff --git a/dx/tests/003-magic-version-access/class-version-50.0.txt b/dx/tests/003-magic-version-access/class-version-50.0.txt
index fa67077..dac631d 100644
--- a/dx/tests/003-magic-version-access/class-version-50.0.txt
+++ b/dx/tests/003-magic-version-access/class-version-50.0.txt
@@ -1,6 +1,5 @@
 #
-# classfile with an invalid version, with a higher major version
-# than the highest valid version.  50.0 (0x32.0x00)
+# classfile with the valid version 50.0 (0x30.0x00)
 #
 
 cafe babe  # magic
diff --git a/dx/tests/003-magic-version-access/class-version-50.1.txt b/dx/tests/003-magic-version-access/class-version-50.1.txt
index 9543be1..2b0d547 100644
--- a/dx/tests/003-magic-version-access/class-version-50.1.txt
+++ b/dx/tests/003-magic-version-access/class-version-50.1.txt
@@ -1,6 +1,5 @@
 #
-# classfile with an invalid version, with a higher major version
-# than the highest valid version.  50.0 (0x32.0x00)
+# classfile with the valid version 50.1 (0x32.0x01)
 #
 
 cafe babe  # magic
diff --git a/dx/tests/003-magic-version-access/class-version-50.65535.txt b/dx/tests/003-magic-version-access/class-version-50.65535.txt
index 9db1958..b74f326 100644
--- a/dx/tests/003-magic-version-access/class-version-50.65535.txt
+++ b/dx/tests/003-magic-version-access/class-version-50.65535.txt
@@ -1,6 +1,5 @@
 #
-# classfile with an invalid version, with a higher major version
-# than the highest valid version.  50.0 (0x32.0x00)
+# classfile with the valid version 50.65535 (0x32.0xffff)
 #
 
 cafe babe  # magic
diff --git a/dx/tests/003-magic-version-access/class-version-51.0.txt b/dx/tests/003-magic-version-access/class-version-51.0.txt
index 2ffb4cd..22c28af 100644
--- a/dx/tests/003-magic-version-access/class-version-51.0.txt
+++ b/dx/tests/003-magic-version-access/class-version-51.0.txt
@@ -1,6 +1,5 @@
 #
-# classfile with an invalid version, with a higher major version
-# than the highest valid version.  50.0 (0x32.0x00)
+# classfile with the highest valid version 51.0 (0x33.0x00)
 #
 
 cafe babe  # magic
diff --git a/dx/tests/003-magic-version-access/class-version-51.1.txt b/dx/tests/003-magic-version-access/class-version-51.1.txt
new file mode 100644
index 0000000..e4632a3
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-51.1.txt
@@ -0,0 +1,26 @@
+#
+# classfile with a minor version 1 higher than the highest valid
+# version.  51.0 (0x33.0x00)
+#
+
+cafe babe  # magic
+0001       # minor_version
+0033       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-51.65535.txt b/dx/tests/003-magic-version-access/class-version-51.65535.txt
new file mode 100644
index 0000000..cab1e64
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-51.65535.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with the same major version
+# as the highest valid version.  51.65535 (0x33.0xffff)
+#
+
+cafe babe  # magic
+ffff       # minor_version
+0033       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/class-version-52.0.txt b/dx/tests/003-magic-version-access/class-version-52.0.txt
new file mode 100644
index 0000000..fe79f5e
--- /dev/null
+++ b/dx/tests/003-magic-version-access/class-version-52.0.txt
@@ -0,0 +1,26 @@
+#
+# classfile with an invalid version, with a higher major version
+# than the highest valid version.  51.0 (0x33.0x00)
+#
+
+cafe babe  # magic
+0000       # minor_version
+0034       # major_version
+0005       # constant_pool_count
+
+#
+# constant_pool
+#
+01 0005 "Small"            # 0001: utf8["Small"]
+01 0010 "java/lang/Object" # 0002: utf8["java/lang/Object"]
+07 0001                    # 0003: class[Small]
+07 0002                    # 0004: class[java/lang/Object]
+
+ffff  # access_flags
+0003  # this_class
+0004  # super_class
+0000  # interfaces_count
+0000  # fields_count
+0000  # methods_count
+
+0000  # attributes_count
diff --git a/dx/tests/003-magic-version-access/expected.txt b/dx/tests/003-magic-version-access/expected.txt
index a632922..24f7365 100644
--- a/dx/tests/003-magic-version-access/expected.txt
+++ b/dx/tests/003-magic-version-access/expected.txt
@@ -198,28 +198,91 @@
 magic: cafebabe
 minor_version: 0001
 major_version: 0032
+constant_pool_count: 0005
 
-trouble parsing:
-bad class file magic (cafebabe) or version (0032.0001)
-...while parsing class-version-50.1.txt
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
 reading class-version-50.65535.txt...
 begin classfile
 magic: cafebabe
 minor_version: ffff
 major_version: 0032
+constant_pool_count: 0005
 
-trouble parsing:
-bad class file magic (cafebabe) or version (0032.ffff)
-...while parsing class-version-50.65535.txt
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
 reading class-version-51.0.txt...
 begin classfile
 magic: cafebabe
 minor_version: 0000
 major_version: 0033
+constant_pool_count: 0005
+
+constant_pool:
+  0001: utf8{"Small"}
+  0002: utf8{"java/lang/Object"}
+  0003: type{Small}
+  0004: type{java.lang.Object}
+end constant_pool
+access_flags: public|final|super|interface|abstract|synthetic|annotation|enum|89ce
+this_class: type{Small}
+super_class: type{java.lang.Object}
+interfaces_count: 0000
+fields_count: 0000
+methods_count: 0000
+attributes_count: 0000
+end classfile
+reading class-version-51.1.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0001
+major_version: 0033
 
 trouble parsing:
-bad class file magic (cafebabe) or version (0033.0000)
-...while parsing class-version-51.0.txt
+bad class file magic (cafebabe) or version (0033.0001)
+...while parsing class-version-51.1.txt
+reading class-version-51.65535.txt...
+begin classfile
+magic: cafebabe
+minor_version: ffff
+major_version: 0033
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0033.ffff)
+...while parsing class-version-51.65535.txt
+reading class-version-52.0.txt...
+begin classfile
+magic: cafebabe
+minor_version: 0000
+major_version: 0034
+
+trouble parsing:
+bad class file magic (cafebabe) or version (0034.0000)
+...while parsing class-version-52.0.txt
 reading small-class.txt...
 begin classfile
 magic: cafebabe
diff --git a/dx/tests/003-magic-version-access/run b/dx/tests/003-magic-version-access/run
index 24de48e..3af6118 100644
--- a/dx/tests/003-magic-version-access/run
+++ b/dx/tests/003-magic-version-access/run
@@ -35,11 +35,14 @@
 dx --debug --dump --width=100 class-version-49.1.txt
 dx --debug --dump --width=100 class-version-49.65535.txt
 dx --debug --dump --width=100 class-version-50.0.txt
+dx --debug --dump --width=100 class-version-50.1.txt
+dx --debug --dump --width=100 class-version-50.65535.txt
+dx --debug --dump --width=100 class-version-51.0.txt
 
 # Too big (throws an exception)
-dx         --dump --strict class-version-50.1.txt
-dx         --dump --strict class-version-50.65535.txt
-dx         --dump --strict class-version-51.0.txt
+dx         --dump --strict class-version-51.1.txt
+dx         --dump --strict class-version-51.65535.txt
+dx         --dump --strict class-version-52.0.txt
 
 # Show that we can dump the access flags even when they
 # don't make any sense.
diff --git a/dx/tests/024-code-bytecode/expected.txt b/dx/tests/024-code-bytecode/expected.txt
index 4637474..26d58bd 100644
--- a/dx/tests/024-code-bytecode/expected.txt
+++ b/dx/tests/024-code-bytecode/expected.txt
@@ -42,10 +42,10 @@
   
   attributes[0]:
     name: Code
-    length: 000001dc
+    length: 000001db
     max_stack: 0001
     max_locals: 0001
-    code_length: 000001d0
+    code_length: 000001cf
     0000: nop
     0001: aconst_null
     0002: iconst_m1 // #-01
@@ -251,41 +251,40 @@
     015d: invokespecial method{Small.blort:()V}
     0160: invokestatic method{Small.blort:()V}
     0163: invokeinterface ifaceMethod{Small.blort:()V}, 0001
-    0168: unused_ba
-    0169: new type{Small}
-    016c: newarray boolean
-    016e: newarray char
-    0170: newarray float
-    0172: newarray double
-    0174: newarray byte
-    0176: newarray short
-    0178: newarray int
-    017a: newarray long
-    017c: anewarray type{Small}
-    017f: arraylength
-    0180: athrow
-    0181: checkcast type{java.lang.Object}
-    0184: instanceof type{java.lang.Object}
-    0187: monitorenter
-    0188: monitorexit
-    0189: wide iload 0123
-    018d: wide lload 0124 // category-2
-    0191: wide fload 0125
-    0195: wide dload 0126 // category-2
-    0199: wide aload 0127
-    019d: wide istore 20f0
-    01a1: wide lstore 20f1 // category-2
-    01a5: wide fstore 20f2
-    01a9: wide dstore 20f3 // category-2
-    01ad: wide astore 20f4
-    01b1: wide ret ffff
-    01b5: wide iinc 0002, #+1000
-    01bb: multianewarray type{java.lang.Object}, 04
-    01bf: ifnull 0000
-    01c2: ifnonnull 01c2
-    01c5: goto_w 700001c5
-    01ca: jsr_w 000001c5
-    01cf: unused_ca
+    0168: new type{Small}
+    016b: newarray boolean
+    016d: newarray char
+    016f: newarray float
+    0171: newarray double
+    0173: newarray byte
+    0175: newarray short
+    0177: newarray int
+    0179: newarray long
+    017b: anewarray type{Small}
+    017e: arraylength
+    017f: athrow
+    0180: checkcast type{java.lang.Object}
+    0183: instanceof type{java.lang.Object}
+    0186: monitorenter
+    0187: monitorexit
+    0188: wide iload 0123
+    018c: wide lload 0124 // category-2
+    0190: wide fload 0125
+    0194: wide dload 0126 // category-2
+    0198: wide aload 0127
+    019c: wide istore 20f0
+    01a0: wide lstore 20f1 // category-2
+    01a4: wide fstore 20f2
+    01a8: wide dstore 20f3 // category-2
+    01ac: wide astore 20f4
+    01b0: wide ret ffff
+    01b4: wide iinc 0002, #+1000
+    01ba: multianewarray type{java.lang.Object}, 04
+    01be: ifnull ffff
+    01c1: ifnonnull 01c1
+    01c4: goto_w 700001c4
+    01c9: jsr_w 000001c4
+    01ce: unused_ca
     exception_table_length: 0000
     attributes_count: 0000
   end attributes[0]
diff --git a/dx/tests/024-code-bytecode/small-class.txt b/dx/tests/024-code-bytecode/small-class.txt
index 2526cf2..7d1ff37 100644
--- a/dx/tests/024-code-bytecode/small-class.txt
+++ b/dx/tests/024-code-bytecode/small-class.txt
@@ -45,10 +45,10 @@
 0001  # attributes_count
 # attributes[0]
 0007      # name
-000001dc  # length (note: == code_length + 0x0c)
+000001db  # length (note: == code_length + 0x0c)
 0001      # max_stack
 0001      # max_locals
-000001d0  # code_length
+000001cf  # code_length
 
 00        # 0000: nop
 01        # 0001: aconst_null
@@ -262,7 +262,6 @@
 b7 0015   # 015d: invokespecial 0015
 b8 0015   # 0160: invokestatic 0015
 b9 0016 01 00  # 0163: invokeinterface 0016
-ba        # 0168: <unused>
 bb 0001   # 0169: new 0001
 bc 04     # 016c: newarray boolean
 bc 05     # 016e: newarray char