Merge branch 'release/v0.6.409'
This commit is contained in:
commit
272df03fba
14 changed files with 637 additions and 373 deletions
18
CHANGELOG.md
18
CHANGELOG.md
|
|
@ -1,6 +1,23 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
|
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
|
||||||
|
|
||||||
|
## [0.6.409] - 2023-03-31
|
||||||
|
### Added
|
||||||
|
- Support for JDK 19
|
||||||
|
- New macros for defining vars with values from native code
|
||||||
|
- New function to allow getting the backing memory segment of a `coffi.ffi.StaticVariable`, to replace the `Addressable` implementation lost in the migration to JDK 18
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Bug where `static-variable`s with primitive types would not deserialize properly on `deref`
|
||||||
|
- Uses of `defvar` not compiling
|
||||||
|
- Bug where nil values would not be correctly coerced to null pointers when passed to inlined functions
|
||||||
|
- Bug where inline serde functions would fail on complex pointer types
|
||||||
|
- Bug where padding in structs may be increased when fields have alignments less than their size
|
||||||
|
- Bug where pointer alignment was incorrectly defined
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- References to `scope` as a term have been changed to `session` to match Panama messaging. Where this conflicts with function names, old versions have been deprecated and new names have been introduced.
|
||||||
|
|
||||||
## [0.5.357] - 2022-07-07
|
## [0.5.357] - 2022-07-07
|
||||||
### Removed
|
### Removed
|
||||||
- `:coffi.mem/long-long` primitive type
|
- `:coffi.mem/long-long` primitive type
|
||||||
|
|
@ -100,6 +117,7 @@ All notable changes to this project will be documented in this file. This change
|
||||||
- Support for serializing and deserializing arbitrary Clojure functions
|
- Support for serializing and deserializing arbitrary Clojure functions
|
||||||
- Support for serializing and deserializing arbitrary Clojure data structures
|
- Support for serializing and deserializing arbitrary Clojure data structures
|
||||||
|
|
||||||
|
[0.6.409]: https://github.com/IGJoshua/coffi/compare/v0.5.357...v0.6.409
|
||||||
[0.5.357]: https://github.com/IGJoshua/coffi/compare/v0.4.341...v0.5.357
|
[0.5.357]: https://github.com/IGJoshua/coffi/compare/v0.4.341...v0.5.357
|
||||||
[0.4.341]: https://github.com/IGJoshua/coffi/compare/v0.3.298...v0.4.341
|
[0.4.341]: https://github.com/IGJoshua/coffi/compare/v0.3.298...v0.4.341
|
||||||
[0.3.298]: https://github.com/IGJoshua/coffi/compare/v0.2.277...v0.3.298
|
[0.3.298]: https://github.com/IGJoshua/coffi/compare/v0.2.277...v0.3.298
|
||||||
|
|
|
||||||
152
README.md
152
README.md
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
Coffi is a foreign function interface library for Clojure, using the new
|
Coffi is a foreign function interface library for Clojure, using the new
|
||||||
[Project Panama](https://openjdk.java.net/projects/panama/) that's a part of the
|
[Project Panama](https://openjdk.java.net/projects/panama/) that's a part of the
|
||||||
incubator in Java 18. This allows calling native code directly from Clojure
|
preview in Java 19. This allows calling native code directly from Clojure
|
||||||
without the need for either Java or native code specific to the library, as e.g.
|
without the need for either Java or native code specific to the library, as e.g.
|
||||||
the JNI does. Coffi focuses on ease of use, including functions and macros for
|
the JNI does. Coffi focuses on ease of use, including functions and macros for
|
||||||
creating wrappers to allow the resulting native functions to act just like
|
creating wrappers to allow the resulting native functions to act just like
|
||||||
|
|
@ -16,8 +16,8 @@ This library is available on Clojars. Add one of the following entries to the
|
||||||
`:deps` key of your `deps.edn`:
|
`:deps` key of your `deps.edn`:
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
org.suskalo/coffi {:mvn/version "0.5.357"}
|
org.suskalo/coffi {:mvn/version "0.6.409"}
|
||||||
io.github.IGJoshua/coffi {:git/tag "v0.5.357" :git/sha "a9e3ed0"}
|
io.github.IGJoshua/coffi {:git/tag "v0.6.409" :git/sha "f974446"}
|
||||||
```
|
```
|
||||||
|
|
||||||
If you use this library as a git dependency, you will need to prepare the
|
If you use this library as a git dependency, you will need to prepare the
|
||||||
|
|
@ -27,19 +27,19 @@ library.
|
||||||
$ clj -X:deps prep
|
$ clj -X:deps prep
|
||||||
```
|
```
|
||||||
|
|
||||||
Coffi requires usage of the module `jdk.incubator.foreign`, which means that the
|
Coffi requires usage of the package `java.lang.foreign`, and everything in this
|
||||||
JVM must enable the usage of this module. In order to use coffi, add the
|
package is considered to be a preview release, which are disabled by default. In
|
||||||
following JVM arguments to your application.
|
order to use coffi, add the following JVM arguments to your application.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
--add-modules=jdk.incubator.foreign --enable-native-access=ALL-UNNAMED
|
--enable-preview --enable-native-access=ALL-UNNAMED
|
||||||
```
|
```
|
||||||
|
|
||||||
You can specify JVM arguments in a particular invocation of the Clojure CLI with
|
You can specify JVM arguments in a particular invocation of the Clojure CLI with
|
||||||
the -J flag like so:
|
the -J flag like so:
|
||||||
|
|
||||||
``` sh
|
``` sh
|
||||||
clj -J--add-modules=jdk.incubator.foreign -J--enable-native-access=ALL-UNNAMED
|
clj -J--enable-preview -J--enable-native-access=ALL-UNNAMED
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also specify them in an alias in your `deps.edn` file under the
|
You can also specify them in an alias in your `deps.edn` file under the
|
||||||
|
|
@ -47,7 +47,7 @@ You can also specify them in an alias in your `deps.edn` file under the
|
||||||
using `-M`, `-A`, or `-X`.
|
using `-M`, `-A`, or `-X`.
|
||||||
|
|
||||||
``` clojure
|
``` clojure
|
||||||
{:aliases {:dev {:jvm-opts ["--add-modules=jdk.incubator.foreign" "--enable-native-access=ALL-UNNAMED"]}}}
|
{:aliases {:dev {:jvm-opts ["--enable-preview" "--enable-native-access=ALL-UNNAMED"]}}}
|
||||||
```
|
```
|
||||||
|
|
||||||
Other build tools should provide similar functionality if you check their
|
Other build tools should provide similar functionality if you check their
|
||||||
|
|
@ -132,14 +132,13 @@ Coffi defines a basic set of primitive types:
|
||||||
|
|
||||||
Each of these types maps to their C counterpart. Values of any of these
|
Each of these types maps to their C counterpart. Values of any of these
|
||||||
primitive types except for `pointer` will be cast with their corresponding
|
primitive types except for `pointer` will be cast with their corresponding
|
||||||
Clojure function (with `long-long` mapping to the `long` function) when they are
|
Clojure function when they are passed as arguments to native functions.
|
||||||
passed as arguments to native functions. Additionally, the `c-string` type is
|
Additionally, the `c-string` type is defined, although it is not primitive.
|
||||||
defined, although it is not primitive.
|
|
||||||
|
|
||||||
### Composite Types
|
### Composite Types
|
||||||
In addition, some composite types are also defined in coffi, including struct
|
In addition, some composite types are also defined in coffi, including struct
|
||||||
and union types (unions will be discussed with serialization and
|
and union types (unions will be discussed with serialization and
|
||||||
deserialization). For an example c struct and function:
|
deserialization). For an example C struct and function:
|
||||||
|
|
||||||
```c
|
```c
|
||||||
typedef struct point {
|
typedef struct point {
|
||||||
|
|
@ -239,6 +238,23 @@ type and message in the registers section, but it's important to be aware of all
|
||||||
the same. Ideally you should test your callbacks before actually passing them to
|
the same. Ideally you should test your callbacks before actually passing them to
|
||||||
native code.
|
native code.
|
||||||
|
|
||||||
|
When writing a wrapper library for a C library, it may be a good choice to wrap
|
||||||
|
all passed Clojure functions in an additional function which catches all
|
||||||
|
throwables, potentially notifies the user in some manner (e.g. logging), and
|
||||||
|
returns a default value. This is on the wrapper library's developer to decide
|
||||||
|
when and where this is appropriate, as in some cases no reasonable default
|
||||||
|
return value can be determined and it is most sensible to simply crash the JVM.
|
||||||
|
This is the reason that coffi defaults to this behavior, as in the author's
|
||||||
|
opinion it is better to fail hard and fast rather than to attempt to produce a
|
||||||
|
default and cause unexpected behavior later.
|
||||||
|
|
||||||
|
Another important thing to keep in mind is the expected lifetime of the function
|
||||||
|
that you pass to native code. For example it is perfectly fine to pass an
|
||||||
|
anonymous function to a native function if the callback will never be called
|
||||||
|
again once the native function returns. If however it saves the callback for
|
||||||
|
later use the JVM may collect it prematurely, causing a crash when the callback
|
||||||
|
is later called by native code.
|
||||||
|
|
||||||
### Variadic Functions
|
### Variadic Functions
|
||||||
Some native functions can take any number of arguments, and in these cases coffi
|
Some native functions can take any number of arguments, and in these cases coffi
|
||||||
provides `vacfn-factory` (for "varargs C function factory").
|
provides `vacfn-factory` (for "varargs C function factory").
|
||||||
|
|
@ -265,18 +281,21 @@ does not support va-list, however it is a planned feature.
|
||||||
|
|
||||||
### Global Variables
|
### Global Variables
|
||||||
Some libraries include global variables or constants accessible through symbols.
|
Some libraries include global variables or constants accessible through symbols.
|
||||||
To start with, constant values stored in symbols can be fetched with `const`
|
To start with, constant values stored in symbols can be fetched with `const`, or
|
||||||
|
the parallel macro `defconst`
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(def some-const (ffi/const "some_const" ::mem/int))
|
(def some-const (ffi/const "some_const" ::mem/int))
|
||||||
|
(ffi/defconst some-const "some_const" ::mem/int)
|
||||||
```
|
```
|
||||||
|
|
||||||
This value is fetched once when you call `const` and is turned into a Clojure
|
This value is fetched once when you call `const` and is turned into a Clojure
|
||||||
value. If you need to refer to a global variable, then `static-variable` can be
|
value. If you need to refer to a global variable, then `static-variable` (or
|
||||||
used to create a reference to the native value.
|
parallel `defvar`) can be used to create a reference to the native value.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(def some-var (ffi/static-variable "some_var" ::mem/int))
|
(def some-var (ffi/static-variable "some_var" ::mem/int))
|
||||||
|
(ffi/defvar some-var "some_var" ::mem/int)
|
||||||
```
|
```
|
||||||
|
|
||||||
This variable is an `IDeref`. Each time you dereference it, the value will be
|
This variable is an `IDeref`. Each time you dereference it, the value will be
|
||||||
|
|
@ -297,6 +316,10 @@ value is being mutated on another thread.
|
||||||
A parallel function `fswap!` is also provided, but it does not provide any
|
A parallel function `fswap!` is also provided, but it does not provide any
|
||||||
atomic semantics either.
|
atomic semantics either.
|
||||||
|
|
||||||
|
The memory that backs the static variable can be fetched with the function
|
||||||
|
`static-variable-segment`, which can be used to pass a pointer to the static
|
||||||
|
variable to native functions that require it.
|
||||||
|
|
||||||
### Complex Wrappers
|
### Complex Wrappers
|
||||||
Some functions require more complex code to map nicely to a Clojure function.
|
Some functions require more complex code to map nicely to a Clojure function.
|
||||||
The `defcfn` macro provides facilities to wrap the native function with some
|
The `defcfn` macro provides facilities to wrap the native function with some
|
||||||
|
|
@ -331,29 +354,33 @@ This can be used to implement out variables often seen in native code.
|
||||||
(deserialize int-ptr [::mem/pointer ::mem/int])))
|
(deserialize int-ptr [::mem/pointer ::mem/int])))
|
||||||
```
|
```
|
||||||
|
|
||||||
### Scopes
|
### Sessions
|
||||||
|
**Before JDK 19 Sessions were called Scopes. Coffi retains functions that are
|
||||||
|
named for creating scopes for backwards compatibility, but they will be removed
|
||||||
|
in version 1.0.**
|
||||||
|
|
||||||
In order to serialize any non-primitive type (such as the previous
|
In order to serialize any non-primitive type (such as the previous
|
||||||
`[::mem/pointer ::mem/int]`), off-heap memory needs to be allocated. When memory
|
`[::mem/pointer ::mem/int]`), off-heap memory needs to be allocated. When memory
|
||||||
is allocated inside the JVM, the memory is associated with a scope. Because none
|
is allocated inside the JVM, the memory is associated with a session. Because
|
||||||
was provided here, the scope is an implicit scope, and the memory will be freed
|
none was provided here, the session is an implicit session, and the memory will
|
||||||
when the serialized object is garbage collected.
|
be freed when the serialized object is garbage collected.
|
||||||
|
|
||||||
In many cases this is not desirable, because the memory is not freed in a
|
In many cases this is not desirable, because the memory is not freed in a
|
||||||
deterministic manner, causing garbage collection pauses to become longer, as
|
deterministic manner, causing garbage collection pauses to become longer, as
|
||||||
well as changing allocation performance. Instead of an implicit scope, there are
|
well as changing allocation performance. Instead of an implicit session, there
|
||||||
other kinds of scopes as well. A `stack-scope` is a thread-local scope. Stack
|
are other kinds of sessions as well. A `stack-session` is a thread-local
|
||||||
scopes are `Closeable`, which means they should usually be used in a `with-open`
|
session. Stack sessions are `Closeable`, which means they should usually be used
|
||||||
form. When a `stack-scope` is closed, it immediately frees all the memory
|
in a `with-open` form. When a `stack-session` is closed, it immediately frees
|
||||||
associated with it. The previous example, `out-int`, can be implemented with a
|
all the memory associated with it. The previous example, `out-int`, can be
|
||||||
stack scope.
|
implemented with a stack session.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(defcfn out-int
|
(defcfn out-int
|
||||||
"out_int" [::mem/pointer] ::mem/void
|
"out_int" [::mem/pointer] ::mem/void
|
||||||
native-fn
|
native-fn
|
||||||
[i]
|
[i]
|
||||||
(with-open [scope (mem/stack-scope)]
|
(with-open [session (mem/stack-session)]
|
||||||
(let [int-ptr (mem/serialize i [::mem/pointer ::mem/int] scope)]
|
(let [int-ptr (mem/serialize i [::mem/pointer ::mem/int] session)]
|
||||||
(native-fn int-ptr)
|
(native-fn int-ptr)
|
||||||
(mem/deserialize int-ptr [::mem/pointer ::mem/int]))))
|
(mem/deserialize int-ptr [::mem/pointer ::mem/int]))))
|
||||||
```
|
```
|
||||||
|
|
@ -361,14 +388,15 @@ stack scope.
|
||||||
This will free the pointer immediately upon leaving the function.
|
This will free the pointer immediately upon leaving the function.
|
||||||
|
|
||||||
When memory needs to be accessible from multiple threads, there's
|
When memory needs to be accessible from multiple threads, there's
|
||||||
`shared-scope`. When using a `shared-scope`, it should be accessed inside a
|
`shared-session`. When using a `shared-session`, it should be accessed inside a
|
||||||
`with-acquired` block. When a `shared-scope` is `.close`d, it will release all
|
`with-acquired` block. When a `shared-session` is `.close`d, it will release all
|
||||||
its associated memory when every `with-acquired` block associated with it is
|
its associated memory when every `with-acquired` block associated with it is
|
||||||
exited.
|
exited.
|
||||||
|
|
||||||
In addition, two non-`Closeable` scopes are `global-scope`, which never frees
|
In addition, two non-`Closeable` sessions are `global-session`, which never
|
||||||
the resources associated with it, and `connected-scope`, which is a scope that
|
frees the resources associated with it, and `connected-session`, which is a
|
||||||
frees its resources on garbage collection, like an implicit scope.
|
session that frees its resources on garbage collection, like an implicit
|
||||||
|
session.
|
||||||
|
|
||||||
### Serialization and Deserialization
|
### Serialization and Deserialization
|
||||||
Custom serializers and deserializers may also be created. This is done using two
|
Custom serializers and deserializers may also be created. This is done using two
|
||||||
|
|
@ -398,8 +426,8 @@ serialize to primitives.
|
||||||
|
|
||||||
```clojure
|
```clojure
|
||||||
(defmethod mem/serialize* ::vector
|
(defmethod mem/serialize* ::vector
|
||||||
[obj _type scope]
|
[obj _type session]
|
||||||
(mem/address-of (mem/serialize obj [::mem/array ::mem/float 3] scope)))
|
(mem/address-of (mem/serialize obj [::mem/array ::mem/float 3] session)))
|
||||||
|
|
||||||
(defmethod mem/deserialize* ::vector
|
(defmethod mem/deserialize* ::vector
|
||||||
[addr _type]
|
[addr _type]
|
||||||
|
|
@ -408,9 +436,9 @@ serialize to primitives.
|
||||||
```
|
```
|
||||||
|
|
||||||
The `slice-global` function allows you to take an address without an associated
|
The `slice-global` function allows you to take an address without an associated
|
||||||
scope and get a memory segment which can be deserialized.
|
session and get a memory segment which can be deserialized.
|
||||||
|
|
||||||
In cases like this where we don't know the scope of the pointer, we could use
|
In cases like this where we don't know the session of the pointer, we could use
|
||||||
`add-close-action!` to ensure it's freed. For example if a `free-vector!`
|
`add-close-action!` to ensure it's freed. For example if a `free-vector!`
|
||||||
function that takes a pointer exists, we could use this:
|
function that takes a pointer exists, we could use this:
|
||||||
|
|
||||||
|
|
@ -418,14 +446,14 @@ function that takes a pointer exists, we could use this:
|
||||||
(defcfn returns-vector
|
(defcfn returns-vector
|
||||||
"returns_vector" [] ::mem/pointer
|
"returns_vector" [] ::mem/pointer
|
||||||
native-fn
|
native-fn
|
||||||
[scope]
|
[session]
|
||||||
(let [ret-ptr (native-fn)]
|
(let [ret-ptr (native-fn)]
|
||||||
(add-close-action! scope #(free-vector! ret-ptr))
|
(add-close-action! session #(free-vector! ret-ptr))
|
||||||
(deserialize ret-ptr ::vector)))
|
(deserialize ret-ptr ::vector)))
|
||||||
```
|
```
|
||||||
|
|
||||||
This function takes a scope and returns the deserialized vector, and it will
|
This function takes a session and returns the deserialized vector, and it will
|
||||||
free the pointer when the scope closes.
|
free the pointer when the session closes.
|
||||||
|
|
||||||
#### Tagged Union
|
#### Tagged Union
|
||||||
For the tagged union type, we will represent the value as a vector of a keyword
|
For the tagged union type, we will represent the value as a vector of a keyword
|
||||||
|
|
@ -477,7 +505,7 @@ deserialize the value into and out of memory segments. This is accomplished with
|
||||||
(map first))))
|
(map first))))
|
||||||
|
|
||||||
(defmethod mem/serialize-into ::tagged-union
|
(defmethod mem/serialize-into ::tagged-union
|
||||||
[obj [_tagged-union tags type-map] segment scope]
|
[obj [_tagged-union tags type-map] segment session]
|
||||||
(mem/serialize-into
|
(mem/serialize-into
|
||||||
{:tag (item-index tags (first obj))
|
{:tag (item-index tags (first obj))
|
||||||
:value (second obj)}
|
:value (second obj)}
|
||||||
|
|
@ -485,7 +513,7 @@ deserialize the value into and out of memory segments. This is accomplished with
|
||||||
[[:tag ::mem/long]
|
[[:tag ::mem/long]
|
||||||
[:value (get type-map (first obj))]]]
|
[:value (get type-map (first obj))]]]
|
||||||
segment
|
segment
|
||||||
scope))
|
session))
|
||||||
```
|
```
|
||||||
|
|
||||||
This serialization method is rather simple, it just turns the vector value into
|
This serialization method is rather simple, it just turns the vector value into
|
||||||
|
|
@ -542,7 +570,7 @@ it could be represented for serialization purposes like so:
|
||||||
This union however would not include the tag when serialized.
|
This union however would not include the tag when serialized.
|
||||||
|
|
||||||
If a union is deserialized, then all that coffi does is to allocate a new
|
If a union is deserialized, then all that coffi does is to allocate a new
|
||||||
segment of the appropriate size with an implicit scope so that it may later be
|
segment of the appropriate size with an implicit session so that it may later be
|
||||||
garbage collected, and copies the data from the source segment into it. It's up
|
garbage collected, and copies the data from the source segment into it. It's up
|
||||||
to the user to call `deserialize-from` on that segment with the appropriate
|
to the user to call `deserialize-from` on that segment with the appropriate
|
||||||
type.
|
type.
|
||||||
|
|
@ -567,12 +595,12 @@ With raw handles, the argument types are expected to exactly match the types
|
||||||
expected by the native function. For primitive types, those are primitives. For
|
expected by the native function. For primitive types, those are primitives. For
|
||||||
addresses, that is `MemoryAddress`, and for composite types like structs and
|
addresses, that is `MemoryAddress`, and for composite types like structs and
|
||||||
unions, that is `MemorySegment`. Both `MemoryAddress` and `MemorySegment` come
|
unions, that is `MemorySegment`. Both `MemoryAddress` and `MemorySegment` come
|
||||||
from the `jdk.incubator.foreign` package.
|
from the `java.lang.foreign` package.
|
||||||
|
|
||||||
In addition, when a raw handle returns a composite type represented with a
|
In addition, when a raw handle returns a composite type represented with a
|
||||||
`MemorySegment`, it requires an additional first argument, a `SegmentAllocator`,
|
`MemorySegment`, it requires an additional first argument, a `SegmentAllocator`,
|
||||||
which can be acquired with `scope-allocator` to get one associated with a
|
which can be acquired with `session-allocator` to get one associated with a
|
||||||
specific scope. The returned value will live until that scope is released.
|
specific session. The returned value will live until that session is released.
|
||||||
|
|
||||||
In addition, function types can be specified as being raw, in the following
|
In addition, function types can be specified as being raw, in the following
|
||||||
manner:
|
manner:
|
||||||
|
|
@ -612,8 +640,8 @@ floats, the following code might be used.
|
||||||
|
|
||||||
(defn returns-float-array
|
(defn returns-float-array
|
||||||
[]
|
[]
|
||||||
(with-open [scope (mem/stack-scope)]
|
(with-open [session (mem/stack-session)]
|
||||||
(let [out-floats (mem/alloc mem/pointer-size scope)
|
(let [out-floats (mem/alloc mem/pointer-size session)
|
||||||
num-floats (function-handle (mem/address-of out-floats))
|
num-floats (function-handle (mem/address-of out-floats))
|
||||||
floats-addr (mem/read-address out-floats)
|
floats-addr (mem/read-address out-floats)
|
||||||
floats-slice (mem/slice-global floats-addr (unchecked-multiply-int mem/float-size num-floats))]
|
floats-slice (mem/slice-global floats-addr (unchecked-multiply-int mem/float-size num-floats))]
|
||||||
|
|
@ -712,6 +740,8 @@ appealing, as they have a smaller API surface area and it's easier to wrap
|
||||||
functions.
|
functions.
|
||||||
|
|
||||||
### Benchmarks
|
### Benchmarks
|
||||||
|
**BENCHMARKS FOR COFFI AND DTYPE-NEXT ARE BASED ON AN OLD VERSION. NEW BENCHMARKS WILL BE CREATED WHEN PANAMA COMES OUT OF PREVIEW**
|
||||||
|
|
||||||
An additional consideration when thinking about alternatives is the performance
|
An additional consideration when thinking about alternatives is the performance
|
||||||
of each available option. It's an established fact that JNA (used by all three
|
of each available option. It's an established fact that JNA (used by all three
|
||||||
alternative libraries on JDK <16) introduces more overhead when calling native
|
alternative libraries on JDK <16) introduces more overhead when calling native
|
||||||
|
|
@ -954,8 +984,6 @@ coming from, but I'll admit that I haven't looked at their implementations very
|
||||||
closely.
|
closely.
|
||||||
|
|
||||||
#### dtype-next
|
#### dtype-next
|
||||||
**BENCHMARKS FOR DTYPE-NEXT ARE BASED ON AN OLD VERSION. NEW BENCHMARKS WILL BE COMING SHORTLY**
|
|
||||||
|
|
||||||
The library dtype-next replaced tech.jna in the toolkit of the group working on
|
The library dtype-next replaced tech.jna in the toolkit of the group working on
|
||||||
machine learning and array-based programming, and it includes support for
|
machine learning and array-based programming, and it includes support for
|
||||||
composite data types including structs, as well as primitive functions and
|
composite data types including structs, as well as primitive functions and
|
||||||
|
|
@ -1089,7 +1117,10 @@ stands, coffi is the fastest FFI available to Clojure developers.
|
||||||
The project author is aware of these issues and plans to fix them in a future
|
The project author is aware of these issues and plans to fix them in a future
|
||||||
release:
|
release:
|
||||||
|
|
||||||
There are currently no known issues! Hooray!
|
- On M1 Macs occasional crashes are present when returning structs by value from
|
||||||
|
native code. At the moment this appears to be an upstream issue with Panama,
|
||||||
|
and will be reported once a minimal reproduction case with only Panama is
|
||||||
|
produced.
|
||||||
|
|
||||||
## Future Plans
|
## Future Plans
|
||||||
These features are planned for future releases.
|
These features are planned for future releases.
|
||||||
|
|
@ -1097,27 +1128,26 @@ These features are planned for future releases.
|
||||||
- Support for va_args type
|
- Support for va_args type
|
||||||
- Header parsing tool for generating a data model?
|
- Header parsing tool for generating a data model?
|
||||||
- Generic type aliases
|
- Generic type aliases
|
||||||
- Helpers for generating enums & bitflags
|
|
||||||
- Unsigned integer types
|
- Unsigned integer types
|
||||||
- Record-based struct types
|
- Record-based struct types
|
||||||
- Helper macro for out arguments
|
- Helper macro for out arguments
|
||||||
- Improve error messages from defcfn macro
|
- Improve error messages from defcfn macro
|
||||||
- Mapped memory
|
- Mapped memory
|
||||||
|
- Helper macros for custom serde implementations for composite data types
|
||||||
|
|
||||||
### Future JDKs
|
### Future JDKs
|
||||||
The purpose of coffi is to provide a wrapper for published versions of Project
|
The purpose of coffi is to provide a wrapper for published versions of Project
|
||||||
Panama, starting with JDK 17. As new JDKs are released, coffi will be ported to
|
Panama, starting with JDK 17. As new JDKs are released, coffi will be ported to
|
||||||
the newer versions of Panama. Version `0.4.341` is the last version compatible
|
the newer versions of Panama. Version `0.4.341` is the last version compatible
|
||||||
with JDK 17. Bugfixes, and potential backports of newer coffi features may be
|
with JDK 17. Version `0.5.357` is the last version compatible with JDK 18.
|
||||||
found on the `jdk17-lts` branch. Development of new features and fixes as well
|
Bugfixes, and potential backports of newer coffi features may be found on the
|
||||||
as support for new Panama idioms and features will continue with focus only on
|
`jdk17-lts` branch. Development of new features and fixes as well as support for
|
||||||
the latest JDK. If a particular feature is not specific to the newer JDK, PRs
|
new Panama idioms and features will continue with focus only on the latest JDK.
|
||||||
backporting it to versions of coffi supporting Java 17 will likely be accepted.
|
If a particular feature is not specific to the newer JDK, PRs backporting it to
|
||||||
|
versions of coffi supporting Java 17 will likely be accepted.
|
||||||
|
|
||||||
### 1.0 Release
|
### 1.0 Release
|
||||||
Because the feature that coffi wraps in the JDK is an incubator feature (and
|
Because the feature that coffi wraps in the JDK is in preview as of JDK 19,
|
||||||
likely in JDK 19 a [preview
|
|
||||||
feature](https://mail.openjdk.java.net/pipermail/panama-dev/2021-September/014946.html))
|
|
||||||
coffi itself will not be released in a 1.0.x version until the feature becomes a
|
coffi itself will not be released in a 1.0.x version until the feature becomes a
|
||||||
core part of the JDK, likely before or during the next LTS release, Java 21, in
|
core part of the JDK, likely before or during the next LTS release, Java 21, in
|
||||||
September 2023.
|
September 2023.
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@
|
||||||
[clojure.tools.build.api :as b]))
|
[clojure.tools.build.api :as b]))
|
||||||
|
|
||||||
(def lib-coord 'org.suskalo/coffi)
|
(def lib-coord 'org.suskalo/coffi)
|
||||||
(def version (format "0.5.%s" (b/git-count-revs nil)))
|
(def version (format "0.6.%s" (b/git-count-revs nil)))
|
||||||
|
|
||||||
(def resource-dirs ["resources/"])
|
(def resource-dirs ["resources/"])
|
||||||
|
|
||||||
|
|
@ -49,11 +49,11 @@
|
||||||
"Compiles java classes required for interop."
|
"Compiles java classes required for interop."
|
||||||
[opts]
|
[opts]
|
||||||
(.mkdirs (io/file class-dir))
|
(.mkdirs (io/file class-dir))
|
||||||
(b/process {:command-args ["javac" "--add-modules=jdk.incubator.foreign"
|
(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" "18"
|
"-target" "19"
|
||||||
"-source" "18"]})
|
"-source" "19"]})
|
||||||
opts)
|
opts)
|
||||||
|
|
||||||
(defn- write-pom
|
(defn- write-pom
|
||||||
|
|
|
||||||
14
deps.edn
14
deps.edn
|
|
@ -1,5 +1,5 @@
|
||||||
{:paths ["src/clj" "target/classes" "resources"]
|
{:paths ["src/clj" "target/classes" "resources"]
|
||||||
:deps {org.clojure/clojure {:mvn/version "1.10.3"}
|
:deps {org.clojure/clojure {:mvn/version "1.11.1"}
|
||||||
insn/insn {:mvn/version "0.2.1"}}
|
insn/insn {:mvn/version "0.2.1"}}
|
||||||
|
|
||||||
:deps/prep-lib {:alias :build
|
:deps/prep-lib {:alias :build
|
||||||
|
|
@ -12,26 +12,26 @@
|
||||||
nodisassemble/nodisassemble {:mvn/version "0.1.3"}}
|
nodisassemble/nodisassemble {:mvn/version "0.1.3"}}
|
||||||
;; NOTE(Joshua): If you want to use nodisassemble you should also add a
|
;; NOTE(Joshua): If you want to use nodisassemble you should also add a
|
||||||
;; -javaagent for the resolved location
|
;; -javaagent for the resolved location
|
||||||
:jvm-opts ["--add-modules=jdk.incubator.foreign" "--enable-native-access=ALL-UNNAMED"]}
|
:jvm-opts ["--enable-native-access=ALL-UNNAMED" "--enable-preview"]}
|
||||||
:test {:extra-paths ["test/clj"]
|
:test {:extra-paths ["test/clj"]
|
||||||
:extra-deps {org.clojure/test.check {:mvn/version "1.1.0"}
|
:extra-deps {org.clojure/test.check {:mvn/version "1.1.0"}
|
||||||
io.github.cognitect-labs/test-runner
|
io.github.cognitect-labs/test-runner
|
||||||
{:git/url "https://github.com/cognitect-labs/test-runner"
|
{:git/url "https://github.com/cognitect-labs/test-runner"
|
||||||
:sha "62ef1de18e076903374306060ac0e8a752e57c86"}}
|
:sha "62ef1de18e076903374306060ac0e8a752e57c86"}}
|
||||||
:jvm-opts ["--add-modules=jdk.incubator.foreign" "--enable-native-access=ALL-UNNAMED"]
|
:jvm-opts ["--enable-native-access=ALL-UNNAMED" "--enable-preview"]
|
||||||
:exec-fn cognitect.test-runner.api/test}
|
:exec-fn cognitect.test-runner.api/test}
|
||||||
|
|
||||||
:codox {:extra-deps {codox/codox {:mvn/version "0.10.7"}}
|
:codox {:extra-deps {codox/codox {:mvn/version "0.10.7"}}
|
||||||
:exec-fn codox.main/generate-docs
|
:exec-fn codox.main/generate-docs
|
||||||
:exec-args {:name "coffi"
|
:exec-args {:name "coffi"
|
||||||
:version "v0.5.357"
|
:version "v0.6.409"
|
||||||
:description "A Foreign Function Interface in Clojure for JDK 18."
|
:description "A Foreign Function Interface in Clojure for JDK 19."
|
||||||
:source-paths ["src/clj"]
|
:source-paths ["src/clj"]
|
||||||
:output-path "docs"
|
:output-path "docs"
|
||||||
:source-uri "https://github.com/IGJoshua/coffi/blob/{git-commit}/{filepath}#L{line}"
|
:source-uri "https://github.com/IGJoshua/coffi/blob/{git-commit}/{filepath}#L{line}"
|
||||||
:metadata {:doc/format :markdown}}
|
:metadata {:doc/format :markdown}}
|
||||||
:jvm-opts ["--add-modules=jdk.incubator.foreign"
|
:jvm-opts ["--add-opens" "java.base/java.lang=ALL-UNNAMED"
|
||||||
"--add-opens" "java.base/java.lang=ALL-UNNAMED"]}
|
"--enable-preview"]}
|
||||||
|
|
||||||
:build {:replace-deps {org.clojure/clojure {:mvn/version "1.10.3"}
|
:build {:replace-deps {org.clojure/clojure {:mvn/version "1.10.3"}
|
||||||
io.github.clojure/tools.build {:git/tag "v0.3.0" :git/sha "e418fc9"}}
|
io.github.clojure/tools.build {:git/tag "v0.3.0" :git/sha "e418fc9"}}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,20 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
<!DOCTYPE html PUBLIC ""
|
||||||
"">
|
"">
|
||||||
<html><head><meta charset="UTF-8" /><title>coffi.ffi documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">coffi</span> <span class="project-version">v0.5.357</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>coffi</span></div></div></li><li class="depth-2 branch current"><a href="coffi.ffi.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ffi</span></div></a></li><li class="depth-2 branch"><a href="coffi.layout.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>layout</span></div></a></li><li class="depth-2"><a href="coffi.mem.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mem</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="coffi.ffi.html#var-cfn"><div class="inner"><span>cfn</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-const"><div class="inner"><span>const</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-defcfn"><div class="inner"><span>defcfn</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-find-symbol"><div class="inner"><span>find-symbol</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-freset.21"><div class="inner"><span>freset!</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-fswap.21"><div class="inner"><span>fswap!</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-load-library"><div class="inner"><span>load-library</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-load-system-library"><div class="inner"><span>load-system-library</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-downcall"><div class="inner"><span>make-downcall</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-serde-varargs-wrapper"><div class="inner"><span>make-serde-varargs-wrapper</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-serde-wrapper"><div class="inner"><span>make-serde-wrapper</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-varargs-factory"><div class="inner"><span>make-varargs-factory</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-reify-libspec"><div class="inner"><span>reify-libspec</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-reify-symbolspec"><div class="inner"><span>reify-symbolspec</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-static-variable"><div class="inner"><span>static-variable</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-vacfn-factory"><div class="inner"><span>vacfn-factory</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">coffi.ffi</h1><div class="doc"><div class="markdown"><p>Functions for creating handles to native functions and loading native libraries.</p></div></div><div class="public anchor" id="var-cfn"><h3>cfn</h3><div class="usage"><code>(cfn symbol args ret)</code></div><div class="doc"><div class="markdown"><p>Constructs a Clojure function to call the native function referenced by <code>symbol</code>.</p>
|
<html><head><meta charset="UTF-8" /><title>coffi.ffi documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">coffi</span> <span class="project-version">v0.6.409</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>coffi</span></div></div></li><li class="depth-2 branch current"><a href="coffi.ffi.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ffi</span></div></a></li><li class="depth-2 branch"><a href="coffi.layout.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>layout</span></div></a></li><li class="depth-2"><a href="coffi.mem.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mem</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="coffi.ffi.html#var-cfn"><div class="inner"><span>cfn</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-const"><div class="inner"><span>const</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-defcfn"><div class="inner"><span>defcfn</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-defconst"><div class="inner"><span>defconst</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-defvar"><div class="inner"><span>defvar</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-ensure-symbol"><div class="inner"><span>ensure-symbol</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-find-symbol"><div class="inner"><span>find-symbol</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-freset.21"><div class="inner"><span>freset!</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-fswap.21"><div class="inner"><span>fswap!</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-load-library"><div class="inner"><span>load-library</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-load-system-library"><div class="inner"><span>load-system-library</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-downcall"><div class="inner"><span>make-downcall</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-serde-varargs-wrapper"><div class="inner"><span>make-serde-varargs-wrapper</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-serde-wrapper"><div class="inner"><span>make-serde-wrapper</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-make-varargs-factory"><div class="inner"><span>make-varargs-factory</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-reify-libspec"><div class="inner"><span>reify-libspec</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-reify-symbolspec"><div class="inner"><span>reify-symbolspec</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-static-variable"><div class="inner"><span>static-variable</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-static-variable-segment"><div class="inner"><span>static-variable-segment</span></div></a></li><li class="depth-1"><a href="coffi.ffi.html#var-vacfn-factory"><div class="inner"><span>vacfn-factory</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">coffi.ffi</h1><div class="doc"><div class="markdown"><p>Functions for creating handles to native functions and loading native libraries.</p></div></div><div class="public anchor" id="var-cfn"><h3>cfn</h3><div class="usage"><code>(cfn symbol args ret)</code></div><div class="doc"><div class="markdown"><p>Constructs a Clojure function to call the native function referenced by <code>symbol</code>.</p>
|
||||||
<p>The function returned will serialize any passed arguments into the <code>args</code> types, and deserialize the return to the <code>ret</code> type.</p>
|
<p>The function returned will serialize any passed arguments into the <code>args</code> types, and deserialize the return to the <code>ret</code> type.</p>
|
||||||
<p>If your <code>args</code> and <code>ret</code> are constants, then it is more efficient to call <a href="coffi.ffi.html#var-make-downcall">make-downcall</a> followed by <a href="coffi.ffi.html#var-make-serde-wrapper">make-serde-wrapper</a> because the latter has an inline definition which will result in less overhead from serdes.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L421">view source</a></div></div><div class="public anchor" id="var-const"><h3>const</h3><div class="usage"><code>(const symbol-or-addr type)</code></div><div class="doc"><div class="markdown"><p>Gets the value of a constant stored in <code>symbol-or-addr</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L565">view source</a></div></div><div class="public anchor" id="var-defcfn"><h3>defcfn</h3><h4 class="type">macro</h4><div class="usage"><code>(defcfn name docstring? attr-map? symbol arg-types ret-type)</code><code>(defcfn name docstring? attr-map? symbol arg-types ret-type native-fn & fn-tail)</code></div><div class="doc"><div class="markdown"><p>Defines a Clojure function which maps to a native function.</p>
|
<p>If your <code>args</code> and <code>ret</code> are constants, then it is more efficient to call <a href="coffi.ffi.html#var-make-downcall">make-downcall</a> followed by <a href="coffi.ffi.html#var-make-serde-wrapper">make-serde-wrapper</a> because the latter has an inline definition which will result in less overhead from serdes.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L429">view source</a></div></div><div class="public anchor" id="var-const"><h3>const</h3><div class="usage"><code>(const symbol-or-addr type)</code></div><div class="doc"><div class="markdown"><p>Gets the value of a constant stored in <code>symbol-or-addr</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L570">view source</a></div></div><div class="public anchor" id="var-defcfn"><h3>defcfn</h3><h4 class="type">macro</h4><div class="usage"><code>(defcfn name docstring? attr-map? symbol arg-types ret-type)</code><code>(defcfn name docstring? attr-map? symbol arg-types ret-type native-fn & fn-tail)</code></div><div class="doc"><div class="markdown"><p>Defines a Clojure function which maps to a native function.</p>
|
||||||
<p><code>name</code> is the symbol naming the resulting var. <code>symbol</code> is a symbol or string naming the library symbol to link against. <code>arg-types</code> is a vector of qualified keywords representing the argument types. <code>ret-type</code> is a single qualified keyword representing the return type. <code>fn-tail</code> is the body of the function (potentially with multiple arities) which wraps the native one. Inside the function, <code>native-fn</code> is bound to a function that will serialize its arguments, call the native function, and deserialize its return type. If any body is present, you must call this function in order to call the native code.</p>
|
<p><code>name</code> is the symbol naming the resulting var. <code>symbol</code> is a symbol or string naming the library symbol to link against. <code>arg-types</code> is a vector of qualified keywords representing the argument types. <code>ret-type</code> is a single qualified keyword representing the return type. <code>fn-tail</code> is the body of the function (potentially with multiple arities) which wraps the native one. Inside the function, <code>native-fn</code> is bound to a function that will serialize its arguments, call the native function, and deserialize its return type. If any body is present, you must call this function in order to call the native code.</p>
|
||||||
<p>If no <code>fn-tail</code> is provided, then the resulting function will simply serialize the arguments according to <code>arg-types</code>, call the native function, and deserialize the return value.</p>
|
<p>If no <code>fn-tail</code> is provided, then the resulting function will simply serialize the arguments according to <code>arg-types</code>, call the native function, and deserialize the return value.</p>
|
||||||
<p>The number of args in the <code>fn-tail</code> need not match the number of <code>arg-types</code> for the native function. It need only call the native wrapper function with the correct arguments.</p>
|
<p>The number of args in the <code>fn-tail</code> need not match the number of <code>arg-types</code> for the native function. It need only call the native wrapper function with the correct arguments.</p>
|
||||||
<p>See <a href="coffi.mem.html#var-serialize">serialize</a>, <a href="coffi.mem.html#var-deserialize">deserialize</a>, <a href="coffi.ffi.html#var-make-downcall">make-downcall</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L701">view source</a></div></div><div class="public anchor" id="var-find-symbol"><h3>find-symbol</h3><div class="usage"><code>(find-symbol sym)</code></div><div class="doc"><div class="markdown"><p>Gets the <a href="null">NativeSymbol</a> of a symbol from the loaded libraries.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L37">view source</a></div></div><div class="public anchor" id="var-freset.21"><h3>freset!</h3><div class="usage"><code>(freset! static-var newval)</code></div><div class="doc"><div class="markdown"><p>Sets the value of <code>static-var</code> to <code>newval</code>, running it through <a href="coffi.mem.html#var-serialize">serialize</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L587">view source</a></div></div><div class="public anchor" id="var-fswap.21"><h3>fswap!</h3><div class="usage"><code>(fswap! static-var f & args)</code></div><div class="doc"><div class="markdown"><p>Non-atomically runs the function <code>f</code> over the value stored in <code>static-var</code>.</p>
|
<p>See <a href="coffi.mem.html#var-serialize">serialize</a>, <a href="coffi.mem.html#var-deserialize">deserialize</a>, <a href="coffi.ffi.html#var-make-downcall">make-downcall</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L745">view source</a></div></div><div class="public anchor" id="var-defconst"><h3>defconst</h3><h4 class="type">macro</h4><div class="usage"><code>(defconst symbol docstring? symbol-or-addr type)</code></div><div class="doc"><div class="markdown"><p>Defines a var named by <code>symbol</code> to be the value of the given <code>type</code> from <code>symbol-or-addr</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L581">view source</a></div></div><div class="public anchor" id="var-defvar"><h3>defvar</h3><h4 class="type">macro</h4><div class="usage"><code>(defvar symbol docstring? symbol-or-addr type)</code></div><div class="doc"><div class="markdown"><p>Defines a var named by <code>symbol</code> to be a reference to the native memory from <code>symbol-or-addr</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L648">view source</a></div></div><div class="public anchor" id="var-ensure-symbol"><h3>ensure-symbol</h3><div class="usage"><code>(ensure-symbol symbol-or-addr)</code></div><div class="doc"><div class="markdown"><p>Returns the argument if it is a <a href="null">MemorySegment</a>, otherwise calls <a href="coffi.ffi.html#var-find-symbol">find-symbol</a> on it.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L189">view source</a></div></div><div class="public anchor" id="var-find-symbol"><h3>find-symbol</h3><div class="usage"><code>(find-symbol sym)</code></div><div class="doc"><div class="markdown"><p>Gets the <a href="null">MemorySegment</a> of a symbol from the loaded libraries.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L39">view source</a></div></div><div class="public anchor" id="var-freset.21"><h3>freset!</h3><div class="usage"><code>(freset! static-var newval)</code></div><div class="doc"><div class="markdown"><p>Sets the value of <code>static-var</code> to <code>newval</code>, running it through <a href="coffi.mem.html#var-serialize">serialize</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L611">view source</a></div></div><div class="public anchor" id="var-fswap.21"><h3>fswap!</h3><div class="usage"><code>(fswap! static-var f & args)</code></div><div class="doc"><div class="markdown"><p>Non-atomically runs the function <code>f</code> over the value stored in <code>static-var</code>.</p>
|
||||||
<p>The value is deserialized before passing it to <code>f</code>, and serialized before putting the value into <code>static-var</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L596">view source</a></div></div><div class="public anchor" id="var-load-library"><h3>load-library</h3><div class="usage"><code>(load-library path)</code></div><div class="doc"><div class="markdown"><p>Loads the library at <code>path</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L32">view source</a></div></div><div class="public anchor" id="var-load-system-library"><h3>load-system-library</h3><div class="usage"><code>(load-system-library libname)</code></div><div class="doc"><div class="markdown"><p>Loads the library named <code>libname</code> from the system’s load path.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L27">view source</a></div></div><div class="public anchor" id="var-make-downcall"><h3>make-downcall</h3><div class="usage"><code>(make-downcall symbol-or-addr args ret)</code></div><div class="doc"><div class="markdown"><p>Constructs a downcall function reference to <code>symbol-or-addr</code> with the given <code>args</code> and <code>ret</code> types.</p>
|
<p>The value is deserialized before passing it to <code>f</code>, and serialized before putting the value into <code>static-var</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L620">view source</a></div></div><div class="public anchor" id="var-load-library"><h3>load-library</h3><div class="usage"><code>(load-library path)</code></div><div class="doc"><div class="markdown"><p>Loads the library at <code>path</code>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L34">view source</a></div></div><div class="public anchor" id="var-load-system-library"><h3>load-system-library</h3><div class="usage"><code>(load-system-library libname)</code></div><div class="doc"><div class="markdown"><p>Loads the library named <code>libname</code> from the system’s load path.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L29">view source</a></div></div><div class="public anchor" id="var-make-downcall"><h3>make-downcall</h3><div class="usage"><code>(make-downcall symbol-or-addr args ret)</code></div><div class="doc"><div class="markdown"><p>Constructs a downcall function reference to <code>symbol-or-addr</code> with the given <code>args</code> and <code>ret</code> types.</p>
|
||||||
<p>The function returned takes only arguments whose types match exactly the <a href="coffi.mem.html#var-java-layout">java-layout</a> for that type, and returns an argument with exactly the <a href="coffi.mem.html#var-java-layout">java-layout</a> of the <code>ret</code> type. This function will perform no serialization or deserialization of arguments or the return type.</p>
|
<p>The function returned takes only arguments whose types match exactly the <a href="coffi.mem.html#var-java-layout">java-layout</a> for that type, and returns an argument with exactly the <a href="coffi.mem.html#var-java-layout">java-layout</a> of the <code>ret</code> type. This function will perform no serialization or deserialization of arguments or the return type.</p>
|
||||||
<p>If the <code>ret</code> type is non-primitive, then the returned function will take a first argument of a <a href="null">SegmentAllocator</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L198">view source</a></div></div><div class="public anchor" id="var-make-serde-varargs-wrapper"><h3>make-serde-varargs-wrapper</h3><div class="usage"><code>(make-serde-varargs-wrapper varargs-factory required-args ret-type)</code></div><div class="doc"><div class="markdown"><p>Constructs a wrapper function for the <code>varargs-factory</code> which produces functions that serialize the arguments and deserialize the return value.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L409">view source</a></div></div><div class="public anchor" id="var-make-serde-wrapper"><h3>make-serde-wrapper</h3><div class="usage"><code>(make-serde-wrapper downcall arg-types ret-type)</code></div><div class="doc"><div class="markdown"><p>Constructs a wrapper function for the <code>downcall</code> which serializes the arguments and deserializes the return value.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L390">view source</a></div></div><div class="public anchor" id="var-make-varargs-factory"><h3>make-varargs-factory</h3><div class="usage"><code>(make-varargs-factory symbol required-args ret)</code></div><div class="doc"><div class="markdown"><p>Returns a function for constructing downcalls with additional types for arguments.</p>
|
<p>If the <code>ret</code> type is non-primitive, then the returned function will take a first argument of a <a href="null">SegmentAllocator</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L197">view source</a></div></div><div class="public anchor" id="var-make-serde-varargs-wrapper"><h3>make-serde-varargs-wrapper</h3><div class="usage"><code>(make-serde-varargs-wrapper varargs-factory required-args ret-type)</code></div><div class="doc"><div class="markdown"><p>Constructs a wrapper function for the <code>varargs-factory</code> which produces functions that serialize the arguments and deserialize the return value.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L417">view source</a></div></div><div class="public anchor" id="var-make-serde-wrapper"><h3>make-serde-wrapper</h3><div class="usage"><code>(make-serde-wrapper downcall arg-types ret-type)</code></div><div class="doc"><div class="markdown"><p>Constructs a wrapper function for the <code>downcall</code> which serializes the arguments and deserializes the return value.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L398">view source</a></div></div><div class="public anchor" id="var-make-varargs-factory"><h3>make-varargs-factory</h3><div class="usage"><code>(make-varargs-factory symbol required-args ret)</code></div><div class="doc"><div class="markdown"><p>Returns a function for constructing downcalls with additional types for arguments.</p>
|
||||||
<p>The <code>required-args</code> are the types of the first arguments passed to the downcall handle, and the values passed to the returned function are only the varargs types.</p>
|
<p>The <code>required-args</code> are the types of the first arguments passed to the downcall handle, and the values passed to the returned function are only the varargs types.</p>
|
||||||
<p>The returned function is memoized, so that only one downcall function will be generated per combination of argument types.</p>
|
<p>The returned function is memoized, so that only one downcall function will be generated per combination of argument types.</p>
|
||||||
<p>See <a href="coffi.ffi.html#var-make-downcall">make-downcall</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L214">view source</a></div></div><div class="public anchor" id="var-reify-libspec"><h3>reify-libspec</h3><div class="usage"><code>(reify-libspec libspec)</code></div><div class="doc"><div class="markdown"><p>Loads all the symbols specified in the <code>libspec</code>.</p>
|
<p>See <a href="coffi.ffi.html#var-make-downcall">make-downcall</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L213">view source</a></div></div><div class="public anchor" id="var-reify-libspec"><h3>reify-libspec</h3><div class="usage"><code>(reify-libspec libspec)</code></div><div class="doc"><div class="markdown"><p>Loads all the symbols specified in the <code>libspec</code>.</p>
|
||||||
<p>The value of each key of the passed map is transformed as by <a href="coffi.ffi.html#var-reify-symbolspec">reify-symbolspec</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L663">view source</a></div></div><div class="public anchor" id="var-reify-symbolspec"><h3>reify-symbolspec</h3><h4 class="type">multimethod</h4><div class="usage"></div><div class="doc"><div class="markdown"><p>Takes a spec for a symbol reference and returns a live value for that type.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L622">view source</a></div></div><div class="public anchor" id="var-static-variable"><h3>static-variable</h3><div class="usage"><code>(static-variable symbol-or-addr type)</code></div><div class="doc"><div class="markdown"><p>Constructs a reference to a mutable value stored in <code>symbol-or-addr</code>.</p>
|
<p>The value of each key of the passed map is transformed as by <a href="coffi.ffi.html#var-reify-symbolspec">reify-symbolspec</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L707">view source</a></div></div><div class="public anchor" id="var-reify-symbolspec"><h3>reify-symbolspec</h3><h4 class="type">multimethod</h4><div class="usage"></div><div class="doc"><div class="markdown"><p>Takes a spec for a symbol reference and returns a live value for that type.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L666">view source</a></div></div><div class="public anchor" id="var-static-variable"><h3>static-variable</h3><div class="usage"><code>(static-variable symbol-or-addr type)</code></div><div class="doc"><div class="markdown"><p>Constructs a reference to a mutable value stored in <code>symbol-or-addr</code>.</p>
|
||||||
<p>The returned value can be dereferenced, and has metadata, and the address of the value can be queried with <a href="coffi.mem.html#var-address-of">address-of</a>.</p>
|
<p>The returned value can be dereferenced, and has metadata.</p>
|
||||||
<p>See <a href="coffi.ffi.html#var-freset.21">freset!</a>, <a href="coffi.ffi.html#var-fswap.21">fswap!</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L604">view source</a></div></div><div class="public anchor" id="var-vacfn-factory"><h3>vacfn-factory</h3><div class="usage"><code>(vacfn-factory symbol required-args ret)</code></div><div class="doc"><div class="markdown"><p>Constructs a varargs factory to call the native function referenced by <code>symbol</code>.</p>
|
<p>See <a href="coffi.ffi.html#var-freset.21">freset!</a>, <a href="coffi.ffi.html#var-fswap.21">fswap!</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L636">view source</a></div></div><div class="public anchor" id="var-static-variable-segment"><h3>static-variable-segment</h3><div class="usage"><code>(static-variable-segment static-var)</code></div><div class="doc"><div class="markdown"><p>Gets the backing <a href="null">MemorySegment</a> from <code>static-var</code>.</p>
|
||||||
<p>The function returned takes any number of type arguments and returns a specialized Clojure function for calling the native function with those arguments.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/ffi.clj#L435">view source</a></div></div></div></body></html>
|
<p>This is primarily useful when you need to pass the static variable’s address to a native function which takes an <a href="null">Addressable</a>.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L628">view source</a></div></div><div class="public anchor" id="var-vacfn-factory"><h3>vacfn-factory</h3><div class="usage"><code>(vacfn-factory symbol required-args ret)</code></div><div class="doc"><div class="markdown"><p>Constructs a varargs factory to call the native function referenced by <code>symbol</code>.</p>
|
||||||
|
<p>The function returned takes any number of type arguments and returns a specialized Clojure function for calling the native function with those arguments.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/ffi.clj#L443">view source</a></div></div></div></body></html>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<!DOCTYPE html PUBLIC ""
|
<!DOCTYPE html PUBLIC ""
|
||||||
"">
|
"">
|
||||||
<html><head><meta charset="UTF-8" /><title>coffi.layout documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">coffi</span> <span class="project-version">v0.5.357</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>coffi</span></div></div></li><li class="depth-2 branch"><a href="coffi.ffi.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ffi</span></div></a></li><li class="depth-2 branch current"><a href="coffi.layout.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>layout</span></div></a></li><li class="depth-2"><a href="coffi.mem.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mem</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="coffi.layout.html#var-with-c-layout"><div class="inner"><span>with-c-layout</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">coffi.layout</h1><div class="doc"><div class="markdown"><p>Functions for adjusting the layout of structs.</p></div></div><div class="public anchor" id="var-with-c-layout"><h3>with-c-layout</h3><div class="usage"><code>(with-c-layout struct-spec)</code></div><div class="doc"><div class="markdown"><p>Forces a struct specification to C layout rules.</p>
|
<html><head><meta charset="UTF-8" /><title>coffi.layout documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">coffi</span> <span class="project-version">v0.6.409</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>coffi</span></div></div></li><li class="depth-2 branch"><a href="coffi.ffi.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>ffi</span></div></a></li><li class="depth-2 branch current"><a href="coffi.layout.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>layout</span></div></a></li><li class="depth-2"><a href="coffi.mem.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>mem</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="coffi.layout.html#var-with-c-layout"><div class="inner"><span>with-c-layout</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">coffi.layout</h1><div class="doc"><div class="markdown"><p>Functions for adjusting the layout of structs.</p></div></div><div class="public anchor" id="var-with-c-layout"><h3>with-c-layout</h3><div class="usage"><code>(with-c-layout struct-spec)</code></div><div class="doc"><div class="markdown"><p>Forces a struct specification to C layout rules.</p>
|
||||||
<p>This will add padding fields between fields to match C alignment requirements.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/e98c5a56bb4699035ebf3b3b49a8e89f8edbe087/src/clj/coffi/layout.clj#L6">view source</a></div></div></div></body></html>
|
<p>This will add padding fields between fields to match C alignment requirements.</p></div></div><div class="src-link"><a href="https://github.com/IGJoshua/coffi/blob/9d6051236550967a00151244e9e0c5630e2944b1/src/clj/coffi/layout.clj#L6">view source</a></div></div></div></body></html>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -13,15 +13,17 @@
|
||||||
MethodHandle
|
MethodHandle
|
||||||
MethodHandles
|
MethodHandles
|
||||||
MethodType)
|
MethodType)
|
||||||
(jdk.incubator.foreign
|
(java.lang.foreign
|
||||||
Addressable
|
Addressable
|
||||||
CLinker
|
Linker
|
||||||
FunctionDescriptor
|
FunctionDescriptor
|
||||||
MemoryAddress
|
MemoryAddress
|
||||||
MemoryLayout
|
MemoryLayout
|
||||||
NativeSymbol
|
MemorySegment
|
||||||
SegmentAllocator)))
|
SegmentAllocator)))
|
||||||
|
|
||||||
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
;;; FFI Code loading and function access
|
;;; FFI Code loading and function access
|
||||||
|
|
||||||
(defn load-system-library
|
(defn load-system-library
|
||||||
|
|
@ -35,7 +37,7 @@
|
||||||
(Loader/loadLibrary (.getAbsolutePath (io/file path))))
|
(Loader/loadLibrary (.getAbsolutePath (io/file path))))
|
||||||
|
|
||||||
(defn find-symbol
|
(defn find-symbol
|
||||||
"Gets the [[NativeSymbol]] of a symbol from the loaded libraries."
|
"Gets the [[MemorySegment]] of a symbol from the loaded libraries."
|
||||||
[sym]
|
[sym]
|
||||||
(Loader/findSymbol (name sym)))
|
(Loader/findSymbol (name sym)))
|
||||||
|
|
||||||
|
|
@ -52,9 +54,9 @@
|
||||||
args-arr)))))
|
args-arr)))))
|
||||||
|
|
||||||
(defn- downcall-handle
|
(defn- downcall-handle
|
||||||
"Gets the [[MethodHandle]] for the function at the `address`."
|
"Gets the [[MethodHandle]] for the function at the `sym`."
|
||||||
[sym function-descriptor]
|
[sym function-descriptor]
|
||||||
(.downcallHandle (CLinker/systemCLinker) sym function-descriptor))
|
(.downcallHandle (Linker/nativeLinker) sym function-descriptor))
|
||||||
|
|
||||||
(def ^:private load-instructions
|
(def ^:private load-instructions
|
||||||
"Mapping from primitive types to the instruction used to load them onto the stack."
|
"Mapping from primitive types to the instruction used to load them onto the stack."
|
||||||
|
|
@ -62,7 +64,6 @@
|
||||||
::mem/short :sload
|
::mem/short :sload
|
||||||
::mem/int :iload
|
::mem/int :iload
|
||||||
::mem/long :lload
|
::mem/long :lload
|
||||||
::mem/long-long :lload
|
|
||||||
::mem/char :cload
|
::mem/char :cload
|
||||||
::mem/float :fload
|
::mem/float :fload
|
||||||
::mem/double :dload
|
::mem/double :dload
|
||||||
|
|
@ -74,7 +75,6 @@
|
||||||
::mem/short Short
|
::mem/short Short
|
||||||
::mem/int Integer
|
::mem/int Integer
|
||||||
::mem/long Long
|
::mem/long Long
|
||||||
::mem/long-long Long
|
|
||||||
::mem/char Character
|
::mem/char Character
|
||||||
::mem/float Float
|
::mem/float Float
|
||||||
::mem/double Double})
|
::mem/double Double})
|
||||||
|
|
@ -110,7 +110,6 @@
|
||||||
::mem/short "shortValue"
|
::mem/short "shortValue"
|
||||||
::mem/int "intValue"
|
::mem/int "intValue"
|
||||||
::mem/long "longValue"
|
::mem/long "longValue"
|
||||||
::mem/long-long "longValue"
|
|
||||||
::mem/char "charValue"
|
::mem/char "charValue"
|
||||||
::mem/float "floatValue"
|
::mem/float "floatValue"
|
||||||
::mem/double "doubleValue"})
|
::mem/double "doubleValue"})
|
||||||
|
|
@ -187,11 +186,11 @@
|
||||||
[handle args ret]
|
[handle args ret]
|
||||||
(insn/new-instance (downcall-class args ret) ^MethodHandle handle))
|
(insn/new-instance (downcall-class args ret) ^MethodHandle handle))
|
||||||
|
|
||||||
(defn- ensure-symbol
|
(defn ensure-symbol
|
||||||
"Gets the address if the argument is [[Addressable]], otherwise
|
"Returns the argument if it is a [[MemorySegment]], otherwise
|
||||||
calls [[find-symbol]] on it."
|
calls [[find-symbol]] on it."
|
||||||
^NativeSymbol [symbol-or-addr]
|
^MemorySegment [symbol-or-addr]
|
||||||
(if (instance? NativeSymbol symbol-or-addr)
|
(if (instance? MemorySegment symbol-or-addr)
|
||||||
symbol-or-addr
|
symbol-or-addr
|
||||||
(find-symbol symbol-or-addr)))
|
(find-symbol symbol-or-addr)))
|
||||||
|
|
||||||
|
|
@ -235,7 +234,6 @@
|
||||||
::mem/short `short
|
::mem/short `short
|
||||||
::mem/int `int
|
::mem/int `int
|
||||||
::mem/long `long
|
::mem/long `long
|
||||||
::mem/long-long `long
|
|
||||||
::mem/char `char
|
::mem/char `char
|
||||||
::mem/float `float
|
::mem/float `float
|
||||||
::mem/double `double})
|
::mem/double `double})
|
||||||
|
|
@ -246,15 +244,24 @@
|
||||||
The return type and any arguments that are primitives will not
|
The return type and any arguments that are primitives will not
|
||||||
be (de)serialized except to be cast. If all arguments and return are
|
be (de)serialized except to be cast. If all arguments and return are
|
||||||
primitive, the `downcall` is returned directly. In cases where arguments must
|
primitive, the `downcall` is returned directly. In cases where arguments must
|
||||||
be serialized, a new [[mem/stack-scope]] is generated."
|
be serialized, a new [[mem/stack-session]] is generated."
|
||||||
[downcall arg-types ret-type]
|
[downcall arg-types ret-type]
|
||||||
(let [;; Complexity of types
|
(let [;; Complexity of types
|
||||||
const-args? (or (vector? arg-types) (nil? arg-types))
|
const-args? (or (vector? arg-types) (nil? arg-types))
|
||||||
simple-args? (when const-args?
|
simple-args? (when const-args?
|
||||||
(every? mem/primitive? arg-types))
|
(and (every? mem/primitive? arg-types)
|
||||||
|
;; NOTE(Joshua): Pointer types with serdes (e.g. [::mem/pointer ::mem/int])
|
||||||
|
;; still require a session, making them not qualify as "simple".
|
||||||
|
(every? keyword? (filter (comp #{::mem/pointer} mem/primitive-type) arg-types))))
|
||||||
const-ret? (s/valid? ::mem/type ret-type)
|
const-ret? (s/valid? ::mem/type ret-type)
|
||||||
primitive-ret? (and const-ret? (or (mem/primitive? ret-type)
|
primitive-ret? (and const-ret?
|
||||||
(#{::mem/void} ret-type)))
|
(or (and (mem/primitive? ret-type)
|
||||||
|
;; NOTE(Joshua): Pointer types with serdes require deserializing the
|
||||||
|
;; return value, but don't require passing a session to the downcall,
|
||||||
|
;; making them cause the return to not be primitive, but it may still
|
||||||
|
;; be "simple".
|
||||||
|
(or (keyword? ret-type) (not (#{::mem/pointer} (mem/primitive-type ret-type)))))
|
||||||
|
(#{::mem/void} ret-type)))
|
||||||
simple-ret? (and const-ret? (mem/primitive-type ret-type))
|
simple-ret? (and const-ret? (mem/primitive-type ret-type))
|
||||||
no-serde? (and const-args? (empty? arg-types)
|
no-serde? (and const-args? (empty? arg-types)
|
||||||
primitive-ret?)]
|
primitive-ret?)]
|
||||||
|
|
@ -266,7 +273,7 @@
|
||||||
~ret-type
|
~ret-type
|
||||||
downcall#)
|
downcall#)
|
||||||
(let [;; All our symbols
|
(let [;; All our symbols
|
||||||
scope (gensym "scope")
|
session (gensym "session")
|
||||||
downcall-sym (gensym "downcall")
|
downcall-sym (gensym "downcall")
|
||||||
args-sym (when-not const-args?
|
args-sym (when-not const-args?
|
||||||
(gensym "args"))
|
(gensym "args"))
|
||||||
|
|
@ -285,21 +292,22 @@
|
||||||
(some->>
|
(some->>
|
||||||
(cond
|
(cond
|
||||||
(not (s/valid? ::mem/type type))
|
(not (s/valid? ::mem/type type))
|
||||||
`(mem/serialize ~sym ~type-sym ~scope)
|
`(mem/serialize ~sym ~type-sym ~session)
|
||||||
|
|
||||||
(and (mem/primitive? type)
|
(and (mem/primitive? type)
|
||||||
(not (#{::mem/pointer} (mem/primitive-type type))))
|
(not (#{::mem/pointer} (mem/primitive-type type))))
|
||||||
(list (primitive-cast-sym (mem/primitive-type type)) sym)
|
(list (primitive-cast-sym (mem/primitive-type type)) sym)
|
||||||
|
|
||||||
|
;; cast null pointers to something understood by panama
|
||||||
(#{::mem/pointer} type)
|
(#{::mem/pointer} type)
|
||||||
nil
|
`(or ~sym (MemoryAddress/NULL))
|
||||||
|
|
||||||
(mem/primitive-type type)
|
(mem/primitive-type type)
|
||||||
`(mem/serialize* ~sym ~type-sym ~scope)
|
`(mem/serialize* ~sym ~type-sym ~session)
|
||||||
|
|
||||||
:else
|
:else
|
||||||
`(let [alloc# (mem/alloc-instance ~type-sym)]
|
`(let [alloc# (mem/alloc-instance ~type-sym)]
|
||||||
(mem/serialize-into ~sym ~type-sym alloc# ~scope)
|
(mem/serialize-into ~sym ~type-sym alloc# ~session)
|
||||||
alloc#))
|
alloc#))
|
||||||
(list sym)))
|
(list sym)))
|
||||||
|
|
||||||
|
|
@ -326,7 +334,7 @@
|
||||||
|
|
||||||
:else
|
:else
|
||||||
`(let [~args-sym (map (fn [obj# type#]
|
`(let [~args-sym (map (fn [obj# type#]
|
||||||
(mem/serialize obj# type# ~scope))
|
(mem/serialize obj# type# ~session))
|
||||||
~args-sym ~args-types-sym)]
|
~args-sym ~args-types-sym)]
|
||||||
~expr)))
|
~expr)))
|
||||||
|
|
||||||
|
|
@ -335,7 +343,7 @@
|
||||||
;; taking restargs, and so the downcall must be applied
|
;; taking restargs, and so the downcall must be applied
|
||||||
(-> `(~@(when (symbol? args) [`apply])
|
(-> `(~@(when (symbol? args) [`apply])
|
||||||
~downcall-sym
|
~downcall-sym
|
||||||
~@(when allocator? [`(mem/scope-allocator ~scope)])
|
~@(when allocator? [`(mem/session-allocator ~session)])
|
||||||
~@(if (symbol? args)
|
~@(if (symbol? args)
|
||||||
[args]
|
[args]
|
||||||
args))
|
args))
|
||||||
|
|
@ -358,12 +366,12 @@
|
||||||
:else
|
:else
|
||||||
(deserialize-segment expr)))
|
(deserialize-segment expr)))
|
||||||
|
|
||||||
wrap-scope (fn [expr]
|
wrap-session (fn [expr]
|
||||||
`(with-open [~scope (mem/stack-scope)]
|
`(with-open [~session (mem/stack-session)]
|
||||||
~expr))
|
~expr))
|
||||||
wrap-fn (fn [call needs-scope?]
|
wrap-fn (fn [call needs-session?]
|
||||||
`(fn [~@(if const-args? arg-syms ['& args-sym])]
|
`(fn [~@(if const-args? arg-syms ['& args-sym])]
|
||||||
~(cond-> call needs-scope? wrap-scope)))]
|
~(cond-> call needs-session? wrap-session)))]
|
||||||
`(let [;; NOTE(Joshua): To ensure all arguments are evaluated once and
|
`(let [;; NOTE(Joshua): To ensure all arguments are evaluated once and
|
||||||
;; in-order, they must be bound here
|
;; in-order, they must be bound here
|
||||||
~downcall-sym ~downcall
|
~downcall-sym ~downcall
|
||||||
|
|
@ -395,15 +403,15 @@
|
||||||
[downcall arg-types ret-type]
|
[downcall arg-types ret-type]
|
||||||
(if (mem/primitive-type ret-type)
|
(if (mem/primitive-type ret-type)
|
||||||
(fn native-fn [& args]
|
(fn native-fn [& args]
|
||||||
(with-open [scope (mem/stack-scope)]
|
(with-open [session (mem/stack-session)]
|
||||||
(mem/deserialize*
|
(mem/deserialize*
|
||||||
(apply downcall (map #(mem/serialize %1 %2 scope) args arg-types))
|
(apply downcall (map #(mem/serialize %1 %2 session) args arg-types))
|
||||||
ret-type)))
|
ret-type)))
|
||||||
(fn native-fn [& args]
|
(fn native-fn [& args]
|
||||||
(with-open [scope (mem/stack-scope)]
|
(with-open [session (mem/stack-session)]
|
||||||
(mem/deserialize-from
|
(mem/deserialize-from
|
||||||
(apply downcall (mem/scope-allocator scope)
|
(apply downcall (mem/session-allocator session)
|
||||||
(map #(mem/serialize %1 %2 scope) args arg-types))
|
(map #(mem/serialize %1 %2 session) args arg-types))
|
||||||
ret-type)))))
|
ret-type)))))
|
||||||
|
|
||||||
(defn make-serde-varargs-wrapper
|
(defn make-serde-varargs-wrapper
|
||||||
|
|
@ -510,10 +518,7 @@
|
||||||
([args] (method-type args ::mem/void))
|
([args] (method-type args ::mem/void))
|
||||||
([args ret]
|
([args ret]
|
||||||
(MethodType/methodType
|
(MethodType/methodType
|
||||||
^Class (let [r (mem/java-layout ret)]
|
^Class (coerce-addressable (mem/java-layout ret))
|
||||||
(if (= r MemoryAddress)
|
|
||||||
Addressable
|
|
||||||
r))
|
|
||||||
^"[Ljava.lang.Class;" (into-array Class (map mem/java-layout args)))))
|
^"[Ljava.lang.Class;" (into-array Class (map mem/java-layout args)))))
|
||||||
|
|
||||||
(defn- upcall-handle
|
(defn- upcall-handle
|
||||||
|
|
@ -531,30 +536,30 @@
|
||||||
|
|
||||||
(defn- upcall-serde-wrapper
|
(defn- upcall-serde-wrapper
|
||||||
"Creates a function that wraps `f` which deserializes the arguments and
|
"Creates a function that wraps `f` which deserializes the arguments and
|
||||||
serializes the return type in the [[global-scope]]."
|
serializes the return type in the [[global-session]]."
|
||||||
[f arg-types ret-type]
|
[f arg-types ret-type]
|
||||||
(fn [& args]
|
(fn [& args]
|
||||||
(mem/serialize
|
(mem/serialize
|
||||||
(apply f (map mem/deserialize args arg-types))
|
(apply f (map mem/deserialize args arg-types))
|
||||||
ret-type
|
ret-type
|
||||||
(mem/global-scope))))
|
(mem/global-session))))
|
||||||
|
|
||||||
(defmethod mem/serialize* ::fn
|
(defmethod mem/serialize* ::fn
|
||||||
[f [_fn arg-types ret-type & {:keys [raw-fn?]}] scope]
|
[f [_fn arg-types ret-type & {:keys [raw-fn?]}] session]
|
||||||
(.upcallStub
|
(.upcallStub
|
||||||
(CLinker/systemCLinker)
|
(Linker/nativeLinker)
|
||||||
(cond-> f
|
(cond-> f
|
||||||
(not raw-fn?) (upcall-serde-wrapper arg-types ret-type)
|
(not raw-fn?) (upcall-serde-wrapper arg-types ret-type)
|
||||||
:always (upcall-handle arg-types ret-type))
|
:always (upcall-handle arg-types ret-type))
|
||||||
(function-descriptor arg-types ret-type)
|
(function-descriptor arg-types ret-type)
|
||||||
scope))
|
session))
|
||||||
|
|
||||||
(defmethod mem/deserialize* ::fn
|
(defmethod mem/deserialize* ::fn
|
||||||
[addr [_fn arg-types ret-type & {:keys [raw-fn?]}]]
|
[addr [_fn arg-types ret-type & {:keys [raw-fn?]}]]
|
||||||
(when-not (mem/null? addr)
|
(when-not (mem/null? addr)
|
||||||
(vary-meta
|
(vary-meta
|
||||||
(-> addr
|
(-> addr
|
||||||
(as-> addr (NativeSymbol/ofAddress "coffi_upcall_symbol" addr (mem/connected-scope)))
|
(MemorySegment/ofAddress mem/pointer-size (mem/connected-session))
|
||||||
(downcall-handle (function-descriptor arg-types ret-type))
|
(downcall-handle (function-descriptor arg-types ret-type))
|
||||||
(downcall-fn arg-types ret-type)
|
(downcall-fn arg-types ret-type)
|
||||||
(cond-> (not raw-fn?) (make-serde-wrapper arg-types ret-type)))
|
(cond-> (not raw-fn?) (make-serde-wrapper arg-types ret-type)))
|
||||||
|
|
@ -567,10 +572,29 @@
|
||||||
[symbol-or-addr type]
|
[symbol-or-addr type]
|
||||||
(mem/deserialize (.address (ensure-symbol symbol-or-addr)) [::mem/pointer type]))
|
(mem/deserialize (.address (ensure-symbol symbol-or-addr)) [::mem/pointer type]))
|
||||||
|
|
||||||
|
(s/def ::defconst-args
|
||||||
|
(s/cat :var-name simple-symbol?
|
||||||
|
:docstring (s/? string?)
|
||||||
|
:symbol-or-addr any?
|
||||||
|
:type ::mem/type))
|
||||||
|
|
||||||
|
(defmacro defconst
|
||||||
|
"Defines a var named by `symbol` to be the value of the given `type` from `symbol-or-addr`."
|
||||||
|
{:arglists '([symbol docstring? symbol-or-addr type])}
|
||||||
|
[& args]
|
||||||
|
(let [args (s/conform ::defconst-args args)]
|
||||||
|
`(let [symbol# (ensure-symbol ~(:symbol-or-addr args))]
|
||||||
|
(def ~(:var-name args)
|
||||||
|
~@(when-let [doc (:docstring args)]
|
||||||
|
(list doc))
|
||||||
|
(const symbol# ~(:type args))))))
|
||||||
|
(s/fdef defconst
|
||||||
|
:args ::defconst-args)
|
||||||
|
|
||||||
(deftype StaticVariable [seg type meta]
|
(deftype StaticVariable [seg type meta]
|
||||||
IDeref
|
IDeref
|
||||||
(deref [_]
|
(deref [_]
|
||||||
(mem/deserialize seg type))
|
(mem/deserialize-from seg type))
|
||||||
|
|
||||||
IObj
|
IObj
|
||||||
(withMeta [_ meta-map]
|
(withMeta [_ meta-map]
|
||||||
|
|
@ -590,7 +614,7 @@
|
||||||
(mem/serialize-into
|
(mem/serialize-into
|
||||||
newval (.-type static-var)
|
newval (.-type static-var)
|
||||||
(.-seg static-var)
|
(.-seg static-var)
|
||||||
(mem/global-scope))
|
(mem/global-session))
|
||||||
newval)
|
newval)
|
||||||
|
|
||||||
(defn fswap!
|
(defn fswap!
|
||||||
|
|
@ -601,19 +625,39 @@
|
||||||
[static-var f & args]
|
[static-var f & args]
|
||||||
(freset! static-var (apply f @static-var args)))
|
(freset! static-var (apply f @static-var args)))
|
||||||
|
|
||||||
|
(defn static-variable-segment
|
||||||
|
"Gets the backing [[MemorySegment]] from `static-var`.
|
||||||
|
|
||||||
|
This is primarily useful when you need to pass the static variable's address
|
||||||
|
to a native function which takes an [[Addressable]]."
|
||||||
|
^MemorySegment [static-var]
|
||||||
|
(.-seg ^StaticVariable static-var))
|
||||||
|
|
||||||
(defn static-variable
|
(defn static-variable
|
||||||
"Constructs a reference to a mutable value stored in `symbol-or-addr`.
|
"Constructs a reference to a mutable value stored in `symbol-or-addr`.
|
||||||
|
|
||||||
The returned value can be dereferenced, and has metadata, and the address of
|
The returned value can be dereferenced, and has metadata.
|
||||||
the value can be queried with [[address-of]].
|
|
||||||
|
|
||||||
See [[freset!]], [[fswap!]]."
|
See [[freset!]], [[fswap!]]."
|
||||||
[symbol-or-addr type]
|
[symbol-or-addr type]
|
||||||
(StaticVariable. (mem/as-segment (.address (ensure-symbol symbol-or-addr))
|
(StaticVariable. (mem/as-segment (.address (ensure-symbol symbol-or-addr))
|
||||||
(mem/size-of type)
|
(mem/size-of type)
|
||||||
(mem/global-scope))
|
(mem/global-session))
|
||||||
type (atom nil)))
|
type (atom nil)))
|
||||||
|
|
||||||
|
(defmacro defvar
|
||||||
|
"Defines a var named by `symbol` to be a reference to the native memory from `symbol-or-addr`."
|
||||||
|
{:arglists '([symbol docstring? symbol-or-addr type])}
|
||||||
|
[& args]
|
||||||
|
(let [args (s/conform ::defconst-args args)]
|
||||||
|
`(let [symbol# (ensure-symbol ~(:symbol-or-addr args))]
|
||||||
|
(def ~(:var-name args)
|
||||||
|
~@(when-let [doc (:docstring args)]
|
||||||
|
(list doc))
|
||||||
|
(static-variable symbol# ~(:type args))))))
|
||||||
|
(s/fdef defvar
|
||||||
|
:args ::defconst-args)
|
||||||
|
|
||||||
(s/def :coffi.ffi.symbolspec/symbol string?)
|
(s/def :coffi.ffi.symbolspec/symbol string?)
|
||||||
(s/def :coffi.ffi.symbolspec/type keyword?)
|
(s/def :coffi.ffi.symbolspec/type keyword?)
|
||||||
(s/def ::symbolspec
|
(s/def ::symbolspec
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,12 @@
|
||||||
(if (seq fields)
|
(if (seq fields)
|
||||||
(let [[[_ type :as field] & fields] fields
|
(let [[[_ type :as field] & fields] fields
|
||||||
size (mem/size-of type)
|
size (mem/size-of type)
|
||||||
r (rem offset (mem/align-of type))]
|
align (mem/align-of type)
|
||||||
|
r (rem offset align)]
|
||||||
(recur (cond-> (+ offset size)
|
(recur (cond-> (+ offset size)
|
||||||
(pos? r) (+ (- size r)))
|
(pos? r) (+ (- align r)))
|
||||||
(cond-> aligned-fields
|
(cond-> aligned-fields
|
||||||
(pos? r) (conj [::padding [::mem/padding (- size r)]])
|
(pos? r) (conj [::padding [::mem/padding (- align r)]])
|
||||||
:always (conj field))
|
:always (conj field))
|
||||||
fields))
|
fields))
|
||||||
(let [strongest-alignment (mem/align-of struct-spec)
|
(let [strongest-alignment (mem/align-of struct-spec)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
||||||
package coffi.ffi;
|
package coffi.ffi;
|
||||||
|
|
||||||
import jdk.incubator.foreign.*;
|
import java.lang.foreign.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loading libraries with the {@link System#load} and {@link System#loadLibrary}
|
* Loading libraries with the {@link System#load} and {@link System#loadLibrary}
|
||||||
|
|
@ -36,8 +36,8 @@ public class Loader {
|
||||||
*
|
*
|
||||||
* @param symbol The name of the symbol to load from a library.
|
* @param symbol The name of the symbol to load from a library.
|
||||||
*/
|
*/
|
||||||
public static NativeSymbol findSymbol(String symbol) {
|
public static MemorySegment findSymbol(String symbol) {
|
||||||
return CLinker.systemCLinker().lookup(symbol)
|
return Linker.nativeLinker().defaultLookup().lookup(symbol)
|
||||||
.orElseGet(() -> SymbolLookup.loaderLookup().lookup(symbol).orElse(null));
|
.orElseGet(() -> SymbolLookup.loaderLookup().lookup(symbol).orElse(null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ CString upcall_test(StringFactory fun) {
|
||||||
return fun();
|
return fun();
|
||||||
}
|
}
|
||||||
|
|
||||||
static 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" };
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,8 @@
|
||||||
{:a \x
|
{:a \x
|
||||||
:x 3.14
|
:x 3.14
|
||||||
:y 42.0})))
|
:y 42.0})))
|
||||||
|
|
||||||
|
(t/deftest static-variables-are-mutable
|
||||||
|
(ffi/freset! (ffi/static-variable "counter" ::mem/int) 1)
|
||||||
|
(t/is (= ((ffi/cfn "get_string1" [] ::mem/c-string))
|
||||||
|
"Goodbye friend.")))
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue