Main interpreter loop implemented

This commit is contained in:
Andrey Breslav
2013-10-06 13:33:15 +04:00
parent 2e374df5a9
commit 7559c3bd94
3 changed files with 150 additions and 0 deletions
@@ -341,6 +341,12 @@
<item name='org.objectweb.asm.tree.MethodInsnNode void accept(org.objectweb.asm.MethodVisitor) 0'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.MethodNode desc'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.MethodNode instructions'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
<item name='org.objectweb.asm.tree.MethodNode org.objectweb.asm.AnnotationVisitor visitAnnotation(java.lang.String, boolean)'>
<annotation name='org.jetbrains.annotations.NotNull'/>
</item>
+139
View File
@@ -0,0 +1,139 @@
package org.jetbrains.eval4j
import org.objectweb.asm.tree.LabelNode
import org.objectweb.asm.tree.AbstractInsnNode
import org.objectweb.asm.tree.analysis.Frame
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.Type
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.tree.analysis.Interpreter
import org.objectweb.asm.tree.JumpInsnNode
import org.objectweb.asm.tree.VarInsnNode
trait InterpreterResult
object NOTHING_DONE: InterpreterResult
class ExceptionThrown(val exception: Value): InterpreterResult
class ValueReturned(val result: Value): InterpreterResult
class AbnormalTermination(val message: String): InterpreterResult
trait InterpretationEventHandler {
// If a non-null value is returned, interpreter loop is terminated and that value is used as a result
fun instructionProcessed(insn: AbstractInsnNode): InterpreterResult?
fun exceptionThrown(currentState: Frame<Value>, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult?
fun exceptionCaught(currentState: Frame<Value>, currentInsn: AbstractInsnNode, exception: Value): InterpreterResult?
}
fun interpreterLoop(
ownerClassInternalName: String,
m: MethodNode,
eval: Eval,
instructionHandler: (AbstractInsnNode) -> InterpreterResult?
): InterpreterResult {
val firstInsn = m.instructions.getFirst()
if (firstInsn == null) return NOTHING_DONE
var currentInsn = firstInsn!!
fun goto(nextInsn: AbstractInsnNode?) {
if (nextInsn == null) throw IllegalArgumentException("Instruction flow ended with no RETURN")
currentInsn = nextInsn
}
val interpreter = SingleInstructionInterpreter(eval)
try {
var frame = initFrame(ownerClassInternalName, m, interpreter)
while (true) {
// TODO try-catch-finally support
// TODO support exceptions thrown by eval
val insnOpcode = currentInsn.getOpcode()
val insnType = currentInsn.getType()
when (insnType) {
AbstractInsnNode.LABEL,
AbstractInsnNode.FRAME,
AbstractInsnNode.LINE -> {
// skip to the next instruction
}
else -> when (insnOpcode) {
GOTO -> {
goto((currentInsn as JumpInsnNode).label)
continue
}
RET -> {
val varNode = currentInsn as VarInsnNode
val address = frame.getLocal(varNode.`var`)
goto((address as LabelValue).value)
continue
}
// TODO: switch
LOOKUPSWITCH -> UnsupportedByteCodeException("LOOKUPSWITCH is not supported yet")
TABLESWITCH -> UnsupportedByteCodeException("TABLESWITCH is not supported yet")
IRETURN, LRETURN, FRETURN, DRETURN, ARETURN -> return ValueReturned(frame.getStack(0)!!)
RETURN -> return ValueReturned(VOID_VALUE)
IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IFNULL, IFNONNULL -> {
if (interpreter.checkUnaryCondition(frame.getStack(0)!!, insnOpcode)) {
goto((currentInsn as JumpInsnNode).label)
continue
}
}
IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> {
if (interpreter.checkBinaryCondition(frame.getStack(0)!!, frame.getStack(1)!!, insnOpcode)) {
goto((currentInsn as JumpInsnNode).label)
continue
}
}
// TODO: try/catch/finally
ATHROW -> return ExceptionThrown(frame.getStack(0)!!)
}
}
frame.execute(currentInsn, interpreter)
val handled = instructionHandler(currentInsn)
if (handled != null) return handled
goto(currentInsn.getNext())
}
}
catch (e: Throwable) {
throw e
}
}
// Copied from org.objectweb.asm.tree.analysis.Analyzer.analyze()
fun <V : org.objectweb.asm.tree.analysis.Value> initFrame(
owner: String,
m: MethodNode,
interpreter: Interpreter<V>
): Frame<V> {
val current = Frame<V>(m.maxLocals, m.maxStack)
current.setReturn(interpreter.newValue(Type.getReturnType(m.desc)))
var local = 0
if ((m.access and ACC_STATIC) == 0) {
val ctype = Type.getObjectType(owner)
current.setLocal(local++, interpreter.newValue(ctype))
}
val args = Type.getArgumentTypes(m.desc)
for (i in 0..args.size - 1) {
current.setLocal(local++, interpreter.newValue(args[i]))
if (args[i].getSize() == 2) {
current.setLocal(local++, interpreter.newValue(null))
}
}
while (local < m.maxLocals) {
current.setLocal(local++, interpreter.newValue(null))
}
return current
}
+5
View File
@@ -21,6 +21,11 @@ object NOT_A_VALUE: Value {
override fun getSize(): Int = 1
}
object VOID_VALUE: Value {
override val asmType: Type = Type.VOID_TYPE
override val valid: Boolean = false
}
class NotInitialized(override val asmType: Type): Value {
override val valid = false
}