Discuss GraalVM related topics. Use clojure 1.10.2 or newer for all new projects. Contribute to and GraalVM slack:
ericdallo 2021-01-27T12:47:21.031900Z

I kind of found the issue, I made a simple reify of the LanguageServer from lsp4j :

(defn fake-server []
  (reify LanguageServer
    (^CompletableFuture initialize [_this ^InitializeParams params]
       (println "INITIALIZING!")
       (log/info "INITIALIZING!")))

    (getTextDocumentService [_this]
    (getWorkspaceService [_this]
During the start of the app, I call:
(log/info "-->" (class server)
                    (:members (r/reflect server)))
That prints all methods from the class, it works perfect with lein run,but with graalvm it prints #{}

borkdude 2021-01-27T12:59:43.032100Z

So what does this tell you?

ericdallo 2021-01-27T13:01:55.032300Z

It means the for some reason, graalvm is not working with that reify

borkdude 2021-01-27T13:04:17.032800Z

@ericdallo what error do you get when using it?

ericdallo 2021-01-27T13:05:43.033Z

Jan 27, 2021 9:46:32 AM request
WARNING: Unsupported request method: initialize

ericdallo 2021-01-27T13:06:23.033200Z

because it can't find the implementation of one of the methods initialize

borkdude 2021-01-27T13:06:49.033400Z

because you haven't reified it?

borkdude 2021-01-27T13:07:01.033600Z

oh sorry I misread

ericdallo 2021-01-27T13:07:46.033800Z

this is what I get from lein run / java from graalvm home:

--> clojure_lsp.main$fake_server$reify__31521 #{#clojure.reflect.Method{:name initialize, :return-type java.util.concurrent.CompletableFuture, :declaring-class clojure_lsp.main$fake_server$reify__31521, :parameter-types [org.eclipse.lsp4j.InitializeParams], :exception-types [], :flags #{:public}} #clojure.reflect.Method{:name getTextDocumentService, :return-type, :declaring-class clojure_lsp.main$fake_server$reify__31521, :parameter-types [], :exception-types [], :flags #{:public}} #clojure.reflect.Field{:name const__4, :type java.lang.Object, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}} #clojure.reflect.Method{:name meta, :return-type clojure.lang.IPersistentMap, :declaring-class clojure_lsp.main$fake_server$reify__31521, :parameter-types [], :exception-types [], :flags #{:public}} #clojure.reflect.Field{:name const__2, :type clojure.lang.Var, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}} #clojure.reflect.Method{:name getWorkspaceService, :return-type, :declaring-class clojure_lsp.main$fake_server$reify__31521, :parameter-types [], :exception-types [], :flags #{:public}} #clojure.reflect.Field{:name const__1, :type clojure.lang.Var, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}} #clojure.reflect.Field{:name const__6, :type clojure.lang.Keyword, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}} #clojure.reflect.Method{:name withMeta, :return-type clojure.lang.IObj, :declaring-class clojure_lsp.main$fake_server$reify__31521, :parameter-types [clojure.lang.IPersistentMap], :exception-types [], :flags #{:public}} #clojure.reflect.Constructor{:name clojure_lsp.main$fake_server$reify__31521, :declaring-class clojure_lsp.main$fake_server$reify__31521, :parameter-types [clojure.lang.IPersistentMap], :exception-types [], :flags #{:public}} #clojure.reflect.Constructor{:name clojure_lsp.main$fake_server$reify__31521, :declaring-class clojure_lsp.main$fake_server$reify__31521, :parameter-types [], :exception-types [], :flags #{:public}} #clojure.reflect.Field{:name const__3, :type clojure.lang.Keyword, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}} #clojure.reflect.Field{:name const__0, :type clojure.lang.Var, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}} #clojure.reflect.Field{:name const__5, :type clojure.lang.Keyword, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}} #clojure.reflect.Field{:name __meta, :type clojure.lang.IPersistentMap, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:final}} #clojure.reflect.Field{:name const__7, :type java.lang.Object, :declaring-class clojure_lsp.main$fake_server$reify__31521, :flags #{:public :static :final}}}
It's printing correct all methods from that reify

borkdude 2021-01-27T13:08:09.034Z

@ericdallo Maybe this will work: Is it possible to make the reified object at the top-level, at compile time?

ericdallo 2021-01-27T13:08:46.034200Z

yes it's possible, changing from defn to def would be enough?

borkdude 2021-01-27T13:09:30.034400Z

think so

ericdallo 2021-01-27T13:09:38.034600Z

Let me try it :loading:

ericdallo 2021-01-27T13:23:41.034800Z

Same issue, this is the output of that print from above:

--> class clojure_lsp.main$reify__31521 #{}

borkdude 2021-01-27T13:24:15.035Z

also the same error when used?

ericdallo 2021-01-27T13:25:07.035200Z

yep, maybe try with gen-class?

borkdude 2021-01-27T13:30:48.035600Z

or add the class to the reflection config

Shantanu Kumar 2021-01-27T13:31:54.035800Z

@ericdallo Maybe try once without type-hinting the initialize method? Since it’s the only method in the interface, it should work without hinting due to auto-inference.

ericdallo 2021-01-27T13:31:55.036Z

But how can I get the class name? or you mean add the interface to the reflection config?

ericdallo 2021-01-27T13:32:34.036200Z

good point @kumarshantanu, I'll need type hint for other methods later, but for that test it's valid, let me try

borkdude 2021-01-27T13:32:38.036400Z

yeah, the interface

ericdallo 2021-01-27T13:44:06.036700Z

It gives another error, but it seems is a step ahead πŸ˜†

SEVERE: Unable to invoke no-args constructor for class org.eclipse.lsp4j.adapters.InitializeParamsTypeAdapter$Factory. Registering an InstanceCreator with Gson for this type may fix this problem.
java.lang.RuntimeException: Unable to invoke no-args constructor for class org.eclipse.lsp4j.adapters.InitializeParamsTypeAdapter$Factory. Registering an InstanceCreator with Gson for this type may fix this problem.
	at org.eclipse.lsp4j.jsonrpc.json.adapters.MessageTypeAdapter.fromJson(
	at org.eclipse.lsp4j.jsonrpc.json.adapters.MessageTypeAdapter.parseParams(
	at org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler.parseMessage(
	at org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler.parseMessage(
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(
	at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(
	at java.util.concurrent.Executors$
	at java.util.concurrent.ThreadPoolExecutor.runWorker(
	at java.util.concurrent.ThreadPoolExecutor$
Caused by: java.lang.reflect.InvocationTargetException
	at java.lang.reflect.Method.invoke(
	... 21 more
Caused by: java.lang.IllegalArgumentException: Class org.eclipse.lsp4j.adapters.InitializeParamsTypeAdapter$Factory is instantiated reflectively but was never registered. Register the class by using org.graalvm.nativeimage.hosted.RuntimeReflection
	at sun.misc.Unsafe.allocateInstance(
	... 24 more

Process clojure-lsp stderr<1> finished

ericdallo 2021-01-27T13:44:27.036900Z

Not sure is related to remove the type hinting or to move to the reflection.json

borkdude 2021-01-27T13:44:41.037100Z

never change two things at a time ;)

ericdallo 2021-01-27T13:44:59.037300Z

yeah hahaha

ericdallo 2021-01-27T13:45:15.037500Z

it seems another issue now, it's serializing the json to respond to client

ericdallo 2021-01-27T13:45:31.037700Z

Probably I should do something to initialize Gson correctly

ericdallo 2021-01-27T13:46:21.037900Z

Oh this looks a good start:

Class org.eclipse.lsp4j.adapters.InitializeParamsTypeAdapter$Factory is instantiated reflectively but was never registered. Register the class by using 

ericdallo 2021-01-27T13:46:36.038100Z

should I add it to reflection.json?

borkdude 2021-01-27T13:46:44.038300Z

guess so

ericdallo 2021-01-27T13:49:23.038600Z

If that works, probably I'll need to add to the reflection too, if that works, I'll certainly open an issue on lsp4j with steps how to make it work with graalvm 🀞

ericdallo 2021-01-27T14:50:04.038800Z

Good news, problem solved with the suggestion from you, @borkdude , I added some classes to reflection json and it's getting into the method initialize correctly now βœ… The next issue πŸ˜† is: clojure-lsp tries to parse Gson json objects via, and it's returning this error on runtime with graal:    192                                                                
                                     java.util.concurrent.ThreadPoolExecutor$  628                                                                
                                      java.util.concurrent.ThreadPoolExecutor.runWorker 1128                                                                
                                    java.util.concurrent.Executors$           515
                            org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen   94
                     org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage  194
                                       org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume      190
                                 org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleRequest      261
             $null$0      65
                                                                 clojure-lsp.main/fn/fn                         main.clj:  326
                                                       clojure-lsp.main/client-settings                         main.clj:  278
                                                          clojure-lsp.interop/json->clj                      interop.clj:  421
java.lang.IllegalArgumentException: No matching field found: isJsonNull for class

ericdallo 2021-01-27T14:50:34.039200Z

Should I add to reflection too?

borkdude 2021-01-27T15:36:56.039500Z


borkdude 2021-01-27T15:37:07.039700Z

at least that method

ericdallo 2021-01-27T15:41:14.039900Z

After that I get:

java.lang.NoSuchMethodError: java.lang.reflect.AccessibleObject.canAccess(java.lang.Object)
Not sure what to add to reflection.json now

borkdude 2021-01-27T15:42:26.040100Z

Maybe java.lang.reflect.AccessibleObject πŸ˜†

ericdallo 2021-01-27T15:42:46.040300Z

Yeah, that was my guess πŸ˜› compiling :loading:

borkdude 2021-01-27T15:43:04.040500Z

you can also run with a graalvm agent and then it will spit out all the things it needed reflection for during the run of a program

borkdude 2021-01-27T15:43:15.040700Z

it will also spit out a lot of classes you don't need, but it might give you some idea

borkdude 2021-01-27T15:43:23.040900Z

be sure to run with direct linking, this might help

ericdallo 2021-01-27T15:44:40.041100Z

Yeah, I thought about that, but the issue is that is not trivial to test clojure-lsp on terminal, we need to send LSP spec jsons, I'll try to move it to a script that use the java from graalvm, hope it works

borkdude 2021-01-27T15:47:33.041300Z

I mean, this is just a setting when creating the uberjar

borkdude 2021-01-27T15:47:43.041500Z

borkdude 2021-01-27T15:47:53.041700Z


borkdude 2021-01-27T15:47:58.041900Z

I see, never mind

ericdallo 2021-01-27T15:51:33.042100Z

np, thanks for the tip πŸ™‚

ericdallo 2021-01-27T18:36:18.042500Z

Having this now:

Can't call public method of non-public class: public sun.nio.fs.UnixPath sun.nio.fs.UnixPath.getParent()
even with this on reflection.json:
        "name": "sun.nio.fs.UnixPath",
        "allDeclaredConstructors": true,
        "allPublicConstructors": true,
        "allDeclaredMethods": true,
        "allPublicMethods": true
Related to this line:

Shantanu Kumar 2021-01-27T19:24:28.042900Z

@ericdallo Try hinting dir as a public interface? (.getParent ^java.nio.file.Path dir)

ericdallo 2021-01-27T19:25:12.043100Z

Hum, good guess again, let me try it, I was thinking in refactor that to use <|> instead of manually using java classes, similar how clj-kondo do to get the config dir

borkdude 2021-01-27T19:27:28.043400Z

@ericdallo You should never have to include OS specific things in your reflection config, this config won't work on Windows for example

borkdude 2021-01-27T19:28:12.043900Z

either for the return type or for the local

ericdallo 2021-01-27T19:30:51.044100Z

You are right, something like that?

[dir ^java.nio.file.Path  (shared/uri-&gt;path project-root)]

borkdude 2021-01-27T19:31:21.044400Z

yes, but if you place this type hint on your function it would be even better