Merge pull request #9 from rutenkolk/feature/jdk21-support
migrate to jdk 22 and fix upcalls
This commit is contained in:
commit
a1a7cd0d47
7 changed files with 99 additions and 34 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
2
deps.edn
2
deps.edn
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"))))
|
||||||
|
|
||||||
|
|
|
||||||
32
test/clj/coffi/mem_test.clj
Normal file
32
test/clj/coffi/mem_test.clj
Normal 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))))
|
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in a new issue