예제 #1
0
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"))
예제 #2
0
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")
예제 #3
0
    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
예제 #4
0
    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]
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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
예제 #8
0
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)
예제 #9
0
 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)
예제 #10
0
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, [])
예제 #11
0
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.")
예제 #12
0
 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")
예제 #13
0
    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)
예제 #14
0
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
예제 #15
0
 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)
예제 #16
0
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
예제 #17
0
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
예제 #18
0
    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.")
예제 #19
0
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
예제 #20
0
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)
예제 #21
0
def class_instance_creation(element):
    name = name_to_str(element.find("name"))
    element.attrib["type"] = element.env.canonicalize_name(name)
    resolve_constructor_name(element)
예제 #22
0
def resolve_constructor_name(element):
    return element.env.get_declaration_site_for_constructor_name(
            name_to_str(element.find("name")),
            argument_list(element))
예제 #23
0
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])
예제 #24
0
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),
        )
예제 #25
0
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