de.jiac.micro.config.analysis.MethodAnalyser.java Source code

Java tutorial

Introduction

Here is the source code for de.jiac.micro.config.analysis.MethodAnalyser.java

Source

/*
 * MicroJIAC - A Lightweight Agent Framework
 * This file is part of MicroJIAC Config.
 *
 * Copyright (c) 2007-2012 DAI-Labor, Technische Universitt Berlin
 *
 * This library includes software developed at DAI-Labor, Technische
 * Universitt Berlin (http://www.dai-labor.de)
 *
 * This library is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This library 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library.  If not, see <http://www.gnu.org/licenses/>.
 */
/*
 * $Id$ 
 */
package de.jiac.micro.config.analysis;

import java.util.HashSet;
import java.util.List;

import org.objectweb.asm.MethodAdapter;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.AnalyzerException;
import org.objectweb.asm.tree.analysis.Interpreter;
import org.objectweb.asm.tree.analysis.Value;

import de.jiac.micro.config.analysis.ClassInfoPool.ClassInfoCreator;

/**
 * @author Marcel Patzlaff
 * @version $Revision:$
 */
final class MethodAnalyser extends MethodAdapter {
    protected final static HashSet<String> HANDLE_SOURCES;

    protected final static MethodKey GET_HANDLE = new MethodKey("getHandle", "()Lde/jiac/micro/core/IHandle;");
    protected final static MethodKey GET_NODE_HANDLE = new MethodKey("getNodeHandle",
            "()Lde/jiac/micro/core/IHandle;");
    protected final static MethodKey ADD_HANDLES_ON = new MethodKey("addHandlesOn",
            "(Lde/jiac/micro/core/scope/AgentScope;)V");

    static {
        HANDLE_SOURCES = new HashSet<String>();
        HANDLE_SOURCES.add("de/jiac/micro/core/scope/Scope");
        HANDLE_SOURCES.add("de/jiac/micro/core/IAgent");
        HANDLE_SOURCES.add("de/jiac/micro/core/INode");
        HANDLE_SOURCES.add("de/jiac/micro/core/IContainer");
        HANDLE_SOURCES.add("de/jiac/micro/core/AbstractContainer");
    }

    protected final ClassInfoCreator parent;
    protected final MethodKey methodKey;

    MethodAnalyser(ClassInfoCreator parent, int access, String name, String desc, String signature,
            String[] exceptions) {
        super(new MethodNode(access, name, desc, signature, exceptions));
        this.parent = parent;
        this.methodKey = new MethodKey(name, desc);
    }

    @Override
    public void visitEnd() {
        final HashSet<String> obtainedHandles = new HashSet<String>();
        final ClassInfo ci = parent.classInfo;
        final MethodNode method = (MethodNode) mv;

        ci.referencedClassesInMethods.put(methodKey, new HashSet<String>());

        // insert argument types
        processType(Type.getReturnType(method.desc));
        for (Type type : Type.getArgumentTypes(method.desc)) {
            processType(type);
        }

        Interpreter interpreter = new BasicGuesser() {
            @Override
            public Value newOperation(AbstractInsnNode insn) {
                final int op = insn.getOpcode();
                String className = null;

                if (op == GETSTATIC) {
                    className = ((FieldInsnNode) insn).owner.replace('/', '.');
                } else if (op == NEW) {
                    className = Type.getObjectType(((TypeInsnNode) insn).desc).getClassName();
                }

                parent.registerDependencyForMethod(methodKey, className);
                return super.newOperation(insn);
            }

            @Override
            public Value unaryOperation(AbstractInsnNode insn, Value value) throws AnalyzerException {
                if (insn.getOpcode() == ARETURN) {
                    if (methodKey.equals(GET_HANDLE) || methodKey.equals(GET_NODE_HANDLE)) {
                        RuntimeGuessValue guessedValue = (RuntimeGuessValue) value;
                        Type type = guessedValue.getType();

                        if (type != null && guessedValue != RuntimeGuessValue.NULL_CONSTANT) {
                            parent.classInfo.directHandle = type.getClassName();
                        }
                    }
                }
                return super.unaryOperation(insn, value);
            }

            @Override
            public Value naryOperation(AbstractInsnNode insn, List values) throws AnalyzerException {
                if (insn instanceof MethodInsnNode) {
                    MethodInsnNode minsn = (MethodInsnNode) insn;
                    final int opCode = minsn.getOpcode();
                    if (minsn.desc.endsWith(")Ljava/lang/Class;") && (minsn.name.equals("class$")
                            || (minsn.owner.equals("java/lang/Class") && minsn.name.equals("forName"))
                            || (minsn.owner.endsWith("ClassLoader") && minsn.name.equals("loadClass")))) {
                        // pattern for class loading
                        RuntimeGuessValue guessedValue = (RuntimeGuessValue) values
                                .get(opCode == Opcodes.INVOKESTATIC ? 0 : 1);
                        String className = (String) guessedValue.getValue();

                        if (className != null) {
                            return new RuntimeGuessValue(Type.getObjectType("java/lang/Class"), className);
                        }
                    } else if (HANDLE_SOURCES.contains(minsn.owner)
                            && minsn.desc.equals("(Ljava/lang/Class;)Lde/jiac/micro/core/IHandle;")
                            && (minsn.name.equals("getHandle") || minsn.name.equals("getScopeHandle"))) {
                        // pattern for obtaining a handle
                        RuntimeGuessValue guessedValue = (RuntimeGuessValue) values
                                .get(opCode == Opcodes.INVOKESTATIC ? 0 : 1);
                        String className = (String) guessedValue.getValue();

                        if (className != null) {
                            obtainedHandles.add(className);
                            parent.registerDependencyForMethod(methodKey, className);
                            String internalName = className.replace('.', '/');
                            return new RuntimeGuessValue(Type.getObjectType(internalName), null);
                        }
                    } else if (HANDLE_SOURCES.contains(minsn.owner) && minsn.name.equals("addHandle")
                            && minsn.desc.equals("(Lde/jiac/micro/core/IHandle;)V")) {
                        // pattern for adding a handle explicitly
                        RuntimeGuessValue guessValue = (RuntimeGuessValue) values.get(1);
                        Type type = guessValue.getType();

                        if (type != null) {
                            parent.registerDependencyForMethod(methodKey, type.getClassName());
                            parent.classInfo.indirectHandles.add(type.getClassName());
                        }
                    }
                }

                return super.naryOperation(insn, values);
            }
        };

        Analyzer a = new Analyzer(interpreter);
        try {
            a.analyze(parent.owner, method);
        } catch (AnalyzerException e) {
            e.printStackTrace();
        }

        if (obtainedHandles.size() > 0) {
            ci.referencedHandlesInMethods.put(methodKey, obtainedHandles);
        }
    }

    private void processType(Type type) {
        if (type.getSort() == Type.ARRAY) {
            type = type.getElementType();
        }

        if (type.getSort() == Type.OBJECT) {
            parent.registerDependencyForMethod(methodKey, type.getClassName());
        }
    }
}