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()
}