Inlining constraint visitor for jar code

Change-Id: I0203a5bc7d24294948565b7cb9aa081d767b59f2
diff --git a/src/main/java/com/android/tools/r8/graph/JarCode.java b/src/main/java/com/android/tools/r8/graph/JarCode.java
index d64eaf0..2c37c6a 100644
--- a/src/main/java/com/android/tools/r8/graph/JarCode.java
+++ b/src/main/java/com/android/tools/r8/graph/JarCode.java
@@ -30,6 +30,7 @@
 import org.objectweb.asm.tree.LabelNode;
 import org.objectweb.asm.tree.LineNumberNode;
 import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.TryCatchBlockNode;
 import org.objectweb.asm.util.Textifier;
 import org.objectweb.asm.util.TraceMethodVisitor;
 
@@ -173,17 +174,27 @@
   }
 
   public Constraint computeInliningConstraint(
-      AppInfoWithLiveness appInfo, GraphLense graphLense, DexType invocationContext) {
+      DexEncodedMethod encodedMethod,
+      AppInfoWithLiveness appInfo,
+      GraphLense graphLense,
+      DexType invocationContext) {
     InliningConstraintVisitor visitor =
-        new InliningConstraintVisitor(application, appInfo, graphLense, method, invocationContext);
+        new InliningConstraintVisitor(
+            application, appInfo, graphLense, encodedMethod, invocationContext);
     AbstractInsnNode insn = node.instructions.getFirst();
     while (insn != null) {
       insn.accept(visitor);
       if (visitor.isFinished()) {
-        break;
+        return visitor.getConstraint();
       }
       insn = insn.getNext();
     }
+    for (TryCatchBlockNode block : node.tryCatchBlocks) {
+      visitor.accept(block);
+      if (visitor.isFinished()) {
+        return visitor.getConstraint();
+      }
+    }
     return visitor.getConstraint();
   }
 
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 53bbb7e..23d9a73 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
@@ -959,7 +959,7 @@
   private Invoke.Type invokeType(MethodInsnNode method) {
     switch (method.getOpcode()) {
       case Opcodes.INVOKEVIRTUAL:
-        if (isCallToPolymorphicSignatureMethod(method)) {
+        if (isCallToPolymorphicSignatureMethod(method.owner, method.name)) {
           return Invoke.Type.POLYMORPHIC;
         }
         return Invoke.Type.VIRTUAL;
@@ -2954,17 +2954,17 @@
     return writer.toString();
   }
 
-  private boolean isCallToPolymorphicSignatureMethod(MethodInsnNode method) {
-    if (method.owner.equals("java/lang/invoke/MethodHandle")) {
-      switch (method.name) {
+  public static boolean isCallToPolymorphicSignatureMethod(String owner, String name) {
+    if (owner.equals("java/lang/invoke/MethodHandle")) {
+      switch (name) {
         case "invoke":
         case "invokeExact":
           return true;
         default :
           return false;
       }
-    } else if (method.owner.equals("java/lang/invoke/VarHandle")) {
-      switch (method.name) {
+    } else if (owner.equals("java/lang/invoke/VarHandle")) {
+      switch (name) {
         case "compareAndExchange":
         case "compareAndExchangeAcquire":
         case "compareAndExchangeRelease":
diff --git a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
index 5652be8..4807b89 100644
--- a/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
+++ b/src/main/java/com/android/tools/r8/jar/InliningConstraintVisitor.java
@@ -9,38 +9,42 @@
 import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.errors.Unreachable;
 import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
 import com.android.tools.r8.graph.DexMethod;
 import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.GraphLense;
 import com.android.tools.r8.graph.JarApplicationReader;
+import com.android.tools.r8.ir.conversion.JarSourceCode;
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.ir.optimize.InliningConstraints;
 import com.android.tools.r8.shaking.Enqueuer.AppInfoWithLiveness;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+import org.objectweb.asm.tree.TryCatchBlockNode;
 
 // This visitor can be used to determine if a piece of jar code has any instructions that the
 // inliner would not be willing to inline. This can be used to determine if a method can be force
 // inlined although its IR is still not available.
 //
-// TODO(christofferqa): This class is incomplete. Still need to add support for ConstClass,
-// InstanceGet, InstanceOf, InstancePut, Monitor, MoveException, NewArrayEmpty, NewInstance,
-// StaticGet, StaticPut, etc.
+// Note that this class has only been implemented for the hooks in InliningConstraints that may
+// return a non-ALWAYS inlining constraint (e.g., InliningConstraints.forReturn is not called).
 public class InliningConstraintVisitor extends MethodVisitor {
 
   private final JarApplicationReader application;
   private final AppInfoWithLiveness appInfo;
   private final InliningConstraints inliningConstraints;
-  private final DexMethod method;
+  private final DexEncodedMethod method;
   private final DexType invocationContext;
 
-  private Constraint constraint = Constraint.ALWAYS;
+  private Constraint constraint;
 
   public InliningConstraintVisitor(
       JarApplicationReader application,
       AppInfoWithLiveness appInfo,
       GraphLense graphLense,
-      DexMethod method,
+      DexEncodedMethod method,
       DexType invocationContext) {
     super(ASM6);
     this.application = application;
@@ -48,6 +52,10 @@
     this.inliningConstraints = new InliningConstraints(appInfo, graphLense);
     this.method = method;
     this.invocationContext = invocationContext;
+
+    // Model a synchronized method as having a monitor instruction.
+    this.constraint =
+        method.accessFlags.isSynchronized() ? inliningConstraints.forMonitor() : Constraint.ALWAYS;
   }
 
   public Constraint getConstraint() {
@@ -64,11 +72,59 @@
     return constraint == Constraint.NEVER;
   }
 
+  public void accept(TryCatchBlockNode tryCatchBlock) {
+    // Model a try-catch as a move-exception instruction.
+    updateConstraint(inliningConstraints.forMoveException());
+  }
+
+  @Override
+  public void visitFieldInsn(int opcode, String owner, String name, String desc) {
+    DexField field = application.getField(owner, name, desc);
+    switch (opcode) {
+      case Opcodes.GETFIELD:
+        updateConstraint(inliningConstraints.forInstanceGet(field, invocationContext));
+        break;
+
+      case Opcodes.PUTFIELD:
+        updateConstraint(inliningConstraints.forInstancePut(field, invocationContext));
+        break;
+
+      case Opcodes.GETSTATIC:
+        updateConstraint(inliningConstraints.forStaticGet(field, invocationContext));
+        break;
+
+      case Opcodes.PUTSTATIC:
+        updateConstraint(inliningConstraints.forStaticPut(field, invocationContext));
+        break;
+
+      default:
+        throw new Unreachable("Unexpected opcode " + opcode);
+    }
+  }
+
+  @Override
+  public void visitLdcInsn(Object cst) {
+    if (cst instanceof Type && ((Type) cst).getSort() != Type.METHOD) {
+      DexType type = application.getType((Type) cst);
+      updateConstraint(inliningConstraints.forConstClass(type, invocationContext));
+    } else {
+      updateConstraint(inliningConstraints.forConstInstruction());
+    }
+  }
+
   @Override
   public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
     DexType ownerType = application.getTypeFromName(owner);
     DexMethod target = application.getMethod(ownerType, name, desc);
     switch (opcode) {
+      case Opcodes.INVOKEDYNAMIC:
+        if (JarSourceCode.isCallToPolymorphicSignatureMethod(owner, name)) {
+          updateConstraint(inliningConstraints.forInvokePolymorphic(target, invocationContext));
+        } else {
+          updateConstraint(inliningConstraints.forInvokeCustom());
+        }
+        break;
+
       case Opcodes.INVOKEINTERFACE:
         updateConstraint(inliningConstraints.forInvokeInterface(target, invocationContext));
         break;
@@ -88,7 +144,7 @@
       case Opcodes.INVOKEVIRTUAL:
         // Instructions that target a private method in the same class are translated to
         // invoke-direct.
-        if (target.holder == method.holder) {
+        if (target.holder == method.method.holder) {
           DexClass clazz = appInfo.definitionFor(target.holder);
           if (clazz != null && clazz.lookupDirectMethod(target) != null) {
             updateConstraint(inliningConstraints.forInvokeDirect(target, invocationContext));
@@ -105,10 +161,30 @@
   }
 
   @Override
+  public void visitInsn(int opcode) {
+    switch (opcode) {
+      case Opcodes.MONITORENTER:
+      case Opcodes.MONITOREXIT:
+        updateConstraint(inliningConstraints.forMonitor());
+        break;
+
+      default:
+        // All instructions here lead to the inlining constraint ALWAYS.
+    }
+  }
+
+  @Override
+  public void visitMultiANewArrayInsn(String desc, int dims) {
+    DexType type = application.getTypeFromDescriptor(desc);
+    updateConstraint(inliningConstraints.forInvokeMultiNewArray(type, invocationContext));
+  }
+
+  @Override
   public void visitTypeInsn(int opcode, String typeName) {
     DexType type = application.getTypeFromName(typeName);
     switch (opcode) {
       case Opcodes.ANEWARRAY:
+        updateConstraint(inliningConstraints.forNewArrayEmpty(type, invocationContext));
         break;
 
       case Opcodes.CHECKCAST:
@@ -116,9 +192,11 @@
         break;
 
       case Opcodes.INSTANCEOF:
+        updateConstraint(inliningConstraints.forInstanceOf(type, invocationContext));
         break;
 
       case Opcodes.NEW:
+        updateConstraint(inliningConstraints.forNewInstance(type, invocationContext));
         break;
 
       default:
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index f38626e..2fbdab2 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -1373,6 +1373,7 @@
       JarCode jarCode = method.getCode().asJarCode();
       Constraint constraint =
           jarCode.computeInliningConstraint(
+              method,
               appInfo,
               new SingleTypeMapperGraphLense(method.method.holder, invocationContext),
               invocationContext);