All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.org.openrndr.exceptions.ExceptionSanitizer.kt Maven / Gradle / Ivy

The newest version!
package org.openrndr.exceptions

import io.github.oshai.kotlinlogging.KotlinLogging
import org.openrndr.platform.Platform
import org.openrndr.platform.PlatformType
import java.lang.Thread.UncaughtExceptionHandler

private val logger = KotlinLogging.logger(name = "")


private class SanitizingUncaughtExceptionHandler : UncaughtExceptionHandler {
    override fun uncaughtException(t: Thread, e: Throwable) { // Write the custom logic here
        findUserCause(e)
    }
}

fun installUncaughtExceptionHandler() {
    if (Platform.type != PlatformType.WINDOWS) {
        System.err.print(color(0x7f, 0x7f, 0x7f))
        if (System.getProperty("org.openrndr.exceptions") == "pretty") {
            Thread.setDefaultUncaughtExceptionHandler(SanitizingUncaughtExceptionHandler())
        }
    }
}


private fun color(r: Int, g: Int, b: Int): String {
    return String.format("\u001b[38;2;%d;%d;%dm", r, g, b)
}

private fun colorReset(): String {
    return "\u001b[0m"
}

private fun cleanClassName(name: String): String {
    return name.replace(("Kt$"), ".").replace(Regex("\\$[0-9]+"), ".{ }").replace("$", ".")
}

private fun cleanMethodName(name: String): String {
    return if (name == "invoke") {
        ".{ }"
    } else {
        ".$name"
    }
}

fun findUserCause(throwable: Throwable) {
    logger.info { "Set -Dorg.openrndr.exceptions=JVM for conventional exception-handling" }

    val bestSolution = throwable.stackTrace.reversed().indexOfLast { !(it.className.contains("org.openrndr")) }

    System.err.println()
    System.err.println()
    throwable.stackTrace.reversed().forEachIndexed { index, it ->
        throwable.stackTrace
        val parts = it.className.split("$")

        //println("--- decomposing ${it.className}")
        var query = parts[0]
        val lambdaReceiverTypes = mutableListOf()

        for (part in parts.drop(1)) {
            query += "$$part"
            try {
                val cl = Class.forName(query)
                if (cl != null) {
                    if (cl.superclass.typeName == "kotlin.jvm.internal.Lambda") {
                        val annotation = cl.getAnnotation(Metadata::class.java)
                        if (annotation != null) {
                            val types = annotation.data2[2].split(";").filter { it.isNotBlank() }.map {
                                it.split("/").last()
                            }.joinToString(", ")

                            lambdaReceiverTypes.add(types)
                        }
                    }
                } else {
                    //println("no class for $query")
                }
            } catch (e: ClassNotFoundException) {
                //println("no such class $query")
            }
        }

        var filledName = cleanClassName(it.className)
        for (receiver in lambdaReceiverTypes) {
            filledName = filledName.replaceFirst("{ }", "{ :$receiver }")
        }

        val cleanName = "${filledName}${cleanMethodName(it.methodName)}"
        if (!it.className.contains("org.openrndr") && (it.lineNumber >= 0)) {
            if (index == bestSolution) {
                System.err.println("${color(0x7f, 0x7f, 0x7f)}├─ ${color(0xff, 0xc0, 0xcb)}${cleanName}(${it.fileName}:${it.lineNumber})${colorReset()}")
            } else {
                System.err.println("${color(0x7f, 0x7f, 0x7f)}├─ ${color(0x7f, 0x7f, 0x7f)}${cleanName}(${it.fileName}:${it.lineNumber})${colorReset()}")
            }
        } else {
            System.err.println("${color(0x7f, 0x7f, 0x7f)}│  ${color(0x4f, 0x4f, 0x4f)}${cleanName}(${it.fileName}:${it.lineNumber})${colorReset()}")
        }
    }
    System.err.println("${color(0x7f, 0x7f, 0x7f)}│${colorReset()}")
    System.err.println("${color(0x7f, 0x7f, 0x7f)}↑ ${color(0xff, 0xc0, 0xcb)}${throwable.message} (${throwable::class.simpleName})${colorReset()} ")

    var cause = throwable.cause
    while (cause != null) {
        val line = cause.stackTrace.first()
        val filledName = cleanClassName(line.className)
        val cleanName = "${filledName}${cleanMethodName(line.methodName)}"
        System.err.println("${color(0x7f, 0x7f, 0x7f)}├─ ${color(0x7f, 0x7f, 0x7f)}${cleanName}(${line.fileName}:${line.lineNumber})${colorReset()}")
        cause = cause.cause
    }

    if (throwable is NoSuchMethodError || throwable is ClassNotFoundException) {
        if (throwable.message?.contains("org.openrndr") == true) {
            System.err.println()
            logger.error {
                "You are likely using incompatible versions of OPENRNDR and ORX. Fix imports and make sure to clean and rebuild your project."
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy