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);