/*
* MicroJIAC - A Lightweight Agent Framework
* This file is part of MicroJIAC Config.
*
* Copyright (c) 2007-2012 DAI-Labor, Technische Universität Berlin
*
* This library includes software developed at DAI-Labor, Technische
* Universität 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());
}
}
}