Exit Full View

Feather2 / feather2-foocad / src / test / kotlin / uk.co.nickthecoder / feather / foocad / FoocadUtils.kt

package uk.co.nickthecoder.feather.foocad

import org.objectweb.asm.ClassReader
import org.objectweb.asm.util.Textifier
import org.objectweb.asm.util.TraceClassVisitor
import uk.co.nickthecoder.feather.core.*
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.reflect.Method
import java.lang.reflect.Modifier

/**
 * The Feather configuration for compiling the FooCAD extensions.
 */
val extensionsConfig = CompileExtensions.createConfig()

/**
 * The Feather configuration for compiling FooCAD scripts.
 * This first compiles all the extensions, and uses the resulting classloader.
 */
val scriptConfig: FeatherConfiguration by lazy {
    val extensionsResults = CompileExtensions.compileExtensions()
    extensionsResults.configuration.clone().apply {
        allowIncludes = true
        openByDefault = true
        classLoader = extensionsResults.classLoader
    }
}

internal fun FeatherConfiguration.compile(vararg strings: String): CompilationResults {

    val compiler = FeatherCompiler(this)
    return compiler.compile(strings.withIndex().map {
        StringFeatherScript(it.value, "script#${it.index + 1}")
    })

}

/**
 * Runs the `test` method, returning the value.
 * No exception is expected.
 */
internal fun FeatherConfiguration.runTest(vararg strings: String): Any? {

    val results = compile(*strings)
    val classLoader = results.classLoader
    val classesMap = results.allBytecode

    var method: Method? = null
    for (className in classesMap.keys) {
        val klass = classLoader.loadClass(className)
        try {
            method = klass.getMethod("test")
            break
        } catch (e: Throwable) {
            // e.printStackTrace()
            // Do nothing
        }
    }


    if (method == null) {
        dumpByteCode(classesMap)
        throw Exception("No test method found")
    } else {
        return if (Modifier.isStatic(method.modifiers)) {
            method.invoke(null)
        } else {
            val constructor = method.declaringClass.getConstructor()
            val instance = constructor.newInstance()
            method.invoke(instance)
        }
    }
}


fun dumpByteCode(classesMap: Map<String, ByteArray>) {
    for (byteCode in classesMap.values) {
        println(byteCodeToString(byteCode))
    }
}

fun byteCodeToString(byteCode: ByteArray): String {
    val parsingOptions = ClassReader.SKIP_DEBUG // or 0

    val writer = StringWriter()
    val visitor = TraceClassVisitor(null, Textifier(), PrintWriter(writer, true))
    ClassReader(byteCode).accept(visitor, parsingOptions)
    return writer.toString()
}