众所周知ASM是一个修改字节码的利器,此为前文.
我们游戏中有一个很重要的接口大致如下所示(因为历史原因返回值无法修改)
public Object load() { BaseClass baseClass = new ImplClass(); return baseClass;}
方法的返回值是一个Object,但是期望返回的是基于某个基类的值.如果返回值写错了会在跨服序列化的时候失败,导致游戏的跨服玩法出现重大问题(内网出现过此问题).本着把Bug扼杀在摇篮中的思想的.就想着能不能写一段代码来校验这个函数呢?所以才用到了我们今天的主角ASM.
话不多说我们直接入正题,要怎么检测我们先看下上面代码的反编译字节码(此处可能需要一丢丢字节码的知识)
0: new #2 // class com/jiang/asm/checkMethodReturn/ImplClass 3: dup 4: invokespecial #3 // Method com/jiang/asm/checkMethodReturn/ImplClass."":()V 7: astore_1 8: aload_1 9: areturn
上面是生成的字节码指令,由上面的代码我们可知,返回一个引用对象使用的是areturn指令,此指令没有栈操作数.那么可得出,在areturn指令之前必然会有一条aload指令把本地变量推到栈上.因此我们就可以推导出几个信息:
1 引用返回使用的是areturn指令
2 areturn指令之前必然有一条aload指令
3 aload 指令是会会操作局部变量的
由此得出结论,我们只要找到我们关键的那条aload指令操作的局部变量索引,通过该索引找到局部变量的定义,我们就能对此作出检测.
通过翻阅Asm的tree api,在MethodNode中查询到了以下信息
instructions保存的是一个方法里面用到的所有指令,localVariables保存的是所有用到的局部变量(这里涉及到一个局部变量复用的问题,不过该问题不在今天的讨论范畴内).下面就是关键代码.
这个功能到此就结束了.
(ASM似乎把aload_0 aload_1 这种指令全部替换成 aload index 这种指令,这里的代码暂时没有深入研究)
贴心小提示(如果想要知道返回的实际类型,那么只需要找到最后一次和此局部变量下标相同的astore 指令即可 astore指令是把栈上的值赋给本地变量)