Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,52 @@
package edu.colorado.plv.cuanto.scoot.concrete_interpreter

import com.sun.org.glassfish.external.amx.MBeanListener.CallbackImpl
import edu.colorado.plv.cuanto.scoot.concrete_interpreter

/**
* @author Shawn Meier
* Created on 9/29/17.
*/
object ConcreteMemory {}
object ConcreteMemory {
def div(v1 : CValue, v2: CValue) = (v1,v2) match{
case (CInteger(i1), CInteger(i2)) => CInteger(i1/i2)
case _ => ???
}
def mul(v1: CValue, v2: CValue) = (v1,v2) match{
case (CInteger(i1), CInteger(i2)) => CInteger(i1*i2)
case _ => ???
}

def add(v1 : CValue, v2 : CValue) = (v1,v2) match{
case (CInteger(i1), CInteger(i2)) => CInteger(i1+i2)
case _ => ???
}
def sub(v1 :CValue, v2 :CValue) = (v1,v2) match{
case (CInteger(i1), CInteger(i2)) => CInteger(i1 - i2)
}
def neg(v1 : CValue) = v1 match{
case CInteger(i1) => CInteger(-i1)
case _ => ???
}
def equ(v1 :CValue, v2 :CValue) = (v1,v2) match{
case (CInteger(i1),CInteger(i2)) => booleanToInteger(i1 == i2)
case _ => ???
}
def nequ(v1 :CValue, v2: CValue) = (v1,v2) match{
case (CInteger(i1), CInteger(i2)) => booleanToInteger(i1 != i2)
case _ => ???
}
def ge(v1 :CValue, v2: CValue) = (v1,v2) match{
case (CInteger(i1), CInteger(i2)) => booleanToInteger(i1 >= i2)
case _ => ???
}
def booleanToInteger(b : Boolean): CInteger = if(b) CInteger(1) else CInteger(0)
def isZero(a: CValue): Boolean = a match{
case CInteger(0) => true
case CInteger(i) => false
case _ => ???
}
}

sealed trait CValue
sealed trait CPrimitive extends CValue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,46 +13,57 @@ import scala.util.Try
*/
object Interpreter {

case class StackFrame(returnLocation: Option[(Body, Stmt)], locals : Map[String, CValue], returnValueLocation : Option[Local])

/** An "execution environment" or state, mapping variables (of type
* `Local`) to integer values */
type Env = Map[String,Int]
//type Env = Map[String,CValue]

private val emptyLocals = new HashMap[String, CValue]()
/** An environment with no assigned variables */
private val emptyEnv: Env = new HashMap[String,Int]() //TODO: update environment
private val emptyEnv: StackFrame = StackFrame(None, emptyLocals, None) //TODO: update environment
def emptyEnv(body : Body, stmt : Stmt, returnValueLocation : Option[Local]) =
StackFrame(Some((body,stmt)), emptyLocals, returnValueLocation)

/** Interpret arithmetic expressions encoded as a single `Value` */
def evaluate_expr(v: Value, env: Env = emptyEnv): Option[Int] = v match { //TODO: update denote
case Local(s) => Some(env.getOrElse(s, throw new RuntimeException(s"Variable $s not found, malformed jimple")))
def evaluate_expr(v: Value, env: StackFrame): Option[CValue] = v match { //TODO: update denote
case Local(s) => {
Some(getEnv(env,s))
}
case IntConstant(v) => {
Some(v)
Some(CInteger(v))
}
case AddExpr(e1, e2) => for {
arg1 <- evaluate_expr(e1, env)
arg2 <- evaluate_expr(e2, env)
} yield arg1 + arg2
} yield ConcreteMemory.add(arg1, arg2)
case SubExpr(e1, e2) => for {
arg1 <- evaluate_expr(e1, env)
arg2 <- evaluate_expr(e2, env)
} yield arg1 - arg2
} yield ConcreteMemory.sub(arg1,arg2)
case MulExpr(e1, e2) => for {
arg1 <- evaluate_expr(e1, env)
arg2 <- evaluate_expr(e2, env)
} yield arg1 * arg2
} yield ConcreteMemory.mul(arg1,arg2)
case DivExpr(e1, e2) => for {
arg1 <- evaluate_expr(e1, env)
arg2 <- {val res = evaluate_expr(e2, env); if(res != 0) res else None}
} yield arg1 / arg2
} yield ConcreteMemory.div(arg1,arg2)
case NegExpr(e) => for {
arg <- evaluate_expr(e, env)
} yield -arg
} yield ConcreteMemory.neg(arg)
case EqExpr(e1,e2) => for{
arg1 <- evaluate_expr(e1,env)
arg2 <- evaluate_expr(e2,env)
} yield if(arg1 == arg2) 1 else 0
} yield ConcreteMemory.equ(arg1,arg2)
case NeExpr(e1,e2) => for{
arg1 <- evaluate_expr(e1,env)
arg2 <- evaluate_expr(e2,env)
}yield if(arg1 != arg2) 1 else 0
}yield ConcreteMemory.nequ(arg1,arg2)
case GeExpr(e1,e2) => for{
arg1 <- evaluate_expr(e1,env)
arg2 <- evaluate_expr(e2,env)
}yield ConcreteMemory.ge(arg1,arg2)
case _ => {
???
}
Expand All @@ -61,20 +72,38 @@ object Interpreter {
Try(internal_interpretBody(List(emptyEnv), b.getFirstNonIdentityStmt, b).getOrElse(throw new RuntimeException("interpreter exception")))
}
//TODO: update environment and update stack
private def updateEnv(env: Env, varname: String, value: Int): Env = {
env + (varname -> value)
private def updateEnv(env: StackFrame, varname: String, value: CValue): StackFrame = env match{
case StackFrame(loc, env, rvl) => StackFrame(loc, env + (varname -> value), rvl)
}
private def getEnv(env: StackFrame, varname : String) : CValue = env match {
case StackFrame(_, env, _) => env.getOrElse (varname, throw new RuntimeException (s"Variable $varname not found, malformed jimple") )
}
private def malformedJimple(): Nothing = throw new RuntimeException("malformed jimple")
@tailrec
private def internal_interpretBody(stack : List[Env], loc: Stmt, b : Body): Option[CValue] = {
private def internal_interpretBody(stack : List[StackFrame], loc: Stmt, b : Body): Option[CValue] = {
//normal successor, conditional successor TODO: exceptional successor
val successor = b.getSuccessors(loc)
val successor: (Option[Stmt], Option[Stmt]) = b.getSuccessors(loc)
interpret_stmt(stack.head, loc) match {
case InterpretNext(env) => internal_interpretBody(env :: stack.tail, successor._1.getOrElse(malformedJimple()), b)
case InterpretConditionalJump(env) =>
internal_interpretBody(env :: stack.tail,
successor._2.getOrElse(malformedJimple()),b)
case ReturnFromBody(returnValue) => returnValue
case ReturnFromBody(returnValue) => stack match{
case h :: Nil => returnValue
case StackFrame(Some((body,stmt)), _, Some(Local(varname))) :: (us@StackFrame(r,e,l)) :: tail => {
val newEnv = returnValue.map(updateEnv(us,varname,_)).getOrElse(us)
val newFrame = StackFrame(r,newEnv.locals,l)
internal_interpretBody(newFrame :: tail, stmt, body)
}
case _ =>
throw new RuntimeException("malformed stack exception")
}
case Invoke(method, args, returnValueLocation) =>
val body = new Body(method.getActiveBody)
val newEnviornment = emptyEnv(b, successor._1.getOrElse(throw new RuntimeException("malformed jimple")), returnValueLocation)
// val newEnvironment = emptyEnv(b,successor._1.)
internal_interpretBody( newEnviornment ::
stack, body.getFirstNonIdentityStmt(), body)
case ExecutionExceptionDivideByZero(stmt) => ???
}
}
Expand All @@ -84,9 +113,10 @@ object Interpreter {
*/
private trait StmtResult
private trait NormalControlFlow extends StmtResult
private sealed case class InterpretNext(newEnvironment : Env) extends NormalControlFlow
private sealed case class InterpretConditionalJump(newEnvironment: Env) extends NormalControlFlow
private sealed case class InterpretNext(newEnvironment : StackFrame) extends NormalControlFlow
private sealed case class InterpretConditionalJump(newEnvironment: StackFrame) extends NormalControlFlow
private sealed case class ReturnFromBody(result: Option[CValue]) extends NormalControlFlow
private sealed case class Invoke(method: soot.SootMethod, arguments : List[Value], returnValueLocation : Option[Local]) extends NormalControlFlow
//TODO: talk about design philosophy, I believe its easier to throw when we encounter malformed jimple
//The design philosophy of java is that there are caught exceptions for places where you need to react to a failure,
// there are also uncaught exceptions which indicate something unexpected happened
Expand All @@ -102,9 +132,9 @@ object Interpreter {



private def wrapExprEvaluationException(exprResult: Option[Int],
private def wrapExprEvaluationException(exprResult: Option[CValue],
stmt: Stmt,
successCondition: Int => NormalControlFlow): StmtResult = exprResult match{
successCondition: CValue => NormalControlFlow): StmtResult = exprResult match{
case Some(v) => successCondition(v)
case None => ExecutionExceptionDivideByZero(stmt)
}
Expand All @@ -114,12 +144,13 @@ object Interpreter {
* @param stmt stmt to interpret
* @return StmtResult conveys what control flow action needs to be taken as well as the information needed
*/
private def interpret_stmt(env: Env, stmt: Stmt): StmtResult = stmt match{
case ReturnStmt(op) => ReturnFromBody(evaluate_expr(op, env).map(a => CInteger(a)))
private def interpret_stmt(env: StackFrame, stmt: Stmt): StmtResult = stmt match{
case ReturnStmt(op) => ReturnFromBody(evaluate_expr(op, env).map(a => a))
case AssignStmt(l@Local(varname), StaticInvokeExpr(method, args)) => Invoke(method, args, Some(l))
case AssignStmt(Local(varname),rval) => wrapExprEvaluationException(
evaluate_expr(rval,env), stmt, a => InterpretNext(updateEnv(env,varname,a)))
case IfStmt(condition,_) => wrapExprEvaluationException(
evaluate_expr(condition,env), stmt, a => if (a == 0) InterpretNext(env) else InterpretConditionalJump(env))
evaluate_expr(condition,env), stmt, a => if (ConcreteMemory.isZero(a)) InterpretNext(env) else InterpretConditionalJump(env))
case GotoStmt(_) => InterpretConditionalJump(env)
case _ => {
???
Expand Down
11 changes: 11 additions & 0 deletions src/main/scala/edu/colorado/plv/cuanto/scoot/jimple/GeExpr.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package edu.colorado.plv.cuanto.scoot.jimple

/**
* @author Shawn Meier
* Created on 10/16/17.
*/
class GeExpr private[jimple] (private val dt: soot.jimple.GeExpr) extends BinopExpr

object GeExpr {
def unapply(arg: GeExpr): Option[(Value,Value)] = Some(arg.dt.getOp1, arg.dt.getOp2)
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package edu.colorado.plv.cuanto.scoot.jimple

import soot.Type

/**
* @author Jared Wright
*/
class Local private[jimple] (private val dt: soot.Local) extends Value

object Local {
def unapply(l: Local): Option[String] = Some(l.dt.getName())
def unapply(l: Local): Option[(String)] = Some(l.dt.getName)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package edu.colorado.plv.cuanto.scoot.jimple

import soot.SootMethod

import scala.collection.JavaConverters._
/**
* @author Shawn Meier
* Created on 10/19/17.
*/
class StaticInvokeExpr private[jimple] (private val dt: soot.jimple.StaticInvokeExpr) extends Expr

object StaticInvokeExpr {
def unapply(arg: StaticInvokeExpr) = {
val method: SootMethod = arg.dt.getMethod
val methodref = arg.dt.getMethodRef

Some(arg.dt.getMethod, arg.dt.getArgs.asScala.toList.map( convertValue ))
}
}
31 changes: 15 additions & 16 deletions src/main/scala/edu/colorado/plv/cuanto/scoot/jimple/package.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package edu.colorado.plv.cuanto.scoot

import scala.language.reflectiveCalls
import soot.jimple._

/**
* @author Jared Wright
Expand All @@ -23,46 +22,46 @@ package object jimple {

// implicit def convertReturnStmt(dt: soot.jimple.ReturnStmt)
implicit def convertUnit(dt: soot.Unit): edu.colorado.plv.cuanto.scoot.jimple.Stmt = {
val s = new StmtSwitch {
val s = new soot.jimple.StmtSwitch {
var retValue : Option[edu.colorado.plv.cuanto.scoot.jimple.Stmt] = None
override def caseIdentityStmt(stmt: IdentityStmt): Unit = ???
override def caseIdentityStmt(stmt: soot.jimple.IdentityStmt): Unit = ???


override def caseAssignStmt(stmt: soot.jimple.AssignStmt): Unit = retValue = Some(new AssignStmt(stmt))

override def caseRetStmt(stmt: RetStmt): Unit = ???
override def caseRetStmt(stmt: soot.jimple.RetStmt): Unit = ???

override def caseInvokeStmt(stmt: InvokeStmt): Unit = ???
override def caseInvokeStmt(stmt: soot.jimple.InvokeStmt): Unit = ???

override def caseGotoStmt(stmt: soot.jimple.GotoStmt): Unit = retValue = Some(new GotoStmt(stmt))

override def caseReturnVoidStmt(stmt: ReturnVoidStmt): Unit = ???
override def caseReturnVoidStmt(stmt: soot.jimple.ReturnVoidStmt): Unit = ???

override def caseExitMonitorStmt(stmt: ExitMonitorStmt): Unit = ???
override def caseExitMonitorStmt(stmt: soot.jimple.ExitMonitorStmt): Unit = ???

override def caseNopStmt(stmt: NopStmt): Unit = ???
override def caseNopStmt(stmt: soot.jimple.NopStmt): Unit = ???

override def caseReturnStmt(stmt: soot.jimple.ReturnStmt): Unit = retValue = Some(new ReturnStmt(stmt))

override def caseLookupSwitchStmt(stmt: LookupSwitchStmt): Unit = ???
override def caseLookupSwitchStmt(stmt: soot.jimple.LookupSwitchStmt): Unit = ???

override def caseIfStmt(stmt: soot.jimple.IfStmt): Unit = retValue = Some(new IfStmt(stmt))

override def caseThrowStmt(stmt: ThrowStmt): Unit = ???
override def caseThrowStmt(stmt: soot.jimple.ThrowStmt): Unit = ???

override def caseTableSwitchStmt(stmt: TableSwitchStmt): Unit = ???
override def caseTableSwitchStmt(stmt: soot.jimple.TableSwitchStmt): Unit = ???

override def caseEnterMonitorStmt(stmt: EnterMonitorStmt): Unit = ???
override def caseEnterMonitorStmt(stmt: soot.jimple.EnterMonitorStmt): Unit = ???

override def defaultCase(obj: scala.Any): Unit = ???

override def caseBreakpointStmt(stmt: BreakpointStmt): Unit = ???
override def caseBreakpointStmt(stmt: soot.jimple.BreakpointStmt): Unit = ???
}
dt.apply(s)
s.retValue.getOrElse(???)
}
implicit def convertValue(dt: soot.Value) : Value = {
val s = new JimpleValueSwitch() {
val s = new soot.jimple.JimpleValueSwitch() {
var retValue : Option[Value] = None

//ExprSwitch methods
Expand All @@ -84,7 +83,7 @@ package object jimple {

override def caseEqExpr(eqExpr: soot.jimple.EqExpr): Unit = retValue = Some(new EqExpr(eqExpr))

override def caseGeExpr(geExpr: soot.jimple.GeExpr): Unit = ???
override def caseGeExpr(geExpr: soot.jimple.GeExpr): Unit = retValue = Some(new GeExpr(geExpr))

override def caseGtExpr(gtExpr: soot.jimple.GtExpr): Unit = ???

Expand Down Expand Up @@ -120,7 +119,7 @@ package object jimple {

override def caseSpecialInvokeExpr(specialInvokeExpr: soot.jimple.SpecialInvokeExpr): Unit = ???

override def caseStaticInvokeExpr(staticInvokeExpr: soot.jimple.StaticInvokeExpr): Unit = ???
override def caseStaticInvokeExpr(staticInvokeExpr: soot.jimple.StaticInvokeExpr): Unit = retValue = Some(new StaticInvokeExpr(staticInvokeExpr))

override def caseSubExpr(subExpr: soot.jimple.SubExpr): Unit = retValue = Some(convertSubExpr(subExpr))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
class ControlFlowTest{
public static void main(String[] args){

test1();
}
public static int test1(){
int foo = 3;
if(foo < 4){
return foo+5;
}else{
return 0;
}
}
}
22 changes: 22 additions & 0 deletions src/test/resources/test_files/InterpreterTests/FunctionTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class FunctionTest{
public static void main(String[] args){
test1();
test2();
}
public static int test1(){
return inner();

}
public static int inner(){
return 3;
}
public static int test2(){
return fact(3);
}
public static int fact(int in){
if(in == 0)
return 1;
else
return fact(in-1);
}
}
Loading