Merge "Only inline constructors if the receiver remains the this value."
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 a11d924..dfe10cf 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
@@ -20,6 +20,7 @@
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.Invoke;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.code.ValueNumberGenerator;
@@ -272,12 +273,17 @@
return true;
}
+ // We cannot invoke <init> on other values than |this| on Dalvik 4.4.4. Compute whether
+ // the receiver to the call was the this value at the call-site.
+ boolean receiverOfInnerCallIsThisOfOuter = invoke.asInvokeDirect().getReceiver().isThis();
+
// Don't allow inlining a constructor into a non-constructor if the first use of the
// 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.
+ // we inline into a constructor, calls to super.<init> are also OK if the receiver of the
+ // super.<init> call is the this argument.
InstructionIterator iterator = code.instructionIterator();
Instruction instruction = iterator.next();
// A constructor always has the un-initialized object as the first argument.
@@ -290,11 +296,17 @@
if (instruction.isInvokeDirect() && !seenSuperInvoke) {
DexMethod target = instruction.asInvokeDirect().getInvokedMethod();
seenSuperInvoke = appInfo.dexItemFactory.isConstructor(target);
+ boolean callOnSupertypeOfThisInConstructor =
+ methodHolder.isImmediateSubtypeOf(target.holder)
+ && instruction.asInvokeDirect().getReceiver() == unInitializedObject
+ && receiverOfInnerCallIsThisOfOuter
+ && methodIsConstructor;
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)) {
+ // If we are inlining into a constructor, calls to superclass init are OK on the
+ // |this| value in the outer context.
+ && !callOnSupertypeOfThisInConstructor) {
return false;
}
}