blob: e354c48f922c6d785902bd13710ca2b1c08bf100 [file] [log] [blame]
// Copyright (c) 2018, 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.neverreturnsnormally;
import com.android.tools.r8.ForceInline;
public class TestClass {
public static boolean throwNpe(String message) {
String newMessage = "prefix:" + message + ":suffix";
System.out.println("Before throwing NPE: " + newMessage);
throw new NullPointerException(newMessage);
}
@ForceInline
public static int throwToBeInlined() {
throwNpe("throwToBeInlined");
return "Nobody cares".length();
}
public static boolean throwOrLoop(boolean b) {
System.out.println("Inside TestClass::throwOrLoop");
if (b) {
while (true) {
}
} else {
throwNpe("");
return false; // Effectively unreachable
}
}
private int alwaysThrow(boolean b) {
int millis = (int) System.currentTimeMillis();
if (((millis & 1) == 0) == b) {
throw new AssertionError("alwaysThrow::if-then");
} else {
switch (millis) {
case 1:
throw new AssertionError("alwaysThrow::case 1");
case 2:
throwNpe("alwaysThrow::case 2");
return alwaysThrow(!b);
default:
throwNpe("alwaysThrow::default");
}
}
return millis;
}
private boolean loop(boolean b) {
while (true) {
if (b) {
throwNpe("");
return b;
}
}
}
private static int innerNotReachable() {
while (true) {
if ((System.currentTimeMillis() & 1) == 0) {
throwNpe("");
return 123;
} else {
innerNotReachable();
}
}
}
private static int outerTrivial() {
return innerNotReachable();
}
static void assertRemoved(String message) {
System.out.println("The method call is unreachable: " + message);
assertRemoved("recursive call");
}
public static void testTrivial() {
throwOrLoop(false);
assertRemoved("testTrivial");
}
public static void testInOneBranch(boolean b) {
if (b) {
throwOrLoop(true);
assertRemoved("testInOneBranch");
} else {
System.out.println("Non-throwing path");
}
}
public static void testInTwoBranches(int i) {
while (i > 0) {
TestClass testClass = new TestClass();
if ((i % 2) == 0) {
testClass.alwaysThrow(true);
} else {
testClass.loop(true);
}
i--;
assertRemoved("testInTwoBranches");
}
}
public static int testWithValueBeingUsed(int i) {
TestClass testClass = new TestClass();
int interim = testClass.alwaysThrow(false);
interim += testClass.alwaysThrow(true);
assertRemoved("testWithValueBeingUsed");
return interim * i;
}
public static int testWithTryCatch(int i) {
TestClass testClass = new TestClass();
int result = i;
try {
result += testClass.alwaysThrow(false);
assertRemoved("testWithTryCatch::try");
} catch (Exception e) {
result += testClass.loop(true) ? +1 : -1;
assertRemoved("testWithTryCatch::catch");
}
assertRemoved("testWithTryCatch::main");
return result;
}
public static int testWithTryFinally(int i) {
TestClass testClass = new TestClass();
int result = i;
try {
result += testClass.alwaysThrow(false);
assertRemoved("testWithTryFinally::try");
} finally {
result += testClass.loop(true) ? +1 : -1;
assertRemoved("testWithTryFinally::finally");
}
assertRemoved("testWithTryFinally::main");
return result;
}
public static void testInlinedIntoVoidMethod() {
throwToBeInlined();
assertRemoved("testInlinedIntoVoidMethod");
}
public static void testOuterTrivial() {
outerTrivial();
assertRemoved("testOuterTrivial");
}
public static void main(String[] args) {
try {
switch (args.length) {
case 0:
testTrivial();
break;
case 1:
testInOneBranch(true);
break;
case 2:
testInOneBranch(false);
break;
case 3:
testInTwoBranches(0);
break;
case 4:
testInTwoBranches(1);
break;
case 5:
testInTwoBranches(10);
break;
case 6:
testWithValueBeingUsed(6);
break;
case 7:
testWithTryCatch(7);
break;
case 8:
testWithTryFinally(8);
break;
case 9:
testInlinedIntoVoidMethod();
break;
case 10:
testOuterTrivial();
break;
}
} catch (Exception e) {
System.out.println("e = " + e);
}
}
}