Align method handle type with the runtime

- Remove temporary hack in invoke-custom test due to runtime bug
since an updated runtime fix it.

Change-Id: I5424bacf7ca0e292974df7e81b35dea51d8a9b51
diff --git a/src/main/java/com/android/tools/r8/code/InvokeCustom.java b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
index 504a9e3..3c0b67c 100644
--- a/src/main/java/com/android/tools/r8/code/InvokeCustom.java
+++ b/src/main/java/com/android/tools/r8/code/InvokeCustom.java
@@ -104,6 +104,9 @@
       case INVOKE_SUPER:
         registry.registerInvokeSuper(methodHandle.asMethod());
         break;
+      case INVOKE_DIRECT:
+        registry.registerInvokeDirect(methodHandle.asMethod());
+        break;
       default:
         throw new AssertionError();
     }
diff --git a/src/main/java/com/android/tools/r8/dex/DexFileReader.java b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
index 768f207..09b3c33 100644
--- a/src/main/java/com/android/tools/r8/dex/DexFileReader.java
+++ b/src/main/java/com/android/tools/r8/dex/DexFileReader.java
@@ -927,6 +927,9 @@
         fieldOrMethod = indexedItems.getField(indexFieldOrMethod);
         break;
       }
+      case INVOKE_CONSTRUCTOR:
+      case INVOKE_DIRECT:
+      case INVOKE_INTERFACE:
       case INVOKE_INSTANCE:
       case INVOKE_STATIC: {
         fieldOrMethod = indexedItems.getMethod(indexFieldOrMethod);
diff --git a/src/main/java/com/android/tools/r8/dex/FileWriter.java b/src/main/java/com/android/tools/r8/dex/FileWriter.java
index 6237872..d47c57d 100644
--- a/src/main/java/com/android/tools/r8/dex/FileWriter.java
+++ b/src/main/java/com/android/tools/r8/dex/FileWriter.java
@@ -684,11 +684,8 @@
     checkThatInvokeCustomIsAllowed();
     MethodHandleType methodHandleDexType;
     switch (methodHandle.type) {
-      case INVOKE_CONSTRUCTOR:
-        throw new CompilationError("Constructor method handle type is not yet supported.");
-      case INVOKE_INTERFACE:
       case INVOKE_SUPER:
-        methodHandleDexType = MethodHandleType.INVOKE_INSTANCE;
+        methodHandleDexType = MethodHandleType.INVOKE_DIRECT;
         break;
       default:
         methodHandleDexType = methodHandle.type;
diff --git a/src/main/java/com/android/tools/r8/graph/DexCallSite.java b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
index 58bf764..1d5720d 100644
--- a/src/main/java/com/android/tools/r8/graph/DexCallSite.java
+++ b/src/main/java/com/android/tools/r8/graph/DexCallSite.java
@@ -48,13 +48,9 @@
   }
 
   public boolean computeEquals(Object other) {
-    if (other instanceof DexCallSite) {
-      DexCallSite o = (DexCallSite) other;
-      return methodName.equals(o.methodName)
-          && methodProto.equals(o.methodProto)
-          && bootstrapMethod.equals(o.bootstrapMethod)
-          && bootstrapArgs.equals(o.bootstrapArgs);
-    }
+    // Call sites are equal only when this == other, which was already computed by the caller of
+    // computeEquals. Do not share call site entries, each invoke-custom must have its own
+    // call site, but the content of the entry (encoded array) in the data section can be shared.
     return false;
   }
 
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
index d223735..eeba4e8 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethodHandle.java
@@ -8,18 +8,17 @@
 public class DexMethodHandle extends IndexedDexItem {
 
   public enum MethodHandleType {
-    // Method handle dex type.
     STATIC_PUT((short) 0x00),
     STATIC_GET((short) 0x01),
     INSTANCE_PUT((short) 0x02),
     INSTANCE_GET((short) 0x03),
     INVOKE_STATIC((short) 0x04),
     INVOKE_INSTANCE((short) 0x05),
-    // Upcoming method handle dex type.
     INVOKE_CONSTRUCTOR((short) 0x06),
+    INVOKE_DIRECT((short) 0x07),
+    INVOKE_INTERFACE((short) 0x08),
     // Internal method handle needed by lambda desugaring.
-    INVOKE_INTERFACE((short) 0x07),
-    INVOKE_SUPER((short) 0x08);
+    INVOKE_SUPER((short) 0x09);
 
     private final short value;
 
@@ -57,9 +56,12 @@
           kind = INVOKE_CONSTRUCTOR;
           break;
         case 0x07:
-          kind = INVOKE_INTERFACE;
+          kind = INVOKE_DIRECT;
           break;
         case 0x08:
+          kind = INVOKE_INTERFACE;
+          break;
+        case 0x09:
           kind = INVOKE_SUPER;
           break;
         default:
@@ -76,7 +78,7 @@
 
     public boolean isMethodType() {
       return isInvokeStatic() || isInvokeInstance() || isInvokeInterface() || isInvokeSuper()
-          || isInvokeConstructor();
+          || isInvokeConstructor() || isInvokeDirect();
     }
 
     public boolean isStaticPut() {
@@ -99,6 +101,10 @@
       return this == MethodHandleType.INVOKE_STATIC;
     }
 
+    public boolean isInvokeDirect() {
+      return this == MethodHandleType.INVOKE_DIRECT;
+    }
+
     public boolean isInvokeInstance() {
       return this == MethodHandleType.INVOKE_INSTANCE;
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
index ca1d53c..1b31a57 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarSourceCode.java
@@ -2635,7 +2635,7 @@
       case Opcodes.H_INVOKESPECIAL:
         DexType owner = application.getTypeFromName(handle.getOwner());
         if (owner == clazz || handle.getName().equals(Constants.INSTANCE_INITIALIZER_NAME)) {
-          return MethodHandleType.INVOKE_INSTANCE;
+          return MethodHandleType.INVOKE_DIRECT;
         } else {
           return MethodHandleType.INVOKE_SUPER;
         }
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
index c957b58..160da3c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/AccessorMethodSourceCode.java
@@ -37,7 +37,7 @@
     DexType[] implParams = implProto.parameters.values;
 
     int index = 0;
-    if (implHandle.type.isInvokeInstance()) {
+    if (implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect()) {
       assert accessorParams[index] == descriptor().getImplReceiverType();
       index++;
     }
@@ -69,9 +69,10 @@
   private Invoke.Type inferInvokeType() {
     switch (descriptor().implHandle.type) {
       case INVOKE_INSTANCE:
-        return isPrivateMethod() ? Invoke.Type.DIRECT : Invoke.Type.VIRTUAL;
+        return Invoke.Type.VIRTUAL;
       case INVOKE_STATIC:
         return Invoke.Type.STATIC;
+      case INVOKE_DIRECT:
       case INVOKE_CONSTRUCTOR:
         return Invoke.Type.DIRECT;
       case INVOKE_INTERFACE:
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index be6252a..353f866 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -250,6 +250,7 @@
         return createConstructorTarget(accessedFrom);
       case INVOKE_STATIC:
         return createStaticMethodTarget(accessedFrom);
+      case INVOKE_DIRECT:
       case INVOKE_INSTANCE:
         return createInstanceMethodTarget(accessedFrom);
       default:
@@ -273,7 +274,7 @@
       return new StaticLambdaImplTarget();
     }
 
-    assert implHandle.type.isInvokeInstance();
+    assert implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect();
 
     // If lambda$ method is an instance method we convert it into a static methods and
     // relax its accessibility.
@@ -291,7 +292,8 @@
   // Create targets for instance method referenced directly without
   // lambda$ methods. It may require creation of accessors in some cases.
   private Target createInstanceMethodTarget(DexType accessedFrom) {
-    assert descriptor.implHandle.type.isInvokeInstance();
+    assert descriptor.implHandle.type.isInvokeInstance() ||
+        descriptor.implHandle.type.isInvokeDirect();
 
     if (!descriptor.needsAccessor(accessedFrom)) {
       return new NoAccessorMethodTarget(Invoke.Type.VIRTUAL);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
index 850f37d..e322717 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaDescriptor.java
@@ -12,6 +12,7 @@
 import com.android.tools.r8.graph.DexItemFactory;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexMethodHandle;
+import com.android.tools.r8.graph.DexMethodHandle.MethodHandleType;
 import com.android.tools.r8.graph.DexProto;
 import com.android.tools.r8.graph.DexString;
 import com.android.tools.r8.graph.DexType;
@@ -91,6 +92,7 @@
     // Find the lambda's impl-method target.
     DexMethod method = implHandle.asMethod();
     switch (implHandle.type) {
+      case INVOKE_DIRECT:
       case INVOKE_INSTANCE: {
         AppInfo appInfo = rewriter.converter.appInfo;
         DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method);
@@ -98,7 +100,8 @@
           target = appInfo.lookupDirectTarget(method);
         }
         assert target == null ||
-            (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic());
+            (implHandle.type.isInvokeInstance() && isInstanceMethod(target)) ||
+            (implHandle.type.isInvokeDirect() && isPrivateInstanceMethod(target));
         return target;
       }
 
@@ -119,8 +122,7 @@
       case INVOKE_INTERFACE: {
         AppInfo appInfo = rewriter.converter.appInfo;
         DexEncodedMethod target = appInfo.lookupVirtualTarget(getImplReceiverType(), method);
-        assert target == null ||
-            (!target.accessFlags.isConstructor() && !target.accessFlags.isStatic());
+        assert target == null || isInstanceMethod(target);
         return target;
       }
 
@@ -129,6 +131,16 @@
     }
   }
 
+  private boolean isInstanceMethod(DexEncodedMethod encodedMethod) {
+    assert encodedMethod != null;
+    return !encodedMethod.accessFlags.isConstructor() && !encodedMethod.accessFlags.isStatic();
+  }
+
+  private boolean isPrivateInstanceMethod(DexEncodedMethod encodedMethod) {
+    assert encodedMethod != null;
+    return encodedMethod.accessFlags.isPrivate() && isInstanceMethod(encodedMethod);
+  }
+
   final DexAccessFlags getAccessibility() {
     return targetMethod == null ? null : targetMethod.accessFlags;
   }
@@ -159,10 +171,12 @@
       return false;
     }
 
+
     boolean staticTarget = implHandle.type.isInvokeStatic();
-    boolean instanceTarget = implHandle.type.isInvokeInstance();
+    boolean instanceTarget = implHandle.type.isInvokeInstance() || implHandle.type.isInvokeDirect();
     boolean initTarget = implHandle.type.isInvokeConstructor();
     assert instanceTarget || staticTarget || initTarget;
+    assert !implHandle.type.isInvokeDirect() || isPrivateInstanceMethod(targetMethod);
 
     if (targetMethod == null) {
       // The target cannot be a private method, since otherwise it
diff --git a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
index 0d4d5c5..449d426 100644
--- a/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
+++ b/src/test/examplesAndroidO/invokecustom/InvokeCustom.java
@@ -102,74 +102,15 @@
     System.out.println("targetMethodTest4");
   }
 
-  public static CallSite bsmCreateCallSite(
-      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
-      throws Throwable {
-    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
-    final MethodHandle targetMH = MethodHandles.lookup().findSpecial(Super.class,
-                "targetMethodTest5", MethodType.methodType(void.class), InvokeCustom.class);
-    return new ConstantCallSite(targetMH);
-  }
-
-  public static CallSite bsmCreateCallCallingtargetMethodTest6(
-      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
-      throws Throwable {
-    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
-    final MethodHandle targetMH =
-        MethodHandles.lookup().findVirtual(
-            I.class, "targetMethodTest6", MethodType.methodType(void.class));
-    return new ConstantCallSite(targetMH);
-  }
-
-  public static CallSite bsmCreateCallCallingtargetMethodTest7(
-      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
-      throws Throwable {
-    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
-    final MethodHandle targetMH =
-        MethodHandles.lookup().findVirtual(
-            J.class, "targetMethodTest7", MethodType.methodType(void.class));
-    return new ConstantCallSite(targetMH);
-  }
-
   public void targetMethodTest8() {
     System.out.println("targetMethodTest8 from InvokeCustom");
   }
 
-  public static CallSite bsmCreateCallCallingtargetMethodTest8(
-      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
-      throws Throwable {
-    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
-    final MethodHandle targetMH =
-        MethodHandles.lookup().findVirtual(
-            J.class, "targetMethodTest8", MethodType.methodType(void.class));
-    return new ConstantCallSite(targetMH);
-  }
-
-  public static CallSite bsmCreateCallCallingtargetMethodTest9(
-      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
-      throws Throwable {
-    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
-    final MethodHandle targetMH =
-        MethodHandles.lookup().findVirtual(
-            InvokeCustom.class, "targetMethodTest9", MethodType.methodType(void.class));
-    return new ConstantCallSite(targetMH);
-  }
-
   public void targetMethodTest10() {
     System.out.println("targetMethodTest10 from InvokeCustom");
   }
 
-  public static CallSite bsmCreateCallCallingtargetMethodTest10(
-      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
-      throws Throwable {
-    // Using mh to create the call site fails when run on Art. See b/36957105 for details.
-    final MethodHandle targetMH =
-        MethodHandles.lookup().findVirtual(
-            InvokeCustom.class, "targetMethodTest10", MethodType.methodType(void.class));
-    return new ConstantCallSite(targetMH);
-  }
-
-  public static CallSite bsmCreateCallCallingtargetMethod(
+  public static CallSite bsmCreateCallSite(
       MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
       throws Throwable {
     return new ConstantCallSite(mh);
diff --git a/src/test/examplesAndroidO/invokecustom/TestGenerator.java b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
index 883106f..5c550e9 100644
--- a/src/test/examplesAndroidO/invokecustom/TestGenerator.java
+++ b/src/test/examplesAndroidO/invokecustom/TestGenerator.java
@@ -189,7 +189,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethodTest6", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
     mv.visitInsn(Opcodes.DUP);
     mv.visitMethodInsn(
@@ -212,7 +212,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethodTest7", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
     mv.visitInsn(Opcodes.DUP);
     mv.visitMethodInsn(
@@ -235,7 +235,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethodTest8", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
     mv.visitInsn(Opcodes.DUP);
     mv.visitMethodInsn(
@@ -258,7 +258,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethodTest9", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
     mv.visitInsn(Opcodes.DUP);
     mv.visitMethodInsn(
@@ -281,7 +281,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethodTest10", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
     mv.visitInsn(Opcodes.DUP);
     mv.visitMethodInsn(
@@ -303,7 +303,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitFieldInsn(Opcodes.GETSTATIC,
         "java/lang/System",
         "out",
@@ -330,7 +330,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
             MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitLdcInsn("Write static field");
     mv.visitInvokeDynamicInsn("staticField1", "(Ljava/lang/String;)V", bootstrap,
         new Handle(Opcodes.H_PUTSTATIC, Type.getInternalName(InvokeCustom.class),
@@ -361,7 +361,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitFieldInsn(Opcodes.GETSTATIC,
         "java/lang/System",
         "out",
@@ -392,7 +392,7 @@
     MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
         MethodType.class, MethodHandle.class);
     Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
-        "bsmCreateCallCallingtargetMethod", mt.toMethodDescriptorString(), false);
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
     mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
     mv.visitInsn(Opcodes.DUP);
     mv.visitMethodInsn(
diff --git a/src/test/examplesAndroidO/invokecustom2/InvokeCustom.java b/src/test/examplesAndroidO/invokecustom2/InvokeCustom.java
new file mode 100644
index 0000000..62048b0
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom2/InvokeCustom.java
@@ -0,0 +1,186 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package invokecustom2;
+
+import java.lang.invoke.CallSite;
+import java.lang.invoke.ConstantCallSite;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.util.Arrays;
+import java.util.List;
+
+abstract class Super {
+  public void targetMethodTest4() {
+    System.out.println("targetMethodTest4 from Super");
+  }
+
+  public abstract void helperMethodTest9();
+}
+
+public class InvokeCustom extends Super implements Runnable {
+
+  public InvokeCustom() {}
+  public InvokeCustom(int i) {
+    System.out.println("InvokeCustom.<init>(" + i + ")");
+  }
+
+  private static void targetMethodTest1() {
+    System.out.println("Hello World!");
+  }
+
+  private static void targetMethodTest2(boolean z, byte b, char c, short s, int i, float f, long l,
+      double d, String str) {
+    System.out.println(z);
+    System.out.println(b);
+    System.out.println(c);
+    System.out.println(s);
+    System.out.println(i);
+    System.out.println(f);
+    System.out.println(l);
+    System.out.println(d);
+    System.out.println(str);
+  }
+
+  private static void targetMethodTest3() {
+    System.out.println("targetMethodTest3 from InvokeCustom");
+  }
+
+  @Override
+  public void targetMethodTest4() {
+    // The generated code should be calling Super.targetMethodTest4.
+    System.out.println("targetMethodTest4 from InvokeCustom (oops!)");
+  }
+
+  public static int targetMethodTest5(int x, int y, int total) {
+    int calculated = x + y;
+    System.out.println("targetMethodTest5 " + x + " + " + y + " = " + calculated);
+    if (calculated != total) {
+      System.out.println("Failed " + calculated + " != " + total);
+    }
+    return calculated;
+  }
+
+  public static long targetMethodTest6(long x, long y, long total) {
+    long calculated = x + y;
+    System.out.println("targetMethodTest6 " + x + " + " + y + " = " + calculated);
+    if (calculated != total) {
+      System.out.println("Failed " + calculated + " != " + total);
+    }
+    return calculated;
+  }
+
+  public static double targetMethodTest7(float x, float y, double product) {
+    double calculated = x * y;
+    System.out.println("targetMethodTest7 " + x + " * " + y + " = " + calculated);
+    if (calculated != product) {
+      System.out.println("Failed " + calculated + " != " + product);
+    }
+    return calculated;
+  }
+
+  public static void targetMethodTest8(String s) {
+    System.out.println("targetMethodTest8 " + s);
+  }
+
+  private static int staticFieldTest9 = 0;
+
+  private static void checkStaticFieldTest9(MethodHandle getter, MethodHandle setter)
+      throws Throwable {
+    final int NEW_VALUE = 0x76543210;
+    int oldValue = (int) getter.invokeExact();
+    setter.invokeExact(NEW_VALUE);
+    int newValue = (int) getter.invokeExact();
+    System.out.print("checkStaticFieldTest9: old " + oldValue + " new " + newValue +
+        " expected " + NEW_VALUE + " ");
+    System.out.println((newValue == NEW_VALUE) ? "OK" : "ERROR");
+  }
+
+  private float fieldTest9 = 0.0f;
+
+  private void checkFieldTest9(MethodHandle getter, MethodHandle setter)
+      throws Throwable {
+    final float NEW_VALUE = 1.99e-19f;
+    float oldValue = (float) getter.invokeExact(this);
+    setter.invokeExact(this, NEW_VALUE);
+    float newValue = (float) getter.invokeExact(this);
+    System.out.print("checkFieldTest9: old " + oldValue + " new " + newValue +
+        " expected " + NEW_VALUE + " ");
+    System.out.println((newValue == NEW_VALUE) ? "OK" : "ERROR");
+  }
+
+  public void helperMethodTest9() {
+    System.out.println("helperMethodTest9 in " + InvokeCustom.class);
+  }
+
+  private static void targetMethodTest9() {
+    System.out.println("targetMethodTest9()");
+  }
+
+  public void run() {
+    System.out.println("run() for Test9");
+  }
+
+  public static CallSite bsmLookupStatic(MethodHandles.Lookup caller, String name, MethodType type)
+      throws NoSuchMethodException, IllegalAccessException {
+    System.out.println("bsmLookupStatic []");
+    final MethodHandles.Lookup lookup = MethodHandles.lookup();
+    final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+    return new ConstantCallSite(targetMH.asType(type));
+  }
+
+  public static CallSite bsmLookupStaticWithExtraArgs(
+      MethodHandles.Lookup caller, String name, MethodType type, int i, long l, float f, double d)
+      throws NoSuchMethodException, IllegalAccessException {
+    System.out.println("bsmLookupStaticWithExtraArgs [" + i + ", " + l + ", " + f + ", " + d + "]");
+    final MethodHandles.Lookup lookup = MethodHandles.lookup();
+    final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+    return new ConstantCallSite(targetMH.asType(type));
+  }
+
+  public static CallSite bsmCreateCallSite(
+      MethodHandles.Lookup caller, String name, MethodType type, MethodHandle mh)
+      throws Throwable {
+    System.out.println("bsmCreateCallSite [" + mh + "]");
+    return new ConstantCallSite(mh);
+  }
+
+  private void privateMethodTest9() {
+    System.out.println("InvokeCustom.privateMethodTest9()");
+  }
+
+  public static CallSite bsmLookupTest9(MethodHandles.Lookup caller, String name, MethodType type,
+      MethodHandle staticGetter,  MethodHandle staticSetter,
+      MethodHandle fieldGetter, MethodHandle fieldSetter,
+      MethodHandle instanceInvoke, MethodHandle constructor,
+      MethodHandle interfaceInvoke, MethodHandle privateInvoke)
+      throws Throwable {
+    System.out.println("bsmLookupTest9 [" + staticGetter + ", " + staticSetter + ", " +
+        fieldGetter + ", " + fieldSetter + "]");
+    System.out.println(name + " " + type);
+
+    // Check constant method handles passed can be invoked.
+    checkStaticFieldTest9(staticGetter, staticSetter);
+    InvokeCustom instance = new InvokeCustom();
+    instance.checkFieldTest9(fieldGetter, fieldSetter);
+
+    // Check virtual method.
+    instanceInvoke.invokeExact(instance);
+
+    InvokeCustom instance2 = (InvokeCustom) constructor.invokeExact(3);
+    interfaceInvoke.invoke(instance2);
+    privateInvoke.invoke(instance2);
+
+    final MethodHandles.Lookup lookup = MethodHandles.lookup();
+    final MethodHandle targetMH = lookup.findStatic(lookup.lookupClass(), name, type);
+    return new ConstantCallSite(targetMH.asType(type));
+  }
+
+  public static void lambdaTest() {
+    List<String> strings = Arrays.asList(new String[] { "Three", "One", "FortyTwo" });
+    String sample = strings.stream().filter(x -> "One".equals(x.trim()))
+        .map(String::trim).findAny().orElse("");
+    strings.stream().forEach(System.out::println);
+  }
+}
\ No newline at end of file
diff --git a/src/test/examplesAndroidO/invokecustom2/TestGenerator.java b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
new file mode 100644
index 0000000..e9cbf7e
--- /dev/null
+++ b/src/test/examplesAndroidO/invokecustom2/TestGenerator.java
@@ -0,0 +1,343 @@
+// Copyright (c) 2017, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package invokecustom2;
+
+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.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(), InvokeCustom.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);
+            generateMethodTest3(cw);
+            generateMethodTest4(cw);
+            generateMethodTest5(cw);
+            generateMethodTest6(cw);
+            generateMethodTest7(cw);
+            generateMethodTest8(cw);
+            generateMethodTest9(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(InvokeCustom.class);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test1", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test2", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test3", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test4", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test5", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test6", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test7", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test8", "()V", false);
+    mv.visitMethodInsn(Opcodes.INVOKESTATIC, internalName, "test9", "()V", false);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method without extra args and no arg
+   *  to the target method.
+   */
+  private void generateMethodTest1(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test1", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitInvokeDynamicInsn("targetMethodTest1", "()V", bootstrap);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method without extra args and
+   *  args to the target method.
+   */
+  private void generateMethodTest2(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test2", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn(new Boolean(true));
+    mv.visitLdcInsn(new Byte((byte) 127));
+    mv.visitLdcInsn(new Character('c'));
+    mv.visitLdcInsn(new Short((short) 1024));
+    mv.visitLdcInsn(new Integer(123456));
+    mv.visitLdcInsn(new Float(1.2f));
+    mv.visitLdcInsn(new Long(123456789));
+    mv.visitLdcInsn(new Double(3.5123456789));
+    mv.visitLdcInsn("String");
+    mv.visitInvokeDynamicInsn("targetMethodTest2", "(ZBCSIFJDLjava/lang/String;)V", bootstrap);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with extra args and no arg
+   *  to the target method.
+   */
+  private void generateMethodTest3(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test3", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class, int.class,
+        long.class, float.class, double.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStaticWithExtraArgs", mt.toMethodDescriptorString(), false);
+    mv.visitInvokeDynamicInsn("targetMethodTest3", "()V", bootstrap, new Integer(1),
+        new Long(123456789), new Float(123.456), new Double(123456.789123));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   *  Generate test with an invokedynamic, a static bootstrap method with an extra arg that is a
+   *  MethodHandle of kind invokespecial.
+   */
+  private void generateMethodTest4(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test4", "()V",
+        null, null);
+    MethodType mt =
+        MethodType.methodType(
+            CallSite.class,
+            MethodHandles.Lookup.class,
+            String.class,
+            MethodType.class,
+            MethodHandle.class);
+    Handle bootstrap = new Handle( Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmCreateCallSite", mt.toMethodDescriptorString(), false);
+    mv.visitTypeInsn(Opcodes.NEW, Type.getInternalName(InvokeCustom.class));
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, Type.getInternalName(InvokeCustom.class),
+        "<init>", "()V", false);
+    mv.visitInvokeDynamicInsn("targetMethodTest4", "(Linvokecustom2/InvokeCustom;)V", bootstrap,
+        new Handle(Opcodes.H_INVOKESPECIAL, Type.getInternalName(Super.class),
+            "targetMethodTest4", "()V", false));
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate a test with an invokedynamic where the target generates
+   * a result that the call site prints out.
+   */
+  private void generateMethodTest5(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test5", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(
+        CallSite.class, MethodHandles.Lookup.class, String.class, MethodType.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitIntInsn(Opcodes.SIPUSH, 1000);
+    mv.visitIntInsn(Opcodes.SIPUSH, -923);
+    mv.visitIntInsn(Opcodes.SIPUSH, 77);
+    mv.visitInvokeDynamicInsn("targetMethodTest5", "(III)I", bootstrap);
+    mv.visitVarInsn(Opcodes.ISTORE, 0);
+    mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+    mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
+    mv.visitLdcInsn("targetMethodTest5 returned: ");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+        "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+    mv.visitVarInsn(Opcodes.ILOAD, 0);
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+        "(I)Ljava/lang/StringBuilder;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
+        "()Ljava/lang/String;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
+        "(Ljava/lang/String;)V");
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate a test with an invokedynamic where the call site invocation tests the summation of
+   * two long values and returns a long.
+   */
+  private void generateMethodTest6(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test6", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn(0x77777777777l);
+    mv.visitLdcInsn(-0x11111111111l);
+    mv.visitLdcInsn(0x66666666666l);
+    mv.visitInvokeDynamicInsn("targetMethodTest6", "(JJJ)J", bootstrap);
+    mv.visitVarInsn(Opcodes.LSTORE, 0);
+    mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+    mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
+    mv.visitLdcInsn("targetMethodTest6 returned: ");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+        "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+    mv.visitVarInsn(Opcodes.LLOAD, 0);
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+        "(J)Ljava/lang/StringBuilder;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
+        "()Ljava/lang/String;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
+        "(Ljava/lang/String;)V");
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate a test with an invokedynamic where the call site invocation tests the product of
+   * two float values and returns a double.
+   */
+  private void generateMethodTest7(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test7", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    double x = 0.5009765625;
+    double y = -x;
+    mv.visitLdcInsn((float) x);
+    mv.visitLdcInsn((float) y);
+    mv.visitLdcInsn(x * y);
+    mv.visitInvokeDynamicInsn("targetMethodTest7", "(FFD)D", bootstrap);
+    mv.visitVarInsn(Opcodes.DSTORE, 0);
+    mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+    mv.visitTypeInsn(Opcodes.NEW, "java/lang/StringBuilder");
+    mv.visitInsn(Opcodes.DUP);
+    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V");
+    mv.visitLdcInsn("targetMethodTest6 returned: ");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+        "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
+    mv.visitVarInsn(Opcodes.DLOAD, 0);
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
+        "(D)Ljava/lang/StringBuilder;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "toString",
+        "()Ljava/lang/String;");
+    mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println",
+        "(Ljava/lang/String;)V");
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate a test with multiple invokedynamic bytecodes operating on the same parameters.
+   * These invocations should each produce invoke-custom bytecodes with unique call site ids.
+   */
+  private void generateMethodTest8(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test8", "()V",
+        null, null);
+    MethodType mt = MethodType.methodType(CallSite.class, MethodHandles.Lookup.class, String.class,
+        MethodType.class);
+    // These should be two distinct call sites and both invoke the
+    // bootstrap method. An erroneous implementation might treat them
+    // as the same call site because the handle arguments are the same.
+    Handle bootstrap1 = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn("First invokedynamic invocation");
+    mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap1);
+
+    Handle bootstrap2 = new Handle(Opcodes.H_INVOKESTATIC, Type.getInternalName(InvokeCustom.class),
+        "bsmLookupStatic", mt.toMethodDescriptorString(), false);
+    mv.visitLdcInsn("Second invokedynamic invocation");
+    mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap2);
+
+    // Using same handle again creates a new call site so invokes the bootstrap method.
+    mv.visitLdcInsn("Dupe first invokedynamic invocation");
+    mv.visitInvokeDynamicInsn("targetMethodTest8", "(Ljava/lang/String;)V", bootstrap1);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+
+  /**
+   * Generate a test with different kinds of constant method handles.
+   */
+  private void generateMethodTest9(ClassVisitor cv) {
+    MethodVisitor mv = cv.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "test9", "()V",
+        null, null);
+    MethodType mt =
+        MethodType.methodType(CallSite.class,
+            MethodHandles.Lookup.class, String.class, MethodType.class,
+            MethodHandle.class, MethodHandle.class,
+            MethodHandle.class, MethodHandle.class,
+            MethodHandle.class, MethodHandle.class,
+            MethodHandle.class, MethodHandle.class);
+    String internalName = Type.getInternalName(InvokeCustom.class);
+    Handle bootstrap = new Handle(Opcodes.H_INVOKESTATIC, internalName, "bsmLookupTest9",
+        mt.toMethodDescriptorString(), false);
+    Handle staticSetter =
+        new Handle(Opcodes.H_GETSTATIC, internalName, "staticFieldTest9", "I", false);
+    Handle staticGetter =
+        new Handle(Opcodes.H_PUTSTATIC, internalName, "staticFieldTest9", "I", false);
+    Handle setter =
+        new Handle(Opcodes.H_GETFIELD, internalName, "fieldTest9", "F", false);
+    Handle getter =
+        new Handle(Opcodes.H_PUTFIELD, internalName, "fieldTest9", "F", false);
+    Handle instanceInvoke =
+        new Handle(Opcodes.H_INVOKEVIRTUAL, internalName, "helperMethodTest9", "()V", false);
+    Handle constructor =
+        new Handle(Opcodes.H_NEWINVOKESPECIAL, internalName, "<init>", "(I)V", false);
+    Handle interfaceInvoke =
+        new Handle(Opcodes.H_INVOKEINTERFACE,
+            Type.getInternalName(Runnable.class),
+            "run", "()V", true);
+    // test4 covers invokespecial for a super method. This covers invokespecial of a private method.
+    Handle privateInvoke =
+        new Handle(Opcodes.H_INVOKESPECIAL, internalName, "privateMethodTest9", "()V", false);
+
+    mv.visitInvokeDynamicInsn("targetMethodTest9", "()V", bootstrap,
+        staticSetter, staticGetter,
+        setter, getter,
+        instanceInvoke, constructor,
+        interfaceInvoke, privateInvoke);
+    mv.visitInsn(Opcodes.RETURN);
+    mv.visitMaxs(-1, -1);
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
index ab350a7..aa30615 100644
--- a/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
+++ b/src/test/java/com/android/tools/r8/R8RunArtTestsTest.java
@@ -644,6 +644,7 @@
           // Invoke-custom is supported by D8 and R8, but it can only run on our newest version
           // of art.
           .put("952-invoke-custom", beforeAndroidO)
+          .put("952-invoke-custom-kinds", beforeAndroidO)
           // Invoke-polymorphic is supported by D8 and R8, but it can only run on our newest version
           // of art.
           .put("953-invoke-polymorphic-compiler", beforeAndroidO)
@@ -773,6 +774,8 @@
               TestCondition.tools(DexTool.NONE, DexTool.DX),
               TestCondition.runtimes(
                   DexVm.ART_4_4_4, DexVm.ART_5_1_1, DexVm.ART_6_0_1, DexVm.ART_7_0_0)))
+          // No input dex files for DX
+          .put("952-invoke-custom-kinds", TestCondition.match(TestCondition.tools(DexTool.DX)))
           .build();
 
   public static List<String> requireInliningToBeDisabled = ImmutableList.of(
@@ -838,11 +841,7 @@
 
       // These two fail with missing *-hostdex.jar files.
       "648-inline-caches-unresolved",
-      "998-redefine-use-after-free",
-
-      // (b/63608437) This test uses method handle kind unsupported by R8 and trigger a R8 bug
-      // that shared call site entries when it should not.
-      "952-invoke-custom-kinds"
+      "998-redefine-use-after-free"
   );
 
   private static class TestSpecification {
diff --git a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
index e8d3c9d..937f55c 100644
--- a/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/RunExamplesAndroidOTest.java
@@ -168,7 +168,8 @@
               "repeat_annotations_new_api",
               // Dex version not supported
               "invokepolymorphic",
-              "invokecustom"
+              "invokecustom",
+              "invokecustom2"
           ),
           DexVm.ART_5_1_1, ImmutableList.of(
               // API not supported
@@ -176,7 +177,8 @@
               "repeat_annotations_new_api",
               // Dex version not supported
               "invokepolymorphic",
-              "invokecustom"
+              "invokecustom",
+              "invokecustom2"
           ),
           DexVm.ART_6_0_1, ImmutableList.of(
               // API not supported
@@ -184,14 +186,16 @@
               "repeat_annotations_new_api",
               // Dex version not supported
               "invokepolymorphic",
-              "invokecustom"
+              "invokecustom",
+              "invokecustom2"
           ),
           DexVm.ART_7_0_0, ImmutableList.of(
               // API not supported
               "paramnames",
               // Dex version not supported
               "invokepolymorphic",
-              "invokecustom"
+              "invokecustom",
+              "invokecustom2"
           ),
           DexVm.ART_DEFAULT, ImmutableList.of(
           )
@@ -228,6 +232,13 @@
   }
 
   @Test
+  public void invokeCustom2() throws Throwable {
+    test("invokecustom2", "invokecustom2", "InvokeCustom")
+        .withMinApiLevel(ANDROID_O_API)
+        .run();
+  }
+
+  @Test
   public void invokeCustomErrorDueToMinSdk() throws Throwable {
     test("invokecustom-error-due-to-min-sdk", "invokecustom", "InvokeCustom")
         .withMinApiLevel(25)