1 /*
  2  * Copyright (c) 2024, 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.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package hat.backend.ffi;
 26 
 27 import java.lang.foreign.FunctionDescriptor;
 28 import java.lang.foreign.Linker;
 29 import java.lang.foreign.MemorySegment;
 30 import java.lang.foreign.SymbolLookup;
 31 import java.lang.invoke.MethodHandle;
 32 
 33 import static java.lang.foreign.ValueLayout.ADDRESS;
 34 import static java.lang.foreign.ValueLayout.JAVA_BOOLEAN;
 35 import static java.lang.foreign.ValueLayout.JAVA_INT;
 36 import static java.lang.foreign.ValueLayout.JAVA_LONG;
 37 
 38 public class FFILib {
 39     final public String name;
 40     public final boolean available;
 41 
 42     final public Linker nativeLinker;
 43 
 44     final public SymbolLookup loaderLookup;
 45 
 46     public static class MethodPtr{
 47         final FFILib ffiLib;
 48         final FunctionDescriptor functionDescriptor;
 49         final MethodHandle mh;
 50         final String name;
 51 
 52         MethodPtr(FFILib ffiLib, FunctionDescriptor descriptor, String name) {
 53             this.ffiLib = ffiLib;
 54             this.functionDescriptor= descriptor;
 55             this.mh = ffiLib.loaderLookup.find(name)
 56                     .map(symbolSegment -> ffiLib.nativeLinker.downcallHandle(symbolSegment, descriptor))
 57                     .orElse(null);
 58             if (this.mh == null) {
 59                 System.err.println("Could not find method " + name + " in " + ffiLib.name);
 60             }
 61             this.name = name;
 62         }
 63 
 64     }
 65 
 66     public static class VoidAddressMethodPtr extends MethodPtr{
 67         VoidAddressMethodPtr(FFILib ffiLib, String name) {
 68             super(ffiLib,FunctionDescriptor.ofVoid(ADDRESS), name);
 69         }
 70         public void invoke(MemorySegment memorySegment) {
 71             if (mh == null){
 72                 throw new RuntimeException("Null methodhandle "+name);
 73             }
 74             try {
 75                 mh.invoke(memorySegment);
 76             } catch (Throwable e) {
 77                 throw new RuntimeException(e);
 78             }
 79         }
 80     }
 81 
 82     public static class VoidHandleMethodPtr extends MethodPtr{
 83         VoidHandleMethodPtr(FFILib ffiLib, String name) {
 84             super(ffiLib, FunctionDescriptor.ofVoid(JAVA_LONG), name);
 85         }
 86         public void invoke(long handle) {
 87             if (mh == null){
 88                 throw new RuntimeException("Null methodhandle "+name);
 89             }
 90             if (handle == 0) {
 91                 throw new RuntimeException("handle is zero");
 92             }
 93             try {
 94                 mh.invoke(handle);
 95             } catch (Throwable e) {
 96                 throw new RuntimeException(e);
 97             }
 98         }
 99     }
100 
101     public static class BooleanHandleMethodPtr extends MethodPtr{
102         BooleanHandleMethodPtr(FFILib ffiLib, String name) {
103             super(ffiLib, FunctionDescriptor.of(JAVA_BOOLEAN,JAVA_LONG),name);
104         }
105         public boolean invoke(long handle) {
106             if (mh == null){
107                 throw new RuntimeException("Null methodhandle "+name);
108             }
109             if (handle == 0L) {
110                 throw new IllegalArgumentException("handle is zero");
111             }
112             try {
113                 return (boolean)mh.invoke(handle);
114             } catch (Throwable e) {
115                 throw new RuntimeException(e);
116             }
117         }
118     }
119 
120     public static class BooleanHandleAddressLongMethodPtr extends MethodPtr{
121         BooleanHandleAddressLongMethodPtr(FFILib ffiLib, String name) {
122             super(ffiLib, FunctionDescriptor.of(JAVA_BOOLEAN,JAVA_LONG,ADDRESS,JAVA_LONG), name);
123         }
124         public boolean invoke(long handle,MemorySegment memorySegment, long len) {
125             if (mh == null){
126                 throw new RuntimeException("Null methodhandle "+name);
127             }
128             if (handle == 0L) {
129                 throw new IllegalArgumentException("handle is zero");
130             }
131             try {
132                 return (boolean)mh.invoke(handle, memorySegment, len);
133             } catch (Throwable e) {
134                 throw new RuntimeException(e);
135             }
136         }
137     }
138 
139     public static class LongHandleIntAddressMethodPtr extends MethodPtr{
140         LongHandleIntAddressMethodPtr(FFILib ffiLib, String name) {
141             super(ffiLib, FunctionDescriptor.of(JAVA_LONG,JAVA_LONG,JAVA_INT,ADDRESS), name);
142         }
143         public long invoke(long handle, int i, MemorySegment memorySegment) {
144             if (mh == null){
145                 throw new RuntimeException("Null methodhandle "+name);
146             }
147             if (handle == 0L) {
148                 throw new IllegalArgumentException("handle is zero");
149             }
150             try {
151                 return (long)mh.invoke(handle, i, memorySegment);
152             } catch (Throwable e) {
153                 throw new RuntimeException(e);
154             }
155         }
156     }
157 
158     public static class LongHandleIntMethodPtr extends MethodPtr{
159         LongHandleIntMethodPtr(FFILib ffiLib, String name) {
160             super(ffiLib,FunctionDescriptor.of(JAVA_LONG,JAVA_INT), name);
161         }
162         public long invoke( int i) {
163             if (mh == null){
164                 throw new RuntimeException("Null method handle trying to invoke "+ffiLib.name+"::"+name+"()");
165             }
166             try {
167                 return (long)mh.invoke(i);
168             } catch (Throwable e) {
169                 throw new RuntimeException(e);
170             }
171         }
172     }
173 
174     public static class LongHandleLongAddressMethodPtr extends MethodPtr{
175         LongHandleLongAddressMethodPtr(FFILib ffiLib, String name) {
176             super(ffiLib,FunctionDescriptor.of(JAVA_LONG,JAVA_LONG,ADDRESS), name);
177         }
178         public long invoke(long l,  MemorySegment memorySegment) {
179             if (mh == null){
180                 throw new RuntimeException("Null methodhandle "+name);
181             }
182             try {
183                 return (long)mh.invoke(l, memorySegment);
184             } catch (Throwable e) {
185                 throw new RuntimeException(e);
186             }
187         }
188     }
189 
190     public FFILib(String name) {
191         this.name = name;
192 
193         boolean nonFinalAvailable = true;
194         try {
195             Runtime.getRuntime().loadLibrary(name);
196         } catch (UnsatisfiedLinkError e) {
197             nonFinalAvailable = false;
198         }
199         this.available = nonFinalAvailable;
200         this.nativeLinker = Linker.nativeLinker();
201         this.loaderLookup = SymbolLookup.loaderLookup();
202     }
203 
204 
205     public VoidAddressMethodPtr voidAddressFunc(String name) {
206         return new VoidAddressMethodPtr(this, name);
207     }
208 
209     public VoidHandleMethodPtr voidHandleFunc(String name) {
210         return new VoidHandleMethodPtr(this, name);
211     }
212     public BooleanHandleMethodPtr booleanHandleFunc(String name) {
213         return new BooleanHandleMethodPtr(this, name);
214     }
215     public BooleanHandleAddressLongMethodPtr booleanHandleAddressLongFunc(String name) {
216         return new BooleanHandleAddressLongMethodPtr(this, name);
217     }
218     public LongHandleIntAddressMethodPtr longHandleIntAddressFunc(String name) {
219         return new LongHandleIntAddressMethodPtr(this, name);
220     }
221     public LongHandleIntMethodPtr longHandleIntFunc(String name) {
222         return new LongHandleIntMethodPtr(this, name);
223     }
224     public LongHandleLongAddressMethodPtr longHandleLongAddressFunc(String name) {
225         return new LongHandleLongAddressMethodPtr(this, name);
226     }
227 
228 }