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