11package processing.app.gradle
22
3- import com.sun.jdi.ObjectReference
3+ import com.sun.jdi.Location
44import com.sun.jdi.StackFrame
5- import com.sun.jdi.StringReference
65import com.sun.jdi.VirtualMachine
76import com.sun.jdi.event.ExceptionEvent
87import com.sun.jdi.request.EventRequest
9- import kotlinx.coroutines.CoroutineScope
10- import kotlinx.coroutines.Dispatchers
118import kotlinx.coroutines.delay
12- import kotlinx.coroutines.launch
139import 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}
0 commit comments