use of org.mapleir.stdlib.collections.map.NullPermeableHashMap in project maple-ir by LLVM-but-worse.
the class SSAGenPass method splitRanges.
private void splitRanges() {
// produce cleaner cfg
List<BasicBlock> order = new ArrayList<>(builder.graph.vertices());
NullPermeableHashMap<BasicBlock, Set<Local>> splits = new NullPermeableHashMap<>(SetCreator.getInstance());
for (ExceptionRange<BasicBlock> er : builder.graph.getRanges()) {
BasicBlock h = er.getHandler();
handlers.add(h);
Set<Local> ls = new HashSet<>(liveness.in(h));
for (BasicBlock b : er.get()) {
splits.getNonNull(b).addAll(ls);
}
}
for (Entry<BasicBlock, Set<Local>> e : splits.entrySet()) {
BasicBlock b = e.getKey();
Set<Local> ls = e.getValue();
ArrayList<Stmt> stmtsCopy = new ArrayList<>(b);
int i = 0;
boolean checkSplit = false;
for (int i1 = 0; i1 < stmtsCopy.size(); i1++) {
Stmt stmt = stmtsCopy.get(i1);
if (b.size() == i)
throw new IllegalStateException("s");
if (checkSplit && stmt.getOpcode() == Opcode.LOCAL_STORE) {
CopyVarStmt copy = (CopyVarStmt) stmt;
VarExpr v = copy.getVariable();
if (ls.contains(v.getLocal())) {
BasicBlock n = splitBlock(b, i);
order.add(order.indexOf(b), n);
i = 0;
checkSplit = false;
}
} else {
// do not split if we have only seen simple or synthetic copies (catch copy is synthetic)
if (stmt instanceof CopyVarStmt) {
CopyVarStmt copy = (CopyVarStmt) stmt;
int opc = copy.getExpression().getOpcode();
if (!copy.isSynthetic() && opc != Opcode.LOCAL_LOAD && opc != Opcode.CATCH) {
checkSplit = true;
}
} else {
checkSplit = true;
}
}
i++;
}
}
builder.graph.naturalise(order);
int po = 0;
for (BasicBlock b : SimpleDfs.preorder(builder.graph, builder.graph.getEntries().iterator().next())) {
insertion.put(b, 0);
process.put(b, 0);
preorder.put(b, po++);
}
}
use of org.mapleir.stdlib.collections.map.NullPermeableHashMap in project maple-ir by LLVM-but-worse.
the class DefaultInvocationResolver method computeVTable.
private void computeVTable(ClassNode c) {
if (c == null) {
throw new NullPointerException();
}
/* only visit each class once */
if (hasVisited(c)) {
return;
}
/* ensure parents loaded */
ClassNode superKlass = null;
/* if the super class is null it means we're at object and so
* we don't even have to consider interfaces: as we stop
* here */
if (c.superName != null) {
computeVTable(superKlass = app.findClassNode(c.superName));
for (String i : c.interfaces) {
computeVTable(app.findClassNode(i));
}
}
/* the general plan of attack here is:
* 1. add our own methods.
* 2. merge without our super class methods.
* 3. merge and resolve with our interfaces. */
/* we build our local tables for the current class. it's
* important to keep these separate from what will become the
* completely hierarchy sensitive lookup tables later and so
* technically should be immutable after the loop but we can
* ignore this as long as we do not modify them later. */
Map<Selector, MethodNode> thisMethodSet = new HashMap<>();
Map<Selector, MethodNode> thisAbstractSet = new HashMap<>();
for (MethodNode m : c.methods) {
/* we can easily resolve these as they come
* so we don't even need them in the table. */
if (Modifier.isStatic(m.access)) {
continue;
}
/* immutable lookup key */
Selector s = new Selector(m.name, m.desc);
/* store our local declarations. */
if (Modifier.isAbstract(m.access)) {
putOrThrow(thisAbstractSet, s, m);
} else {
putOrThrow(thisMethodSet, s, m);
}
}
if (debugLevel >= 2) {
LOGGER.debug("Class: " + c);
LOGGER.debug(" super: " + c.superName);
LOGGER.debug(" interfaces: " + c.interfaces.toString());
LOGGER.debug(" implSet: ");
print(thisMethodSet);
LOGGER.debug(" absSet: ");
print(thisAbstractSet);
}
/* Store the local results as the global as in the case of
* java/lang/Object we don't have a super class and so the
* ensuing analysis (below) is never executed. It's important
* to note that we shouldn't be looking in the global vtable
* for a given class while processing that class; the local
* maps/vtable values are used but not the field
* references. */
concreteVTables.put(c, thisMethodSet);
abstractVTables.put(c, thisAbstractSet);
if (superKlass != null) {
if (!hasVisited(superKlass)) {
throw new IllegalStateException(String.format("Parent of %s, %s is not initialised", c, superKlass));
}
Map<Selector, MethodNode> globalCVT = new HashMap<>();
Map<Selector, MethodNode> globalAVT = new HashMap<>();
/* inherit all super class methods */
globalCVT.putAll(concreteVTables.get(superKlass));
globalAVT.putAll(abstractVTables.get(superKlass));
assertIntersection(thisAbstractSet.entrySet(), thisMethodSet.entrySet(), Collections.emptySet());
/* (1) and (2)
* NOTE: thisAbstractSet and thisMethodSet should
* intersect to give the null set. */
for (Selector s : thisAbstractSet.keySet()) {
globalCVT.remove(s);
}
for (Selector s : thisMethodSet.keySet()) {
globalAVT.remove(s);
}
/* we shouldn't ever get merge errors from considering the
* current class with it's super. (this can happen with
* interfaces, however) */
assertIntersection(globalAVT.entrySet(), globalCVT.entrySet(), Collections.emptySet());
/* add our own declarations to the tables. this could possibly
* override methods from the super class and we're happy
* about this. */
globalCVT.putAll(thisMethodSet);
globalAVT.putAll(thisAbstractSet);
assertIntersection(globalAVT.entrySet(), globalCVT.entrySet(), Collections.emptySet());
concreteVTables.put(c, globalCVT);
abstractVTables.put(c, globalAVT);
if (debugLevel >= 3) {
LOGGER.debug(" globalCVT: ");
print(globalCVT);
LOGGER.debug(" globalAVT: ");
print(globalAVT);
}
NullPermeableHashMap<Selector, Set<MethodNode>> mergeMap = new NullPermeableHashMap<>(new SetCreator<>());
if (debugLevel >= 2) {
LOGGER.debug(" process interfaces:");
}
for (String i : c.interfaces) {
if (debugLevel >= 2) {
LOGGER.debug(" " + i);
}
ClassNode interfaceKlass = app.findClassNode(i);
/* An abstract interface method cannot kill
* a concrete class implementation of the method
* from the super class. However, if the reaching
* definition is a default implementation from
* another interface which is a superclass of the
* current interface, then the reaching definition
* in the current interface is an abstract method
* and so that is propagated into the implementor.
*
* If for example the super interface defines the
* abstract method and the sub interface has a
* default implementation and we implement the
* default interface, the default method will become
* the reaching definition for the current class,
* not the abstract one.
*
* If a class that implements a default method
* already has a reaching definition in the parent,
* the subinterface default method takes priority.*/
add(concreteVTables.get(interfaceKlass), mergeMap);
add(abstractVTables.get(interfaceKlass), mergeMap);
}
if (debugLevel >= 3) {
LOGGER.debug(" mergeMap:");
printMap(mergeMap);
}
for (Entry<Selector, Set<MethodNode>> e : mergeMap.entrySet()) {
Selector selector = e.getKey();
Set<MethodNode> conflictingMethods = e.getValue();
MethodNode resolve;
if (conflictingMethods.size() > 1) {
if (debugLevel >= 2) {
LOGGER.debug(" conflicts: " + conflictingMethods.size());
LOGGER.debug(" " + selector);
for (MethodNode m : conflictingMethods) {
LOGGER.debug(" ^" + m);
}
}
/* if we have any sort of declaration, we can easily say that
* that is the target without having to check the hierarchy
* and globalCVT rules.
*
* 05/10/17: globalCVT should contain super defs as well or ours
* so surely we use this as it doesn't contain any
* abstracts?*/
if (globalCVT.containsKey(selector)) {
resolve = globalCVT.get(selector);
if (debugLevel >= 2) {
LOGGER.debug(" (1)resolved to " + resolve);
}
} else if (thisAbstractSet.containsKey(selector)) {
resolve = thisAbstractSet.get(selector);
if (debugLevel >= 2) {
LOGGER.debug(" (2)resolved to " + resolve);
}
} else {
/* here is where it gets tricky. */
/* 05/10/17: weird bug? example:
* javax/swing/LayoutComparator implements Comparable
* Comparable extends Object by OUR own definition.
* Comparable reabstracts equals(Object) but LayoutComparator
* extends Object implicitly, so the Object definition is
* reaching unless it is redefined in LayoutComparator.
* Note that you can't have default methods for Object methods.
*
* So do we check for it here or inherit Objects methods before we
* do anything else?
* solution: use global vtable merge interface merges above?
*/
Collection<MethodNode> contenders = getMaximallySpecific(conflictingMethods);
if (contenders.isEmpty()) {
throw new IllegalStateException();
}
if (contenders.size() == 1) {
/* design decision: if there is only 1 contender but multiple
* conflicting methods, all of the conflicting methods are
* defaults, then we have a clear target method, but if any of
* them are abstract, we are actually propagating a
* reabstraction through this class, so we can insert a
* miranda. */
boolean anyAbstract = false;
for (MethodNode m : contenders) {
anyAbstract |= Modifier.isAbstract(m.access);
}
if (anyAbstract) {
/* valid: miranda (reabstraction) */
resolve = null;
} else {
resolve = contenders.iterator().next();
}
if (debugLevel >= 2) {
LOGGER.debug(" (3)resolved to " + resolve);
}
} else {
if (c.isAbstract()) {
boolean allAbstract = true;
for (MethodNode m : contenders) {
allAbstract &= Modifier.isAbstract(m.access);
}
if (allAbstract) {
/* valid: miranda */
resolve = null;
} else {
/* require a declaration but we don't
* have one -> die. */
throw new IllegalStateException(String.format("Method %s is ambiguous in %s, candidates:%s", selector, c, conflictingMethods));
}
} else {
/* require a declaration but we don't
* have one -> die. */
throw new IllegalStateException(String.format("Method %s is ambiguous in %s, candidates:%s", selector, c, conflictingMethods));
}
}
}
} else {
/* no conflicts, so we can just choose the resolve method as
* the target method from one of the tables. */
if (globalCVT.containsKey(selector)) {
resolve = globalCVT.get(selector);
} else if (thisAbstractSet.containsKey(selector)) {
/* thisAbstractSet instead of the global one because
* these conflicts would make things confusing if
* the global table was constantly changing. */
resolve = thisAbstractSet.get(selector);
} else {
/* no conflict but the current class just does not implement
* the method itself. we could either create a miranda here or
* reference to method. we will do the latter. */
// size == 1
resolve = conflictingMethods.iterator().next();
}
}
/* delete from both so we can do a clean add after. */
globalAVT.remove(selector);
globalCVT.remove(selector);
if (resolve == null && !c.isAbstract()) {
throw new IllegalStateException(String.format("Miranda %s in non abstract class %s", conflictingMethods, c));
}
if (resolve == null) {
// TODO: sigs?
resolve = new MethodNode(c, Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, selector.name, selector.desc, null, getExceptionClasses(conflictingMethods));
c.methods.add(resolve);
if (debugLevel >= 2) {
LOGGER.debug(" generated miranda " + resolve);
}
}
if (Modifier.isAbstract(resolve.access)) {
globalAVT.put(selector, resolve);
} else {
globalCVT.put(selector, resolve);
}
}
assertIntersection(concreteVTables.get(c).entrySet(), abstractVTables.get(c).entrySet(), Collections.emptySet());
if (debugLevel >= 2) {
LOGGER.debug(" cvtable: ");
print(concreteVTables.get(c));
LOGGER.debug(" avtable: ");
print(abstractVTables.get(c));
}
}
}
use of org.mapleir.stdlib.collections.map.NullPermeableHashMap in project maple-ir by LLVM-but-worse.
the class LocalsPool method realloc.
/* public Local newLocal(boolean isStack) {
int index = cache.size();
while(true) {
String key = key(index, isStack);
if(!cache.containsKey(key)) {
return get(index, isStack);
}
}
} */
public int realloc(ControlFlowGraph cfg) {
NullPermeableHashMap<Local, Set<Type>> types = new NullPermeableHashMap<>(SetCreator.getInstance());
int min = 0;
Set<Local> safe = new HashSet<>();
for (BasicBlock b : cfg.vertices()) {
for (Stmt stmt : b) {
if (stmt.getOpcode() == Opcode.LOCAL_STORE) {
CopyVarStmt cp = (CopyVarStmt) stmt;
VarExpr var = cp.getVariable();
Local local = var.getLocal();
if (!cp.isSynthetic()) {
types.getNonNull(local).add(var.getType());
} else {
safe.add(local);
}
types.getNonNull(local).add(var.getType());
}
for (Expr s : stmt.enumerateOnlyChildren()) {
if (s.getOpcode() == Opcode.LOCAL_LOAD) {
VarExpr var = (VarExpr) s;
Local local = var.getLocal();
types.getNonNull(local).add(var.getType());
}
}
}
}
Map<Local, Type> stypes = new HashMap<>();
for (Entry<Local, Set<Type>> e : types.entrySet()) {
Set<Type> set = e.getValue();
Set<Type> refined = new HashSet<>();
if (set.size() > 1) {
for (Type t : set) {
refined.add(TypeUtils.asSimpleType(t));
}
if (refined.size() != 1) {
boolean valid = false;
if (refined.size() == 2) {
// TODO: proper check
Iterator<Type> it = refined.iterator();
if (it.next().getSize() == it.next().getSize()) {
Type t = refined.iterator().next();
refined.clear();
refined.add(t);
valid = true;
}
}
if (!valid) {
for (Entry<Local, Set<Type>> e1 : types.entrySet()) {
System.err.println(e1.getKey() + " == " + e1.getValue());
}
// String.format("illegal typesets for %s, set=%s, refined=%s", args)
throw new RuntimeException("illegal typesets for " + e.getKey());
}
}
Local l = e.getKey();
stypes.put(l, refined.iterator().next());
// if(!safe.contains(l)) {
// stypes.put(l, refined.iterator().next());
// }
} else {
Local l = e.getKey();
stypes.put(l, set.iterator().next());
// if(!safe.contains(l)) {
// }
}
}
// for(Entry<Local, Type> e : stypes.entrySet()) {
// System.out.println(e.getKey() + " == " + e.getValue());
// }
// lvars then svars, ordered of course,
List<Local> wl = new ArrayList<>(stypes.keySet());
// System.out.println("safe: " + safe);
wl.sort(new Comparator<Local>() {
@Override
public int compare(Local o1, Local o2) {
boolean s1 = safe.contains(o1);
boolean s2 = safe.contains(o2);
if (s1 && !s2) {
return -1;
} else if (!s1 && s2) {
return 1;
} else {
return o1.compareTo(o2);
}
}
});
// System.out.println("wl: " + wl);
Map<Local, Local> remap = new HashMap<>();
int idx = min;
for (Local l : wl) {
Type type = stypes.get(l);
Local newL = get(idx, false);
if (l != newL) {
remap.put(l, newL);
}
idx += type.getSize();
}
remap(cfg, remap);
return idx;
}
use of org.mapleir.stdlib.collections.map.NullPermeableHashMap in project maple-ir by LLVM-but-worse.
the class BoissinotDestructor method copyPhiOperands.
private void copyPhiOperands(BasicBlock b) {
NullPermeableHashMap<BasicBlock, List<PhiRes>> wl = new NullPermeableHashMap<>(new ListCreator<>());
ParallelCopyVarStmt dst_copy = new ParallelCopyVarStmt();
for (Stmt stmt : b) {
// phis only appear at the start of a block.
if (stmt.getOpcode() != Opcode.PHI_STORE) {
break;
}
CopyPhiStmt copy = (CopyPhiStmt) stmt;
PhiExpr phi = copy.getExpression();
// so that we can parallelise the copy when we insert it.
for (Entry<BasicBlock, Expr> e : phi.getArguments().entrySet()) {
BasicBlock h = e.getKey();
// these are validated in init().
VarExpr v = (VarExpr) e.getValue();
PhiRes r = new PhiRes(copy.getVariable().getLocal(), phi, h, v.getLocal(), v.getType());
wl.getNonNull(h).add(r);
}
// for each x0, where x0 is a phi copy target, create a new variable z0 for
// a copy x0 = z0 and replace the phi copy target to z0.
Local x0 = copy.getVariable().getLocal();
Local z0 = locals.makeLatestVersion(x0);
// x0 = z0
dst_copy.pairs.add(new CopyPair(x0, z0, copy.getVariable().getType()));
// z0 = phi(...)
copy.getVariable().setLocal(z0);
}
// resolve
if (dst_copy.pairs.size() > 0)
insertStart(b, dst_copy);
for (Entry<BasicBlock, List<PhiRes>> e : wl.entrySet()) {
BasicBlock p = e.getKey();
ParallelCopyVarStmt copy = new ParallelCopyVarStmt();
for (PhiRes r : e.getValue()) {
// for each xi source in a phi, create a new variable zi, and insert the copy
// zi = xi in the pred Li. then replace the phi arg from Li with zi.
Local xi = r.l;
Local zi = locals.makeLatestVersion(xi);
copy.pairs.add(new CopyPair(zi, xi, r.type));
// we consider phi args to be used in the pred instead of the block
// where the phi is, so we need to update the def/use maps here.
r.phi.setArgument(r.pred, new VarExpr(zi, r.type));
}
insertEnd(p, copy);
}
}
use of org.mapleir.stdlib.collections.map.NullPermeableHashMap in project maple-ir by LLVM-but-worse.
the class DefUseVerifier method verify0.
public static void verify0(ControlFlowGraph cfg) {
LocalsPool lp = cfg.getLocals();
Map<Local, AbstractCopyStmt> defs = new HashMap<>();
NullPermeableHashMap<VersionedLocal, Set<VarExpr>> uses = new NullPermeableHashMap<>(SetCreator.getInstance());
for (BasicBlock b : cfg.vertices()) {
for (Stmt stmt : b) {
if (stmt.getOpcode() == Opcode.LOCAL_STORE || stmt.getOpcode() == Opcode.PHI_STORE) {
AbstractCopyStmt copy = (AbstractCopyStmt) stmt;
defs.put(copy.getVariable().getLocal(), copy);
}
for (Expr e : stmt.enumerateOnlyChildren()) {
if (e.getOpcode() == Opcode.LOCAL_LOAD) {
VarExpr v = (VarExpr) e;
uses.getNonNull((VersionedLocal) v.getLocal()).add(v);
}
}
}
}
{
Set<Local> dlocals = new HashSet<>();
dlocals.addAll(defs.keySet());
dlocals.addAll(lp.defs.keySet());
for (Local l : dlocals) {
if (!defs.containsKey(l)) {
throw new IllegalStateException("(other): def of " + l);
}
if (!lp.defs.containsKey(l)) {
throw new IllegalStateException("(real): def of " + l);
}
AbstractCopyStmt copy1 = defs.get(l);
AbstractCopyStmt copy2 = lp.defs.get(l);
if (copy1 != copy2) {
throw new IllegalStateException("dtest: " + copy1 + " :: " + copy2);
}
}
}
{
Set<VersionedLocal> ulocals = new HashSet<>();
ulocals.addAll(uses.keySet());
ulocals.addAll(lp.uses.keySet());
for (VersionedLocal l : ulocals) {
/*if(!uses.containsKey(l)) {
throw new IllegalStateException("(other): use of " + l);
}
if(!lp.uses.containsKey(l)) {
throw new IllegalStateException("(real): use of " + l);
}*/
Set<VarExpr> uses1 = uses.get(l);
Set<VarExpr> uses2 = lp.uses.get(l);
if (uses1 == null) {
if (uses2.size() != 0) {
throw new IllegalStateException(String.format("utest1: %s, u1:null :: u2:%d", l, uses2.size()));
}
} else if (uses2 == null) {
if (uses1.size() == 0) {
throw new IllegalStateException(String.format("utest2: %s, u1:%d :: u2:null", l, uses1.size()));
}
} else {
if (uses2.size() != uses1.size()) {
throw new IllegalStateException(String.format("utest3: %s, u1:%d :: u2:%d", l, uses1.size(), uses2.size()));
}
}
}
}
}
Aggregations