#4 register maybe used before calling its constructor

https://github.com/pxb1988/dex2jar/issues/4

--HG--
branch : 2.x
This commit is contained in:
Bob Pan 2015-05-17 18:12:39 +08:00
parent d4746863a9
commit 45d7036a97
2 changed files with 333 additions and 92 deletions

View File

@ -16,132 +16,91 @@
*/
package com.googlecode.dex2jar.ir.ts;
import com.googlecode.dex2jar.ir.ET;
import com.googlecode.dex2jar.ir.IrMethod;
import com.googlecode.dex2jar.ir.StmtTraveler;
import com.googlecode.dex2jar.ir.expr.*;
import com.googlecode.dex2jar.ir.stmt.AssignStmt;
import com.googlecode.dex2jar.ir.stmt.LabelStmt;
import com.googlecode.dex2jar.ir.stmt.Stmt;
import com.googlecode.dex2jar.ir.stmt.Stmts;
import java.util.*;
import static com.googlecode.dex2jar.ir.stmt.Stmt.ST.*;
import static com.googlecode.dex2jar.ir.expr.Value.VT.*;
import static com.googlecode.dex2jar.ir.stmt.Stmt.ST.*;
/**
* simply merge
*
* <p/>
* <pre>
* a=NEW Labc;
* a.<init>();
* </pre>
*
* <p/>
* to
*
* <p/>
* <pre>
* a = new abc();
* </pre>
*
* <p/>
* Run after [SSATransformer, RemoveLocalFromSSA]
*/
public class NewTransformer implements Transformer {
static Vx IGNORED = new Vx(null, true);
@Override
public void transform(IrMethod method) {
final Map<Local, NewExpr> nAssign = new HashMap<>();
final Map<Local, AssignStmt> init = new HashMap<>();
for (Iterator<Stmt> it = method.stmts.iterator(); it.hasNext();) {
Stmt p = it.next();
// 1. replace
// =========
// a=NEW Abc;
// b=a
// b.<init>()
// to ======
// a=new Abc();
// b=a;
// =========
replaceX(method);
// 2. replace NEW Abc;.<init>() -> new Abc();
replaceAST(method);
}
void replaceX(IrMethod method) {
final Map<Local, TObject> init = new HashMap<>();
for (Stmt p : method.stmts) {
if (p.st == ASSIGN && p.getOp1().vt == LOCAL && p.getOp2().vt == NEW) {
// the stmt is a new assign stmt
Local local = (Local) p.getOp1();
nAssign.put(local, (NewExpr) p.getOp2());
init.put(local, (AssignStmt) p);
}
}
if (nAssign.size() == 0) {
return;
}
int[] reads = Cfg.countLocalReads(method);
final Set<Local> oneOrLess = new HashSet<>();
final boolean changed[] = { true };
while (changed[0]) {
changed[0] = false;
for (Local local : nAssign.keySet()) {
if (reads[local._ls_index] < 2) {
oneOrLess.add(local);
method.stmts.remove(init.remove(local));
method.locals.remove(local);
}
}
if (oneOrLess.size() > 0) {
new StmtTraveler() {
@Override
public Stmt travel(Stmt stmt) {
Stmt p = super.travel(stmt);
if (p.st == ASSIGN && p.getOp1().vt == LOCAL && p.getOp2().vt == NEW) {
// the stmt is a new assign stmt
Local local = (Local) p.getOp1();
if (!nAssign.containsKey(local)) {
nAssign.put(local, (NewExpr) p.getOp2());
init.put(local, (AssignStmt) p);
changed[0] = true;
}
}
return p;
}
@Override
public Value travel(Value op) {
if (op.vt == LOCAL) {
Local local = (Local) op;
if (oneOrLess.contains(local)) {
return nAssign.get(local);
}
}
return super.travel(op);
}
}.travel(method.stmts);
for (Local local : oneOrLess) {
nAssign.remove(local);
}
oneOrLess.clear();
init.put(local, new TObject(local, (AssignStmt) p));
}
}
Set<Local> replaced = new HashSet<>();
for (Iterator<Stmt> it = method.stmts.iterator(); it.hasNext();) {
if (init.size() > 0) {
final int size = Cfg.reIndexLocal(method);
makeSureUsedBeforeConstructor(method, init, size);
if (init.size() > 0) {
replace0(method, init, size);
}
for (Stmt stmt : method.stmts) {
stmt.frame = null;
}
}
}
void replaceAST(IrMethod method) {
for (Iterator<Stmt> it = method.stmts.iterator(); it.hasNext(); ) {
Stmt p = it.next();
InvokeExpr ie = null;
if (p.st == ASSIGN) {
if (p.getOp2().vt == INVOKE_SPECIAL) {
ie = (InvokeExpr) p.getOp2();
}
} else if (p.st == VOID_INVOKE) {
ie = (InvokeExpr) p.getOp();
}
InvokeExpr ie = findInvokeExpr(p, null);
if (ie != null) {
if ("<init>".equals(ie.name) && "V".equals(ie.ret)) {
Value[] orgOps = ie.getOps();
if (orgOps[0].vt == LOCAL) {
Local objToInit = (Local) ie.getOps()[0];
NewExpr newExpr = nAssign.get(objToInit);
if (newExpr != null) {
if (!ie.owner.equals(newExpr.type)) {
throw new RuntimeException("");
}
Value[] nOps = new Value[orgOps.length - 1];
System.arraycopy(orgOps, 1, nOps, 0, nOps.length);
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.args, ie.owner);
method.stmts.insertBefore(p, Stmts.nAssign(objToInit, invokeNew));
it.remove();
replaced.add(objToInit);
}
} else if (orgOps[0].vt == NEW) {
if (orgOps[0].vt == NEW) {
NewExpr newExpr = (NewExpr) ie.getOps()[0];
if (newExpr != null) {
Value[] nOps = new Value[orgOps.length - 1];
@ -151,13 +110,283 @@ public class NewTransformer implements Transformer {
it.remove();
}
}
}
}
}
nAssign.clear();
for (Local x : replaced) {
method.stmts.remove(init.remove(x));
}
void replace0(IrMethod method, Map<Local, TObject> init, int size) {
Set<Local> toDelete = new HashSet<>();
Local locals[] = new Local[size];
for (Local local : method.locals) {
locals[local._ls_index] = local;
}
// find all locals to delete
for (TObject obj : init.values()) {
Vx[] frame = (Vx[]) obj.invokeStmt.frame;
for (int i = 0; i < frame.length; i++) {
Vx s = frame[i];
if (s != null && s.obj == obj) {
toDelete.add(locals[i]);
}
}
}
// delete the locals
for (Iterator<Stmt> it = method.stmts.iterator(); it.hasNext(); ) {
Stmt p = it.next();
if (p.st == ASSIGN && p.getOp1().vt == LOCAL) {
if (toDelete.contains((Local) p.getOp1())) {
it.remove();
}
}
}
// add the locals back
for (TObject obj : init.values()) {
Vx[] frame = (Vx[]) obj.invokeStmt.frame;
for (int i = 0; i < frame.length; i++) {
Vx s = frame[i];
if (s != null && s.obj == obj) {
Local b = locals[i];
if (b != obj.local) {
method.stmts.insertAfter(obj.invokeStmt, Stmts.nAssign(b, obj.local));
}
}
}
InvokeExpr ie = findInvokeExpr(obj.invokeStmt, null);
Value[] orgOps = ie.getOps();
Value[] nOps = new Value[orgOps.length - 1];
System.arraycopy(orgOps, 1, nOps, 0, nOps.length);
InvokeExpr invokeNew = Exprs.nInvokeNew(nOps, ie.args, ie.owner);
method.stmts.replace(obj.invokeStmt, Stmts.nAssign(obj.local, invokeNew));
}
}
void makeSureUsedBeforeConstructor(IrMethod method, final Map<Local, TObject> init, final int size) {
Cfg.createCFG(method);
Cfg.dfs(method.stmts, new Cfg.FrameVisitor<Vx[]>() {
boolean keepFrame = false;
Vx[] tmp = new Vx[size];
StmtTraveler stmtTraveler = new StmtTraveler() {
Stmt current;
@Override
public Stmt travel(Stmt stmt) {
this.current = stmt;
if (stmt.et == ET.E2) {
if (stmt.getOp1().vt == LOCAL) {
Local op1 = (Local) stmt.getOp1();
if (stmt.getOp2().vt == LOCAL) {
Local op2 = (Local) stmt.getOp2();
tmp[op1._ls_index] = tmp[op2._ls_index];
return stmt;
} else if (stmt.getOp2().vt == NEW) {
tmp[op1._ls_index] = new Vx(init.get(op1), false);
return stmt;
} else {
travel(stmt.getOp2());
tmp[op1._ls_index] = IGNORED;
return stmt;
}
}
}
if (stmt.st == LABEL) {
LabelStmt labelStmt = (LabelStmt) stmt;
if (labelStmt.phis != null) {
for (AssignStmt phi : labelStmt.phis) {
Local local = (Local) phi.getOp1();
tmp[local._ls_index] = IGNORED;
}
}
return stmt;
}
return super.travel(stmt);
}
@Override
public Value travel(Value op) {
if (op.vt == INVOKE_SPECIAL) {
if (op.getOps().length >= 1) {
InvokeExpr ie = (InvokeExpr) op;
if ("<init>".equals(ie.name)) {
Value thiz = op.getOps()[0];
if (thiz.vt == LOCAL) {
Local local = (Local) thiz;
Vx vx = tmp[local._ls_index];
TObject object = vx.obj;
if (object != null) {
if (object.invokeStmt != null) {
object.useBeforeInit = true;
} else {
vx.init = true;
object.invokeStmt = current;
for (int i = 0; i < tmp.length; i++) {
Vx s = tmp[i];
if (s != null && s.obj == object) {
tmp[i] = IGNORED;
}
}
keepFrame = true;
}
}
}
}
}
}
op = super.travel(op);
if (op.vt == LOCAL) {
use((Local) op);
}
return op;
}
};
@Override
public Vx[] merge(Vx[] srcFrame, Vx[] distFrame, Stmt src, Stmt dist) {
if (distFrame == null) {
distFrame = new Vx[size];
System.arraycopy(srcFrame, 0, distFrame, 0, size);
} else {
for (int i = 0; i < size; i++) {
Vx s = srcFrame[i];
Vx d = distFrame[i];
if (s != null) {
if (d == null) {
distFrame[i] = s;
} else {
if (s != d) {
TObject obj = s.obj;
if (obj != null) {
obj.useBeforeInit = true;
}
obj = d.obj;
if (obj != null) {
obj.useBeforeInit = true;
}
}
}
}
}
}
if (dist.st == LABEL) {
List<AssignStmt> phis = ((LabelStmt) dist).phis;
if (phis != null && phis.size() > 0) {
for (AssignStmt phi : phis) {
for (Value value : phi.getOp2().getOps()) {
Local local = (Local) value;
int i = local._ls_index;
Vx s = srcFrame[i];
Vx d = distFrame[i];
if (d != null) {
if (!d.init) {
TObject obj = d.obj;
if (obj != null) {
obj.useBeforeInit = true;
}
}
} else if (s != null) {
if (!s.init) {
TObject obj = s.obj;
if (obj != null) {
obj.useBeforeInit = true;
}
}
}
}
}
}
}
return distFrame;
}
@Override
public Vx[] initFirstFrame(Stmt first) {
return new Vx[size];
}
@Override
public Vx[] exec(Vx[] frame, Stmt stmt) {
keepFrame = false;
System.arraycopy(frame, 0, tmp, 0, size);
stmtTraveler.travel(stmt);
if (stmt._cfg_froms.size() > 1) {
keepFrame = true;
}
if (!keepFrame) {
stmt.frame = null;
}
return tmp;
}
void use(Local local) {
Vx vx = tmp[local._ls_index];
if (!vx.init) {
TObject object = vx.obj;
if (object != null) {
object.useBeforeInit = true;
}
tmp[local._ls_index] = IGNORED;
}
}
});
for (Iterator<Map.Entry<Local, TObject>> iterator = init.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<Local, TObject> e = iterator.next();
boolean keep = true;
TObject obj = e.getValue();
if (obj.useBeforeInit) {
keep = false;
}
if (obj.invokeStmt == null) {
keep = false;
}
if (!keep) {
iterator.remove();
}
}
}
InvokeExpr findInvokeExpr(Stmt p, InvokeExpr ie) {
if (p.st == ASSIGN) {
if (p.getOp2().vt == INVOKE_SPECIAL) {
ie = (InvokeExpr) p.getOp2();
}
} else if (p.st == VOID_INVOKE) {
ie = (InvokeExpr) p.getOp();
}
return ie;
}
static class TObject {
public Stmt invokeStmt;
Local local;
boolean useBeforeInit;
private AssignStmt init;
TObject(Local local, AssignStmt init) {
this.local = local;
this.init = init;
}
}
static class Vx {
boolean init;
TObject obj;
public Vx(TObject obj, boolean init) {
this.obj = obj;
this.init = init;
}
}
}

View File

@ -0,0 +1,12 @@
.class Lgh/i4;
.super Ljava/lang/Object;
.method static public a(LIndenter;)Ljava/lang/Object;
.registers 2
if-nez v1, :L0
new-instance v0, LNopIndenter;
move-object v1, v0 #### use before init
invoke-direct { v0 }, LNopIndenter;-><init>()V
:L0
return-object v1
.end method