def name_element(tree, tag, canonicalize=False): """Gives elements a name= attribute as their name. Searches for tags with the specified tag name. Takes the first identifier under the tag and uses the text of that element as the name= atribute of the tag. Arguments: tree: The parse tree to modify tag: The name of the tag to name specified as a string""" for tag in tree.findall(".//" + tag): found = tag.find("tok_identifier") if found is None: found = tag.find("./simple_name") name = util.name_to_str(found) else: name = found.text tag.set("name", name) tag.remove(found) if canonicalize: if tree.find("package_declaration") is None: tag.set("canonical_name", tag.get("name")) else: tag.set("canonical_name", util.name_to_str(tree.find("package_declaration")) + "." + tag.get("name"))
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 initialize_class_or_interface_environment(self, tree): self.fields["this"] = self.tree self.add_field_declarations_to_environment(tree) self.add_constructor_declarations_to_environments(tree) self.add_method_declarations_to_environment(tree) implemented_interface_names = [self.findclass(name_to_str(x)) for x in tree.findall(".//implements/name")] extended_interface_names = [self.findclass(name_to_str(x)) for x in tree.findall(".//extends_interfaces/name")] self.tree.interfaces = implemented_interface_names + \ extended_interface_names
def add_import_star_statements(self, imported, tree, trees): star_import_names = [name_to_str(x) for x in tree.findall(".//star_import/name")] star_import_names += ["java.lang"] for fullname in star_import_names: if fullname not in imported: imported += [fullname] tmp = find_all_in_package(fullname, trees) used = [name_to_str(x) for x in tree.findall(".//name")] + ["Object"] for key in tmp: if key not in self.classes_single: # and key in used: error_if(key in self.classes_star and key in used, "Ambiguous class %s" % key) self.classes_star[key] = tmp[key]
def type_tag_of_subtree(subtree, if_array_then_contents=False): if isarray(collect_token_text(subtree)): if if_array_then_contents: subtree = subtree[0] if subtree.tag == "primitive_type": return {"short": -1, "int": -2, "char": -3, "byte": -4, "boolean": -5}[collect_token_text(subtree)] return subtree.env.findclass(name_to_str(subtree)).type_tag
def check(files): try: trees = build_envs(files) #All statements must be reachable. Details of the exact definition of #reachability are specified in Section 14.20 of the Java Language #Specification. for tree in trees: for block in (tree.findall(".//method/block") + tree.findall(".//constructor_body")): check_reachability(block) for method in tree.findall(".//method"): if method.find(".//tok_void") is None: returns = always_returns(method.find("block")) error_if(not returns, "Doesn't always return from nonvoid method") #Every local variable must have an initializer, and the variable must #not occur in its own initializer. for tree in trees: for lvar in tree.findall(".//local_variable_declaration"): name = lvar.find(".//variable/tok_identifier").text error_if(lvar.find(".//expression") is None, "Every local variable must have an initializer") for ident in lvar.findall(".//expression//name"): error_if(name == name_to_str(ident), "Self cannot appear in local variable initializer.") return 0 except JoosSyntaxException, e: if not Testing.testing: print e.msg return 42
def method_invocation(element): if element[0].tag == "name": name = name_to_str(element.find("name")) to_file(element) declaration_site = element.env.get_declaration_site_for_method_name( name, argument_list(element)) element.attrib["type"] = element.env.get_type_for_declaration_site( declaration_site) element.declaration = declaration_site elif element[0].tag == "primary": primary_type = element[0].attrib["type"] error_if(is_primitive(primary_type), "Cannot invoke method on primitive " + primary_type) declaration_site = element.env.get_declaration_site_for_class_name( primary_type) identifier = element.find("tok_identifier").text method_declaration = \ declaration_site.env.get_declaration_site_for_method_name( identifier, argument_list(element)) element.attrib["type"] = \ method_declaration.env.get_type_for_declaration_site( method_declaration) element.declaration = method_declaration else: assert False
def check_field_initializers(tree): #Check the rules specified in Section 8.3.2.3 of the Java Language #Specification regarding forward references. The initializer of a #non-static field must not use (i.e. read) by simple name (i.e. without an #explicit this) itself or a non-static field declared later in the same #class. #The declaration of a member needs to appear before it is used only if the #member is an instance (respectively static) field of a class or interface #C and all of the following conditions hold: #The usage occurs in an instance (respectively static) variable initializer #of C or in an instance (respectively static) initializer of C. #The usage is not on the left hand side of an assignment. #C is the innermost class or interface enclosing the usage. fields = tree.findall(".//field") for x in xrange(len(fields)): field = fields[x] forward_references = fields[x:] forward_reference_names = [] for forward_reference in forward_references: forward_reference_names.append( name_to_str(forward_reference.find(".//variable"))) potential_elements = set() expression_element = field.find("expression") if expression_element is not None: for child in expression_element.findall(".//name"): child_name = name_to_str(child).split(".")[0] if child_name in forward_reference_names: potential_elements.add(child) valid_references = field.findall(".//left_hand_side/name") valid_references += \ field.findall(".//class_instance_creation_expression/name") for child in valid_references: for subchild in child.getiterator(): potential_elements.discard(subchild) error_if(len(potential_elements), "Forward reference not allowed in initializer.") if static_declaration(field): for name in field.findall(".//name"): if hasattr(child, "declaration") and isvariable(child.declaration): field.env.get_declaration_site_for_variable_name(name_to_str(name),name)
def add_single_type_import_statements(self, pkg, this_clazz, tree, trees): import_names = [name_to_str(x) for x in tree.findall(".//import/name")] for fullname in import_names: name = fullname[fullname.rfind(".") + 1:] error_if(name in self.classes_this and not fullname == pkg + "." + this_clazz.get("name"), "Single-type imports clash with class defined in file.") error_if(name in self.classes_single and not self.__findclass(fullname, trees) == self.classes_single[name], "Two single-type import decls clash with each other.") self.classes_single[name] = self.__findclass(fullname, trees)
def check_hierarchy(tree): clazz = tree.find(".//class") if not clazz: clazz = tree.find(".//interface") ifaces = [clazz.env.findclass(name_to_str(x)) for x in clazz.findall(".//implements/name")] for i in range(0, len(ifaces)): error_if(ifaces[i] in ifaces[i + 1:], "Mention an interface more than once") if clazz.tag == "interface": check_cyclic(clazz, [])
def array_access(element): if element[0].tag == "name": name = name_to_str(element[0]) decl_site = element.env.get_declaration_site_for_variable_name(name, element[0]) type_name = element.env.get_type_for_declaration_site(decl_site) element.declaration = decl_site else: type_name = element[0].attrib["type"] error_if(not isarray(type_name), "Array access on non-array type.") element.attrib["type"] = type_name[:-2] index_expression_type = element[-2].attrib["type"] error_if(not is_integral_primitive(index_expression_type), "Cannot index into array with non-integral expression.")
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 initialize_compilation_unit_environment(self, name_to_class, tree, trees): pkg = "" if tree.find(".//package//name"): pkg = name_to_str(tree.find(".//package//name")) self.package_name = pkg this_clazz = find_type_decl(tree) self.classes_this[this_clazz.get("name")] = this_clazz self.classes_this["this"] = this_clazz self.classes_package = find_all_in_package(pkg, trees) self.add_single_type_import_statements(pkg, this_clazz, tree, trees) imported = [] self.add_import_star_statements(imported, tree, trees) add_dict(find_all_in_package(pkg, trees), self.classes_package) add_dict(name_to_class, self.classes_fully_qualified)
def cast_expression(element): if element[1].tag == "name": expression_being_cast = element.find("unary_expression_not_plus_minus") cast_type = element.env.canonicalize_name(name_to_str(element[1])) if element.find("dims"): cast_type += "[]" elif element[1].tag == "expression": # Expression case cast_type = element[1].attrib["type"] expression_being_cast = element[-1] else: # Primitive cast case cast_type = collect_token_text(element[1]) if element[2].tag == "dims": cast_type += "[]" expression_being_cast = element[-1] error_if(not element.env.can_be_cast(expression_being_cast.attrib["type"], cast_type), "TODO(thurn): This is an error.") element.attrib["type"] = cast_type
def result(element): if len(element) == 1: if element[0].tag == "name" or element[0].tag == "qualified_name": name_string = name_to_str(element[0]) declaration_site = \ element.env.get_declaration_site_for_variable_name( name_string, element[0]) type_string = \ declaration_site.env.get_type_for_declaration_site( declaration_site) element.attrib["type"] = type_string element[0].attrib["type"] = type_string elif element[0].tag == "tok_this": element.attrib["type"] = element.env.canonicalize_name("this") element[0].declaration = element.env.findclass("this") else: element.attrib["type"] = element[0].attrib["type"] else: function(element)
def interface_methods(clazz): """Returns all methods declared in interfaces the class implements.""" methods = [] if clazz.tag == "class": iface_supers = clazz.interfaces else: iface_supers = [clazz] found_ifaces = [] while iface_supers: cur_clazz = iface_supers[0] error_if(cur_clazz is None, "Class not defined.") iface_supers = iface_supers[1:] if cur_clazz not in found_ifaces: methods += cur_clazz.findall(".//abstract_method") tmp = [cur_clazz.env.findclass(name_to_str(x)) for x in cur_clazz.findall(".//extends_interfaces/name")] iface_supers += tmp found_ifaces += [cur_clazz] return methods
def find_all_in_package(packagename, trees): """Returns all classses in packagename.""" global find_all_in_package_cache if packagename in find_all_in_package_cache: return find_all_in_package_cache[packagename] retn = {} prefix = False for tree in trees: clazz = find_type_decl(tree) if clazz is None: continue if tree.find(".//package/name") is not None: package_name = name_to_str(tree.find(".//package/name")) if package_name == packagename: retn[clazz.get("name")] = clazz elif (packagename == package_name[:len(packagename)] and package_name[len(packagename)] == "."): prefix = True elif packagename == "": retn[clazz.get("name")] = clazz error_if(not prefix and len(retn.keys()) == 0, "No class in %s" % packagename) find_all_in_package_cache[packagename] = retn return retn
def __findclass(self, fullname, trees): """Returns the 'class' object for the class. Fullname: fully qualified name of a class pkg: package to look in trees: set of ASTs to look in """ name = fullname[fullname.rfind(".") + 1:] pkg = fullname[:fullname.rfind(".")] for tree in trees: clazz = find_type_decl(tree) if clazz is None: continue if name == clazz.get("name"): package_name = "" if tree.find(".//package/name") is not None: package_name = name_to_str(tree.find(".//package/name")) if ((package_name == "" and fullname == name) or (package_name == "java.lang" and fullname == name) or pkg == package_name or fullname[:len(fullname) - len(name) - 1] == package_name): return clazz error_if(True, "No class to import / Class Not Found.")
def class_qualified_name_for_field(field): class_name = field.env.findclass("this").get("name") field_name = name_to_str(field.find(".//variable")) return class_name + "." + field_name
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 class_instance_creation(element): name = name_to_str(element.find("name")) element.attrib["type"] = element.env.canonicalize_name(name) resolve_constructor_name(element)
def resolve_constructor_name(element): return element.env.get_declaration_site_for_constructor_name( name_to_str(element.find("name")), argument_list(element))
def check_cyclic(iface, pifaces): error_if(iface in pifaces, "Cycle in interface") for eiface in [iface.env.findclass(name_to_str(x)) for x in iface.findall(".//extends_interfaces/name")]: check_cyclic(eiface, pifaces + [iface])
def instanceof_assembly(value_slot, destination_slot, type_subtree, debug_string, isarray=False): rhs_type = type_subtree.env.findclass(name_to_str(type_subtree)) label_no = new_label_no() zero_case = "EQUALITY_ZERO_" + str(label_no) one_case = "EQUALITY_ONE_" + str(label_no) final = "EQUALITY_FINAL_" + str(label_no) # TODO: arrays if rhs_type is None or isarray: return """ ;instanceof_array {dbg} mov eax, {lhs} sub eax, 0 jz .{false_case} mov ebx, [eax] mov ebx, [ebx] sub ebx, 0 jz .{array_case} jmp .{false_case} .{array_case}: mov eax, [eax + 4] sub eax, {type_tag_rhs} jnz .{false_case} mov {result}, {true} jmp .{final} .{false_case}: mov {result}, {false} .{final}: ;end instanceof """.format( lhs=stack_slot_to_operand(value_slot), dbg=debug_string, array_case=zero_case, false_case=one_case, type_tag_rhs=type_tag_of_subtree(type_subtree, True), result=stack_slot_to_operand(destination_slot), true=true, final=final, false=false, ) else: return """ ; instanceof {dbg} mov eax, {lhs} sub eax, 0 jz .{zero_case} ; check for null mov eax, [eax] ; deference to object mov eax, [eax] ; dereference to vable mov ecx, {number_of_classes} imul ecx add eax, {type_tag} mov eax, [__instanceof_table + eax] sub eax, -1 jz .{one_case} .{zero_case}: mov {result}, {false} jmp .{final_location} .{one_case}: mov {result}, {true} .{final_location}: """.format( dbg=debug_string, type_tag=str(rhs_type.type_tag * 4), lhs=stack_slot_to_operand(value_slot), zero_case=zero_case, one_case=one_case, result=stack_slot_to_operand(destination_slot), true=true, false=false, final_location=final, number_of_classes=str(len(method_list.class_list) * 4), )
def name(subtree, args=None): # TODO: pathing # subtree.slot = subtree.declaration.slot try: path = subtree.env.get_declaration_path_for_name(name_to_str(subtree), args) if not path: return subtree.assembly = "; name {dbg}\n".format(dbg=collect_debug_text(subtree)) if path[0].tag == "method" or path[0].tag == "abstract_method": subtree.slot = this if path[0].tag == "class": subtree.slot = generate_new_stack_slot(subtree) subtree.memory_location = generate_new_stack_slot(subtree) mangled_name = mangle_field_name(path[1]) subtree.assembly += """ mov eax, {mangled_name} mov ebx, DWORD [eax] mov {memory_dst}, eax mov {value_dst}, ebx """.format( mangled_name=mangled_name, memory_dst=stack_slot_to_operand(subtree.memory_location), value_dst=stack_slot_to_operand(subtree.slot), ) path = path[1:] elif path[0].tag == "field": subtree.slot = generate_new_stack_slot(subtree) subtree.memory_location = generate_new_stack_slot(subtree) subtree.assembly += """ mov eax, DWORD [ebp + 8] ; this -> eax mov ebx, [eax + {field_location}] add eax, {field_location} mov {mem_loc}, eax mov {dest}, ebx """.format( field_location=path[0].field_offset * 4, dest=stack_slot_to_operand(subtree.slot), mem_loc=stack_slot_to_operand(subtree.memory_location), ) elif path[0].tag == "local_variable_declaration" or path[0].tag == "param": if not hasattr(path[0], "slot"): return subtree.slot = generate_new_stack_slot(subtree) subtree.memory_location = generate_new_stack_slot(subtree) subtree.assembly += """ mov eax, {source} mov {dest}, eax mov ebx, ebp sub ebx, {source_stack_slot} mov {mem_loc}, ebx """.format( source=stack_slot_to_operand(path[0].slot), source_stack_slot=path[0].slot * 4, mem_loc=stack_slot_to_operand(subtree.memory_location), dest=stack_slot_to_operand(subtree.slot), ) for child in path[1:]: if child.tag in ["method", "abstract_method"]: return former_slot = subtree.slot subtree.slot = generate_new_stack_slot(subtree) subtree.memory_location = generate_new_stack_slot(subtree) if not hasattr(subtree, "assembly"): subtree.assembly = "" subtree.assembly += """ mov eax, {former_location} mov ebx, [eax + {field_location}] add eax, {field_location} mov {mem_loc}, eax mov {dest}, ebx """.format( former_location=stack_slot_to_operand(former_slot), field_location=child.field_offset * 4, dest=stack_slot_to_operand(subtree.slot), mem_loc=stack_slot_to_operand(subtree.memory_location), ) except JoosSyntaxException: pass