def check_methods(tree): for method in tree.findall(".//method"): for modifier_list in method.findall(".//block//modifiers"): error_if(onein(["private", "public", "protected", "final"], modifiers(modifier_list)), "local variables cannot be private/public/protected/final.") for lvar in tree.findall(".//local_variable_declaration"): error_if(lvar.find(".//expression") is None and lvar.find( ".//array_initializer") is None, "Cannot have uninitialized local var declaration.") mods = modifiers(method) error_if("private" in mods, "private methods disallowed in joos.") error_if(allin(["static", "final"], mods), "#A static method cannot be final.") error_if("native" in mods and not "static" in mods, "#A native method must be static.") error_if("abstract" in mods and onein(["static", "final"], mods), "#An abstract method cannot be static or final.") if "abstract" in mods or "native" in mods: error_if(method.find(".//block") is not None, "#A method has a body if and only if it is neither " + "abstract nor native.") else: error_if(method.find(".//block") is None, "A method has a body if and only if it is neither " + "abstract nor native.") error_if(not onein(["public", "protected", "private"], mods), "A method cannot be package-private") error_if(tree.find(".//param//tok_final") is not None, "A formal parameter cannot be final.")
def typecheck_other_conditions(tree): """Takes an abstract syntax tree which has Environments associated with it and typechecks it. Raises JoosSyntaxException if the tree does not typecheck.""" # A constructor in a class other than java.lang.Object implicitly calls the # zero-argument constructor of its superclass. Check that this # zero-argument constructor exists. # # Check that the name of a constructor is the same as the name of its # enclosing class. for ctor in tree.findall(".//constructor_declaration"): clazz = ctor.env.findclass(ctor.get("name")) superclazz = clazz.superclass error_if(not superclazz.env.find_constructor(tuple([])), "No zero-argument constructor") for lhs in tree.findall(".//assignment/left_hand_side"): if lhs.find("./name") is None: continue type_name = name_to_str(lhs.find("./name")) type_decl = lhs.env.get_declaration_site_for_variable_name(type_name, lhs.find("./name")) error_if("final" in modifiers(type_decl), "Final field on LHS") #Check that no objects of abstract classes are created. for expr in tree.findall(".//class_instance_creation_expression"): expr_type = name_to_str(expr.find("./name")) instantiated_class = tree.env.findclass(expr_type) error_if("abstract" in modifiers(instantiated_class) or instantiated_class.tag == "interface", "Cannot instantiate abstract classes.") class_name = instantiated_class.get("canonical_name") ctor_decl = expr.env.get_declaration_site_for_constructor_name( class_name, argument_list(expr)) error_if(expr.env.find_package_name() != ctor_decl.env.find_package_name() and "protected" in modifiers(ctor_decl), "Invocation of Protected Constructor.") # Check that the implicit this variable is not accessed in a static method # or in the initializer of a static field. for smethod in \ [x for x in tree.findall(".//method") if "static" in modifiers(x)]: error_if(smethod.find(".//tok_this") is not None, "Cannot acces this in static method.") for field in \ [x for x in tree.findall(".//field") if "static" in modifiers(x)]: error_if(field.find(".//tok_this") is not None, "Cannot acces this in static field.") #if_then_stmts = tree.find(".//if_then_statement/expression//assignment") #error_if(if_then_stmts is not None, #"No assignment in if_then expressions") while_stmts = tree.find(".//while_statement/expression//assignment")
def check_classes(tree, filename): for clazz in tree.findall(".//class"): mods = modifiers(clazz) error_if(allin(["abstract", "final"], mods), "A class cannot be both abstract and final") error_if(not onein(["public", "protected", "private"], modifiers(clazz)), "A class cannot be package-private") name = clazz.get("name") error_if(name != filename, "Class must have same name as file.")
def field_access(subtree): subtree.slot = generate_new_stack_slot(subtree) subtree.memory_location = generate_new_stack_slot(subtree) if "static" in modifiers(subtree.declaration): mangled_name = mangle_field_name(subtree) subtree.assembly = """ ; static field access to {mangled_named} mov eax, {mangled_name} mov {memory_dst}, eax mov {value_dst}, DWORD [eax] """.format( mangled_name=mangled_name, memory_dst=stack_slot_to_operand(subtree.memory_location), value_dst=stack_slot_to_operand(subtree.slot), ) else: subtree.assembly = """ mov eax, {object_location} mov ebx, [eax + {field_offset}] mov {dest}, ebx add eax, {field_offset} mov {mem_loc}, eax """.format( object_location=stack_slot_to_operand(subtree[0].slot), field_offset=subtree.declaration.field_offset * 4, dest=stack_slot_to_operand(subtree.slot), mem_loc=stack_slot_to_operand(subtree.memory_location), )
def check_path_for_protected(self, path): if len(path) == 1: # Impossible to violate protected with a 1 length path return for x in range(0, len(path) - 1): previous_path_element = path[x] current_path_element = path[x + 1] if not "protected" in modifiers(current_path_element): continue if self.find_package_name() == \ current_path_element.env.find_package_name(): continue current_class_name = self.get_current_class_name() current_path_element_class_name = \ current_path_element.env.get_current_class_name() if previous_path_element.tag == "class": error_if(not self.is_subtype(current_class_name, current_path_element_class_name), "must be a subtype to access a proteced static member") else: previous_path_element_type = \ self.canonicalize_name(type_string(previous_path_element)) error_if(not self.is_subtype(previous_path_element_type, current_class_name), "must invoke on a subtype to access a protected member") error_if(not self.is_subtype(current_class_name, current_path_element_class_name), "Cannot invoke protected member in subtype")
def create_globl_section(trees): asm = "" for tree in trees: for method in tree.findall(".//method") + tree.findall(".//constructor_declaration"): asm += ".globl " + mangle_fn_name(method) for field in tree.findall(".//field"): if "static" in modifiers(field): asm += ".globl " + mangle_field_name(field)
def check_interfaces(tree, filename): for interface in tree.findall(".//interface"): name = interface.get("name") error_if(name != filename, "interface must have same name as file.") for method in interface.findall(".//abstract_method"): mods = modifiers(method) error_if(onein(["static", "final", "native"], mods), "An interface method cannot be static, final, or native.")
def check_fields(tree): for field_declaration in tree.findall(".//field"): mods = modifiers(field_declaration) error_if("private" in mods, "Fields cannot be private.") error_if("final" in mods and not field_declaration.find( "field_initializer"), "Final fields need a initializer.") error_if(not onein(["public", "protected", "private"], mods), "A field cannot be package-private")
def push_arguments(subtree): pushes = [] if "static" in modifiers(subtree): return pushes if subtree.find("./argument_list") is not None: for expr in subtree.find("./argument_list"): pushes.insert(0, push_to_stack(stack_slot_to_operand(expr.slot))) return pushes
def add_method_from_parent_class(self, method): name = method.get("name") args = argument_list_for_declaration(method.env, method) if (name, args) in self.methods: # If this method is already defined error_if("final" in modifiers(method), "Cannot override final methods.") error_if("static" in modifiers(self.methods[name, args]), "Cannot override a method with a static methods.") error_if("static" in modifiers(method), "Cannot override a static method.") error_if("protected" in modifiers(self.methods[name, args]) and "public" in modifiers(method), "A protected method must not override a public method.") error_if((return_type(self.methods[name, args]) != return_type(method)), "An overriding method cannot have a different return " + "type from what it is overriding") else: error_if(is_abstract(method) and "abstract" not in modifiers(self.tree), "Must override abstract method.") self.methods[name, args] = method
def constructor_declaration(subtree): mangled_name = mangle_fn_name(subtree) constructor_body = subtree.find("constructor_body") error_if(constructor_body is None, "No constructor body") superclass = subtree.env.findclass("this").superclass superclass_mangled_name = mangle_class_name(superclass.get("canonical_name")) superclass_constructor = superclass_mangled_name + mangle_class_name(superclass.get("name")) fields = subtree.env.findclass("this").findall(".//field") field_initializers = "" for field in fields: if "static" not in modifiers(field) and field.find("expression") is not None: if not hasattr(field.find("expression"), "assembly"): generate(field.find("expression")) field_initializers += field.find("expression").assembly + "\n" field_initializers += """ ; field_initializer mov eax, DWORD [ebp + 8] ; this -> eax add eax, {field_location} mov ebx, {value} mov DWORD [eax], ebx ; end_field_initializer """.format( field_location=field.env.find_nonlocal(collect_token_text(field.find("variable"))).field_offset * 4, value=stack_slot_to_operand(field.find("expression").slot), ) chained_constructor_call = """ ;initialize fields {field_initializers} ; call superclass default constructor push DWORD [ebp + 8] call {superclass_constructor} """.format( superclass_constructor=superclass_constructor, field_initializers=field_initializers ) this_class = subtree.env.findclass("this") if this_class.get("canonical_name") == "java.lang.Object": chained_constructor_call = field_initializers subtree.assembly = method_declaration_asm( constructor_body.assembly, mangled_name, subtree, chained_constructor_call ) subtree.assembly += """ mov eax, {this_ptr} leave ret ; end constructor {name} """.format( this_ptr=stack_slot_to_operand(this), name=mangled_name )
def method_invocation(subtree): method = subtree.declaration subtree.slot = generate_new_stack_slot(subtree) pushes = push_arguments(subtree) if "static" not in modifiers(method): if subtree[0].tag == "name": name(subtree[0], args=argument_list(subtree)) pushes.append("push " + stack_slot_to_operand(subtree[0].slot)) return_slot = stack_slot_to_operand(subtree.slot) if "native" in modifiers(method): call = "call " + mangle_native_fn_name(subtree.declaration) argument_slot = subtree.find("./argument_list").slot operand = stack_slot_to_operand(argument_slot) pushes = ["mov eax, {slot}".format(slot=operand)] elif "static" in modifiers(method): call = "call " + mangle_fn_name(subtree.declaration) else: call = """ mov eax, {this_ptr} mov eax, [eax] add eax, {method_offset} mov eax, [eax] call eax """.format( this_ptr=stack_slot_to_operand(subtree[0].slot), method_offset=str(4 * method.mega_vtable_offset) ) subtree.assembly = """ ; method invocation {dbg} {push_arguments} {call} mov {return_slot}, eax ; end method invocation """.format( dbg=collect_debug_text(subtree), call=call, push_arguments="\n".join(pushes), return_slot=return_slot )
def add_method_from_parent_interface(self, method): name = method.get("name") args = argument_list_for_declaration(method.env, method) error_if((name, args) not in self.methods and "abstract" not in modifiers(self.tree) and "class" == self.tree.tag, "Must override interface methods") if ((name, args) in self.methods and "protected" in modifiers(self.methods[name, args]) and "public" in modifiers(method)): concrete_method = self.methods[name, args] if (concrete_method.find(".//block") is None and concrete_method not in self.tree.findall(".//method")): self.methods[name, args] = method else: error_if(True, "A protected method canot override a public method.") if (name, args) in self.methods: error_if((return_type(self.methods[name, args]) != return_type(method)), "An overriding method cannot have a different return " + "type from what it is overriding") else: self.methods[name, args] = method
def __superclass(self, clazz): """For a given 'class' subtree and environment, returns the 'class' referring to the class's supertype. Used to assign the superclass of a tree.""" if clazz.find(".//extends//name") is not None: c_name = name_to_str(clazz.find(".//extends//name")) if clazz.env.findclass(c_name) is not None: error_if(clazz.env.findclass(c_name).tag != "class", "Must not extend an interface class.") error_if(clazz == clazz.env.findclass(c_name), "Class cannot extend itself") error_if("final" in modifiers(clazz.env.findclass(c_name)), "Cannot extend a final class.") return clazz.env.findclass(c_name) else: error_if(True, "Must extend an existing class.") return clazz.env.findclass("java.lang.Object")
def check_types(tree): """Checks for type and other errors in tree.""" toks = tree.findall(".//package//tok_identifier") name = "" for ident in range(0, len(toks)): name += toks[ident].text error_if(name in tree.env.classes_fully_qualified and name.find(".") != -1, "Package prefix conflicts with class name.") if ident < len(toks) - 1: name += "." clazz = find_type_decl(tree) if clazz is None: return for _ in all_with_modifier(clazz, "method", "abstract"): error_if("abstract" not in modifiers(clazz), "Abstract methods must be in abstract classes.") for it in clazz.findall(".//implements/name") + clazz.findall( ".//extends/name") + clazz.findall( ".//extends_interfaces/name"): name = name_to_str(it) error_if(clazz.env.findclass(name) is None, "No class %s defined." % name) for type_use in clazz.findall(".//type") + clazz.findall( ".//class_instance_creation_expression/name"): name = "" toks = type_use.findall(".//tok_identifier") for ident in range(0, len(toks)): name += toks[ident].text if ident < len(toks) - 1: error_if(tree.env.findclass(name), "Prefixes of fully qualified type resolves to type.") name += "." if type_use.find(".//name") is not None: clazz_name = name_to_str(type_use.find(".//name")) error_if(not clazz.env.findclass(clazz_name), "Type " + clazz_name + " doesn't exist.") for i_type in clazz.findall(".//implements/name"): i_name = name_to_str(i_type) error_if(clazz.env.findclass(i_name) is not None and clazz.env.findclass(i_name).tag != "interface", "Must implement an interface class.") for c_type in clazz.findall(".//extends_interfaces/name"): c_name = name_to_str(c_type) error_if(clazz.env.findclass(c_name).tag != "interface", "Must extend an interface class.") error_if(clazz == clazz.env.findclass(c_name), "Class cannot extend itself") for inf in tree.findall(".//implements/name"): name = name_to_str(inf) error_if(clazz.env.findclass(name).tag != "interface", "%s is not an interface." % name) for clz in tree.findall(".//class_type/name") + tree.findall( ".//implements/name"): name = name_to_str(clz) error_if((tree.env.findclass(name) is None) or (tree.env.findclass( name).tag == "class"), "%s is not an interface." % name)
def field(subtree): if "static" in modifiers(subtree): for sub in subtree: if hasattr(sub, "slot"): subtree.slot = sub.slot return
def get_nonstatic_fields(clazz): env = clazz.env return [env[field_name] for field_name in env.fields if "static" not in modifiers(env[field_name])]
def static_declaration(declaration): return "static" in modifiers(declaration)
def check_and_generate(files): init_globals() # reset global state try: out = open("output/out.s", "w") trees = typechecker.check_files(files) initializer_list = [] mega_vtable_offset = 1 type_tag = 0 string_literal_index = 0 for tree in trees: clazz = find_type_decl(tree) clazz.type_tag = type_tag method_list.class_list += [clazz] type_tag += 1 methods = ( tree.findall(".//method") + tree.findall(".//constructor_declaration") + tree.findall(".//abstract_method") ) for method in methods: method.mega_vtable_offset = mega_vtable_offset mega_vtable_offset += 1 method_list.method_list += [method] if "static" in modifiers(method): slot = -2 else: slot = -3 for param in method.findall(".//param"): param.slot = slot slot -= 1 counters.mega_vtable_members = mega_vtable_offset for string_literal in tree.findall(".//string_literal"): string_literal.index = string_literal_index string_literal_index += 1 out.write(instanceof_table(trees)) for tree in trees: CurrentFile.name = tree.filename CurrentFile.mangled_name = CurrentFile.name.replace(".", "_").replace("$", "_").replace("/", "_") CurrentFile.static_slot = 0 for this in tree.findall(".//tok_this"): this.slot = -2 generate(tree) to_file(tree, tree.filename + ".xml") out.write("section .data\n") for clazz in tree.findall(".//class"): out.write(mega_vtable_for_class(clazz)) out.write("section .text\n") initializer_name = "static_init_" + CurrentFile.mangled_name out.write( """ ; global {initializer_name} {initializer_name}: push ebp mov ebp, esp sub esp, {frame_size} """.format( initializer_name=initializer_name, frame_size=CurrentFile.static_slot * 4 ) ) initializer_list.append(initializer_name) for field in tree.findall(".//field"): if "static" in modifiers(field): mangled_name = mangle_field_name(field) out.write( """ ; initializing {mangled_name} {field_assembly} mov eax, {mangled_name} mov ebx, {assigned_value} mov DWORD [eax], ebx """.format( mangled_name=mangled_name, field_assembly=field.assembly, assigned_value=stack_slot_to_operand(field.slot), ) ) out.write( """ leave ret ; done global static initialization """ ) for constructor in tree.findall(".//constructor_declaration"): out.write(constructor.assembly + "\n") for method in tree.findall(".//method"): if method.find("block") is not None: out.write(method.assembly + "\n") out.write("\nsection .data\n") for string_literal in tree.findall(".//string_literal"): string_value = string_literal.get("value") expanded_value = expand_string_literal(string_value) out.write("\n; string literal " + string_value + "\n") out.write("__string_literal_" + str(string_literal.index) + ":\n") out.write("dd _vtable_java_lang_$Array_ ; vtable pointer\n") out.write("dd -3 ; type tag for char\n") out.write("dd " + str(len(expanded_value)) + " ; string length\n") for character in expanded_value: out.write("dd " + str(hex(ord(character))) + "\n") out.write("") out.write("\nsection .bss\n") for field in tree.findall(".//field"): if "static" in modifiers(field): mangled_name = mangle_field_name(field) out.write("""{mangled_name} resb 4""".format(mangled_name=mangled_name) + "\n") out.write("section .text\n") out.write("; prelude\n") out.write("extern __malloc\n") out.write("extern __debexit\n") out.write("extern __exception\n") out.write("extern NATIVEjava.io.OutputStream.nativeWrite\n") out.write("global _start\n") out.write("_start:\n") out.write("call install_segv_handler\n") for initializer in initializer_list: out.write("call " + initializer + "\n") class_name = mangle_class_name(find_type_decl(trees[1]).get("canonical_name")) out.write("call " + class_name + "test_\n") out.write("mov ebx, eax\n") out.write("mov eax, 1\n") out.write("int 0x80 ; invoke exit(1)\n") out.write("; end prelude\n") out.write( """ %define __NR_signal 48 %define SIGSEGV 11 global install_segv_handler install_segv_handler: mov eax, __NR_signal mov ebx, SIGSEGV mov ecx, __exception int 0x80 ret """ ) return 0 except JoosSyntaxException, e: if Testing.testing: return 42 else: out.write("\n" + e.msg + "\n") raise