1 package Analysis.SSJava;
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.Comparator;
6 import java.util.HashSet;
7 import java.util.Hashtable;
8 import java.util.Iterator;
11 import java.util.Vector;
13 import Analysis.Liveness;
14 import IR.AnnotationDescriptor;
15 import IR.ClassDescriptor;
16 import IR.MethodDescriptor;
19 import IR.SymbolTable;
20 import IR.TypeDescriptor;
21 import IR.VarDescriptor;
23 import IR.Flat.FlatMethod;
24 import IR.Flat.FlatNode;
25 import IR.Flat.FlatOpNode;
26 import IR.Flat.TempDescriptor;
27 import IR.Tree.ArrayAccessNode;
28 import IR.Tree.ArrayInitializerNode;
29 import IR.Tree.AssignmentNode;
30 import IR.Tree.BlockExpressionNode;
31 import IR.Tree.BlockNode;
32 import IR.Tree.BlockStatementNode;
33 import IR.Tree.CastNode;
34 import IR.Tree.CreateObjectNode;
35 import IR.Tree.DeclarationNode;
36 import IR.Tree.ExpressionNode;
37 import IR.Tree.FieldAccessNode;
38 import IR.Tree.IfStatementNode;
40 import IR.Tree.LoopNode;
41 import IR.Tree.MethodInvokeNode;
42 import IR.Tree.NameNode;
43 import IR.Tree.OffsetNode;
44 import IR.Tree.OpNode;
45 import IR.Tree.ReturnNode;
46 import IR.Tree.SubBlockNode;
47 import IR.Tree.SwitchBlockNode;
48 import IR.Tree.SwitchStatementNode;
49 import IR.Tree.SynchronizedNode;
50 import IR.Tree.TertiaryNode;
51 import IR.Tree.TreeNode;
53 public class LinearTypeCheck {
56 SSJavaAnalysis ssjava;
57 String needToNullify = null;
58 AssignmentNode prevAssignNode;
60 Set<TreeNode> linearTypeCheckSet;
62 Hashtable<TreeNode, FlatMethod> mapTreeNode2FlatMethod;
64 Set<MethodDescriptor> delegateThisMethodSet;
68 boolean deterministic = true;
70 public LinearTypeCheck(SSJavaAnalysis ssjava, State state) {
73 this.linearTypeCheckSet = new HashSet<TreeNode>();
74 this.mapTreeNode2FlatMethod = new Hashtable<TreeNode, FlatMethod>();
75 this.delegateThisMethodSet = new HashSet<MethodDescriptor>();
76 this.liveness = new Liveness();
79 public void linearTypeCheck() {
81 // first, parsing DELEGATE annotation from method declarations
82 Iterator it = state.getClassSymbolTable().getDescriptorsIterator();
83 while (it.hasNext()) {
84 ClassDescriptor cd = (ClassDescriptor) it.next();
85 for (Iterator method_it = cd.getMethods(); method_it.hasNext();) {
86 MethodDescriptor md = (MethodDescriptor) method_it.next();
91 // second, check the linear type
94 SymbolTable classtable = state.getClassSymbolTable();
96 List<ClassDescriptor> toanalyzeList = new ArrayList<ClassDescriptor>();
97 List<MethodDescriptor> toanalyzeMethodList = new ArrayList<MethodDescriptor>();
99 toanalyzeList.addAll(classtable.getValueSet());
100 Collections.sort(toanalyzeList, new Comparator<ClassDescriptor>() {
101 public int compare(ClassDescriptor o1, ClassDescriptor o2) {
102 return o1.getClassName().compareTo(o2.getClassName());
106 for (int i = 0; i < toanalyzeList.size(); i++) {
107 ClassDescriptor cd = toanalyzeList.get(i);
109 SymbolTable methodtable = cd.getMethodTable();
110 toanalyzeMethodList.clear();
111 toanalyzeMethodList.addAll(methodtable.getValueSet());
112 Collections.sort(toanalyzeMethodList, new Comparator<MethodDescriptor>() {
113 public int compare(MethodDescriptor o1, MethodDescriptor o2) {
114 return o1.getSymbol().compareTo(o2.getSymbol());
118 for (int mdIdx = 0; mdIdx < toanalyzeMethodList.size(); mdIdx++) {
119 MethodDescriptor md = toanalyzeMethodList.get(mdIdx);
120 checkMethodBody(cd, md);
126 it = state.getClassSymbolTable().getDescriptorsIterator();
127 while (it.hasNext()) {
128 ClassDescriptor cd = (ClassDescriptor) it.next();
129 for (Iterator method_it = cd.getMethods(); method_it.hasNext();) {
130 MethodDescriptor md = (MethodDescriptor) method_it.next();
131 checkMethodBody(cd, md);
136 // third, check if original references are destroyed after creating new
139 for (Iterator<TreeNode> iterator = linearTypeCheckSet.iterator(); iterator.hasNext();) {
140 TreeNode tn = iterator.next();
141 Set<FlatNode> fnSet = ssjava.getBuildFlat().getFlatNodeSet(tn);
143 for (Iterator iterator2 = fnSet.iterator(); iterator2.hasNext();) {
144 FlatNode fn = (FlatNode) iterator2.next();
145 if (isLiveOut(tn, fn)) {
149 + "', which is read by a method, should be destroyed after introducing new alias at "
150 + mapTreeNode2FlatMethod.get(tn).getMethod().getClassDesc().getSourceFileName()
151 + "::" + tn.getNumLine());
161 private boolean isLiveOut(TreeNode tn, FlatNode fn) {
162 Set<TempDescriptor> liveOutTemp = liveness.getLiveOutTemps(mapTreeNode2FlatMethod.get(tn), fn);
163 if (fn.kind() == FKind.FlatOpNode) {
164 FlatOpNode fon = (FlatOpNode) fn;
165 return liveOutTemp.contains(fon.getLeft());
170 private void parseAnnotations(MethodDescriptor md) {
172 // method annotation parsing
173 Vector<AnnotationDescriptor> methodAnnotations = md.getModifiers().getAnnotations();
174 if (methodAnnotations != null) {
175 for (int i = 0; i < methodAnnotations.size(); i++) {
176 AnnotationDescriptor an = methodAnnotations.elementAt(i);
177 if (an.getMarker().equals(ssjava.DELEGATETHIS)) {
178 delegateThisMethodSet.add(md);
179 md.getThis().getType().setExtension(new SSJavaType(true));
184 // paramter annotation parsing
185 for (int i = 0; i < md.numParameters(); i++) {
186 // process annotations on method parameters
187 VarDescriptor vd = (VarDescriptor) md.getParameter(i);
189 Vector<AnnotationDescriptor> annotationVec = vd.getType().getAnnotationMarkers();
191 for (int anIdx = 0; anIdx < annotationVec.size(); anIdx++) {
192 AnnotationDescriptor ad = annotationVec.elementAt(anIdx);
193 if (ad.getMarker().equals(SSJavaAnalysis.DELEGATE)) {
194 SSJavaType locationType = new SSJavaType(true);
195 vd.getType().setExtension(locationType);
201 private void checkMethodBody(ClassDescriptor cd, MethodDescriptor md) {
202 BlockNode bn = state.getMethodBody(md);
203 checkBlockNode(md, md.getParameterTable(), bn);
206 private void checkBlockNode(MethodDescriptor md, SymbolTable nametable, BlockNode bn) {
207 for (int i = 0; i < bn.size(); i++) {
208 BlockStatementNode bsn = bn.get(i);
209 checkBlockStatementNode(md, bn.getVarTable(), bsn);
213 private void checkBlockStatementNode(MethodDescriptor md, SymbolTable nametable,
214 BlockStatementNode bsn) {
216 if (needToNullify != null) {
217 if (!checkNullifying(bsn)) {
221 + "', which is read by a method, should be assigned to null before executing any following statement of the reference copy statement at "
222 + md.getClassDesc().getSourceFileName() + "::" + prevAssignNode.getNumLine());
226 switch (bsn.kind()) {
228 case Kind.BlockExpressionNode:
229 checkBlockExpressionNode(md, nametable, (BlockExpressionNode) bsn);
232 case Kind.DeclarationNode:
233 checkDeclarationNode(md, nametable, (DeclarationNode) bsn);
236 case Kind.IfStatementNode:
237 checkIfStatementNode(md, nametable, (IfStatementNode) bsn);
240 case Kind.SwitchStatementNode:
241 checkSwitchStatementNode(md, nametable, (SwitchStatementNode) bsn);
245 checkLoopNode(md, nametable, (LoopNode) bsn);
248 case Kind.ReturnNode:
249 checkReturnNode(md, nametable, (ReturnNode) bsn);
252 case Kind.SubBlockNode:
253 checkSubBlockNode(md, nametable, (SubBlockNode) bsn);
256 case Kind.SynchronizedNode:
257 checkSynchronizedNode(md, nametable, (SynchronizedNode) bsn);
263 private void checkSynchronizedNode(MethodDescriptor md, SymbolTable nametable,
264 SynchronizedNode sbn) {
265 checkBlockNode(md, nametable, sbn.getBlockNode());
266 // todo this could be Object
267 checkExpressionNode(md, nametable, sbn.getExpr());
270 private void checkReturnNode(MethodDescriptor md, SymbolTable nametable, ReturnNode rn) {
271 if (rn.getReturnExpression() != null) {
272 checkExpressionNode(md, nametable, rn.getReturnExpression());
276 private void checkSubBlockNode(MethodDescriptor md, SymbolTable nametable, SubBlockNode sbn) {
277 checkBlockNode(md, nametable, sbn.getBlockNode());
280 private void checkIfStatementNode(MethodDescriptor md, SymbolTable nametable, IfStatementNode isn) {
281 checkExpressionNode(md, nametable, isn.getCondition());
282 checkBlockNode(md, nametable, isn.getTrueBlock());
283 if (isn.getFalseBlock() != null)
284 checkBlockNode(md, nametable, isn.getFalseBlock());
287 private void checkSwitchStatementNode(MethodDescriptor md, SymbolTable nametable,
288 SwitchStatementNode ssn) {
290 checkExpressionNode(md, nametable, ssn.getCondition());
292 BlockNode sbn = ssn.getSwitchBody();
293 for (int i = 0; i < sbn.size(); i++) {
294 checkSwitchBlockNode(md, nametable, (SwitchBlockNode) sbn.get(i));
298 private void checkSwitchBlockNode(MethodDescriptor md, SymbolTable nametable, SwitchBlockNode sbn) {
299 checkBlockNode(md, nametable, sbn.getSwitchBlockStatement());
302 private void checkBlockExpressionNode(MethodDescriptor md, SymbolTable nametable,
303 BlockExpressionNode ben) {
304 checkExpressionNode(md, nametable, ben.getExpression());
307 private void checkExpressionNode(MethodDescriptor md, SymbolTable nametable, ExpressionNode en) {
309 case Kind.AssignmentNode:
310 checkAssignmentNode(md, nametable, (AssignmentNode) en);
314 checkCastNode(md, nametable, (CastNode) en);
317 case Kind.CreateObjectNode:
318 checkCreateObjectNode(md, nametable, (CreateObjectNode) en);
321 case Kind.FieldAccessNode:
322 checkFieldAccessNode(md, nametable, (FieldAccessNode) en);
325 case Kind.ArrayAccessNode:
326 checkArrayAccessNode(md, nametable, (ArrayAccessNode) en);
329 // case Kind.LiteralNode:
330 // checkLiteralNode(md, nametable, (LiteralNode) en);
333 case Kind.MethodInvokeNode:
334 checkMethodInvokeNode(md, nametable, (MethodInvokeNode) en);
338 checkNameNode(md, nametable, (NameNode) en);
342 checkOpNode(md, nametable, (OpNode) en);
345 case Kind.OffsetNode:
346 checkOffsetNode(md, nametable, (OffsetNode) en);
349 case Kind.TertiaryNode:
350 checkTertiaryNode(md, nametable, (TertiaryNode) en);
353 // case Kind.InstanceOfNode:
354 // checkInstanceOfNode(md, nametable, (InstanceOfNode) en);
357 // case Kind.ArrayInitializerNode:
358 // checkArrayInitializerNode(md, nametable, (ArrayInitializerNode) en);
361 // case Kind.ClassTypeNode:
362 // checkClassTypeNode(md, nametable, (ClassTypeNode) ens);
367 private void checkTertiaryNode(MethodDescriptor md, SymbolTable nametable, TertiaryNode en) {
368 // TODO Auto-generated method stub
372 private void checkOffsetNode(MethodDescriptor md, SymbolTable nametable, OffsetNode en) {
373 // TODO Auto-generated method stub
377 private void checkOpNode(MethodDescriptor md, SymbolTable nametable, OpNode en) {
378 // TODO Auto-generated method stub
382 private void checkNameNode(MethodDescriptor md, SymbolTable nametable, NameNode en) {
383 // TODO Auto-generated method stub
387 private boolean isOwned(VarDescriptor varDesc) {
388 if (varDesc.getType().getExtension() != null) {
389 SSJavaType locationType = (SSJavaType) varDesc.getType().getExtension();
390 return locationType.isOwned();
395 private void checkMethodInvokeNode(MethodDescriptor md, SymbolTable nametable,
396 MethodInvokeNode min) {
398 MethodDescriptor calleeMethodDesc = min.getMethod();
400 // check delegate_this annotation
401 // only method that owns itself 'THIS' can call method with delegate_this
404 if (delegateThisMethodSet.contains(calleeMethodDesc)) {
406 if (min.getBaseName() == null) {
407 if (!delegateThisMethodSet.contains(md)) {
408 throw new Error("Caller does not own the 'THIS' argument at " + md.getClassDesc() + "::"
412 VarDescriptor baseVar = (VarDescriptor) nametable.get(min.getBaseName().getIdentifier());
413 if (!isOwned(baseVar)) {
414 throw new Error("Caller does not own the 'THIS' argument at " + md.getClassDesc() + "::"
420 // check delegate parameter annotation
421 for (int i = 0; i < min.numArgs(); i++) {
422 ExpressionNode argNode = min.getArg(i);
424 TypeDescriptor paramType = calleeMethodDesc.getParamType(i);
426 if (isReference(argNode.getType())) {
428 boolean isParamOwnedByCallee = false;
429 if (paramType.getExtension() != null) {
430 SSJavaType locationType = (SSJavaType) paramType.getExtension();
431 isParamOwnedByCallee = locationType.isOwned();
434 TypeDescriptor argType = getTypeDescriptor(argNode);
436 if (isParamOwnedByCallee) {
438 // cannot pass field reference through ownership transition
439 if (isField(argNode)) {
440 throw new Error("Caller cannot transfer its ownership of the field reference at "
441 + md.getClassDesc() + "::" + min.getNumLine());
444 // method expects that argument is owned by caller
445 SSJavaType locationType = (SSJavaType) argType.getExtension();
447 if (locationType == null || !locationType.isOwned()) {
448 throw new Error("Caller passes an argument not owned by itself at " + md.getClassDesc()
449 + "::" + min.getNumLine());
459 private void checkArrayAccessNode(MethodDescriptor md, SymbolTable nametable, ArrayAccessNode en) {
460 // TODO Auto-generated method stub
464 private void checkFieldAccessNode(MethodDescriptor md, SymbolTable nametable, FieldAccessNode fan) {
468 private void checkCreateObjectNode(MethodDescriptor md, SymbolTable nametable,
469 CreateObjectNode con) {
471 TypeDescriptor[] tdarray = new TypeDescriptor[con.numArgs()];
472 for (int i = 0; i < con.numArgs(); i++) {
473 ExpressionNode en = con.getArg(i);
474 checkExpressionNode(md, nametable, en);
475 tdarray[i] = en.getType();
478 if ((con.getArrayInitializer() != null)) {
479 checkArrayInitializerNode(md, nametable, con.getArrayInitializer());
482 // the current method owns a instance that it makes inside
483 SSJavaType locationType = new SSJavaType(true);
484 con.getType().setExtension(locationType);
488 private void checkArrayInitializerNode(MethodDescriptor md, SymbolTable nametable,
489 ArrayInitializerNode arrayInitializer) {
490 // TODO Auto-generated method stub
494 private void checkCastNode(MethodDescriptor md, SymbolTable nametable, CastNode cn) {
495 ExpressionNode en = cn.getExpression();
496 checkExpressionNode(md, nametable, en);
499 private boolean checkNullifying(BlockStatementNode bsn) {
501 if (bsn.kind() == Kind.BlockExpressionNode) {
502 ExpressionNode en = ((BlockExpressionNode) bsn).getExpression();
503 if (en.kind() == Kind.AssignmentNode) {
504 AssignmentNode an = (AssignmentNode) en;
506 String destName = an.getDest().printNode(0);
507 if (destName.startsWith("this.")) {
508 destName = destName.substring(5);
511 if (an.getSrc().getType().isNull() && destName.equals(needToNullify)) {
512 needToNullify = null;
521 private void checkLoopNode(MethodDescriptor md, SymbolTable nametable, LoopNode ln) {
522 if (ln.getType() == LoopNode.WHILELOOP || ln.getType() == LoopNode.DOWHILELOOP) {
523 checkExpressionNode(md, nametable, ln.getCondition());
524 checkBlockNode(md, nametable, ln.getBody());
527 /* Link in the initializer naming environment */
528 BlockNode bn = ln.getInitializer();
529 for (int i = 0; i < bn.size(); i++) {
530 BlockStatementNode bsn = bn.get(i);
531 checkBlockStatementNode(md, bn.getVarTable(), bsn);
533 // check the condition
534 checkExpressionNode(md, bn.getVarTable(), ln.getCondition());
535 checkBlockNode(md, bn.getVarTable(), ln.getBody());
536 checkBlockNode(md, bn.getVarTable(), ln.getUpdate());
540 private void checkAssignmentNode(MethodDescriptor md, SymbolTable nametable, AssignmentNode an) {
542 boolean postinc = true;
543 if (an.getOperation().getBaseOp() == null
544 || (an.getOperation().getBaseOp().getOp() != Operation.POSTINC && an.getOperation()
545 .getBaseOp().getOp() != Operation.POSTDEC))
550 checkExpressionNode(md, nametable, an.getSrc());
552 if (isReference(an.getSrc().getType()) && isReference(an.getDest().getType())) {
554 if (an.getSrc().kind() == Kind.NameNode) {
556 NameNode nn = (NameNode) an.getSrc();
558 if (nn.getField() != null) {
559 needToNullify = nn.getField().getSymbol();
561 } else if (nn.getExpression() != null) {
562 if (nn.getExpression() instanceof FieldAccessNode) {
563 FieldAccessNode fan = (FieldAccessNode) nn.getExpression();
564 needToNullify = fan.printNode(0);
570 // local variable case
571 linearTypeCheckSet.add(an.getSrc());
572 mapTreeNode2FlatMethod.put(an.getSrc(), state.getMethodFlat(md));
574 } else if (an.getSrc().kind() == Kind.FieldAccessNode) {
575 FieldAccessNode fan = (FieldAccessNode) an.getSrc();
576 needToNullify = fan.printNode(0);
577 if (needToNullify.startsWith("this.")) {
578 needToNullify = needToNullify.substring(5);
581 } else if (an.getSrc().kind() == Kind.ArrayAccessNode) {
582 if (an.getSrc().getType().isPtr()) {
584 "Not allowed to create an alias to the middle of the multidimensional array at "
585 + md.getClassDesc().getSourceFileName() + "::" + an.getNumLine());
589 if (isCreatingAlias(an.getSrc())) {
591 TypeDescriptor srcType = getTypeDescriptor(an.getSrc());
592 boolean isSourceOwned = false;
594 if (srcType.getExtension() != null) {
595 SSJavaType srcLocationType = (SSJavaType) srcType.getExtension();
596 isSourceOwned = srcLocationType.isOwned();
599 if (!isField(an.getDest()) && isSourceOwned) {
600 // here, transfer ownership from LHS to RHS when it creates alias
601 TypeDescriptor destType = getTypeDescriptor(an.getDest());
602 destType.setExtension(new SSJavaType(isSourceOwned));
604 // if instance is not owned by the method, not able to store
605 // instance into field
606 if (!isSourceOwned) {
608 "Method is not allowed to store an instance not owned by itself into a field at "
609 + md.getClassDesc().getSourceFileName() + "::" + an.getNumLine());
621 private boolean isCreatingAlias(ExpressionNode en) {
623 int kind = en.kind();
624 if (kind == Kind.NameNode || kind == Kind.ArrayAccessNode || kind == Kind.FieldAccessNode) {
631 private TypeDescriptor getTypeDescriptor(ExpressionNode en) {
633 if (en.kind() == Kind.NameNode) {
634 NameNode nn = (NameNode) en;
635 if (nn.getField() != null) {
636 return nn.getVar().getType();
637 } else if (nn.getVar() != null) {
638 return nn.getVar().getType();
640 return getTypeDescriptor(nn.getExpression());
642 } else if (en.kind() == Kind.FieldAccessNode) {
643 FieldAccessNode fan = (FieldAccessNode) en;
644 return getTypeDescriptor(fan.getExpression());
645 } else if (en.kind() == Kind.CreateObjectNode) {
646 CreateObjectNode con = (CreateObjectNode) en;
647 return con.getType();
653 private boolean isField(ExpressionNode en) {
655 if (en.kind() == Kind.NameNode) {
656 NameNode nn = (NameNode) en;
657 if (nn.getField() != null) {
661 if (nn.getName() != null && nn.getName().getBase() != null) {
665 } else if (en.kind() == Kind.FieldAccessNode) {
671 private void checkDeclarationNode(MethodDescriptor md, SymbolTable nametable, DeclarationNode dn) {
672 if (dn.getExpression() != null) {
673 checkExpressionNode(md, nametable, dn.getExpression());
674 if (dn.getExpression().kind() == Kind.CreateObjectNode) {
675 dn.getVarDescriptor().getType().setExtension(new SSJavaType(true));
682 private boolean isReference(TypeDescriptor td) {