【JVM学习】Class文件解析

这篇具有很好参考价值的文章主要介绍了【JVM学习】Class文件解析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

最近为了加深对于JAVA的理解,在复习JVM,这里面的内容大部分都是比较确定的、文档性质的内容,目前并没有特别的总结。但是看到类文件结构,手痒,想写了个解析文件的解析器,那就简单记录下吧。

JAVA类文件结构

详细内容不记了,太多了,有兴趣的同学看虚拟机相关书籍了解,这里只简单介绍下。
1 类文件使用类似结构体的伪结构,基本数据类型有两种:无符号数和表。无符号数有1、2、4、8四种长度;表可以由无符号数或其它复合类型作为元素,组成数据列表。
2 复合类型也只由上述两种基本类型组成。
3 类文件按固定结构存储,表类型例外,因为里面的元素可以是不同的复合类型,因此排列顺序不一定是固定顺序。
4 常量池。类名、函数名、字符串等信息,类文件的解析最终都要在常量池中找到对应的索引。不同的常量结构体用tag(数字)区分。
5 属性表。不管是field、method还是类本身,都有属性表,不同的属性结构体用属性名称(字符串)区分。

设计思路

做过协议解析的同学肯定了解,类似这种情况,一般都会使用TLV(type、length、value)结构解析,知道类型,才知道长度,知道长度才能完整读出该类型对应的数据。无论是协议,还是class、elf文件,这种方式都是常规操作。使用这种方式,除了定义完整的类型外,还要在解析中处理不同的类型。
那这次用的是JAVA,想换一个不一样的思路,即使用泛型和反射,类型的定义不可避免,那么是否可以减少解析过程中对于类型的处理?
基于这个想法,简单的按照类文件结构进行了相关结构体的定义,并实现了一版,直接上代码吧,我个人觉得基本达到了目的, 不算打印方法,250行左右,解析逻辑比较简单。

public class Parser {
    public Parser() {
        initConstantPoolMap();
        initAttributeMap();
    }

    /**
     * 复合类型解析
     * @param obj 待解析填充的对象
     * @param inputStream 文件流
     */
    public void parse(Object obj, DataInputStream inputStream) throws ClassNotFoundException, IllegalAccessException, IOException, InstantiationException {
        Field[] fields = obj.getClass().getDeclaredFields();

        for (Field field : fields) {
            System.out.println(field.getName() + " : " + field.getGenericType());

            field.setAccessible(true);
            Type type = field.getGenericType();
            Class fieldClass = null;
            Class[] actualClassArguments = new Class[2];
            if (type instanceof Class) {
                fieldClass = (Class) type;
            } else if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                fieldClass = (Class) parameterizedType.getRawType();
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                actualClassArguments = new Class[actualTypeArguments.length];
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    actualClassArguments[i] = (Class) actualTypeArguments[i];
                }
            } else {
                throw new InstantiationException("illegal field type: " + field.getGenericType());
            }
            if (fieldClass.getSuperclass().equals(AtomicType.class)) {
                AtomicType o = parseAtomic(fieldClass, inputStream);
                field.set(obj, o);
            } else if (fieldClass.equals(Table.class)) {
                Table table = (Table) field.get(obj);
                table.setCountClass(actualClassArguments[0]);
                table.setElementClass(actualClassArguments[1]);
                parseTable(table, inputStream);
            } else {
                field.set(obj, parseMeta(fieldClass, inputStream));
            }
        }
    }

    /**
     * 原子数据类型解析(类文件定义的四种无符号数据类型)
     * @param cl 带解析的类信息
     * @param inputStream 文件流
     * @return 解析后实例的的对象
     */
    private AtomicType parseAtomic(Class<?> cl, DataInputStream inputStream) throws IllegalAccessException, InstantiationException, IOException {
        AtomicType obj = (AtomicType) cl.newInstance();
        inputStream.read(obj.getBytes());
        System.out.println(cl.getTypeName() + " parsed: " + obj.getValue());
        return obj;
    }

    /**
     * 通用表结构解析
     * @param table 表对象
     * @param inputStream 文件流
     */
    private void parseTable(Table table, DataInputStream inputStream) throws IllegalAccessException, InstantiationException, ClassNotFoundException, IOException {
        AtomicType count = parseAtomic(table.getCountClass(), inputStream);
        table.setCount(count);
        System.out.println("table count: " + count.getValue());
        if (count.getIntValue() == 0) {
            return;
        } else if (table.getElementClass().getSuperclass().equals(Number.class)) {
            parseNumber(table, inputStream);
        }  else if (table.getElementClass().getTypeName().equals(cp_info.class.getTypeName())) {
            parseConstantPool(table, inputStream);
        } else if (table.getElementClass().getTypeName().equals(attribute_info.class.getTypeName())) {
            parseAttribute(table, inputStream);
        } else {
            parseMetas(table, inputStream);
        }
    }

    /**
     * 其它复合类型的表结构解析
     * @param table 表对象
     * @param inputStream 文件流
     */
    private void parseMetas(Table table, DataInputStream inputStream) throws ClassNotFoundException, IllegalAccessException, InstantiationException, IOException {
        for (int i = 0; i < table.getCount().getIntValue(); i++) {
            table.getData().add(parseMeta(table.getElementClass(), inputStream));
        }
    }

    /**
     * 复合类型解析(根据类型实例化后填充)
     * @param cl 待解析类信息
     * @param inputStream 文件流
     * @return 解析后的实例
     */
    private Object parseMeta(Class cl, DataInputStream inputStream) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
        Object o = cl.newInstance();
        parse(o, inputStream);
        return o;
    }

    /**
     * 解析原子数据类型(非class文件定义的无符号数)
     * @param table 表对象
     * @param inputStream 文件流
     */
    private void parseNumber(Table table, DataInputStream inputStream) throws IOException, InstantiationException {
        for (int i = 0; i < table.getCount().getIntValue(); i++) {
            if (table.getElementClass().equals(Byte.class)) {
                table.getData().add(inputStream.readByte());
            } else if (table.getElementClass().equals(Short.class)) {
                table.getData().add(inputStream.readShort());
            } else {
                throw new InstantiationException("illegal number class : " + table.getElementClass());
            }
        }
    }

    /**
     * 解析常量池
     * @param table 表对象
     * @param inputStream 文件流
     */
    private void parseConstantPool(Table table, DataInputStream inputStream) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
        for (int i = 0; i < table.getCount().getIntValue() - 1; i++) {
            table.getData().add(parseConstantPool(inputStream));
        }
    }

    /**
     * 解析单个常量对象
     * @param inputStream 表对象
     * @return 常量对象
     */
    private Object parseConstantPool(DataInputStream inputStream) throws IllegalAccessException, IOException, InstantiationException, ClassNotFoundException {
        AtomicType tag = parseAtomic(u1.class, inputStream);

        Class cl = constPoolMap.get(tag.getIntValue());
        if (cl == null) {
            throw new InstantiationException("not found const pool class: " + tag.getValue());
        }
        Object obj = cl.newInstance();
        parse(obj, inputStream);
        System.out.println("parse constant pool: tag = " + tag.getValue());
        return obj;
    }

    /**
     * 解析属性表
     * @param table 表对象
     * @param inputStream 文件流
     */
    private void parseAttribute(Table table, DataInputStream inputStream) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
        for (int i = 0; i < table.getCount().getIntValue(); i++) {
            table.getData().add(parseAttribute(inputStream));
        }
    }

    /**
     * 解析单个属性
     * @param inputStream 文件流
     * @return 单个属性对象
     */
    private Object parseAttribute(DataInputStream inputStream) throws ClassNotFoundException, IOException, InstantiationException, IllegalAccessException {
        attribute_info attr = new attribute_info();
        parse(attr, inputStream);
        String name = getUtf8((int) attr.getAttribute_name_index().getValue());
        if (name == null) {
            throw new InstantiationException("not found attribute name: " + attr.getAttribute_name_index().getValue());
        }
        System.out.println("parse attribute: " + name);

        Class cl = attributeMap.get(name);
        if (cl == null) {
            throw new InstantiationException("not found attribute: " + name);
        }

        return parseMeta(cl, inputStream);
    }

    /**
     * 获取常量池中的对象
     * @param index 常量池索引
     * @return 常量对象
     */
    private cp_info getConstant(int index) {
        if (index == 0 || index >= classInfo.getConstant_pool().getData().size()) {
            return null;
        }
        return classInfo.getConstant_pool().getData().get(index - 1);
    }

    /**
     * 获取字符串对象
     * @param index 索引
     * @return 字符串
     */
    private String getUtf8(int index) {
        cp_info c = getConstant(index);
        if (c == null) {
            return null;
        }
        if (!(c instanceof utf8_info)) {
            System.out.println("not utf8 constant");
            return null;
        }
        utf8_info utf8 = (utf8_info) c;
        return utf8.toString();
    }

    // 类文件对象
    private static class_info classInfo = new class_info();
    // 常量池类型映射表
    private static Map<Integer, Class> constPoolMap = new HashMap<>();
    // 属性映射表
    private static Map<String, Class> attributeMap = new HashMap<>();

    private static void initConstantPoolMap() {
        constPoolMap.put(1, utf8_info.class);
        constPoolMap.put(3, integer_info.class);
        constPoolMap.put(4, float_info.class);
        constPoolMap.put(5, long_info.class);
        constPoolMap.put(6, double_info.class);
        constPoolMap.put(7, constant_class_info.class);
        constPoolMap.put(8, string_info.class);
        constPoolMap.put(9, fieldref_info.class);
        constPoolMap.put(10, methodref_info.class);
        constPoolMap.put(11, interfacemethodref_info.class);
        constPoolMap.put(12, nametype_info.class);
    }

    private static void initAttributeMap() {
        attributeMap.put("Code", code_info.class);
        attributeMap.put("Exceptions", throws_info.class);
        attributeMap.put("LineNumberTable", LineNumberTable.class);
        attributeMap.put("LocalVariableTable", LocalVariableTable.class);
        attributeMap.put("SourceFile", SourceFile.class);
        attributeMap.put("ConstantValue", ConstantValue.class);
        attributeMap.put("InnerClasses", InnerClasses.class);
    }

    private static void print(Class cl, Object obj, String prefix, StringBuilder sb) throws IllegalAccessException, ClassNotFoundException, InstantiationException {
        Field[] fields = cl.getDeclaredFields();

        sb.append(prefix).append(cl.getSimpleName()).append(":{\n");
        for (Field field : fields) {
            field.setAccessible(true);
            Type type = field.getGenericType();
            Class fieldClass = null;
            Class[] actualClassArguments = new Class[2];
            if (type instanceof Class) {
                fieldClass = (Class) type;
            } else if (type instanceof ParameterizedType) {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                fieldClass = (Class) parameterizedType.getRawType();
                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                actualClassArguments = new Class[actualTypeArguments.length];
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    actualClassArguments[i] = (Class) actualTypeArguments[i];
                }
            } else {
                throw new InstantiationException("illegal field type: " + field.getGenericType());
            }

            Object o = field.get(obj);
            if (fieldClass.getSuperclass().equals(AtomicType.class) && field.getName().endsWith("_index")) {
                AtomicType v = (AtomicType) o;
                sb.append(prefix).append(field.getName() + ": #" + v.getValue() + " : ");
                printConstant(v.getIntValue(), sb);
                sb.append("\n");
            } else if (fieldClass.getSuperclass().equals(AtomicType.class)) {
                AtomicType v = (AtomicType) o;
                sb.append(prefix + field.getName() + " : " + v.getValue()).append("\n");
            } else if (fieldClass.equals(Table.class)) {
                Table table = (Table) o;

                sb.append(prefix).append(field.getName() + ": ").append("[\n");
                for (int i = 0; i < table.getData().size(); i++) {
                    Class fcl = table.getData().get(i).getClass();
                    if (table.getElementClass().equals(cp_info.class)) {
                        sb.append(prefix).append("    ").append("#" + (i + 1) + " = " + table.getData().get(i).toString()).append("\n");
                    } else if (fcl.equals(Byte.class) || fcl.equals(Short.class)) {
                        sb.append(table.getData().get(i));
                    } else {
                        print(fcl, table.getData().get(i), prefix + "   ", sb);
                    }
                }
                if (table.getCount().getIntValue() > 0 &&
                        (table.getElementClass().equals(Byte.class) || table.getElementClass().equals(Short.class))) {
                    sb.append("\n");
                }
                sb.append(prefix).append("]\n");
            } else {
                sb.append(prefix).append(field.getName() + ": ").append("\n");
                Class fcl = Class.forName(field.getGenericType().getTypeName());
                print(fcl, o, prefix + "    ", sb);
            }
        }
        sb.append(prefix).append("}\n");
    }

    private static void printConstant(int index, StringBuilder sb) throws IllegalAccessException {
        if (index == 0) {
            return;
        }
        cp_info info = classInfo.getConstant_pool().getData().get(index - 1);
        if (info instanceof utf8_info) {
            sb.append(info.toString());
            return;
        }
        sb.append(" -> " + info.getClass().getSimpleName() + " :");
        Field[] fields = info.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (!field.getName().endsWith("_index")) {
                continue;
            }
            field.setAccessible(true);
            AtomicType o = (AtomicType) field.get(info);
            sb.append(field.getName() + " :#");
            sb.append(o.getValue() + " -> ");
            printConstant(o.getIntValue(), sb);
            sb.append("; ");
        }
    }

    public static void main(String[] args) throws IOException {
        Parser parser = new Parser();
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream("Bean.class");

            // Create a new DataInputStream object with the FileInputStream object
            DataInputStream dataInputStream = new DataInputStream(fileInputStream);
            parser.parse(classInfo, dataInputStream);
            StringBuilder sb = new StringBuilder();
            print(class_info.class, classInfo, "", sb);
            System.out.println(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            fileInputStream.close();
        }
    }

}

再补充几个结构体定义实例:

public class Table<N extends AtomicType, T> {
    private N count;
    private LinkedList<T> data = new LinkedList<>();
    private Class countClass;
    private Class elementClass;

    public Table() {

    }
    public N getCount() {
        return count;
    }

    public void setCount(N count) {
        this.count = count;
    }

    public LinkedList<T> getData() {
        return data;
    }

    public void setData(LinkedList<T> data) {
        this.data = data;
    }

    public Class getCountClass() {
        return countClass;
    }

    public Class getElementClass() {
        return elementClass;
    }

    public void setCountClass(Class countClass) {
        this.countClass = countClass;
    }

    public void setElementClass(Class elementClass) {
        this.elementClass = elementClass;
    }

    public String toString() {
        return "count: " + count;
    }
}


public abstract class AtomicType<T extends Number> {
    private int length;
    private byte[] bytes;
    private T value;

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }

    public T getValue() {
        if (value != null) {
            return value;
        }
        parse();
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public byte[] getBytes() {
        return bytes;
    }

    public void setBytes(byte[] bytes) {
        this.bytes = bytes;
    }

    public abstract void parse();

    public int getIntValue() {
        return getValue().intValue();
    }
}

public class u2 extends AtomicType<Short> {
    public u2() {
        setLength(2);
        setBytes(new byte[2]);
    }

    @Override
    public void parse() {
        short x =  (short) ((((short)(getBytes()[0])) << 8) + (((short)(getBytes()[1])) << 0));
        setValue(x);
    }
}
public class attribute_info {
    public u2 attribute_name_index;
    public u4 attribute_length;

    public u2 getAttribute_name_index() {
        return attribute_name_index;
    }
}
public class cp_info implements Printable {
    public u1 tag;
}
public class class_info {
    public u4 magic;
    public u2 minor_version;
    public u2 major_version;
    public Table<u2, cp_info> constant_pool = new Table<>();
    public u2 access_flags;
    public u2 this_class;
    public u2 super_class;
    public Table<u2, Short> interfaces = new Table<>();
    public Table<u2, field_info> fields = new Table<>();
    public Table<u2, method_info> methods = new Table<>();
    public Table<u2, attribute_info> attributes = new Table<>();

    public Table<u2, cp_info> getConstant_pool() {
        return constant_pool;
    }
}

总结

不算完美,阶段性成果吧。关于泛型,值得单独写一篇记录一下。文章来源地址https://www.toymoban.com/news/detail-411918.html

到了这里,关于【JVM学习】Class文件解析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • class文件中,常量池之后的相关数据解析!【class二进制文件分析】

    前言:前段时间读《深入java虚拟机》介绍到class文件的时候,由于理论知识较多,人总感觉疲惫不堪,就泛泛阅读了一下。在工作中使用起来知识点知道,但是总是需要查阅各种资料。今天有时间,继续整理常量池后面的相关知识。 class文件中,我们可以通过背或记也好,或

    2024年02月07日
    浏览(32)
  • 解析用GraalVm编译的class文件

    如果只是单纯的用javap +class文件名的话,那只是单纯的反编译class文件而已。 我们都知道class文件的字节码文件,是难以理解的。 很好理解。这里保存了最新更改时间和文件大小为414字节。 类声明: 版本信息: 这表示 Java 编译器的次要版本为 0,主要版本为 61。Java 版本号

    2024年01月18日
    浏览(33)
  • Golang实现JAVA虚拟机-解析class文件

    原文链接:https://gaoyubo.cn/blogs/de1bedad.html 所需前置知识为:JAVA语言、JVM知识、Go笔记 对应项目:jvmgo 操作系统:Windows 11 openjdk version \\\"1.8.0_382\\\" go version go1.21.0 windows/amd64 Java虚拟机的工作是运行Java应用程序。和其他类型的应用程序一样,Java应用程序也需要一个入口点,这个入

    2024年02月04日
    浏览(32)
  • Java中Excel文件解析(POI简介及基本使用)

    在Java技术生态圈中,可以进行Excel文件处理的主流技术包括: Apache POI 、 JXL 、 Alibaba EasyExcel 等。 其中各个技术都有最适合的场景 Apache POI 基于 DOM 方式进行解析,将文件直接加载内存,所以速度较快,适合 Excel 文件数据量不大的应用场景。 JXL 只支持Excel 2003以下版本,所以

    2024年02月08日
    浏览(33)
  • jvm 什么是常量池,常量池定义 class常量池

    首先需要理解下Java的class文件,以及class文件结构: 1. Class文件 是一组以8个字节为基础单位的 二进制流 ,各个数据项目严格按照顺序紧凑地排列在文 件之中, 中间没有任何分隔符 ,这使得整个Class文件中存储的内容几乎全部是程序运行的必要数据, 没有空隙存在 。当遇到

    2024年02月11日
    浏览(32)
  • java中的线程不安全和实例解析(1),为了跳槽强刷1000道网络安全真题

    先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7 深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年最新网络安全全套学习资料》

    2024年04月22日
    浏览(23)
  • ubuntu下自启动设置,为了开机自启动launch文件

    每隔5秒钟启动一个launch文件,也可以直接在一个launch文件中启动多个,这里为了确保启动顺利,添加了一些延时 (1)、进入主菜单界面在搜索框中输入startup applications (2)、按照如下提示,加入后重启启动即可实现自启动

    2024年02月10日
    浏览(29)
  • 文件系统内部的inode是为了解决什么问题?有什么用途?

    文件系统内部的inode(Index Node)是为了解决文件管理和数据存储的问题,并提供了一些重要的用途,包括: 1. 文件索引:inode是文件系统中的一个数据结构,它存储了文件的元数据,如文件名、文件大小、文件权限、文件所有者等信息。inode相当于一个文件的索引,通过它可

    2024年02月15日
    浏览(30)
  • 为了快速掌握使用 ChatGPT,我应该着重学习什么?

    当然!下面是更详细的学习计划,包含了每个章节的内容和建议的学习时间: 章节一:ChatGPT简介 ChatGPT是什么(10分钟) 了解ChatGPT是一个基于深度学习的自然语言处理模型,能够生成人类般的对话回复。 应用领域和用途(10分钟) 探索ChatGPT在客户支持、虚拟助手、教育等领

    2024年02月12日
    浏览(61)
  • Vue3学习(仅为了记录,参考意义不大)

    vue-cli是创建vue2.0的脚手架工具,create-vue是创建vue3的脚手架工具,create-vue构建速度非常快 setup语法糖: 总结: 开启深度监听同样的写法 beforeUnmount和unmounted对应beforeDestoryed和destoryed defineProps原理还是props,只不过在setup里面可以去接收使用 父组件里面写v-model,子组件里defi

    2024年02月09日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包