blob: ac779d61b9012129e75719647d95d04bbe742388 [file] [log] [blame]
// Copyright (c) 2017, 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 trywithresources;
import java.io.Closeable;
import java.io.IOException;
public abstract class TryWithResources {
// --- TEST SUPPORT ---
interface Test {
void test() throws Throwable;
}
private void test(Test test) {
try {
test.test();
} catch (Throwable e) {
dumpException(e);
}
}
private void dumpException(Throwable e) {
dumpException(e, "Exception: ");
}
private void dumpException(Throwable e, String indent) {
assert e != null;
System.out.println(indent + e.getMessage());
indent = indent.replaceAll("[^:]", " ");
Throwable cause = e.getCause();
if (cause != null) {
dumpException(cause, indent + " cause: ");
}
// Dump suppressed UNLESS it is a desugared code running
// on JVM, in which case we avoid dumping suppressed, since
// the output will be used for comparison with desugared code
// running on device.
if (!desugaredCodeRunningOnJvm()) {
Throwable[] suppressed = e.getSuppressed();
for (int i = 0; i < suppressed.length; i++) {
dumpException(suppressed[i], indent + "supp[" + i + "]: ");
}
}
}
abstract boolean desugaredCodeRunningOnJvm();
// --- TEST SYMBOLS ---
static class Resource implements Closeable {
final String tag;
Resource(String tag) {
this.tag = tag;
}
@Override
public void close() throws IOException {
Class<? extends Resource> cls = this.getClass();
System.out.println("Closing " + tag + " (" +
cls.getName().substring(TryWithResources.class.getName().length() + 1) + ")");
}
}
// --- TEST ---
class RegularTryWithResources {
class RegularResource extends Resource {
RegularResource(String tag) {
super(tag);
}
}
private void test() throws Throwable {
test(2);
}
private void test(int level) throws Throwable {
try (RegularResource a = new RegularResource("a" + level);
RegularResource b = new RegularResource("b" + level)) {
if (level > 0) {
try {
test(level - 1);
} catch (Throwable e) {
throw new RuntimeException("e" + level, e);
}
}
throw new RuntimeException("primary cause");
}
}
}
// --- TEST ---
class FailingTryWithResources {
class FailingResource extends Resource {
FailingResource(String tag) {
super(tag);
}
@Override
public void close() throws IOException {
super.close();
throw new RuntimeException("failed to close '" + tag + "'");
}
}
private void test() throws Throwable {
test(2);
}
private void test(int level) throws Throwable {
try (FailingResource a = new FailingResource("a" + level);
FailingResource b = new FailingResource("b" + level)) {
if (level > 0) {
try {
test(level - 1);
} catch (Throwable e) {
throw new RuntimeException("e" + level, e);
}
}
throw new RuntimeException("primary cause");
}
}
}
// --- TEST ---
class ExplicitAddGetSuppressed {
class RegularResource extends Resource {
RegularResource(String tag) {
super(tag);
}
@Override
public void close() throws IOException {
super.close();
throw new RuntimeException("failed to close '" + tag + "'");
}
}
private void test() throws Throwable {
test(2);
}
private void test(int level) throws RuntimeException {
try (RegularResource a = new RegularResource("a" + level);
RegularResource b = new RegularResource("b" + level)) {
if (level > 0) {
try {
test(level - 1);
} catch (RuntimeException e) {
// Just collect suppressed, but throw away the exception.
RuntimeException re = new RuntimeException("e" + level);
for (Throwable suppressed : e.getSuppressed()) {
re.addSuppressed(suppressed);
}
throw re;
}
}
throw new RuntimeException("primary cause");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
// --- TEST ---
interface Consumer {
void act(RuntimeException re);
}
interface Supplier {
Throwable[] get();
}
class AddGetSuppressedRoundTrip {
private void test() throws Throwable {
RuntimeException carrier = new RuntimeException("carrier");
Consumer packer = carrier::addSuppressed;
Supplier unpacker = carrier::getSuppressed;
packer.act(new RuntimeException("original exception A"));
packer.act(new RuntimeException("original exception Z"));
for (Throwable unpacked : unpacker.get()) {
if (!desugaredCodeRunningOnJvm()) {
dumpException(unpacked);
}
}
}
}
// --- TEST ---
class UnreachableCatchAfterCallsRemoved {
private void test() throws Throwable {
RuntimeException main = new RuntimeException("main");
RuntimeException origA = new RuntimeException("original exception A");
RuntimeException origB = new RuntimeException("original exception Z");
try {
// After both calls below are removed, the whole catch
// handler should be removed.
main.addSuppressed(origA);
main.addSuppressed(origB);
} catch (Throwable t) {
throw new RuntimeException("UNREACHABLE");
}
// Return value not used.
main.getSuppressed();
}
}
// --- MAIN TEST ---
void test() throws Exception {
System.out.println("----- TEST 1 -----");
test(new RegularTryWithResources()::test);
System.out.println("----- TEST 2 -----");
test(new FailingTryWithResources()::test);
System.out.println("----- TEST 3 -----");
test(new ExplicitAddGetSuppressed()::test);
System.out.println("----- TEST 4 -----");
test(new AddGetSuppressedRoundTrip()::test);
System.out.println("----- TEST 5 -----");
test(new UnreachableCatchAfterCallsRemoved()::test);
System.out.println("------------------");
}
}