Day 6 HomeWork-ConstantPropagation

写在前面

参考这个项目实现的:南大软件分析作业代码非官方实现

这个学起来就难,概念落到代码上更难…这里就记录一下我的理解(还是要看完前六节课)

顺便,期待下今年的Tai-e项目

Test Framework

package com.CP;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import soot.*;
import soot.options.Options;
import java.util.Arrays;

public class ConstantPropagationTest {

    @Before
    public void init(){
        soot.G.reset();
        Options.v().set_allow_phantom_refs(true);//容错
        Options.v().set_src_prec(Options.src_prec_class);
        Options.v().set_process_dir(Arrays.asList("target/classes/com/TestCP"));
        Options.v().set_whole_program(true);
        Options.v().set_prepend_classpath(true);
        Options.v().set_output_format(Options.output_format_jimple);
        Options.v().setPhaseOption("jb", "use-original-names:true");//保留变量原有名字
        Options.v().set_keep_line_number(true);//保留原始行号
        Scene.v().loadNecessaryClasses();
    }

    @Test
    public void test(){
        PackManager.v().getPack("jtp").add(new Transform("jtp.CP", new CPTransformer()));
        for (SootClass appClazz : Scene.v().getApplicationClasses()) {
            for (SootMethod method : appClazz.getMethods()) {
                Body body = method.retrieveActiveBody();
                PackManager.v().getPack("jtp").apply(body);
            }
        }
    }

    @After
    public void write(){
        PackManager.v().writeOutput();
    }
}

看过之前的soot使用笔记的话,这些代码大部分不成问题,稍微有一些也添加了注释

CPTransformer

package com.CP;

import soot.Body;
import soot.BodyTransformer;
import soot.Unit;
import soot.toolkits.graph.BriefUnitGraph;
import soot.toolkits.graph.UnitGraph;

import java.util.Map;

public class CPTransformer extends BodyTransformer {
    @Override
    protected void internalTransform(Body body, String s, Map<String, String> map) {
        UnitGraph graph = new BriefUnitGraph(body);
        CPFlowAnalysis CP = new CPFlowAnalysis(graph);
        System.out.println(String.format("=========== Method %s ============\n", body.getMethod().getName()));
        for (Unit unit : body.getUnits()) {
            System.out.println(String.format("Before %s: %s", unit, CP.getFlowBefore(unit)));
            System.out.println(String.format("After %s: %s", unit, CP.getFlowAfter(unit)));
            System.out.println("==================================================================\n");
        }
    }
}

这里CPFlowAnalysis CP = new CPFlowAnalysis(graph);就是之前提到的soot中flow analysis framework中我们自己要创建设计的分析类,按照之前的理论框架来建缺少的类就可以了(这时候再看这些代码就没有那么陌生冰冷了)

CPFlowAnalysis

package com.CP;

import soot.Local;
import soot.Unit;
import soot.Value;
import soot.jimple.DefinitionStmt;
import soot.toolkits.graph.DirectedGraph;
import soot.toolkits.scalar.ForwardFlowAnalysis;

public class CPFlowAnalysis extends ForwardFlowAnalysis<Unit, CPFlowSet> {

    public CPFlowAnalysis(DirectedGraph<Unit> graph) {
        super(graph);
        super.doAnalysis();
    }

    @Override
    protected CPFlowSet newInitialFlow() {
        return new CPFlowSet();//may,初始化为空
    }

    @Override
    protected void merge(CPFlowSet in1, CPFlowSet in2, CPFlowSet out) {
        CPFlowSet meetResult = CPFlowSet.meet(in1, in2);
        copy(meetResult, out);
    }

    @Override
    protected void copy(CPFlowSet src, CPFlowSet dest) {
        dest.copyFrom(src);
    }

    @Override
    protected void flowThrough(CPFlowSet in, Unit unit, CPFlowSet out) {
        copy(in, out);
        if(unit instanceof DefinitionStmt){
            DefinitionStmt ass = (DefinitionStmt) unit;
            Value lVal = ass.getLeftOp();
            if(lVal instanceof Local){
                Local local = (Local) lVal;
                Value rVal = ass.getRightOp();
                CPValue rCPVal = in.computeValue(rVal);
                out.put(local, rCPVal);
            }
        }
    }
}

简单说一下这个flowThroughsoot.jimple.DefinitionStmt这个类代表了所有的定义语句,若左边的是变量,则我们拿到右边的式子(情况会稍微有些复杂),进一步处理后再放入out中

Day 6 HomeWork-ConstantPropagation

_:通配符,kill掉x对应的任意值

值域的抽象

参考课程作业的要求

CPFlowSet

是一对对的pair(x,v):x为变量,v是具体值

package com.CP;

import soot.Local;
import soot.Value;
import soot.jimple.*;
import java.util.*;

public class CPFlowSet {

    Map<Local, CPValue> delegateMap;//Local中也会有我们不太需要关注的变量

    public CPFlowSet() {
        this(new HashMap<>());//can not new a Map
    }

    public CPFlowSet(Map<Local, CPValue> map){
        this.delegateMap = map;
    }

    public static CPFlowSet meet(CPFlowSet in1, CPFlowSet in2) {//对应gen
        CPFlowSet result = new CPFlowSet();
        Set<Local> localSet = new HashSet<>();
        localSet.addAll(in1.keySet());
        localSet.addAll(in2.keySet());

        for(Local local: localSet){
            CPValue v1 = in1.get(local);
            CPValue v2 = in2.get(local);
            CPValue meetVal = CPValue.meetValue(v1, v2);
            result.put(local, meetVal);
        }
        return result;
    }

    public Set<Local> keySet() {
        return delegateMap.keySet();
    }

    public void copyFrom(CPFlowSet src) {
        delegateMap.putAll(src.delegateMap);
    }

    public CPValue computeValue(Value rVal) {
        if(rVal instanceof Local){
            return get((Local) rVal);//变量对应值
        }else if(rVal instanceof IntConstant){
            return CPValue.makeConstant(((IntConstant) rVal).value);//直接赋值为常量
        }else if(rVal instanceof BinopExpr){
            BinopExpr Expr = (BinopExpr) rVal;
            Value op1 = Expr.getOp1();//左
            CPValue op1Val = computeValue(op1);
            Value op2 = Expr.getOp2();//右
            CPValue op2Val = computeValue(op2);

            // 如果其中有未定义,那么整个计算也是未定义的
            if (op1Val == CPValue.getUndef() || op2Val == CPValue.getUndef()) {
                return CPValue.getNAC();
            }

            // 其中有一个不是常数,那么整个计算就不是常数
            if (op1Val == CPValue.getNAC() || op2Val == CPValue.getNAC()) {
                return CPValue.getNAC();
            }

            // 两个都是常量
            if (Expr instanceof AddExpr) {
                return CPValue.makeConstant(op1Val.val() + op2Val.val());
            } else if (Expr instanceof SubExpr) {
                return CPValue.makeConstant(op1Val.val() - op2Val.val());
            } else if (Expr instanceof MulExpr) {
                return CPValue.makeConstant(op1Val.val() * op2Val.val());
            } else if (Expr instanceof DivExpr) {
                return CPValue.makeConstant(op1Val.val() / op2Val.val());
            }
        }
        //对于其他的语句类型就不再考虑,in是什么out是什么,保守估计为NAC
        return CPValue.getNAC();
    }

    public CPValue get(Local local) {
        return delegateMap.computeIfAbsent(local, l -> CPValue.getUndef());
    }//存在key对应的value则返回,不存在用lamba表达式返回undefined

    public CPValue put(Local local, CPValue value) {
        return delegateMap.put(local, value);
    }

    @Override
    public String toString() {
        return delegateMap.toString();
    }//方便查看
}

对于computeValue(Value rVal)这个函数,代码实现功能对应如下:

Day 6 HomeWork-ConstantPropagation

这里就参考视频中理解,借鉴的项目中有一个是UNDEF则为NAC,觉得不太对???

CPValue

package com.CP;

public class CPValue {

    private final static CPValue NAC = new CPValue() {
        @Override
        public int hashCode() {
            return Integer.MIN_VALUE;
        }
    };

    private final static CPValue UNDEF = new CPValue() {
        @Override
        public int hashCode() {
            return Integer.MAX_VALUE;
        }
    };

    private int val;

    private CPValue() { }

    private CPValue(int val) {
        this.val = val;
    }

    public int val() {
        return val;
    }

    public static CPValue getNAC() {
        return NAC;
    }

    public static CPValue getUndef() {
        return UNDEF;
    }

    public static CPValue makeConstant(int val) {
        return new CPValue(val);
    }

    public static CPValue meetValue(CPValue value1, CPValue value2) {
        if (value1 == getUndef()) {
            return value2;
        }

        if (value2 == getUndef()) {
            return value1;
        }

        if (value1 == getNAC() || value2 == getNAC()) {
            return getNAC();
        }

        // 两个具体的值meet
        if (value1.val() == value2.val()) {
            return makeConstant(value1.val());
        }

        return getNAC();
    }

    @Override
    public String toString() {
        if (this == NAC) {
            return "NAC";
        }
        if (this == UNDEF) {
            return "UNDEF";
        }
        return String.valueOf(val);
    }
}

meetValue对应要实现的:

Day 6 HomeWork-ConstantPropagation

测试

package com.TestCP;

public class CPTest {

    public void nonDistributiveTest() {
        int a;
        int b;
        if (Math.random() > 0.5) {
            a = 1;
            b = 9;
        } else {
            a = 9;
            b = 1;
        }
        int c = a + b;
    }

    public void sameValueMeetTest() {
        int a;
        int b;
        if (Math.random() > 0.5) {
            a = 1; // a: same value as false branch
            b = 2; // b: not same value as false branch
        } else {
            a = 1;
            b = 1;
        }
        int c = a + b;
        int d = a + a;
        int e = b + b;
    }

    public void testParam(int x) {
        int a;
        a = x; 
        int b = a; 
    }

}

简单看一下结果:

Day 6 HomeWork-ConstantPropagation

如果之前用的是AssignStmt而不是DefinitionStmt,就不会有this

$stack4:random产生的值,对应jimple文件更容易看

$stack5:if中条件值

Day 6 HomeWork-ConstantPropagation

对于第二个函数:

Day 6 HomeWork-ConstantPropagation

最后一个:

Day 6 HomeWork-ConstantPropagation

这几个例子看样子都是对的

上一篇:java-异常-原理异常对象的抛出throw


下一篇:15-static和extern关键字1-对函数的作用