def parse(self, filename): self.class_file = ClassFile() with open(filename, "rb") as f: self.class_file.magic = f.read(4) self.class_file.minor_version = convert_bytes_to_int(f.read(2)) self.class_file.major_versio = convert_bytes_to_int(f.read(2)) self.class_file.constant_pool_count = convert_bytes_to_int( f.read(2)) for _ in range(self.class_file.constant_pool_count - 1): self.class_file.add_constant_pool(self.parse_constant_pool(f)) self.class_file.access_flags = convert_bytes_to_int(f.read(2)) self.class_file.this_class = convert_bytes_to_int(f.read(2)) self.class_file.super_class = convert_bytes_to_int(f.read(2)) self.class_file.interfaces_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.interfaces_count): self.class_file.add_interfaces(int.from_bytes(f.read(2))) self.class_file.fields_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.fields_count): self.class_file.add_field(self.parse_field()) self.class_file.methods_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.methods_count): self.class_file.add_method(self.parse_methods(f)) self.class_file.attributes_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.attributes_count): self.class_file.add_attribute(self.parse_attributes(f)) return self.class_file
def __init__(self, file_name, functions): self.file_name = file_name.replace(".bas","") self.class_name = self.file_name.capitalize() self.functions = functions self.code = ClassFile(self.class_name, self.file_name) self.method_bytecode = b"" self.vars = {"args": 0};
def create_code(file_name, message): """Write the compile code to a class file""" code = ClassFile(file_name.capitalize(), file_name) method_bytecode = b"\x2a\xb7" + struct.pack("!h", code.add_method_to_const_pool("java/lang/Object", "<init>", "()V") ) + b"\xb1" code.add_method(0x0000, "<init>", "()V", 1, 1, method_bytecode) method_bytecode = b"\xb2" + struct.pack("!h", code.add_field_to_const_pool("java/lang/System", "out", "Ljava/io/PrintStream;")) + b"\x12" + struct.pack("B", code.add_string_ref_to_const_pool(message)) + b"\xb6" + struct.pack("!h", code.add_method_to_const_pool("java/io/PrintStream", "println", "(Ljava/lang/String;)V")) + b"\xb1" code.add_method(0x0009, "main", "([Ljava/lang/String;)V", 2, 1, method_bytecode) open(file_name.capitalize() + ".class", "wb").write(code.write_class())
class Compiler: def __init__(self, file_name, functions): self.file_name = file_name.replace(".bas","") self.class_name = self.file_name.capitalize() self.functions = functions self.code = ClassFile(self.class_name, self.file_name) self.method_bytecode = b"" self.vars = {"args": 0}; def parse(self, _fileinput): self.AST = parse_to_AST(_fileinput) self._tree_walker(self.AST) def _tree_walker(self, tree): if type(tree) == list: for node in tree: self._tree_walker(node) if type(tree) == Symbol: for function in self.functions: if function.__name__ == tree.__name__: function(self, tree.what) self._tree_walker(tree.what) def _add_var(self, var_name): if var_name in self.vars: return self.vars[var_name] idx = len(self.vars) self.vars[var_name] = idx return idx def save(self): method_bytecode = b"\x2a\xb7" + struct.pack("!h", self.code.add_method_to_const_pool("java/lang/Object", "<init>", "()V") ) + b"\xb1" self.code.add_method(0x0000, "<init>", "()V", 1, 1, method_bytecode) self.method_bytecode += b"\xb1" # TODO: Keep track of stack size self.code.add_method(0x0009, "main", "([Ljava/lang/String;)V", 100, len(self.vars), self.method_bytecode) open(self.class_name + ".class", "wb").write(self.code.write_class())
class Parser(): constant_pool_tags = { 'CONSTANT_Utf8': 1, 'CONSTANT_Class': 7, 'CONSTANT_String': 8, 'CONSTANT_Fieldref': 9, 'CONSTANT_Methodref': 10, 'CONSTANT_NameAndType': 12 } def __init__(self): self.constant_parser = { self.constant_pool_tags['CONSTANT_Utf8']: lambda f: self.parse_utf8(f), self.constant_pool_tags['CONSTANT_Class']: lambda f: self.parse_class(f), self.constant_pool_tags['CONSTANT_String']: lambda f: self.parse_string(f), self.constant_pool_tags['CONSTANT_Fieldref']: lambda f: self.parse_field_ref(f), self.constant_pool_tags['CONSTANT_Methodref']: lambda f: self.parse_method_ref(f), self.constant_pool_tags['CONSTANT_NameAndType']: lambda f: self.parse_name_and_type(f) } self.attribute_parser = { 'Code': lambda f: self.parse_code(f), 'LineNumberTable': lambda f: self.parse_line_number_table(f), 'SourceFile': lambda f: self.parse_source_file(f) } def parse(self, filename): self.class_file = ClassFile() with open(filename, "rb") as f: self.class_file.magic = f.read(4) self.class_file.minor_version = convert_bytes_to_int(f.read(2)) self.class_file.major_versio = convert_bytes_to_int(f.read(2)) self.class_file.constant_pool_count = convert_bytes_to_int( f.read(2)) for _ in range(self.class_file.constant_pool_count - 1): self.class_file.add_constant_pool(self.parse_constant_pool(f)) self.class_file.access_flags = convert_bytes_to_int(f.read(2)) self.class_file.this_class = convert_bytes_to_int(f.read(2)) self.class_file.super_class = convert_bytes_to_int(f.read(2)) self.class_file.interfaces_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.interfaces_count): self.class_file.add_interfaces(int.from_bytes(f.read(2))) self.class_file.fields_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.fields_count): self.class_file.add_field(self.parse_field()) self.class_file.methods_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.methods_count): self.class_file.add_method(self.parse_methods(f)) self.class_file.attributes_count = convert_bytes_to_int(f.read(2)) for _ in range(self.class_file.attributes_count): self.class_file.add_attribute(self.parse_attributes(f)) return self.class_file def parse_constant_pool(self, file_object): tag = convert_bytes_to_int(file_object.read(1)) return self.constant_parser[tag](file_object) def parse_utf8(self, file_object): length = convert_bytes_to_int(file_object.read(2)) bytes_list = [file_object.read(1) for _ in range(length)] return { 'tag': self.constant_pool_tags['CONSTANT_Utf8'], 'length': length, 'bytes': bytes_list } def parse_class(self, file_object): return { 'tag': self.constant_pool_tags['CONSTANT_Class'], 'name_index': convert_bytes_to_int(file_object.read(2)) } def parse_string(self, file_object): return { 'tag': self.constant_pool_tags['CONSTANT_String'], 'string_index': convert_bytes_to_int(file_object.read(2)) } def parse_field_ref(self, file_object): return { 'tag': self.constant_pool_tags['CONSTANT_Fieldref'], 'class_index': convert_bytes_to_int(file_object.read(2)), 'name_and_type_index': convert_bytes_to_int(file_object.read(2)) } def parse_method_ref(self, file_object): return { 'tag': self.constant_pool_tags['CONSTANT_Methodref'], 'class_index': convert_bytes_to_int(file_object.read(2)), 'name_and_type_index': convert_bytes_to_int(file_object.read(2)) } def parse_name_and_type(self, file_object): return { 'tag': self.constant_pool_tags['CONSTANT_NameAndType'], 'name_index': convert_bytes_to_int(file_object.read(2)), 'descriptor_index': convert_bytes_to_int(file_object.read(2)) } def parse_field(self): # Not implemented because this isn't used in hello world pass def parse_methods(self, file_object): return { 'access_flags': convert_bytes_to_int(file_object.read(2)), 'name_index': convert_bytes_to_int(file_object.read(2)), 'descriptor_index': convert_bytes_to_int(file_object.read(2)), 'attributes_count': convert_bytes_to_int(file_object.read(2)), 'attributes': self.parse_attributes(file_object) } def parse_attributes(self, file_object): attribute_name_index = convert_bytes_to_int(file_object.read(2)) attribute_length = convert_bytes_to_int(file_object.read(4)) attribute_name = self.class_file.constant_pool[attribute_name_index][ 'bytes'] attribute_name = ''.join( map(lambda x: x.decode('UTF-8'), attribute_name)) attributes = { 'attribute_name_index': attribute_name_index, 'attribute_length': attribute_length, } attributes.update(self.attribute_parser[attribute_name](file_object)) return attributes def parse_code(self, file_object): max_stack = convert_bytes_to_int(file_object.read(2)) max_locals = convert_bytes_to_int(file_object.read(2)) code_length = convert_bytes_to_int(file_object.read(4)) codes = [file_object.read(1) for _ in range(code_length)] exception_table_length = convert_bytes_to_int(file_object.read(2)) exception_table = [ file_object.read(1) for _ in range(exception_table_length) ] attributes_count = convert_bytes_to_int(file_object.read(2)) attributes = [ self.parse_attributes(file_object) for _ in range(attributes_count) ] return { 'max_stack': max_stack, 'max_locals': max_locals, 'code_length': code_length, 'codes': codes, 'exception_table_length': exception_table_length, 'exception_table': exception_table, 'attributes_count': attributes_count, 'attributes': attributes } def parse_exception_table(self, file_object): return { 'start_pc': convert_bytes_to_int(file_object.read(2)), 'end_pc': convert_bytes_to_int(file_object.read(2)), 'handler_pc': convert_bytes_to_int(file_object.read(2)), 'catch_type': convert_bytes_to_int(file_object.read(2)) } def parse_line_number_table(self, file_object): line_number_table_length = convert_bytes_to_int(file_object.read(2)) line_number_table = [{ 'start_pc': convert_bytes_to_int(file_object.read(2)), 'line_number': convert_bytes_to_int(file_object.read(2)) } for _ in range(line_number_table_length)] return { 'line_number_table_length': line_number_table_length, 'line_number_table': line_number_table } def parse_source_file(self, file_object): return {'sourcefile_index': convert_bytes_to_int(file_object.read(2))}