blob: 9bc40d77107aa85c86fa88b8dd1979cfa836c62c [file] [log] [blame]
// Copyright (c) 2019, 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 com.android.tools.r8.ir.optimize.inliner;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.utils.StringUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
// Regression test for b/128987064
@RunWith(Parameterized.class)
public class DoubleInliningInvokeSuperTest extends TestBase {
public static final String EXPECTED = StringUtils.lines("8", "8");
@Parameters(name = "{0}")
public static TestParametersCollection params() {
return getTestParameters().withAllRuntimes().build();
}
private final TestParameters parameters;
public DoubleInliningInvokeSuperTest(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(DoubleInliningInvokeSuperTest.class)
.addKeepMainRule(TestClass.class)
.addKeepRules("-keepclassmembers class * { void fooCaller(...); }")
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.enableMergeAnnotations()
.setMinApi(parameters.getRuntime())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
@NeverClassInline
@NeverMerge
static class A {
int x;
@NeverInline
A foo(int x) {
this.x = x;
return this;
}
}
@NeverClassInline
@NeverMerge
static class B extends A {
// B#foo is invoked twice by other wrappers in the same class.
@Override
B foo(int x) {
// this invoke-super should not be inlined to the class outside of the class hierarchy.
super.foo(x);
return this;
}
// place-holder to make B#foo to be a double-inline selected target.
B fooWrapper(Integer x) {
int y = System.currentTimeMillis() > 0 ? x : x * 2;
return foo(y);
}
// Another B#foo caller.
B anotherWrapper(Integer x) {
int y = System.currentTimeMillis() > 0 ? x : x * 2;
// invoke-super in B#foo is inlined here during double-inlining.
return foo(y);
}
}
static class TestClass {
// place-holder to make B#fooWrapper live. This one is force kept.
static void fooCaller(B b) {
System.out.println(b.fooWrapper(8).x);
}
public static void main(String[] args) {
B instance = new B();
// By invoking B#anotherWrapper twice, `main` is regarded as double-inline caller.
// Due to the name order, B#fooWrapper is processed first, and invoke-super is moved.
// Its inlining constraints aren't updated yet since inliner does not finish processing all
// the methods in the double-inline pool. Now, when processing `main`, invoke-super in
// B#anotherWrapper (which is inlined from B#foo) is flown to here, resulting in illegal
// invoke-super at the 2nd tree shaking phase.
System.out.println(instance.anotherWrapper(8).x);
System.out.println(instance.anotherWrapper(8).x);
}
}
}