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 junit/othervm -Dbabylon.tryFinally=sharedDispatch TestTryWithResources
30 * @run main Unreflect TestTryWithResources
31 * @run junit TestTryWithResources
32 * @run junit/othervm -Dbabylon.tryFinally=sharedDispatch TestTryWithResources
33 */
34
35 import java.io.Closeable;
36 import java.io.IOException;
37 import java.lang.invoke.MethodHandle;
38 import java.lang.invoke.MethodHandles;
39 import java.lang.invoke.MethodType;
40 import java.lang.reflect.Method;
41 import java.util.ArrayList;
42 import java.util.function.Consumer;
43 import java.util.stream.Stream;
44
45 import jdk.incubator.code.Op;
46 import jdk.incubator.code.Reflect;
47 import jdk.incubator.code.bytecode.BytecodeGenerator;
48 import jdk.incubator.code.dialect.core.CoreOp;
49 import org.junit.jupiter.params.ParameterizedTest;
50 import org.junit.jupiter.params.provider.MethodSource;
51
52 import static org.junit.jupiter.api.Assertions.*;
53
54 public class TestTryWithResources {
55
56 record Resource(Consumer<String> log, String suffix, boolean throwOnClose, boolean throwOnCreate) implements Closeable {
57
58 Resource {
59 log.accept("open" + suffix);
60 if (throwOnCreate) {
61 log.accept("throwCreate" + suffix);
62 throw new RuntimeException();
63 }
64 }
65
66 @Override
67 public void close() throws IOException {
68 log.accept("close" + suffix);
69 if (throwOnClose) {
70 log.accept("throwClose" + suffix);
71 throw new IOException();
72 }
73 }
74 }
75
76 @Reflect
77 public static void tryWithResources(Consumer<String> log, boolean throwInBody, boolean throwOnClose1,
78 boolean throwOnClose2, boolean throwOnClose3, boolean throwOnCreate1,
79 boolean throwOnCreate2, boolean throwOnCreate3) throws IOException {
80 var r2 = new Resource(log, "2", throwOnClose2, throwOnCreate2);
81 try {
82 try (var _ = new Resource(log, "1", throwOnClose1, throwOnCreate1)) {
83 log.accept("outerBody");
84 try (var _ = r2;
85 var _ = new Resource(log, "3", throwOnClose3, throwOnCreate3)) {
86 log.accept("innerBody");
87 if (throwInBody) {
88 log.accept("throwBody");
89 throw new IllegalStateException("body");
90 }
91 } finally {
92 log.accept("innerFinally");
93 }
94 } finally {
95 log.accept("outerFinally");
96 }
97 } finally {
98 log.accept("end");
99 }
100 }
101
102 static Stream<MethodHandle> mhs() throws NoSuchMethodException, IllegalAccessException {
103 Method m = TestTryWithResources.class.getDeclaredMethod("tryWithResources", Consumer.class, boolean.class,
104 boolean.class, boolean.class, boolean.class, boolean.class, boolean.class, boolean.class);
105 CoreOp.FuncOp fop = Op.ofMethod(m).orElseThrow();
106 return Stream.of(
107 BytecodeGenerator.generate(MethodHandles.lookup(), fop),
108 MethodHandles.lookup().findStatic(Interpreter.class, "invoke",
109 MethodType.methodType(Object.class, MethodHandles.Lookup.class, Op.class, Object[].class))
110 .bindTo(MethodHandles.lookup())
111 .bindTo(fop)
112 .asVarargsCollector(Object[].class)
113 );
114 }
115
116 @ParameterizedTest
117 @MethodSource("mhs")
118 public void testTryWithResources(MethodHandle mh) {
119 for (int i = 0; i < 128; i++) {
120 boolean throwInBody = (i & 1) != 0;
121 boolean throwOnClose1 = (i & 2) != 0;
122 boolean throwOnClose2 = (i & 4) != 0;
123 boolean throwOnClose3 = (i & 8) != 0;
124 boolean throwOnCreate1 = (i & 16) != 0;
125 boolean throwOnCreate2 = (i & 32) != 0;
126 boolean throwOnCreate3 = (i & 64) != 0;
127 var expected = new ArrayList<String>();
128 var actual = new ArrayList<String>();
129 try {
130 tryWithResources(expected::add, throwInBody, throwOnClose1, throwOnClose2, throwOnClose3,
131 throwOnCreate1, throwOnCreate2, throwOnCreate3);
132 mh.invoke((Consumer<String>)actual::add, throwInBody, throwOnClose1, throwOnClose2, throwOnClose3,
133 throwOnCreate1, throwOnCreate2, throwOnCreate3);
134 } catch (Throwable t) {
135 assertEquals(t.getSuppressed().length,
136 assertThrowsExactly(t.getClass(), () -> mh.invoke((Consumer<String>)actual::add, throwInBody,
137 throwOnClose1, throwOnClose2, throwOnClose3, throwOnCreate1, throwOnCreate2,
138 throwOnCreate3)).getSuppressed().length);
139 }
140 assertIterableEquals(expected, actual);
141 }
142 }
143
144 }