谢谢您的快速回复!
也许我可以通过一个来自我们实验仪器对Clojure cloout的真实示例来解释
特别是我们试图向这个route-matches函数中注入代码
```
(route-matches [_ request]
(let [path-info (if absolute?
(request-url request)
(path-info request))]
(let [groups (re-match-groups re path-info)]
(when groups
(assoc-keys-with-groups groups keys)))))
```
为route-matches生成的字节码如下(仅提取从`(assoc-keys-with-groups groups keys)`开始的最后几行)
80: getstatic #136 // 字段 const__37:Lclojure/lang/Var; #'clout.core/assoc-keys-with-groups
83: invokevirtual #120 // 方法 clojure/lang/Var.getRawRoot:()Ljava/lang/Object;
86: checkcast #122 // 类 clojure/lang/IFn
89: aload_3
90: aload_0
91: getfield #44 // 字段 keys:Ljava/lang/Object;
94: aconst_null
95: astore_0
96: invokeinterface #133, 3 // 接口方法 clojure/lang/IFn.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
101: goto 106
104: pop
105: aconst_null
106: areturn
现在我们尝试使用字节码库(javassist)将字节码(一个`System.out.println(this.toString())` Java调用)注入方法体末尾,它将如下所示
80: getstatic #136 // 字段 const__37:Lclojure/lang/Var; #'clout.core/assoc-keys-with-groups
83: invokevirtual #120 // 方法 clojure/lang/Var.getRawRoot:()Ljava/lang/Object;
86: checkcast #122 // 类 clojure/lang/IFn
89: aload_3
90: aload_0
91: getfield #44 // 字段 keys:Ljava/lang/Object;
94: aconst_null
95: astore_0
96: invokeinterface #133, 3 // 接口方法 clojure/lang/IFn.invoke:(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;
101: goto 106
104: pop
105: aconst_null
>> 开始代码注入
106: goto 109
109: astore 5
111: getstatic #489 // 字段 java/lang/System.out:Ljava/io/PrintStream;
>> inst 114 有问题,因为 aload_0 不是 `this`,而是 null
114: aload_0
115: invokevirtual #491 // 方法 toString:()Ljava/lang/String;
118: invokevirtual #496 // 方法 java/io/PrintStream.println:(Ljava/lang/String;)V
121: aload 5
>> 完成代码注入
123: areturn
我们从指令114可以看到,它失败了,因为 aload_0 不再是预期的 `this` 指针。
我对JVM规范和Clojure字节码输出不甚熟悉,因为我对这个语言相当陌生 :) 但根据
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-2.html#jvms-2.6.1:
> 在实例方法调用中,局部变量0始终用于传递要调用实例方法的对象的引用(Java编程语言中的this)
我认为它没有限制局部变量0改为null?但我假设对于调用帧来说,保持变量0始终为`this`是有好处的?
正如这个案例,当我们在使用字节码注入库时,在方法体末尾注入代码时将局部变量0改为null会导致问题。
作为finally标签注入代码并没有任何区别,因为javasisst基本上只重复注入相同字节数据两次(在instr 105之后,异常表的指针指向105之后的语句)。但是由于null赋值,`this`在这个点上就已经丢失。