Add support for returning non-primitive types via segment allocators
This commit is contained in:
parent
d67d539285
commit
fc3b0a96a5
1 changed files with 46 additions and 10 deletions
|
|
@ -20,7 +20,8 @@
|
||||||
MemoryAddress
|
MemoryAddress
|
||||||
MemoryLayout
|
MemoryLayout
|
||||||
MemorySegment
|
MemorySegment
|
||||||
ResourceScope)))
|
ResourceScope
|
||||||
|
SegmentAllocator)))
|
||||||
|
|
||||||
(defn stack-scope
|
(defn stack-scope
|
||||||
"Constructs a new scope for use only in this thread.
|
"Constructs a new scope for use only in this thread.
|
||||||
|
|
@ -59,6 +60,15 @@
|
||||||
^ResourceScope []
|
^ResourceScope []
|
||||||
(ResourceScope/globalScope))
|
(ResourceScope/globalScope))
|
||||||
|
|
||||||
|
(defn scope-allocator
|
||||||
|
"Constructs a segment allocator from the given `scope`.
|
||||||
|
|
||||||
|
This is primarily used when working with unwrapped downcall functions. When a
|
||||||
|
downcall function returns a non-primitive type, it must be provided with an
|
||||||
|
allocator."
|
||||||
|
^SegmentAllocator [^ResourceScope scope]
|
||||||
|
(SegmentAllocator/ofScope scope))
|
||||||
|
|
||||||
(defn alloc
|
(defn alloc
|
||||||
"Allocates `size` bytes.
|
"Allocates `size` bytes.
|
||||||
|
|
||||||
|
|
@ -66,6 +76,13 @@
|
||||||
([size] (alloc size (connected-scope)))
|
([size] (alloc size (connected-scope)))
|
||||||
([size scope] (MemorySegment/allocateNative ^long size ^ResourceScope scope)))
|
([size scope] (MemorySegment/allocateNative ^long size ^ResourceScope scope)))
|
||||||
|
|
||||||
|
(defn alloc-with
|
||||||
|
"Allocates `size` bytes using the `allocator`."
|
||||||
|
([allocator size]
|
||||||
|
(.allocate ^SegmentAllocator allocator (long size)))
|
||||||
|
([allocator size alignment]
|
||||||
|
(.allocate ^SegmentAllocator allocator (long size) (long alignment))))
|
||||||
|
|
||||||
(defmacro with-acquired
|
(defmacro with-acquired
|
||||||
"Acquires a `scope` to ensure it will not be released until the `body` completes.
|
"Acquires a `scope` to ensure it will not be released until the `body` completes.
|
||||||
|
|
||||||
|
|
@ -719,17 +736,25 @@
|
||||||
[:return]]}
|
[:return]]}
|
||||||
{:name :invoke
|
{:name :invoke
|
||||||
:flags #{:public}
|
:flags #{:public}
|
||||||
:desc (repeat (inc (count args)) Object)
|
:desc (repeat (cond-> (inc (count args))
|
||||||
|
(not (primitive-type ret)) inc)
|
||||||
|
Object)
|
||||||
:emit [[:aload 0]
|
:emit [[:aload 0]
|
||||||
[:getfield :this "downcall_handle" MethodHandle]
|
[:getfield :this "downcall_handle" MethodHandle]
|
||||||
|
(when-not (primitive-type ret)
|
||||||
|
[[:aload 1]
|
||||||
|
[:checkcast SegmentAllocator]])
|
||||||
(map-indexed
|
(map-indexed
|
||||||
(fn [idx arg]
|
(fn [idx arg]
|
||||||
[[:aload (inc idx)]
|
[[:aload (cond-> (inc idx)
|
||||||
|
(not (primitive-type ret)) inc)]
|
||||||
(to-prim-asm arg)])
|
(to-prim-asm arg)])
|
||||||
args)
|
args)
|
||||||
[:invokevirtual MethodHandle "invokeExact"
|
[:invokevirtual MethodHandle "invokeExact"
|
||||||
(conj (mapv insn-layout args)
|
(cond->>
|
||||||
(insn-layout ret))]
|
(conj (mapv insn-layout args)
|
||||||
|
(insn-layout ret))
|
||||||
|
(not (primitive-type ret)) (cons SegmentAllocator))]
|
||||||
(to-object-asm ret)
|
(to-object-asm ret)
|
||||||
[:areturn]]}]})
|
[:areturn]]}]})
|
||||||
|
|
||||||
|
|
@ -752,7 +777,10 @@
|
||||||
The function returned takes only arguments whose types match exactly
|
The function returned takes only arguments whose types match exactly
|
||||||
the [[java-layout]] for that type, and returns an argument with exactly
|
the [[java-layout]] for that type, and returns an argument with exactly
|
||||||
the [[java-layout]] of the `ret` type. This function will perform no
|
the [[java-layout]] of the `ret` type. This function will perform no
|
||||||
serialization or deserialization of arguments or the return type."
|
serialization or deserialization of arguments or the return type.
|
||||||
|
|
||||||
|
If the `ret` type is non-primitive, then the returned function will take a
|
||||||
|
first argument of a [[SegmentAllocator]]."
|
||||||
[symbol-or-addr args ret]
|
[symbol-or-addr args ret]
|
||||||
(-> symbol-or-addr
|
(-> symbol-or-addr
|
||||||
ensure-address
|
ensure-address
|
||||||
|
|
@ -782,10 +810,18 @@
|
||||||
"Constructs a wrapper function for the `downcall` which serializes the arguments
|
"Constructs a wrapper function for the `downcall` which serializes the arguments
|
||||||
and deserializes the return value."
|
and deserializes the return value."
|
||||||
[downcall arg-types ret-type]
|
[downcall arg-types ret-type]
|
||||||
(fn native-fn [& args]
|
(if (primitive-type ret-type)
|
||||||
(with-open [scope (stack-scope)]
|
(fn native-fn [& args]
|
||||||
(deserialize (apply downcall (map #(serialize %1 %2 scope) args arg-types))
|
(with-open [scope (stack-scope)]
|
||||||
ret-type))))
|
(deserialize
|
||||||
|
(apply downcall (map #(serialize %1 %2 scope) args arg-types))
|
||||||
|
ret-type)))
|
||||||
|
(fn native-fn [& args]
|
||||||
|
(with-open [scope (stack-scope)]
|
||||||
|
(deserialize
|
||||||
|
(apply downcall (scope-allocator scope)
|
||||||
|
(map #(serialize %1 %2 scope) args arg-types))
|
||||||
|
ret-type)))))
|
||||||
|
|
||||||
(defn make-serde-varargs-wrapper
|
(defn make-serde-varargs-wrapper
|
||||||
"Constructs a wrapper function for the `varargs-factory` which produces
|
"Constructs a wrapper function for the `varargs-factory` which produces
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue