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

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

The newest version!
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.*;

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.size() == 0) {
            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;
        }
        throw FAIL;
    }

    private static MergeResult FAIL = new MergeResult();
    private static 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);
                }
        }

        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 {

    }

    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.size() == 0) { return false; } final int[] readCounts = Cfg.countLocalReads(method); Set useInPhi = collectLocalUsedInPhi(method); final Map toReplace = new HashMap<>(); for (Iterator it = method.stmts.iterator(); 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._ls_index] < 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; } return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy