package cn.wensiqun.visitor.invoker;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import jw.asmsupport.clazz.AClass;
import jw.asmsupport.clazz.AClassFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.objectweb.asm.Type;
import cn.wensiqun.entity.invoker.LocalVariableSignature;
import cn.wensiqun.info.InvokeInfo;
import cn.wensiqun.info.InvokeInfo.FunctionInfo;
import cn.wensiqun.utils.CommonUtils;
public class MethodInvokeGrepVisitor extends StackLocalMethodVisitor {
private static final Log LOG = LogFactory.getLog(MethodInvokeGrepVisitor.class);
private List<FunctionInfo> calledFuns;
private String[] calledFunNames;
private String[] calledFunDescs;
private Class<?>[] calledFunOwners;
private Type callingFunOwner;
private FunctionInfo callingFun;
private Map<FunctionInfo, List<InvokeInfo>> invokeInfos;
private ClassLoader classLoader;
public MethodInvokeGrepVisitor(
int callingFunModifier,
String callingFunName,
String callingFunDesc,
Type callingFunOwner,
List<LocalVariableSignature> callingFunLocVarSigns,
List<FunctionInfo> calledFuns,
Map<FunctionInfo, List<InvokeInfo>> invokeInfoMap,
ClassLoader classLoader) {
super(callingFunDesc, callingFunModifier, callingFunOwner, callingFunLocVarSigns);
this.calledFuns = calledFuns;
this.classLoader = classLoader;
this.calledFunNames = new String[calledFuns.size()];//calledFun.getName();
this.calledFunDescs = new String[calledFuns.size()];//Type.getMethodDescriptor(calledFun);
this.calledFunOwners = new Class<?>[calledFuns.size()];//calledFun.getDeclaringClass();
for(int i=0, len=calledFuns.size(); i<len; i++){
FunctionInfo calledFun = calledFuns.get(i);
this.calledFunNames[i] = calledFun.getName();
this.calledFunDescs[i] = calledFun.getDescription();
this.calledFunOwners[i] = calledFun.getDeclaringClass();
}
this.callingFunOwner = callingFunOwner;
this.invokeInfos = invokeInfoMap;
//reflect to get calling function
try {
try {
if (callingFunName.equals("<init>")) {
callingFun = InvokeInfo.buildConstructorFunctionInfo(CommonUtils.reflect2Constructor(callingFunOwner.getDescriptor()
, callingFunDesc, classLoader));
}else if(callingFunName.equals("<clinit>")){
Class<?> callingFunOwnerCls = CommonUtils.forName(callingFunOwner.getDescriptor(), true, classLoader);
callingFun = InvokeInfo.buildStaticFunctionInfo(callingFunOwnerCls);
}else{
callingFun = InvokeInfo.buildCommonFunctionInfo(CommonUtils.reflect2Method(callingFunOwner.getDescriptor(),
callingFunName, callingFunDesc, classLoader));
}
} catch (Exception e) {
LOG.error("cannot get method : " + callingFunName + " cause by : " + e.getMessage());
}
} catch (Exception e) {
LOG.error("cannot load class : " + callingFunOwner + " cause by : " + e.getMessage());
}
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
for(int idx=0, len=calledFuns.size(); idx<len; idx++){
String calledFunDesc = calledFunDescs[idx];
String calledFunName = calledFunNames[idx];
Class<?> calledFunOwner = calledFunOwners[idx];
FunctionInfo calledFun = calledFuns.get(idx);
if(desc.equals(calledFunDesc) && name.equals(calledFunName)){
try{
AClass ownerClass = AClassFactory.getProductClass( CommonUtils.forName(owner.replace("/", "."), true, classLoader));
AClass byInvokedMethodOwnerAClass = AClassFactory.getProductClass(calledFunOwner);
if(ownerClass.isChildOrEqual(byInvokedMethodOwnerAClass)){
int argumentsSize = calledFun.getParameterTypes().length;
List<Type> allTypeInStack = new ArrayList<Type>();
for(int i=0; i<argumentsSize;i++){
allTypeInStack.add(stack.get(stack.size() - 1 - i));
}
Class<?>[] argumentActuallyTypes = new Class<?>[argumentsSize];
for(int i=argumentsSize; i>0;i--){
Type typeInStack = allTypeInStack.get(i-1);
Class<?> argCls = null;
int sort = typeInStack.getSort();
switch(sort){
case 1 :
argCls = boolean.class;
break;
case 2 :
argCls = char.class;
break;
case 3 :
argCls = byte.class;
break;
case 4 :
argCls = short.class;
break;
case 5 :
argCls = int.class;
break;
case 6 :
argCls = float.class;
break;
case 7 :
argCls = long.class;
break;
case 8 :
argCls = double.class;
break;
case 9 :
argCls = CommonUtils.forName(typeInStack.getDescriptor(), true, classLoader);
break;
default :
argCls = CommonUtils.forName(typeInStack.getClassName(), true, classLoader);
break;
}
argumentActuallyTypes[argumentsSize-i] = argCls;
}
InvokeInfo invokeInfo = new InvokeInfo(CommonUtils.forName(callingFunOwner.getClassName(), true, classLoader),
callingFun, calledFun, argumentActuallyTypes, currentLineNumber);
invokeInfos.get(calledFun).add(invokeInfo);
}
} catch (Throwable e) {
LOG.warn("error when extart method invoke information : " + calledFun + " cause by : " + e.getMessage(), e);
}
}
}
super.visitMethodInsn(opcode, owner, name, desc);
}
}