用Java实现JVM第五章《指令集和解释器》

案例介绍
本案例通过java代码实现jvm规范中指令集和解释器,完成后就可以开始执行1到100的加和计算。

Java虚拟机顾名思义,就是一台虚拟的机器,而字节码(bytecode)就是运行在这台虚拟机器上的机器码。我们已经知道,每一个类或者接口都会被Java编译器编译成一个class文件,类或接口的方法信息就放在class文件的method_info结构中。如果方法不是抽象的,也不是本地方法,方法的Java代码就会被编译器编译成字节码(即使方法是空的,编译器也会生成一条return语句),存在method_info结构的Code属性中。

环境准备
1、jdk 1.8.0
2、IntelliJ IDEA Community Edition 2018.3.1 x64

配置信息
1、调试配置
2.1、配置位置:Run/Debug Configurations -> program arguments
2.2、配置内容:-Xjre “C:\Program Files\Java\jdk1.8.0_161\jre” E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
itstack-demo-jvm-05
├── pom.xml
└── src
└── main
│ └── java
│ └── org.itstack.demo.jvm
│ ├── classfile
│ │ ├── attributes {BootstrapMethods/Code/ConstantValue...}
│ │ ├── constantpool {CONSTANT_TAG_CLASS/CONSTANT_TAG_FIELDREF/CONSTANT_TAG_METHODREF...}
│ │ ├── ClassFile.java
│ │ ├── ClassReader.java
│ │ └── MemberInfo.java
│ ├── classpath
│ │ ├── impl
│ │ │ ├── CompositeEntry.java
│ │ │ ├── DirEntry.java
│ │ │ ├── WildcardEntry.java
│ │ │ └── ZipEntry.java
│ │ ├── Classpath.java
│ │ └── Entry.java
│ ├── classpath
│ │ ├── base
│ │ ├── comparisons
│ │ ├── constants
│ │ ├── control
│ │ ├── conversions
│ │ ├── extended
│ │ ├── loads
│ │ ├── math
│ │ │ ├── add
│ │ │ ├── and
│ │ │ ├── div
│ │ │ ├── iinc
│ │ │ ├── mul
│ │ │ ├── neg
│ │ │ ├── or
│ │ │ ├── rem
│ │ │ ├── sh
│ │ │ ├── sub
│ │ │ └── xor
│ │ ├── stack
│ │ ├── store
│ │ └── Factory
│ ├── rtda
│ │ ├── Frame.java
│ │ ├── JvmStack.java
│ │ ├── LocalVars.java
│ │ ├── OperandStack.java
│ │ ├── Slot.java
│ │ └── Thread.java
│ ├── Cmd.java
│ ├── Interpret.java
│ └── Main.java
└── test
└── java
└── org.itstack.demo.test
└── HelloWorld.java

Factory.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
public class Factory {

public static Instruction newInstruction(byte opcode) {
switch (opcode) {
case 0x00:
return new NOP();
case 0x01:
return new ACONST_NULL();
case 0x02:
return new ICONST_M1();
case 0x03:
return new ICONST_0();
case 0x04:
return new ICONST_1();
case 0x05:
return new ICONST_2();
case 0x06:
return new ICONST_3();
case 0x07:
return new ICONST_4();
case 0x08:
return new ICONST_5();
case 0x09:
return new LCONST_0();
case 0x0a:
return new LCONST_1();
case 0x0b:
return new FCONST_0();
case 0x0c:
return new FCONST_1();
case 0x0d:
return new FCONST_2();
case 0x0e:
return new DCONST_0();
case 0x0f:
return new DCONST_1();
case 0x10:
return new BIPUSH();
case 0x11:
return new SIPUSH();
// case 0x12:
// return &LDC{}
// case 0x13:
// return &LDC_W{}
// case 0x14:
// return &LDC2_W{}
case 0x15:
return new ILOAD();
case 0x16:
return new LLOAD();
case 0x17:
return new FLOAD();
case 0x18:
return new DLOAD();
case 0x19:
return new ALOAD();
case 0x1a:
return new ILOAD_0();
case 0x1b:
return new ILOAD_1();
case 0x1c:
return new ILOAD_2();
case 0x1d:
return new ILOAD_3();
case 0x1e:
return new LLOAD_0();
case 0x1f:
return new LLOAD_1();
case 0x20:
return new LLOAD_2();
case 0x21:
return new LLOAD_3();
case 0x22:
return new FLOAD_0();
case 0x23:
return new FLOAD_1();
case 0x24:
return new FLOAD_2();
case 0x25:
return new FLOAD_3();
case 0x26:
return new DLOAD_0();
case 0x27:
return new DLOAD_1();
case 0x28:
return new DLOAD_2();
case 0x29:
return new DLOAD_3();
case 0x2a:
return new ALOAD_0();
case 0x2b:
return new ALOAD_1();
case 0x2c:
return new ALOAD_2();
case 0x2d:
return new ALOAD_3();
// case 0x2e:
// return iaload
// case 0x2f:
// return laload
// case 0x30:
// return faload
// case 0x31:
// return daload
// case 0x32:
// return aaload
// case 0x33:
// return baload
// case 0x34:
// return caload
// case 0x35:
// return saload
case 0x36:
return new ISTORE();
case 0x37:
return new LSTORE();
case 0x38:
return new FSTORE();
case 0x39:
return new DSTORE();
case 0x3a:
return new ASTORE();
case 0x3b:
return new ISTORE_0();
case 0x3c:
return new ISTORE_1();
case 0x3d:
return new ISTORE_2();
case 0x3e:
return new ISTORE_3();
case 0x3f:
return new LSTORE_0();
case 0x40:
return new LSTORE_1();
case 0x41:
return new LSTORE_2();
case 0x42:
return new LSTORE_3();
case 0x43:
return new FSTORE_0();
case 0x44:
return new FSTORE_1();
case 0x45:
return new FSTORE_2();
case 0x46:
return new FSTORE_3();
case 0x47:
return new DSTORE_0();
case 0x48:
return new DSTORE_1();
case 0x49:
return new DSTORE_2();
case 0x4a:
return new DSTORE_3();
case 0x4b:
return new ASTORE_0();
case 0x4c:
return new ASTORE_1();
case 0x4d:
return new ASTORE_2();
case 0x4e:
return new ASTORE_3();
// case 0x4f:
// return iastore
// case 0x50:
// return lastore
// case 0x51:
// return fastore
// case 0x52:
// return dastore
// case 0x53:
// return aastore
// case 0x54:
// return bastore
// case 0x55:
// return castore
// case 0x56:
// return sastore
case 0x57:
return new POP();
case 0x58:
return new POP2();
case 0x59:
return new DUP();
case 0x5a:
return new DUP_X1();
case 0x5b:
return new DUP_X2();
case 0x5c:
return new DUP2();
case 0x5d:
return new DUP2_X1();
case 0x5e:
return new DUP2_X2();
case 0x5f:
return new SWAP();
case 0x60:
return new IADD();
case 0x61:
return new LADD();
case 0x62:
return new FADD();
case 0x63:
return new DADD();
case 0x64:
return new ISUB();
case 0x65:
return new LSUB();
case 0x66:
return new FSUB();
case 0x67:
return new DSUB();
case 0x68:
return new IMUL();
case 0x69:
return new LMUL();
case 0x6a:
return new FMUL();
case 0x6b:
return new DMUL();
case 0x6c:
return new IDIV();
case 0x6d:
return new LDIV();
case 0x6e:
return new FDIV();
case 0x6f:
return new DDIV();
case 0x70:
return new IREM();
case 0x71:
return new LREM();
case 0x72:
return new FREM();
case 0x73:
return new DREM();
case 0x74:
return new INEG();
case 0x75:
return new LNEG();
case 0x76:
return new FNEG();
case 0x77:
return new DNEG();
case 0x78:
return new ISHL();
case 0x79:
return new LSHL();
case 0x7a:
return new ISHR();
case 0x7b:
return new LSHR();
case 0x7c:
return new IUSHR();
case 0x7d:
return new LUSHR();
case 0x7e:
return new IAND();
case 0x7f:
return new LAND();
case (byte) 0x80:
return new IOR();
case (byte) 0x81:
return new LOR();
case (byte) 0x82:
return new IXOR();
case (byte) 0x83:
return new LXOR();
case (byte) 0x84:
return new IINC();
case (byte) 0x85:
return new I2L();
case (byte) 0x86:
return new I2F();
case (byte) 0x87:
return new I2D();
case (byte) 0x88:
return new L2I();
case (byte) 0x89:
return new L2F();
case (byte) 0x8a:
return new L2D();
case (byte) 0x8b:
return new F2I();
case (byte) 0x8c:
return new F2L();
case (byte) 0x8d:
return new F2D();
case (byte) 0x8e:
return new D2I();
case (byte) 0x8f:
return new D2L();
case (byte) 0x90:
return new D2F();
case (byte) 0x91:
return new I2B();
case (byte) 0x92:
return new I2C();
case (byte) 0x93:
return new I2S();
case (byte) 0x94:
return new LCMP();
case (byte) 0x95:
return new FCMPL();
case (byte) 0x96:
return new FCMPG();
case (byte) 0x97:
return new DCMPL();
case (byte) 0x98:
return new DCMPG();
case (byte) 0x99:
return new IFEQ();
case (byte) 0x9a:
return new IFNE();
case (byte) 0x9b:
return new IFLT();
case (byte) 0x9c:
return new IFGE();
case (byte) 0x9d:
return new IFGT();
case (byte) 0x9e:
return new IFLE();
case (byte) 0x9f:
return new IF_ICMPEQ();
case (byte) 0xa0:
return new IF_ICMPNE();
case (byte) 0xa1:
return new IF_ICMPLT();
case (byte) 0xa2:
return new IF_ICMPGE();
case (byte) 0xa3:
return new IF_ICMPGT();
case (byte) 0xa4:
return new IF_ICMPLE();
case (byte) 0xa5:
return new IF_ACMPEQ();
case (byte) 0xa6:
return new IF_ACMPNE();
case (byte) 0xa7:
return new GOTO();
// case 0xa8:
// return &JSR{}
// case 0xa9:
// return &RET{}
case (byte) 0xaa:
return new TABLE_SWITCH();
case (byte) 0xab:
return new LOOKUP_SWITCH();
// case 0xac:
// return ireturn
// case 0xad:
// return lreturn
// case 0xae:
// return freturn
// case 0xaf:
// return dreturn
// case 0xb0:
// return areturn
// case 0xb1:
// return _return
// case 0xb2:
// return &GET_STATIC{}
// case 0xb3:
// return &PUT_STATIC{}
// case 0xb4:
// return &GET_FIELD{}
// case 0xb5:
// return &PUT_FIELD{}
// case 0xb6:
// return &INVOKE_VIRTUAL{}
// case 0xb7:
// return &INVOKE_SPECIAL{}
// case 0xb8:
// return &INVOKE_STATIC{}
// case 0xb9:
// return &INVOKE_INTERFACE{}
// case 0xba:
// return &INVOKE_DYNAMIC{}
// case 0xbb:
// return &NEW{}
// case 0xbc:
// return &NEW_ARRAY{}
// case 0xbd:
// return &ANEW_ARRAY{}
// case 0xbe:
// return arraylength
// case 0xbf:
// return athrow
// case 0xc0:
// return &CHECK_CAST{}
// case 0xc1:
// return &INSTANCE_OF{}
// case 0xc2:
// return monitorenter
// case 0xc3:
// return monitorexit
case (byte) 0xc4:
return new WIDE();
// case 0xc5:
// return &MULTI_ANEW_ARRAY{}
case (byte) 0xc6:
return new IFNULL();
case (byte) 0xc7:
return new IFNONNULL();
case (byte) 0xc8:
return new GOTO_W();
// case 0xc9:
// return &JSR_W{}
// case 0xca: breakpoint
// case 0xfe: impdep1
// case 0xff: impdep2
default:
return null;

}

}

}

Interpret.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//指令集解释器
class Interpret {

Interpret(MemberInfo m) {
CodeAttribute codeAttr = m.codeAttribute();
int maxLocals = codeAttr.maxLocals();
int maxStack = codeAttr.maxStack();
byte[] byteCode = codeAttr.data();
Thread thread = new Thread();
Frame frame = thread.newFrame(maxLocals, maxStack);
thread.pushFrame(frame);
loop(thread, byteCode);
}

private void loop(Thread thread, byte[] byteCode) {
Frame frame = thread.popFrame();
BytecodeReader reader = new BytecodeReader();

while (true) {
//循环
int pc = frame.nextPC();
thread.setPC(pc);
//decode
reader.reset(byteCode, pc);
byte opcode = reader.readByte();
Instruction inst = Factory.newInstruction(opcode);
if (null == inst) {
System.out.println("寄存器(指令)尚未实现 " + byteToHexString(new byte[]{opcode}));
break;
}
inst.fetchOperands(reader);
frame.setNextPC(reader.pc());
System.out.println("寄存器(指令):" + byteToHexString(new byte[]{opcode}) + " -> " + inst.getClass().getSimpleName() + " => 局部变量表:" + JSON.toJSONString(frame.operandStack().getSlots()) + " 操作数栈:" + JSON.toJSONString(frame.operandStack().getSlots())); //exec
inst.execute(frame);
}

}

private static String byteToHexString(byte[] codes) {
StringBuilder sb = new StringBuilder();
sb.append("0x");
for (byte b : codes) {
int value = b & 0xFF;
String strHex = Integer.toHexString(value);
if (strHex.length() < 2) {
strHex = "0" + strHex;
}
sb.append(strHex);
}
return sb.toString();
}

}

HelloWorld.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package org.itstack.demo.test;

public class HelloWorld {

public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
System.out.println(sum);
}

}

测试结果 {此时还不能输出结果,但是在操作数据栈中已经可以看到结果:5050}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\lib\idea_rt.jar=61887:D:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2018.3.1\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\classes;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\beust\jcommander\1.72\jcommander-1.72.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\org\projectlombok\lombok\1.18.0\lombok-1.18.0.jar;D:\Program Files (x86)\apache-maven-2.2.1\repository\com\alibaba\fastjson\1.2.40\fastjson-1.2.40.jar" org.itstack.demo.jvm.Main -Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld
classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:E:\itstack\git\istack-demo\itstack-demo-jvm\itstack-demo-jvm-05\target\test-classes\org\itstack\demo\test\HelloWorld args:null
寄存器(指令):0x03 -> ICONST_0 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
寄存器(指令):0x04 -> ICONST_1 => 局部变量表:[{"num":0},{"num":0}] 操作数栈:[{"num":0},{"num":0}]
寄存器(指令):0x3d -> ISTORE_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":1},{"num":0}] 操作数栈:[{"num":1},{"num":0}]
寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}]
寄存器(指令):0x1b -> ILOAD_1 => 局部变量表:[{"num":1},{"num":100}] 操作数栈:[{"num":1},{"num":100}]
寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":0},{"num":100}] 操作数栈:[{"num":0},{"num":100}]

... ...

寄存器(指令):0x60 -> IADD => 局部变量表:[{"num":4950},{"num":100}] 操作数栈:[{"num":4950},{"num":100}]
寄存器(指令):0x3c -> ISTORE_1 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0x84 -> IINC => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0xa7 -> GOTO => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0x1c -> ILOAD_2 => 局部变量表:[{"num":5050},{"num":100}] 操作数栈:[{"num":5050},{"num":100}]
寄存器(指令):0x10 -> BIPUSH => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}]
寄存器(指令):0xa3 -> IF_ICMPGT => 局部变量表:[{"num":101},{"num":100}] 操作数栈:[{"num":101},{"num":100}]
寄存器(指令)尚未实现 0xb2

Process finished with exit code 0