Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit f8f9460

Browse files
authored
Merge pull request #5 from hankluo6/long
Implement long related opcodes
2 parents cc13aa7 + cf49c7e commit f8f9460

File tree

10 files changed

+726
-424
lines changed

10 files changed

+726
-424
lines changed

‎Makefile‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CC ?= gcc
22
CFLAGS = -std=c99 -Os -Wall -Wextra
33

44
BIN = jvm
5-
OBJS = jvm.o stack.o
5+
OBJS = jvm.o stack.o constant-pool.o classfile.o
66

77
deps := $(OBJS:%.o=.%.o.d)
88

@@ -36,7 +36,8 @@ TESTS = \
3636
Jumps \
3737
PalindromeProduct \
3838
Primes \
39-
Recursion
39+
Recursion \
40+
Long
4041

4142
check: $(addprefix tests/,$(TESTS:=-result.out))
4243

‎classfile.c‎

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
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+
}

‎classfile.h‎

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
#pragma once
2+
3+
#include "constant-pool.h"
4+
#include "type.h"
5+
6+
typedef struct {
7+
u4 magic;
8+
u2 minor_version;
9+
u2 major_version;
10+
} class_header_t;
11+
12+
typedef struct {
13+
u2 access_flags;
14+
u2 this_class;
15+
u2 super_class;
16+
} class_info_t;
17+
18+
typedef struct {
19+
u2 access_flags;
20+
u2 name_index;
21+
u2 descriptor_index;
22+
u2 attributes_count;
23+
} method_info;
24+
25+
typedef struct {
26+
u2 attribute_name_index;
27+
u4 attribute_length;
28+
} attribute_info;
29+
30+
typedef struct {
31+
u2 max_stack;
32+
u2 max_locals;
33+
u4 code_length;
34+
u1 *code;
35+
} code_t;
36+
37+
typedef struct {
38+
char *name;
39+
char *descriptor;
40+
code_t code;
41+
} method_t;
42+
43+
typedef struct {
44+
constant_pool_t constant_pool;
45+
method_t *methods;
46+
} class_file_t;
47+
48+
class_header_t get_class_header(FILE *class_file);
49+
class_info_t get_class_info(FILE *class_file);
50+
method_t *get_methods(FILE *class_file, constant_pool_t *cp);
51+
void read_method_attributes(FILE *class_file,
52+
method_info *info,
53+
code_t *code,
54+
constant_pool_t *cp);
55+
uint16_t get_number_of_parameters(method_t *method);
56+
method_t *find_method(const char *name, const char *desc, class_file_t *clazz);
57+
method_t *find_method_from_index(uint16_t idx, class_file_t *clazz);
58+
class_file_t get_class(FILE *class_file);
59+
void free_class(class_file_t *clazz);

‎constant-pool.c‎

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
#include "constant-pool.h"
2+
3+
/* Read unsigned big-endian integers */
4+
u1 read_u1(FILE *class_file)
5+
{
6+
int result = fgetc(class_file);
7+
assert(result != EOF && "Reached end of file prematurely");
8+
return result;
9+
}
10+
11+
u2 read_u2(FILE *class_file)
12+
{
13+
return (u2) read_u1(class_file) << 8 | read_u1(class_file);
14+
}
15+
16+
u4 read_u4(FILE *class_file)
17+
{
18+
return (u4) read_u2(class_file) << 16 | read_u2(class_file);
19+
}
20+
21+
/**
22+
* Get the constant at the given index in a constant pool.
23+
* Assert that the index is valid (i.e. between 1 and the pool size).
24+
*
25+
* @param constant_pool the class's constant pool
26+
* @param index the 1-indexed constant pool index
27+
* @return the constant at the given index
28+
*/
29+
const_pool_info *get_constant(constant_pool_t *constant_pool, u2 index)
30+
{
31+
assert(0 < index && index <= constant_pool->constant_pool_count &&
32+
"Invalid constant pool index");
33+
/* Convert 1-indexed index to 0-indexed index */
34+
return &constant_pool->constant_pool[index - 1];
35+
}
36+
37+
CONSTANT_NameAndType_info *get_method_name_and_type(constant_pool_t *cp, u2 idx)
38+
{
39+
const_pool_info *method = get_constant(cp, idx);
40+
assert(method->tag == CONSTANT_MethodRef && "Expected a MethodRef");
41+
const_pool_info *name_and_type_constant = get_constant(
42+
cp,
43+
((CONSTANT_FieldOrMethodRef_info *) method->info)->name_and_type_index);
44+
assert(name_and_type_constant->tag == CONSTANT_NameAndType &&
45+
"Expected a NameAndType");
46+
return (CONSTANT_NameAndType_info *) name_and_type_constant->info;
47+
}
48+
49+
constant_pool_t get_constant_pool(FILE *class_file)
50+
{
51+
constant_pool_t cp = {
52+
/* Constant pool count includes unused constant at index 0 */
53+
.constant_pool_count = read_u2(class_file) - 1,
54+
.constant_pool =
55+
malloc(sizeof(const_pool_info) * cp.constant_pool_count),
56+
};
57+
assert(cp.constant_pool && "Failed to allocate constant pool");
58+
59+
const_pool_info *constant = cp.constant_pool;
60+
for (u2 i = 0; i < cp.constant_pool_count; i++, constant++) {
61+
constant->tag = read_u1(class_file);
62+
switch (constant->tag) {
63+
case CONSTANT_Utf8: {
64+
u2 length = read_u2(class_file);
65+
char *value = malloc(length + 1);
66+
assert(value && "Failed to allocate UTF8 constant");
67+
size_t bytes_read = fread(value, 1, length, class_file);
68+
assert(bytes_read == length && "Failed to read UTF8 constant");
69+
value[length] = '0円';
70+
constant->info = (u1 *) value;
71+
break;
72+
}
73+
74+
case CONSTANT_Integer: {
75+
CONSTANT_Integer_info *value = malloc(sizeof(*value));
76+
assert(value && "Failed to allocate integer constant");
77+
value->bytes = read_u4(class_file);
78+
constant->info = (u1 *) value;
79+
break;
80+
}
81+
82+
case CONSTANT_Long: {
83+
CONSTANT_LongOrDouble_info *value = malloc(sizeof(*value));
84+
assert(value && "Failed to allocate long constant");
85+
value->high_bytes = read_u4(class_file);
86+
value->low_bytes = read_u4(class_file);
87+
constant->info = (u1 *) value;
88+
constant++;
89+
constant->info = NULL;
90+
i++;
91+
break;
92+
}
93+
94+
case CONSTANT_Class: {
95+
CONSTANT_Class_info *value = malloc(sizeof(*value));
96+
assert(value && "Failed to allocate class constant");
97+
value->string_index = read_u2(class_file);
98+
constant->info = (u1 *) value;
99+
break;
100+
}
101+
102+
case CONSTANT_MethodRef:
103+
case CONSTANT_FieldRef: {
104+
CONSTANT_FieldOrMethodRef_info *value = malloc(sizeof(*value));
105+
assert(value &&
106+
"Failed to allocate FieldRef or MethodRef constant");
107+
value->class_index = read_u2(class_file);
108+
value->name_and_type_index = read_u2(class_file);
109+
constant->info = (u1 *) value;
110+
break;
111+
}
112+
113+
case CONSTANT_NameAndType: {
114+
CONSTANT_NameAndType_info *value = malloc(sizeof(*value));
115+
assert(value && "Failed to allocate NameAndType constant");
116+
value->name_index = read_u2(class_file);
117+
value->descriptor_index = read_u2(class_file);
118+
constant->info = (u1 *) value;
119+
break;
120+
}
121+
122+
default:
123+
fprintf(stderr, "Unknown constant type %d\n", constant->tag);
124+
exit(1);
125+
}
126+
}
127+
return cp;
128+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /