in bytekit-core/src/main/java/com/alibaba/bytekit/asm/interceptor/InterceptorProcessor.java [46:190]
public List<Location> process(MethodProcessor methodProcessor) throws Exception {
List<Location> locations = locationMatcher.match(methodProcessor);
List<Binding> interceptorBindings = interceptorMethodConfig.getBindings();
for (Location location : locations) {
// 有三小段代码,1: 保存当前栈上的值的 , 2: 插入的回调的 , 3:恢复当前栈的
InsnList toInsert = new InsnList();
InsnList stackSaveInsnList = new InsnList();
InsnList stackLoadInsnList = new InsnList();
StackSaver stackSaver = null;
if(location.isStackNeedSave()) {
stackSaver = location.getStackSaver();
}
BindingContext bindingContext = new BindingContext(location, methodProcessor, stackSaver);
if(stackSaver != null) {
stackSaver.store(stackSaveInsnList, bindingContext);
stackSaver.load(stackLoadInsnList, bindingContext);
}
Type methodType = Type.getMethodType(interceptorMethodConfig.getMethodDesc());
Type[] argumentTypes = methodType.getArgumentTypes();
// 检查回调函数的参数和 binding数一致
if(interceptorBindings.size() != argumentTypes.length) {
throw new IllegalArgumentException("interceptorBindings size no equals with interceptorMethod args size.");
}
// 把当前栈上的数据保存起来
int fromStackBindingCount = 0;
for (Binding binding : interceptorBindings) {
if(binding.fromStack()) {
fromStackBindingCount++;
}
}
// 只允许一个binding从栈上保存数据
if(fromStackBindingCount > 1) {
throw new IllegalArgumentException("interceptorBindings have more than one from stack Binding.");
}
// 组装好要调用的 static 函数的参数
for(int i = 0 ; i < argumentTypes.length; ++i) {
Binding binding = interceptorBindings.get(i);
binding.pushOntoStack(toInsert, bindingContext);
// 检查 回调函数的参数类型,看是否要box一下 ,检查是否原始类型就可以了。
// 只有类型不一样时,才需要判断。比如两个都是 long,则不用判断
Type bindingType = binding.getType(bindingContext);
if(!bindingType.equals(argumentTypes[i])) {
if(AsmOpUtils.needBox(bindingType)) {
AsmOpUtils.box(toInsert, binding.getType(bindingContext));
}
}
}
// TODO 要检查 binding 和 回调的函数的参数类型是否一致。回调函数的类型可以是 Object,或者super。但是不允许一些明显的类型问题,比如array转到int
toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, interceptorMethodConfig.getOwner(), interceptorMethodConfig.getMethodName(),
interceptorMethodConfig.getMethodDesc(), false));
if (!methodType.getReturnType().equals(Type.VOID_TYPE)) {
if (location.canChangeByReturn()) {
// 当回调函数有返回值时,需要更新到之前保存的栈上
// TODO 这里应该有 type 的问题?需要检查是否要 box
Type returnType = methodType.getReturnType();
Type stackSaverType = stackSaver.getType(bindingContext);
if (!returnType.equals(stackSaverType)) {
AsmOpUtils.unbox(toInsert, stackSaverType);
}
stackSaver.store(toInsert, bindingContext);
} else {
// 没有使用到回调函数的返回值的话,则需要从栈上清理掉
int size = methodType.getReturnType().getSize();
if (size == 1) {
AsmOpUtils.pop(toInsert);
} else if (size == 2) {
AsmOpUtils.pop2(toInsert);
}
}
}
TryCatchBlock errorHandlerTryCatchBlock = null;
// 生成的代码用try/catch包围起来
if( exceptionHandlerConfig != null) {
LabelNode gotoDest = new LabelNode();
errorHandlerTryCatchBlock = new TryCatchBlock(methodProcessor.getMethodNode(), exceptionHandlerConfig.getSuppress());
toInsert.insertBefore(toInsert.getFirst(), errorHandlerTryCatchBlock.getStartLabelNode());
toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest));
toInsert.add(errorHandlerTryCatchBlock.getEndLabelNode());
// 这里怎么把栈上的数据保存起来?还是强制回调函数的第一个参数是 exception,后面的binding可以随便搞。
// MethodInsnNode printStackTrace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
// toInsert.add(printStackTrace);
errorHandler(methodProcessor, toInsert);
toInsert.add(gotoDest);
}
// System.err.println(Decompiler.toString(toInsert));
stackSaveInsnList.add(toInsert);
stackSaveInsnList.add(stackLoadInsnList);
if (location.isWhenComplete()) {
methodProcessor.getMethodNode().instructions.insert(location.getInsnNode(), stackSaveInsnList);
}else {
methodProcessor.getMethodNode().instructions.insertBefore(location.getInsnNode(), stackSaveInsnList);
}
if( exceptionHandlerConfig != null) {
errorHandlerTryCatchBlock.sort();
}
// inline callback
if(interceptorMethodConfig.isInline()) {
// Class<?> forName = Class.forName(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName());
Class<?> forName = classLoader.loadClass(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName());
MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, interceptorMethodConfig.getMethodName(), interceptorMethodConfig.getMethodDesc());
methodProcessor.inline(interceptorMethodConfig.getOwner(), toInlineMethodNode);
}
if(exceptionHandlerConfig != null && exceptionHandlerConfig.isInline()) {
// Class<?> forName = Class.forName(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName());
Class<?> forName = classLoader.loadClass(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName());
MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, exceptionHandlerConfig.getMethodName(), exceptionHandlerConfig.getMethodDesc());
methodProcessor.inline(exceptionHandlerConfig.getOwner(), toInlineMethodNode);
}
// System.err.println(Decompiler.toString(methodProcessor.getMethodNode()));
// System.err.println(AsmUtils.toASMCode(methodProcessor.getMethodNode()));
}
return locations;
}