Further constrain constructor inlining.

This brings is in compliance with Section 4.10.2.4 of the JVM spec,
which DALVIK seems to enforce.

Bug:
Change-Id: I6d9df726efa6af2f12250db35e98c14ce727fc76
diff --git a/src/main/java/com/android/tools/r8/graph/DexType.java b/src/main/java/com/android/tools/r8/graph/DexType.java
index e06c0ad..a371684 100644
--- a/src/main/java/com/android/tools/r8/graph/DexType.java
+++ b/src/main/java/com/android/tools/r8/graph/DexType.java
@@ -453,4 +453,9 @@
     }
     return getPackageOrName(false);
   }
+
+  public boolean isImmediateSubtypeOf(DexType type) {
+    assert hierarchyLevel != UNKNOWN_LEVEL;
+    return type.directSubtypes.contains(this);
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
index f750b09..b017db0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Inliner.java
@@ -252,12 +252,13 @@
     // Before that method invokes another instance initialization method of myClass or its direct
     // superclass on this, the only operation the method can perform on this is assigning fields
     // declared within myClass.
-    //
 
     // Allow inlining a constructor into a constructor of the same class, as the constructor code
     // is expected to adhere to the VM specification.
-    if (method.accessFlags.isConstructor()
-        && method.method.holder == invoke.getInvokedMethod().holder) {
+    DexType methodHolder = method.method.holder;
+    boolean methodIsConstructor =
+        method.accessFlags.isConstructor() && !method.accessFlags.isStatic();
+    if (methodIsConstructor && methodHolder == invoke.asInvokeMethod().getInvokedMethod().holder) {
       return true;
     }
 
@@ -265,6 +266,8 @@
     // un-initialized object is not an argument of an invoke of <init>.
     // Also, we cannot inline a constructor if it initializes final fields, as such is only allowed
     // from within a constructor of the corresponding class.
+    // Lastly, we can only inline a constructor, if its own <init> call is on the method's class. If
+    // we inline into a constructor, calls to super.<init> are also OK.
     InstructionIterator iterator = code.instructionIterator();
     Instruction instruction = iterator.next();
     // A constructor always has the un-initialized object as the first argument.
@@ -277,12 +280,23 @@
         if (instruction.isInvokeDirect() && !seenSuperInvoke) {
           DexMethod target = instruction.asInvokeDirect().getInvokedMethod();
           seenSuperInvoke = appInfo.dexItemFactory.isConstructor(target);
+          if (seenSuperInvoke
+              // Calls to init on same class are always OK.
+              && target.holder != methodHolder
+              // If we are inlining into a constructor, calls to superclass init are OK.
+              && (!methodHolder.isImmediateSubtypeOf(target.holder) || !methodIsConstructor)) {
+            return false;
+          }
         }
         if (!seenSuperInvoke) {
           return false;
         }
       }
       if (instruction.isInstancePut()) {
+        // Fields may not be initialized outside of a constructor.
+        if (!methodIsConstructor) {
+          return false;
+        }
         DexField field = instruction.asInstancePut().getField();
         DexEncodedField target = appInfo.lookupInstanceTarget(field.getHolder(), field);
         if (target != null && target.accessFlags.isFinal()) {
diff --git a/src/test/examples/inlining/Inlining.java b/src/test/examples/inlining/Inlining.java
index bd8870f..f89a0e1 100644
--- a/src/test/examples/inlining/Inlining.java
+++ b/src/test/examples/inlining/Inlining.java
@@ -52,14 +52,39 @@
     this.a = a;
   }
 
-  @CheckDiscarded
   InlineConstructor(long a) {
     this((int) a);
   }
 
+  InlineConstructor(int a, int loopy) {
+    this.a = a;
+    // Make this too big to inline.
+    if (loopy > 10) {
+      throw new RuntimeException("Too big!");
+    }
+    for (int i = 1; i < loopy; i++) {
+      this.a = this.a * i;
+    }
+  }
+
+  @CheckDiscarded
+  InlineConstructor() {
+    this(42, 9);
+  }
+
   static InlineConstructor create() {
     return new InlineConstructor(10L);
   }
+
+  static InlineConstructor createMore() {
+    new InlineConstructor(0, 0);
+    new InlineConstructor(0, 0);
+    new InlineConstructor(0, 0);
+    new InlineConstructor(0, 0);
+    new InlineConstructor(0, 0);
+    new InlineConstructor(0, 0);
+    return new InlineConstructor();
+  }
 }
 
 class InlineConstructorOfInner {
@@ -152,6 +177,8 @@
 
     InlineConstructor ic = InlineConstructor.create();
     Assert(ic != null);
+    InlineConstructor ic2 = InlineConstructor.createMore();
+    Assert(ic2 != null);
     InlineConstructorOfInner icoi = new InlineConstructorOfInner();
     Assert(icoi != null);