Skip to content

Commit 4cca647

Browse files
committed
Updated error reporting
1 parent 0d1fcc4 commit 4cca647

File tree

4 files changed

+79
-123
lines changed

4 files changed

+79
-123
lines changed
Lines changed: 69 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,103 +1,98 @@
11
package processing.app.gradle
22

3-
import com.sun.jdi.ObjectReference
3+
import com.sun.jdi.Location
44
import com.sun.jdi.StackFrame
5-
import com.sun.jdi.StringReference
65
import com.sun.jdi.VirtualMachine
76
import com.sun.jdi.event.ExceptionEvent
87
import com.sun.jdi.request.EventRequest
9-
import kotlinx.coroutines.CoroutineScope
10-
import kotlinx.coroutines.Dispatchers
118
import kotlinx.coroutines.delay
12-
import kotlinx.coroutines.launch
139
import processing.app.Messages
10+
import processing.app.SketchException
11+
import processing.app.ui.Editor
1412

1513
// TODO: Consider adding a panel to the footer
16-
class Exceptions {
17-
companion object {
18-
suspend fun listen(vm: VirtualMachine) {
19-
try {
20-
val manager = vm.eventRequestManager()
14+
class Exceptions (val vm: VirtualMachine, val editor: Editor?) {
15+
suspend fun listen() {
16+
try {
17+
val manager = vm.eventRequestManager()
2118

22-
val request = manager.createExceptionRequest(null, false, true)
23-
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD)
24-
request.enable()
19+
val request = manager.createExceptionRequest(null, false, true)
20+
request.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD)
21+
request.enable()
2522

26-
val queue = vm.eventQueue()
27-
while (true) {
28-
val eventSet = queue.remove()
29-
for (event in eventSet) {
30-
if (event is ExceptionEvent) {
31-
printExceptionDetails(event)
32-
event.thread().resume()
33-
}
23+
val queue = vm.eventQueue()
24+
while (true) {
25+
val eventSet = queue.remove()
26+
for (event in eventSet) {
27+
if (event is ExceptionEvent) {
28+
printExceptionDetails(event)
29+
event.thread().resume()
3430
}
35-
eventSet.resume()
36-
delay(10)
3731
}
38-
} catch (e: Exception) {
39-
Messages.log("Error while listening for exceptions: ${e.message}")
32+
eventSet.resume()
33+
delay(10)
4034
}
35+
} catch (e: Exception) {
36+
Messages.log("Error while listening for exceptions: ${e.message}")
4137
}
38+
}
4239

43-
fun printExceptionDetails(event: ExceptionEvent) {
44-
val exception = event.exception()
45-
val thread = event.thread()
46-
val location = event.location()
47-
val stackFrames = thread.frames()
40+
fun printExceptionDetails(event: ExceptionEvent) {
41+
val exception = event.exception()
42+
val thread = event.thread()
43+
val location = event.location().mapToPdeFile()
44+
val stackFrames = thread.frames()
4845

49-
println("\n🚨 Exception Caught 🚨")
50-
println("Type : ${exception.referenceType().name()}")
51-
// TODO: Fix exception message retrieval
52-
// println("Message : ${getExceptionMessage(exception)}")
53-
println("Thread : ${thread.name()}")
54-
println("Location : ${location.sourcePath()}:${location.lineNumber()}\n")
46+
val (processingFrames, userFrames) = stackFrames
47+
.map{
48+
val location = it.location().mapToPdeFile()
49+
val method = location.method()
50+
it to "${method.declaringType().name()}.${method.name()}() @ ${location.sourcePath()}:${location.lineNumber()}"
51+
}
52+
.partition {
53+
it.first.location().declaringType().name().startsWith("processing.")
54+
}
5555

56-
// TODO: Map to .pde file again
57-
// TODO: Communicate back to Editor
56+
/*
57+
We have 6 lines by default within the editor to display more information about the exception.
58+
*/
5859

59-
// Separate stack frames
60-
val userFrames = mutableListOf<StackFrame>()
61-
val processingFrames = mutableListOf<StackFrame>()
60+
val message = """
61+
In Processing code:
62+
#processingFrames
63+
64+
In your code:
65+
#userFrames
66+
67+
"""
68+
.trimIndent()
69+
.replace("#processingFrames", processingFrames.joinToString("\n ") { it.second })
70+
.replace("#userFrames", userFrames.joinToString("\n ") { it.second })
6271

63-
stackFrames.forEach { frame ->
64-
val className = frame.location().declaringType().name()
65-
if (className.startsWith("processing.")) {
66-
processingFrames.add(frame)
67-
} else {
68-
userFrames.add(frame)
69-
}
70-
}
72+
val error = """
73+
Exception: ${exception.referenceType().name()} @ ${location.sourcePath()}:${location.lineNumber()}
74+
""".trimIndent()
7175

72-
// Print user frames first
73-
println("🔍 Stacktrace (Your Code First):")
74-
userFrames.forEachIndexed { index, frame -> printStackFrame(index, frame) }
76+
println(message)
77+
System.err.println(error)
7578

76-
// Print Processing frames second
77-
if (processingFrames.isNotEmpty()) {
78-
println("\n🔧 Processing Stacktrace (Hidden Initially):")
79-
processingFrames.forEachIndexed { index, frame -> printStackFrame(index, frame) }
80-
}
79+
editor?.statusError(exception.referenceType().name())
80+
}
8181

82-
println("──────────────────────────────────\n")
83-
}
82+
fun Location.mapToPdeFile(): Location {
83+
if(editor == null) return this
8484

85-
fun printStackFrame(index: Int, frame: StackFrame) {
86-
val location = frame.location()
87-
val method = location.method()
88-
println(
89-
" #$index ${location.sourcePath()}:${location.lineNumber()} -> ${
90-
method.declaringType().name()
91-
}.${method.name()}()"
92-
)
85+
// Check if the source is a .java file
86+
val sketch = editor.sketch
87+
sketch.code.forEach { code ->
88+
if(code.extension != "java") return@forEach
89+
if(sourceName() != code.fileName) return@forEach
90+
return@mapToPdeFile this
9391
}
9492

95-
// Extracts the exception's message
96-
fun getExceptionMessage(exception: ObjectReference): String {
97-
val messageMethod = exception.referenceType().methodsByName("getMessage").firstOrNull() ?: return "Unknown"
98-
val messageValue =
99-
exception.invokeMethod(null, messageMethod, emptyList(), ObjectReference.INVOKE_SINGLE_THREADED)
100-
return (messageValue as? StringReference)?.value() ?: "Unknown"
101-
}
93+
// TODO: Map to .pde file again, @see JavaBuild.placeException
94+
// BLOCKED: Because we don't run the JavaBuild code.prepocOffset is empty
95+
96+
return this
10297
}
10398
}

app/src/processing/app/gradle/GradleJob.kt

Lines changed: 6 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ class GradleJob{
4242
private val scope = CoroutineScope(Dispatchers.IO)
4343
private val cancel = GradleConnector.newCancellationTokenSource()
4444

45-
private val outputStream = PipedOutputStream()
46-
private val errorStream = PipedOutputStream()
47-
4845
fun start() {
4946
val folder = service?.sketch?.folder ?: throw IllegalStateException("Sketch folder is not set")
5047
scope.launch {
@@ -60,8 +57,10 @@ class GradleJob{
6057
withCancellationToken(cancel.token())
6158
addStateListener()
6259
addDebugging()
63-
setStandardOutput(outputStream)
64-
setStandardError(errorStream)
60+
if(Base.DEBUG) {
61+
setStandardOutput(System.out)
62+
setStandardError(System.err)
63+
}
6564
run()
6665
}
6766
}catch (e: Exception){
@@ -71,49 +70,6 @@ class GradleJob{
7170
vm.value = null
7271
}
7372
}
74-
// TODO: I'm sure this can be done better
75-
scope.launch {
76-
try {
77-
InputStreamReader(PipedInputStream(outputStream)).buffered().use { reader ->
78-
reader.lineSequence()
79-
.forEach { line ->
80-
if (cancel.token().isCancellationRequested) {
81-
return@launch
82-
}
83-
if (state.value != State.RUNNING) {
84-
return@forEach
85-
}
86-
service?.out?.println(line)
87-
}
88-
}
89-
}catch (e: Exception){
90-
Messages.log("Error while reading output: ${e.message}")
91-
}
92-
}
93-
scope.launch {
94-
try {
95-
InputStreamReader(PipedInputStream(errorStream)).buffered().use { reader ->
96-
reader.lineSequence()
97-
.forEach { line ->
98-
if (cancel.token().isCancellationRequested) {
99-
return@launch
100-
}
101-
if (state.value != State.RUNNING) {
102-
return@forEach
103-
}
104-
when{
105-
line.contains("+[IMKClient subclass]: chose IMKClient_Modern") -> return@forEach
106-
line.contains("+[IMKInputSession subclass]: chose IMKInputSession_Modern") -> return@forEach
107-
line.startsWith("__MOVE__") -> return@forEach
108-
else -> service?.err?.println(line)
109-
}
110-
}
111-
}
112-
}catch (e: Exception){
113-
Messages.log("Error while reading error: ${e.message}")
114-
}
115-
}
116-
11773
}
11874

11975
fun cancel(){
@@ -169,7 +125,8 @@ class GradleJob{
169125
scope.launch {
170126
val debugger = Debugger.connect(service?.debugPort) ?: return@launch
171127
vm.value = debugger
172-
Exceptions.listen(debugger)
128+
val exceptions = Exceptions(debugger, service?.editor)
129+
exceptions.listen()
173130
}
174131

175132
})

app/src/processing/app/gradle/GradleService.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import kotlin.io.path.writeText
2020
// TODO: Test running examples
2121
// TODO: Report failures to the console
2222
// TODO: Highlight errors in the editor
23+
// TODO: Stop running sketches if modern build system is turned off
2324

2425
// TODO: ---- FUTURE ----
2526
// TODO: Improve progress tracking
@@ -114,6 +115,7 @@ class GradleService(
114115
"fullscreen" to false, // TODO: Implement
115116
"display" to 1, // TODO: Implement
116117
"external" to true,
118+
"location" to null, // TODO: Implement
117119
"editor.location" to editor?.location?.let { "${it.x},${it.y}" },
118120
//"awt.disable" to false,
119121
//"window.color" to "0xFF000000", // TODO: Implement

java/gradle/src/main/kotlin/DependenciesTask.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ abstract class DependenciesTask: DefaultTask() {
5252
}
5353
project.dependencies.add("implementation", project.files(dependencies) )
5454

55+
// TODO: Mutating the dependencies of configuration ':implementation' after it has been resolved or consumed. This
56+
5557
// TODO: Add only if user is compiling for P2D or P3D
5658
// Add JOGL and Gluegen dependencies
5759
project.dependencies.add("runtimeOnly", "org.jogamp.jogl:jogl-all-main:2.5.0")

0 commit comments

Comments
 (0)