com.googlecode.dex2jar.ir.ts.RemoveLocalFromSSA Maven / Gradle / Ivy
The newest version!
/*
* dex2jar - Tools to work with android .dex and java .class files
* Copyright (c) 2009-2013 Panxiaobo
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.googlecode.dex2jar.ir.ts;
import java.util.*;
import com.googlecode.dex2jar.ir.IrMethod;
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 com.googlecode.dex2jar.ir.stmt.StmtList;
public class RemoveLocalFromSSA extends StatedTransformer {
static void replaceAssign(List assignStmtList, Map toReplace) {
for (AssignStmt as : assignStmtList) {
Value right = as.getOp2();
T to = toReplace.get(right);
if (to != null) {
as.setOp2(to);
}
}
}
private boolean simpleAssign(List phiLabels, List assignStmtList,
Map toReplace, StmtList stmts) {
Set usedInPhi = new HashSet<>();
if (phiLabels != null) {
for (LabelStmt labelStmt : phiLabels) {
for (AssignStmt phi : labelStmt.phis) {
usedInPhi.addAll(Arrays.asList(phi.getOp2().getOps()));
}
}
}
boolean changed = false;
for (Iterator it = assignStmtList.iterator(); it.hasNext(); ) {
AssignStmt as = it.next();
if (!usedInPhi.contains(as.getOp1())) {
it.remove();
stmts.remove(as);
toReplace.put((Local) as.getOp1(), (Local) as.getOp2());
changed = true;
}
}
return changed;
}
private void replacePhi(List phiLabels, Map toReplace, Set set) {
if (phiLabels != null) {
for (LabelStmt labelStmt : phiLabels) {
for (AssignStmt phi : labelStmt.phis) {
Value[] ops = phi.getOp2().getOps();
for (Value op : ops) {
Value n = toReplace.get(op);
if (n != null) {
set.add(n);
} else {
set.add(op);
}
}
set.remove(phi.getOp1());
phi.getOp2().setOps(set.toArray(new Value[set.size()]));
set.clear();
}
}
}
}
static class PhiObject {
Set parent = new HashSet<>();
Set children = new HashSet<>();
Local local;
boolean isInitByPhi = false;
}
public static PhiObject getOrCreate(Map map, Local local) {
PhiObject po = map.get(local);
if (po == null) {
po = new PhiObject();
po.local = local;
map.put(local, po);
}
return po;
}
public static void linkPhiObject(PhiObject parent, PhiObject child) {
parent.children.add(child);
child.parent.add(parent);
}
private boolean simplePhi(List phiLabels, Map toReplace, Set set) {
boolean changed = false;
if (phiLabels != null) {
for (Iterator itLabel = phiLabels.iterator(); itLabel.hasNext(); ) {
LabelStmt labelStmt = itLabel.next();
for (Iterator it = labelStmt.phis.iterator(); it.hasNext(); ) {
AssignStmt phi = it.next();
set.addAll(Arrays.asList(phi.getOp2().getOps()));
set.remove(phi.getOp1());
if (set.size() == 1) {
it.remove();
changed = true;
toReplace.put((Local) phi.getOp1(), (Local) set.iterator().next());
}
set.clear();
}
if (labelStmt.phis.size() == 0) {
labelStmt.phis = null;
itLabel.remove();
}
}
}
return changed;
}
private boolean removeLoopFromPhi(List phiLabels, Map toReplace) {
boolean changed = false;
if (phiLabels != null) {
Set toDeletePhiAssign = new HashSet<>();
Map phis;
// detect loop init in phi
phis = collectPhiObjects(phiLabels);
Queue q = new UniqueQueue<>();
q.addAll(phis.values());
while (!q.isEmpty()) {
PhiObject po = q.poll();
for (PhiObject child : po.children) {
if (child.isInitByPhi) {
if (child.parent.addAll(po.parent)) {
q.add(child);
}
}
}
}
for (PhiObject po : phis.values()) {
if (po.isInitByPhi) {
Local local = null;
for (PhiObject p : po.parent) {
if (!p.isInitByPhi) {
if (local == null) { // the first non-phi value
local = p.local;
} else {
local = null;
break;
}
}
}
if (local != null) {
toReplace.put(po.local, local);
toDeletePhiAssign.add(po.local);
changed = true;
}
}
}
for (Iterator itLabel = phiLabels.iterator(); itLabel.hasNext(); ) {
LabelStmt labelStmt = itLabel.next();
for (Iterator it = labelStmt.phis.iterator(); it.hasNext(); ) {
AssignStmt phi = it.next();
if (toDeletePhiAssign.contains(phi.getOp1())) {
it.remove();
}
}
if (labelStmt.phis.size() == 0) {
labelStmt.phis = null;
itLabel.remove();
}
}
}
return changed;
}
private Map collectPhiObjects(List phiLabels) {
Map phis;
phis = new HashMap<>();
for (LabelStmt labelStmt : phiLabels) {
for (AssignStmt as : labelStmt.phis) {
Local local = (Local) as.getOp1();
PhiObject child = getOrCreate(phis, local);
child.isInitByPhi = true;
for (Value op : as.getOp2().getOps()) {
if (op == local) {
continue;
}
PhiObject parent = getOrCreate(phis, (Local) op);
linkPhiObject(parent, child);
}
}
}
return phis;
}
static void fixReplace(Map toReplace) {
List> set = new ArrayList<>(toReplace.entrySet());
Collections.sort(set, new Comparator>() {
@Override
public int compare(Map.Entry localTEntry, Map.Entry t1) {
return Integer.compare(localTEntry.getKey()._ls_index, t1.getKey()._ls_index);
}
});
boolean changed = true;
while (changed) {
changed = false;
for (Map.Entry e : set) {
T b = e.getValue();
if(b instanceof Local) {
T n = toReplace.get(b);
if (n != null && b != n) {
changed = true;
e.setValue(n);
}
}
}
}
}
@Override
public boolean transformReportChanged(IrMethod method) {
boolean irChanged = false;
List assignStmtList = new ArrayList<>();
List phiLabels = method.phiLabels;
for (Stmt p = method.stmts.getFirst(); p != null; p = p.getNext()) {
if (p.st == Stmt.ST.ASSIGN) {
AssignStmt as = (AssignStmt) p;
if (as.getOp1().vt == Value.VT.LOCAL && as.getOp2().vt == Value.VT.LOCAL) {
assignStmtList.add(as);
}
}
}
final Map toReplace = new HashMap<>();
Set set = new HashSet<>();
boolean changed = true;
while (changed) {
changed = false;
if (removeLoopFromPhi(phiLabels, toReplace)) {
fixReplace(toReplace);
replacePhi(phiLabels, toReplace, set);
}
while (simplePhi(phiLabels, toReplace, set)) {// remove a = phi(b)
fixReplace(toReplace);
replacePhi(phiLabels, toReplace, set);
}
while (simpleAssign(phiLabels, assignStmtList, toReplace, method.stmts)) {// remove a=b
fixReplace(toReplace);
replaceAssign(assignStmtList, toReplace);
changed = true;
irChanged = true;
}
replacePhi(phiLabels, toReplace, set);
}
for (Local local : toReplace.keySet()) {
method.locals.remove(local);
irChanged = true;
}
if (toReplace.size() > 0) {
Cfg.travelMod(method.stmts, new Cfg.TravelCallBack() {
@Override
public Value onAssign(Local v, AssignStmt as) {
return v;
}
@Override
public Value onUse(Local v) {
Local n = toReplace.get(v);
return n == null ? v : n;
}
}, true);
}
return irChanged;
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy