package org.bdware.analysis.example;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bdware.analysis.BasicBlock;
import org.bdware.analysis.BreadthFirstSearch;
import org.bdware.analysis.taint.TaintBB;
import org.bdware.analysis.taint.TaintCFG;
import org.bdware.analysis.taint.TaintResult;
import org.bdware.analysis.taint.TaintValue;
import org.objectweb.asm.tree.MethodNode;

import java.util.*;

public class FieldSensitiveTaintAnalysis extends BreadthFirstSearch<TaintResult, TaintBB> {
    private static final Logger LOGGER = LogManager.getLogger(FieldSensitiveTaintAnalysis.class);
    public static boolean isDebug = false;
    TaintCFG cfg;

    public FieldSensitiveTaintAnalysis(TaintCFG cfg) {
        this.cfg = cfg;
        List<TaintBB> toAnalysis = new ArrayList<>();
        /*
         * NaiveTaintBB������ boolean inList NaiveTaintResult preResult NaiveTaintResult
         * sucResult
         */
        TaintBB b = (TaintBB) cfg.getBasicBlockAt(0);
        /*
         * NaiveTaintResult�еı����� Frame<TaintValue> frame TaintInterpreter interpreter
         * TaintValue ret InsnPrinter printer int nLocals int nStack
         * NaiveTaintResult�е��ࣺ TaintValue int size boolean isTainted
         * TaintInterpreter() NaiveTaintResult currentResult Frame�еı����� V returnValue
         * //���������ķ������ͣ����Ϊvoid��Ϊnull V[] values //�ֲ������Ͳ����� int locals
         * //�ֲ������ĸ��� int top //������ջ��Ԫ�ظ���
         */
        b.preResult = new TaintResult();
        // ��ʼ��
        int arg = cfg.argsLocal();
        cfg.executeLocal();
        TaintResult.interpreter.setTaintBits(cfg.taintBits);
        b.preResult.frame.setLocal(arg, new TaintValue(1, cfg.taintBits.allocate("arg" + arg)));
        /*
         * if (NaiveTaintResult.nLocals > 2) { b.preResult.frame.setLocal(0, new
         * TaintValue(1, cfg.taintBits.allocate("arg0"))); b.preResult.frame.setLocal(1,
         * new TaintValue(1, cfg.taintBits.allocate("arg1")));
         * b.preResult.frame.setLocal(2, new TaintValue(1,
         * cfg.taintBits.allocate("arg2"))); }
         */
        // .ret = TaintValue������size=1
        b.preResult.ret = new TaintValue(1);
        TaintResult.printer.setLabelOrder(cfg.getLabelOrder());
        // ����ǰ��b���뵽�����б���
        toAnalysis.add(b);
        b.setInList(true);
        // ����BreadthFirstSearch���еĺ���setToAnalysis����ʱBreadthFirstSearch�����toAnalysisΪ��ǰ�Ŀ��б�ֻ��һ����B0
        /*
         * BreadthFirstSearch���еı����� Map<T, AnalysisResult> results; List<T>
         * toAnalysis;
         */
        setToAnalysis(toAnalysis);
        if (isDebug) {
            MethodNode methodNode = cfg.getMethodNode();
            LOGGER.info("Method: " + methodNode.name + " " + methodNode.desc);
            LOGGER.info("Local: " + methodNode.maxLocals + " " + methodNode.maxStack);
        }
    }

    @Override
    public TaintResult execute(TaintBB t) {
        return t.forwardAnalysis();
    }

    // Current Block is done, merge sucResult to sucBlocks!
    @Override
    public Collection<TaintBB> getSuc(TaintBB t) {
        // System.out.println("---------------------------------");
        // System.out.println("XXXXXB" + t.blockID + " sucBlock:" +
        // t.sucResult.frame2Str());
        Set<BasicBlock> subBlock = cfg.getSucBlocks(t);
        Set<TaintBB> ret = new HashSet<>();
        for (BasicBlock bb : subBlock) {
            TaintBB ntbb = (TaintBB) bb;
            ntbb.preResult.mergeResult(t.sucResult);
            // System.out.println(
            // "XXXXXB" + ntbb.blockID + " mergedBlock:" +
            // ntbb.preResult.frame2Str() + " "
            // + ntbb.preResult);
            ret.add(ntbb);
        }
        // System.out.println("---------------------------------");
        return ret;
    }

}
