This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-11-15
Channels
- # aleph (2)
- # babashka (35)
- # beginners (31)
- # biff (6)
- # calva (6)
- # cider (5)
- # clojure (61)
- # clojure-android (1)
- # clojure-dev (12)
- # clojure-europe (22)
- # clojure-norway (7)
- # clojure-uk (4)
- # clojurescript (19)
- # datomic (5)
- # events (3)
- # fulcro (15)
- # graalvm (41)
- # guix (2)
- # honeysql (2)
- # hoplon (8)
- # hyperfiddle (10)
- # jobs (1)
- # off-topic (29)
- # overtone (5)
- # podcasts-discuss (1)
- # remote-jobs (1)
- # sci (30)
- # shadow-cljs (186)
- # specter (2)
- # squint (22)
I'm getting a "the class was requested to be initialized at run time (from feature clj_easy.graal_build_time.InitClojureClasses.duringSetup with 'com.phronemophobic')" when calling a static method on a class. Are static methods not supported in clojure code compiled with native image? More info in 𧡠.
This is the offending line: https://github.com/phronmophobic/llama.clj/blob/a6d1a9c7b1c1cda06835f4abc0b3e9f007cfb2a5/src/com/phronemophobic/llama/raw_gguf.clj#L447
If I change (Structure/newInstance ...)
to nil
, it compiles just fine (obviously, that breaks the program).
The full error looks like:
Error: Classes that should be initialized at run time got initialized during image building:
com.sun.jna.Structure was unintentionally initialized at build time. com.phronemophobic.llama.raw_gguf$ctx__GT_candidates$fn__14271 caused initialization of this class with the following trace:
at com.sun.jna.Structure.<clinit>(Structure.java:114)
at java.lang.Class.forName0(Unknown Source)
at java.lang.Class.forName(Class.java:495)
at java.lang.Class.forName(Class.java:474)
at clojure.lang.RT.classForName(RT.java:2209)
at clojure.lang.RT.classForName(RT.java:2218)
at com.phronemophobic.llama.raw_gguf$ctx__GT_candidates$fn__14271.<clinit>(raw_gguf.clj:460)
The enclosing function ctx->candidates
isn't called until runtime.
I'd try to make a pure Java repro that calls Class/forName in a function body to see if you get the same behavior. If so, ask around in the graalvm slack - native-image channel + post an issue. If not, perhaps you can learn why this class gets initialized by inspecting the bytecode? Not sure...
com.sun.jna.Structure
looks like native interop using JNA. So my guess it's sniffing around how to map native structs to some java classes. This will be tough I'm afraid, but I don't want to be a messenger with a bad news.It seems like the Class/forName call is from the clojure compiler when it's compiling the static method. It seems like there should be a way to compile the invocation of the static method without loading the class.
but all classes are processed through Class/forName... Perhaps you can work around this by placing this method in your own Java class and then calling that class
The idea being that calling a method on my own Java class will load that particular class, but not its dependencies?
ie. create my own java class MyStructWrapper
with a static method that just calls Structure/newInstance
. Calling MyStructWrapper/newInstance
from clojure will load MyStructWrapper
, but not Structure
?
That's worth a shot. Thanks for the idea!
Oh, interesting. I checked with *warn-on-reflection*
and it didn't get flagged. There's only a single overload of Structure/newInstance
with 2 args.
maybe if I just wait on the new method thunk stuff to be released, my problem will go away π
Is there a release with Method thunk support already?
oh, weird. There's a second 2 arity overload for that method showing at the repl that doesn't appear in the javadocs.
It doesn't seem to.
hmm, there is an overload, but it's private: https://github.com/java-native-access/jna/blob/master/src/com/sun/jna/Structure.java#L1846
Anyway, adding type hints fixed the problem and native-image now compiles.
I guess that makes sense. It's possible to convert a method to be public at runtime, so there's now way for the compiler to rule it out.
I think the only issue is that *warn-on-reflection*
doesn't flag it.
I'll check ask.clojure to see if there's an existing question and add one if not.
perhaps the native image agent would have caught it (along with many other false positives)
ok, I created a minimal repro and it seems the problem had nothing to do with the static method and it was calling Class/forName on the arg llama_token_dataByReference
which subclasses Structure
. :man-facepalming:
The compiler has to use reflection to find available methods, even if the generated code ends up being a non-reflective call
What I mean by is is the Compiler(.java) uses the reflector to detect methods at compile time to see what it should compile into. https://github.com/clojure/clojure/blob/08a2d9bdd013143a87e50fa82e9740e2ab4ee2c2/src/jvm/clojure/lang/Compiler.java#L977 This is visible in agents which detect reflection and cause false positives. I guess you can bypass this by first compiling to bytecode and then running that with an agent.