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

pascal.taie.frontend.soot.MethodIRBuilder Maven / Gradle / Ivy

The newest version!
/*
 * Tai-e: A Static Analysis Framework for Java
 *
 * Copyright (C) 2022 Tian Tan 
 * Copyright (C) 2022 Yue Li 
 *
 * This file is part of Tai-e.
 *
 * Tai-e is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * Tai-e is distributed in the hope that it will be useful,but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
 * Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Tai-e. If not, see .
 */

package pascal.taie.frontend.soot;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import pascal.taie.ir.DefaultIR;
import pascal.taie.ir.IR;
import pascal.taie.ir.exp.ArithmeticExp;
import pascal.taie.ir.exp.ArrayAccess;
import pascal.taie.ir.exp.ArrayLengthExp;
import pascal.taie.ir.exp.BinaryExp;
import pascal.taie.ir.exp.BitwiseExp;
import pascal.taie.ir.exp.CastExp;
import pascal.taie.ir.exp.ClassLiteral;
import pascal.taie.ir.exp.ComparisonExp;
import pascal.taie.ir.exp.ConditionExp;
import pascal.taie.ir.exp.DoubleLiteral;
import pascal.taie.ir.exp.FieldAccess;
import pascal.taie.ir.exp.FloatLiteral;
import pascal.taie.ir.exp.InstanceFieldAccess;
import pascal.taie.ir.exp.InstanceOfExp;
import pascal.taie.ir.exp.IntLiteral;
import pascal.taie.ir.exp.InvokeDynamic;
import pascal.taie.ir.exp.InvokeExp;
import pascal.taie.ir.exp.InvokeInterface;
import pascal.taie.ir.exp.InvokeSpecial;
import pascal.taie.ir.exp.InvokeStatic;
import pascal.taie.ir.exp.InvokeVirtual;
import pascal.taie.ir.exp.Literal;
import pascal.taie.ir.exp.LongLiteral;
import pascal.taie.ir.exp.MethodHandle;
import pascal.taie.ir.exp.MethodType;
import pascal.taie.ir.exp.NegExp;
import pascal.taie.ir.exp.NewArray;
import pascal.taie.ir.exp.NewExp;
import pascal.taie.ir.exp.NewInstance;
import pascal.taie.ir.exp.NewMultiArray;
import pascal.taie.ir.exp.NullLiteral;
import pascal.taie.ir.exp.ShiftExp;
import pascal.taie.ir.exp.StaticFieldAccess;
import pascal.taie.ir.exp.StringLiteral;
import pascal.taie.ir.exp.UnaryExp;
import pascal.taie.ir.exp.Var;
import pascal.taie.ir.proginfo.ExceptionEntry;
import pascal.taie.ir.proginfo.MemberRef;
import pascal.taie.ir.proginfo.MethodRef;
import pascal.taie.ir.stmt.AssignLiteral;
import pascal.taie.ir.stmt.Binary;
import pascal.taie.ir.stmt.Cast;
import pascal.taie.ir.stmt.Catch;
import pascal.taie.ir.stmt.Copy;
import pascal.taie.ir.stmt.Goto;
import pascal.taie.ir.stmt.If;
import pascal.taie.ir.stmt.InstanceOf;
import pascal.taie.ir.stmt.Invoke;
import pascal.taie.ir.stmt.LoadArray;
import pascal.taie.ir.stmt.LoadField;
import pascal.taie.ir.stmt.LookupSwitch;
import pascal.taie.ir.stmt.Monitor;
import pascal.taie.ir.stmt.New;
import pascal.taie.ir.stmt.Nop;
import pascal.taie.ir.stmt.Return;
import pascal.taie.ir.stmt.Stmt;
import pascal.taie.ir.stmt.StoreArray;
import pascal.taie.ir.stmt.StoreField;
import pascal.taie.ir.stmt.SwitchStmt;
import pascal.taie.ir.stmt.TableSwitch;
import pascal.taie.ir.stmt.Throw;
import pascal.taie.ir.stmt.Unary;
import pascal.taie.language.classes.JMethod;
import pascal.taie.language.type.ArrayType;
import pascal.taie.language.type.ClassType;
import pascal.taie.language.type.ReferenceType;
import pascal.taie.language.type.Type;
import pascal.taie.util.collection.CollectionUtils;
import pascal.taie.util.collection.Lists;
import pascal.taie.util.collection.Maps;
import pascal.taie.util.collection.MultiMap;
import pascal.taie.util.collection.Sets;
import soot.Body;
import soot.Local;
import soot.SootMethod;
import soot.SootMethodRef;
import soot.Trap;
import soot.Unit;
import soot.Value;
import soot.ValueBox;
import soot.jimple.AbstractConstantSwitch;
import soot.jimple.AbstractJimpleValueSwitch;
import soot.jimple.AbstractStmtSwitch;
import soot.jimple.AddExpr;
import soot.jimple.AndExpr;
import soot.jimple.AnyNewExpr;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.BinopExpr;
import soot.jimple.CastExpr;
import soot.jimple.CaughtExceptionRef;
import soot.jimple.ClassConstant;
import soot.jimple.CmpExpr;
import soot.jimple.CmpgExpr;
import soot.jimple.CmplExpr;
import soot.jimple.ConditionExpr;
import soot.jimple.Constant;
import soot.jimple.DivExpr;
import soot.jimple.DoubleConstant;
import soot.jimple.DynamicInvokeExpr;
import soot.jimple.EnterMonitorStmt;
import soot.jimple.EqExpr;
import soot.jimple.ExitMonitorStmt;
import soot.jimple.FieldRef;
import soot.jimple.FloatConstant;
import soot.jimple.GeExpr;
import soot.jimple.GotoStmt;
import soot.jimple.GtExpr;
import soot.jimple.IdentityStmt;
import soot.jimple.IfStmt;
import soot.jimple.InstanceFieldRef;
import soot.jimple.InstanceInvokeExpr;
import soot.jimple.InstanceOfExpr;
import soot.jimple.IntConstant;
import soot.jimple.InterfaceInvokeExpr;
import soot.jimple.InvokeExpr;
import soot.jimple.InvokeStmt;
import soot.jimple.LeExpr;
import soot.jimple.LengthExpr;
import soot.jimple.LongConstant;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.LtExpr;
import soot.jimple.MulExpr;
import soot.jimple.NeExpr;
import soot.jimple.NegExpr;
import soot.jimple.NewArrayExpr;
import soot.jimple.NewExpr;
import soot.jimple.NewMultiArrayExpr;
import soot.jimple.NopStmt;
import soot.jimple.NullConstant;
import soot.jimple.OrExpr;
import soot.jimple.RemExpr;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.jimple.ShlExpr;
import soot.jimple.ShrExpr;
import soot.jimple.SpecialInvokeExpr;
import soot.jimple.StaticFieldRef;
import soot.jimple.StringConstant;
import soot.jimple.SubExpr;
import soot.jimple.TableSwitchStmt;
import soot.jimple.ThrowStmt;
import soot.jimple.UnopExpr;
import soot.jimple.UshrExpr;
import soot.jimple.VirtualInvokeExpr;
import soot.jimple.XorExpr;
import soot.util.Chain;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static pascal.taie.language.type.VoidType.VOID;

/**
 * Converts Jimple to Tai-e IR.
 */
class MethodIRBuilder extends AbstractStmtSwitch {

    private static final Logger logger = LogManager.getLogger(MethodIRBuilder.class);

    private final JMethod method;

    private final Converter converter;

    private VarManager varManager;

    private Set returnVars;

    private List stmts;

    private List exceptionEntries;

    MethodIRBuilder(JMethod method, Converter converter) {
        this.method = method;
        this.converter = converter;
    }

    IR build() {
        SootMethod m = (SootMethod) method.getMethodSource();
        Body body = m.retrieveActiveBody();
        m.releaseActiveBody(); // release body to save memory
        varManager = new VarManager(method, converter);
        if (method.getReturnType().equals(VOID)) {
            returnVars = Set.of();
        } else {
            returnVars = Sets.newLinkedSet();
        }
        stmts = new ArrayList<>();
        if (!method.isStatic()) {
            buildThis(body.getThisLocal());
        }
        buildParams(body.getParameterLocals());
        preprocessTemps(body,
                tempToDef = Maps.newHybridMap(),
                unusedInvokeTempRets = Sets.newHybridSet());
        buildStmts(body);
        buildExceptionEntries(body);
        return new DefaultIR(method,
                varManager.getThis(), varManager.getParams(), returnVars,
                varManager.getVars(), stmts, exceptionEntries);
    }

    private void buildThis(Local thisLocal) {
        varManager.addThis(thisLocal);
    }

    private void buildParams(List params) {
        varManager.addParams(params);
    }

    private void buildStmts(Body body) {
        if (!body.getTraps().isEmpty()) {
            trapUnits = Sets.newSet();
            body.getTraps().forEach(trap -> {
                trapUnits.add(trap.getBeginUnit());
                trapUnits.add(trap.getEndUnit());
                trapUnits.add(findRealHandler(body, trap.getHandlerUnit()));
            });
            trapUnitMap = Maps.newMap(body.getTraps().size() * 3);
        }
        body.getUnits().forEach(unit -> unit.apply(this));
        linkJumpTargets(jumpMap, jumpTargetMap);
    }

    /**
     * Finds out the real exception handler in {@link Trap}.
     * When Soot uses Java frontend, the handler unit in {@link Trap} is
     * a {@link NopStmt} instead of catch statement, and the catch statement
     * follows the {@link NopStmt} in {@link Chain}. To ensure that the
     * exception handler of Tai-e IR is {@link Catch}, we find out the real
     * exception handler in Jimple and so that it can be mapped to {@link Catch}.
     *
     * @param body the method body being processed.
     * @param unit the handler unit in {@link Trap}.
     * @return the real exception handler, i.e., e = @caughtexception;
     */
    private static Unit findRealHandler(Body body, Unit unit) {
        while (!isJimpleCatch(unit)) {
            // if unit is not a Jimple catch statement, traverse the unit chain
            unit = body.getUnits().getSuccOf(unit);
        }
        return unit;
    }

    private static boolean isJimpleCatch(Unit unit) {
        return unit instanceof IdentityStmt identity &&
                identity.getRightOp() instanceof CaughtExceptionRef;
    }

    private static void linkJumpTargets(
            Map jumpMap, Map jumpTargetMap) {
        jumpMap.forEach((unit, stmt) -> {
            if (unit instanceof GotoStmt jimpleGoto) {
                Goto taieGoto = (Goto) stmt;
                taieGoto.setTarget(jumpTargetMap.get(jimpleGoto.getTarget()));
            } else if (unit instanceof IfStmt jimpleIf) {
                If taieIf = (If) stmt;
                taieIf.setTarget(jumpTargetMap.get(jimpleIf.getTarget()));
            } else if (unit instanceof soot.jimple.SwitchStmt jimpleSwitch) {
                SwitchStmt taieSwitch = (SwitchStmt) stmt;
                taieSwitch.setTargets(Lists.map(jimpleSwitch.getTargets(),
                        jumpTargetMap::get));
                taieSwitch.setDefaultTarget(
                        jumpTargetMap.get(jimpleSwitch.getDefaultTarget()));
            }
        });
    }

    private void buildExceptionEntries(Body body) {
        Chain traps = body.getTraps();
        if (traps.isEmpty()) {
            exceptionEntries = List.of();
        } else {
            exceptionEntries = new ArrayList<>(traps.size());
            for (Trap trap : traps) {
                Unit begin = trap.getBeginUnit();
                Unit end = trap.getEndUnit();
                Unit handler = findRealHandler(body, trap.getHandlerUnit());
                soot.Type catchType = trap.getException().getType();
                exceptionEntries.add(new ExceptionEntry(
                        trapUnitMap.get(begin),
                        trapUnitMap.get(end),
                        (Catch) trapUnitMap.get(handler),
                        (ClassType) converter.convertType(catchType)));
            }
        }
    }

    /**
     * Current Jimple unit being converted.
     */
    private Unit currentUnit;

    /**
     * Map from jump statements in Jimple to the corresponding Tai-e statements.
     */
    private final Map jumpMap = Maps.newHybridMap();

    /**
     * Map from jump target statements in Jimple to the corresponding Tai-e statements.
     */
    private final Map jumpTargetMap = Maps.newHybridMap();

    /**
     * All trap-related units of current Jimple body.
     */
    private Set trapUnits = Set.of();

    /**
     * Map from trap beginning statements in Jimple to
     * the corresponding Tai-e statements.
     */
    private Map trapUnitMap = Map.of();

    /**
     * If {@link #currentUnit} is a jump target (or trap begin unit,
     * which can be seen as the beginning target of exception scope) in Jimple,
     * then this field holds the corresponding statement in Tai-e IR.
     * This field is useful when converting the Jimple statements that
     * contain constant values, as Tai-e IR will emit {@link AssignLiteral}
     * for constant values before the actual corresponding {@link Stmt}.
     * 

* For example, consider following Jimple code: * if a > b goto label1; * label1: * x = 1 + y; *

* In Tai-e IR, above code will be converted to this: * if a > b goto label1; * label1: * %intconst0 = 1; // <-- tempTarget * x = %intconst0 + y; *

* Tai-e adds an {@link AssignLiteral} statement before the * corresponding addition, so we need to set the target * of {@link If} to the tempTarget instead of the addition. * We use this field to maintain temporary targets. */ private Stmt tempTarget; private void addTempStmt(Stmt stmt) { if (tempTarget == null) { tempTarget = stmt; } processNewStmt(stmt); } private void addStmt(Stmt stmt) { if (!currentUnit.getBoxesPointingToThis().isEmpty()) { if (tempTarget != null) { jumpTargetMap.put(currentUnit, tempTarget); } else { jumpTargetMap.put(currentUnit, stmt); } } if (trapUnits.contains(currentUnit)) { if (tempTarget != null) { trapUnitMap.put(currentUnit, tempTarget); } else { trapUnitMap.put(currentUnit, stmt); } } tempTarget = null; processNewStmt(stmt); } private void processNewStmt(Stmt stmt) { stmt.setLineNumber(currentUnit.getJavaSourceStartLineNumber()); stmt.setIndex(stmts.size()); stmts.add(stmt); } /** * Map from temp variable to its definition stmt, e.g., * for temp$i = x, we map temp$i -> temp$i = x. */ private Map tempToDef; /** * Set of temp variables that have been processed in {@link #getVar(Local)}. */ private final Set processedTemps = Sets.newHybridSet(); /** * Set of temp variables that receives invocation results but never used, * e.g., temp$i = foo(), but temp$i is never used. */ private Set unusedInvokeTempRets; /** * Collects information about special temp variables generated by Soot frontend: * (1) collects the temp variables and their relevant definition statements; * (2) collects the temp variables that receive invocation results but never used. * TODO: remove this step for body parsed from .class files. */ private static void preprocessTemps(Body body, Map tempToDef, Set unusedInvokeTempRets) { MultiMap tempToAssigns = Maps.newMultiMap(); MultiMap tempToUses = Maps.newMultiMap(); for (Unit unit : body.getUnits()) { if (unit instanceof AssignStmt assign) { Value lhs = assign.getLeftOp(); if (lhs instanceof Local var) { if (var.getName().startsWith("temp$")) { tempToAssigns.put(var, assign); if (assign.containsInvokeExpr()) { unusedInvokeTempRets.add(var); } } } } // collect the uses of temp variables unit.getUseBoxes() .stream() .map(ValueBox::getValue) .forEach(value -> { if (value instanceof Local var) { if (var.getName().startsWith("temp$")) { tempToUses.put(var, unit); unusedInvokeTempRets.remove(var); } } }); } tempToAssigns.forEachSet((var, assigns) -> { if (assigns.size() == 1) { AssignStmt assign = CollectionUtils.getOne(assigns); Value rhs = assign.getRightOp(); if ((rhs instanceof Constant || rhs instanceof Local || rhs instanceof BinopExpr) && tempToUses.get(var).size() <= 1) { // if multiple units use a temp variable, then we don't // inline the RHS expression, because the value of RHS may // change between the two units, and lead to wrong result. // y = ++x; could trigger this issue. tempToDef.put(var, assign); } } }); } private boolean isTempVar(Local local) { return tempToDef.containsKey(local); } /** * Converts Jimple Constants to Literals. */ private final AbstractConstantSwitch constantConverter = new AbstractConstantSwitch<>() { @Override public void caseDoubleConstant(DoubleConstant v) { setResult(DoubleLiteral.get(v.value)); } @Override public void caseFloatConstant(FloatConstant v) { setResult(FloatLiteral.get(v.value)); } @Override public void caseIntConstant(IntConstant v) { setResult(IntLiteral.get(v.value)); } @Override public void caseLongConstant(LongConstant v) { setResult(LongLiteral.get(v.value)); } @Override public void caseNullConstant(NullConstant v) { setResult(NullLiteral.get()); } @Override public void caseStringConstant(StringConstant v) { setResult(StringLiteral.get(v.value)); } @Override public void caseClassConstant(ClassConstant v) { Type type = converter.convertType(v.toSootType()); setResult(ClassLiteral.get(type)); } @Override public void caseMethodHandle(soot.jimple.MethodHandle v) { MethodHandle.Kind kind = MethodHandle.Kind.get(v.getKind()); MemberRef memberRef = v.isMethodRef() ? converter.convertMethodRef(v.getMethodRef()) : converter.convertFieldRef(v.getFieldRef()); setResult(MethodHandle.get(kind, memberRef)); } @Override public void caseMethodType(soot.jimple.MethodType v) { List paramTypes = Lists.map(v.getParameterTypes(), converter::convertType); Type returnType = converter.convertType(v.getReturnType()); setResult(MethodType.get(paramTypes, returnType)); } @Override public void defaultCase(Object v) { throw new SootFrontendException( "Cannot convert constant: " + v); } }; @Override public void caseAssignStmt(AssignStmt stmt) { currentUnit = stmt; Value lhs = stmt.getLeftOp(); Value rhs = stmt.getRightOp(); if (rhs instanceof InvokeExpr) { buildInvoke((Local) lhs, stmt.getInvokeExpr()); return; } if (lhs instanceof Local lvar) { if (rhs instanceof Local rvar) { if (isTempVar(rvar)) { // for assignment like x = temp$i, if we have recorded // definition to temp$i, then we directly replace temp$i // by its definition value. // FIXME (?): this optimization reduces number of temporary // variables, but it does not work with // System.setProperty("ENABLE_JIMPLE_OPT", "true"); Value defValue = tempToDef.get(rvar).getRightOp(); if (defValue instanceof Constant) { defValue.apply(constantConverter); addStmt(new AssignLiteral(getVar(lvar), constantConverter.getResult())); } else if (defValue instanceof Local) { addStmt(new Copy(getVar(lvar), getVar((Local) defValue))); } else if (defValue instanceof BinopExpr) { buildBinary(lvar, (BinopExpr) defValue); } } else if (!isTempVar(lvar)) { addStmt(new Copy(getVar(lvar), getVar(rvar))); } } else if (rhs instanceof AnyNewExpr) { buildNew(lvar, (AnyNewExpr) rhs); } else if (rhs instanceof Constant) { if (!isTempVar(lvar)) { rhs.apply(constantConverter); addStmt(new AssignLiteral(getVar(lvar), constantConverter.getResult())); } } else if (rhs instanceof FieldRef) { addStmt(new LoadField(getVar(lvar), getFieldAccess((FieldRef) rhs))); } else if (rhs instanceof ArrayRef) { addStmt(new LoadArray(getVar(lvar), getArrayAccess((ArrayRef) rhs))); } else if (rhs instanceof BinopExpr) { if (!isTempVar(lvar)) { buildBinary(lvar, (BinopExpr) rhs); } } else if (rhs instanceof UnopExpr) { buildUnary(lvar, (UnopExpr) rhs); } else if (rhs instanceof InstanceOfExpr) { buildInstanceOf(lvar, (InstanceOfExpr) rhs); } else if (rhs instanceof CastExpr) { buildCast(lvar, (CastExpr) rhs); } else { throw new SootFrontendException( "Cannot handle AssignStmt: " + stmt); } } else if (lhs instanceof FieldRef) { addStmt(new StoreField( getFieldAccess((FieldRef) lhs), getLocalOrConstant(rhs))); } else if (lhs instanceof ArrayRef) { addStmt(new StoreArray( getArrayAccess((ArrayRef) lhs), getLocalOrConstant(rhs))); } else { throw new SootFrontendException( "Cannot handle AssignStmt: " + stmt); } } /** * Shortcut: obtains Jimple Value's Type and convert to Tai-e Type. */ private Type getTypeOf(Value value) { return converter.convertType(value.getType()); } /** * Shortcut: converts Jimple Local to Var. */ private Var getVar(Local local) { if (tempToDef.containsKey(local)) { // handle the case when the given local is recorded in tempToDef AssignStmt def = tempToDef.get(local); Value defValue = def.getRightOp(); if (defValue instanceof Local) { // return x for case temp$i = x return varManager.getVar((Local) defValue); } // add (skipped) assignment for the temp var if necessary if (!processedTemps.contains(local)) { processedTemps.add(local); Unit temp = currentUnit; currentUnit = def; if (defValue instanceof Constant) { defValue.apply(constantConverter); addStmt(new AssignLiteral(varManager.getVar(local), constantConverter.getResult())); } else if (defValue instanceof BinopExpr) { buildBinary(local, (BinopExpr) defValue); } else { throw new SootFrontendException( "Expected Local, Constant or BinopExpr, given " + defValue); } currentUnit = temp; } } return varManager.getVar(local); } /** * Caches variables that hold constant values, so that we don't need to * create multiple temp variables and assignments for the same constants * in the same method. */ private final Map constantVars = Maps.newHybridMap(); /** * Converts a Jimple Local or Constant to Var. * If value is Local, then directly return the corresponding Var. * If value is Constant, then add a temporary assignment, * e.g., x = 10 for constant 10, and return Var x. */ private Var getLocalOrConstant(Value value) { if (value instanceof Local) { return getVar((Local) value); } else if (value instanceof Constant) { value.apply(constantConverter); Literal rvalue = constantConverter.getResult(); return constantVars.computeIfAbsent(rvalue, v -> { Var lvalue = varManager.newConstantVar(v); if (!(rvalue instanceof NullLiteral)) { // add temp assignment for non-null variable addTempStmt(new AssignLiteral(lvalue, v)); } return lvalue; }); } throw new SootFrontendException("Expected Local or Constant, given " + value); } /** * Converts Jimple FieldRef to FieldAccess. */ private FieldAccess getFieldAccess(FieldRef fieldRef) { pascal.taie.ir.proginfo.FieldRef jfieldRef = converter.convertFieldRef(fieldRef.getFieldRef()); if (fieldRef instanceof InstanceFieldRef) { return new InstanceFieldAccess(jfieldRef, getVar((Local) ((InstanceFieldRef) fieldRef).getBase())); } else { assert fieldRef instanceof StaticFieldRef; return new StaticFieldAccess(jfieldRef); } } /** * Converts Jimple ArrayRef to ArrayAccess. */ private ArrayAccess getArrayAccess(ArrayRef arrayRef) { return new ArrayAccess(getVar((Local) arrayRef.getBase()), getLocalOrConstant(arrayRef.getIndex())); } /** * Converts Jimple NewExpr to NewExp */ private final AbstractJimpleValueSwitch newExprConverter = new AbstractJimpleValueSwitch<>() { @Override public void caseNewExpr(NewExpr v) { setResult(new NewInstance((ClassType) getTypeOf(v))); } @Override public void caseNewArrayExpr(NewArrayExpr v) { setResult(new NewArray((ArrayType) getTypeOf(v), getLocalOrConstant(v.getSize()))); } @Override public void caseNewMultiArrayExpr(NewMultiArrayExpr v) { List lengths = Lists.map(v.getSizes(), MethodIRBuilder.this::getLocalOrConstant); setResult(new NewMultiArray((ArrayType) getTypeOf(v), lengths)); } }; private void buildNew(Local lhs, AnyNewExpr rhs) { rhs.apply(newExprConverter); NewExp newExp = newExprConverter.getResult(); New newStmt = new New(method, getVar(lhs), newExp); addStmt(newStmt); } /** * Extracts BinaryExp.Op from Jimple BinopExpr. */ private final AbstractJimpleValueSwitch binaryOpExtractor = new AbstractJimpleValueSwitch<>() { // ---------- Arithmetic expression ---------- @Override public void caseAddExpr(AddExpr v) { setResult(ArithmeticExp.Op.ADD); } @Override public void caseSubExpr(SubExpr v) { setResult(ArithmeticExp.Op.SUB); } @Override public void caseMulExpr(MulExpr v) { setResult(ArithmeticExp.Op.MUL); } @Override public void caseDivExpr(DivExpr v) { setResult(ArithmeticExp.Op.DIV); } @Override public void caseRemExpr(RemExpr v) { setResult(ArithmeticExp.Op.REM); } // ---------- Bitwise expression ---------- @Override public void caseAndExpr(AndExpr v) { setResult(BitwiseExp.Op.AND); } @Override public void caseOrExpr(OrExpr v) { setResult(BitwiseExp.Op.OR); } @Override public void caseXorExpr(XorExpr v) { setResult(BitwiseExp.Op.XOR); } // ---------- Comparison expression ---------- @Override public void caseCmpExpr(CmpExpr v) { setResult(ComparisonExp.Op.CMP); } @Override public void caseCmplExpr(CmplExpr v) { setResult(ComparisonExp.Op.CMPL); } @Override public void caseCmpgExpr(CmpgExpr v) { setResult(ComparisonExp.Op.CMPG); } // ---------- Shift expression ---------- @Override public void caseShlExpr(ShlExpr v) { setResult(ShiftExp.Op.SHL); } @Override public void caseShrExpr(ShrExpr v) { setResult(ShiftExp.Op.SHR); } @Override public void caseUshrExpr(UshrExpr v) { setResult(ShiftExp.Op.USHR); } @Override public void defaultCase(Object v) { throw new SootFrontendException( "Expected binary expression, given " + v); } }; private void buildBinary(Local lhs, BinopExpr rhs) { rhs.apply(binaryOpExtractor); BinaryExp.Op op = binaryOpExtractor.getResult(); BinaryExp binaryExp; Var v1 = getLocalOrConstant(rhs.getOp1()); Var v2 = getLocalOrConstant(rhs.getOp2()); if (op instanceof ArithmeticExp.Op) { binaryExp = new ArithmeticExp((ArithmeticExp.Op) op, v1, v2); } else if (op instanceof ComparisonExp.Op) { binaryExp = new ComparisonExp((ComparisonExp.Op) op, v1, v2); } else if (op instanceof BitwiseExp.Op) { binaryExp = new BitwiseExp((BitwiseExp.Op) op, v1, v2); } else if (op instanceof ShiftExp.Op) { binaryExp = new ShiftExp((ShiftExp.Op) op, v1, v2); } else { throw new SootFrontendException("Cannot handle BinopExpr: " + rhs); } addStmt(new Binary(getVar(lhs), binaryExp)); } private void buildUnary(Local lhs, UnopExpr rhs) { Var v = getLocalOrConstant(rhs.getOp()); UnaryExp unaryExp; if (rhs instanceof NegExpr) { unaryExp = new NegExp(v); } else if (rhs instanceof LengthExpr) { unaryExp = new ArrayLengthExp(v); } else { throw new SootFrontendException("Cannot handle UnopExpr: " + rhs); } addStmt(new Unary(getVar(lhs), unaryExp)); } private void buildInstanceOf(Local lhs, InstanceOfExpr rhs) { InstanceOfExp instanceOfExp = new InstanceOfExp( getLocalOrConstant(rhs.getOp()), (ReferenceType) converter.convertType(rhs.getCheckType())); addStmt(new InstanceOf(getVar(lhs), instanceOfExp)); } private void buildCast(Local lhs, CastExpr rhs) { CastExp castExp = new CastExp( getLocalOrConstant(rhs.getOp()), converter.convertType(rhs.getCastType())); addStmt(new Cast(getVar(lhs), castExp)); } @Override public void caseGotoStmt(GotoStmt stmt) { currentUnit = stmt; Goto gotoStmt = new Goto(); jumpMap.put(currentUnit, gotoStmt); addStmt(gotoStmt); } @Override public void caseIfStmt(IfStmt stmt) { currentUnit = stmt; ConditionExpr condition = (ConditionExpr) stmt.getCondition(); ConditionExp.Op op; if (condition instanceof EqExpr) { op = ConditionExp.Op.EQ; } else if (condition instanceof NeExpr) { op = ConditionExp.Op.NE; } else if (condition instanceof LtExpr) { op = ConditionExp.Op.LT; } else if (condition instanceof GtExpr) { op = ConditionExp.Op.GT; } else if (condition instanceof LeExpr) { op = ConditionExp.Op.LE; } else if (condition instanceof GeExpr) { op = ConditionExp.Op.GE; } else { throw new SootFrontendException( "Expected conditional expression, given: " + condition); } Var v1 = getLocalOrConstant(condition.getOp1()); Var v2 = getLocalOrConstant(condition.getOp2()); If ifStmt = new If(new ConditionExp(op, v1, v2)); jumpMap.put(currentUnit, ifStmt); addStmt(ifStmt); } @Override public void caseLookupSwitchStmt(LookupSwitchStmt stmt) { currentUnit = stmt; Var var = getLocalOrConstant(stmt.getKey()); List caseValues = Lists.map(stmt.getLookupValues(), v -> v.value); LookupSwitch lookupSwitch = new LookupSwitch(var, caseValues); jumpMap.put(currentUnit, lookupSwitch); addStmt(lookupSwitch); } @Override public void caseTableSwitchStmt(TableSwitchStmt stmt) { currentUnit = stmt; Var var = getLocalOrConstant(stmt.getKey()); TableSwitch tableSwitch = new TableSwitch(var, stmt.getLowIndex(), stmt.getHighIndex()); jumpMap.put(currentUnit, tableSwitch); addStmt(tableSwitch); } @Override public void caseInvokeStmt(InvokeStmt stmt) { currentUnit = stmt; buildInvoke(null, stmt.getInvokeExpr()); } private void buildInvoke(Local lhs, InvokeExpr invokeExpr) { Var result = (lhs == null // remove unused temp variables that receive invoke result || unusedInvokeTempRets.contains(lhs)) ? null : getVar(lhs); InvokeExp invokeExp = getInvokeExp(invokeExpr); Invoke invoke = new Invoke(method, invokeExp, result); addStmt(invoke); } /** * Converts Jimple InvokeExpr to InvokeExp. */ private InvokeExp getInvokeExp(InvokeExpr invokeExpr) { if (invokeExpr instanceof DynamicInvokeExpr) { return getInvokeDynamic((DynamicInvokeExpr) invokeExpr); } else { MethodRef methodRef = converter .convertMethodRef(invokeExpr.getMethodRef()); List args = Lists.map(invokeExpr.getArgs(), this::getLocalOrConstant); if (invokeExpr instanceof InstanceInvokeExpr) { Var base = getVar( (Local) ((InstanceInvokeExpr) invokeExpr).getBase()); if (invokeExpr instanceof VirtualInvokeExpr) { return new InvokeVirtual(methodRef, base, args); } else if (invokeExpr instanceof InterfaceInvokeExpr) { return new InvokeInterface(methodRef, base, args); } else if (invokeExpr instanceof SpecialInvokeExpr) { return new InvokeSpecial(methodRef, base, args); } } return new InvokeStatic(methodRef, args); } } private InvokeDynamic getInvokeDynamic(DynamicInvokeExpr invokeExpr) { MethodRef bootstrapMethodRef = converter.convertMethodRef( invokeExpr.getBootstrapMethodRef()); SootMethodRef sigInfo = invokeExpr.getMethodRef(); String methodName = sigInfo.getName(); List paramTypes = Lists.map(sigInfo.getParameterTypes(), converter::convertType); Type returnType = converter.convertType(sigInfo.getReturnType()); MethodType methodType = MethodType.get(paramTypes, returnType); List bootstrapArgs = Lists.map(invokeExpr.getBootstrapArgs(), v -> { v.apply(constantConverter); return constantConverter.getResult(); }); List args = Lists.map(invokeExpr.getArgs(), this::getLocalOrConstant); return new InvokeDynamic(bootstrapMethodRef, methodName, methodType, bootstrapArgs, args); } @Override public void caseReturnStmt(ReturnStmt stmt) { currentUnit = stmt; Var returnVar = getLocalOrConstant(stmt.getOp()); returnVars.add(returnVar); addStmt(new Return(returnVar)); } @Override public void caseReturnVoidStmt(ReturnVoidStmt stmt) { currentUnit = stmt; addStmt(new Return()); } @Override public void caseThrowStmt(ThrowStmt stmt) { currentUnit = stmt; addStmt(new Throw(getLocalOrConstant(stmt.getOp()))); } @Override public void caseIdentityStmt(IdentityStmt stmt) { currentUnit = stmt; Value lhs = stmt.getLeftOp(); Value rhs = stmt.getRightOp(); if (rhs instanceof CaughtExceptionRef) { addStmt(new Catch(getVar((Local) lhs))); } } @Override public void caseEnterMonitorStmt(EnterMonitorStmt stmt) { currentUnit = stmt; addStmt(new Monitor(Monitor.Op.ENTER, getLocalOrConstant(stmt.getOp()))); } @Override public void caseExitMonitorStmt(ExitMonitorStmt stmt) { currentUnit = stmt; addStmt(new Monitor(Monitor.Op.EXIT, getLocalOrConstant(stmt.getOp()))); } @Override public void caseNopStmt(NopStmt stmt) { currentUnit = stmt; addStmt(new Nop()); } @Override public void defaultCase(Object obj) { logger.error("Unhandled Stmt: " + obj); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy