按照JVM 字节码的存储格式 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html 规范,实现了一段程序解析字节码结构。
运行:
./run.sh
因为字节码指令太多了,所以还没有实现解析每一个字节码指令(
Code属性里面的内容)。Instruction.java 实现了一个字节码指令(ldc)的解析,其他指令类似。
========== Start Parse out/site/jiyang/Main.class =========
== Magic Number ======================================
MagicNumber{b1=CA, b2=FE, b3=BA, b4=BE}
== Version ======================================
Version{minorVersion=0, majorVersion=52}
== Constant Pool ======================================
ConstantPool{poolCount=214, constantsSize=2376, mConstantItems=
#1 MethodRef{classInfoIndex=65, nameAndTypeIndex=130}
#2 String{index=131}
...
#213 Utf8{attributeLength=20, value='()Ljava/lang/String;'}
}
== Access Flags ======================================
AccessFlags: public,final,super,
bytecode.AccessFlags@27bc2616
== This class ======================================
ClassIndex{classInfoIndex=49 -> 171 -> bytecode/BytecodeParser}
== Super class ======================================
ClassIndex{classInfoIndex=65 -> 185 -> java/lang/Object}
== Interfaces ======================================
Interface count: 0
Interface Indexes: []
== Fields ======================================
FieldOrMethod{count=2, name='Fields', mEntities=[Entity{accessFlag=[Public,Static,], nameIndex=66, name=constantItemHashMap, descriptorIndex=67, attributesCount=1, mAttributeInfos=[AttributeInfo{nameIndex=68, attributeLength=2, mInfo=Signature{signatureIndex=69 -> Ljava/util/HashMap<Ljava/lang/Integer;Lbytecode/ConstantItem;>;}}]}, Entity{accessFlag=[Private,Static,Final,], nameIndex=70, name=path, descriptorIndex=71, attributesCount=1, mAttributeInfos=[AttributeInfo{nameIndex=72, attributeLength=2, mInfo=ConstantValue{constantValueIndex=4}}]}]}
== Methods ======================================
FieldOrMethod{count=4, name='Methods', mEntities=[Entity{accessFlag=[Public,], nameIndex=73,...
== This class attribute_info ======================================
[AttributeInfo{nameIndex=128, attributeLength=2, mInfo=SourceFile{sourceFileIndex=129 -> BytecodeParser.java}}]
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
按照字节码的储存顺序依次解析每一块内容。所有解析过程都共用一个 byte[] 数组,每个具体的解析过程,通过控制 offset 实现解析不同部分的数据。
public final class BytecodeParser { // 用来在常量池解析完之后,存在全局方便后面使用 public static HashMap<Integer, ConstantItem> constantItemHashMap; private void parse(final byte[] bytes) { printSectionDivider("Magic Number"); Section magicNumber = new MagicNumber(0, bytes); magicNumber.parse(); System.out.println(magicNumber); printSectionDivider("Version"); Section version = new Version(magicNumber.end(), bytes); version.parse(); System.out.println(version); printSectionDivider("Constant Pool"); ConstantPool constantPool = new ConstantPool(version.end(), bytes); constantPool.parse(); System.out.println(constantPool); constantItemHashMap = constantPool.getConstantItems(); printSectionDivider("Access Flags"); Section accessFlags = new AccessFlags(constantPool.end(), bytes); accessFlags.parse(); System.out.println(accessFlags); printSectionDivider("This class"); Section thisClass = new ClassIndex(accessFlags.end(), bytes); thisClass.parse(); System.out.println(thisClass); printSectionDivider("Super class"); Section superClass = new ClassIndex(thisClass.end(), bytes); superClass.parse(); System.out.println(superClass); printSectionDivider("Interfaces"); Interfaces interfaces = new Interfaces(superClass.end(), bytes); interfaces.parse(); System.out.println(interfaces); printSectionDivider("Fields"); FieldOrMethod fields = new FieldOrMethod("Fields", interfaces.end(), bytes); fields.parse(); System.out.println(fields); printSectionDivider("Methods"); FieldOrMethod methods = new FieldOrMethod("Methods", fields.end(), bytes); methods.parse(); System.out.println(methods); printSectionDivider("This class attribute_info"); int offset = methods.end(); int attributeCount = Utils.read2Number(bytes, offset); offset += 2; AttributeInfo[] attributeInfos = new AttributeInfo[attributeCount]; for (int i = 0; i < attributeCount; i++) { attributeInfos[i] = new AttributeInfo(); attributeInfos[i].parse(bytes, offset); offset += attributeInfos[i].size(); } System.out.println(Arrays.toString(attributeInfos)); } }
public static void main(String[] args) { if (args.length < 1) { throw new IllegalArgumentException("Must pass class file path."); } String path = args[0]; System.out.println("========== Start Parse========="); try { FileInputStream fis = new FileInputStream(new File(path)); int count = fis.available(); byte[] buff = new byte[count]; int read = fis.read(buff); if (read != count) { return; } new BytecodeParser().parse(buff); } catch (IOException e) { e.printStackTrace(); } } private static void printSectionDivider(String name) { System.out.println("== " + name + " ======================================"); }
方便后面实现时生成统一的方法
interface Parsable { public void parse(byte[] bytes, int offset); }
abstract class Section { final int start; //记录每一块结构的开始位置 final byte[] bytes; // 所有人共用的数据 public Section(int start, byte[] bytes) { this.start = start; this.bytes = bytes; } public final int start() { return start; } public final int end() { return start + size(); } abstract int size(); //每一块结构需要返回自己占用了多少字节 abstract public void parse(); }
class MagicNumber extends Section { public MagicNumber(int start, byte[] bytes) { super(start, bytes); } @Override int size() { return 4; } private int b1, b2, b3, b4;//u1 @Override public void parse() { b1 = Utils.readUnsignedByte(bytes, start); b2 = Utils.readUnsignedByte(bytes, start + 1); b3 = Utils.readUnsignedByte(bytes, start + 2); b4 = Utils.readUnsignedByte(bytes, start + 3); } @Override public String toString() { return "MagicNumber{" + "b1=" + Integer.toHexString(b1).toUpperCase() + ", b2=" + Integer.toHexString(b2).toUpperCase() + ", b3=" + Integer.toHexString(b3).toUpperCase() + ", b4=" + Integer.toHexString(b4).toUpperCase() + '}'; } }
class Version extends Section { public Version(int start, byte[] bytes) { super(start, bytes); } @Override int size() { return 4; } private int minorVersion, majorVersion; //u2 @Override public void parse() { minorVersion = Utils.read2Number(bytes, start); majorVersion = Utils.read2Number(bytes, start + 2); } @Override public String toString() { return "Version{" + "minorVersion=" + minorVersion + ", majorVersion=" + majorVersion + '}'; } }
常量池这里有一点要注意:代表常量池有多少常量的 poolCount,是包括了 poolCount 它本身的。也就是真正的常量池的常量其实是 poolCount -1 个。
每个常量项都有一个 u1 的 tag 表示其类型。解析时需要先解析 tag, 然后根据 tag 做对应类型的解析。
class ConstantPool extends Section { int poolCount; //u2 private int constantsSize; private HashMap<Integer, ConstantItem> mConstantItems = new HashMap<>(); public HashMap<Integer, ConstantItem> getConstantItems() { return mConstantItems; } public ConstantPool(int start, byte[] bytes) { super(start, bytes); } @Override int size() { return 2/*u2 的常量池计数占用*/ + constantsSize; } @Override public void parse() { poolCount = Utils.read2Number(bytes, start); // 遍历常量表的每一项常量 int offset = 2; for (int i = 1; i <= poolCount - 1; i++) { int tag = Utils.readUnsignedByte(bytes, start + offset); // 根据 tag 找到匹配的常量 ConstantItem item = ConstantItem.getConstantItemTags(tag); if (item == null) { System.err.println("Not found ConstantItem for " + tag); return; } item.parse(bytes, start + offset); offset += item.size(); constantsSize += item.size(); mConstantItems.put(i, item); } } @Override public String toString() { StringBuilder sb = new StringBuilder(); for (int i = 1; i <= poolCount - 1; i++) { sb.append(" #").append(i).append(" ").append(mConstantItems.get(i)).append("\n"); } return "ConstantPool{" + "poolCount=" + poolCount + ", constantsSize=" + constantsSize + ", mConstantItems=\n" + sb.toString() + '}'; } }
/** * 常量项 * 每个常量项都有 u1 的 tag, 表示其类型 * <pre> * cp_info { * u1 tag; * u1 info[]; * } * </pre> */ abstract class ConstantItem implements Parsable { final int tag; ConstantItem(int tag) { this.tag = tag; } abstract protected int contentSize(); int size() { return 1/*u1的tag*/ + contentSize(); } @Nullable static ConstantItem getConstantItemTags(int tag) { switch (tag) { case 1: return new UTF8(); case 3: return new INTEGER(); case 4: return new FLOAT(); case 5: return new LONG(); case 6: return new DOUBLE(); case 7: return new CLASS(); case 8: return new STRING(); case 9: return new FIELD_REF(); case 10: return new METHOD_REF(); case 11: return new Interface_Method_Ref(); case 12: return new NAME_AND_TYPE(); case 15: return new Method_Handle(); case 16: return new Method_Type(); case 18: return new Invoke_Dynamic(); default: return null; } } }
所有的字面量都储存在 UTF8 常量中。value 存储着以 UTF-8 编码的字符串的原始字节数据。
//region Constants class UTF8 extends ConstantItem { private int length; public String value; UTF8() { super(1); } @Override protected int contentSize() { return length /* 字符串占用的字节数 */ + 2 /* u2 的字符串长度 attributeLength*/; } @Override public void parse(byte[] bytes, int start) { length = Utils.read2Number(bytes, start + 1); value = new String(bytes, start + 3, length); } @Override public String toString() { return "Utf8{" + "attributeLength=" + length + ", value='" + value + '\'' + '}'; } }
class INTEGER extends ConstantItem { int value; protected INTEGER() { super(3); } @Override protected int contentSize() { return 4 /*u4 值*/; } @Override public void parse(byte[] bytes, int start) { value = Utils.read4Number(bytes, start + 1); } @Override public String toString() { return "Integer{" + "value=" + value + '}'; } }
class FLOAT extends ConstantItem { float value; FLOAT() { super(4); } @Override protected int contentSize() { return 4 /* 同 INTEGER 的注释 */; } @Override public void parse(byte[] bytes, int start) { value = Utils.read4Number(bytes, start + 1); } @Override public String toString() { return "Float{" + "value=" + value + '}'; } }
class LONG extends ConstantItem { long value; LONG() { super(5); } @Override protected int contentSize() { return 8 /* u8 值*/; } @Override public void parse(byte[] bytes, int start) { value = Utils.read8Number(bytes, start + 1); } @Override public String toString() { return "Long{" + "value=" + value + '}'; } }
class DOUBLE extends ConstantItem { DOUBLE() { super(6); } double value; @Override protected int contentSize() { return 8 /*u8 值*/; } @Override public void parse(byte[] bytes, int start) { value = Utils.read8Number(bytes, start + 1); } @Override public String toString() { return "Double{" + "value=" + value + '}'; } }
存储指向类的全限定名在常量池中的索引 index
class CLASS extends ConstantItem {
CLASS() {
super(7);
}
int index;
@Override
protected int contentSize() {
return 2 /*u2 常量池索引*/;
}
@Override
public void parse(byte[] bytes, int start) {
index = Utils.read2Number(bytes, start + 1);
}
@Override
public String toString() {
return "Class{" +
"index=" + index +
'}';
}
}
存储指向 Constant_UTF8 的索引 index
class STRING extends ConstantItem { STRING() { super(8); } int index; //u2 @Override protected int contentSize() { return 2/*u2 常量池索引*/; } @Override public void parse(byte[] bytes, int start) { index = Utils.read2Number(bytes, start + 1); } @Override public String toString() { return "String{" + "index=" + index + '}'; } }
- classInfoIndex 声明字段的类的信息,指向 CONSTANT_Class
- nameAndTypeIndex 字段的信息,指向 CONSTANT_NameAndType 类型的索引
class FIELD_REF extends ConstantItem {
int classInfoIndex, nameAndTypeIndex;
FIELD_REF() {
super(9);
}
@Override
protected int contentSize() {
return 2/*u2 类信息常量池索引*/ + 2/*u2 名字和类型常量池索引*/;
}
@Override
public void parse(byte[] bytes, int start) {
classInfoIndex = Utils.read2Number(bytes, start + 1);
nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
}
@Override
public String toString() {
return "FileRef{" +
"classInfoIndex=" + classInfoIndex +
", nameAndTypeIndex=" + nameAndTypeIndex +
'}';
}
}
- classInfoIndex 声明方法的类的信息,指向 CONSTANT_Class
- nameAndTypeIndex 方法的信息,指向 CONSTANT_NameAndType 类型的索引
class METHOD_REF extends ConstantItem {
int classInfoIndex, nameAndTypeIndex;
METHOD_REF() {
super(10);
}
@Override
protected int contentSize() {
return 4 /*同 FIELD_REF*/;
}
@Override
public void parse(byte[] bytes, int start) {
classInfoIndex = Utils.read2Number(bytes, start + 1);
nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
}
@Override
public String toString() {
return "MethodRef{" +
"classInfoIndex=" + classInfoIndex +
", nameAndTypeIndex=" + nameAndTypeIndex +
'}';
}
}
- classInfoIndex 声明接口的类的信息,指向 CONSTANT_Class
- nameAndTypeIndex 接口的信息,指向 CONSTANT_NameAndType 类型的索引
class Interface_Method_Ref extends ConstantItem {
int classInfoIndex, nameAndTypeIndex;
Interface_Method_Ref() {
super(11);
}
@Override
protected int contentSize() {
return 4/*同FIELD_REF*/;
}
@Override
public void parse(byte[] bytes, int start) {
classInfoIndex = Utils.read2Number(bytes, start + 1);
nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
}
@Override
public String toString() {
return "InterfaceMethodRef{" +
"classInfoIndex=" + classInfoIndex +
", nameAndTypeIndex=" + nameAndTypeIndex +
'}';
}
}
- nameIndex 指向方法或字段的名称在常量池中的索引
- descriptorIndex 指向方法或字段的描述符在常量池中的索引
class NAME_AND_TYPE extends ConstantItem {
int nameIndex, descriptorIndex; //u2
NAME_AND_TYPE() {
super(12);
}
@Override
protected int contentSize() {
return 2/*u2 名称在常量池的索引*/ + 2/*u2 描述符在常量池的索引*/;
}
@Override
public void parse(byte[] bytes, int start) {
nameIndex = Utils.read2Number(bytes, start + 1);
descriptorIndex = Utils.read2Number(bytes, start + 3);
}
@Override
public String toString() {
return "NameAndType{" +
"nameIndex=" + nameIndex +
", descriptorIndex=" + descriptorIndex +
'}';
}
}
详细信息参考 https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.8
- referenceKind 方法句柄的类型,代表字节码被执行时的行为. ? 不太懂这个... 还没有遇到出现的情况
- referenceIndex
class Method_Handle extends ConstantItem { int referenceKind; //u1 int referenceIndex; //u2 Method_Handle() { super(15); } @Override protected int contentSize() { return 1/**/ + 2/**/; } @Override public void parse(byte[] bytes, int start) { referenceKind = Utils.readUnsignedByte(bytes, start + 1); referenceIndex = Utils.read2Number(bytes, start + 2); } @Override public String toString() { return "MethodHandle{" + "referenceKind=" + referenceKind + ", referenceIndex=" + referenceIndex + '}'; } }
- descriptorIndex 指向常量池中 UTF8 类型的索引,表示方法的描述符
class Method_Type extends ConstantItem { int descriptorIndex; //u2 Method_Type() { super(16); } @Override protected int contentSize() { return 2; } @Override public void parse(byte[] bytes, int start) { descriptorIndex = Utils.read2Number(bytes, start + 1); } @Override public String toString() { return "MethodType{" + "descriptorIndex=" + descriptorIndex + '}'; } }
https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.10
与 Java 的动态方法调用有关。
class Invoke_Dynamic extends ConstantItem {
int bootstrapAttrIndex, nameAndTypeIndex;
Invoke_Dynamic() {
super(18);
}
@Override
protected int contentSize() {
return 4;
}
@Override
public void parse(byte[] bytes, int start) {
bootstrapAttrIndex = Utils.read2Number(bytes, start + 1);
nameAndTypeIndex = Utils.read2Number(bytes, start + 3);
}
@Override
public String toString() {
return "InvokeDynamic{" +
"bootstrapAttrIndex=" + bootstrapAttrIndex +
", nameAndTypeIndex=" + nameAndTypeIndex +
'}';
}
}
//endregion
一个字段或方法或类的访问标志都是使用一个 u2 类型的数字表示,通过位运算 | 赋予,通过 &运算判断是否拥有某种访问标志。
class AccessFlags extends Section { private static final int PUBLIC = 0x0001; // 0000 0000 0000 0001 private static final int FINAL = 0x0010; // 0000 0000 0001 0000 private static final int SUPER = 0x0020; // 0000 0000 0010 0000 private static final int INTERFACE = 0x0200; // 0000 0010 0000 0000 private static final int ABSTRACT = 0x0400; // 0000 0100 0000 0000 private static final int SYNTHETIC = 0x1000; // 0001 0000 0000 0000 private static final int ANNOTATION = 0x2000;// 0010 0000 0000 0000 private static final int ENUM = 0x4000; // 0100 0000 0000 0000 private static final int PRIVATE = 0x0002; private int accessFlags; //u2 public AccessFlags(int start, byte[] bytes) { super(start, bytes); } @Override int size() { return 2; } @Override public void parse() { accessFlags = Utils.read2Number(bytes, start); System.out.println("AccessFlags: " + printAccess(accessFlags)); } public static String printAccess(int access) { ArrayList<String> accessFlags = new ArrayList<>(); if ((access & PUBLIC) == PUBLIC) { accessFlags.add("public"); } if ((access & FINAL) == FINAL) { accessFlags.add("final"); } if ((access & SUPER) == SUPER) { accessFlags.add("super"); } if ((access & INTERFACE) == INTERFACE) { accessFlags.add("interface"); } if ((access & ABSTRACT) == ABSTRACT) { accessFlags.add("abstract"); } if ((access & SYNTHETIC) == SYNTHETIC) { accessFlags.add("synthetic"); } if ((access & ANNOTATION) == ANNOTATION) { accessFlags.add("annotation"); } if ((access & ENUM) == ENUM) { accessFlags.add("enum"); } if ((access & PRIVATE) == PRIVATE) { accessFlags.add("private"); } StringBuilder sb = new StringBuilder(); accessFlags.forEach(s -> sb.append(s).append(",")); return sb.toString(); } }
类自身和其父类都存储在 class_info 结构中,classInfoIndex 指向常量池中 Constant_Class 类型的常量
/** * 类索引 * 用于解析自身类和父类在常量池的索引 */ class ClassIndex extends Section { private int classInfoIndex; public ClassIndex(int start, byte[] bytes) { super(start, bytes); } @Override int size() { return 2; } @Override public void parse() { classInfoIndex = Utils.read2Number(bytes, start); } @Override public String toString() { CLASS clasz = (CLASS) BytecodeParser.constantItemHashMap.get(classInfoIndex); UTF8 utf8 = (UTF8) BytecodeParser.constantItemHashMap.get(clasz.index); return "ClassIndex{" + "classInfoIndex=" + classInfoIndex + " -> " + clasz.index + " -> " + utf8.value + '}'; } }
类实现的所有接口在常量池的 Constant_class 类型索引
class Interfaces extends Section { int interfaceCount; //u2 int[] indexs; // u2[interfaceCount] public Interfaces(int start, byte[] bytes) { super(start, bytes); } @Override int size() { return interfaceCount * 2 + 2; } @Override public void parse() { interfaceCount = Utils.read2Number(bytes, start); int offset = 2; indexs = new int[interfaceCount]; for (short i = 0; i < interfaceCount; i++) { int index = Utils.read2Number(bytes, start + offset); indexs[i] = index; } } @Override public String toString() { return "Interfaces{" + "interfaceCount=" + interfaceCount + ", indexs=" + Arrays.toString(indexs) + '}'; } }
/** * <pre> * field_info { * u2 access_flags; * u2 name_index; * u2 descriptor_index; * u2 attributes_count; * attribute_info attributes[attributes_count]; * } * </pre> */ class FieldOrMethod extends Section { private int infoSize = 0; int count; //u2 static class Entity { int accessFlag; //u2 int nameIndex; //u2 int descriptorIndex; //u2 int attributesCount; //u2 private ArrayList<AttributeInfo> mAttributeInfos = new ArrayList<>(); public int size() { int infoSize = 0; for (AttributeInfo info : mAttributeInfos) { infoSize += info.size(); } return 8 + infoSize; } public void parse(byte[] bytes, int offset) { accessFlag = Utils.read2Number(bytes, offset); offset += 2; nameIndex = Utils.read2Number(bytes, offset); offset += 2; descriptorIndex = Utils.read2Number(bytes, offset); offset += 2; attributesCount = Utils.read2Number(bytes, offset); offset += 2; for (int i = 0; i < attributesCount; i++) { AttributeInfo attributeInfo = new AttributeInfo(); attributeInfo.parse(bytes, offset); mAttributeInfos.add(attributeInfo); offset += attributeInfo.size(); } } static final int PUBLIC = 0x0001; static final int PRIVATE = 0x0002; static final int PROTECTED = 0x0004; static final int STATIC = 0x0008; static final int FINAL = 0x0010; // field special static final int VOLATILE = 0x0040; static final int TRANSIENT = 0x0080; static final int SYNTHETIC = 0x1000; static final int ENUM = 0x4000; // method special static final int SYNCHRONIZED = 0x0020; static final int BRIDGE = 0x0040; static final int VARARGS = 0x0080; static final int NATIVE = 0x0100; static final int ABSTRACT = 0x0400; static final int STRICTFP = 0x0500; private static String accessFlagReadable(int access) { StringBuilder sb = new StringBuilder(); sb.append("["); if ((access & PUBLIC) == PUBLIC) sb.append("Public,"); if ((access & PRIVATE) == PRIVATE) sb.append("Private,"); if ((access & PROTECTED) == PROTECTED) sb.append("Protected,"); if ((access & STATIC) == STATIC) sb.append("Static,"); if ((access & FINAL) == FINAL) sb.append("Final,"); if ((access & VOLATILE) == VOLATILE) sb.append("Volatile,"); if ((access & TRANSIENT) == TRANSIENT) sb.append("Transient,"); if ((access & SYNTHETIC) == SYNTHETIC) sb.append("Synthetic,"); if ((access & ENUM) == ENUM) sb.append("Enum,"); if ((access & SYNCHRONIZED) == SYNCHRONIZED) sb.append(", Synchronized"); if ((access & BRIDGE) == BRIDGE) sb.append(",Bridge"); if ((access & VARARGS) == VARARGS) sb.append(",Varargs"); if ((access & NATIVE) == NATIVE) sb.append(",Native"); if ((access & ABSTRACT) == ABSTRACT) sb.append(",Abstract"); if ((access & STRICTFP) == STRICTFP) sb.append(",Strictfp"); sb.append("]"); return sb.toString(); } @Override public String toString() { return "Entity{" + "accessFlag=" + accessFlagReadable(accessFlag) + ", nameIndex=" + nameIndex + ", name=" + ((UTF8) BytecodeParser.constantItemHashMap.get(nameIndex)).value + ", descriptorIndex=" + descriptorIndex + ", attributesCount=" + attributesCount + ", mAttributeInfos=" + mAttributeInfos + '}'; } } private final String name; private final ArrayList<Entity> mEntities = new ArrayList<>(); public FieldOrMethod(String name, int start, byte[] bytes) { super(start, bytes); this.name = name; } @Override int size() { return infoSize + 2; } @Override public void parse() { int offset = start; count = Utils.read2Number(bytes, offset); offset += 2; for (int i = 0; i < count; i++) { Entity entity = new Entity(); entity.parse(bytes, offset); mEntities.add(entity); infoSize += entity.size(); offset += entity.size(); } } @Override public String toString() { return "FieldOrMethod{" + "count=" + count + ", name='" + name + '\'' + ", mEntities=" + mEntities + '}'; } }
字段、方法、类都能拥有自己的属性表。只是某些属性只会出现在字段上(比如 volatile)或方法上(比如 synchronized)。
u2 attribute_name_index;
u4 attribute_length;
attribute_name_index 和 attribute_length 是每个属性都有的两个字段,所以提出来由父类实现解析。
/**
* <pre>
* attribute_info {
* u2 attribute_name_index;
* u4 attribute_length;
* u1 info[attribute_length];
* }
* </pre>
*/
class AttributeInfo implements Parsable {
private int nameIndex; //u2
private int attributeLength; //u4
private Info mInfo;
@Override
public void parse(byte[] bytes, int offset) {
nameIndex = Utils.read2Number(bytes, offset);
offset += 2;
attributeLength = Utils.read4Number(bytes, offset);
offset += 4;
// 根据属性名称找到匹配的属性
ConstantItem constantItem = BytecodeParser.constantItemHashMap.get(nameIndex);
UTF8 utf8 = (UTF8) constantItem;
String infoName = utf8.value;
mInfo = Info.getMatchInfo(infoName);
if (mInfo == null) {
System.err.println("Not found matching Attributes: " + infoName);
return;
}
mInfo.parse(bytes, offset);
}
public int size() {
return 2 + 4 + attributeLength;
}
@Override
public String toString() {
return "AttributeInfo{" +
"nameIndex=" + nameIndex +
", attributeLength=" + attributeLength +
", mInfo=" + mInfo +
'}';
}
}
abstract class Info implements Parsable {
public int size() {
return contentSize();
}
abstract protected int contentSize();
abstract public void parseInner(byte[] bytes, int offset);
@Override
public void parse(byte[] bytes, int offset) {
parseInner(bytes, offset);
}
@Nullable
public static Info getMatchInfo(String name) {
switch (name) {
case "Code":
return new CodeInfo();
case "ConstantValue":
return new ConstantValue();
case "Exceptions":
return new Exceptions();
case "LineNumberTable":
return new LineNumberTable();
case "LocalVariableTable":
return new LocalVariableTable();
case "LocalVariableTypeTable":
return new LocalVariableTypeTable();
case "SourceFile":
return new SourceFile();
case "InnerClasses":
return new InnerClasses();
case "Deprecated":
return new Deprecated();
case "Synthetic":
return new Synthetic();
case "StackMapTable":
// TODO StackMapTable 待实现
return null;
case "Signature":
return new Signature();
default:
return null;
}
}
}
//region Info /** * <pre> * Code_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 max_stack; * u2 max_locals; * u4 code_length; * u1 code[code_length]; * u2 exception_table_length; * { u2 start_pc; * u2 end_pc; * u2 handler_pc; * u2 catch_type; * } exception_table[exception_table_length]; * u2 attributes_count; * attribute_info attributes[attributes_count]; * } * </pre> */ class CodeInfo extends Info { class ExceptionTable implements Parsable { int startPc; //u2 int endPc; //u2 int handlePc; // u2 int catchType; //u2 public int size() { return 2 + 2 + 2 + 2; } @Override public void parse(byte[] bytes, int offset) { startPc = Utils.read2Number(bytes, offset); offset += 2; endPc = Utils.read2Number(bytes, offset); offset += 2; handlePc = Utils.read2Number(bytes, offset); offset += 2; catchType = Utils.read2Number(bytes, offset); } @Override public String toString() { return "ExceptionTable{" + "startPc=" + startPc + ", endPc=" + endPc + ", handlePc=" + handlePc + ", catchType=" + catchType + '}'; } } int maxStack; //u2 int maxLocals; //u2 int codeLength; //u4 int[] code; // u1[codeLength] int exceptionTableLength; //u2 ExceptionTable[] exceptionTable; int attributeCount; //u2 AttributeInfo[] attributes; private int attributesSize; @Override public int contentSize() { return attributesSize + 2 + 2 + 4 + codeLength + exceptionTableLength; } @Override public void parseInner(byte[] bytes, int offset) { maxStack = Utils.read2Number(bytes, offset); offset += 2; maxLocals = Utils.read2Number(bytes, offset); offset += 2; codeLength = Utils.read4Number(bytes, offset); offset += 4; code = new int[codeLength]; for (int i = 0; i < codeLength; i++) { code[i] = Utils.readUnsignedByte(bytes, offset); offset += 1; } exceptionTableLength = Utils.read2Number(bytes, offset); offset += 2; exceptionTable = new ExceptionTable[exceptionTableLength]; for (int i = 0; i < exceptionTableLength; i++) { exceptionTable[i] = new ExceptionTable(); exceptionTable[i].parse(bytes, offset); offset += exceptionTable[i].size(); } attributeCount = Utils.read2Number(bytes, offset); offset += 2; attributes = new AttributeInfo[attributeCount]; for (int i = 0; i < attributeCount; i++) { attributes[i] = new AttributeInfo(); attributes[i].parse(bytes, offset); offset += attributes[i].size(); attributesSize += attributes[i].size(); } } public String toString() { return "CodeInfo{" + "maxStack=" + maxStack + ", maxLocals=" + maxLocals + ", codeLength=" + codeLength + ", code=" + Arrays.toString(code) + ", exceptionTableLength=" + exceptionTableLength + ", exceptionTable=" + Arrays.toString(exceptionTable) + ", attributeCount=" + attributeCount + ", attributes=" + Arrays.toString(attributes) + '}'; } }
class ConstantValue extends Info { int constantValueIndex; //u2 @Override public void parseInner(byte[] bytes, int offset) { constantValueIndex = Utils.read2Number(bytes, offset + 2); } @Override public int contentSize() { return 2; } @Override public String toString() { return "ConstantValue{" + "constantValueIndex=" + constantValueIndex + '}'; } }
/** * <pre> * Exceptions_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 number_of_exceptions; * u2 exception_index_table[number_of_exceptions]; * } * </pre> */ class Exceptions extends Info { int numberOfExceptions; //u2 int[] exceptionIndexTable; //u2[numberOfExceptions] @Override public int contentSize() { return 2; } @Override public void parseInner(byte[] bytes, int offset) { numberOfExceptions = Utils.read2Number(bytes, offset); offset += 2; exceptionIndexTable = new int[numberOfExceptions]; for (int i = 0; i < numberOfExceptions; i++) { exceptionIndexTable[i] = Utils.read2Number(bytes, offset); offset += 2; } } @Override public String toString() { return "Exceptions{" + "numberOfExceptions=" + numberOfExceptions + ", exceptionIndexTable=" + Arrays.toString(exceptionIndexTable) + '}'; } }
/** * <pre> * LineNumberTable_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 line_number_table_length; * { u2 start_pc; * u2 line_number; * } line_number_table[line_number_table_length]; * } * </pre> */ class LineNumberTable extends Info { class LineNumberInfo implements Parsable { int startPc; //u2 int lineNumber; //u2 public static final int size = 4; @Override public void parse(byte[] bytes, int offset) { startPc = Utils.read2Number(bytes, offset); offset += 2; lineNumber = Utils.read2Number(bytes, offset); } @Override public String toString() { return "LineNumberInfo{" + "startPc=" + startPc + ", lineNumber=" + lineNumber + '}'; } } int lineNumberTableLength; //u2 private LineNumberInfo[] mLineNumberInfos; @Override protected int contentSize() { return mLineNumberInfos.length * LineNumberInfo.size + 2; } @Override public void parseInner(byte[] bytes, int offset) { lineNumberTableLength = Utils.read2Number(bytes, offset); offset += 2; mLineNumberInfos = new LineNumberInfo[lineNumberTableLength]; for (int i = 0; i < lineNumberTableLength; i++) { mLineNumberInfos[i] = new LineNumberInfo(); mLineNumberInfos[i].parse(bytes, offset); offset += LineNumberInfo.size; } } @Override public String toString() { return "LineNumberTable{" + "lineNumberTableLength=" + lineNumberTableLength + ", mLineNumberInfos=" + Arrays.toString(mLineNumberInfos) + '}'; } }
/** * <pre> * LocalVariableTable_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 local_variable_table_length; * { u2 start_pc; * u2 length; * u2 name_index; * u2 descriptor_index; * u2 index; * } local_variable_table[local_variable_table_length]; * } * </pre> */ class LocalVariableTable extends Info { class LocalVairableTableItem implements Parsable { int startPc; int length; int nameIndex; int descriptorIndex; int index; public static final int size = 10; @Override public void parse(byte[] bytes, int offset) { startPc = Utils.read2Number(bytes, offset); length = Utils.read2Number(bytes, offset + 2); nameIndex = Utils.read2Number(bytes, offset + 2); descriptorIndex = Utils.read2Number(bytes, offset + 4); index = Utils.read2Number(bytes, offset + 6); } @Override public String toString() { return "LocalVairableTableItem{" + "startPc=" + startPc + ", length=" + length + ", nameIndex=" + nameIndex + ", descriptorIndex=" + descriptorIndex + ", index=" + index + '}'; } } int localVariableTableLength; //u2 private LocalVairableTableItem[] items; @Override protected int contentSize() { return localVariableTableLength * LocalVairableTableItem.size + 2; } @Override public void parseInner(byte[] bytes, int offset) { localVariableTableLength = Utils.read2Number(bytes, offset); offset += 2; items = new LocalVairableTableItem[localVariableTableLength]; for (int i = 0; i < localVariableTableLength; i++) { items[i] = new LocalVairableTableItem(); items[i].parse(bytes, offset); offset += LocalVairableTableItem.size; } } @Override public String toString() { return "LocalVariableTable{" + "localVariableTableLength=" + localVariableTableLength + ", items=" + Arrays.toString(items) + '}'; } }
/** * <pre> * SourceFile_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 sourcefile_index; * } * </pre> */ class SourceFile extends Info { int sourceFileIndex; //u2 @Override protected int contentSize() { return 2; } @Override public void parseInner(byte[] bytes, int offset) { sourceFileIndex = Utils.read2Number(bytes, offset); } @Override public String toString() { return "SourceFile{" + "sourceFileIndex=" + sourceFileIndex + " -> " + ((UTF8) BytecodeParser.constantItemHashMap.get(sourceFileIndex)).value + '}'; } }
/** * <pre> * InnerClasses_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 number_of_classes; * { u2 inner_class_info_index; * u2 outer_class_info_index; * u2 inner_name_index; * u2 inner_class_access_flags; * } classes[number_of_classes]; * } * </pre> */ class InnerClasses extends Info { class Classes implements Parsable { int innerClassInfoIndex, outerClassInfoIndex, innerNameInex, innerClassAccessFlags; //u2 public static final int size = 8; @Override public void parse(byte[] bytes, int offset) { innerClassInfoIndex = Utils.read2Number(bytes, offset); offset += 2; outerClassInfoIndex = Utils.read2Number(bytes, offset); offset += 2; innerNameInex = Utils.read2Number(bytes, offset); offset += 2; innerClassAccessFlags = Utils.read2Number(bytes, offset); } @Override public String toString() { return "Classes{" + "innerClassInfoIndex=" + innerClassInfoIndex + ", outerClassInfoIndex=" + outerClassInfoIndex + ", innerNameInex=" + innerNameInex + " -> " + ((UTF8) BytecodeParser.constantItemHashMap.get(innerNameInex)).value + ", innerClassAccessFlags=" + innerClassAccessFlags + " -> " + AccessFlags.printAccess(innerClassAccessFlags) + '}'; } } int numberOfClasses; //u2 private Classes[] mClasses; @Override protected int contentSize() { return numberOfClasses * Classes.size + 2; } @Override public void parseInner(byte[] bytes, int offset) { numberOfClasses = Utils.read2Number(bytes, offset); offset += 2; mClasses = new Classes[numberOfClasses]; for (int i = 0; i < numberOfClasses; i++) { mClasses[i] = new Classes(); mClasses[i].parse(bytes, offset); offset += Classes.size; } } @Override public String toString() { return "InnerClasses{" + "numberOfClasses=" + numberOfClasses + ", mClasses=" + Arrays.toString(mClasses) + '}'; } }
/** * <pre> * Signature_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 signature_index; * } * </pre> */ class Signature extends Info { int signatureIndex; //u2 @Override protected int contentSize() { return 2; } @Override public void parseInner(byte[] bytes, int offset) { signatureIndex = Utils.read2Number(bytes, offset); } @Override public String toString() { ConstantItem constantItem = BytecodeParser.constantItemHashMap.get(signatureIndex); String signature = constantItem.toString(); if (constantItem instanceof UTF8) { signature = ((UTF8) constantItem).value; } else if (constantItem instanceof METHOD_REF) { signature = constantItem.toString(); } else if (constantItem instanceof STRING) { signature = ((UTF8) BytecodeParser.constantItemHashMap.get(((STRING) constantItem).index)).value; } return "Signature{" + "signatureIndex=" + signatureIndex + " -> " + signature + '}'; } }
StackMapTable 用于优化类型检查效率的。https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4
/** * https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.7.4 * <pre> * StackMapTable_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 number_of_entries; * stack_map_frame entries[number_of_entries]; * } * * union stack_map_frame { * same_frame; * same_locals_1_stack_item_frame; * same_locals_1_stack_item_frame_extended; * chop_frame; * same_frame_extended; * append_frame; * full_frame; * } * </pre> */ class StackMapTable extends Info { class StackMapFrame { /** * <pre> * same_frame { * u1 frame_type = SAME; // 0-63 * } * </pre> */ class SameFrame { int frame_type; //u1 } /** * <pre> * same_locals_1_stack_item_frame { * u1 frame_type = SAME_LOCALS_1_STACK_ITEM; // 64-127 * verification_type_info stack[1]; * } * </pre> */ class SameLocals1StackItemFrame { } class SameLocals1StackItemFrameExtended { } class ChopFrame { } class SameFrameExtended { } class AppendFrame { } class FullFrame { } } int numberOfEntries; //u2 private StackMapFrame[] mStackMapFrames; @Override protected int contentSize() { return 0; } @Override public void parseInner(byte[] bytes, int offset) { } }
/** * <pre> * Synthetic_attribute { * u2 attribute_name_index; * u4 attribute_length; //always zero * } * </pre> */ class Synthetic extends Info { @Override protected int contentSize() { return 0; } @Override public void parseInner(byte[] bytes, int offset) { } }
/** * <pre> * Deprecated_attribute { * u2 attribute_name_index; * u4 attribute_length; * } * </pre> */ class Deprecated extends Info { @Override protected int contentSize() { return 0; } @Override public void parseInner(byte[] bytes, int offset) { } }
局部变量类型属性
/** * <pre> * LocalVariableTypeTable_attribute { * u2 attribute_name_index; * u4 attribute_length; * u2 local_variable_type_table_length; * { u2 start_pc; * u2 length; * u2 name_index; * u2 signature_index; * u2 index; * } local_variable_type_table[local_variable_type_table_length]; * } * </pre> */ class LocalVariableTypeTable extends Info { class Local_variable_type_table implements Parsable { int start_pc; int length; int name_index; int signature_index; int index; public static final int size = 10; @Override public void parse(byte[] bytes, int offset) { start_pc = Utils.read2Number(bytes, offset); length = Utils.read2Number(bytes, offset + 2); name_index = Utils.read2Number(bytes, offset + 4); signature_index = Utils.read2Number(bytes, offset + 6); index = Utils.read2Number(bytes, offset + 8); } @Override public String toString() { return "Local_variable_type_table{" + "start_pc=" + start_pc + ", length=" + length + ", name_index=" + name_index + ", signature_index=" + signature_index + ", index=" + index + '}'; } } int lvtt_length; //u2 Local_variable_type_table[] mTables; @Override protected int contentSize() { return lvtt_length * Local_variable_type_table.size + 2; } @Override public void parseInner(byte[] bytes, int offset) { lvtt_length = Utils.read2Number(bytes, offset); offset += 2; mTables = new Local_variable_type_table[lvtt_length]; for (int i = 0; i < lvtt_length; i++) { mTables[i] = new Local_variable_type_table(); mTables[i].parse(bytes, offset); offset += Local_variable_type_table.size; } } @Override public String toString() { return "LocalVariableTypeTable{" + "lvtt_length=" + lvtt_length + ", mTables=" + Arrays.toString(mTables) + '}'; } } //endregion
由于 Java 的所有数字类型都是 signed 类型,就会导致 unsigned 的数到了 Java 中可能越界溢出。所以统一使用 Java 的 int 代表 unsigned byte。
class Utils { /** * 获取占4字节的数 */ static int read4Number(byte[] bytes, int offset) { return ((bytes[offset] & 0xFF) << 24) | ((bytes[offset + 1] & 0xFF) << 16) | ((bytes[offset + 2] & 0xFF) << 8) | ((bytes[offset + 3] & 0xFF)); } /** * 获取占2字节的数, 为了避免 java 中只有 signed short 越界出现显示了负数, 所以返回 int */ static int read2Number(byte[] bytes, int offset) { return ((bytes[offset] & 0xFF) << 8) | (bytes[offset + 1] & 0xFF); } static int readUnsignedByte(byte[] bytes, int offset) { return (bytes[offset] & 0xFF); } /** * 获得占8字节的数 */ static long read8Number(byte[] bytes, int offset) { return ( ((long) bytes[offset] & 0xFF) << 56) | (((long) bytes[offset + 1] & 0xFF) << 48) | (((long) bytes[offset + 2] & 0xFF) << 40) | (((long) bytes[offset + 3] & 0xFF) << 32) | (((long) bytes[offset + 3] & 0xFF) << 24) | (((long) bytes[offset + 3] & 0xFF) << 16) | (((long) bytes[offset + 3] & 0xFF) << 8) | (((long) bytes[offset + 3] & 0xFF)); } }