Java ASM系列:(058)opcode: jump
Posted lsieun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java ASM系列:(058)opcode: jump相关的知识,希望对你有一定的参考价值。
本文属于Java ASM系列二:OPCODE当中的一篇。
对于《Java ASM系列二:OPCODE》有配套的视频讲解,可以点击这里和这里进行查看;同时,也可以点击这里查看源码资料。
1. 概览
从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
2. if and goto
2.1 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
2.2. 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
2.3. 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.
2.4. 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.
2.5. 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
3. switch
3.1. 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.
3.2. 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.
以上是关于Java ASM系列:(058)opcode: jump的主要内容,如果未能解决你的问题,请参考以下文章
Java ASM系列:(051)opcode: constant
Java ASM系列:(056)opcode: method