Merge pull request #9 from rutenkolk/feature/jdk21-support

migrate to jdk 22 and fix upcalls
This commit is contained in:
Joshua Suskalo 2024-07-24 18:25:26 -05:00 committed by GitHub
commit a1a7cd0d47
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 99 additions and 34 deletions

View file

@ -52,8 +52,8 @@
(b/process {:command-args ["javac" "--enable-preview" (b/process {:command-args ["javac" "--enable-preview"
"src/java/coffi/ffi/Loader.java" "src/java/coffi/ffi/Loader.java"
"-d" class-dir "-d" class-dir
"-target" "21" "-target" "22"
"-source" "21"]}) "-source" "22"]})
opts) opts)
(defn- write-pom (defn- write-pom

View file

@ -1,6 +1,6 @@
{:paths ["src/clj" "target/classes" "resources"] {:paths ["src/clj" "target/classes" "resources"]
:deps {org.clojure/clojure {:mvn/version "1.11.1"} :deps {org.clojure/clojure {:mvn/version "1.11.1"}
insn/insn {:mvn/version "0.2.1"}} insn/insn {:mvn/version "0.5.4"}}
:deps/prep-lib {:alias :build :deps/prep-lib {:alias :build
:fn build/compile-java :fn build/compile-java

View file

@ -135,6 +135,7 @@
method handle without reflection, unboxing primitives when needed." method handle without reflection, unboxing primitives when needed."
[args ret] [args ret]
{:flags #{:public :final} {:flags #{:public :final}
:version 8
:super clojure.lang.AFunction :super clojure.lang.AFunction
:fields [{:name "downcall_handle" :fields [{:name "downcall_handle"
:type MethodHandle :type MethodHandle
@ -291,7 +292,7 @@
;; cast null pointers to something understood by panama ;; cast null pointers to something understood by panama
(#{::mem/pointer} type) (#{::mem/pointer} type)
`(or ~sym (MemoryAddress/NULL)) `(or ~sym (MemorySegment/NULL))
(mem/primitive-type type) (mem/primitive-type type)
`(mem/serialize* ~sym ~type-sym ~session) `(mem/serialize* ~sym ~type-sym ~session)
@ -466,6 +467,7 @@
boxes any primitives passed to it and calls a closed over [[IFn]]." boxes any primitives passed to it and calls a closed over [[IFn]]."
[arg-types ret-type] [arg-types ret-type]
{:flags #{:public :final} {:flags #{:public :final}
:version 8
:fields [{:name "upcall_ifn" :fields [{:name "upcall_ifn"
:type IFn :type IFn
:flags #{:final}}] :flags #{:final}}]
@ -538,7 +540,6 @@
(defmethod mem/serialize* ::fn (defmethod mem/serialize* ::fn
[f [_fn arg-types ret-type & {:keys [raw-fn?]}] arena] [f [_fn arg-types ret-type & {:keys [raw-fn?]}] arena]
(println "Attempting to serialize function of type" (str ret-type "(*)(" (clojure.string/join "," arg-types) ")"))
(.upcallStub (.upcallStub
(Linker/nativeLinker) (Linker/nativeLinker)
^MethodHandle (cond-> f ^MethodHandle (cond-> f

View file

@ -269,14 +269,12 @@
(.addCloseAction session action) (.addCloseAction session action)
nil) nil)
;; TODO(Joshua): Determine if this needs to exist at all
#_
(defn as-segment (defn as-segment
"Dereferences an `address` into a memory segment associated with the `session`." "Dereferences an `address` into a memory segment associated with the `session`."
(^MemorySegment [^MemoryAddress address size] (^MemorySegment [^MemorySegment address size]
(MemorySegment/ofAddress address (long size) (connected-session))) (.reinterpret (MemorySegment/ofAddress address) (long size) (connected-session) nil))
(^MemorySegment [^MemoryAddress address size session] (^MemorySegment [^MemorySegment address size session]
(MemorySegment/ofAddress address (long size) session))) (.reinterpret (MemorySegment/ofAddress address) (long size) session nil)))
(defn copy-segment (defn copy-segment
"Copies the content to `dest` from `src`. "Copies the content to `dest` from `src`.
@ -1247,15 +1245,15 @@
::pointer) ::pointer)
(defmethod serialize* ::c-string (defmethod serialize* ::c-string
[obj _type session] [obj _type ^Arena session]
(if obj (if obj
(address-of (.allocateUtf8String (arena-allocator session) ^String obj)) (.allocateFrom session ^String obj)
(MemorySegment/NULL))) (MemorySegment/NULL)))
(defmethod deserialize* ::c-string (defmethod deserialize* ::c-string
[addr _type] [addr _type]
(when-not (null? addr) (when-not (null? addr)
(.getUtf8String (.reinterpret ^MemorySegment addr Integer/MAX_VALUE) 0))) (.getString (.reinterpret ^MemorySegment addr Integer/MAX_VALUE) 0)))
;;; Union types ;;; Union types

View file

@ -26,40 +26,55 @@ CString upcall_test(StringFactory fun) {
return fun(); return fun();
} }
int upcall_test2(int (*f)(void)) {
return f();
}
int counter = 0; int counter = 0;
static char* responses[] = { "Hello, world!", "Goodbye friend.", "co'oi prenu" }; static char* responses[] = { "Hello, world!", "Goodbye friend.", "co'oi prenu" };
char* upcall_test_int_fn_string_ret(int (*f)(void)) {
return responses[f()];
}
CString get_string1(void) { CString get_string1(void) {
return responses[counter++ % 3]; return responses[counter++ % 3];
} }
CString get_string2(void) { CString get_string2(void) {
return "Alternate string"; return "Alternate string";
} }
StringFactory get_downcall(int whichString) { StringFactory get_downcall(int whichString) {
switch (whichString % 2) { switch (whichString % 2) {
case 0: case 0:
return get_string1; return get_string1;
case 1: case 1:
return get_string2; return get_string2;
default: default:
return 0; return 0;
} }
} }
typedef struct alignment_test { typedef struct alignment_test {
char a; char a;
double x; double x;
float y; float y;
} AlignmentTest; } AlignmentTest;
AlignmentTest get_struct() { AlignmentTest get_struct() {
AlignmentTest ret = {}; AlignmentTest ret = {};
ret.a = 'x'; ret.a = 'x';
ret.x = 3.14; ret.x = 3.14;
ret.y = 42.0; ret.y = 42.0;
return ret; return ret;
} }
void test_call_with_trailing_string_arg(int a, int b, char* text) {
printf("call of `test_call_with_trailing_string_arg` with a=%i b=%i text='%s'",1,2,text);
printf("\r ");
return;
}

View file

@ -29,8 +29,18 @@
(t/deftest can-make-upcall (t/deftest can-make-upcall
(t/is (= ((ffi/cfn "upcall_test" [[::ffi/fn [] ::mem/c-string]] ::mem/c-string) (t/is (= ((ffi/cfn "upcall_test" [[::ffi/fn [] ::mem/c-string]] ::mem/c-string)
(fn [] "hello")) (fn [] "hello from clojure from c from clojure"))
"hello"))) "hello from clojure from c from clojure")))
(t/deftest can-make-upcall2
(t/is (= ((ffi/cfn "upcall_test2" [[::ffi/fn [] ::mem/int]] ::mem/int)
(fn [] 5))
5)))
(t/deftest can-make-upcall-int-fn-string-ret
(t/is (= ((ffi/cfn "upcall_test_int_fn_string_ret" [[::ffi/fn [] ::mem/int]] ::mem/c-string)
(fn [] 2))
"co'oi prenu")))
(mem/defalias ::alignment-test (mem/defalias ::alignment-test
(layout/with-c-layout (layout/with-c-layout
@ -49,3 +59,12 @@
(ffi/freset! (ffi/static-variable "counter" ::mem/int) 1) (ffi/freset! (ffi/static-variable "counter" ::mem/int) 1)
(t/is (= ((ffi/cfn "get_string1" [] ::mem/c-string)) (t/is (= ((ffi/cfn "get_string1" [] ::mem/c-string))
"Goodbye friend."))) "Goodbye friend.")))
(t/deftest can-call-with-trailing-string-arg
(t/is
(=
((ffi/cfn "test_call_with_trailing_string_arg"
[::mem/int ::mem/int ::mem/c-string]
::mem/void)
1 2 "third arg"))))

View file

@ -0,0 +1,32 @@
(ns coffi.mem-test
(:require
[clojure.test :as t]
[coffi.ffi :as ffi]
[coffi.layout :as layout]
[coffi.mem :as mem])
(:import
(java.lang.foreign
AddressLayout
Arena
MemoryLayout
MemorySegment
MemorySegment$Scope
SegmentAllocator
ValueLayout
ValueLayout$OfByte
ValueLayout$OfShort
ValueLayout$OfInt
ValueLayout$OfLong
ValueLayout$OfChar
ValueLayout$OfFloat
ValueLayout$OfDouble)
(java.lang.ref Cleaner)
(java.nio ByteOrder)))
(ffi/load-library "target/ffi_test.so")
(t/deftest can-serialize-string
(t/is
(instance? MemorySegment (mem/serialize "this is a string" ::mem/c-string))))