From 2aee38ffa2e958e61ee16b82bbba0ba26c82db80 Mon Sep 17 00:00:00 2001
From: Nicholas Robinson <nwrobins@edu.uwaterloo.ca>
Date: Wed, 4 Mar 2020 02:31:28 -0500
Subject: [PATCH] streamline contain set creation

- removed getContains function from individual class and interface nodes
- now getContains is only in ClassInterNode
- added a new helper function to catch any errors when trying to replace
a method
---
 TypeNodes.py | 166 ++++++++++++++++++++++++---------------------------
 1 file changed, 78 insertions(+), 88 deletions(-)

diff --git a/TypeNodes.py b/TypeNodes.py
index 7a08e80..eb392b7 100644
--- a/TypeNodes.py
+++ b/TypeNodes.py
@@ -29,6 +29,7 @@ class ClassInterNode(ASTNode):
                 unique.append(inter.name)
 
         # 7. A class or interface must not declare two methods with the same signature (name and parameter types).
+        # 9. A class/interface must not contain two methods with the same signature but different return types
         unique = []
         for method in self.methods:
             key = (method.name, method.paramTypes)
@@ -36,39 +37,69 @@ class ClassInterNode(ASTNode):
                 raise Exception("ERROR: Class/Interface '{}' declares 2 methods with the same signature '{}'".format(self.name, key[0]))
             unique.append(key)
 
-        # 6. The hierarchy must be acyclic
-        # 9. A class or interface must not contain (declare or inherit) two methods with the same signature but different return types
-        # 11. A nonstatic method must not replace a static method
-        # 13. A protected method must not replace a public method
         self.contains = self.getContains([])
 
-    def getContains(self, hierarchy):
-        # check if not acyclic
+        # for c in self.contains:
+        #     self.env.addToEnv(c)
 
+    # hierarchy: string[]
+    def getContains(self, hierarchy):
+        # ---- ACYCLIC CHECK ---- #
+        # 6. The hierarchy must be acyclic
         if self.canonName in hierarchy:
             raise Exception("ERROR: The hierarchy is not acyclic '{}', saw '{}'".format(hierarchy, self.canonName))
 
-        contains = []
+        # ---- GET SUPER CONTAINS ---- #
+        superContains = []
+        superInterContains = []
+        superClassContains = []
 
+        # parent interface methods
         for inter in self.superInter:
-            superContains = inter.getContains(hierarchy + [self.canonName])
-            for con in superContains:
-                conOverwritten = False
-                for method in self.methods:
-                    if (method == con):
-                        # cannot have same signiture but different return types
-                        if (method.methodType != con.methodType):
-                            raise Exception("ERROR: Class '{}' contains 2 methods '{}' with the same signature but different return types".format(self.name, method.name))
-                        # protected must not replace public
-                        if 'protected' in method.mods and 'public' in con.mods:
-                            raise Exception("ERROR: Protected method '{}' in class '{}' replaces public method '{}' in class {}".format(method.name, self.name, con.name, inter.name))
-                        # 14. A method must not replace a final method
-                        if 'final' in con.mods:
-                            raise Exception("ERROR: Final method '{}' in class '{}' can't be overrided by method '{}' in class {}".format(method.name, self.name, con.name, inter.name))
-                        conOverwritten = True
+            superInterContains.extend(inter.getContains(hierarchy + [self.canonName]))
+        
+        # parent class methods
+        if hasattr(self, 'superClass') and self.superClass:
+            superClassContains.extend(self.superClass.getContains(hierarchy + [self.canonName]))
+
+            # replace superInter methods that superClass implements
+            # this adds methods from superInterContains to superContains
+            # example: Tests/A2/J1_supermethod_override
+            for sic in superInterContains:
+                sicOverwritten = False
+                for scc in superClassContains:
+                    if sic == scc:
+                        safeReplace(sic, scc, self.name)
+                        sicOverwritten = True
                         break
-                if not conOverwritten:
-                    contains.append(con)
+                if not sicOverwritten:
+                    superContains.append(sic)
+            
+            superContains.extend(superClassContains)
+
+        # an interface without any super interfaces implicitly declares an abstract version of every public method in java.lang.Object
+        elif isinstance(self, InterNode) and not self.superInter:
+            objectInterface = self.env.getNode('java.lang.Object', 'type')
+            objectContains = objectInterface.getContains(hierarchy + [self.canonName])
+            for oc in objectContains:
+                if 'public' in oc.mods:
+                    superContains.append(oc)
+
+        elif superInterContains: # if no superClass and we do have superInterContains
+            superContains.extend(superInterContains)
+
+        # ---- SUPER CONTAINS AGAINST SELF METHODS ---- #
+        contains = []
+
+        for sc in superContains:
+            scOverwritten = False
+            for m in self.methods:
+                if sc == m:
+                    safeReplace(sc, m, self.name)
+                    scOverwritten = True
+                    break
+            if not scOverwritten:
+                contains.append(sc)
 
         contains.extend(self.methods)
         return contains
@@ -177,51 +208,16 @@ class ClassNode(ClassInterNode):
                 raise Exception("ERROR: Class '{}' declares 2 constructors with the same parameter types".format(self.name))
             unique.append(key)
 
-        # centralized point for overlapping class & interface logic. Also sets self.contains
+        # overlapping class/interface logic & sets self.contains
         super().checkHierarchy()
 
-        # 10. A class that contains (declares or inherits) any abstract methods must be abstract.
+        # 10. A class that contains any abstract methods must be abstract.
         for con in self.contains:
             if 'abstract' in con.mods and (not('abstract' in self.mods)):
                 raise Exception("ERROR: Non-abstract Class '{}' contains an abstract method".format(self.name))
             if (not con.body) and (not ('native' in con.mods)) and (not ('abstract' in self.mods)) and (not (con in self.constructors)):
                 raise Exception("ERROR: Non-abstract Class '{}' contains an abstract method {}".format(self.name, con.name))
 
-    # hierarchy: string[]
-    def getContains(self, hierarchy):
-        # centralized logic
-        contains = super().getContains(hierarchy)
-
-        # get contains from extends class
-        if self.superClass:
-            addToContains = []
-            superContains = self.superClass.getContains(hierarchy + [self.canonName])
-            for con in superContains:
-                conOverwritten = False
-                for index, method in enumerate(contains):
-                    if (method == con):
-                        # cannot have same signiture but different return types
-                        if (method.methodType != con.methodType):
-                            raise Exception("ERROR: Class '{}' contains 2 methods '{}' with the same signature but different return types".format(self.name, method.name))
-                        # must not replace final
-                        if 'final' in con.mods:
-                            raise Exception("ERROR: Method '{}' in class '{}' replaces final method '{}' in class '{}'".format(method.name, self.name, con.name, self.superClass.name))
-                        # nonstatic must not replace static
-                        if 'static' not in method.mods and 'static' in con.mods:
-                            raise Exception("ERROR: Non-static method '{}' in class '{}' replaces static method '{}' in class '{}'".format(method.name, self.name, con.name, self.superClass.name))
-                        # protected must not replace public
-                        if 'protected' in method.mods and 'public' in con.mods:
-                            raise Exception("ERROR: Protected method '{}' in class '{}' replaces public method '{}' in class {}".format(method.name, self.name, con.name, self.superClass.name))
-                        conOverwritten = True
-                        if not method.body:
-                            contains[index] = con
-                            conOverwritten = False
-                        break
-                if not conOverwritten:
-                    addToContains.append(con)
-            contains += addToContains
-        return contains
-
     def getConstructor(self, argTypes):
         for c in self.constructors:
             if c.paramTypes == argTypes:
@@ -290,30 +286,24 @@ class InterNode(ClassInterNode):
         # centralized point for overlapping class & interface logic. Also sets self.contains
         super().checkHierarchy()
 
-    # hierarchy: string[]
-    def getContains(self, hierarchy):
-        # centralized logic
-        contains = super().getContains(hierarchy)
-
-        # an interface without any super interfaces implicitly declares an abstract version of every public method in java.lang.Object
-        if not self.superInter:
-            addToContains = []
-            objectInterface = self.env.getNode('java.lang.Object', 'type')
-            superContains = objectInterface.getContains(hierarchy + [self.canonName])
-            for con in superContains:
-                conOverwritten = False
-                if 'public' in con.mods:
-                    for method in self.methods:
-                        if (method == con):
-                            # cannot have same signiture but different return types
-                            if (method.methodType != con.methodType):
-                                raise Exception("ERROR: Class '{}' contains 2 methods '{}' with the same signature but different return types".format(self.name, method.name))
-                            # protected must not replace public
-                            if 'protected' in method.mods and 'public' in con.mods:
-                                raise Exception("ERROR: Protected method '{}' in class '{}' replaces public method '{}' in class {}".format(method.name, self.name, con.name, inter.name))
-                            conOverwritten = True
-                            break
-                    if not conOverwritten:
-                        addToContains.append(con)
-            # contains += addToContains # this is causing very VERY WEIRD ERRORS I AM VERY FRUSTRATED, DO NOT UNCOMMENT THIS IF YOU WISH TO HAVE A GOOD TIME, UNCOMMENT AT YOUR PERIL
-        return contains
+# helper - replace method check
+# curMethod/newMethod: MethodNode
+def safeReplace(curMethod, newMethod, className):
+    # getting here signifies that curMethod and newMethod have the same signature
+
+    # 11. A nonstatic method must not replace a static method
+    if 'static' in curMethod.mods and 'static' not in newMethod.mods:
+        raise Exception("ERROR: Non-static method '{}' in class '{}' replaces static method '{}'".format(newMethod.name, className, curMethod.name))
+
+    # 9. A class/interface must not contain two methods with the same signature but different return types
+    # 12. A method must not replace a method with a different return type
+    if curMethod.methodType != newMethod.methodType:
+        raise Exception("ERROR: Method '{}' in class '{}' replaces a method with a different return type".format(className, curMethod.name))
+
+    # 13. A protected method must not replace a public method
+    if 'public' in curMethod.mods and 'protected' in newMethod.mods:
+        raise Exception("ERROR: Protected method '{}' in class '{}' replaces public method '{}'".format(newMethod.name, className, curMethod.name))
+    
+    # 14. A method must not replace a final method
+    if 'final' in curMethod.mods:
+        raise Exception("ERROR: Method '{}' in class '{}' replaces final method '{}'".format(curMethod.name, className, curMethod.name))
-- 
GitLab