Skip to content

Commit 635e14b

Browse files
committed
初步解析 .class 文件
1 parent e444a47 commit 635e14b

File tree

7 files changed

+328
-5
lines changed

7 files changed

+328
-5
lines changed

CMakeLists.txt

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,30 @@ cmake_minimum_required(VERSION 3.28.1)
22

33
project(jvm)
44

5-
set(CMAKE_CXX_STANDARD 20)
5+
set(CMAKE_CXX_STANDARD 17)
66
set(CMAKE_CXX_STANDARD_REQUIRED ON)
77

8-
add_executable(jvm)
9-
target_sources(jvm PRIVATE src/Entry.cpp)
8+
include_directories(src)
109

10+
add_library(jvm)
11+
target_sources(jvm PRIVATE
12+
src/jvm/Class.h
13+
src/jvm/Class.cc
14+
)
1115
find_package(Sese CONFIG REQUIRED)
12-
target_link_libraries(jvm PRIVATE Sese::Core)
16+
target_link_libraries(jvm PUBLIC Sese::Core)
17+
18+
add_executable(runner)
19+
target_sources(runner PRIVATE
20+
src/runner/Entry.cpp
21+
)
22+
target_link_libraries(runner PUBLIC jvm)
23+
24+
add_executable(test)
25+
target_sources(test PRIVATE
26+
src/test/Main.cpp
27+
src/test/TestClass.cpp
28+
)
29+
target_link_libraries(test PUBLIC jvm)
30+
find_package(GTest CONFIG REQUIRED)
31+
target_link_libraries(test PRIVATE GTest::gtest)

src/jvm/Class.cc

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#include "Class.h"
2+
3+
#include <sese/Log.h>
4+
#include <sese/util/Endian.h>
5+
#include <sese/util/Exception.h>
6+
7+
jvm::Class::Class(const std::string &path) {
8+
SESE_DEBUG("open %s", path.c_str());
9+
file = sese::io::File::create(path, sese::io::File::B_READ);
10+
if (!file) throw sese::Exception("cannot open file");
11+
SESE_DEBUG("open success");
12+
}
13+
14+
void jvm::Class::parse() {
15+
parseMagicNumber();
16+
parseVersion();
17+
parseConstantPool();
18+
parseClass();
19+
parseFields();
20+
parseMethods();
21+
parseAttributes();
22+
}
23+
24+
#define IF_READ(m) if (sizeof(m) != file->read(&m, sizeof(m)))
25+
#define ASSERT_READ(m) IF_READ(m) throw sese::Exception("failed to parse " #m);
26+
27+
void jvm::Class::parseMagicNumber() {
28+
ASSERT_READ(magic)
29+
magic = ToBigEndian32(magic);
30+
SESE_DEBUG("magic number 0x%x", magic);
31+
}
32+
33+
void jvm::Class::parseVersion() {
34+
ASSERT_READ(minor)
35+
minor = FromBigEndian16(minor);
36+
SESE_DEBUG("minor version %d", minor);
37+
ASSERT_READ(minor)
38+
major = FromBigEndian16(major);
39+
SESE_DEBUG("major version %d", major);
40+
}
41+
42+
void jvm::Class::parseConstantPool() {
43+
ASSERT_READ(constant_pool_count)
44+
constant_pool_count = FromBigEndian16(constant_pool_count);
45+
SESE_DEBUG("constant pool count %d", constant_pool_count);
46+
47+
for (int i = 1; i < constant_pool_count; i++) {
48+
int8_t tag;
49+
ASSERT_READ(tag)
50+
SESE_DEBUG("tag %d", tag);
51+
if (tag == utf8_info) {
52+
int16_t length;
53+
char bytes[UINT16_MAX]{};
54+
ASSERT_READ(length)
55+
length = FromBigEndian16(length);
56+
SESE_DEBUG("length %d", length);
57+
if (length != file->read(bytes, length)) {
58+
throw sese::Exception("failed to parse bytes");
59+
}
60+
SESE_DEBUG("bytes %s", bytes);
61+
} else if (tag == class_info) {
62+
int16_t index;
63+
ASSERT_READ(index)
64+
index = FromBigEndian16(index);
65+
SESE_DEBUG("index %d", index);
66+
} else if (tag == method_ref_info) {
67+
int16_t index;
68+
ASSERT_READ(index)
69+
index = FromBigEndian16(index);
70+
SESE_DEBUG("index %d", index);
71+
ASSERT_READ(index)
72+
index = FromBigEndian16(index);
73+
SESE_DEBUG("index %d", index);
74+
} else if (tag == name_and_type_info) {
75+
int16_t index;
76+
ASSERT_READ(index)
77+
index = FromBigEndian16(index);
78+
SESE_DEBUG("index %d", index);
79+
ASSERT_READ(index)
80+
index = FromBigEndian16(index);
81+
SESE_DEBUG("index %d", index);
82+
}
83+
}
84+
}
85+
86+
void jvm::Class::parseClass() {
87+
ASSERT_READ(access_flags)
88+
access_flags = FromBigEndian16(access_flags);
89+
SESE_DEBUG("access flags 0x%x", access_flags);
90+
91+
ASSERT_READ(this_class)
92+
this_class = FromBigEndian16(this_class);
93+
SESE_DEBUG("this class %d", this_class);
94+
95+
ASSERT_READ(super_class)
96+
super_class = FromBigEndian16(super_class);
97+
SESE_DEBUG("super class %d", super_class);
98+
99+
ASSERT_READ(interface_count)
100+
interface_count = FromBigEndian16(interface_count);
101+
SESE_DEBUG("interface count %d", interface_count);
102+
103+
interfaces.reserve(interface_count);
104+
for (int i = 0; i < interface_count; i++) {
105+
uint16_t iface;
106+
ASSERT_READ(iface)
107+
iface = FromBigEndian16(iface);
108+
SESE_DEBUG("interface %d", iface);
109+
interfaces.push_back(iface);
110+
}
111+
}
112+
113+
void jvm::Class::parseFields() {
114+
ASSERT_READ(fields_count)
115+
fields_count = FromBigEndian16(fields_count);
116+
SESE_DEBUG("fields count %d", fields_count);
117+
118+
field_infos.reserve(fields_count);
119+
for (int i = 0; i < fields_count; ++i) {
120+
FieldInfo field_info;
121+
ASSERT_READ(field_info.access_flags)
122+
field_info.access_flags = FromBigEndian16(field_info.access_flags);
123+
ASSERT_READ(field_info.name_index)
124+
field_info.name_index = FromBigEndian16(field_info.name_index);
125+
ASSERT_READ(field_info.descriptor_index)
126+
field_info.descriptor_index = FromBigEndian16(field_info.descriptor_index);
127+
ASSERT_READ(field_info.attributes_count)
128+
field_info.attributes_count = FromBigEndian16(field_info.attributes_count);
129+
field_info.attribute_infos.reserve(field_info.attributes_count);
130+
for (int j = 0; i < field_info.attributes_count; j++) {
131+
AttributeInfo attribute_info;
132+
ASSERT_READ(attribute_info.name_index)
133+
ASSERT_READ(attribute_info.length)
134+
attribute_info.length = FromBigEndian32(attribute_info.length);
135+
attribute_info.info.reserve(attribute_info.length);
136+
for (int k = 0; k < attribute_info.length; k++) {
137+
uint8_t byte;
138+
ASSERT_READ(byte)
139+
attribute_info.info.push_back(byte);
140+
}
141+
field_info.attribute_infos.push_back(attribute_info);
142+
}
143+
field_infos.push_back(field_info);
144+
}
145+
}
146+
147+
void jvm::Class::parseMethods() {
148+
ASSERT_READ(methods_count)
149+
methods_count = FromBigEndian16(methods_count);
150+
SESE_DEBUG("methods count %d", methods_count);
151+
152+
for (int i = 0; i< methods_count; ++i) {
153+
MethodInfo method_info;
154+
ASSERT_READ(method_info.access_flags)
155+
method_info.access_flags = FromBigEndian16(method_info.access_flags);
156+
ASSERT_READ(method_info.name_index)
157+
method_info.name_index = FromBigEndian16(method_info.name_index);
158+
ASSERT_READ(method_info.descriptor_index)
159+
method_info.descriptor_index = FromBigEndian16(method_info.descriptor_index);
160+
ASSERT_READ(method_info.attributes_count)
161+
method_info.attributes_count = FromBigEndian16(method_info.attributes_count);
162+
method_info.attribute_infos.reserve(method_info.attributes_count);
163+
for (int j = 0; j < method_info.attributes_count; ++j) {
164+
AttributeInfo attribute_info;
165+
ASSERT_READ(attribute_info.name_index)
166+
ASSERT_READ(attribute_info.length)
167+
attribute_info.length = FromBigEndian32(attribute_info.length);
168+
attribute_info.info.reserve(attribute_info.length);
169+
for (int k = 0; k < attribute_info.length; k++) {
170+
uint8_t byte;
171+
ASSERT_READ(byte)
172+
attribute_info.info.push_back(byte);
173+
}
174+
method_info.attribute_infos.push_back(attribute_info);
175+
}
176+
method_infos.push_back(method_info);
177+
}
178+
}
179+
180+
void jvm::Class::parseAttributes() {
181+
ASSERT_READ(attributes_count)
182+
attributes_count = FromBigEndian16(attributes_count);
183+
SESE_DEBUG("attributes count %d", attributes_count);
184+
attribute_infos.reserve(attributes_count);
185+
for (int j = 0; j < attributes_count; ++j) {
186+
AttributeInfo attribute_info;
187+
ASSERT_READ(attribute_info.name_index)
188+
ASSERT_READ(attribute_info.length)
189+
attribute_info.length = FromBigEndian32(attribute_info.length);
190+
attribute_info.info.reserve(attribute_info.length);
191+
for (int k = 0; k < attribute_info.length; k++) {
192+
uint8_t byte;
193+
ASSERT_READ(byte)
194+
attribute_info.info.push_back(byte);
195+
}
196+
attribute_infos.push_back(attribute_info);
197+
}
198+
}
199+

src/jvm/Class.h

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#pragma once
2+
3+
#include <sese/io/FileStream.h>
4+
#include <vector>
5+
6+
namespace jvm {
7+
class Class {
8+
public:
9+
enum Constant : int8_t {
10+
utf8_info = 1,
11+
integer_info = 3,
12+
float_info = 4,
13+
long_info = 5,
14+
double_info = 6,
15+
class_info = 7,
16+
string_info = 8,
17+
field_ref_info = 9,
18+
method_ref_info = 10,
19+
interface_method_ref_info = 11,
20+
name_and_type_info = 12,
21+
method_handle_info = 15,
22+
method_type_info = 16,
23+
invoke_dynamic_info = 18
24+
};
25+
26+
struct AttributeInfo {
27+
uint16_t name_index{};
28+
uint32_t length{};
29+
std::vector<uint8_t> info;
30+
};
31+
32+
struct FieldInfo {
33+
uint16_t access_flags{};
34+
uint16_t name_index{};
35+
uint16_t descriptor_index{};
36+
uint16_t attributes_count{};
37+
std::vector<AttributeInfo> attribute_infos{};
38+
};
39+
40+
struct MethodInfo {
41+
uint16_t access_flags;
42+
uint16_t name_index;
43+
uint16_t descriptor_index;
44+
uint16_t attributes_count;
45+
std::vector<AttributeInfo> attribute_infos;
46+
};
47+
48+
explicit Class(const std::string &path);
49+
50+
void parse();
51+
52+
private:
53+
void parseMagicNumber();
54+
55+
void parseVersion();
56+
57+
void parseConstantPool();
58+
59+
void parseClass();
60+
61+
void parseFields();
62+
63+
void parseMethods();
64+
65+
void parseAttributes();
66+
67+
sese::io::File::Ptr file{};
68+
69+
uint32_t magic{};
70+
uint16_t minor{}, major{};
71+
uint16_t constant_pool_count{};
72+
uint16_t access_flags{};
73+
uint16_t this_class{};
74+
uint16_t super_class{};
75+
uint16_t interface_count{};
76+
std::vector<uint16_t> interfaces{};
77+
uint16_t fields_count{};
78+
std::vector<FieldInfo> field_infos{};
79+
uint16_t methods_count{};
80+
std::vector<MethodInfo> method_infos{};
81+
uint16_t attributes_count{};
82+
std::vector<AttributeInfo> attribute_infos{};
83+
};
84+
}
File renamed without changes.

src/test/Main.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <gtest/gtest.h>
2+
#include <sese/Init.h>
3+
4+
int main(int argc, char **argv) {
5+
testing::InitGoogleTest(&argc, argv);
6+
sese::initCore(argc, argv);
7+
return RUN_ALL_TESTS();
8+
}

src/test/TestClass.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <gtest/gtest.h>
2+
#include <jvm/Class.h>
3+
#include <sese/util/Exception.h>
4+
5+
TEST(TestClass, Parse) {
6+
try {
7+
auto cl = jvm::Class("C:/Users/kaoru/Desktop/Hello.class");
8+
cl.parse();
9+
} catch (sese::Exception &exception) {
10+
exception.printStacktrace();
11+
}
12+
}

vcpkg.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"name": "sese",
55
"version>=": "2.1.2",
66
"default-features": false
7-
}
7+
},
8+
"gtest"
89
]
910
}

0 commit comments

Comments
 (0)