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

com.googlecode.dex2jar.ir.ts.AggTransformer Maven / Gradle / Ivy

package com.googlecode.dex2jar.ir.ts;

import com.googlecode.dex2jar.ir.IrMethod;
import com.googlecode.dex2jar.ir.expr.InvokeExpr;
import com.googlecode.dex2jar.ir.expr.Local;
import com.googlecode.dex2jar.ir.expr.Value;
import com.googlecode.dex2jar.ir.stmt.AssignStmt;
import com.googlecode.dex2jar.ir.stmt.LabelStmt;
import com.googlecode.dex2jar.ir.stmt.Stmt;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

public class AggTransformer extends StatedTransformer {

    @Override
    public boolean transformReportChanged(IrMethod method) {
        boolean changed = false;


        Set locationSensitiveStmts = new HashSet<>();
        // 1. merge location Insensitive stmts
        changed = simpleMergeLocals(method, changed, locationSensitiveStmts);

        if (locationSensitiveStmts.isEmpty()) {
            return changed;
        }

        ReplaceX replaceX = new ReplaceX();
        Queue q = new UniqueQueue<>();
        q.addAll(locationSensitiveStmts);

        // 2. merge location sensitive stmts
        while (!q.isEmpty()) {
            Stmt stmt = q.poll();
            Local local = (Local) stmt.getOp1();
            Stmt next = stmt.getNext();

            switch (next.st) {
            case LABEL:
            case GOTO:
            case IDENTITY:
            case FILL_ARRAY_DATA:
            case NOP:
            case RETURN_VOID:
                continue;
            default:
            }
            try {
                localCanExecFirst(local, next);
                throw new RuntimeException(); // impossible here
            } catch (MergeResult e) {
                if (e == SUCCESS) {
                    replaceX.local = local;
                    replaceX.replaceWith = stmt.getOp2();
                    method.locals.remove(local);
                    method.stmts.remove(stmt);

                    Cfg.travelMod(next, replaceX, false);

                    Stmt pre = next.getPre();
                    if (pre != null && locationSensitiveStmts.contains(pre)) {
                        q.add(pre);
                    }

                }
            }
        }


        return changed;
    }

    /**
     * dfs find find local and the first locationInsensitive Value
     * // TODO if can not merge, try adjust the stmt to fit the local
     */
    private static void localCanExecFirst(Local local, Stmt target) throws MergeResult {

        switch (target.et) {
        case E0: // impossible
        case En: // no EnStmt yet
            throw FAIL;
        case E1:
            localCanExecFirst(local, target.getOp());
            break;
        case E2:
            AssignStmt as = (AssignStmt) target;
            Value op1 = as.getOp1();
            Value op2 = as.getOp2();
            switch (op1.vt) {
            case LOCAL:
                localCanExecFirst(local, op2);
                break;
            case FIELD:
                localCanExecFirst(local, op1.getOp());
                // pass through
            case STATIC_FIELD:
                localCanExecFirst(local, op2);
                break;
            case ARRAY:
                localCanExecFirst(local, op1.getOp1());
                localCanExecFirst(local, op1.getOp2());
                localCanExecFirst(local, op2);
                break;
            default:
            }
            break;
        default:
            break;
        }
        throw FAIL;
    }

    private static final MergeResult FAIL = new MergeResult();

    private static final MergeResult SUCCESS = new MergeResult();

    /**
     * dfs searching, if local is appear before first location-insensitive value, throws SUCCESS, or throws FAIL
     */
    private static void localCanExecFirst(Local local, Value op) throws MergeResult {
        switch (op.et) {
        case E0:
            if (local.vt == Value.VT.LOCAL) {
                if (op == local) {
                    throw SUCCESS;
                }
            }
            break;
        case E1:
            localCanExecFirst(local, op.getOp());
            break;
        case E2:
            localCanExecFirst(local, op.getOp1());
            localCanExecFirst(local, op.getOp2());
            break;
        case En:
            for (Value v : op.getOps()) {
                localCanExecFirst(local, v);
            }
        default:
            break;
        }

        boolean shouldExclude = false;
        if (op.vt == Value.VT.INVOKE_STATIC) {
            InvokeExpr ie = (InvokeExpr) op;
            if (ie.getName().equals("valueOf") && ie.getOwner().startsWith("Ljava/lang/")
                    && ie.getArgs().length == 1 && ie.getArgs()[0].length() == 1) {
                shouldExclude = true;
            }
        }

        if (!isLocationInsensitive(op.vt) && !shouldExclude) {  // this is the first insensitive Value
            throw FAIL;
        }
    }

    static class MergeResult extends Throwable {

        private static final long serialVersionUID = -1563502983848655360L;

    }

    static class ReplaceX implements Cfg.TravelCallBack {

        Local local;

        Value replaceWith;

        @Override
        public Value onAssign(Local v, AssignStmt as) {
            return v;
        }

        @Override
        public Value onUse(Local v) {
            if (v == local) {
                return replaceWith;
            }
            return v;
        }

    }

    /**
     * if a local is only used in one place, and the value is isLocationInsensitive,
     * remove the local and replace it with its value
     * 
     *     a=b+c
     *     d=a+e
     * 
* to *
     *     d=(b+c)+e
     * 
*/ private boolean simpleMergeLocals(IrMethod method, boolean changed, Set locationSensitiveStmts) { if (method.locals.isEmpty()) { return false; } final int[] readCounts = Cfg.countLocalReads(method); Set useInPhi = collectLocalUsedInPhi(method); final Map toReplace = new HashMap<>(); Iterator it = method.stmts.iterator(); while (it.hasNext()) { Stmt p = it.next(); if (p.st == Stmt.ST.ASSIGN && p.getOp1().vt == Value.VT.LOCAL) { Local local = (Local) p.getOp1(); if (useInPhi.contains(local)) { continue; } if (readCounts[local.lsIndex] < 2) { Value op2 = p.getOp2(); if (isLocationInsensitive(op2)) { method.locals.remove(local); toReplace.put(local, op2); it.remove(); changed = true; } else { locationSensitiveStmts.add(p); } } } } Cfg.TravelCallBack tcb = new Cfg.TravelCallBack() { @Override public Value onAssign(Local v, AssignStmt as) { return v; } @Override public Value onUse(Local v) { Value v2 = toReplace.get(v); if (v2 != null) { return v2; } return v; } }; modReplace(toReplace, tcb); Cfg.travelMod(method.stmts, tcb, false); return changed; } private Set collectLocalUsedInPhi(IrMethod method) { Set useInPhi = new HashSet<>(); if (method.phiLabels != null) { for (LabelStmt labelStmt : method.phiLabels) { if (labelStmt.phis != null) { for (AssignStmt phi : labelStmt.phis) { useInPhi.add((Local) phi.getOp1()); for (Value op : phi.getOp2().getOps()) { useInPhi.add((Local) op); } } } } } return useInPhi; } private void modReplace(Map toReplace, Cfg.TravelCallBack tcb) { for (Map.Entry e : toReplace.entrySet()) { Value v = e.getValue(); if (v.vt == Value.VT.LOCAL) { while (true) { Value v2 = toReplace.get(v); if (v2 == null) { break; } v = v2; if (v.vt != Value.VT.LOCAL) { break; } } e.setValue(v); } else { Cfg.travelMod(v, tcb); } } } static boolean isLocationInsensitive(Value.VT vt) { switch (vt) { case LOCAL: case CONSTANT: // +-*/ case ADD: case SUB: case MUL: // case DIV: // div is not case REM: // logical case AND: case OR: case XOR: case SHL: case SHR: case USHR: //cmp case GE: case GT: case LE: case LT: case EQ: case NE: case DCMPG: case DCMPL: case LCMP: case FCMPG: case FCMPL: case NOT: return true; default: } return false; } static boolean isLocationInsensitive(Value op) { switch (op.et) { case E0: return isLocationInsensitive(op.vt); case E1: return isLocationInsensitive(op.vt) && isLocationInsensitive(op.getOp()); case E2: return isLocationInsensitive(op.vt) && isLocationInsensitive(op.getOp1()) && isLocationInsensitive(op.getOp2()); case En: if (op.vt == Value.VT.INVOKE_STATIC) { InvokeExpr ie = (InvokeExpr) op; if (ie.getName().equals("valueOf") && ie.getOwner().startsWith("Ljava/lang/") && ie.getArgs().length == 1 && ie.getArgs()[0].length() == 1) { for (Value v : op.getOps()) { if (!isLocationInsensitive(v)) { return false; } } return true; } return false; } if (isLocationInsensitive(op.vt)) { for (Value v : op.getOps()) { if (!isLocationInsensitive(v)) { return false; } } return true; } return false; default: return false; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy