@Test public void test() { IrMethod jm = new IrMethod(); Local a = nLocal("a"); Local b = nLocal("b"); Local c = nLocal("c"); jm.locals.add(a); jm.locals.add(b); jm.locals.add(c); StmtList list = jm.stmts; LabelStmt L1 = nLabel(); L1.displayName = "L1"; LabelStmt L2 = nLabel(); L2.displayName = "L2"; LabelStmt L3 = nLabel(); L3.displayName = "L3"; LabelStmt L4 = nLabel(); L4.displayName = "L4"; AssignStmt st1 = nAssign(a, nInt(0)); list.add(st1); list.add(nIf(nInt(1), L4)); list.add(L1); list.add(nIf(nInt(0), L3)); list.add(L2); AssignStmt st2 = nAssign(c, a); list.add(st2); list.add(nGoto(L1)); list.add(L3); AssignStmt st3 = nAssign(b, a); list.add(st3); list.add(nAssign(a, nString("aaaa"))); list.add(L4); list.add(nReturn(a)); new ZeroTransformer().transform(jm); Assert.assertEquals(((Constant) st2.op2.value).value, Integer.valueOf(0)); Assert.assertEquals(((Constant) st3.op2.value).value, Integer.valueOf(0)); }
/** * the statements must be simplest. * * <pre> * a = b + c; // ok * a = b[0] + c;// may cause exception, must expend to tmp1=b[0];a=tmp1+c; * </pre> * * @see com.googlecode.dex2jar.ir.ts.Transformer#transform(com.googlecode.dex2jar.ir.IrMethod) */ @Override public void transform(IrMethod je) { StmtList list = je.stmts; List<Stmt> orderList = list._ls_visit_order; for (int p = 0; p < orderList.size(); p++) { Stmt st = orderList.get(p); if (st == null || !list.contains(st)) { continue; } switch (st.st) { case ASSIGN: case IDENTITY: AssignStmt as = (AssignStmt) st; if (as.op1.value.vt == VT.LOCAL) { Local aLeft = (Local) as.op1.value; if (as.op2.value.vt == VT.NEW) { // remove new NewExpr c = (NewExpr) as.op2.value; boolean replaced = false; for (Iterator<AssignStmt> it = list._ls_inits.iterator(); it.hasNext(); ) { AssignStmt stmt = it.next(); InvokeExpr ie = (InvokeExpr) stmt.op2.value; if (ie.ops[0].value == aLeft && ie.methodOwnerType.equals(c.type)) { list.remove(st); it.remove(); je.locals.remove(stmt.op1.value); ValueBox[] vb = new ValueBox[ie.ops.length - 1]; System.arraycopy(ie.ops, 1, vb, 0, vb.length); AssignStmt nas = Stmts.nAssign( as.op1, new ValueBox(Exprs.nInvokeNew(vb, ie.argmentTypes, ie.methodOwnerType))); list.replace(stmt, nas); aLeft._ls_read_count--; orderList.set(orderList.indexOf(stmt), nas); replaced = true; break; } } if (replaced) { continue; } } if (aLeft._ls_write_count == 1) { switch (as.op2.value.vt) { case LOCAL: { Local b = (Local) as.op2.value; if (b._ls_write_count == 1) { // if b is only write for once b._ls_read_count += aLeft._ls_read_count - 1; je.locals.remove(aLeft); aLeft._ls_vb.value = b; list.remove(st); orderList.set(p, null); } continue; } case CONSTANT: { as.op1.value = as.op2.value; je.locals.remove(aLeft); list.remove(st); orderList.set(p, null); continue; } } } } } } /* * Merge some simple filled arrays * merge: * a[][]=new xx[2][];a[0]=new xx[2];a[0][0]=b;a[0][1]=c;a[1]=new xx[2];a[1][0]=d;a[1][1]=e; -> a[][]=new xx[][]{{b,c},{d,e}}; * tmp[][]=new xx[][]{{b,c},{d,e}};a=tmp; -> a=new xx[][]{{b,c},{d,e}}; * not merge: * a[]=new xx[3];a[0]=b;a[1]=c;(Not full) * a[]=new xx[2];a[0]=b;a[1]=c.gg();(Not simple) */ boolean changed = false; do { changed = false; for (int p = 0; p < orderList.size(); p++) { Stmt st = orderList.get(p); if (st == null || !list.contains(st)) { continue; } switch (st.st) { case ASSIGN: AssignStmt as = (AssignStmt) st; if (as.op2.value.vt == VT.NEW_ARRAY) { TypeExpr te = (TypeExpr) as.op2.value; Value val = as.op1.value; if (te.op.value instanceof Constant) { int arraySize = (Integer) ((Constant) te.op.value).value; Type type = te.type; int size = 0; int empty = 0; // Verify array data for (int j = 1; j < orderList.size() - p; j++) { Stmt st2 = orderList.get(p + j); if (st2 == null) { empty++; continue; } if (st2.st == ST.ASSIGN) { AssignStmt as2 = (AssignStmt) st2; if (as2.op1.value.vt == VT.ARRAY) { ArrayExpr ae2 = (ArrayExpr) as2.op1.value; if ((ae2.op1.value == val) && (ae2.op2.value instanceof Constant)) { int idx = (Integer) ((Constant) ae2.op2.value).value; if (idx == (j - empty - 1)) { continue; } } } } size = j - 1; break; } int dataSize = size - empty; if (dataSize == arraySize) { // Not full array may cause some NullPoint problems Value[] vbs = new Value[arraySize]; for (int j = 1; j <= size; j++) { Stmt st2 = orderList.get(p + j); if (st2 == null) { continue; } AssignStmt as2 = (AssignStmt) st2; ArrayExpr ae2 = (ArrayExpr) as2.op1.value; int idx = ((Integer) ((Constant) ae2.op2.value).value); vbs[idx] = as2.op2.value; orderList.set((p + j), null); list.remove(st2); } Local loc = (Local) val; loc._ls_read_count -= dataSize; FilledArrayExpr fa = Exprs.nFilledArray(type, vbs); AssignStmt nas = Stmts.nAssign(loc, fa); list.replace(st, nas); orderList.set(p, nas); changed = true; // Merge tmp Locals if (loc._ls_write_count == 1 && loc._ls_read_count == 1) { Stmt st3 = null; for (int j = size + 1; j < orderList.size() - p; j++) { st3 = orderList.get(p + j); if (st3 != null) { break; } } if (st3 != null && (st3.st == ST.ASSIGN || st3.st == ST.IDENTITY)) { AssignStmt as3 = (AssignStmt) st3; if (as3.op2.value == loc) { loc._ls_read_count = 0; loc._ls_write_count = 0; list.remove(nas); orderList.set(p, null); AssignStmt nas3 = Stmts.nAssign(as3.op1.value, fa); list.replace(st3, nas3); orderList.set(orderList.indexOf(st3), nas3); je.locals.remove(loc); } } } } } } } } } while (changed); { List<ValueBox> vbs = new ArrayList<ValueBox>(20); Stack<ValueBox> tmp = new Stack<ValueBox>(); for (int p = 0; p < orderList.size(); p++) { Stmt st = orderList.get(p); if (st == null || !list.contains(st)) { continue; } switch (st.st) { case RETURN_VOID: case LABEL: case GOTO: case NOP: case IDENTITY: continue; } Stmt pre = st.getPre(); if (pre == null) { continue; } if (canRemove(pre)) { vbs.clear(); tmp.clear(); AssignStmt as = (AssignStmt) pre; Local preLocal = (Local) as.op1.value; execStmt(vbs, st, preLocal); for (ValueBox vb : vbs) { switch (vb.value.vt) { case CONSTANT: continue; case LOCAL: tmp.push(vb); continue; } break; } while (!tmp.isEmpty()) { ValueBox vb = tmp.pop(); if (vb.value == preLocal) { vb.value = as.op2.value; list.remove(as); je.locals.remove(preLocal); pre = st.getPre(); if (pre != null && canRemove(pre)) { as = (AssignStmt) pre; preLocal = (Local) as.op1.value; } else { break; } } } } } } if (list._ls_inits.size() > 0) { // replace new again for (int p = 0; p < orderList.size(); p++) { Stmt st = orderList.get(p); if (st == null || !list.contains(st)) { continue; } switch (st.st) { case ASSIGN: case IDENTITY: AssignStmt as = (AssignStmt) st; if (as.op1.value.vt == VT.LOCAL) { Local aLeft = (Local) as.op1.value; if (as.op2.value.vt == VT.NEW) { // remove new NewExpr c = (NewExpr) as.op2.value; boolean replaced = false; for (Iterator<AssignStmt> it = list._ls_inits.iterator(); it.hasNext(); ) { AssignStmt stmt = it.next(); InvokeExpr ie = (InvokeExpr) stmt.op2.value; if (ie.ops[0].value == aLeft && ie.methodOwnerType.equals(c.type)) { list.remove(st); it.remove(); ValueBox[] vb = new ValueBox[ie.ops.length - 1]; System.arraycopy(ie.ops, 1, vb, 0, vb.length); AssignStmt nas = Stmts.nAssign( as.op1, new ValueBox( Exprs.nInvokeNew(vb, ie.argmentTypes, ie.methodOwnerType))); list.replace(stmt, nas); aLeft._ls_read_count--; orderList.set(orderList.indexOf(stmt), nas); replaced = true; break; } } if (replaced) { continue; } } } } } } list._ls_inits = null; }