Merge "New bytecodes for constant-method-{handle,type}"
diff --git a/dexdump/DexDump.cpp b/dexdump/DexDump.cpp
index 8dbdd22..f76cae3 100644
--- a/dexdump/DexDump.cpp
+++ b/dexdump/DexDump.cpp
@@ -916,9 +916,27 @@
             free(protoInfo.parameterTypes);
         }
         break;
-    case kCallSiteRef:
+    case kIndexCallSiteRef:
         outSize = snprintf(buf, bufSize, "call_site@%0*x", width, index);
         break;
+    case kIndexMethodHandleRef:
+        outSize = snprintf(buf, bufSize, "methodhandle@%0*x", width, index);
+        break;
+    case kIndexProtoRef:
+        {
+            ProtoInfo protoInfo;
+            if (getProtoInfo(pDexFile, index, &protoInfo)) {
+                outSize = snprintf(buf, bufSize, "(%s)%s // proto@%0*x",
+                                   protoInfo.parameterTypes, protoInfo.returnType,
+                                   width, index);
+
+            } else {
+                outSize = snprintf(buf, bufSize, "<proto?> // proto@%0*x",
+                                   width, secondaryIndex);
+            }
+            free(protoInfo.parameterTypes);
+        }
+        break;
     default:
         outSize = snprintf(buf, bufSize, "<?>");
         break;
diff --git a/dx/src/com/android/dex/DexFormat.java b/dx/src/com/android/dex/DexFormat.java
index 97771d8..f86917a 100644
--- a/dx/src/com/android/dex/DexFormat.java
+++ b/dx/src/com/android/dex/DexFormat.java
@@ -23,8 +23,11 @@
 public final class DexFormat {
     private DexFormat() {}
 
-    /** API level to target in order to generate invoke-polymorphic */
-    public static final int API_INVOKE_POLYMORPHIC = 26;
+    /** API level to target in order to generate const-method-handle and const-method-type */
+    public static final int API_CONST_METHOD_HANDLE = 27;
+
+    /** API level to target in order to generate invoke-polymorphic and invoke-custom */
+    public static final int API_METHOD_HANDLES = 26;
 
     /** API level to target in order to pass through default and static interface methods */
     public static final int API_DEFAULT_INTERFACE_METHODS = 24;
@@ -36,7 +39,10 @@
      * API level to target in order to produce the most modern file
      * format
      */
-    public static final int API_CURRENT = API_INVOKE_POLYMORPHIC;
+    public static final int API_CURRENT = API_CONST_METHOD_HANDLE;
+
+    /** dex file version number for API level 27 and earlier */
+    public static final String VERSION_FOR_API_27 = "039";
 
     /** dex file version number for API level 26 and earlier */
     public static final String VERSION_FOR_API_26 = "038";
@@ -54,7 +60,7 @@
      * completed and is not considered a valid dex file format.
      * </p>
      */
-    public static final String VERSION_CURRENT = VERSION_FOR_API_26;
+    public static final String VERSION_CURRENT = VERSION_FOR_API_27;
 
     /**
      * file name of the primary {@code .dex} file inside an
@@ -90,6 +96,9 @@
      * Returns the API level corresponding to the given magic number,
      * or {@code -1} if the given array is not a well-formed dex file
      * magic number.
+     *
+     * @param magic array of bytes containing DEX file magic string
+     * @return API level corresponding to magic string if valid, -1 otherwise.
      */
     public static int magicToApi(byte[] magic) {
         if (magic.length != 8) {
@@ -108,7 +117,9 @@
         } else if (version.equals(VERSION_FOR_API_24)) {
             return API_DEFAULT_INTERFACE_METHODS;
         } else if (version.equals(VERSION_FOR_API_26)) {
-            return API_INVOKE_POLYMORPHIC;
+            return API_METHOD_HANDLES;
+        } else if (version.equals(VERSION_FOR_API_27)) {
+            return API_CONST_METHOD_HANDLE;
         } else if (version.equals(VERSION_CURRENT)) {
             return API_CURRENT;
         }
@@ -118,13 +129,18 @@
 
     /**
      * Returns the magic number corresponding to the given target API level.
+     *
+     * @param targetApiLevel level of API (minimum supported value 13).
+     * @return Magic string corresponding to API level supplied.
      */
     public static String apiToMagic(int targetApiLevel) {
         String version;
 
         if (targetApiLevel >= API_CURRENT) {
             version = VERSION_CURRENT;
-        } else if (targetApiLevel >= API_INVOKE_POLYMORPHIC) {
+        } else if (targetApiLevel >= API_CONST_METHOD_HANDLE) {
+            version = VERSION_FOR_API_27;
+        } else if (targetApiLevel >= API_METHOD_HANDLES) {
             version = VERSION_FOR_API_26;
         } else if (targetApiLevel >= API_DEFAULT_INTERFACE_METHODS) {
             version = VERSION_FOR_API_24;
@@ -135,6 +151,11 @@
         return MAGIC_PREFIX + version + MAGIC_SUFFIX;
     }
 
+    /**
+     * Checks whether a DEX file magic string is supported.
+     * @param magic string from DEX file
+     * @return
+     */
     public static boolean isSupportedDexMagic(byte[] magic) {
         int api = magicToApi(magic);
         return api > 0;
diff --git a/dx/src/com/android/dx/cf/code/BasicBlocker.java b/dx/src/com/android/dx/cf/code/BasicBlocker.java
index 31f9627..0c5d5a4 100644
--- a/dx/src/com/android/dx/cf/code/BasicBlocker.java
+++ b/dx/src/com/android/dx/cf/code/BasicBlocker.java
@@ -19,6 +19,8 @@
 import com.android.dx.rop.cst.Constant;
 import com.android.dx.rop.cst.CstInvokeDynamic;
 import com.android.dx.rop.cst.CstMemberRef;
+import com.android.dx.rop.cst.CstMethodHandle;
+import com.android.dx.rop.cst.CstProtoRef;
 import com.android.dx.rop.cst.CstString;
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.type.Type;
@@ -204,8 +206,9 @@
             Constant cst, int value) {
         visitCommon(offset, length, true);
 
-        if ((cst instanceof CstMemberRef) || (cst instanceof CstType) ||
-            (cst instanceof CstString) || (cst instanceof CstInvokeDynamic)) {
+        if (cst instanceof CstMemberRef || cst instanceof CstType ||
+            cst instanceof CstString || cst instanceof CstInvokeDynamic ||
+            cst instanceof CstMethodHandle || cst instanceof CstProtoRef) {
             /*
              * Instructions with these sorts of constants have the
              * possibility of throwing, so this instruction needs to
diff --git a/dx/src/com/android/dx/cf/code/Simulator.java b/dx/src/com/android/dx/cf/code/Simulator.java
index 2fa625c..05d0d4f 100644
--- a/dx/src/com/android/dx/cf/code/Simulator.java
+++ b/dx/src/com/android/dx/cf/code/Simulator.java
@@ -24,7 +24,9 @@
 import com.android.dx.rop.cst.CstInteger;
 import com.android.dx.rop.cst.CstInterfaceMethodRef;
 import com.android.dx.rop.cst.CstInvokeDynamic;
+import com.android.dx.rop.cst.CstMethodHandle;
 import com.android.dx.rop.cst.CstMethodRef;
+import com.android.dx.rop.cst.CstProtoRef;
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.type.Prototype;
 import com.android.dx.rop.type.Type;
@@ -670,7 +672,7 @@
                      */
                     if (cst instanceof CstInterfaceMethodRef) {
                         if (opcode != ByteOps.INVOKEINTERFACE) {
-                            if (!dexOptions.canUseDefaultInterfaceMethods()) {
+                            if (!dexOptions.apiIsSupported(DexFormat.API_DEFAULT_INTERFACE_METHODS)) {
                                 throw new SimException(
                                     "default or static interface method used without " +
                                     "--min-sdk-version >= " + DexFormat.API_DEFAULT_INTERFACE_METHODS);
@@ -685,10 +687,10 @@
                     if (cst instanceof CstMethodRef) {
                         CstMethodRef methodRef = (CstMethodRef) cst;
                         if (methodRef.isSignaturePolymorphic()) {
-                            if (!dexOptions.canUseInvokePolymorphic()) {
+                            if (!dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
                                 throw new SimException(
                                     "signature-polymorphic method called without " +
-                                    "--min-sdk-version >= " + DexFormat.API_INVOKE_POLYMORPHIC);
+                                    "--min-sdk-version >= " + DexFormat.API_METHOD_HANDLES);
                             }
                             if (opcode != ByteOps.INVOKEVIRTUAL) {
                                 throw new SimException(
@@ -709,11 +711,11 @@
                     break;
                 }
                 case ByteOps.INVOKEDYNAMIC: {
-                    if (!dexOptions.canUseInvokeCustom()) {
+                    if (!dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
                         throw new SimException(
                             "invalid opcode " + Hex.u1(opcode) +
                             " (invokedynamic requires --min-sdk-version >= " +
-                            DexFormat.API_INVOKE_POLYMORPHIC + ")");
+                            DexFormat.API_METHOD_HANDLES + ")");
                     }
                     CstInvokeDynamic invokeDynamicRef = (CstInvokeDynamic) cst;
                     Prototype prototype = invokeDynamicRef.getPrototype();
@@ -738,6 +740,19 @@
                     machine.popArgs(frame, prototype);
                     break;
                 }
+                case ByteOps.LDC:
+                case ByteOps.LDC_W: {
+                    if ((cst instanceof CstMethodHandle || cst instanceof CstProtoRef)) {
+                        if (!dexOptions.apiIsSupported(DexFormat.API_CONST_METHOD_HANDLE)) {
+                            throw new SimException(
+                                "invalid constant type " + cst.typeName() +
+                                " requires --min-sdk-version >= " +
+                                DexFormat.API_CONST_METHOD_HANDLE + ")");
+                        }
+                    }
+                    machine.clearArgs();
+                    break;
+                }
                 default: {
                     machine.clearArgs();
                     break;
diff --git a/dx/src/com/android/dx/dex/DexOptions.java b/dx/src/com/android/dx/dex/DexOptions.java
index 34f4b6f..0875994 100644
--- a/dx/src/com/android/dx/dex/DexOptions.java
+++ b/dx/src/com/android/dx/dex/DexOptions.java
@@ -45,44 +45,20 @@
 
     /**
      * Gets the dex file magic number corresponding to this instance.
+     * @return string representing the dex file magic number
      */
     public String getMagic() {
         return DexFormat.apiToMagic(minSdkVersion);
     }
 
     /**
-     * Returns whether default and static interface methods are allowed.
-     *
-     * This became allowed as of Nougat (SDK version 24).
-     *
-     * @return true if supported on the currently selected SDK.
+     * Checks whether an API feature is supported.
+     * @param apiLevel the API level to test
+     * @return returns true if the current API level is at least sdkVersion
      */
-    public boolean canUseDefaultInterfaceMethods() {
-        return minSdkVersion >= DexFormat.API_DEFAULT_INTERFACE_METHODS;
+    public boolean apiIsSupported(int apiLevel) {
+        // TODO: the naming here is awkward. Tooling may rely on the minSdkVersion,
+        // but it is referred to as API in DexFormat. Currently indistinguishable.
+        return minSdkVersion >= apiLevel;
     }
-
-    /**
-     * Returns whether invoke-polymorphic can be used. This is emitted for calls
-     * to {@code java.lang.invoke.MethodHandle.invoke()} and
-     * {@code java.lang.invoke.MethodHandle.invokeExact()}.
-     *
-     * This became allowed as of the Android O release (SDK version 26).
-     *
-     * @return true if supported on the currently selected SDK.
-     */
-    public boolean canUseInvokePolymorphic() {
-        return minSdkVersion >= DexFormat.API_INVOKE_POLYMORPHIC;
-    }
-
-    /**
-     * Returns whether invoke-custom can be used.
-     *
-     * This became allowed as of the Android O release (SDK version 26).
-     *
-     * @return true if supported on the currently selected SDK.
-     */
-    public boolean canUseInvokeCustom() {
-        // invoke-custom and invoke-polymorphic are both covered by the same API level.
-        return minSdkVersion >= DexFormat.API_INVOKE_POLYMORPHIC;
-    }
-}
+}
\ No newline at end of file
diff --git a/dx/src/com/android/dx/dex/code/Dops.java b/dx/src/com/android/dx/dex/code/Dops.java
index b70911e..743e544 100644
--- a/dx/src/com/android/dx/dex/code/Dops.java
+++ b/dx/src/com/android/dx/dex/code/Dops.java
@@ -953,6 +953,14 @@
         new Dop(Opcodes.INVOKE_CUSTOM_RANGE, Opcodes.INVOKE_CUSTOM,
             Opcodes.NO_NEXT, Form3rc.THE_ONE, false);
 
+    public static final Dop CONST_METHOD_HANDLE =
+        new Dop(Opcodes.CONST_METHOD_HANDLE, Opcodes.CONST_METHOD_HANDLE,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
+    public static final Dop CONST_METHOD_TYPE =
+        new Dop(Opcodes.CONST_METHOD_TYPE, Opcodes.CONST_METHOD_TYPE,
+            Opcodes.NO_NEXT, Form21c.THE_ONE, true);
+
     // END(dops)
 
     // Static initialization.
@@ -1184,6 +1192,8 @@
         set(INVOKE_POLYMORPHIC_RANGE);
         set(INVOKE_CUSTOM);
         set(INVOKE_CUSTOM_RANGE);
+        set(CONST_METHOD_HANDLE);
+        set(CONST_METHOD_TYPE);
         // END(dops-init)
     }
 
diff --git a/dx/src/com/android/dx/dex/code/RopToDop.java b/dx/src/com/android/dx/dex/code/RopToDop.java
index 1f1d131..082d051 100644
--- a/dx/src/com/android/dx/dex/code/RopToDop.java
+++ b/dx/src/com/android/dx/dex/code/RopToDop.java
@@ -24,6 +24,8 @@
 import com.android.dx.rop.code.ThrowingCstInsn;
 import com.android.dx.rop.cst.Constant;
 import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodHandle;
+import com.android.dx.rop.cst.CstProtoRef;
 import com.android.dx.rop.cst.CstString;
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.rop.type.Type;
@@ -215,6 +217,8 @@
     //     Opcodes.USHR_INT_LIT8
     //     Opcodes.INVOKE_POLYMORPHIC
     //     Opcodes.INVOKE_CUSTOM
+    //     Opcodes.CONST_METHOD_HANDLE
+    //     Opcodes.CONST_METHOD_TYPE
     // END(first-opcodes)
 
     static {
@@ -580,8 +584,13 @@
                     return Dops.CONST_CLASS;
                 } else if (cst instanceof CstString) {
                     return Dops.CONST_STRING;
+                } else if (cst instanceof CstMethodHandle) {
+                    return Dops.CONST_METHOD_HANDLE;
+                } else if (cst instanceof CstProtoRef) {
+                    return Dops.CONST_METHOD_TYPE;
+                } else {
+                    throw new RuntimeException("Unexpected constant type");
                 }
-                break;
             }
         }
 
diff --git a/dx/src/com/android/dx/dex/code/form/Form21c.java b/dx/src/com/android/dx/dex/code/form/Form21c.java
index 9d074aa..40a03da 100644
--- a/dx/src/com/android/dx/dex/code/form/Form21c.java
+++ b/dx/src/com/android/dx/dex/code/form/Form21c.java
@@ -23,6 +23,8 @@
 import com.android.dx.rop.code.RegisterSpecList;
 import com.android.dx.rop.cst.Constant;
 import com.android.dx.rop.cst.CstFieldRef;
+import com.android.dx.rop.cst.CstMethodHandle;
+import com.android.dx.rop.cst.CstProtoRef;
 import com.android.dx.rop.cst.CstString;
 import com.android.dx.rop.cst.CstType;
 import com.android.dx.util.AnnotatedOutput;
@@ -110,9 +112,11 @@
             return false;
         }
 
-        return (cst instanceof CstType) ||
-            (cst instanceof CstFieldRef) ||
-            (cst instanceof CstString);
+        return cst instanceof CstType ||
+            cst instanceof CstFieldRef ||
+            cst instanceof CstString ||
+            cst instanceof CstMethodHandle ||
+            cst instanceof CstProtoRef;
     }
 
     /** {@inheritDoc} */
diff --git a/dx/src/com/android/dx/dex/file/DexFile.java b/dx/src/com/android/dx/dex/file/DexFile.java
index 04babb3..7df046c 100644
--- a/dx/src/com/android/dx/dex/file/DexFile.java
+++ b/dx/src/com/android/dx/dex/file/DexFile.java
@@ -16,6 +16,7 @@
 
 package com.android.dx.dex.file;
 
+import com.android.dex.DexFormat;
 import com.android.dex.util.ExceptionWithContext;
 import com.android.dx.dex.DexOptions;
 import com.android.dx.dex.file.MixedItemSection.SortType;
@@ -140,7 +141,7 @@
          * Prepare the list of sections in the order they appear in
          * the final output.
          */
-        if (dexOptions.canUseInvokeCustom()) {
+        if (dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
             /*
              * Method handles and call sites only visible in DEX files
              * from SDK version 26 onwards. Do not create or add sections unless
@@ -618,12 +619,12 @@
         classDefs.prepare();
         classData.prepare();
         wordData.prepare();
-        if (dexOptions.canUseInvokePolymorphic()) {
+        if (dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
             // Prepare call site ids before byteData where the call site items are placed.
             callSiteIds.prepare();
         }
         byteData.prepare();
-        if (dexOptions.canUseInvokePolymorphic()) {
+        if (dexOptions.apiIsSupported(DexFormat.API_METHOD_HANDLES)) {
             // Prepare method handles after call site items placed in byteData.
             methodHandles.prepare();
         }
diff --git a/dx/src/com/android/dx/dex/file/MethodHandleItem.java b/dx/src/com/android/dx/dex/file/MethodHandleItem.java
index 8e5f01e..faa64c2 100644
--- a/dx/src/com/android/dx/dex/file/MethodHandleItem.java
+++ b/dx/src/com/android/dx/dex/file/MethodHandleItem.java
@@ -67,7 +67,7 @@
     public void writeTo(DexFile file, AnnotatedOutput out) {
         int targetIndex = getTargetIndex(file);
         if (out.annotates()) {
-            out.annotate(2, "kind: " + Hex.u2(methodHandle.getType()));
+            out.annotate(2, "kind: " + Hex.u2(methodHandle.getMethodHandleType()));
             out.annotate(2, "reserved:" + Hex.u2(0));
             if (methodHandle.isAccessor()) {
                 out.annotate(2, "fieldId: " + targetIndex);
@@ -76,7 +76,7 @@
             }
             out.annotate(2, "reserved:" + Hex.u2(0));
         }
-        out.writeShort(methodHandle.getType());
+        out.writeShort(methodHandle.getMethodHandleType());
         out.writeShort(0);
         out.writeShort(getTargetIndex(file));
         out.writeShort(0);
diff --git a/dx/src/com/android/dx/io/IndexType.java b/dx/src/com/android/dx/io/IndexType.java
index 0bd142a..fb8763e 100644
--- a/dx/src/com/android/dx/io/IndexType.java
+++ b/dx/src/com/android/dx/io/IndexType.java
@@ -54,5 +54,11 @@
     VTABLE_OFFSET,
 
     /** direct field offset (for static linked field accesses) */
-    FIELD_OFFSET;
+    FIELD_OFFSET,
+
+    /** method handle reference index (for loading constant method handles) */
+    METHOD_HANDLE_REF,
+
+    /** proto reference index (for loading constant proto ref) */
+    PROTO_REF;
 }
diff --git a/dx/src/com/android/dx/io/OpcodeInfo.java b/dx/src/com/android/dx/io/OpcodeInfo.java
index 500abf7..40def04 100644
--- a/dx/src/com/android/dx/io/OpcodeInfo.java
+++ b/dx/src/com/android/dx/io/OpcodeInfo.java
@@ -947,6 +947,14 @@
         new Info(Opcodes.INVOKE_CUSTOM_RANGE, "invoke-custom/range",
             InstructionCodec.FORMAT_3RC, IndexType.CALL_SITE_REF);
 
+    public static final Info CONST_METHOD_HANDLE =
+        new Info(Opcodes.CONST_METHOD_HANDLE, "const-method-handle",
+            InstructionCodec.FORMAT_21C, IndexType.METHOD_HANDLE_REF);
+
+    public static final Info CONST_METHOD_TYPE =
+        new Info(Opcodes.CONST_METHOD_TYPE, "const-method-type",
+            InstructionCodec.FORMAT_21C, IndexType.PROTO_REF);
+
     // END(opcode-info-defs)
 
     // Static initialization.
@@ -1184,6 +1192,8 @@
         set(INVOKE_POLYMORPHIC_RANGE);
         set(INVOKE_CUSTOM);
         set(INVOKE_CUSTOM_RANGE);
+        set(CONST_METHOD_HANDLE);
+        set(CONST_METHOD_TYPE);
         // END(opcode-info-init)
     }
 
diff --git a/dx/src/com/android/dx/io/Opcodes.java b/dx/src/com/android/dx/io/Opcodes.java
index 9afee41..ee2c279 100644
--- a/dx/src/com/android/dx/io/Opcodes.java
+++ b/dx/src/com/android/dx/io/Opcodes.java
@@ -263,6 +263,8 @@
     public static final int INVOKE_POLYMORPHIC_RANGE = 0xfb;
     public static final int INVOKE_CUSTOM = 0xfc;
     public static final int INVOKE_CUSTOM_RANGE = 0xfd;
+    public static final int CONST_METHOD_HANDLE = 0xfe;
+    public static final int CONST_METHOD_TYPE = 0xff;
     // END(opcodes)
 
     // TODO: Generate these payload opcodes with opcode-gen.
diff --git a/dx/src/com/android/dx/rop/cst/CstMethodHandle.java b/dx/src/com/android/dx/rop/cst/CstMethodHandle.java
index d571b5e..6c39dae 100644
--- a/dx/src/com/android/dx/rop/cst/CstMethodHandle.java
+++ b/dx/src/com/android/dx/rop/cst/CstMethodHandle.java
@@ -16,10 +16,12 @@
 
 package com.android.dx.rop.cst;
 
+import com.android.dx.rop.type.Type;
+
 /**
  * Constants of type {@code MethodHandle}.
  */
-public final class CstMethodHandle extends Constant {
+public final class CstMethodHandle extends TypedConstant {
 
     public static final int METHOD_HANDLE_TYPE_STATIC_PUT = 0;
     public static final int METHOD_HANDLE_TYPE_STATIC_GET = 1;
@@ -92,7 +94,7 @@
      *
      * @return the type
      */
-    public int getType() {
+    public int getMethodHandleType() {
         return type;
     }
 
@@ -171,10 +173,10 @@
     @Override
     protected int compareTo0(Constant other) {
         CstMethodHandle otherHandle = (CstMethodHandle) other;
-        if (getType() == otherHandle.getType()) {
+        if (getMethodHandleType() == otherHandle.getMethodHandleType()) {
             return getRef().compareTo(otherHandle.getRef());
         } else {
-            return Integer.compare(getType(), otherHandle.getType());
+            return Integer.compare(getMethodHandleType(), otherHandle.getMethodHandleType());
         }
     }
 
@@ -195,4 +197,9 @@
     public String toHuman() {
         return getTypeName(type)+ "," + ref.toString();
     }
+
+    @Override
+    public Type getType() {
+        return Type.METHOD_HANDLE;
+    }
 }
diff --git a/dx/src/com/android/dx/rop/cst/CstProtoRef.java b/dx/src/com/android/dx/rop/cst/CstProtoRef.java
index a91ec87..9d0a783 100644
--- a/dx/src/com/android/dx/rop/cst/CstProtoRef.java
+++ b/dx/src/com/android/dx/rop/cst/CstProtoRef.java
@@ -16,11 +16,12 @@
 package com.android.dx.rop.cst;
 
 import com.android.dx.rop.type.Prototype;
+import com.android.dx.rop.type.Type;
 
 /**
  * Prototype reference.
  */
-public final class CstProtoRef extends Constant {
+public final class CstProtoRef extends TypedConstant {
 
     /** {@code non-null;} the prototype */
     private final Prototype prototype;
@@ -90,4 +91,9 @@
     public Prototype getPrototype() {
         return prototype;
     }
+
+    @Override
+    public Type getType() {
+        return Type.METHOD_TYPE;
+    }
 }
diff --git a/dx/src/com/android/dx/rop/type/Type.java b/dx/src/com/android/dx/rop/type/Type.java
index a8c0b3b..783ef45 100644
--- a/dx/src/com/android/dx/rop/type/Type.java
+++ b/dx/src/com/android/dx/rop/type/Type.java
@@ -122,6 +122,9 @@
     /** {@code non-null;} instance representing {@code java.lang.invoke.MethodHandle} */
     public static final Type METHOD_HANDLE = new Type("Ljava/lang/invoke/MethodHandle;", BT_OBJECT);
 
+    /** {@code non-null;} instance representing {@code java.lang.invoke.MethodType} */
+    public static final Type METHOD_TYPE = new Type("Ljava/lang/invoke/MethodType;", BT_OBJECT);
+
     /** {@code non-null;} instance representing {@code java.lang.invoke.VarHandle} */
     public static final Type VAR_HANDLE = new Type("Ljava/lang/invoke/VarHandle;", BT_OBJECT);
 
diff --git a/dx/tests/142-const-method-handle/constmethodhandle.jar b/dx/tests/142-const-method-handle/constmethodhandle.jar
new file mode 100644
index 0000000..692303e
--- /dev/null
+++ b/dx/tests/142-const-method-handle/constmethodhandle.jar
Binary files differ
diff --git a/dx/tests/142-const-method-handle/expected.txt b/dx/tests/142-const-method-handle/expected.txt
new file mode 100644
index 0000000..56b8ed6
--- /dev/null
+++ b/dx/tests/142-const-method-handle/expected.txt
@@ -0,0 +1,5 @@
+Trying SDK version 26 with const-method-handle.
+Trying SDK version 27 with const-method-handle.
+"dex\n039\0"
+const-method-handle v0, invoke-instance,method{java.lang.Object.getClass:()Ljava/lang/Class;}
+const-method-type v0, (CSIJFDLjava/lang/Object;)Z
diff --git a/dx/tests/142-const-method-handle/generate-test b/dx/tests/142-const-method-handle/generate-test
new file mode 100755
index 0000000..1658bca
--- /dev/null
+++ b/dx/tests/142-const-method-handle/generate-test
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+function fail() {
+  echo Build failed: $1 1>&2
+  exit 1
+}
+
+if [[ -z "${ANDROID_BUILD_TOP}" ]]; then
+  fail "ANDROID_BUILD_TOP is not defined. Try running 'lunch' first."
+fi
+
+SCRIPT_PATH=$( cd $(dirname $0) ; pwd -P )
+ASM_CLASSPATH="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-5.2.jar"
+SRC_PATH="${SCRIPT_PATH}/src"
+BUILD_PATH="${SCRIPT_PATH}/classes"
+JAR_FILE="${SCRIPT_PATH}/constmethodhandle.jar"
+
+if [[ ! -d "${BUILD_PATH}" ]]; then
+    mkdir "$BUILD_PATH" || exit 1
+fi
+
+(cd "${SRC_PATH}" && javac -cp "${ASM_CLASSPATH}" -d "${BUILD_PATH}" Main.java constmethodhandle/*.java) || fail "javac error"
+(cd "${SCRIPT_PATH}" && java -cp "${ASM_CLASSPATH}:${BUILD_PATH}" constmethodhandle.TestGenerator "${BUILD_PATH}") || fail "generator failure"
+(cd "${BUILD_PATH}" && jar cf "${JAR_FILE}" Main.class constmethodhandle/ConstTest.class) || fail "jar creation error"
diff --git a/dx/tests/142-const-method-handle/info.txt b/dx/tests/142-const-method-handle/info.txt
new file mode 100644
index 0000000..4942773
--- /dev/null
+++ b/dx/tests/142-const-method-handle/info.txt
@@ -0,0 +1,2 @@
+This test checks the conversion of ldc with a MethodHandle operand
+and a MethodType operand.
diff --git a/dx/tests/142-const-method-handle/run b/dx/tests/142-const-method-handle/run
new file mode 100755
index 0000000..2d61db7
--- /dev/null
+++ b/dx/tests/142-const-method-handle/run
@@ -0,0 +1,40 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+UNSUPPORTED_SDK_VERSION=26
+SUPPORTED_SDK_VERSION=27
+
+# Expect failure with unsupported SDK version
+EXPECTED_STATUS[${UNSUPPORTED_SDK_VERSION}]=1
+
+# Expect success with supported SDK version
+EXPECTED_STATUS[${SUPPORTED_SDK_VERSION}]=0
+
+DX_OUTPUT=dx.log
+rm -f ${DX_OUTPUT} 2>/dev/null
+
+for SDK_VERSION in ${UNSUPPORTED_SDK_VERSION} ${SUPPORTED_SDK_VERSION}; do
+  echo Trying SDK version ${SDK_VERSION} with const-method-handle.
+  dx --min-sdk-version=${SDK_VERSION} --dex --output=constmethodhandle.dex \
+     --verbose-dump --dump-to=- --dump-width=1000 constmethodhandle.jar 2>&1
+  STATUS=$?
+  if [[ ${STATUS} != ${EXPECTED_STATUS[$SDK_VERSION]} ]]; then
+    echo Unexpected status ${STATUS} for SDK version ${SDK_VERSION}.
+    exit 1
+  fi
+done > ${DX_OUTPUT}
+
+sed -n -e 's/.*: //g' -e 's_ *//.*__' -e '/const-method/p' -e '/dex[\\]/p' ${DX_OUTPUT}
diff --git a/dx/tests/142-const-method-handle/src/Main.java b/dx/tests/142-const-method-handle/src/Main.java
new file mode 100644
index 0000000..6ab922d
--- /dev/null
+++ b/dx/tests/142-const-method-handle/src/Main.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import constmethodhandle.ConstTest;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+
+public class Main{
+  public static void main(String[] args) throws Throwable {
+    // At compilation time, ConstTest.main has not yet been
+    // generated. It is generated before execution / linking.
+    MethodHandle generatedMain = MethodHandles.lookup()
+        .findStatic(ConstTest.class, "main", MethodType.methodType(void.class, String[].class));
+    generatedMain.invokeExact(args);
+  }
+}
diff --git a/dx/tests/142-const-method-handle/src/constmethodhandle/ConstTest.java b/dx/tests/142-const-method-handle/src/constmethodhandle/ConstTest.java
new file mode 100644
index 0000000..31abb97
--- /dev/null
+++ b/dx/tests/142-const-method-handle/src/constmethodhandle/ConstTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package constmethodhandle;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+
+public class ConstTest {
+  private static void displayMethodHandle(MethodHandle mh) throws Throwable {
+    System.out.println("MethodHandle " + mh + " => " +
+                       (Class) mh.invoke((Object) Float.valueOf(1.23e4f)));
+  }
+
+  private static void displayMethodType(MethodType mt) {
+    System.out.println("MethodType " + mt);
+  }
+}
diff --git a/dx/tests/142-const-method-handle/src/constmethodhandle/TestGenerator.java b/dx/tests/142-const-method-handle/src/constmethodhandle/TestGenerator.java
new file mode 100644
index 0000000..f769da9
--- /dev/null
+++ b/dx/tests/142-const-method-handle/src/constmethodhandle/TestGenerator.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package constmethodhandle;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+public class TestGenerator {
+
+  private final Path classNamePath;
+
+  public static void main(String[] args) throws IOException {
+    assert args.length == 1;
+    TestGenerator testGenerator = new TestGenerator(Paths.get(args[0],
+        TestGenerator.class.getPackage().getName(), ConstTest.class.getSimpleName() + ".class"));
+    testGenerator.generateTests();
+  }
+
+  public TestGenerator(Path classNamePath) {
+    this.classNamePath = classNamePath;
+  }
+
+  private void generateTests() throws IOException {
+    ClassReader cr = new ClassReader(new FileInputStream(classNamePath.toFile()));
+    ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+    cr.accept(
+        new ClassVisitor(Opcodes.ASM5, cw) {
+          @Override
+          public void visitEnd() {
+            generateMethodTest1(cw);
+            generateMethodTest2(cw);
+            generateMethodMain(cw);
+            super.visitEnd();
+          }
+        }, 0);
+    new FileOutputStream(classNamePath.toFile()).write(cw.toByteArray());
+  }
+
+  /* generate main method that only call all test methods. */
+  private void generateMethodMain(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC,
+                                      "main", "([Ljava/lang/String;)V", null, null);
+    String internalName = Type.getInternalName(ConstTest.class);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test1",
+                       "()Ljava/lang/invoke/MethodHandle;", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName,
+                       "displayMethodHandle", "(Ljava/lang/invoke/MethodHandle;)V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test2",
+                       "()Ljava/lang/invoke/MethodType;", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "displayMethodType",
+                       "(Ljava/lang/invoke/MethodType;)V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate a test that returns a constant method handle.
+   */
+  private void generateMethodTest1(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1",
+                                      "()Ljava/lang/invoke/MethodHandle;", null, null);
+    MethodType mt = MethodType.methodType(Class.class);
+    Handle mh = new Handle(Opcodes.H_INVOKEVIRTUAL, Type.getInternalName(Object.class),
+                           "getClass", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn(mh);
+    mv.visitInsn(Opcodes.ARETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate a test that returns a constant method type.
+   */
+  private void generateMethodTest2(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2",
+                                      "()Ljava/lang/invoke/MethodType;", null, null);
+    Type mt = Type.getMethodType(Type.getType(boolean.class), Type.getType(char.class),
+                                 Type.getType(short.class), Type.getType(int.class),
+                                 Type.getType(long.class), Type.getType(float.class),
+                                 Type.getType(double.class), Type.getType(Object.class));
+    mv.visitLdcInsn(mt);
+    mv.visitInsn(Opcodes.ARETURN);
+    mv.visitMaxs(-1, -1);
+  }
+}
diff --git a/libdex/DexFile.h b/libdex/DexFile.h
index c274e64..a12f025 100644
--- a/libdex/DexFile.h
+++ b/libdex/DexFile.h
@@ -97,6 +97,11 @@
  */
 #define DEX_MAGIC_VERS_38  "038\0"
 
+/* The version for android P, encoded in 4 bytes of ASCII. This differentiates dex files that may
+ * contain const-method-handle and const-proto.
+ */
+#define DEX_MAGIC_VERS_39  "039\0"
+
 /* current version, encoded in 4 bytes of ASCII */
 #define DEX_MAGIC_VERS  "036\0"
 
diff --git a/libdex/DexOpcodes.cpp b/libdex/DexOpcodes.cpp
index 8a98772..d3db79b 100644
--- a/libdex/DexOpcodes.cpp
+++ b/libdex/DexOpcodes.cpp
@@ -273,19 +273,19 @@
     "+invoke-object-init/range",
     "+return-void-barrier",
     "+iget-quick",
-    "+iget-wide-quick",
-    "+iget-object-quick",
-    "+iput-quick",
-    "+iput-wide-quick",
-    "+iput-object-quick",
-    "+invoke-virtual-quick",
-    "+invoke-virtual-quick/range",
+    "unused-f3",
+    "unused-f4",
+    "unused-f5",
+    "unused-f6",
+    "unused-f7",
+    "unused-f8",
+    "unused-f9",
     "invoke-polymorphic",
     "invoke-polymorphic/range",
     "invoke-custom",
     "invoke-custom/range",
-    "+sput-object-volatile",
-    "unused-ff",
+    "const-method-handle",
+    "const-method-type",
     // END(libdex-opcode-names)
 };
 
diff --git a/libdex/DexOpcodes.h b/libdex/DexOpcodes.h
index 625a2e8..1c684ab 100644
--- a/libdex/DexOpcodes.h
+++ b/libdex/DexOpcodes.h
@@ -311,19 +311,19 @@
     OP_INVOKE_OBJECT_INIT_RANGE     = 0xf0,
     OP_RETURN_VOID_BARRIER          = 0xf1,
     OP_IGET_QUICK                   = 0xf2,
-    OP_IGET_WIDE_QUICK              = 0xf3,
-    OP_IGET_OBJECT_QUICK            = 0xf4,
-    OP_IPUT_QUICK                   = 0xf5,
-    OP_IPUT_WIDE_QUICK              = 0xf6,
-    OP_IPUT_OBJECT_QUICK            = 0xf7,
-    OP_INVOKE_VIRTUAL_QUICK         = 0xf8,
-    OP_INVOKE_VIRTUAL_QUICK_RANGE   = 0xf9,
+    OP_UNUSED_F3                    = 0xf3,
+    OP_UNUSED_F4                    = 0xf4,
+    OP_UNUSED_F5                    = 0xf5,
+    OP_UNUSED_F6                    = 0xf6,
+    OP_UNUSED_F7                    = 0xf7,
+    OP_UNUSED_F8                    = 0xf8,
+    OP_UNUSED_F9                    = 0xf9,
     OP_INVOKE_POLYMORPHIC           = 0xfa,
     OP_INVOKE_POLYMORPHIC_RANGE     = 0xfb,
     OP_INVOKE_CUSTOM                = 0xfc,
     OP_INVOKE_CUSTOM_RANGE          = 0xfd,
-    OP_SPUT_OBJECT_VOLATILE         = 0xfe,
-    OP_UNUSED_FF                    = 0xff,
+    OP_CONST_METHOD_HANDLE          = 0xfe,
+    OP_CONST_METHOD_TYPE            = 0xff,
     // END(libdex-opcode-enum)
 };
 
@@ -577,19 +577,19 @@
         H(OP_INVOKE_OBJECT_INIT_RANGE),                                       \
         H(OP_RETURN_VOID_BARRIER),                                            \
         H(OP_IGET_QUICK),                                                     \
-        H(OP_IGET_WIDE_QUICK),                                                \
-        H(OP_IGET_OBJECT_QUICK),                                              \
-        H(OP_IPUT_QUICK),                                                     \
-        H(OP_IPUT_WIDE_QUICK),                                                \
-        H(OP_IPUT_OBJECT_QUICK),                                              \
-        H(OP_INVOKE_VIRTUAL_QUICK),                                           \
-        H(OP_INVOKE_VIRTUAL_QUICK_RANGE),                                     \
+        H(OP_UNUSED_F3),                                                      \
+        H(OP_UNUSED_F4),                                                      \
+        H(OP_UNUSED_F5),                                                      \
+        H(OP_UNUSED_F6),                                                      \
+        H(OP_UNUSED_F7),                                                      \
+        H(OP_UNUSED_F8),                                                      \
+        H(OP_UNUSED_F9),                                                      \
         H(OP_INVOKE_POLYMORPHIC),                                             \
         H(OP_INVOKE_POLYMORPHIC_RANGE),                                       \
         H(OP_INVOKE_CUSTOM),                                                  \
         H(OP_INVOKE_CUSTOM_RANGE),                                            \
-        H(OP_SPUT_OBJECT_VOLATILE),                                           \
-        H(OP_UNUSED_FF),                                                      \
+        H(OP_CONST_METHOD_HANDLE),                                            \
+        H(OP_CONST_METHOD_TYPE),                                              \
         /* END(libdex-goto-table) */                                          \
     };
 
diff --git a/libdex/InstrUtils.cpp b/libdex/InstrUtils.cpp
index 3e81398..168f3e1 100644
--- a/libdex/InstrUtils.cpp
+++ b/libdex/InstrUtils.cpp
@@ -47,7 +47,7 @@
     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
     2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 3, 3,
-    3, 1, 2, 2, 2, 2, 2, 2, 3, 3, 4, 4, 3, 3, 2, 0,
+    3, 1, 2, 0, 0, 0, 0, 0, 0, 0, 4, 4, 3, 3, 2, 2,
     // END(libdex-widths)
 };
 
@@ -300,19 +300,19 @@
     kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
     kInstrCanReturn,
     kInstrCanContinue|kInstrCanThrow,
-    kInstrCanContinue|kInstrCanThrow,
-    kInstrCanContinue|kInstrCanThrow,
-    kInstrCanContinue|kInstrCanThrow,
-    kInstrCanContinue|kInstrCanThrow,
-    kInstrCanContinue|kInstrCanThrow,
-    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
-    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
-    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
-    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
-    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
-    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
-    kInstrCanContinue|kInstrCanThrow,
     0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    0,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow|kInstrInvoke,
+    kInstrCanContinue|kInstrCanThrow,
+    kInstrCanContinue|kInstrCanThrow,
     // END(libdex-flags)
 };
 
@@ -356,9 +356,9 @@
     kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,  kFmt22b,
     kFmt22b,  kFmt22b,  kFmt22b,  kFmt22c,  kFmt22c,  kFmt21c,  kFmt21c,
     kFmt22c,  kFmt22c,  kFmt22c,  kFmt21c,  kFmt21c,  kFmt00x,  kFmt20bc,
-    kFmt35mi, kFmt3rmi, kFmt35c,  kFmt10x,  kFmt22cs, kFmt22cs, kFmt22cs,
-    kFmt22cs, kFmt22cs, kFmt22cs, kFmt35ms, kFmt3rms, kFmt45cc, kFmt4rcc,
-    kFmt35c,  kFmt3rc,  kFmt21c,  kFmt00x,
+    kFmt35mi, kFmt3rmi, kFmt35c,  kFmt10x,  kFmt22cs, kFmt00x,  kFmt00x,
+    kFmt00x,  kFmt00x,  kFmt00x,  kFmt00x,  kFmt00x,  kFmt45cc, kFmt4rcc,
+    kFmt35c,  kFmt3rc,  kFmt21c,  kFmt21c,
     // END(libdex-formats)
 };
 
@@ -449,11 +449,11 @@
     kIndexFieldRef,     kIndexFieldRef,     kIndexUnknown,
     kIndexVaries,       kIndexInlineMethod, kIndexInlineMethod,
     kIndexMethodRef,    kIndexNone,         kIndexFieldOffset,
-    kIndexFieldOffset,  kIndexFieldOffset,  kIndexFieldOffset,
-    kIndexFieldOffset,  kIndexFieldOffset,  kIndexVtableOffset,
-    kIndexVtableOffset, kIndexMethodAndProtoRef, kIndexMethodAndProtoRef,
-    kCallSiteRef,       kCallSiteRef,       kIndexFieldRef,
-    kIndexUnknown,
+    kIndexUnknown,      kIndexUnknown,      kIndexUnknown,
+    kIndexUnknown,      kIndexUnknown,      kIndexUnknown,
+    kIndexUnknown,      kIndexMethodAndProtoRef, kIndexMethodAndProtoRef,
+    kIndexCallSiteRef,  kIndexCallSiteRef,  kIndexMethodHandleRef,
+    kIndexProtoRef,
     // END(libdex-index-types)
 };
 
diff --git a/libdex/InstrUtils.h b/libdex/InstrUtils.h
index e8ae4c8..c5bf77c 100644
--- a/libdex/InstrUtils.h
+++ b/libdex/InstrUtils.h
@@ -81,7 +81,9 @@
     kIndexVtableOffset,      // vtable offset (for static linked methods)
     kIndexFieldOffset,       // field offset (for static linked fields)
     kIndexMethodAndProtoRef, // method index and proto index
-    kCallSiteRef             // call site index
+    kIndexCallSiteRef,       // call site index
+    kIndexMethodHandleRef,   // constant method handle reference index
+    kIndexProtoRef,          // constant prototype reference index
 };
 
 /*
diff --git a/opcode-gen/bytecode.txt b/opcode-gen/bytecode.txt
index 840d433..0129ad9 100644
--- a/opcode-gen/bytecode.txt
+++ b/opcode-gen/bytecode.txt
@@ -70,6 +70,8 @@
 #     field-offset
 #     method-and-proto-ref
 #     call-site-ref
+#     method-handle-ref
+#     proto-ref
 #   flags; pipe-combined combo of one or more of:
 #     optimized     -- optimized; not to be included in unoptimized dex files
 #     branch        -- might branch to an address
@@ -333,22 +335,20 @@
 op   f0 +invoke-object-init/range   35c  n method-ref    optimized|continue|throw|invoke
 op   f1 +return-void-barrier        10x  n none          optimized|return
 op   f2 +iget-quick                 22cs y field-offset  optimized|continue|throw
-op   f3 +iget-wide-quick            22cs y field-offset  optimized|continue|throw
-op   f4 +iget-object-quick          22cs y field-offset  optimized|continue|throw
-op   f5 +iput-quick                 22cs n field-offset  optimized|continue|throw
-op   f6 +iput-wide-quick            22cs n field-offset  optimized|continue|throw
-op   f7 +iput-object-quick          22cs n field-offset  optimized|continue|throw
-op   f8 +invoke-virtual-quick       35ms n vtable-offset optimized|continue|throw|invoke
-op   f9 +invoke-virtual-quick/range 3rms n vtable-offset optimized|continue|throw|invoke
+
+# unused: op f3..f9
+
+#
+# Bytecodes relating to method handles API.
+#
 
 # Invoke-polymorphic
-op   fa invoke-polymorphic          45cc y method-and-proto-ref continue|throw|invoke
-op   fb invoke-polymorphic/range    4rcc y method-and-proto-ref continue|throw|invoke
-op   fc invoke-custom               35c  y call-site-ref continue|throw|invoke
-op   fd invoke-custom/range         3rc  y call-site-ref continue|throw|invoke
+op   fa invoke-polymorphic          45cc n method-and-proto-ref continue|throw|invoke
+op   fb invoke-polymorphic/range    4rcc n method-and-proto-ref continue|throw|invoke
+op   fc invoke-custom               35c  n call-site-ref continue|throw|invoke
+op   fd invoke-custom/range         3rc  n call-site-ref continue|throw|invoke
 
-# More optimized opcodes (not valid in an unoptimized dex file)
+# Constant loading for method handles and method types. NB these may throw OOME
+op   fe const-method-handle         21c y method-handle-ref continue|throw
+op   ff const-method-type           21c y proto-ref         continue|throw
 
-op   fe +sput-object-volatile       21c  n field-ref     optimized|continue|throw
-
-# unused: op ff
diff --git a/opcode-gen/opcode-gen.awk b/opcode-gen/opcode-gen.awk
index baf774b..16823bb 100644
--- a/opcode-gen/opcode-gen.awk
+++ b/opcode-gen/opcode-gen.awk
@@ -485,7 +485,9 @@
     indexTypeValues["vtable-offset"]        = "kIndexVtableOffset";
     indexTypeValues["field-offset"]         = "kIndexFieldOffset";
     indexTypeValues["method-and-proto-ref"] = "kIndexMethodAndProtoRef";
-    indexTypeValues["call-site-ref"]        = "kCallSiteRef";
+    indexTypeValues["call-site-ref"]        = "kIndexCallSiteRef";
+    indexTypeValues["method-handle-ref"]    = "kIndexMethodHandleRef";
+    indexTypeValues["proto-ref"]            = "kIndexProtoRef";
 }
 
 # Initialize the flags data.