diff --git a/build.clj b/build.clj index 90919c4..7d00c55 100644 --- a/build.clj +++ b/build.clj @@ -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 diff --git a/deps.edn b/deps.edn index d0e1469..a6c2599 100644 --- a/deps.edn +++ b/deps.edn @@ -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 diff --git a/src/clj/coffi/ffi.clj b/src/clj/coffi/ffi.clj index bff4e99..365fa10 100644 --- a/src/clj/coffi/ffi.clj +++ b/src/clj/coffi/ffi.clj @@ -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 diff --git a/src/clj/coffi/mem.clj b/src/clj/coffi/mem.clj index fab0981..23139c1 100644 --- a/src/clj/coffi/mem.clj +++ b/src/clj/coffi/mem.clj @@ -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 diff --git a/test/c/ffi_test.c b/test/c/ffi_test.c index c689e87..e38ff30 100644 --- a/test/c/ffi_test.c +++ b/test/c/ffi_test.c @@ -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; +} + diff --git a/test/clj/coffi/ffi_test.clj b/test/clj/coffi/ffi_test.clj index 15cc4b6..d45e3ae 100644 --- a/test/clj/coffi/ffi_test.clj +++ b/test/clj/coffi/ffi_test.clj @@ -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")))) + diff --git a/test/clj/coffi/mem_test.clj b/test/clj/coffi/mem_test.clj new file mode 100644 index 0000000..b52c1eb --- /dev/null +++ b/test/clj/coffi/mem_test.clj @@ -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)))) + +