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"
"src/java/coffi/ffi/Loader.java"
"-d" class-dir
"-target" "21"
"-source" "21"]})
"-target" "22"
"-source" "22"]})
opts)
(defn- write-pom

View file

@ -1,6 +1,6 @@
{:paths ["src/clj" "target/classes" "resources"]
: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
:fn build/compile-java

View file

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

View file

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

View file

@ -26,40 +26,55 @@ CString upcall_test(StringFactory fun) {
return fun();
}
int upcall_test2(int (*f)(void)) {
return f();
}
int counter = 0;
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) {
return responses[counter++ % 3];
return responses[counter++ % 3];
}
CString get_string2(void) {
return "Alternate string";
return "Alternate string";
}
StringFactory get_downcall(int whichString) {
switch (whichString % 2) {
case 0:
return get_string1;
case 1:
return get_string2;
default:
return 0;
}
switch (whichString % 2) {
case 0:
return get_string1;
case 1:
return get_string2;
default:
return 0;
}
}
typedef struct alignment_test {
char a;
double x;
float y;
char a;
double x;
float y;
} AlignmentTest;
AlignmentTest get_struct() {
AlignmentTest ret = {};
ret.a = 'x';
ret.x = 3.14;
ret.y = 42.0;
AlignmentTest ret = {};
ret.a = 'x';
ret.x = 3.14;
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/is (= ((ffi/cfn "upcall_test" [[::ffi/fn [] ::mem/c-string]] ::mem/c-string)
(fn [] "hello"))
"hello")))
(fn [] "hello from clojure from c from clojure"))
"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
(layout/with-c-layout
@ -49,3 +59,12 @@
(ffi/freset! (ffi/static-variable "counter" ::mem/int) 1)
(t/is (= ((ffi/cfn "get_string1" [] ::mem/c-string))
"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))))