Skip to content

Commit 2b64b6c

Browse files
author
Tobias Bordenca
committed
Add language server feature to handle decompile requests
1 parent e0bd562 commit 2b64b6c

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

language-server/src/dotty/tools/languageserver/DottyLanguageServer.scala

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,13 @@ import reporting._, reporting.diagnostic.{Message, MessageContainer, messages}
2626
import typer.Typer
2727
import util.{Set => _, _}
2828
import interactive._, interactive.InteractiveDriver._
29+
import decompiler.IDEDecompilerDriver
2930
import Interactive.Include
3031
import config.Printers.interactiv
3132

3233
import languageserver.config.ProjectConfig
33-
import languageserver.worksheet.{Worksheet, WorksheetClient, WorksheetService}
34+
import languageserver.worksheet.{Worksheet, WorksheetService}
35+
import languageserver.decompiler.{TastyDecompilerService}
3436

3537
import lsp4j.services._
3638

@@ -43,7 +45,7 @@ import lsp4j.services._
4345
* - This implementation is based on the LSP4J library: https://github.com/eclipse/lsp4j
4446
*/
4547
class DottyLanguageServer extends LanguageServer
46-
with TextDocumentService with WorkspaceService with WorksheetService { thisServer =>
48+
with TextDocumentService with WorkspaceService with WorksheetService with TastyDecompilerService { thisServer =>
4749
import ast.tpd._
4850

4951
import DottyLanguageServer._
@@ -128,6 +130,25 @@ class DottyLanguageServer extends LanguageServer
128130
drivers(configFor(uri))
129131
}
130132

133+
/** The driver instance responsible for decompiling `uri` in `classPath` */
134+
def decompilerDriverFor(uri: URI, classPath: String): IDEDecompilerDriver = thisServer.synchronized {
135+
val config = configFor(uri)
136+
val defaultFlags = List("-color:never")
137+
138+
implicit class updateDeco(ss: List[String]) {
139+
def update(pathKind: String, pathInfo: String) = {
140+
val idx = ss.indexOf(pathKind)
141+
val ss1 = if (idx >= 0) ss.take(idx) ++ ss.drop(idx + 2) else ss
142+
ss1 ++ List(pathKind, pathInfo)
143+
}
144+
}
145+
val settings =
146+
defaultFlags ++
147+
config.compilerArguments.toList
148+
.update("-classpath", (classPath +: config.dependencyClasspath).mkString(File.pathSeparator))
149+
new IDEDecompilerDriver(settings)
150+
}
151+
131152
/** A mapping from project `p` to the set of projects that transitively depend on `p`. */
132153
def dependentProjects: Map[ProjectConfig, Set[ProjectConfig]] = thisServer.synchronized {
133154
if (myDependentProjects == null) {
@@ -184,7 +205,8 @@ class DottyLanguageServer extends LanguageServer
184205
rootUri = params.getRootUri
185206
assert(rootUri != null)
186207

187-
class DottyServerCapabilities(val worksheetRunProvider: Boolean = true) extends lsp4j.ServerCapabilities
208+
class DottyServerCapabilities(val worksheetRunProvider: Boolean = true,
209+
val tastyDecompiler: Boolean = true) extends lsp4j.ServerCapabilities
188210

189211
val c = new DottyServerCapabilities
190212
c.setTextDocumentSync(TextDocumentSyncKind.Full)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dotty.tools.languageserver.decompiler
2+
3+
import org.eclipse.lsp4j.TextDocumentIdentifier
4+
5+
// All case classes in this file should have zero-parameters secondary
6+
// constructors to allow Gson to reflectively create instances on
7+
// deserialization without relying on sun.misc.Unsafe.
8+
9+
/** The parameter for the `tasty/decompile` request. */
10+
case class TastyDecompileParams(textDocument: TextDocumentIdentifier) {
11+
def this() = this(null)
12+
}
13+
14+
/** The response to a `tasty/decompile` request. */
15+
case class TastyDecompileResult(tastyTree: String = null, scala: String = null, error: Int = 0) {
16+
def this() = this(null, null, 0)
17+
}
18+
19+
object TastyDecompileResult {
20+
val ErrorTastyVersion = 1
21+
val ErrorClassNotFound = 2
22+
val ErrorOther = -1
23+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package dotty.tools
2+
package languageserver
3+
package decompiler
4+
5+
import java.net.URI
6+
import java.nio.file._
7+
import java.util.concurrent.CompletableFuture
8+
9+
import dotc.core.tasty.TastyUnpickler.UnpickleException
10+
import dotc.fromtasty.TastyFileUtil
11+
12+
import org.eclipse.lsp4j.jsonrpc.services._
13+
14+
15+
@JsonSegment("tasty")
16+
trait TastyDecompilerService {
17+
thisServer: DottyLanguageServer =>
18+
19+
@JsonRequest
20+
def decompile(params: TastyDecompileParams): CompletableFuture[TastyDecompileResult] =
21+
computeAsync(synchronize = false, fun = { cancelChecker =>
22+
val uri = new URI(params.textDocument.getUri)
23+
try {
24+
TastyFileUtil.getClassName(Paths.get(uri)) match {
25+
case Some((classPath, className)) =>
26+
val driver = thisServer.decompilerDriverFor(uri, classPath)
27+
28+
val (tree, source) = driver.run(className)
29+
30+
TastyDecompileResult(tree, source)
31+
case _ =>
32+
TastyDecompileResult(error = TastyDecompileResult.ErrorClassNotFound)
33+
}
34+
} catch {
35+
case _: UnpickleException =>
36+
TastyDecompileResult(error = TastyDecompileResult.ErrorTastyVersion)
37+
case t: Throwable =>
38+
t.printStackTrace()
39+
TastyDecompileResult(error = TastyDecompileResult.ErrorOther)
40+
}
41+
})
42+
}

0 commit comments

Comments
 (0)