public List process()

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;
    }