谢谢您的快速回复!
或许我可以从我们关于 clojure clout 的实验性仪表的实际情况中解释
具体来说,我们正在尝试注入代码到这个 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 ( [?
118: invokevirtual #496 ( [?@
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 作为 call frame 的 `this` 是可取的?
就像这个情况,当我们使用 bytecode 注入库时,在方法体末尾注入代码将导致由于将局部变量 0 赋值为 null 而出现问题时。
将代码注入到 finally 中也不会有任何区别,因为 javasisst 在 instr 105 后(带异常表指向 105 后的语句)基本上只是将相同的字节码注入两次(带有和没有异常表的)。但由于 null 赋值,`this` 已经在此时丢失了。