eclipse-lsp4j / lsp4j

A Java implementation of the language server protocol intended to be consumed by tools and language servers implemented in Java.
https://eclipse.org/lsp4j
Other
613 stars 145 forks source link

JsonIOException when trying to launch debug session #674

Closed sebthom closed 2 years ago

sebthom commented 2 years ago

I am trying to get launching debug sessions working with the haxe-language-server and it's debug adapter eval-debugger using LSP4J 0.15.0 and LSP4E 0.20.7.

However when I launch the session, LSP4J and the debugger process are communicating until the progress hangs at this step:

image

The following exception is visible in the log: com.google.gson.JsonIOException: Failed making constructor 'java.lang.Void#Void()' accessible

Oct 17, 2022 8:16:29 PM org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer fireError
SEVERE: Failed making constructor 'java.lang.Void#Void()' accessible; either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: Unable to make private java.lang.Void() accessible: module java.base does not "opens java.lang" to unnamed module @1c9cdfd8
com.google.gson.JsonIOException: Failed making constructor 'java.lang.Void#Void()' accessible; either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: Unable to make private java.lang.Void() accessible: module java.base does not "opens java.lang" to unnamed module @1c9cdfd8
    at com.google.gson.internal.ConstructorConstructor$8.construct(ConstructorConstructor.java:233)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:256)
    at com.google.gson.Gson.fromJson(Gson.java:1058)
    at com.google.gson.Gson.fromJson(Gson.java:1129)
    at org.eclipse.lsp4j.jsonrpc.debug.adapters.DebugMessageTypeAdapter.createMessage(DebugMessageTypeAdapter.java:392)
    at org.eclipse.lsp4j.jsonrpc.debug.adapters.DebugMessageTypeAdapter.read(DebugMessageTypeAdapter.java:255)
    at org.eclipse.lsp4j.jsonrpc.debug.adapters.DebugMessageTypeAdapter.read(DebugMessageTypeAdapter.java:157)
    at com.google.gson.Gson.fromJson(Gson.java:1058)
    at org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler.parseMessage(MessageJsonHandler.java:119)
    at org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler.parseMessage(MessageJsonHandler.java:114)
    at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:193)
    at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:94)
    at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:113)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)

Here is a screenshot of the stacktrace + code + variables. image

This are the messages send from the debugger process:

{"seq":1,"type":"response","request_seq":1,"command":"initialize","success":true,"body":{"supportsSetVariable":true,"supportsEvaluateForHovers":true,"supportsConditionalBreakpoints":true,"supportsExceptionOptions":true,"exceptionBreakpointFilters":[{"filter":"all","label":"All Exceptions"},{"filter":"uncaught","label":"Uncaught Exceptions"}],"supportsFunctionBreakpoints":true,"supportsConfigurationDoneRequest":true,"supportsCompletionsRequest":true}}
{"seq":2,"type":"event","event":"output","body":{"category":"console","output":"Haxe connected!\n"}}
{"seq":3,"type":"event","event":"initialized"}
{"seq":4,"type":"response","request_seq":2,"command":"launch","success":true}
{"seq":5,"type":"event","event":"output","body":{"category":"console","output":"Sending command: {\"id\":1,\"method\":\"setBreakpoints\",\"params\":{}}\n"}}
{"seq":6,"type":"event","event":"output","body":{"category":"console","output":"GOT MESSAGE {\"jsonrpc\":\"2.0\",\"id\":1,\"result\":[{\"id\":1}]}\n"}}
{"seq":7,"type":"response","request_seq":3,"command":"setBreakpoints","success":true,"body":{"breakpoints":[{"verified":true,"id":1}]}}
{"seq":8,"type":"event","event":"output","body":{"category":"console","output":"Sending command: {\"id\":2,\"method\":\"continue\",\"params\":{}}\n"}}
{"seq":9,"type":"event","event":"output","body":{"category":"console","output":"GOT MESSAGE {\"jsonrpc\":\"2.0\",\"id\":2,\"result\":null}\n"}}
{"seq":10,"type":"response","request_seq":4,"command":"configurationDone","success":true,"body":{"allThreadsContinued":false}}
{"seq":11,"type":"event","event":"output","body":{"category":"console","output":"Socket error: Error: read ECONNRESET\n"}}
{"seq":12,"type":"event","event":"terminated","body":{"restart":false}}

The launch code can be found here: https://github.com/haxe4e/haxe4e/blob/0ec9dcfa2ff8fe32ef92d6a810ff497355441eec/haxe4e-plugin/src/main/java/org/haxe4e/launch/LaunchConfigLauncher.java#L133-L137

Any suggestions are highly appreciated.

sebthom commented 2 years ago

Same issue when using latest LSP4J 0.16.0:

Oct 17, 2022 11:40:30 PM org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer fireError
SEVERE: Failed making constructor 'java.lang.Void#Void()' accessible; either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: Unable to make private java.lang.Void() accessible: module java.base does not "opens java.lang" to unnamed module @159040aa
com.google.gson.JsonIOException: Failed making constructor 'java.lang.Void#Void()' accessible; either change its visibility or write a custom InstanceCreator or TypeAdapter for its declaring type: Unable to make private java.lang.Void() accessible: module java.base does not "opens java.lang" to unnamed module @159040aa
    at com.google.gson.internal.ConstructorConstructor$8.construct(ConstructorConstructor.java:233)
    at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:256)
    at com.google.gson.Gson.fromJson(Gson.java:1058)
    at com.google.gson.Gson.fromJson(Gson.java:1129)
    at org.eclipse.lsp4j.jsonrpc.debug.adapters.DebugMessageTypeAdapter.createMessage(DebugMessageTypeAdapter.java:392)
    at org.eclipse.lsp4j.jsonrpc.debug.adapters.DebugMessageTypeAdapter.read(DebugMessageTypeAdapter.java:255)
    at org.eclipse.lsp4j.jsonrpc.debug.adapters.DebugMessageTypeAdapter.read(DebugMessageTypeAdapter.java:157)
    at com.google.gson.Gson.fromJson(Gson.java:1058)
    at org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler.parseMessage(MessageJsonHandler.java:119)
    at org.eclipse.lsp4j.jsonrpc.json.MessageJsonHandler.parseMessage(MessageJsonHandler.java:114)
    at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:193)
    at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:94)
    at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:113)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
jonahgraham commented 2 years ago

@sebthom the issue here is the debug adapter is returning a non-void response body to configurationDone request.

LSP4J's implementation expects (and requires) that the debug adapter return void:

https://github.com/eclipse/lsp4j/blob/b8cdb316c1512134b6ebd3758abc2e3195227777/org.eclipse.lsp4j.debug/src/main/java/org/eclipse/lsp4j/debug/services/IDebugProtocolServer.java#L160

but in the case of the debug adapter you are working on it is returning:

{"allThreadsContinued":false}

which goes against what is documented in the DAP specification


I assume it works in VSCode. In VSCode the type system is more lenient. VSCode doesn't use the return value from configurationDone, but because the return value deserializes properly, it also doesn't complain.


What can LSP4J do?

It may be able to avoid the error and simply ignore the body. I have an idea for a PR, I will try that out and gather some feedback.


What can you do?

  1. File an issue against https://github.com/vshaxe/eval-debugger asking them not to return a value. The issue in their code is the line that casts the response so that it is filled with the extra return stuff which is really the response of a continue
  2. Try running the code with stopOnEntry as true to avoid the above.
jonahgraham commented 2 years ago

Ahh! A piece of the puzzle I missed before. Java 17! Which leaves you option 3 in what can you do:

  1. Until this issue is fixed, run with --add-opens java.base/java.lang=ALL-UNNAMED
sebthom commented 2 years ago

@jonahgraham thanks a lot for looking into this. that helps a lot.

Regarding the spec, it doesn't seem to be really clear. Response to configurationDone request. This is just an acknowledgement, so no body field is required. does not say that a response body is prohibited only that it is not required.

sebthom commented 2 years ago

Ahh! A piece of the puzzle I missed before. Java 17! Which leaves you option 3 in what can you do:

3. Until this issue is fixed, run with `--add-opens java.base/java.lang=ALL-UNNAMED`

Using this for now. Works. Thanks!

jonahgraham commented 2 years ago

Regarding the spec, it doesn't seem to be really clear. Response to configurationDone request. This is just an acknowledgement, so no body field is required. does not say that a response body is prohibited only that it is not required.

Yes, I think you are correct. The LSP4J implementation is overly restrictive in this regard.

jonahgraham commented 1 year ago

Regarding the spec, it doesn't seem to be really clear. Response to configurationDone request. This is just an acknowledgement, so no body field is required. does not say that a response body is prohibited only that it is not required.

Yes, I think you are correct. The LSP4J implementation is overly restrictive in this regard.

@sebthom FYI #721 was recently raised on this topic.