svaante / dape

Debug Adapter Protocol for Emacs
GNU General Public License v3.0
448 stars 25 forks source link

Java Debug Server sends stacktraces that dape can not handle #78

Closed MagielBruntink closed 5 months ago

MagielBruntink commented 5 months ago

Java unit tests can be debugged by instructing Dape to attach JDTLS + Java Debug Server plugin to the JVM executing the tests. This works fine for the most part, except for the "Stack" window, which remains empty. It appears that the Java Debug Server sends (quite long) stacktrace responses that Dape does not handle well.

Attached is a full example of such a stacktrace response.

The repro of this is quite involved. If needed, I can create a test project and instructions to run these debug sessions, but perhaps it's clear enough what the problem is by looking at the stacktrace response.

stacktrace.json

The error message printed by Dape is as follows:

Error running timer: (wrong-type-argument (or eieio-object cl-structure-object oclosure) nil)
svaante commented 5 months ago

Hey seams like the same issue as #51 but now I got something to work with.

The "path" property stands out.

      {
        "column": 1,
        "id": 2,
        "line": -1,
        "name": "NativeMethodAccessorImpl.invoke0(Method,Object,Object[])[native method]",
        "presentationHint": "subtle",
        "source": {
          "name": "NativeMethodAccessorImpl.java",
          "path": "jdt://contents/java.base/jdk.internal.reflect/NativeMethodAccessorImpl.class?=jdt.ls-java-project/C:%5C/Tools%5C/Java%5C/jdk-17.0.6+10%5C/lib%5C/jrt-fs.jar%60java.base=/javadoc_location=/https:%5C/%5C/docs.oracle.com%5C/en%5C/java%5C/javase%5C/17%5C/docs%5C/api%5C/=/%3Cjdk.internal.reflect(NativeMethodAccessorImpl.class",
          "sourceReference": 0
        }
      },

I'll see what I can come up with

MagielBruntink commented 5 months ago

I can create a repro config if you want.

MagielBruntink commented 5 months ago

Here is the repro, check the repro.el file for instructions: https://github.com/MagielBruntink/test-project

svaante commented 5 months ago

Thank you, it helped a ton.

The issue is caused by is in eglot-java.

It registers an file-name-handler for "jdt" and if the lookup is not done inside of a buffer where eglot is running with jdtls it errors.

  1. dape invokes tramp-file-local-name
  2. eglot-java assumes that current buffer has eglot running with jdtls or fails
(defun eglot-java--jdt-uri-handler (_operation &rest args)
  "Support Eclipse jdtls `jdt://' uri scheme."
  (let* ((uri (car args))
         (cache-dir (expand-file-name ".eglot-java" (temporary-file-directory)))
         (source-file
          (expand-file-name
           (eglot-java--make-path
            cache-dir
            (save-match-data
              (when (string-match "jdt://contents/\\(.*?\\)/\\(.*\\)\.class\\?" uri)
                (format "%s.java" (replace-regexp-in-string "/" "." (match-string 2 uri) t t))))))))
    (unless (file-readable-p source-file)
       ;;`eglot-current-server' returns nil here, causing *dape-info Stack* to fail printing stack
      (let ((content (jsonrpc-request (eglot-current-server) :java/classFileContents (list :uri uri)))
            (metadata-file (format "%s.%s.metadata"
                                   (file-name-directory source-file)
                                   (file-name-base source-file))))
        (unless (file-directory-p cache-dir) (make-directory cache-dir t))
        (with-temp-file source-file (insert content))
        (with-temp-file metadata-file (insert uri))))
    source-file))

I would suggest that eglot-java tries to find a eglot instance in an more robust way then eglot-current-server

MagielBruntink commented 5 months ago

Thank you for debugging! Surprising outcome. I'll investigate further and see how to get this fixed on the eglot-java side.