1 /*
  2  *  Copyright (c) 2021, 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  * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
 27  * @run testng/othervm --enable-native-access=ALL-UNNAMED SafeFunctionAccessTest
 28  */
 29 
 30 import jdk.incubator.foreign.Addressable;
 31 import jdk.incubator.foreign.CLinker;
 32 import jdk.incubator.foreign.FunctionDescriptor;
 33 import jdk.incubator.foreign.NativeSymbol;
 34 import jdk.incubator.foreign.SymbolLookup;
 35 import jdk.incubator.foreign.MemoryLayout;
 36 import jdk.incubator.foreign.MemorySegment;
 37 import jdk.incubator.foreign.ResourceScope;
 38 
 39 import java.lang.invoke.MethodHandle;
 40 import java.lang.invoke.MethodHandles;
 41 import java.lang.invoke.MethodType;
 42 
 43 import jdk.incubator.foreign.VaList;
 44 import org.testng.annotations.*;
 45 
 46 import static org.testng.Assert.*;
 47 
 48 public class SafeFunctionAccessTest extends NativeTestHelper {
 49     static {
 50         System.loadLibrary("SafeAccess");
 51     }
 52 
 53     static MemoryLayout POINT = MemoryLayout.structLayout(
 54             C_INT, C_INT
 55     );
 56 
 57     static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();
 58 
 59     @Test(expectedExceptions = IllegalStateException.class)
 60     public void testClosedStruct() throws Throwable {
 61         MemorySegment segment;
 62         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
 63             segment = MemorySegment.allocateNative(POINT, scope);
 64         }
 65         assertFalse(segment.scope().isAlive());
 66         MethodHandle handle = CLinker.systemCLinker().downcallHandle(
 67                 LOOKUP.lookup("struct_func").get(),
 68                 FunctionDescriptor.ofVoid(POINT));
 69 
 70         handle.invokeExact(segment);
 71     }
 72 
 73     @Test(expectedExceptions = IllegalStateException.class)
 74     public void testClosedVaList() throws Throwable {
 75         VaList list;
 76         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
 77             list = VaList.make(b -> b.addVarg(C_INT, 42), scope);
 78         }
 79         assertFalse(list.scope().isAlive());
 80         MethodHandle handle = CLinker.systemCLinker().downcallHandle(
 81                 LOOKUP.lookup("addr_func").get(),
 82                 FunctionDescriptor.ofVoid(C_POINTER));
 83 
 84         handle.invokeExact((Addressable)list);
 85     }
 86 
 87     @Test(expectedExceptions = IllegalStateException.class)
 88     public void testClosedUpcall() throws Throwable {
 89         NativeSymbol upcall;
 90         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
 91             MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class));
 92             upcall = CLinker.systemCLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), scope);
 93         }
 94         assertFalse(upcall.scope().isAlive());
 95         MethodHandle handle = CLinker.systemCLinker().downcallHandle(
 96                 LOOKUP.lookup("addr_func").get(),
 97                 FunctionDescriptor.ofVoid(C_POINTER));
 98 
 99         handle.invokeExact((Addressable)upcall);
100     }
101 
102     static void dummy() { }
103 
104     @Test
105     public void testClosedVaListCallback() throws Throwable {
106         MethodHandle handle = CLinker.systemCLinker().downcallHandle(
107                 LOOKUP.lookup("addr_func_cb").get(),
108                 FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
109 
110         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
111             VaList list = VaList.make(b -> b.addVarg(C_INT, 42), scope);
112             handle.invoke(list, scopeChecker(scope));
113         }
114     }
115 
116     @Test
117     public void testClosedStructCallback() throws Throwable {
118         MethodHandle handle = CLinker.systemCLinker().downcallHandle(
119                 LOOKUP.lookup("addr_func_cb").get(),
120                 FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
121 
122         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
123             MemorySegment segment = MemorySegment.allocateNative(POINT, scope);
124             handle.invoke(segment, scopeChecker(scope));
125         }
126     }
127 
128     @Test
129     public void testClosedUpcallCallback() throws Throwable {
130         MethodHandle handle = CLinker.systemCLinker().downcallHandle(
131                 LOOKUP.lookup("addr_func_cb").get(),
132                 FunctionDescriptor.ofVoid(C_POINTER, C_POINTER));
133 
134         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
135             MethodHandle dummy = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "dummy", MethodType.methodType(void.class));
136             NativeSymbol upcall = CLinker.systemCLinker().upcallStub(dummy, FunctionDescriptor.ofVoid(), scope);
137             handle.invoke(upcall, scopeChecker(scope));
138         }
139     }
140 
141     NativeSymbol scopeChecker(ResourceScope scope) {
142         try {
143             MethodHandle handle = MethodHandles.lookup().findStatic(SafeFunctionAccessTest.class, "checkScope",
144                     MethodType.methodType(void.class, ResourceScope.class));
145             handle = handle.bindTo(scope);
146             return CLinker.systemCLinker().upcallStub(handle, FunctionDescriptor.ofVoid(), ResourceScope.newImplicitScope());
147         } catch (Throwable ex) {
148             throw new AssertionError(ex);
149         }
150     }
151 
152     static void checkScope(ResourceScope scope) {
153         try {
154             scope.close();
155             fail("Scope closed unexpectedly!");
156         } catch (IllegalStateException ex) {
157             assertTrue(ex.getMessage().contains("kept alive")); //if acquired, fine
158         }
159     }
160 }