|  | // 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: "); | 
|  | } | 
|  |  | 
|  | Throwable[] suppressed = e.getSuppressed(); | 
|  | for (int i = 0; i < suppressed.length; i++) { | 
|  | dumpException(suppressed[i], indent + "supp[" + i + "]: "); | 
|  | } | 
|  | } | 
|  |  | 
|  | // --- 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()) { | 
|  | 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("------------------"); | 
|  | } | 
|  | } |