blob: bf95231f0eee6998e237de628a6591a2254d95e3 [file] [log] [blame]
// Copyright (c) 2020, 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.regress.b165825758;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
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 com.android.tools.r8.utils.codeinspector.ClassSubject;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
import com.android.tools.r8.utils.codeinspector.InstructionOffsetSubject;
import com.android.tools.r8.utils.codeinspector.InstructionSubject;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.android.tools.r8.utils.codeinspector.RangeSubject;
import com.android.tools.r8.utils.codeinspector.TryCatchSubject;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class Regress165825758Test extends TestBase {
static final String EXPECTED = StringUtils.lines("Hello, world");
private final TestParameters parameters;
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimes().withAllApiLevels().build();
}
public Regress165825758Test(TestParameters parameters) {
this.parameters = parameters;
}
@Test
public void testReference() throws Exception {
testForRuntime(parameters)
.addInnerClasses(Regress165825758Test.class)
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED);
}
@Test
public void testR8() throws Exception {
testForR8(parameters.getBackend())
.enableInliningAnnotations()
.addInnerClasses(Regress165825758Test.class)
.addKeepMainRule(TestClass.class)
.addKeepClassRules(A.class)
.setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(EXPECTED)
.inspect(this::checkDeadCodeThrowInTryRange);
}
private void checkDeadCodeThrowInTryRange(CodeInspector inspector) {
ClassSubject classSubject = inspector.clazz(A.class);
assertThat(classSubject, isPresent());
MethodSubject method = classSubject.uniqueMethodWithName("synchronizedMethod");
assertThat(method, isPresent());
// Ensure that the "throwNpe" method remains and that it was not inlined by checking that no
// allocations of NullPointerException are in the method.
assertTrue(method.streamInstructions().noneMatch(InstructionSubject::isNewInstance));
assertThat(inspector.clazz(TestClass.class).uniqueMethodWithName("throwNpe"), isPresent());
// Source has 2 catch ranges:
// 1st try catch is the source range, 2nd is the compiler inserted catch over monitor-exit.
// When compiled with R8 the catch ranges are collapsed.
List<TryCatchSubject> tryCatchSubjects = method.streamTryCatches().collect(Collectors.toList());
assertEquals(1, tryCatchSubjects.size());
TryCatchSubject sourceTry = tryCatchSubjects.get(0);
// 1st throw is the "dead code" throw, the 2nd is the exceptional rethrow after method exit.
List<InstructionSubject> throwInstructions =
method
.streamInstructions()
.filter(InstructionSubject::isThrow)
.collect(Collectors.toList());
assertEquals(2, throwInstructions.size());
InstructionSubject deadCodeThrow = throwInstructions.get(0);
InstructionOffsetSubject throwOffset = deadCodeThrow.getOffset(method);
RangeSubject range = sourceTry.getRange();
assertTrue(
"Expected throw@" + throwOffset + " to be in try-range " + range,
range.includes(throwOffset));
}
static class A {
@NeverInline
void synchronizedMethod() {
synchronized (this) {
TestClass.throwNpe();
System.out.println("Never hit");
}
}
}
static class TestClass {
@NeverInline
static void throwNpe() {
throw new NullPointerException();
}
public static void main(String[] args) {
try {
new A().synchronizedMethod();
} catch (NullPointerException e) {
System.out.println("Hello, world");
}
}
}
}