use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.
the class ReflectiveFunctorFactory method makeBase.
private MethodNode makeBase(String name, String desc) {
ClassNode owner = new ClassNode();
owner.node.version = Opcodes.V1_7;
owner.node.name = name;
owner.node.superName = "java/lang/Object";
owner.node.access = Opcodes.ACC_PUBLIC;
MethodNode m = new MethodNode(new org.objectweb.asm.tree.MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "eval", desc, null, null), owner);
owner.addMethod(m);
return m;
}
use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.
the class ReflectiveFunctorFactory method arithmetic.
@Override
public EvaluationFunctor<Number> arithmetic(Type t1, Type t2, Type rt, ArithmeticExpr.Operator op) {
String name = t1.getClassName() + op.name() + t2.getClassName() + "RET" + rt.getClassName();
if (cache.containsKey(name)) {
return _get(name);
}
String desc = ("(" + t1.getDescriptor() + t2.getDescriptor() + ")" + rt.getDescriptor());
MethodNode m = makeBase(name, desc);
InsnList insns = new InsnList();
{
Type leftType = null;
Type rightType = null;
if (op == ArithmeticExpr.Operator.SHL || op == ArithmeticExpr.Operator.SHR || op == ArithmeticExpr.Operator.USHR) {
leftType = rt;
rightType = Type.INT_TYPE;
} else {
leftType = rightType = rt;
}
insns.add(new VarInsnNode(TypeUtils.getVariableLoadOpcode(t1), 0));
cast(insns, t1, leftType);
insns.add(new VarInsnNode(TypeUtils.getVariableLoadOpcode(t2), t1.getSize()));
cast(insns, t2, rightType);
int opcode;
switch(op) {
case ADD:
opcode = TypeUtils.getAddOpcode(rt);
break;
case SUB:
opcode = TypeUtils.getSubtractOpcode(rt);
break;
case MUL:
opcode = TypeUtils.getMultiplyOpcode(rt);
break;
case DIV:
opcode = TypeUtils.getDivideOpcode(rt);
break;
case REM:
opcode = TypeUtils.getRemainderOpcode(rt);
break;
case SHL:
opcode = TypeUtils.getBitShiftLeftOpcode(rt);
break;
case SHR:
opcode = TypeUtils.bitShiftRightOpcode(rt);
break;
case USHR:
opcode = TypeUtils.getBitShiftRightUnsignedOpcode(rt);
break;
case OR:
opcode = TypeUtils.getBitOrOpcode(rt);
break;
case AND:
opcode = TypeUtils.getBitAndOpcode(rt);
break;
case XOR:
opcode = TypeUtils.getBitXorOpcode(rt);
break;
default:
throw new RuntimeException();
}
insns.add(new InsnNode(opcode));
insns.add(new InsnNode(TypeUtils.getReturnOpcode(rt)));
m.node.instructions = insns;
}
return buildBridge(m);
}
use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.
the class DetectIrreducibleFlowPass method accept.
@Override
public PassResult accept(PassContext pcxt) {
AnalysisContext cxt = pcxt.getAnalysis();
for (Entry<MethodNode, ControlFlowGraph> e : cxt.getIRCache().entrySet()) {
MethodNode mn = e.getKey();
ControlFlowGraph cfg = e.getValue();
if (!GraphUtils.isReducibleGraph(cfg, cfg.getEntries().iterator().next())) {
return PassResult.with(pcxt, this).fatal(new IllegalStateException(String.format("%s contains irreducible loop", mn))).make();
}
}
return PassResult.with(pcxt, this).finished().make();
}
use of org.mapleir.asm.MethodNode in project maple-ir by LLVM-but-worse.
the class FieldRenamerPass method accept.
@Override
public PassResult accept(PassContext pcxt) {
AnalysisContext cxt = pcxt.getAnalysis();
Map<FieldNode, String> remapped = new HashMap<>();
// int totalFields = 0;
// int i = RenamingUtil.computeMinimum(totalFields);
ApplicationClassSource source = cxt.getApplication();
int i = RenamingUtil.numeric("aaaaa");
for (ClassNode cn : source.iterate()) {
// totalFields += cn.fields.size();
for (FieldNode fn : cn.getFields()) {
remapped.put(fn, RenamingUtil.createName(i++));
}
}
InvocationResolver resolver = cxt.getInvocationResolver();
for (ClassNode cn : source.iterate()) {
for (MethodNode m : cn.getMethods()) {
ControlFlowGraph cfg = cxt.getIRCache().getFor(m);
for (BasicBlock b : cfg.vertices()) {
for (Stmt stmt : b) {
if (stmt.getOpcode() == Opcode.FIELD_STORE) {
FieldStoreStmt fs = (FieldStoreStmt) stmt;
FieldNode f = resolver.findField(fs.getOwner(), fs.getName(), fs.getDesc(), fs.getInstanceExpression() == null);
if (f != null) {
if (remapped.containsKey(f)) {
fs.setName(remapped.get(f));
} else if (mustMark(source, f.getOwner())) {
System.err.println(" no remap for " + f + ", owner: " + f.getOwner());
}
} else {
if (mustMark(source, fs.getOwner())) {
System.err.println(" can't resolve field(set): " + fs.getOwner() + "." + fs.getName() + " " + fs.getDesc() + ", " + (fs.getInstanceExpression() == null));
}
}
}
for (Expr e : stmt.enumerateOnlyChildren()) {
if (e.getOpcode() == Opcode.FIELD_LOAD) {
FieldLoadExpr fl = (FieldLoadExpr) e;
FieldNode f = resolver.findField(fl.getOwner(), fl.getName(), fl.getDesc(), fl.getInstanceExpression() == null);
if (f != null) {
if (remapped.containsKey(f)) {
fl.setName(remapped.get(f));
} else if (mustMark(source, f.getOwner())) {
System.err.println(" no remap for " + f + ", owner: " + f.getOwner());
}
} else {
if (mustMark(source, fl.getOwner())) {
System.err.println(" can't resolve field(get): " + fl.getOwner() + "." + fl.getName() + " " + fl.getDesc() + ", " + (fl.getInstanceExpression() == null));
}
}
}
}
}
}
}
}
for (Entry<FieldNode, String> e : remapped.entrySet()) {
e.getKey().node.name = e.getValue();
}
System.out.printf(" Renamed %d fields.%n", remapped.size());
return PassResult.with(pcxt, this).finished().make();
}
use of org.mapleir.asm.MethodNode 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.node.superName != null) {
computeVTable(superKlass = app.findClassNode(c.node.superName));
for (String i : c.node.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.getMethods()) {
/* we can easily resolve these as they come
* so we don't even need them in the table. */
if (Modifier.isStatic(m.node.access)) {
continue;
}
/* immutable lookup key */
Selector s = new Selector(m.getName(), m.getDesc());
/* store our local declarations. */
if (Modifier.isAbstract(m.node.access)) {
putOrThrow(thisAbstractSet, s, m);
} else {
putOrThrow(thisMethodSet, s, m);
}
}
if (debugLevel >= 2) {
LOGGER.debug("Class: " + c);
LOGGER.debug(" super: " + c.node.superName);
LOGGER.debug(" interfaces: " + c.node.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<>(HashSet::new);
if (debugLevel >= 2) {
LOGGER.debug(" process interfaces:");
}
for (String i : c.node.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.node.access);
}
if (anyAbstract) {
/* valid: miranda (reabstraction) */
resolve = null;
} else {
resolve = contenders.iterator().next();
}
if (debugLevel >= 2) {
LOGGER.debug(" (3)resolved to " + resolve);
}
} else {
if (Modifier.isAbstract(c.node.access)) {
boolean allAbstract = true;
for (MethodNode m : contenders) {
allAbstract &= Modifier.isAbstract(m.node.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 && !Modifier.isAbstract(c.node.access)) {
throw new IllegalStateException(String.format("Miranda %s in non abstract class %s", conflictingMethods, c));
}
if (resolve == null) {
// TODO: sigs?
resolve = new MethodNode(new org.objectweb.asm.tree.MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_ABSTRACT, selector.name, selector.desc, null, getExceptionClasses(conflictingMethods)), c);
c.addMethod(resolve);
if (debugLevel >= 2) {
LOGGER.debug(" generated miranda " + resolve);
}
}
if (Modifier.isAbstract(resolve.node.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));
}
}
}
Aggregations