如何用Java实现JVM中的解析class文件?

Java
225
0
0
2023-11-04
标签   JVM

案例介绍

本案例主要介绍通过java代码从class文件中解析;class文件、常量池、属性表;

环境准备

  1. jdk 1.8.0
  2. IntelliJ IDEA Community Edition 2018.3.1 x64

配置信息

  1. 调试配置配置位置:Run/Debug Configurations -> program arguments配置内容:-X jre “C:Program FilesJavajdk1.8.0_161jre” java. lang .String

代码示例

 itstack-demo- jvm -03
├── 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
    │             │   ├── Class reader .java
    │             │   └── MemberInfo.java
    │             ├──  classpath 
    │             │   ├── impl
    │             │   │   ├── CompositeEntry.java
    │             │   │   ├── DirEntry.java 
    │             │   │   ├── WildcardEntry.java 
    │             │   │   └── ZipEntry.java    
    │             │   ├── Classpath.java
    │             │   └── Entry.java    
    │             ├── Cmd.java
    │             └── Main.java
    └── test
         └── java
             └── org.itstack.demo.test
                 └── HelloWorld.java
AttributeInfo.java
 package org.itstack.demo.jvm.classfile.attributes;

import org.itstack.demo.jvm.classfile.ClassReader;
import org.itstack.demo.jvm.classfile.attributes.impl.*;
import org.itstack.demo.jvm.classfile.constantpool.ConstantPool;

/**
 * 
 * create by fuzhengwei on/4/26
 */public interface AttributeInfo {

    void readInfo(ClassReader reader);

    static AttributeInfo[] readAttributes(ClassReader reader, ConstantPool constantPool) {
        int attributesCount = reader.readUToInt();
        AttributeInfo[] attributes = new AttributeInfo[attributesCount];
        for (int i =; i < attributesCount; i++) {
            attributes[i] = readAttribute(reader, constantPool);
        }
        return attributes;
    }

    static AttributeInfo readAttribute(ClassReader reader, ConstantPool constantPool) {
        int attrNameIdx = reader.readUToInt();
        String attrName = constantPool.getUTF(attrNameIdx);
        int attrLen = reader.readUToInt();
        AttributeInfo attrInfo = newAttributeInfo(attrName, attrLen, constantPool);
        attrInfo.readInfo(reader);
        return attrInfo;
    }

    static AttributeInfo newAttributeInfo(String attrName, int attrLen, ConstantPool constantPool) {
        switch (attrName) {
            case "BootstrapMethods":
                return new BootstrapMethodsAttribute();
            case "Code":
                return new CodeAttribute(constantPool);
            case "ConstantValue":
                return new ConstantValueAttribute();
            case "Deprecated":
                return new DeprecatedAttribute();
            case "EnclosingMethod":
                return new EnclosingMethodAttribute(constantPool);
            case "Exceptions":
                return new ExceptionsAttribute();
            case "InnerClasses":
                return new InnerClassesAttribute();
            case "LineNumberTable":
                return new LineNumberTableAttribute();
            case "LocalVariableTable":
                return new LocalVariableTableAttribute();
            case "LocalVariableTypeTable":
                return new LocalVariableTypeTableAttribute();
            // case "MethodParameters":
            // case "RuntimeInvisibleAnnotations":
            // case "RuntimeInvisibleParameterAnnotations":
            // case "RuntimeInvisibleTypeAnnotations":
            // case "RuntimeVisibleAnnotations":
            // case "RuntimeVisibleParameterAnnotations":
            // case "RuntimeVisibleTypeAnnotations":
            case "Signature":
                return new SignatureAttribute(constantPool);
            case "SourceFile":
                return new SourceFileAttribute(constantPool);
            // case "SourceDebugExtension":
            // case "StackMapTable":
            case "Synthetic":
                return new SyntheticAttribute();
            default:
                return new UnparsedAttribute(attrName, attrLen);
        }

    }

}
ConstantInfo.java
 package org.itstack.demo.jvm.classfile.constantpool;

import org.itstack.demo.jvm.classfile.ClassReader;
import org.itstack.demo.jvm.classfile.constantpool.impl.*;

/**
 * 
 * create by fuzhengwei on/4/26
 */public interface ConstantInfo {

    int CONSTANT_TAG_CLASS =;
    int CONSTANT_TAG_FIELDREF =;
    int CONSTANT_TAG_METHODREF =;
    int CONSTANT_TAG_INTERFACEMETHODREF =;
    int CONSTANT_TAG_STRING =;
    int CONSTANT_TAG_INTEGER =;
    int CONSTANT_TAG_FLOAT =;
    int CONSTANT_TAG_LONG =;
    int CONSTANT_TAG_DOUBLE =;
    int CONSTANT_TAG_NAMEANDTYPE =;
    int CONSTANT_TAG_UTF = 1;
    int CONSTANT_TAG_METHODHANDLE =;
    int CONSTANT_TAG_METHODTYPE =;
    int CONSTANT_TAG_INVOKEDYNAMIC =;

    void readInfo(ClassReader reader);

    int tag();
    
    static ConstantInfo readConstantInfo(ClassReader reader, ConstantPool constantPool) {
        int tag = reader.readUToInt();
        ConstantInfo constantInfo = newConstantInfo(tag, constantPool);
        constantInfo.readInfo(reader);
        return constantInfo;
    }

    static ConstantInfo newConstantInfo(int tag, ConstantPool constantPool) {
        switch (tag) {
            case CONSTANT_TAG_INTEGER:
                return new ConstantIntegerInfo();
            case CONSTANT_TAG_FLOAT:
                return new ConstantFloatInfo();
            case CONSTANT_TAG_LONG:
                return new ConstantLongInfo();
            case CONSTANT_TAG_DOUBLE:
                return new ConstantDoubleInfo();
            case CONSTANT_TAG_UTF:
                return new ConstantUtfInfo();
            case CONSTANT_TAG_STRING:
                return new ConstantStringInfo(constantPool);
            case CONSTANT_TAG_CLASS:
                return new ConstantClassInfo(constantPool);
            case CONSTANT_TAG_FIELDREF:
                return new ConstantFieldRefInfo(constantPool);
            case CONSTANT_TAG_METHODREF:
                return new ConstantMethodRefInfo(constantPool);
            case CONSTANT_TAG_INTERFACEMETHODREF:
                return new ConstantInterfaceMethodRefInfo(constantPool);
            case CONSTANT_TAG_NAMEANDTYPE:
                return new ConstantNameAndTypeInfo();
            case CONSTANT_TAG_METHODTYPE:
                return new ConstantMethodTypeInfo();
            case CONSTANT_TAG_METHODHANDLE:
                return new ConstantMethodHandleInfo();
            case CONSTANT_TAG_INVOKEDYNAMIC:
                return new ConstantInvokeDynamicInfo();
            default:
                throw new ClassFormatError("constant pool tag");
        }
    }
}
ClassFile.java
 package org.itstack.demo.jvm.classfile;

import org.itstack.demo.jvm.classfile.attributes.AttributeInfo;
import org.itstack.demo.jvm.classfile.constantpool.ConstantPool;

/**
 * 
 * create by fuzhengwei on/4/26
 */public class ClassFile {

    private int minorVersion;
    private int majorVersion;
    private ConstantPool constantPool;
    private int accessFlags;
    private int thisClassIdx;
    private int supperClassIdx;
    private int[] interfaces;
    private MemberInfo[] fields;
    private MemberInfo[] methods;
    private AttributeInfo[] attributes;

    public ClassFile(byte[] classData) {
        ClassReader reader = new ClassReader(classData);
        this.readAndCheckMagic(reader);
        this.readAndCheckVersion(reader);
        this.constantPool = this.readConstantPool(reader);
        this.accessFlags = reader.readUToInt();
        this.thisClassIdx = reader.readUToInt();
        this.supperClassIdx = reader.readUToInt();
        this.interfaces = reader.readUInts();
        this.fields = MemberInfo.readMembers(reader, constantPool);
        this.methods = MemberInfo.readMembers(reader, constantPool);
        this.attributes = AttributeInfo.readAttributes(reader, constantPool);
    }

    private void readAndCheckMagic(ClassReader reader) {
        String magic = reader.readUToHexStr();
        if (!"cafebabe".equals(magic)) {
            throw new ClassFormatError("magic!");
        }
    }

    private void readAndCheckVersion(ClassReader reader) {
        this.minorVersion = reader.readUToInt();
        this.majorVersion = reader.readUToInt();
        switch (this.majorVersion) {
            case:
                return;
            case:
            case:
            case:
            case:
            case:
            case:
            case:
                if (this.minorVersion ==)
                    return;
        }
        throw new UnsupportedClassVersionError();
    }

    private ConstantPool readConstantPool(ClassReader reader) {
        return new ConstantPool(reader);
    }

    public int minorVersion(){
        return this.minorVersion;
    }

    public int majorVersion(){
        return this.majorVersion;
    }

    public ConstantPool constantPool(){
        return this.constantPool;
    }

    public int accessFlags() {
        return this.accessFlags;
    }

    public MemberInfo[] fields() {
        return this.fields;
    }

    public MemberInfo[] methods() {
        return this.methods;
    }

    public String className() {
        return this.constantPool.getClassName(this.thisClassIdx);
    }

    public String superClassName() {
        if (this.supperClassIdx <=) return "";
        return this.constantPool.getClassName(this.supperClassIdx);
    }

    public String[] interfaceNames() {
        String[] interfaceNames = new String[this.interfaces.length];
        for (int i =; i < this.interfaces.length; i++) {
            interfaceNames[i] = this.constantPool.getClassName(interfaces[i]);
        }
        return interfaceNames;
    }

}
ClassReader.java
 package org.itstack.demo.jvm.classfile;

import java.math.BigInteger;

/**
 * 
 * create by fuzhengwei on/5/13
 * <p>
 * java虚拟机定义了u、u2、u4三种数据类型来表示;1字节、2字节、4字节,无符号整数。
 * 在如下实现中,用增位方式表示无符号类型:
 * u、u2可以用int类型存储,因为int类型是4字节
 * u 需要用long类型存储,因为long类型是8字节
 */public class ClassReader {

    private byte[] data;

    public ClassReader(byte[] data) {
        this.data = data;
    }

    //u
    public int readUint() {
        byte[] val = readByte();
        return byteint(val);
    }

    //u
    public int readUint() {
        byte[] val = readByte();
        return byteint(val);
    }

    //u
    public long readUint() {
        byte[] val = readByte();
        String str_hex = new BigInteger(, val). toString (16);
        return Long.parseLong(str_hex,);
    }

    public float readUintTFloat() {
        byte[] val = readByte();
        return new BigInteger(, val).floatValue();
    }

    public long readUintTLong() {
        byte[] val = readByte();
        return new BigInteger(, val).longValue();
    }

    public double readUintTDouble() {
        byte[] val = readByte();
        return new BigInteger(, val).doubleValue();
    }

    public int[] readUints() {
        int n = this.readUint();
        int[] s = new int[n];
        for (int i =; i < n; i++) {
            s[i] = this.readUint();
        }
        return s;
    }

    public byte[] readBytes(int n) {
        return readByte(n);
    }

    private byte[] readByte(int length) {
        byte[] copy = new byte[length];
        System.arraycopy(data,, copy, 0, length);
        System.arraycopy(data, length, data,, data.length - length);
        return copy;
    }

    private int byteint(byte[] val) {
        String str_hex = new BigInteger(, val).toString(16);
        return Integer.parseInt(str_hex,);
    }

}
MemberInfo.java
 package org.itstack.demo.jvm.classfile;

import org.itstack.demo.jvm.classfile.attributes.AttributeInfo;
import org.itstack.demo.jvm.classfile.attributes.impl.CodeAttribute;
import org.itstack.demo.jvm.classfile.attributes.impl.ConstantValueAttribute;
import org.itstack.demo.jvm.classfile.constantpool.ConstantPool;

/**
 * 
 * create by fuzhengwei on/4/26
 */public class MemberInfo {

    private ConstantPool constantPool;
    private int accessFlags;
    private int nameIdx;
    private int descriptorIdx;
    private AttributeInfo[] attributes;

    public MemberInfo(ClassReader reader, ConstantPool constantPool) {
        this.constantPool = constantPool;
        this.accessFlags = reader.readUToInt();
        this.nameIdx = reader.readUToInt();
        this.descriptorIdx = reader.readUToInt();
        this.attributes = AttributeInfo.readAttributes(reader, constantPool);
    }

    public static MemberInfo[] readMembers(ClassReader reader, ConstantPool constantPool) {
        int fieldCount = reader.readUToInt();
        MemberInfo[] fields = new MemberInfo[fieldCount];
        for (int i =; i < fieldCount; i++) {
            fields[i] = new MemberInfo(reader, constantPool);
        }
        return fields;
    }

    public int accessFlags() {
        return this.accessFlags;
    }

    public String name() {
        return this.constantPool.getUTF(this.nameIdx);
    }

    public String descriptor() {
        return this.constantPool.getUTF(this.descriptorIdx);
    }

    public CodeAttribute codeAttribute() {
        for (AttributeInfo attrInfo : attributes) {
            if (attrInfo instanceof CodeAttribute) return (CodeAttribute) attrInfo;
        }
        return null;
    }

    public ConstantValueAttribute ConstantValueAttribute() {
        for (AttributeInfo attrInfo : attributes) {
            if (attrInfo instanceof ConstantValueAttribute) return (ConstantValueAttribute) attrInfo;
        }
        return null;
    }

}
Main.java
 package org.itstack.demo.jvm;

import org.itstack.demo.jvm.classfile.ClassFile;
import org.itstack.demo.jvm.classfile.MemberInfo;
import org.itstack.demo.jvm.classpath.Classpath;

import java.util.Arrays;

/**
 * 
 * create by fuzhengwei on/4/24
 */public class Main {

    public static void main(String[] args) {
        Cmd cmd = Cmd.parse(args);
        if (!cmd.ok || cmd.helpFlag) {
            System.out.println("Usage: <main class> [-options] class [args...]");
            return;
        }
        if (cmd.versionFlag) {
            //注意案例测试都是基于.8,另外jdk1.9以后使用模块化没有rt.jar
            System.out.println("java version ".8.0"");
            return;
        }
        startJVM(cmd);
    }

    private static void startJVM(Cmd cmd) {
        Classpath classpath = new Classpath(cmd.jre, cmd.classpath);
        System.out.printf("classpath:%s class:%s args:%sn",
                classpath, cmd.getMainClass(), cmd.getAppArgs());
        //获取className
        String className = cmd.getMainClass().replace(".", "/");
        ClassFile classFile = loadClass(className, classpath);
        assert classFile != null;
        printClassInfo(classFile);
    }

    private static ClassFile loadClass(String className, Classpath classpath) {
        try {
            byte[] classData = classpath.readClass(className);
            return new ClassFile(classData);
        } catch (Exception e) {
            System.out.println("Could not find or load main class " + className);
            return null;
        }
    }

    private static void printClassInfo(ClassFile cf) {
        System.out.println("version: " + cf.majorVersion() + "." + cf.minorVersion());
        System.out.println("constants count:" + cf.constantPool().getSiz());
        System.out.format("access flags:x%xn", cf.accessFlags());
        System.out.println("this class:" + cf.className());
        System.out.println("super class:" + cf.superClassName());
        System.out.println("interfaces:" + Arrays.toString(cf.interfaceNames()));
        System.out.println("fields count:" + cf.fields().length);
        for (MemberInfo memberInfo : cf.fields()) {
            System.out.format("  %sn", memberInfo.name());
        }
        System.out.println("methods count: " + cf.methods().length);
        for (MemberInfo memberInfo : cf.methods()) {
            System.out.format("  %sn", memberInfo.name());
        }
    }

}

测试结果

 "C:Program FilesJavajdk.8.0_161binjava.exe" "-javaagent:D:Program FilesJetBrainsIntelliJ IDEA Community Edition 2018.3.1libidea_rt.jar=61458:D:Program FilesJetBrainsIntelliJ IDEA Community Edition 2018.3.1bin" -Dfile.encoding=UTF-8 -classpath "C:Program FilesJavajdk1.8.0_161jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_161jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_161jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_161jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_161jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_161jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_161jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_161jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_161jrelibextnashorn.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_161jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_161jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_161jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_161jrelibjce.jar;C:Program FilesJavajdk1.8.0_161jrelibjfr.jar;C:Program FilesJavajdk1.8.0_161jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_161jrelibjsse.jar;C:Program FilesJavajdk1.8.0_161jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_161jrelibplugin.jar;C:Program FilesJavajdk1.8.0_161jrelibresources.jar;C:Program FilesJavajdk1.8.0_161jrelibrt.jar;E:itstackgitistack-demoitstack-demo-jvmitstack-demo-jvm-03targetclasses;D:Program Files (x86)apache-maven-2.2.1repositorycombeustjcommander1.72jcommander-1.72.jar;D:Program Files (x86)apache-maven-2.2.1repositoryorgprojectlomboklombok1.18.0lombok-1.18.0.jar;D:Program Files (x86)apache-maven-2.2.1repositorycomalibabafastjson1.2.40fastjson-1.2.40.jar" org.itstack.demo.jvm.Main -Xjre "C:Program FilesJavajdk1.8.0_161jre" java.lang.String
classpath:org.itstack.demo.jvm.classpath.Classpath@bf558aa class:java.lang.String args:null
version:.0
constants count:
access flags:x31
this class:java/lang/String
super class:java/lang/ Object 
interfaces:[java/io/Serializable, java/lang/Comparable, java/lang/CharSequence]
fields count:
value  [C
hash  I
serialVersionUID  J
serialPersistentFields  [Ljava/io/ObjectStreamField;
CASE_INSENSITIVE_ORDER  Ljava/util/Comparator;
methods count:
<init>  ()V
<init>  (Ljava/lang/String;)V
<init>  ([C)V
<init>  ([CII)V
<init>  ([III)V
<init>  ([BIII)V
<init>  ([BI)V
checkBounds  ([BII)V
<init>  ([BIILjava/lang/String;)V
<init>  ([BIILjava/nio/charset/Charset;)V
<init>  ([BLjava/lang/String;)V
<init>  ([BLjava/nio/charset/Charset;)V
<init>  ([BII)V
<init>  ([B)V
<init>  (Ljava/lang/StringBuffer;)V
<init>  (Ljava/lang/StringBuilder;)V
<init>  ([CZ)V
length  ()I
isEmpty  ()Z
charAt  (I)C
codePointAt  (I)I
codePointBefore  (I)I
codePointCount  (II)I
offsetByCodePoints  (II)I
getChars  ([CI)V
getChars  (II[CI)V
getBytes  (II[BI)V
getBytes  (Ljava/lang/String;)[B
getBytes  (Ljava/nio/charset/Charset;)[B
getBytes  ()[B
equals  (Ljava/lang/Object;)Z
contentEquals  (Ljava/lang/StringBuffer;)Z
nonSyncContentEquals  (Ljava/lang/AbstractStringBuilder;)Z
contentEquals  (Ljava/lang/CharSequence;)Z
equalsIgnoreCase  (Ljava/lang/String;)Z
compareTo  (Ljava/lang/String;)I
compareToIgnoreCase  (Ljava/lang/String;)I
regionMatches  (ILjava/lang/String;II)Z
regionMatches  (ZILjava/lang/String;II)Z
startsWith  (Ljava/lang/String;I)Z
startsWith  (Ljava/lang/String;)Z
endsWith  (Ljava/lang/String;)Z
hashCode  ()I
indexOf  (I)I
indexOf  (II)I
indexOfSupplementary  (II)I
lastIndexOf  (I)I
lastIndexOf  (II)I
lastIndexOfSupplementary  (II)I
indexOf  (Ljava/lang/String;)I
indexOf  (Ljava/lang/String;I)I
indexOf  ([CIILjava/lang/String;I)I
indexOf  ([CII[CIII)I
lastIndexOf  (Ljava/lang/String;)I
lastIndexOf  (Ljava/lang/String;I)I
lastIndexOf  ([CIILjava/lang/String;I)I
lastIndexOf  ([CII[CIII)I
substring  (I)Ljava/lang/String;
substring  (II)Ljava/lang/String;
subSequence  (II)Ljava/lang/CharSequence;
concat  (Ljava/lang/String;)Ljava/lang/String;
replace  (CC)Ljava/lang/String;
matches  (Ljava/lang/String;)Z
contains  (Ljava/lang/CharSequence;)Z
replaceFirst  (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
replaceAll  (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
replace  (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String;
split  (Ljava/lang/String;I)[Ljava/lang/String;
split  (Ljava/lang/String;)[Ljava/lang/String;
join  (Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String;
join  (Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String;
toLowerCase  (Ljava/util/Locale;)Ljava/lang/String;
toLowerCase  ()Ljava/lang/String;
toUpperCase  (Ljava/util/Locale;)Ljava/lang/String;
toUpperCase  ()Ljava/lang/String;
trim  ()Ljava/lang/String;
toString  ()Ljava/lang/String;
toCharArray  ()[C
format  (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
format  (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
valueOf  (Ljava/lang/Object;)Ljava/lang/String;
valueOf  ([C)Ljava/lang/String;
valueOf  ([CII)Ljava/lang/String;
copyValueOf  ([CII)Ljava/lang/String;
copyValueOf  ([C)Ljava/lang/String;
valueOf  (Z)Ljava/lang/String;
valueOf  (C)Ljava/lang/String;
valueOf  (I)Ljava/lang/String;
valueOf  (J)Ljava/lang/String;
valueOf  (F)Ljava/lang/String;
valueOf  (D)Ljava/lang/String;
intern  ()Ljava/lang/String;
compareTo  (Ljava/lang/Object;)I
<clinit>  ()V

Process finished with exit code

今天的分享就到这了。