From 1fe9d2cb0223ecc21aeb547d80e9429698b92477 Mon Sep 17 00:00:00 2001 From: Joshua Suskalo Date: Thu, 23 Sep 2021 08:15:16 -0500 Subject: [PATCH] Move library loading interop to a java class --- build.clj | 12 +++++++++- deps.edn | 2 +- src/{ => clj}/coffi/ffi.clj | 8 +++---- src/java/coffi/ffi/Loader.java | 43 ++++++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+), 6 deletions(-) rename src/{ => clj}/coffi/ffi.clj (99%) create mode 100644 src/java/coffi/ffi/Loader.java diff --git a/build.clj b/build.clj index 1597ce4..856d970 100644 --- a/build.clj +++ b/build.clj @@ -19,7 +19,8 @@ (def lib-coord 'org.suskalo/coffi) (def version (format "0.1.%s-SNAPSHOT" (b/git-count-revs nil))) -(def source-dirs ["src/"]) +(def source-dirs ["src/clj/"]) +(def java-source-dirs ["src/java/"]) (def c-test-dirs ["test/c/"]) @@ -42,6 +43,15 @@ [& path-components] (.exists ^java.io.File (apply io/file path-components))) +(defn compile-java + "Compiles java classes required for interop." + [opts] + (.mkdirs (io/file class-dir)) + (b/process {:command-args ["javac" "--add-modules=jdk.incubator.foreign" + "src/java/coffi/ffi/Loader.java" + "-d" class-dir]}) + opts) + (defn- write-pom "Writes a pom file if one does not already exist." [opts] diff --git a/deps.edn b/deps.edn index 35e6310..4e3c4ee 100644 --- a/deps.edn +++ b/deps.edn @@ -1,4 +1,4 @@ -{:paths ["src"] +{:paths ["src" "target/classes"] :deps {org.clojure/clojure {:mvn/version "1.10.3"} insn/insn {:mvn/version "0.2.1"}} :aliases diff --git a/src/coffi/ffi.clj b/src/clj/coffi/ffi.clj similarity index 99% rename from src/coffi/ffi.clj rename to src/clj/coffi/ffi.clj index fb5bbc7..70779d8 100644 --- a/src/coffi/ffi.clj +++ b/src/clj/coffi/ffi.clj @@ -7,6 +7,7 @@ (:import (clojure.lang IDeref IFn IMeta IObj IReference) + (coffi.ffi Loader) (java.lang.invoke MethodHandle MethodHandles @@ -567,19 +568,18 @@ (defn load-system-library "Loads the library named `libname` from the system's load path." [libname] - (System/loadLibrary (name libname))) + (Loader/loadSystemLibrary (name libname))) (defn load-library "Loads the library at `path`." [path] - (System/load (.getAbsolutePath (io/file path)))) + (Loader/loadLibrary (.getAbsolutePath (io/file path)))) (defn find-symbol "Gets the [[MemoryAddress]] of a symbol from the loaded libraries." [sym] (let [sym (name sym)] - (or (.. (CLinker/systemLookup) (lookup sym) (orElse nil)) - (.. (SymbolLookup/loaderLookup) (lookup sym) (orElse nil))))) + (Loader/findSymbol sym))) (defn- method-type "Gets the [[MethodType]] for a set of `args` and `ret` types." diff --git a/src/java/coffi/ffi/Loader.java b/src/java/coffi/ffi/Loader.java new file mode 100644 index 0000000..b06d00f --- /dev/null +++ b/src/java/coffi/ffi/Loader.java @@ -0,0 +1,43 @@ +package coffi.ffi; + +import jdk.incubator.foreign.*; + +/** + * Loading libraries with the {@link System#load} and {@link System#loadLibrary} + * relies on the classloader, which Clojure messes with. This class exists to + * have a consistent classloader, providing a consistent way to load libraries + * and symbols from them. + */ +public class Loader { + + /** + * Loads a library from a given absolute file path. + * + * @param filepath The absolute file path of the library to load + */ + public static void loadLibrary(String filepath) { + System.load(filepath); + } + + /** + * Loads a library on the system loadpath with the given name. + * + * @param libname The library name, stripped of platform-specific prefixes and suffixes. + */ + public static void loadSystemLibrary(String libname) { + System.loadLibrary(libname); + } + + /** + * Load the memory address of a symbol. + * + * First attempts to load the symbol from system libraries, like libc, and + * afterwards attempts to load the symbol from other libraries. + * + * @param symbol The name of the symbol to load from a library. + */ + public static MemoryAddress findSymbol(String symbol) { + return CLinker.systemLookup().lookup(symbol) + .orElseGet(() -> SymbolLookup.loaderLookup().lookup(symbol).orElse(null)); + } +}