https://github.com/JuneLeo/class-parse
解析程度:比 editor Class.bt 要解析的更深,同javap解析一样。
属性表也已经全部解析完毕
https://github.com/JuneLeo/class-parse/tree/master/src/main/java/org/example/bytecode/parse/attribute
原理
根据class字节码的组成,分段解析class字节码,依此逐个解析;Parse中pase方法传入解析开始位置索引;返回结束位置索引
1 | public interface Parse { |
- code:为class字节码
- start:字节码开始位置
- return:下一个开始解析的位置
1
2
3
4
5
6public void parse(byte[] bytes) {
int start = 0;
for (Parse pars : parses) { // parses为解析器集合
start = pars.parse(start, bytes);
}
}
1 | // magic |
ClassFormat
1 | public class ClassFormat { |
MagicParse
1 | public class MagicParse implements Parse { //u4 |
VersionParse
1 | public class VersionParse implements Parse { |
getU2Int
1 | public static int getU2Int(int start, byte[] bytes) { |
未解析部分:属性表
属性表
深入理解JAVA虚拟机 180页部分
Code
源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void play(String name) {
if (name.equals("basket")) {
System.out.println("篮球");
}
}
}javap
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
47public org.example.Person(java.lang.String, int);
descriptor: (Ljava/lang/String;I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=3
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #2 // Field name:Ljava/lang/String;
9: aload_0
10: iload_2
11: putfield #3 // Field age:I
14: return
LineNumberTable:
line 8: 0
line 9: 4
line 10: 9
line 11: 14
LocalVariableTable:
Start Length Slot Name Signature
0 15 0 this Lorg/example/Person;
0 15 1 name Ljava/lang/String;
0 15 2 age I
public void play(java.lang.String);
descriptor: (Ljava/lang/String;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_1
1: ldc #4 // String basket
3: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z
6: ifeq 17
9: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
12: ldc #7 // String 篮球
14: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
17: return
LineNumberTable:
line 15: 0
line 16: 9
line 18: 17
LocalVariableTable:
Start Length Slot Name Signature
0 18 0 this Lorg/example/Person;
0 18 1 name Ljava/lang/String;我们的实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19方法:<init>
aload_0
invokespecial java/lang/Object <init> ()V
aload_0
aload_1
putfield org/example/Person name Ljava/lang/String;
aload_0
iload_2
putfield org/example/Person age I
return
方法:play
aload_1
ldc basket
invokevirtual java/lang/String equals (Ljava/lang/Object;)Z
ifeq name
getstatic java/lang/System out Ljava/io/PrintStream;
ldc 篮球
invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
return原理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23@Override
public int parse(int start, byte[] bytes) {
nameIndex = Utils.getU2Int(start, bytes);
start += 2;
attributeLength = Utils.getU4Int(start, bytes);
start += 4;
String utfConstant = String.valueOf(constantParse.getUtfConstant(nameIndex));
switch (utfConstant.toLowerCase()) {
case "code":
attributeFormatParse = new CodeParse(attributeLength,constantParse);
break;
default:
attributeFormatParse = new Info(attributeLength);
break;
}
start = attributeFormatParse.parse(start, bytes);
return start;
}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
77package org.example.bytecode.parse.attribute;
import org.example.bytecode.Utils;
import org.example.bytecode.parse.ConstantParse;
import org.example.bytecode.parse.constant.ConstantInfoParse;
public class CodeParse extends AttributeFormatParse {
public int attributeNameIndex;
public int attributeLength;
public int maxStack;
public int maxLocals;
public int codeLength;
public int exceptionTableLength;
public int attributesCount;
private ConstantParse constantParse;
public CodeParse(int length, ConstantParse constantParse) {
super(length);
this.constantParse = constantParse;
}
@Override
public int parse(int start, byte[] code) {
int index = start;
maxStack = Utils.getU2Int(index, code);//u2
index += 2;
maxLocals = Utils.getU2Int(index, code); //u2
index += 2;
codeLength = Utils.getU4Int(index, code); //u4
index += 4;
int l = index + codeLength;
while (index < l) {
int cmd = Utils.getU1Int(index, code);
index += 1;
String cmdStr = Utils.getCMD(cmd); // 这里是复制的asm中的字节码数组
System.out.print(cmdStr);
int paramsLength = Utils.getParamsLength(cmd); // 这里是复制的asm中的字节码后面参数的length数组
if (paramsLength >= 0) {
if (paramsLength == 1) {
int u1Int = Utils.getU1Int(index, code);
Object utfConstant = constantParse.getUtfConstant(u1Int);
System.out.print(" " + String.valueOf(utfConstant));
} else if (paramsLength == 2) {
int u2Int = Utils.getU2Int(index, code);
Object utfConstant = constantParse.getUtfConstant(u2Int);
System.out.print(" " + String.valueOf(utfConstant));
} else {
for (int i = 0; i < paramsLength; i++) {
System.out.print(" " + Utils.getU1Int(index + i, code));
}
}
index += paramsLength;
} else {
break;
}
System.out.print("\n");
}
index += codeLength;
// 这里是 exception和attribute,attribute主要解析LineNumberTable和LocalVariableTable即可,后续实现
// exceptionTableLength = Utils.getU2Int(index, code);
// index += 2;
// // todo exception info
// attributeLength = Utils.getU2Int(index, code);
// index += 2;
// // todo attribute
return start + length;
}
}
Why
最近读了深入理解java虚拟机,为了加深对于字节码的理解和记忆,写了一个class解析器
What1-Class结构
字节码
Class结构
class解析器
Constant Pool
- CONSTANT_Utf8_info (tag/length/bytes-u1)
- CONSTANT_Integer_info (tag/bytes-u4)
- CONSTANT_Float_info (tag/bytes-u4)
- CONSTANT_Long_info (tag/bytes-u8)
- CONSTANT_Double_info (tag/bytes-u8)
- CONSTANT_Class_info (tag/index)
- CONSTANT_String_info (tag/index)
- CONSTANT_Fieldref_info (tag/index类/index)
- CONSTANT_Methodref_info (tag/index类/index)
- CONSTANT_Interface-Methodref_info (tag/index接口类/index)
- CONSTANT_NameAndType_info (tag/index名称/index签名)
- CONSTANT_Method-Handle_info
- CONSTANT_Method-type_info
- CONSTANT_Invoke-Dynamic_info
attribute
- Code 方法表
- LineNumberTable 调试表
- LocalVariableTable 本地变量表
- SourceFile 源文件表
- Exceptions 异常表
- ConstantValue 常量表
- InnerClasses 内部类表
- Deprecated 废弃标识
- Synthetic 表示方法或者字段
- StackMapTable 类型检查器
- Signature 泛型的方法签名 - 泛型会类型擦除
- BootstrapMethods invokedymic相关
- LocalVariableTypeTable 泛型相关
- 还有其他一些没有实现