我正在创建一个 JVM 并尝试理解字节码指令。我正在尝试编写java代码,编译时会生成一个带有宽指令的.class文件。哪个java代码会生成这种扩展指令?我对广泛的行为有点迷失
正如 JVM 规范所述:
wide指令修改另一条指令的行为。它采用两种格式之一,具体取决于所修改的指令。 wide指令的第一种形式修改指令之一 iload, fload, aload, lload, dload, istore, fstore, astore, lstore, dstore,或ret […]。第二种形式仅适用于 iinc 指令[…]。
因此,
wide
指令用于编码加载或存储变量的指令,其索引无法使用单个byte
寻址,即高于255,或具有变量索引或int偏移量的iinc
指令,不适合一个字节。
对于前者,我们必须声明尽可能多的局部变量来触发匹配的
wide
指令的生成,对于后者,只需足够大的增量值就足够了:
class WideInstruction {
public static void largeIncrement(int i) { i += 1000; }
public static int largeVarIndex() {
int i1, i2, i3, i4, i5, i6, i7, i8, i9, i10, i11, i12, i13, i14, i15, i16, i17, i18,
i19, i20, i21, i22, i23, i24, i25, i26, i27, i28, i29, i30, i31, i32, i33, i34, i35,
i36, i37, i38, i39, i40, i41, i42, i43, i44, i45, i46, i47, i48, i49, i50, i51, i52,
i53, i54, i55, i56, i57, i58, i59, i60, i61, i62, i63, i64, i65, i66, i67, i68, i69,
i70, i71, i72, i73, i74, i75, i76, i77, i78, i79, i80, i81, i82, i83, i84, i85, i86,
i87, i88, i89, i90, i91, i92, i93, i94, i95, i96, i97, i98, i99, j0, j1, j2, j3, j4,
j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15, j16, j17, j18, j19, j20, j21, j22,
j23, j24, j25, j26, j27, j28, j29, j30, j31, j32, j33, j34, j35, j36, j37, j38, j39,
j40, j41, j42, j43, j44, j45, j46, j47, j48, j49, j50, j51, j52, j53, j54, j55, j56,
j57, j58, j59, j60, j61, j62, j63, j64, j65, j66, j67, j68, j69, j70, j71, j72, j73,
j74, j75, j76, j77, j78, j79, j80, j81, j82, j83, j84, j85, j86, j87, j88, j89, j90,
j91, j92, j93, j94, j95, j96, j97, j98, j99, k0, k1, k2, k3, k4, k5, k6, k7, k8, k9,
k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26,
k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43,
k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57 = 42;
return k57;
}
public static void main(String[] args) {
decompile();
}
private static void decompile() {
try {
new ProcessBuilder(
Paths.get(System.getProperty("java.home"), "bin", "javap").toString(),
"-cp", System.getProperty("java.class.path"),
"-c", MethodHandles.lookup().lookupClass().getName())
.inheritIO().start().waitFor();
} catch(IOException | InterruptedException ex) {}
}
private WideInstruction() {}
}
有趣的是,
javap
不会打印wide iinc
或wide iload
。 wide istore
,而是iinc_w
、iload_w
和istore_w
。但这只是表明不存在“Java 字节码汇编语言”标准。
Compiled from "WideInstruction.java"
class WideInstruction {
public static void largeIncrement(int);
Code:
0: iinc_w 0, 1000
6: return
public static int largeVarIndex();
Code:
0: bipush 42
2: istore_w 256
6: iload_w 256
10: ireturn
public static void main(java.lang.String[]);
Code:
0: invokestatic #1 // Method decompile:()V
3: return
}
JVM 字节码中具有广泛替代格式的指令之一是 IINC 指令,当您将局部变量增加某个常量时,会调用该指令。
它的两个参数只是局部变量数组的索引和一个有符号常量。通常,这些参数都是 1 字节有符号整数,但在宽格式中,参数都是 2 字节。
要生成 IINC 的宽形式,您只需将局部变量增加超过 1 字节整数的最大值即可:
public class Wide
{
public static void main(String[] args)
{
int localVar = 0;
localVar += 128;
}
}
这是 javap 输出:
Compiled from "Wide.java"
public class Wide {
public Wide();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_0
1: istore_1
2: iinc_w 1, 128
8: return
}