changes.
[IRC.git] / Robust / src / Analysis / SSJava / LinearTypeCheck.java
index 90aee1e700b5c7bc47f7e75e001b3e85d8975c6f..f6d8d35a60d75d57924d0e59cb264a3e40f199af 100644 (file)
@@ -1,38 +1,42 @@
 package Analysis.SSJava;
 
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
 import java.util.Vector;
 
+import Analysis.Liveness;
 import IR.AnnotationDescriptor;
 import IR.ClassDescriptor;
-import IR.Descriptor;
 import IR.MethodDescriptor;
+import IR.Operation;
 import IR.State;
 import IR.SymbolTable;
-import IR.TaskDescriptor;
 import IR.TypeDescriptor;
 import IR.VarDescriptor;
+import IR.Flat.FKind;
+import IR.Flat.FlatMethod;
+import IR.Flat.FlatNode;
+import IR.Flat.FlatOpNode;
+import IR.Flat.TempDescriptor;
 import IR.Tree.ArrayAccessNode;
 import IR.Tree.ArrayInitializerNode;
 import IR.Tree.AssignmentNode;
-import IR.Tree.AtomicNode;
 import IR.Tree.BlockExpressionNode;
 import IR.Tree.BlockNode;
 import IR.Tree.BlockStatementNode;
 import IR.Tree.CastNode;
-import IR.Tree.ClassTypeNode;
-import IR.Tree.ContinueBreakNode;
 import IR.Tree.CreateObjectNode;
 import IR.Tree.DeclarationNode;
 import IR.Tree.ExpressionNode;
 import IR.Tree.FieldAccessNode;
 import IR.Tree.IfStatementNode;
-import IR.Tree.InstanceOfNode;
 import IR.Tree.Kind;
-import IR.Tree.LiteralNode;
 import IR.Tree.LoopNode;
 import IR.Tree.MethodInvokeNode;
 import IR.Tree.NameNode;
@@ -41,25 +45,35 @@ import IR.Tree.OpNode;
 import IR.Tree.ReturnNode;
 import IR.Tree.SubBlockNode;
 import IR.Tree.SwitchBlockNode;
-import IR.Tree.SwitchLabelNode;
 import IR.Tree.SwitchStatementNode;
 import IR.Tree.SynchronizedNode;
-import IR.Tree.TagDeclarationNode;
-import IR.Tree.TaskExitNode;
 import IR.Tree.TertiaryNode;
+import IR.Tree.TreeNode;
 
 public class LinearTypeCheck {
 
   State state;
   SSJavaAnalysis ssjava;
   String needToNullify = null;
+  AssignmentNode prevAssignNode;
 
-  Hashtable<MethodDescriptor, Set<VarDescriptor>> md2DelegateParamSet;
+  Set<TreeNode> linearTypeCheckSet;
+
+  Hashtable<TreeNode, FlatMethod> mapTreeNode2FlatMethod;
+
+  Set<MethodDescriptor> delegateThisMethodSet;
+
+  Liveness liveness;
+
+  boolean deterministic = true;
 
   public LinearTypeCheck(SSJavaAnalysis ssjava, State state) {
     this.ssjava = ssjava;
     this.state = state;
-    md2DelegateParamSet = new Hashtable<MethodDescriptor, Set<VarDescriptor>>();
+    this.linearTypeCheckSet = new HashSet<TreeNode>();
+    this.mapTreeNode2FlatMethod = new Hashtable<TreeNode, FlatMethod>();
+    this.delegateThisMethodSet = new HashSet<MethodDescriptor>();
+    this.liveness = new Liveness();
   }
 
   public void linearTypeCheck() {
@@ -73,25 +87,101 @@ public class LinearTypeCheck {
         parseAnnotations(md);
       }
     }
-    System.out.println("###");
-    System.out.println("md2DelegateParamSet=" + md2DelegateParamSet);
 
     // second, check the linear type
-    it = state.getClassSymbolTable().getDescriptorsIterator();
-    while (it.hasNext()) {
-      ClassDescriptor cd = (ClassDescriptor) it.next();
-      for (Iterator method_it = cd.getMethods(); method_it.hasNext();) {
-        MethodDescriptor md = (MethodDescriptor) method_it.next();
-        if (ssjava.needTobeAnnotated(md)) {
+    if (deterministic) {
+
+      SymbolTable classtable = state.getClassSymbolTable();
+
+      List<ClassDescriptor> toanalyzeList = new ArrayList<ClassDescriptor>();
+      List<MethodDescriptor> toanalyzeMethodList = new ArrayList<MethodDescriptor>();
+
+      toanalyzeList.addAll(classtable.getValueSet());
+      Collections.sort(toanalyzeList, new Comparator<ClassDescriptor>() {
+        public int compare(ClassDescriptor o1, ClassDescriptor o2) {
+          return o1.getClassName().compareTo(o2.getClassName());
+        }
+      });
+
+      for (int i = 0; i < toanalyzeList.size(); i++) {
+        ClassDescriptor cd = toanalyzeList.get(i);
+
+        SymbolTable methodtable = cd.getMethodTable();
+        toanalyzeMethodList.clear();
+        toanalyzeMethodList.addAll(methodtable.getValueSet());
+        Collections.sort(toanalyzeMethodList, new Comparator<MethodDescriptor>() {
+          public int compare(MethodDescriptor o1, MethodDescriptor o2) {
+            return o1.getSymbol().compareTo(o2.getSymbol());
+          }
+        });
+
+        for (int mdIdx = 0; mdIdx < toanalyzeMethodList.size(); mdIdx++) {
+          MethodDescriptor md = toanalyzeMethodList.get(mdIdx);
           checkMethodBody(cd, md);
         }
+
+      }
+
+    } else {
+      it = state.getClassSymbolTable().getDescriptorsIterator();
+      while (it.hasNext()) {
+        ClassDescriptor cd = (ClassDescriptor) it.next();
+        for (Iterator method_it = cd.getMethods(); method_it.hasNext();) {
+          MethodDescriptor md = (MethodDescriptor) method_it.next();
+          checkMethodBody(cd, md);
+        }
+      }
+    }
+
+    // third, check if original references are destroyed after creating new
+    // alias
+
+    for (Iterator<TreeNode> iterator = linearTypeCheckSet.iterator(); iterator.hasNext();) {
+      TreeNode tn = iterator.next();
+      Set<FlatNode> fnSet = ssjava.getBuildFlat().getFlatNodeSet(tn);
+      if (fnSet != null) {
+        for (Iterator iterator2 = fnSet.iterator(); iterator2.hasNext();) {
+          FlatNode fn = (FlatNode) iterator2.next();
+          if (isLiveOut(tn, fn)) {
+            throw new Error(
+                "Local variable '"
+                    + tn.printNode(0)
+                    + "', which is read by a method, should be destroyed after introducing new alias at "
+                    + mapTreeNode2FlatMethod.get(tn).getMethod().getClassDesc().getSourceFileName()
+                    + "::" + tn.getNumLine());
+          }
+
+        }
       }
+
     }
 
   }
 
+  private boolean isLiveOut(TreeNode tn, FlatNode fn) {
+    Set<TempDescriptor> liveOutTemp = liveness.getLiveOutTemps(mapTreeNode2FlatMethod.get(tn), fn);
+    if (fn.kind() == FKind.FlatOpNode) {
+      FlatOpNode fon = (FlatOpNode) fn;
+      return liveOutTemp.contains(fon.getLeft());
+    }
+    return false;
+  }
+
   private void parseAnnotations(MethodDescriptor md) {
 
+    // method annotation parsing
+    Vector<AnnotationDescriptor> methodAnnotations = md.getModifiers().getAnnotations();
+    if (methodAnnotations != null) {
+      for (int i = 0; i < methodAnnotations.size(); i++) {
+        AnnotationDescriptor an = methodAnnotations.elementAt(i);
+        if (an.getMarker().equals(ssjava.DELEGATETHIS)) {
+          delegateThisMethodSet.add(md);
+          md.getThis().getType().setExtension(new SSJavaType(true));
+        }
+      }
+    }
+
+    // paramter annotation parsing
     for (int i = 0; i < md.numParameters(); i++) {
       // process annotations on method parameters
       VarDescriptor vd = (VarDescriptor) md.getParameter(i);
@@ -101,18 +191,11 @@ public class LinearTypeCheck {
       for (int anIdx = 0; anIdx < annotationVec.size(); anIdx++) {
         AnnotationDescriptor ad = annotationVec.elementAt(anIdx);
         if (ad.getMarker().equals(SSJavaAnalysis.DELEGATE)) {
-
-          Set<VarDescriptor> delegateSet = md2DelegateParamSet.get(md);
-          if (delegateSet == null) {
-            delegateSet = new HashSet<VarDescriptor>();
-            md2DelegateParamSet.put(md, delegateSet);
-          }
-          delegateSet.add(vd);
+          SSJavaType locationType = new SSJavaType(true);
+          vd.getType().setExtension(locationType);
         }
       }
-
     }
-
   }
 
   private void checkMethodBody(ClassDescriptor cd, MethodDescriptor md) {
@@ -133,8 +216,10 @@ public class LinearTypeCheck {
     if (needToNullify != null) {
       if (!checkNullifying(bsn)) {
         throw new Error(
-            "Reference field, which is read by a method, should be assigned to null before executing any following statement of the reference copy statement at "
-                + md.getClassDesc().getSourceFileName() + "::" + bsn.getNumLine());
+            "Reference field '"
+                + needToNullify
+                + "', which is read by a method, should be assigned to null before executing any following statement of the reference copy statement at "
+                + md.getClassDesc().getSourceFileName() + "::" + prevAssignNode.getNumLine());
       }
     }
 
@@ -173,7 +258,6 @@ public class LinearTypeCheck {
       return;
     }
 
-    throw new Error();
   }
 
   private void checkSynchronizedNode(MethodDescriptor md, SymbolTable nametable,
@@ -278,7 +362,6 @@ public class LinearTypeCheck {
       // checkClassTypeNode(md, nametable, (ClassTypeNode) ens);
       // return;
     }
-    throw new Error();
   }
 
   private void checkTertiaryNode(MethodDescriptor md, SymbolTable nametable, TertiaryNode en) {
@@ -301,8 +384,75 @@ public class LinearTypeCheck {
 
   }
 
-  private void checkMethodInvokeNode(MethodDescriptor md, SymbolTable nametable, MethodInvokeNode en) {
-    // TODO Auto-generated method stub
+  private boolean isOwned(VarDescriptor varDesc) {
+    if (varDesc.getType().getExtension() != null) {
+      SSJavaType locationType = (SSJavaType) varDesc.getType().getExtension();
+      return locationType.isOwned();
+    }
+    return false;
+  }
+
+  private void checkMethodInvokeNode(MethodDescriptor md, SymbolTable nametable,
+      MethodInvokeNode min) {
+
+    MethodDescriptor calleeMethodDesc = min.getMethod();
+
+    // check delegate_this annotation
+    // only method that owns itself 'THIS' can call method with delegate_this
+    // annotation
+
+    if (delegateThisMethodSet.contains(calleeMethodDesc)) {
+
+      if (min.getBaseName() == null) {
+        if (!delegateThisMethodSet.contains(md)) {
+          throw new Error("Caller does not own the 'THIS' argument at " + md.getClassDesc() + "::"
+              + min.getNumLine());
+        }
+      } else {
+        VarDescriptor baseVar = (VarDescriptor) nametable.get(min.getBaseName().getIdentifier());
+        if (!isOwned(baseVar)) {
+          throw new Error("Caller does not own the 'THIS' argument at " + md.getClassDesc() + "::"
+              + min.getNumLine());
+        }
+      }
+    }
+
+    // check delegate parameter annotation
+    for (int i = 0; i < min.numArgs(); i++) {
+      ExpressionNode argNode = min.getArg(i);
+
+      TypeDescriptor paramType = calleeMethodDesc.getParamType(i);
+
+      if (isReference(argNode.getType())) {
+
+        boolean isParamOwnedByCallee = false;
+        if (paramType.getExtension() != null) {
+          SSJavaType locationType = (SSJavaType) paramType.getExtension();
+          isParamOwnedByCallee = locationType.isOwned();
+        }
+
+        TypeDescriptor argType = getTypeDescriptor(argNode);
+
+        if (isParamOwnedByCallee) {
+
+          // cannot pass field reference through ownership transition
+          if (isField(argNode)) {
+            throw new Error("Caller cannot transfer its ownership of the field reference at "
+                + md.getClassDesc() + "::" + min.getNumLine());
+          }
+
+          // method expects that argument is owned by caller
+          SSJavaType locationType = (SSJavaType) argType.getExtension();
+
+          if (locationType == null || !locationType.isOwned()) {
+            throw new Error("Caller passes an argument not owned by itself at " + md.getClassDesc()
+                + "::" + min.getNumLine());
+          }
+
+        }
+
+      }
+    }
 
   }
 
@@ -317,6 +467,7 @@ public class LinearTypeCheck {
 
   private void checkCreateObjectNode(MethodDescriptor md, SymbolTable nametable,
       CreateObjectNode con) {
+
     TypeDescriptor[] tdarray = new TypeDescriptor[con.numArgs()];
     for (int i = 0; i < con.numArgs(); i++) {
       ExpressionNode en = con.getArg(i);
@@ -328,6 +479,10 @@ public class LinearTypeCheck {
       checkArrayInitializerNode(md, nametable, con.getArrayInitializer());
     }
 
+    // the current method owns a instance that it makes inside
+    SSJavaType locationType = new SSJavaType(true);
+    con.getType().setExtension(locationType);
+
   }
 
   private void checkArrayInitializerNode(MethodDescriptor md, SymbolTable nametable,
@@ -348,7 +503,12 @@ public class LinearTypeCheck {
       if (en.kind() == Kind.AssignmentNode) {
         AssignmentNode an = (AssignmentNode) en;
 
-        if (an.getSrc().getType().isNull() && an.getDest().printNode(0).equals(needToNullify)) {
+        String destName = an.getDest().printNode(0);
+        if (destName.startsWith("this.")) {
+          destName = destName.substring(5);
+        }
+
+        if (an.getSrc().getType().isNull() && destName.equals(needToNullify)) {
           needToNullify = null;
           return true;
         }
@@ -377,26 +537,153 @@ public class LinearTypeCheck {
     }
   }
 
-  private void checkAssignmentNode(Descriptor md, SymbolTable nametable, AssignmentNode an) {
-    needToNullify(an.getSrc());
+  private void checkAssignmentNode(MethodDescriptor md, SymbolTable nametable, AssignmentNode an) {
+
+    boolean postinc = true;
+    if (an.getOperation().getBaseOp() == null
+        || (an.getOperation().getBaseOp().getOp() != Operation.POSTINC && an.getOperation()
+            .getBaseOp().getOp() != Operation.POSTDEC))
+      postinc = false;
+
+    if (!postinc) {
+
+      checkExpressionNode(md, nametable, an.getSrc());
+
+      if (isReference(an.getSrc().getType()) && isReference(an.getDest().getType())) {
+
+        if (an.getSrc().kind() == Kind.NameNode) {
+
+          NameNode nn = (NameNode) an.getSrc();
+
+          if (nn.getField() != null) {
+            needToNullify = nn.getField().getSymbol();
+            prevAssignNode = an;
+          } else if (nn.getExpression() != null) {
+            if (nn.getExpression() instanceof FieldAccessNode) {
+              FieldAccessNode fan = (FieldAccessNode) nn.getExpression();
+              needToNullify = fan.printNode(0);
+              prevAssignNode = an;
+
+            }
+
+          } else {
+            // local variable case
+            linearTypeCheckSet.add(an.getSrc());
+            mapTreeNode2FlatMethod.put(an.getSrc(), state.getMethodFlat(md));
+          }
+        } else if (an.getSrc().kind() == Kind.FieldAccessNode) {
+          FieldAccessNode fan = (FieldAccessNode) an.getSrc();
+          needToNullify = fan.printNode(0);
+          if (needToNullify.startsWith("this.")) {
+            needToNullify = needToNullify.substring(5);
+          }
+          prevAssignNode = an;
+        } else if (an.getSrc().kind() == Kind.ArrayAccessNode) {
+          if (an.getSrc().getType().isPtr()) {
+            throw new Error(
+                "Not allowed to create an alias to the middle of the multidimensional array at "
+                    + md.getClassDesc().getSourceFileName() + "::" + an.getNumLine());
+          }
+        }
+
+        if (isCreatingAlias(an.getSrc())) {
+
+          TypeDescriptor srcType = getTypeDescriptor(an.getSrc());
+          boolean isSourceOwned = false;
+
+          if (srcType.getExtension() != null) {
+            SSJavaType srcLocationType = (SSJavaType) srcType.getExtension();
+            isSourceOwned = srcLocationType.isOwned();
+          }
+
+          if (!isField(an.getDest()) && isSourceOwned) {
+            // here, transfer ownership from LHS to RHS when it creates alias
+            TypeDescriptor destType = getTypeDescriptor(an.getDest());
+            destType.setExtension(new SSJavaType(isSourceOwned));
+          } else {
+            // if instance is not owned by the method, not able to store
+            // instance into field
+            if (!isSourceOwned) {
+              throw new Error(
+                  "Method is not allowed to store an instance not owned by itself into a field at "
+                      + md.getClassDesc().getSourceFileName() + "::" + an.getNumLine());
+            }
+          }
+
+        }
+
+      }
+
+    }
+
+  }
+
+  private boolean isCreatingAlias(ExpressionNode en) {
+
+    int kind = en.kind();
+    if (kind == Kind.NameNode || kind == Kind.ArrayAccessNode || kind == Kind.FieldAccessNode) {
+      return true;
+    }
+    return false;
+
   }
 
-  private void checkDeclarationNode(Descriptor md, SymbolTable nametable, DeclarationNode dn) {
-    needToNullify(dn.getExpression());
+  private TypeDescriptor getTypeDescriptor(ExpressionNode en) {
+
+    if (en.kind() == Kind.NameNode) {
+      NameNode nn = (NameNode) en;
+      if (nn.getField() != null) {
+        return nn.getVar().getType();
+      } else if (nn.getVar() != null) {
+        return nn.getVar().getType();
+      } else {
+        return getTypeDescriptor(nn.getExpression());
+      }
+    } else if (en.kind() == Kind.FieldAccessNode) {
+      FieldAccessNode fan = (FieldAccessNode) en;
+      return getTypeDescriptor(fan.getExpression());
+    } else if (en.kind() == Kind.CreateObjectNode) {
+      CreateObjectNode con = (CreateObjectNode) en;
+      return con.getType();
+    }
+
+    return null;
   }
 
-  private void needToNullify(ExpressionNode en) {
+  private boolean isField(ExpressionNode en) {
 
-    if (en != null && en.getType().isPtr() && !en.getType().isString()) {
-      if (en.kind() != Kind.CreateObjectNode && en.kind() != Kind.LiteralNode) {
-        if (en.kind() == Kind.CastNode) {
-          needToNullify = ((CastNode) en).getExpression().printNode(0);
-        } else {
-          needToNullify = en.printNode(0);
-        }
+    if (en.kind() == Kind.NameNode) {
+      NameNode nn = (NameNode) en;
+      if (nn.getField() != null) {
+        return true;
+      }
+
+      if (nn.getName() != null && nn.getName().getBase() != null) {
+        return true;
       }
+
+    } else if (en.kind() == Kind.FieldAccessNode) {
+      return true;
     }
+    return false;
+  }
 
+  private void checkDeclarationNode(MethodDescriptor md, SymbolTable nametable, DeclarationNode dn) {
+    if (dn.getExpression() != null) {
+      checkExpressionNode(md, nametable, dn.getExpression());
+      if (dn.getExpression().kind() == Kind.CreateObjectNode) {
+        dn.getVarDescriptor().getType().setExtension(new SSJavaType(true));
+      }
+
+    }
+
+  }
+
+  private boolean isReference(TypeDescriptor td) {
+    if (td.isPtr()) {
+      return true;
+    }
+    return false;
   }
 
 }