Search in sources :

Example 16 with ClassNode

use of org.mapleir.asm.ClassNode 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();
}
Also used : FieldStoreStmt(org.mapleir.ir.code.stmt.FieldStoreStmt) ClassNode(org.mapleir.asm.ClassNode) FieldLoadExpr(org.mapleir.ir.code.expr.FieldLoadExpr) FieldNode(org.mapleir.asm.FieldNode) HashMap(java.util.HashMap) BasicBlock(org.mapleir.ir.cfg.BasicBlock) AnalysisContext(org.mapleir.context.AnalysisContext) FieldStoreStmt(org.mapleir.ir.code.stmt.FieldStoreStmt) Stmt(org.mapleir.ir.code.Stmt) ApplicationClassSource(org.mapleir.app.service.ApplicationClassSource) MethodNode(org.mapleir.asm.MethodNode) Expr(org.mapleir.ir.code.Expr) FieldLoadExpr(org.mapleir.ir.code.expr.FieldLoadExpr) ControlFlowGraph(org.mapleir.ir.cfg.ControlFlowGraph) InvocationResolver(org.mapleir.app.service.InvocationResolver)

Example 17 with ClassNode

use of org.mapleir.asm.ClassNode 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));
        }
    }
}
Also used : ClassNode(org.mapleir.asm.ClassNode) NullPermeableHashMap(org.mapleir.stdlib.collections.map.NullPermeableHashMap) MethodNode(org.mapleir.asm.MethodNode) NullPermeableHashMap(org.mapleir.stdlib.collections.map.NullPermeableHashMap)

Example 18 with ClassNode

use of org.mapleir.asm.ClassNode in project maple-ir by LLVM-but-worse.

the class DefaultInvocationResolver method resolveVirtualCalls.

@Override
public Set<MethodNode> resolveVirtualCalls(String owner, String name, String desc, boolean strict) {
    /* find concrete receivers and resolve */
    ClassNode cn = app.findClassNode(owner);
    if (!checkNullClass(cn, owner)) {
        return Collections.emptySet();
    }
    Set<MethodNode> result = new HashSet<>();
    for (ClassNode receiver : app.getClassTree().getAllChildren(cn)) {
        if (!Modifier.isAbstract(receiver.node.access)) {
            // use strict mode = false for incomplete analysis
            MethodNode target = resolve(receiver, name, desc, true);
            if (target == null || Modifier.isAbstract(target.node.access)) {
                throw new IllegalStateException(String.format("Could not find vtarget for %s.%s%s", owner, name, desc));
            }
            result.add(target);
        }
    }
    return result;
}
Also used : ClassNode(org.mapleir.asm.ClassNode) MethodNode(org.mapleir.asm.MethodNode)

Example 19 with ClassNode

use of org.mapleir.asm.ClassNode in project maple-ir by LLVM-but-worse.

the class HierarchyMethods method getHierarchyMethodChain.

public Set<MethodNode> getHierarchyMethodChain(ClassNode cn, String name, String desc, boolean exact) {
    ClassTree structures = app.getClassTree();
    Set<MethodNode> foundMethods = new HashSet<>();
    Collection<ClassNode> toSearch = structures.getAllBranches(cn);
    for (ClassNode viable : toSearch) {
        foundMethods.addAll(findMethods(viable, name, desc, exact ? EXACT_TYPES : CONGRUENT_TYPES, VIRTUAL_METHOD | Modifier.ABSTRACT | Opcodes.ACC_BRIDGE, 0));
    }
    return foundMethods;
}
Also used : ClassNode(org.mapleir.asm.ClassNode) MethodNode(org.mapleir.asm.MethodNode) ClassTree(org.mapleir.app.service.ClassTree) HashSet(java.util.HashSet)

Example 20 with ClassNode

use of org.mapleir.asm.ClassNode in project maple-ir by LLVM-but-worse.

the class HierarchyMethods method areTypesCongruent.

/**
 * Two types are congruent if they are primitive and the same, or if one is a subclass of another.
 * @param a type A
 * @param b type B
 * @return true if type A and B are congruent
 */
private boolean areTypesCongruent(Type a, Type b) {
    if (a.equals(b)) {
        return true;
    }
    boolean eArr = a.getSort() == Type.ARRAY;
    boolean aArr = b.getSort() == Type.ARRAY;
    if (eArr != aArr) {
        return false;
    }
    if (eArr) {
        a = a.getElementType();
        b = b.getElementType();
    }
    if (TypeUtils.isPrimitive(a) || TypeUtils.isPrimitive(b)) {
        return false;
    }
    if (a == Type.VOID_TYPE || b == Type.VOID_TYPE) {
        return false;
    }
    ClassNode cnA = app.findClassNode(a.getInternalName());
    ClassNode cnB = app.findClassNode(b.getInternalName());
    ClassTree tree = app.getClassTree();
    return tree.getAllParents(cnB).contains(cnA) || tree.getAllParents(cnA).contains(cnB);
}
Also used : ClassNode(org.mapleir.asm.ClassNode) ClassTree(org.mapleir.app.service.ClassTree)

Aggregations

ClassNode (org.mapleir.asm.ClassNode)45 MethodNode (org.mapleir.asm.MethodNode)24 ControlFlowGraph (org.mapleir.ir.cfg.ControlFlowGraph)16 AnalysisContext (org.mapleir.context.AnalysisContext)13 ApplicationClassSource (org.mapleir.app.service.ApplicationClassSource)11 JarInfo (org.topdank.byteengineer.commons.data.JarInfo)11 SingleJarDownloader (org.topdank.byteio.in.SingleJarDownloader)11 IRCache (org.mapleir.context.IRCache)8 ControlFlowGraphBuilder (org.mapleir.ir.cfg.builder.ControlFlowGraphBuilder)8 SimpleApplicationContext (org.mapleir.app.client.SimpleApplicationContext)7 LibraryClassSource (org.mapleir.app.service.LibraryClassSource)7 BasicAnalysisContext (org.mapleir.context.BasicAnalysisContext)7 IPass (org.mapleir.deob.IPass)7 PassGroup (org.mapleir.deob.PassGroup)7 LiveDataFlowAnalysisImpl (org.mapleir.deob.dataflow.LiveDataFlowAnalysisImpl)7 ControlFlowGraphDumper (org.mapleir.ir.codegen.ControlFlowGraphDumper)7 BasicBlock (org.mapleir.ir.cfg.BasicBlock)6 JarOutputStream (java.util.jar.JarOutputStream)5 Expr (org.mapleir.ir.code.Expr)5 Stmt (org.mapleir.ir.code.Stmt)5