1 /*
2 * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 /*
25 * @test
26 * @modules jdk.incubator.code
27 * @library lib
28 * @run junit TestTryWithResources
29 * @run main Unreflect TestTryWithResources
30 * @run junit TestTryWithResources
31 */
32
33 import java.io.Closeable;
34 import java.io.IOException;
35 import java.lang.invoke.MethodHandle;
36 import java.lang.invoke.MethodHandles;
37 import java.lang.reflect.Method;
38 import java.util.ArrayList;
39 import java.util.function.Consumer;
40 import jdk.incubator.code.Op;
41 import jdk.incubator.code.Reflect;
42 import jdk.incubator.code.bytecode.BytecodeGenerator;
43 import org.junit.jupiter.api.Test;
44
45 import static org.junit.jupiter.api.Assertions.*;
46
47 public class TestTryWithResources {
48
49 record Resource(Consumer<String> log, String suffix, boolean throwOnClose) implements Closeable {
50
51 Resource {
52 log.accept("open" + suffix);
53 }
54
55 @Override
56 public void close() throws IOException {
57 log.accept("close" + suffix);
58 if (throwOnClose) {
59 log.accept("throwClose" + suffix);
60 throw new IOException();
61 }
62 }
63 }
64
65 @Reflect
66 public static void tryWithResources(Consumer<String> log, boolean throwInBody, boolean throwOnClose1, boolean throwOnClose2, boolean throwOnClose3) throws IOException {
67 var r2 = new Resource(log, "2", throwOnClose2);
68 try {
69 try (var _ = new Resource(log, "1", throwOnClose1)) {
70 log.accept("outerBody");
71 try (var _ = r2;
72 var _ = new Resource(log, "3", throwOnClose3)) {
73 log.accept("innerBody");
74 if (throwInBody) {
75 log.accept("throwBody");
76 throw new IllegalStateException("body");
77 }
78 } finally {
79 log.accept("innerFinally");
80 }
81 } finally {
82 log.accept("outerFinally");
83 }
84 } finally {
85 log.accept("end");
86 }
87 }
88
89 @Test
90 public void testTryWithResources() throws Throwable {
91 Method m = TestTryWithResources.class.getDeclaredMethod("tryWithResources", Consumer.class, boolean.class, boolean.class, boolean.class, boolean.class);
92 MethodHandle mh = BytecodeGenerator.generate(MethodHandles.lookup(), Op.ofMethod(m).orElseThrow());
93 for (int i = 0; i < 16; i++) {
94 boolean throwInBody = (i & 1) != 0;
95 boolean throwOnClose1 = (i & 2) != 0;
96 boolean throwOnClose2 = (i & 4) != 0;
97 boolean throwOnClose3 = (i & 8) != 0;
98 var expected = new ArrayList<String>();
99 var actual = new ArrayList<String>();
100 try {
101 tryWithResources(expected::add, throwInBody, throwOnClose1, throwOnClose2, throwOnClose3);
102 mh.invoke((Consumer<String>)actual::add, throwInBody, throwOnClose1, throwOnClose2, throwOnClose3);
103 } catch (Throwable t) {
104 assertEquals(t.getSuppressed().length,
105 assertThrowsExactly(t.getClass(), () -> mh.invoke((Consumer<String>)actual::add, throwInBody, throwOnClose1, throwOnClose2, throwOnClose3)).getSuppressed().length);
106 }
107 assertIterableEquals(expected, actual);
108 }
109 }
110 }