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 }