谢谢您的快速回复!
也许我可以通过从我们的实验性仪器中提取的实际示例来解释这个示例
具体来说,我们试图向这个 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;
>> 指令 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基本只在指令105之后(异常表指向105之后的语句)重复注入相同的字节码两次。但由于null赋值,`this`在这个点上已经丢失。