Skip to content

Commit bd1bee8

Browse files
authored
Merge pull request #32 from fwcd/jdk11-support
Support JDK 11+
2 parents 86f9fc2 + d641514 commit bd1bee8

File tree

14 files changed

+501
-1
lines changed

14 files changed

+501
-1
lines changed

.vscode/launch.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@
1414
"name": "Debug KotlinDebugAdapter",
1515
"projectRoot": "${workspaceFolder}/adapter",
1616
"mainClass": "org.javacs.ktda.KDAMainKt"
17+
},
18+
{
19+
"type": "kotlin",
20+
"request": "launch",
21+
"name": "Debug Sample Workspace",
22+
"projectRoot": "${workspaceFolder}/adapter/src/test/resources/sample-workspace",
23+
"mainClass": "sample.workspace.AppKt"
1724
}
1825
]
1926
}

adapter/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ dependencies {
2929
// The Java Debug Interface classes (com.sun.jdi.*)
3030
implementation files("${System.properties['java.home']}/../lib/tools.jar")
3131
testImplementation 'junit:junit:4.12'
32+
testImplementation 'org.hamcrest:hamcrest-all:1.3'
3233
}
3334

3435
task fixFilePermissions(type: Exec) {

adapter/src/main/kotlin/org/javacs/ktda/jdi/launch/JDILauncher.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ class JDILauncher(
7474
?: throw KotlinDAException("Could not find an attaching connector (for a new debuggee VM)")
7575

7676
private fun createLaunchConnector(): LaunchingConnector = vmManager.launchingConnectors()
77-
.firstOrNull() // TODO: Investigate whether this connector works fine on JDK 10+
77+
// Workaround for JDK 11+ where the first launcher (RawCommandLineLauncher) does not properly support args
78+
.let { it.find { it.javaClass.name == "com.sun.tools.jdi.SunCommandLineLauncher" } ?: it.firstOrNull() }
7879
?: throw KotlinDAException("Could not find a launching connector (for a new debuggee VM)")
7980

8081
private fun sourcesRootsOf(projectRoot: Path): Set<Path> = projectRoot.resolve("src")
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package org.javacs.ktda
2+
3+
import java.nio.file.Path
4+
import java.nio.file.Paths
5+
import org.eclipse.lsp4j.debug.ConfigurationDoneArguments
6+
import org.eclipse.lsp4j.debug.InitializeRequestArguments
7+
import org.eclipse.lsp4j.debug.DisconnectArguments
8+
import org.eclipse.lsp4j.debug.OutputEventArguments
9+
import org.eclipse.lsp4j.debug.services.IDebugProtocolClient
10+
import org.javacs.ktda.adapter.KotlinDebugAdapter
11+
import org.javacs.ktda.jdi.launch.JDILauncher
12+
import org.junit.After
13+
import org.junit.Assert.assertThat
14+
import org.junit.Before
15+
import org.hamcrest.Matchers.equalTo
16+
17+
abstract class DebugAdapterTestFixture(
18+
relativeWorkspaceRoot: String,
19+
private val mainClass: String
20+
) : IDebugProtocolClient {
21+
val absoluteWorkspaceRoot: Path = Paths.get(DebugAdapterTestFixture::class.java.getResource("/Anchor.txt").toURI()).parent.resolve(relativeWorkspaceRoot)
22+
lateinit var debugAdapter: KotlinDebugAdapter
23+
24+
@Before fun startDebugAdapter() {
25+
// Build the project first
26+
val process = ProcessBuilder("./gradlew", "assemble")
27+
.directory(absoluteWorkspaceRoot.toFile())
28+
.inheritIO()
29+
.start()
30+
process.waitFor()
31+
assertThat(process.exitValue(), equalTo(0))
32+
33+
debugAdapter = JDILauncher()
34+
.let(::KotlinDebugAdapter)
35+
.also {
36+
it.connect(this)
37+
val configDone = it.configurationDone(ConfigurationDoneArguments())
38+
it.initialize(InitializeRequestArguments().apply {
39+
adapterID = "test-debug-adapter"
40+
linesStartAt1 = true
41+
columnsStartAt1 = true
42+
}).join()
43+
// Slightly hacky workaround to ensure someone is
44+
// waiting on the ConfigurationDoneResponse. See
45+
// KotlinDebugAdapter.kt:performInitialization for
46+
// details.
47+
Thread {
48+
configDone.join()
49+
}.start()
50+
}
51+
}
52+
53+
fun launch() {
54+
println("Launching...")
55+
debugAdapter.launch(mapOf(
56+
"projectRoot" to absoluteWorkspaceRoot.toString(),
57+
"mainClass" to mainClass
58+
)).join()
59+
println("Launched")
60+
}
61+
62+
@After fun closeDebugAdapter() {
63+
debugAdapter.disconnect(DisconnectArguments()).join()
64+
}
65+
66+
override fun output(args: OutputEventArguments) {
67+
println(args.output)
68+
}
69+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package org.javacs.ktda
2+
3+
import org.eclipse.lsp4j.debug.ScopesArguments
4+
import org.eclipse.lsp4j.debug.SetBreakpointsArguments
5+
import org.eclipse.lsp4j.debug.Source
6+
import org.eclipse.lsp4j.debug.SourceBreakpoint
7+
import org.eclipse.lsp4j.debug.StackFrame
8+
import org.eclipse.lsp4j.debug.StackTraceArguments
9+
import org.eclipse.lsp4j.debug.StoppedEventArguments
10+
import org.eclipse.lsp4j.debug.VariablesArguments
11+
import org.junit.Assert.assertThat
12+
import org.junit.Test
13+
import org.hamcrest.Matchers.containsInAnyOrder
14+
import org.hamcrest.Matchers.equalTo
15+
import org.hamcrest.Matchers.hasItem
16+
import org.hamcrest.Matchers.nullValue
17+
import org.hamcrest.Matchers.not
18+
import java.util.concurrent.CountDownLatch
19+
20+
/**
21+
* Tests a very basic debugging scenario
22+
* using a sample application.
23+
*/
24+
class SampleWorkspaceTest : DebugAdapterTestFixture("sample-workspace", "sample.workspace.AppKt") {
25+
private val latch = CountDownLatch(1)
26+
private var asyncException: Throwable? = null
27+
28+
@Test fun testBreakpointsAndVariables() {
29+
debugAdapter.setBreakpoints(SetBreakpointsArguments().apply {
30+
source = Source().apply {
31+
name = "App.kt"
32+
path = absoluteWorkspaceRoot
33+
.resolve("src")
34+
.resolve("main")
35+
.resolve("kotlin")
36+
.resolve("sample")
37+
.resolve("workspace")
38+
.resolve("App.kt")
39+
.toString()
40+
}
41+
breakpoints = arrayOf(SourceBreakpoint().apply {
42+
line = 8
43+
})
44+
}).join()
45+
46+
launch()
47+
latch.await() // Wait for the breakpoint event to finish
48+
asyncException?.let { throw it }
49+
}
50+
51+
override fun stopped(args: StoppedEventArguments) {
52+
try {
53+
assertThat(args.reason, equalTo("breakpoint"))
54+
55+
// Query information about the debuggee's current state
56+
val stackTrace = debugAdapter.stackTrace(StackTraceArguments().apply {
57+
threadId = args.threadId
58+
}).join()
59+
val locals = stackTrace.stackFrames.asSequence().flatMap {
60+
debugAdapter.scopes(ScopesArguments().apply {
61+
frameId = it.id
62+
}).join().scopes.asSequence().flatMap {
63+
debugAdapter.variables(VariablesArguments().apply {
64+
variablesReference = it.variablesReference
65+
}).join().variables.asSequence()
66+
}
67+
}.toList()
68+
val receiver = locals.find { it.name == "this" }
69+
70+
assertThat(locals.map { Pair(it.name, it.value) }, hasItem(Pair("local", "123")))
71+
assertThat(receiver, not(nullValue()))
72+
73+
val members = debugAdapter.variables(VariablesArguments().apply {
74+
variablesReference = receiver!!.variablesReference
75+
}).join().variables
76+
77+
assertThat(members.map { Pair(it.name, it.value) }, containsInAnyOrder(Pair("member", "\"test\"")))
78+
} catch (e: Throwable) {
79+
asyncException = e
80+
} finally {
81+
latch.countDown()
82+
}
83+
}
84+
}

adapter/src/test/resources/Anchor.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Anchor for finding the test/resources directory.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#
2+
# https://help.github.com/articles/dealing-with-line-endings/
3+
#
4+
# These are explicitly windows files and should use crlf
5+
*.bat text eol=crlf
6+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
plugins {
2+
id 'org.jetbrains.kotlin.jvm' version '1.3.71'
3+
id 'application'
4+
}
5+
6+
repositories {
7+
jcenter()
8+
}
9+
10+
dependencies {
11+
// Align versions of all Kotlin components
12+
implementation platform('org.jetbrains.kotlin:kotlin-bom')
13+
// Use the Kotlin JDK 8 standard library.
14+
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
15+
}
16+
17+
application {
18+
// Define the main class for the application.
19+
mainClassName = 'sample.workspace.AppKt'
20+
}
Binary file not shown.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
distributionBase=GRADLE_USER_HOME
2+
distributionPath=wrapper/dists
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip
4+
zipStoreBase=GRADLE_USER_HOME
5+
zipStorePath=wrapper/dists

0 commit comments

Comments
 (0)