1
+ #include "classfile.h"
2
+
3
+ class_header_t get_class_header (FILE * class_file )
4
+ {
5
+ return (class_header_t ){
6
+ .magic = read_u4 (class_file ),
7
+ .major_version = read_u2 (class_file ),
8
+ .minor_version = read_u2 (class_file ),
9
+ };
10
+ }
11
+
12
+ class_info_t get_class_info (FILE * class_file )
13
+ {
14
+ class_info_t info = {
15
+ .access_flags = read_u2 (class_file ),
16
+ .this_class = read_u2 (class_file ),
17
+ .super_class = read_u2 (class_file ),
18
+ };
19
+ u2 interfaces_count = read_u2 (class_file );
20
+ assert (!interfaces_count && "This VM does not support interfaces." );
21
+ u2 fields_count = read_u2 (class_file );
22
+ assert (!fields_count && "This VM does not support fields." );
23
+ return info ;
24
+ }
25
+
26
+ /**
27
+ * Get the number of integer parameters that a method takes.
28
+ * Use the descriptor string of the method to determine its signature.
29
+ */
30
+ uint16_t get_number_of_parameters (method_t * method )
31
+ {
32
+ /* Type descriptors have the length ( + #params + ) + return type */
33
+ return strlen (method -> descriptor ) - 3 ;
34
+ }
35
+
36
+ /**
37
+ * Find the method with the given name and signature.
38
+ * The descriptor is necessary because Java allows method overloading.
39
+ * This only needs to be called directly to invoke main();
40
+ * for the invokestatic instruction, use find_method_from_index().
41
+ *
42
+ * @param name the method name, e.g. "factorial"
43
+ * @param desc the method descriptor string, e.g. "(I)I"
44
+ * @param clazz the parsed class file
45
+ * @return the method if it was found, or NULL
46
+ */
47
+ method_t * find_method (const char * name , const char * desc , class_file_t * clazz )
48
+ {
49
+ for (method_t * method = clazz -> methods ; method -> name ; method ++ ) {
50
+ if (!(strcmp (name , method -> name ) || strcmp (desc , method -> descriptor )))
51
+ return method ;
52
+ }
53
+ return NULL ;
54
+ }
55
+
56
+ /**
57
+ * Find the method corresponding to the given constant pool index.
58
+ *
59
+ * @param index the constant pool index of the Methodref to call
60
+ * @param clazz the parsed class file
61
+ * @return the method if it was found, or NULL
62
+ */
63
+ method_t * find_method_from_index (uint16_t idx , class_file_t * clazz )
64
+ {
65
+ CONSTANT_NameAndType_info * name_and_type =
66
+ get_method_name_and_type (& clazz -> constant_pool , idx );
67
+ const_pool_info * name =
68
+ get_constant (& clazz -> constant_pool , name_and_type -> name_index );
69
+ assert (name -> tag == CONSTANT_Utf8 && "Expected a UTF8" );
70
+ const_pool_info * descriptor =
71
+ get_constant (& clazz -> constant_pool , name_and_type -> descriptor_index );
72
+ assert (descriptor -> tag == CONSTANT_Utf8 && "Expected a UTF8" );
73
+ return find_method ((char * ) name -> info , (char * ) descriptor -> info , clazz );
74
+ }
75
+
76
+ void read_method_attributes (FILE * class_file ,
77
+ method_info * info ,
78
+ code_t * code ,
79
+ constant_pool_t * cp )
80
+ {
81
+ bool found_code = false;
82
+ for (u2 i = 0 ; i < info -> attributes_count ; i ++ ) {
83
+ attribute_info ainfo = {
84
+ .attribute_name_index = read_u2 (class_file ),
85
+ .attribute_length = read_u4 (class_file ),
86
+ };
87
+ long attribute_end = ftell (class_file ) + ainfo .attribute_length ;
88
+ const_pool_info * type_constant =
89
+ get_constant (cp , ainfo .attribute_name_index );
90
+ assert (type_constant -> tag == CONSTANT_Utf8 && "Expected a UTF8" );
91
+ if (!strcmp ((char * ) type_constant -> info , "Code" )) {
92
+ assert (!found_code && "Duplicate method code" );
93
+ found_code = true;
94
+
95
+ code -> max_stack = read_u2 (class_file );
96
+ code -> max_locals = read_u2 (class_file );
97
+ code -> code_length = read_u4 (class_file );
98
+ code -> code = malloc (code -> code_length );
99
+ assert (code -> code && "Failed to allocate method code" );
100
+ size_t bytes_read =
101
+ fread (code -> code , 1 , code -> code_length , class_file );
102
+ assert (bytes_read == code -> code_length &&
103
+ "Failed to read method code" );
104
+ }
105
+ /* Skip the rest of the attribute */
106
+ fseek (class_file , attribute_end , SEEK_SET );
107
+ }
108
+ assert (found_code && "Missing method code" );
109
+ }
110
+
111
+ #define IS_STATIC 0x0008
112
+
113
+ method_t * get_methods (FILE * class_file , constant_pool_t * cp )
114
+ {
115
+ u2 method_count = read_u2 (class_file );
116
+ method_t * methods = malloc (sizeof (* methods ) * (method_count + 1 ));
117
+ assert (methods && "Failed to allocate methods" );
118
+
119
+ method_t * method = methods ;
120
+ for (u2 i = 0 ; i < method_count ; i ++ , method ++ ) {
121
+ method_info info = {
122
+ .access_flags = read_u2 (class_file ),
123
+ .name_index = read_u2 (class_file ),
124
+ .descriptor_index = read_u2 (class_file ),
125
+ .attributes_count = read_u2 (class_file ),
126
+ };
127
+
128
+ const_pool_info * name = get_constant (cp , info .name_index );
129
+ assert (name -> tag == CONSTANT_Utf8 && "Expected a UTF8" );
130
+ method -> name = (char * ) name -> info ;
131
+ const_pool_info * descriptor = get_constant (cp , info .descriptor_index );
132
+ assert (descriptor -> tag == CONSTANT_Utf8 && "Expected a UTF8" );
133
+ method -> descriptor = (char * ) descriptor -> info ;
134
+
135
+ /* FIXME: this VM can only execute static methods, while every class
136
+ * has a constructor method <init>
137
+ */
138
+ if (strcmp (method -> name , "<init>" ))
139
+ assert ((info .access_flags & IS_STATIC ) &&
140
+ "Only static methods are supported by this VM." );
141
+
142
+ read_method_attributes (class_file , & info , & method -> code , cp );
143
+ }
144
+
145
+ /* Mark end of array with NULL name */
146
+ method -> name = NULL ;
147
+ return methods ;
148
+ }
149
+
150
+ /**
151
+ * Read an entire class file.
152
+ * The end of the parsed methods array is marked by a method with a NULL name.
153
+ *
154
+ * @param class_file the open file to read
155
+ * @return the parsed class file
156
+ */
157
+ class_file_t get_class (FILE * class_file )
158
+ {
159
+ /* Read the leading header of the class file */
160
+ get_class_header (class_file );
161
+
162
+ /* Read the constant pool */
163
+ class_file_t clazz = {.constant_pool = get_constant_pool (class_file )};
164
+
165
+ /* Read information about the class that was compiled. */
166
+ get_class_info (class_file );
167
+
168
+ /* Read the list of static methods */
169
+ clazz .methods = get_methods (class_file , & clazz .constant_pool );
170
+ return clazz ;
171
+ }
172
+
173
+ /**
174
+ * Frees the memory used by a parsed class file.
175
+ *
176
+ * @param class the parsed class file
177
+ */
178
+ void free_class (class_file_t * clazz )
179
+ {
180
+ constant_pool_t * cp = & clazz -> constant_pool ;
181
+ for (u2 i = 0 ; i < cp -> constant_pool_count ; i ++ )
182
+ free (cp -> constant_pool [i ].info );
183
+ free (cp -> constant_pool );
184
+
185
+ for (method_t * method = clazz -> methods ; method -> name ; method ++ )
186
+ free (method -> code .code );
187
+ free (clazz -> methods );
188
+ }
0 commit comments