我正在尝试使用JavaAsm库生成Java字节码(我基本上是在尝试创建另一种JVM编程语言)
下面是我正在编译的代码
float f = 2f
float f2 = new Float(2f)
Float f3 = f2
println(f2)
double d = 2d
double d2 = new Double(2d)
Double d3 = d2
println(d2)
正如您所看到的,语法与Java非常相似。println
只是对System.out.println
的调用。
当我使用我的编译器时,它生成以下代码(使用JavaP得到这个输出)
Code:
0: ldc #14 // float 2.0f
2: fstore_1
3: new #16 // class java/lang/Float
6: dup
7: ldc #14 // float 2.0f
9: invokespecial #19 // Method java/lang/Float."<init>":(F)V
12: invokevirtual #23 // Method java/lang/Float.floatValue:()F
15: fstore_2
16: fload_2
17: invokestatic #27 // Method java/lang/Float.valueOf:(F)Ljava/lang/Float;
20: astore_3
21: getstatic #33 // Field java/lang/System.out:Ljava/io/PrintStream;
24: fload_2
25: invokevirtual #38 // Method java/io/PrintStream.println:(F)V
28: ldc2_w #39 // double 2.0d
31: dstore 4
33: new #42 // class java/lang/Double
36: dup
37: ldc2_w #39 // double 2.0d
40: invokespecial #45 // Method java/lang/Double."<init>":(D)V
43: invokevirtual #49 // Method java/lang/Double.doubleValue:()D
46: dstore 5
48: dload 5
50: invokestatic #52 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
53: astore 6
55: getstatic #33 // Field java/lang/System.out:Ljava/io/PrintStream;
58: dload 5
60: invokevirtual #54 // Method java/io/PrintStream.println:(D)V
63: aconst_null
64: areturn
但是当我执行这段代码时,我得到了以下错误
Exception in thread "main" java.lang.VerifyError: Bad local variable type
Exception Details:
Location:
Test.run()Ljava/lang/Object; @58: dload
Reason:
Type top (current frame, locals[5]) is not assignable to double
Current Frame:
bci: @58
flags: { }
locals: { 'Test', float, float, 'java/lang/Float', top, top, 'java/lang/Double' }
stack: { 'java/io/PrintStream' }
我在示例化ClassWriter时使用了COMPUTE_MAXS
和COMPUTE_FRAMES
我已经看过这个字节码好几次了(甚至问了ChatGPT lol)但是找不到问题所在,从我看到的情况来看,我已经在变量5中存储了一个primitive double,然后我想加载它来调用println(double)。我不明白为什么Java会告诉我这个错误。它在第一部分使用float时有效,但在使用double时无效。我在使用long类型时遇到了同样的问题。但不使用int
有人能帮我找到我错过的吗?
1条答案
按热度按时间ftf50wuq1#
您违反了JVMS § 2.6.1:
2.6.1.局部变量
每个框架(参见2.6节)包含一个变量数组,称为局部变量,框架的局部变量数组的长度在编译时确定,并以类或接口的二进制表示形式提供,同时提供与框架相关的方法的代码(参见4.7.3节)。
单个局部变量可以保存boolean、byte、char、short、int、float、reference或returnAddress类型的值。一对局部变量可以保存long或double类型的值。
局部变量通过索引来寻址。第一个局部变量的索引为零。当且仅当一个整数介于零和小于局部变量数组大小的一之间时,该整数才被认为是局部变量数组的索引。
Java虚拟机不要求n为偶数。直观地说,long和double类型的值在局部变量数组中不需要64位对齐。实现者可以自由决定使用为该值保留的两个局部变量来表示这些值的适当方式。
Java虚拟机使用局部变量在方法调用时传递参数。在类方法调用时,任何参数都将从局部变量0开始在连续的局部变量中传递。在示例方法调用时,局部变量0始终用于传递对调用示例方法的对象的引用(这在Java编程语言中)。任何参数随后被传递到从局部变量1开始的连续局部变量中。
(强调我的)
在槽5中存储一个double变量(占用槽5和槽6),然后在槽6中存储一些内容,从而使槽5中的变量无效。