analysis.ReferenceGenerator.java Source code

Java tutorial

Introduction

Here is the source code for analysis.ReferenceGenerator.java

Source

/*******************************************************************************
 *     AndroidPortAssist, a Java application porting tool
 *     Copyright (C) 2016 
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package analysis;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MultiANewArrayInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.Type;

import jar.JarClasses;
import jar.JarClasses.Entry;

public class ReferenceGenerator {

    ClassNode[] nodes;
    JarClasses[] classPath;
    List<Map<String, ClassNode>> classPathNodes;

    public ReferenceGenerator(JarClasses input, JarClasses[] classpath) {
        nodes = classNodesFromJarClasses(input);
        classPath = classpath;
        classReferenceMap = new HashMap<String, ClassReference>();

        classPathNodes = new ArrayList<Map<String, ClassNode>>();
        for (JarClasses jc : classpath) {
            classPathNodes.add(new HashMap<String, ClassNode>());
        }
    }

    /**
     * Create a {@link ClassNode} from an {@link Entry}
     * @param e entry
     * @return
     */
    private static ClassNode classNodeFromEntry(Entry e) {
        ClassReader cr;
        ClassNode node;

        cr = new ClassReader(e.getData());
        node = new ClassNode();
        cr.accept(node, ClassReader.SKIP_FRAMES);
        return node;
    }

    private static ClassNode[] classNodesFromJarClasses(JarClasses jarClasses) {
        Entry[] entries = jarClasses.getEntries();
        LinkedList<ClassNode> nodeList = new LinkedList<ClassNode>();

        for (Entry e : entries) {
            nodeList.add(classNodeFromEntry(e));
        }

        return nodeList.toArray(new ClassNode[0]);
    }

    private static ClassNode classNodeFromJarClasses(String name, JarClasses jarClasses) {
        return classNodeFromEntry(jarClasses.getEntry(name));
    }

    /**
     * Adds a {@link ClassReference} for the given class name if we haven't already.
     * @param name name of the class
     * @return {@link ClassReference} for the specified class
     */
    private ClassReference addClassRef(String name) {
        if (name == null) {
            name = "java/lang/Object";
        }

        ClassReference ref;

        if (classReferenceMap.containsKey(name) == false) {
            ref = new ClassReference(name);
            classReferenceMap.put(name, ref);
        } else {
            ref = classReferenceMap.get(name);
        }

        return ref;
    }

    /**
     * Adds a {@link MethodReference}
     * @param className
     * @param name
     * @param desc
     */
    private void addMethodRef(String className, String name, String desc) {
        ClassReference classReference = addClassRef(className);
        classReference.addMethod(name, desc);
    }

    /**
     * Adds a {@link FieldReference} with the name of the field and of the owner class
     * @param className field owner name
     * @param name field name
     */
    private void addFieldRef(String className, String name) {
        ClassReference classReference = addClassRef(className);
        classReference.addField(name);
    }

    /**
     * Runs the analysis
     */
    public void go() {
        for (ClassNode node : nodes) {
            refInterfaces((List<MethodNode>) node.methods, (List<String>) node.interfaces);
            refSuperclass((List<MethodNode>) node.methods, node.superName);
            refMethods((List<MethodNode>) node.methods);
            refFields((List<FieldNode>) node.fields);
        }
    }

    /**
     * Gets a Collection of all found ClassReferences 
     * @return
     */
    public Collection<ClassReference> getCollection() {
        return classReferenceMap.values();
    }

    /**
     * Add a class reference for the each implemented interface and method references for overridden methods
     * @param classMethods array of methods in the current class being analyzed
     * @param ifaces list of interfaced implemented by the current class being analyzed
     */
    private void refInterfaces(List<MethodNode> classMethods, List<String> ifaces) {
        for (String iface : ifaces) {
            addClassRef(iface);
            refOverriddenMethods(classMethods, iface);
        }
    }

    /**
     * Add a class reference for the superclass and method references for overridden methods
     * @param classMethods array of methods in the current class being analyzed
     * @param superName name of the superclass
     */
    private void refSuperclass(List<MethodNode> classMethods, String superName) {
        addClassRef(superName);
        refOverriddenMethods(classMethods, superName);
    }

    /**
     * Find and add method references resulting from overridden methods of the superclass or an implemented interface
     * @param classMethods array of methods in the current class being analyzed
     * @param superOrInterfaceName name of the superclass or implemented interface to be examined
     */
    private void refOverriddenMethods(List<MethodNode> classMethods, String superOrInterfaceName) {
        ClassNode node;
        Map<String, ClassNode> map;
        JarClasses classes;

        MethodNode inheritedMethod;

        do {
            int i = 0;
            node = null;
            for (i = 0; i < classPathNodes.size(); i++) {
                map = classPathNodes.get(i);
                classes = classPath[i];

                if (map.containsKey(superOrInterfaceName)) {
                    node = map.get(superOrInterfaceName);
                } else {
                    if (classes.containsClass(superOrInterfaceName)) {
                        node = classNodeFromJarClasses(superOrInterfaceName, classes);
                        map.put(superOrInterfaceName, node);
                    } else {
                        continue;
                    }
                }
            }

            if (node != null) {
                for (Object superNodeObject : node.methods) {
                    inheritedMethod = (MethodNode) superNodeObject;
                    for (MethodNode method : classMethods) {
                        if (method.name.equals(inheritedMethod.name) && method.desc.equals(inheritedMethod.desc)) {
                            addMethodRef(superOrInterfaceName, inheritedMethod.name, inheritedMethod.desc);
                        }
                    }
                }
                superOrInterfaceName = node.superName;
            }
        } while (node != null && node.superName != null);
    }

    private void refMethods(List<MethodNode> methods) {
        for (MethodNode node : methods) {
            refExceptions((List<String>) node.exceptions);
            refMethodDesc(node.desc);
            refTryCatchBlocks((List<TryCatchBlockNode>) node.tryCatchBlocks);
            refInstructions(node.instructions);
        }
    }

    private void refExceptions(List<String> exceptions) {
        for (String except : exceptions) {
            addClassRef(except);
        }
    }

    private void addTypeClassRef(Type type) {
        if (type.getSort() == Type.OBJECT) {
            addClassRef(type.getInternalName());
        }
    }

    private void refMethodDesc(String desc) {
        Type[] args = Type.getArgumentTypes(desc);
        for (Type arg : args) {
            addTypeClassRef(arg);
        }

        Type retType = Type.getReturnType(desc);
        addTypeClassRef(retType);
    }

    private void refTryCatchBlocks(List<TryCatchBlockNode> blocks) {
        for (TryCatchBlockNode block : blocks) {
            addClassRef(block.type);
        }
    }

    private void refFields(List<FieldNode> fields) {
        Type type;
        for (FieldNode field : fields) {
            type = Type.getType(field.desc);
            addTypeClassRef(type);
        }
    }

    private void refInstructions(InsnList insnList) {
        AbstractInsnNode insn;

        insn = insnList.getFirst();

        Object cst;

        while (insn != null) {
            switch (insn.getType()) {
            case AbstractInsnNode.FIELD_INSN:
                //addTypeClassRef(Type.getType(((FieldInsnNode)insn).desc));
                addFieldRef(((FieldInsnNode) insn).owner, ((FieldInsnNode) insn).name);
                break;
            /*case AbstractInsnNode.INVOKE_DYNAMIC_INSN:
                   
               break;*/
            /*case AbstractInsnNode.LDC_INSN:
               cst = ((LdcInsnNode)insn).cst;
               if(cst instanceof Type) {
                  addTypeClassRef((Type)cst);
               }
               break;*/
            case AbstractInsnNode.METHOD_INSN:
                addMethodRef(((MethodInsnNode) insn).owner, ((MethodInsnNode) insn).name,
                        ((MethodInsnNode) insn).desc);
                break;
            case AbstractInsnNode.MULTIANEWARRAY_INSN:
                addTypeClassRef(Type.getType(((MultiANewArrayInsnNode) insn).desc));
                break;
            case AbstractInsnNode.TYPE_INSN:
                addTypeClassRef(Type.getType(((TypeInsnNode) insn).desc));
                break;
            }
            insn = insn.getNext();
        }
    }

    Map<String, ClassReference> classReferenceMap;
}