概览
从 Instruction 的角度来说,与 jump 相关的 opcode 有 25 个,内容如下:
opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol |
---|---|---|---|---|---|---|---|
148 | lcmp | 153 | ifeq | 158 | ifle | 163 | if_icmpgt |
149 | fcmpl | 154 | ifne | 159 | if_icmpeq | 164 | if_icmple |
150 | fcmpg | 155 | iflt | 160 | if_icmpne | 165 | if_acmpeq |
151 | dcmpl | 156 | ifge | 161 | if_icmplt | 166 | if_acmpne |
152 | dcmpg | 157 | ifgt | 162 | if_icmpge | 167 | goto |
opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol |
---|---|---|---|---|---|---|---|
170 | tableswitch | 171 | lookupswitch |
opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol | opcode | mnemonic symbol |
---|---|---|---|---|---|---|---|
198 | ifnull | 199 | ifnonnull | 200 | goto_w |
从 ASM 的角度来说,这些 opcode 与 MethodVisitor.visitXxxInsn()
方法对应关系如下:
MethodVisitor.visitInsn()
:lcmp
,fcmpl
,fcmpg
,dcmpl
,dcmpg
MethodVisitor.visitJumpInsn()
:ifeq
,ifne
,iflt
,ifge
,ifgt
,ifle
if_icmpeq
,if_icmpne
,if_icmplt
,if_icmpge
,if_icmpgt
,if_icmple
,if_acmpeq
,if_acmpne
ifnull
,ifnonnull
goto
,goto_w
MethodVisitor.visitTableSwitchInsn()
:tableswitch
,lookupswitch
if and goto
compare int with zero
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test(int val) {
if (val == 0) {
System.out.println("val is 0");
}
else {
System.out.println("val is not 0");
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test(int);
Code:
0: iload_1
1: ifne 15
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String val is 0
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: goto 23
15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
18: ldc #5 // String val is not 0
20: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: return
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitJumpInsn(IFNE, label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("val is 0");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitJumpInsn(GOTO, label1);
methodVisitor.visitLabel(label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("val is not 0");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitLabel(label1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this, int} | {}
0000: iload_1 // {this, int} | {int}
0001: ifne 14 // {this, int} | {}
0004: getstatic #2 // {this, int} | {PrintStream}
0007: ldc #3 // {this, int} | {PrintStream, String}
0009: invokevirtual #4 // {this, int} | {}
0012: goto 11 // {} | {}
// {this, int} | {}
0015: getstatic #2 // {this, int} | {PrintStream}
0018: ldc #5 // {this, int} | {PrintStream, String}
0020: invokevirtual #4 // {this, int} | {}
// {this, int} | {}
0023: return // {} | {}
从 JVM 规范的角度来看,ifne
指令对应的 Operand Stack 的变化如下:
..., value →
...
The value
must be of type int
. It is popped from the operand stack and compared against zero. All comparisons are signed. The results of the comparisons are as follows:
ifeq
succeeds if and only ifvalue = 0
ifne
succeeds if and only ifvalue ≠ 0
iflt
succeeds if and only ifvalue < 0
ifle
succeeds if and only ifvalue ≤ 0
ifgt
succeeds if and only ifvalue > 0
ifge
succeeds if and only ifvalue ≥ 0
compare int with non-zero
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test(int a, int b) {
if (a > b) {
System.out.println("a > b");
}
else {
System.out.println("a <= b");
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test(int, int);
Code:
0: iload_1
1: iload_2
2: if_icmple 16
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #3 // String a > b
10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: goto 24
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: ldc #5 // String a <= b
21: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
24: return
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitVarInsn(ILOAD, 2);
methodVisitor.visitJumpInsn(IF_ICMPLE, label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("a > b");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitJumpInsn(GOTO, label1);
methodVisitor.visitLabel(label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("a <= b");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitLabel(label1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 3);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this, int, int} | {}
0000: iload_1 // {this, int, int} | {int}
0001: iload_2 // {this, int, int} | {int, int}
0002: if_icmple 14 // {this, int, int} | {}
0005: getstatic #2 // {this, int, int} | {PrintStream}
0008: ldc #3 // {this, int, int} | {PrintStream, String}
0010: invokevirtual #4 // {this, int, int} | {}
0013: goto 11 // {} | {}
// {this, int, int} | {}
0016: getstatic #2 // {this, int, int} | {PrintStream}
0019: ldc #5 // {this, int, int} | {PrintStream, String}
0021: invokevirtual #4 // {this, int, int} | {}
// {this, int, int} | {}
0024: return // {} | {}
从 JVM 规范的角度来看,if_icmple
指令对应的 Operand Stack 的变化如下:
..., value1, value2 →
...
Both value1
and value2
must be of type int
. They are both popped from the operand stack and compared. All comparisons are signed. The results of the comparison are as follows:
if_icmpeq
succeeds if and only ifvalue1 = value2
if_icmpne
succeeds if and only ifvalue1 ≠ value2
if_icmplt
succeeds if and only ifvalue1 < value2
if_icmple
succeeds if and only ifvalue1 ≤ value2
if_icmpgt
succeeds if and only ifvalue1 > value2
if_icmpge
succeeds if and only ifvalue1 ≥ value2
compare long
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test(long a, long b) {
if (a > b) {
System.out.println("a > b");
}
else {
System.out.println("a <= b");
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test(long, long);
Code:
0: lload_1
1: lload_3
2: lcmp
3: ifle 17
6: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
9: ldc #3 // String a > b
11: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
14: goto 25
17: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
20: ldc #5 // String a <= b
22: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
25: return
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
methodVisitor.visitCode();
methodVisitor.visitVarInsn(LLOAD, 1);
methodVisitor.visitVarInsn(LLOAD, 3);
methodVisitor.visitInsn(LCMP);
methodVisitor.visitJumpInsn(IFLE, label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("a > b");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitJumpInsn(GOTO, label1);
methodVisitor.visitLabel(label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("a <= b");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitLabel(label1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(4, 5);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this, long, top, long, top} | {}
0000: lload_1 // {this, long, top, long, top} | {long, top}
0001: lload_3 // {this, long, top, long, top} | {long, top, long, top}
0002: lcmp // {this, long, top, long, top} | {int}
0003: ifle 14 // {this, long, top, long, top} | {}
0006: getstatic #2 // {this, long, top, long, top} | {PrintStream}
0009: ldc #3 // {this, long, top, long, top} | {PrintStream, String}
0011: invokevirtual #4 // {this, long, top, long, top} | {}
0014: goto 11 // {} | {}
// {this, long, top, long, top} | {}
0017: getstatic #2 // {this, long, top, long, top} | {PrintStream}
0020: ldc #5 // {this, long, top, long, top} | {PrintStream, String}
0022: invokevirtual #4 // {this, long, top, long, top} | {}
// {this, long, top, long, top} | {}
0025: return // {} | {}
从 JVM 规范的角度来看,lcmp
指令对应的 Operand Stack 的变化如下:
..., value1, value2 →
..., result
Both value1
and value2
must be of type long
. They are both popped from the operand stack, and a signed integer comparison is performed. If value1
is greater than value2
, the int value 1
is pushed onto the operand stack. If value1
is equal to value2
, the int value 0
is pushed onto the operand stack. If value1
is less than value2
, the int value -1
is pushed onto the operand stack.
compare obj with null
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test(Object obj) {
if (obj == null) {
System.out.println("obj is null");
}
else {
System.out.println("obj is not null");
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test(java.lang.Object);
Code:
0: aload_1
1: ifnonnull 15
4: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
7: ldc #3 // String obj is null
9: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: goto 23
15: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
18: ldc #5 // String obj is not null
20: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: return
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitJumpInsn(IFNONNULL, label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("obj is null");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitJumpInsn(GOTO, label1);
methodVisitor.visitLabel(label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("obj is not null");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitLabel(label1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this, Object} | {}
0000: aload_1 // {this, Object} | {Object}
0001: ifnonnull 14 // {this, Object} | {}
0004: getstatic #2 // {this, Object} | {PrintStream}
0007: ldc #3 // {this, Object} | {PrintStream, String}
0009: invokevirtual #4 // {this, Object} | {}
0012: goto 11 // {} | {}
// {this, Object} | {}
0015: getstatic #2 // {this, Object} | {PrintStream}
0018: ldc #5 // {this, Object} | {PrintStream, String}
0020: invokevirtual #4 // {this, Object} | {}
// {this, Object} | {}
0023: return // {} | {}
从 JVM 规范的角度来看,ifnonnull
指令对应的 Operand Stack 的变化如下:
..., value →
...
另外,ifnonnull
指令对应的 Format 如下:
ifnonnull
branchbyte1
branchbyte2
The value
must be of type reference
. It is popped from the operand stack. If value
is not null
, the unsigned branchbyte1
and branchbyte2
are used to construct a signed 16-bit offset, where the offset is calculated to be (branchbyte1 << 8) | branchbyte2
. Execution then proceeds at that offset from the address of the opcode of this ifnonnull
instruction. The target address must be that of an opcode of an instruction within the method that contains this ifnonnull
instruction.
Otherwise, execution proceeds at the address of the instruction following this ifnonnull
instruction.
compare objA with objB
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test(Object objA, Object objB) {
if (objA == objB) {
System.out.println("objA == objB");
}
else {
System.out.println("objA != objB");
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test(java.lang.Object, java.lang.Object);
Code:
0: aload_1
1: aload_2
2: if_acmpne 16
5: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
8: ldc #3 // String objA == objB
10: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
13: goto 24
16: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
19: ldc #5 // String objA != objB
21: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
24: return
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitVarInsn(ALOAD, 2);
methodVisitor.visitJumpInsn(IF_ACMPNE, label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("objA == objB");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitJumpInsn(GOTO, label1);
methodVisitor.visitLabel(label0);
methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
methodVisitor.visitLdcInsn("objA != objB");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
methodVisitor.visitLabel(label1);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 3);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this, Object, Object} | {}
0000: aload_1 // {this, Object, Object} | {Object}
0001: aload_2 // {this, Object, Object} | {Object, Object}
0002: if_acmpne 14 // {this, Object, Object} | {}
0005: getstatic #2 // {this, Object, Object} | {PrintStream}
0008: ldc #3 // {this, Object, Object} | {PrintStream, String}
0010: invokevirtual #4 // {this, Object, Object} | {}
0013: goto 11 // {} | {}
// {this, Object, Object} | {}
0016: getstatic #2 // {this, Object, Object} | {PrintStream}
0019: ldc #5 // {this, Object, Object} | {PrintStream, String}
0021: invokevirtual #4 // {this, Object, Object} | {}
// {this, Object, Object} | {}
0024: return // {} | {}
从 JVM 规范的角度来看,if_acmpne
指令对应的 Operand Stack 的变化如下:
..., value1, value2 →
...
Both value1
and value2
must be of type reference
. They are both popped from the operand stack and compared. The results of the comparison are as follows:
if_acmpeq
succeeds if and only ifvalue1 = value2
if_acmpne
succeeds if and only ifvalue1 ≠ value2
switch
tableswitch
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test(int val) {
int result = 0;
switch (val) {
case 1:
result = 1;
break;
case 2:
result = 2;
break;
case 3:
result = 3;
break;
default:
result = 4;
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test(int);
Code:
0: iconst_0
1: istore_2
2: iload_1
3: tableswitch { // 1 to 3
1: 28
2: 33
3: 38
default: 43
}
28: iconst_1
29: istore_2
30: goto 45
33: iconst_2
34: istore_2
35: goto 45
38: iconst_3
39: istore_2
40: goto 45
43: iconst_4
44: istore_2
45: return
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
Label label2 = new Label();
Label label3 = new Label();
Label label4 = new Label();
methodVisitor.visitCode();
methodVisitor.visitInsn(ICONST_0);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitTableSwitchInsn(1, 3, label3, new Label[] { label0, label1, label2 });
methodVisitor.visitLabel(label0);
methodVisitor.visitInsn(ICONST_1);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label1);
methodVisitor.visitInsn(ICONST_2);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label2);
methodVisitor.visitInsn(ICONST_3);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label3);
methodVisitor.visitInsn(ICONST_4);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitLabel(label4);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 3);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this, int} | {}
0000: iconst_0 // {this, int} | {int}
0001: istore_2 // {this, int, int} | {}
0002: iload_1 // {this, int, int} | {int}
0003: tableswitch // {} | {}
{
1: 25
2: 30
3: 35
default: 40
}
// {this, int, int} | {}
0028: iconst_1 // {this, int, int} | {int}
0029: istore_2 // {this, int, int} | {}
0030: goto 15 // {} | {}
// {this, int, int} | {}
0033: iconst_2 // {this, int, int} | {int}
0034: istore_2 // {this, int, int} | {}
0035: goto 10 // {} | {}
// {this, int, int} | {}
0038: iconst_3 // {this, int, int} | {int}
0039: istore_2 // {this, int, int} | {}
0040: goto 5 // {} | {}
// {this, int, int} | {}
0043: iconst_4 // {this, int, int} | {int}
0044: istore_2 // {this, int, int} | {}
// {this, int, int} | {}
0045: return // {} | {}
从 JVM 规范的角度来看,tableswitch
指令对应的 Operand Stack 的变化如下:
..., index →
...
另外,tableswitch
指令对应的 Format 如下:
tableswitch
<0-3 byte pad>
defaultbyte1
defaultbyte2
defaultbyte3
defaultbyte4
lowbyte1
lowbyte2
lowbyte3
lowbyte4
highbyte1
highbyte2
highbyte3
highbyte4
jump offsets...
The index
must be of type int
and is popped from the operand stack.
- If
index
is less thanlow
orindex
is greater thanhigh
, then a target address is calculated by addingdefault
to the address of the opcode of thistableswitch
instruction. - Otherwise, the offset at position
index - low
of the jump table is extracted. The target address is calculated by adding that offset to the address of the opcode of thistableswitch
instruction. Execution then continues at the target address.
lookupswitch
从 Java 语言的视角,有一个 HelloWorld
类,代码如下:
public class HelloWorld {
public void test(int val) {
int result = 0;
switch (val) {
case 10:
result = 1;
break;
case 20:
result = 2;
break;
case 30:
result = 3;
break;
default:
result = 4;
}
}
}
从 Instruction 的视角来看,方法体对应的内容如下:
$ javap -c sample.HelloWorld
Compiled from "HelloWorld.java"
public class sample.HelloWorld {
...
public void test(int);
Code:
0: iconst_0
1: istore_2
2: iload_1
3: lookupswitch { // 3
10: 36
20: 41
30: 46
default: 51
}
36: iconst_1
37: istore_2
38: goto 53
41: iconst_2
42: istore_2
43: goto 53
46: iconst_3
47: istore_2
48: goto 53
51: iconst_4
52: istore_2
53: return
}
从 ASM 的视角来看,方法体对应的内容如下:
Label label0 = new Label();
Label label1 = new Label();
Label label2 = new Label();
Label label3 = new Label();
Label label4 = new Label();
methodVisitor.visitCode();
methodVisitor.visitInsn(ICONST_0);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitVarInsn(ILOAD, 1);
methodVisitor.visitLookupSwitchInsn(label3, new int[] { 10, 20, 30 }, new Label[] { label0, label1, label2 });
methodVisitor.visitLabel(label0);
methodVisitor.visitInsn(ICONST_1);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label1);
methodVisitor.visitInsn(ICONST_2);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label2);
methodVisitor.visitInsn(ICONST_3);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitJumpInsn(GOTO, label4);
methodVisitor.visitLabel(label3);
methodVisitor.visitInsn(ICONST_4);
methodVisitor.visitVarInsn(ISTORE, 2);
methodVisitor.visitLabel(label4);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(1, 3);
methodVisitor.visitEnd();
从 Frame 的视角来看,local variable 和 operand stack 的变化:
// {this, int} | {}
0000: iconst_0 // {this, int} | {int}
0001: istore_2 // {this, int, int} | {}
0002: iload_1 // {this, int, int} | {int}
0003: lookupswitch // {} | {}
{
10: 33
20: 38
30: 43
default: 48
}
// {this, int, int} | {}
0036: iconst_1 // {this, int, int} | {int}
0037: istore_2 // {this, int, int} | {}
0038: goto 15 // {} | {}
// {this, int, int} | {}
0041: iconst_2 // {this, int, int} | {int}
0042: istore_2 // {this, int, int} | {}
0043: goto 10 // {} | {}
// {this, int, int} | {}
0046: iconst_3 // {this, int, int} | {int}
0047: istore_2 // {this, int, int} | {}
0048: goto 5 // {} | {}
// {this, int, int} | {}
0051: iconst_4 // {this, int, int} | {int}
0052: istore_2 // {this, int, int} | {}
// {this, int, int} | {}
0053: return // {} | {}
从 JVM 规范的角度来看,lookupswitch
指令对应的 Operand Stack 的变化如下:
..., key →
...
另外,lookupswitch
指令对应的 Format 如下:
lookupswitch
<0-3 byte pad>
defaultbyte1
defaultbyte2
defaultbyte3
defaultbyte4
npairs1
npairs2
npairs3
npairs4
match-offset pairs...
The key
must be of type int
and is popped from the operand stack. The key
is compared against the match values.
- If it is equal to one of them, then a target address is calculated by adding the corresponding offset to the address of the opcode of this
lookupswitch
instruction. - If the
key
does not match any of the match values, the target address is calculated by addingdefault
to the address of the opcode of thislookupswitch
instruction. Execution then continues at the target address.