JVM字节码常量池解析

JVM字节码常量池解析

前言

最近写J2ME深感各种java反编译不得我心,决定自己尝试写一个自己的java反编译,第一步实现javap。现在把字节码的常量池的解析代码分享出来。

常量池类型

前面已经分析过了,现在直接贴代码

cpp 复制代码
//	§4.4.1
#define CONSTANT_Class    7
//	§4.4.2
#define CONSTANT_Fieldref    9
//	§4.4.2
#define CONSTANT_Methodref    10
//	§4.4.2
#define CONSTANT_InterfaceMethodref    11
//	§4.4.3
#define CONSTANT_String    8
//	§4.4.4
#define CONSTANT_Integer    3
//	§4.4.4
#define CONSTANT_Float    4
//	§4.4.5
#define CONSTANT_Long    5
//	§4.4.5
#define CONSTANT_Double    6
//	§4.4.6
#define CONSTANT_NameAndType    12
//	§4.4.7
#define CONSTANT_Utf8    1
//	§4.4.8
#define CONSTANT_MethodHandle    15
//	§4.4.9
#define CONSTANT_MethodType    16
//	§4.4.10
#define CONSTANT_Dynamic    17
//	§4.4.10
#define CONSTANT_InvokeDynamic    18
//	§4.4.11
#define CONSTANT_Module    19
//	§4.4.12
#define CONSTANT_Package    20

代码定义

ConstPool.h

3.1常量池基类定义

cpp 复制代码
// ==================== 常量池基类定义 ====================

/**
 * 常量池项基类 - 所有常量池项的抽象基类
 * 定义了常量池项的基本接口和公共属性
 */
class constant_pool_info {
public:
    u1 tag; // 常量池项类型标识

    virtual ~constant_pool_info() = default;

    // 获取常量池项的字符串表示
    virtual std::string toString() const = 0;

    // 获取常量池项的大小(字节)
    virtual size_t getSize() const = 0;

    // 验证常量池项的有效性
    virtual bool validate() const = 0;

    // 从文件流中读取数据
    virtual void readFrom(std::istream& file) = 0;
};

3.2 CONSTANT_Class_info (tag=7) - 类或接口引用

cpp 复制代码
/**
 * CONSTANT_Class_info (tag=7) - 类或接口引用
 * 表示一个类或接口的符号引用
 */
class CONSTANT_Class_info : public constant_pool_info {
public:
    u2 name_index; // 指向常量池中UTF8字符串的索引,表示类的全限定名

    CONSTANT_Class_info() { tag = CONSTANT_Class; }

    std::string toString() const override;
    size_t getSize() const override { return 3; } // 1字节tag + 2字节name_index
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.3 CONSTANT_Fieldref_info (tag=9) - 字段引用

cpp 复制代码
/**
 * CONSTANT_Fieldref_info (tag=9) - 字段引用
 * 表示对一个字段的符号引用
 */
class CONSTANT_Fieldref_info : public constant_pool_info {
public:
    u2 class_index;            // 指向类或接口的CONSTANT_Class_info索引
    u2 name_and_type_index;    // 指向字段名和类型的CONSTANT_NameAndType_info索引

    CONSTANT_Fieldref_info() { tag = CONSTANT_Fieldref; }

    std::string toString() const override;
    size_t getSize() const override { return 5; } // 1字节tag + 4字节索引
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.4 CONSTANT_Methodref_info (tag=10) - 方法引用

cpp 复制代码
/**
 * CONSTANT_Methodref_info (tag=10) - 方法引用
 * 表示对一个方法的符号引用
 */
class CONSTANT_Methodref_info : public constant_pool_info {
public:
    u2 class_index;            // 指向类或接口的CONSTANT_Class_info索引
    u2 name_and_type_index;    // 指向方法名和类型的CONSTANT_NameAndType_info索引

    CONSTANT_Methodref_info() { tag = CONSTANT_Methodref; }

    std::string toString() const override;
    size_t getSize() const override { return 5; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.5 CONSTANT_InterfaceMethodref_info (tag=11) - 接口方法引用

cpp 复制代码
/**
 * CONSTANT_InterfaceMethodref_info (tag=11) - 接口方法引用
 * 表示对一个接口方法的符号引用
 */
class CONSTANT_InterfaceMethodref_info : public constant_pool_info {
public:
    u2 class_index;            // 指向接口的CONSTANT_Class_info索引
    u2 name_and_type_index;    // 指向方法名和类型的CONSTANT_NameAndType_info索引

    CONSTANT_InterfaceMethodref_info() { tag = CONSTANT_InterfaceMethodref; }

    std::string toString() const override;
    size_t getSize() const override { return 5; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.6 CONSTANT_String_info (tag=8) - 字符串引用

cpp 复制代码
/**
 * CONSTANT_String_info (tag=8) - 字符串引用
 * 表示对一个字符串字面量的引用
 */
class CONSTANT_String_info : public constant_pool_info {
public:
    u2 string_index; // 指向CONSTANT_Utf8_info的索引,表示字符串内容

    CONSTANT_String_info() { tag = CONSTANT_String; }

    std::string toString() const override;
    size_t getSize() const override { return 3; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.7 CONSTANT_Integer_info (tag=3) - 整型常量

cpp 复制代码
/**
 * CONSTANT_Integer_info (tag=3) - 整型常量
 * 表示4字节int类型的常量值
 */
class CONSTANT_Integer_info : public constant_pool_info {
public:
    u4 bytes; // 整数值,按大端序存储

    CONSTANT_Integer_info() { tag = CONSTANT_Integer; }

    std::string toString() const override;
    size_t getSize() const override { return 5; }
    bool validate() const override;
    void readFrom(std::istream& file) override;

    // 获取整数值
    int32_t getValue() const { return static_cast<int32_t>(bytes); }
};

3.8 CONSTANT_Float_info (tag=4) - 浮点常量

cpp 复制代码
/**
 * CONSTANT_Float_info (tag=4) - 浮点常量
 * 表示4字节float类型的常量值
 */
class CONSTANT_Float_info : public constant_pool_info {
public:
    u4 bytes; // 浮点数值,按IEEE 754格式存储

    CONSTANT_Float_info() { tag = CONSTANT_Float; }

    std::string toString() const override;
    size_t getSize() const override { return 5; }
    bool validate() const override;
    void readFrom(std::istream& file) override;

    // 获取浮点数值
    float getValue() const {
        auto val = bytes;
        return *reinterpret_cast<float*>(&val);
    }
};

3.9 CONSTANT_Long_info (tag=5) - 长整型常量

cpp 复制代码
/**
 * CONSTANT_Long_info (tag=5) - 长整型常量
 * 表示8字节long类型的常量值
 * 注意:此类型在常量池中占用两个位置
 */
class CONSTANT_Long_info : public constant_pool_info {
public:
    u4 high_bytes; // 高32位
    u4 low_bytes;  // 低32位

    CONSTANT_Long_info() { tag = CONSTANT_Long; }

    std::string toString() const override;
    size_t getSize() const override { return 9; } // 在文件中占9字节,但在常量池中占两个位置
    bool validate() const override;
    void readFrom(std::istream& file) override;

    // 获取长整数值
    int64_t getValue() const {
        return (static_cast<int64_t>(high_bytes) << 32) | low_bytes;
    }
};

3.10 CONSTANT_Double_info (tag=6) - 双精度浮点常量

cpp 复制代码
/**
 * CONSTANT_Double_info (tag=6) - 双精度浮点常量
 * 表示8字节double类型的常量值
 * 注意:此类型在常量池中占用两个位置
 */
class CONSTANT_Double_info : public constant_pool_info {
public:
    u4 high_bytes; // 高32位
    u4 low_bytes;  // 低32位

    CONSTANT_Double_info() { tag = CONSTANT_Double; }

    std::string toString() const override;
    size_t getSize() const override { return 9; }
    bool validate() const override;
    void readFrom(std::istream& file) override;

    // 获取双精度浮点数值
    double getValue() const {
        auto val = (static_cast<long long>(high_bytes) << 32) | low_bytes;
        return *reinterpret_cast<double*>(&val);
    }
};

3.11 CONSTANT_NameAndType_info (tag=12) - 名称和类型

cpp 复制代码
/**
 * CONSTANT_NameAndType_info (tag=12) - 名称和类型
 * 表示字段或方法的名称和描述符
 */
class CONSTANT_NameAndType_info : public constant_pool_info {
public:
    u2 name_index;       // 指向名称的CONSTANT_Utf8_info索引
    u2 descriptor_index; // 指向描述符的CONSTANT_Utf8_info索引

    CONSTANT_NameAndType_info() { tag = CONSTANT_NameAndType; }

    std::string toString() const override;
    size_t getSize() const override { return 5; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.12 CONSTANT_Utf8_info (tag=1) - UTF-8字符串

cpp 复制代码
/**
 * CONSTANT_Utf8_info (tag=1) - UTF-8字符串
 * 表示UTF-8编码的字符串,常量池中实际的字符串存储
 */
class CONSTANT_Utf8_info : public constant_pool_info {
public:
    u2 length; // 字符串长度(字节数)
    u1 *bytes; // UTF-8编码的字节数组

    CONSTANT_Utf8_info() {
        tag = CONSTANT_Utf8;
        bytes = nullptr;
        length = 0;
    }

    ~CONSTANT_Utf8_info() override {
        delete[] bytes;
    }

    // 拷贝构造函数和赋值操作符
    CONSTANT_Utf8_info(const CONSTANT_Utf8_info& other);
    CONSTANT_Utf8_info& operator=(const CONSTANT_Utf8_info& other);

    std::string toString() const override;
    size_t getSize() const override { return 3 + length; }
    bool validate() const override;
    void readFrom(std::istream& file) override;

    // 获取字符串
    std::string getString() const;

    // 设置字符串
    void setString(const std::string& str);
};

3.13 CONSTANT_MethodHandle_info (tag=15) - 方法句柄

cpp 复制代码
/**
 * CONSTANT_MethodHandle_info (tag=15) - 方法句柄
 * Java 7引入,表示方法句柄,用于动态语言支持
 */
class CONSTANT_MethodHandle_info : public constant_pool_info {
public:
    u1 reference_kind;   // 引用类型 (1-9)
    u2 reference_index;  // 引用索引,指向常量池中的一项

    CONSTANT_MethodHandle_info() { tag = CONSTANT_MethodHandle; }

    std::string toString() const override;
    size_t getSize() const override { return 4; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.14 CONSTANT_MethodType_info (tag=16) - 方法类型

cpp 复制代码
/**
 * CONSTANT_MethodType_info (tag=16) - 方法类型
 * Java 7引入,表示方法类型,用于动态语言支持
 */
class CONSTANT_MethodType_info : public constant_pool_info {
public:
    u2 descriptor_index; // 指向方法描述符的CONSTANT_Utf8_info索引

    CONSTANT_MethodType_info() { tag = CONSTANT_MethodType; }

    std::string toString() const override;
    size_t getSize() const override { return 3; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.15 CONSTANT_Dynamic_info (tag=17) - 动态调用点

cpp 复制代码
/**
 * CONSTANT_Dynamic_info (tag=17) - 动态调用点
 * Java 11引入,用于更高级的动态语言特性
 */
class CONSTANT_Dynamic_info : public constant_pool_info {
public:
    u2 bootstrap_method_attr_index; // 指向BootstrapMethods属性的索引
    u2 name_and_type_index;         // 指向名称和类型的CONSTANT_NameAndType_info索引

    CONSTANT_Dynamic_info() { tag = CONSTANT_Dynamic; }

    std::string toString() const override;
    size_t getSize() const override { return 5; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.16 CONSTANT_InvokeDynamic_info (tag=18) - 调用动态

cpp 复制代码
/**
 * CONSTANT_InvokeDynamic_info (tag=18) - 调用动态
 * Java 7引入,用于invokedynamic指令
 */
class CONSTANT_InvokeDynamic_info : public constant_pool_info {
public:
    u2 bootstrap_method_attr_index; // 指向BootstrapMethods属性的索引
    u2 name_and_type_index;         // 指向方法名和类型的CONSTANT_NameAndType_info索引

    CONSTANT_InvokeDynamic_info() { tag = CONSTANT_InvokeDynamic; }

    std::string toString() const override;
    size_t getSize() const override { return 5; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.17 CONSTANT_Module_info (tag=19) - 模块名

cpp 复制代码
/**
 * CONSTANT_Module_info (tag=19) - 模块名
 * Java 9引入,用于模块系统
 */
class CONSTANT_Module_info : public constant_pool_info {
public:
    u2 name_index; // 指向模块名的CONSTANT_Utf8_info索引

    CONSTANT_Module_info() { tag = CONSTANT_Module; }

    std::string toString() const override;
    size_t getSize() const override { return 3; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

3.18 CONSTANT_Package_info (tag=20) - 包名

cpp 复制代码
/**
 * CONSTANT_Package_info (tag=20) - 包名
 * Java 9引入,用于模块系统
 */
class CONSTANT_Package_info : public constant_pool_info {
public:
    u2 name_index; // 指向包名的CONSTANT_Utf8_info索引

    CONSTANT_Package_info() { tag = CONSTANT_Package; }

    std::string toString() const override;
    size_t getSize() const override { return 3; }
    bool validate() const override;
    void readFrom(std::istream& file) override;
};

实现代码与管理代码

4.1工厂管理工具类

cpp 复制代码
/**
 * 常量池工厂类 - 根据tag创建对应的常量池对象
 * 使用工厂模式,提供统一的创建接口
 */
class ConstantPoolFactory {
public:
    /**
     * 根据tag值创建对应的常量池对象
     * @param tag 常量池项类型标识
     * @return 对应的常量池对象指针,如果tag无效则返回nullptr
     */
    static constant_pool_info* createConstant(u1 tag);

    /**
     * 检查tag是否为有效的常量池类型
     * @param tag 要检查的tag值
     * @return 是否有效
     */
    static bool isValidTag(u1 tag);

    /**
     * 获取常量池类型的名称
     * @param tag 常量池类型标识
     * @return 类型名称字符串
     */
    static std::string getConstantTypeName(u1 tag);

    /**
     * 检查该常量池类型是否在常量池中占用两个位置
     * @param tag 常量池类型标识
     * @return 是否占用两个位置
     */
    static bool takesTwoSlots(u1 tag);
};

这里的实现逻辑比较简单switch或者if判断tag的类型就行

cpp 复制代码
constant_pool_info *ConstantPoolFactory::createConstant(u1 tag) {
    switch (tag) {
        case CONSTANT_Class:
            return new CONSTANT_Class_info();
        case CONSTANT_Fieldref:
            return new CONSTANT_Fieldref_info();
        case CONSTANT_Methodref:
            return new CONSTANT_Methodref_info();
        case CONSTANT_InterfaceMethodref:
            // 省略机械重复性代码

4.2 具体常量实现代码

贴两个示例

cpp 复制代码
// CONSTANT_Long_info
void CONSTANT_Long_info::readFrom(std::istream& file) {
    high_bytes = IoUtil::readU4(file);
    low_bytes = IoUtil::readU4(file);
}


// CONSTANT_Utf8_info
void CONSTANT_Utf8_info::readFrom(std::istream& file) {
    // 读取长度
    length = IoUtil::readU2(file);
    delete[] bytes;
    // 读取字符串数据
    if (length > 0) {
        bytes = new u1[length];
        for (u2 i = 0; i < length; i++) {
            bytes[i] = IoUtil::readU1(file);
        }
    } else {
        bytes = nullptr;
    }
}

读取代码

这里注意几点

  • long和duble占用两个索引
  • 常量池的个数是constant_pool_count-1
  • 我这里设计的ConstPool会用一个空的展位下标为0的
cpp 复制代码
// 读取常量池
void ClassFileReader::readConstantPool(std::istream& file, ClassFile* classFile) {
    classFile->constant_pool_count = IoUtil::readU2(file);

    // 创建新的常量池对象
    classFile->constant_pool = new ConstPool();

    // 读取每个常量池项
    for (u2 i = 1; i < classFile->constant_pool_count; i++) {
        u1 tag = IoUtil::readU1(file);

        // 使用工厂创建对应的常量池对象
        auto constant = std::unique_ptr<constant_pool_info>(ConstantPoolFactory::createConstant(tag));
        if (!constant) {
            throw std::runtime_error("Invalid constant pool tag: " + std::to_string(tag));
        }

        // 根据类型填充具体数据
        constant->readFrom(file);

        // 添加到常量池
        classFile->constant_pool->addConstant(std::move(constant));

        // Long和Double类型占用两个位置,需要跳过下一个位置
        if (ConstantPoolFactory::takesTwoSlots(tag)) {
            i++; // 跳过下一个位置,常量池会自动处理
        }
    }
    classFile->constant_pool->nameMapping();
}

运行代码展示

示例代码 本期略,发现还没实现完善的toString

结束以及参考

本文重点在于分享常量池的数据结构定义,实现代码较为简单不是本文重点。这里充分利用面向对象的设计思路,把解析分担给各个实现类。

参考和链接:

Chapter 4. The class File Format

JVM字节码attribute_info

JVM StackMapTable 属性详解

JVM字节码数据结构总览和读取

相关推荐
Maỿbe5 分钟前
JVM中的类加载&&Minor GC与Full GC
jvm
人道领域1 小时前
【零基础学java】(等待唤醒机制,线程池补充)
java·开发语言·jvm
小突突突1 小时前
浅谈JVM
jvm
饺子大魔王的男人3 小时前
远程调试总碰壁?局域网成 “绊脚石”?Remote JVM Debug与cpolar的合作让效率飙升
网络·jvm
天“码”行空13 小时前
java面向对象的三大特性之一多态
java·开发语言·jvm
独自破碎E18 小时前
JVM的内存区域是怎么划分的?
jvm
期待のcode19 小时前
认识Java虚拟机
java·开发语言·jvm
leaves falling1 天前
一篇文章深入理解指针
jvm
曹轲恒1 天前
String.intern() 方法
java·常量池
linweidong1 天前
C++ 中避免悬挂引用的企业策略有哪些?
java·jvm·c++