489 scan(tree.init);
490 if (isParamOrVar(tree.sym))
491 refs.replaceExprs(depth, direct -> new VarRef(tree.sym, direct));
492 else
493 refs.discardExprs(depth); // we don't track fields yet
494 }
495
496 //
497 // Visitor methods - Methods
498 //
499
500 @Override
501 public void visitMethodDef(JCMethodDecl tree) {
502 Assert.check(false); // we should never get here
503 }
504
505 @Override
506 public void visitApply(JCMethodInvocation invoke) {
507
508 // Get method symbol
509 MethodSymbol sym = (MethodSymbol)TreeInfo.symbolFor(invoke.meth);
510
511 // Recurse on method expression
512 scan(invoke.meth);
513 boolean direct = refs.remove(ExprRef.direct(depth));
514 boolean indirect = refs.remove(ExprRef.indirect(depth));
515
516 // Determine if method receiver represents a possible reference
517 RefSet<ThisRef> receiverRefs = RefSet.newEmpty();
518 if (sym != null && !sym.isStatic()) {
519 if (direct)
520 receiverRefs.add(ThisRef.direct());
521 if (indirect)
522 receiverRefs.add(ThisRef.indirect());
523 }
524
525 // If "super()": ignore - we don't try to track into superclasses
526 if (TreeInfo.name(invoke.meth) == names._super)
527 return;
528
529 // "Invoke" the method
530 invoke(invoke, sym, invoke.args, receiverRefs);
531 }
532
533 private void invoke(JCTree site, MethodSymbol sym, List<JCExpression> args, RefSet<?> receiverRefs) {
534
535 // Skip if ignoring warnings for a constructor invoked via 'this()'
536 if (suppressed.contains(sym))
537 return;
538
539 // Ignore final methods in java.lang.Object (getClass(), notify(), etc.)
540 if (sym != null &&
541 sym.owner.kind == TYP &&
542 sym.owner.type.tsym == syms.objectType.tsym &&
543 sym.isFinal()) {
544 return;
545 }
546
547 // Analyze method if possible, otherwise assume nothing
548 MethodInfo methodInfo = methodMap.get(sym);
549 if (methodInfo != null && methodInfo.invokable())
550 invokeInvokable(site, args, receiverRefs, methodInfo);
551 else
552 invokeUnknown(site, args, receiverRefs);
553 }
757 refs.remove(ExprRef.direct(depth));
758 boolean indirectRef = refs.remove(ExprRef.indirect(depth));
759 scan(tree.index);
760 refs.discardExprs(depth);
761 if (indirectRef) {
762 refs.add(ExprRef.direct(depth));
763 refs.add(ExprRef.indirect(depth));
764 }
765 }
766
767 @Override
768 public void visitSelect(JCFieldAccess tree) {
769
770 // Scan the selected thing
771 scan(tree.selected);
772 boolean selectedDirectRef = refs.remove(ExprRef.direct(depth));
773 boolean selectedIndirectRef = refs.remove(ExprRef.indirect(depth));
774
775 // Explicit 'this' reference?
776 Type.ClassType currentClassType = (Type.ClassType)methodClass.sym.type;
777 if (isExplicitThisReference(types, currentClassType, tree)) {
778 refs.mapInto(refs, ThisRef.class, direct -> new ExprRef(depth, direct));
779 return;
780 }
781
782 // Explicit outer 'this' reference?
783 Type selectedType = types.erasure(tree.selected.type);
784 if (selectedType.hasTag(CLASS)) {
785 ClassSymbol currentClassSym = (ClassSymbol)currentClassType.tsym;
786 ClassSymbol selectedTypeSym = (ClassSymbol)selectedType.tsym;
787 if (tree.name == names._this &&
788 selectedTypeSym != currentClassSym &&
789 currentClassSym.isEnclosedBy(selectedTypeSym)) {
790 refs.mapInto(refs, OuterRef.class, direct -> new ExprRef(depth, direct));
791 return;
792 }
793 }
794
795 // Methods - the "value" of a non-static method is a reference to its instance
796 Symbol sym = tree.sym;
797 if (sym.kind == MTH) {
1141 pendingWarning = callStack.toArray(new DiagnosticPosition[0]);
1142 callStack.pop();
1143 }
1144
1145 // Copy pending warning, if any, to the warning list and reset
1146 private boolean copyPendingWarning() {
1147 if (pendingWarning == null)
1148 return false;
1149 warningList.add(pendingWarning);
1150 pendingWarning = null;
1151 return true;
1152 }
1153
1154 // Does the symbol correspond to a parameter or local variable (not a field)?
1155 private boolean isParamOrVar(Symbol sym) {
1156 return sym != null &&
1157 sym.kind == VAR &&
1158 (sym.owner.kind == MTH || sym.owner.kind == VAR);
1159 }
1160
1161 /** Check if the given tree is an explicit reference to the 'this' instance of the
1162 * class currently being compiled. This is true if tree is:
1163 * - An unqualified 'this' identifier
1164 * - A 'super' identifier qualified by a class name whose type is 'currentClass' or a supertype
1165 * - A 'this' identifier qualified by a class name whose type is 'currentClass' or a supertype
1166 * but also NOT an enclosing outer class of 'currentClass'.
1167 */
1168 private boolean isExplicitThisReference(Types types, Type.ClassType currentClass, JCTree tree) {
1169 switch (tree.getTag()) {
1170 case PARENS:
1171 return isExplicitThisReference(types, currentClass, TreeInfo.skipParens(tree));
1172 case IDENT:
1173 {
1174 JCIdent ident = (JCIdent)tree;
1175 Names names = ident.name.table.names;
1176 return ident.name == names._this;
1177 }
1178 case SELECT:
1179 {
1180 JCFieldAccess select = (JCFieldAccess)tree;
1181 Type selectedType = types.erasure(select.selected.type);
1182 if (!selectedType.hasTag(CLASS))
1183 return false;
1184 ClassSymbol currentClassSym = (ClassSymbol)((Type.ClassType)types.erasure(currentClass)).tsym;
1185 ClassSymbol selectedClassSym = (ClassSymbol)((Type.ClassType)selectedType).tsym;
1186 Names names = select.name.table.names;
1187 return currentClassSym.isSubClass(selectedClassSym, types) &&
1188 (select.name == names._super ||
1189 (select.name == names._this &&
1190 (currentClassSym == selectedClassSym ||
1191 !currentClassSym.isEnclosedBy(selectedClassSym))));
1192 }
1193 default:
1194 return false;
1195 }
1196 }
1197
1198 // When scanning nodes we can be in one of two modes:
1199 // (a) Looking for constructors - we do not recurse into any code blocks
1200 // (b) Analyzing a constructor - we are tracing its possible execution paths
1201 private boolean isAnalyzing() {
1202 return targetClass != null;
1203 }
1204
1205 // Debugging
1206
1207 // Invariant checks
1208 private boolean checkInvariants(boolean analyzing, boolean allowExpr) {
1209 Assert.check(analyzing == isAnalyzing());
1210 if (isAnalyzing()) {
1211 Assert.check(methodClass != null);
1212 Assert.check(targetClass != null);
1213 Assert.check(refs != null);
1214 Assert.check(depth >= 0);
1215 Assert.check(refs.stream().noneMatch(ref -> ref.getDepth() > depth));
1216 Assert.check(allowExpr || !refs.contains(ExprRef.direct(depth)));
1217 Assert.check(allowExpr || !refs.contains(ExprRef.indirect(depth)));
|
489 scan(tree.init);
490 if (isParamOrVar(tree.sym))
491 refs.replaceExprs(depth, direct -> new VarRef(tree.sym, direct));
492 else
493 refs.discardExprs(depth); // we don't track fields yet
494 }
495
496 //
497 // Visitor methods - Methods
498 //
499
500 @Override
501 public void visitMethodDef(JCMethodDecl tree) {
502 Assert.check(false); // we should never get here
503 }
504
505 @Override
506 public void visitApply(JCMethodInvocation invoke) {
507
508 // Get method symbol
509 Symbol sym = TreeInfo.symbolFor(invoke.meth);
510
511 // Recurse on method expression
512 scan(invoke.meth);
513 boolean direct = refs.remove(ExprRef.direct(depth));
514 boolean indirect = refs.remove(ExprRef.indirect(depth));
515
516 // Determine if method receiver represents a possible reference
517 RefSet<ThisRef> receiverRefs = RefSet.newEmpty();
518 if (sym != null && !sym.isStatic()) {
519 if (direct)
520 receiverRefs.add(ThisRef.direct());
521 if (indirect)
522 receiverRefs.add(ThisRef.indirect());
523 }
524
525 // If "super()": ignore - we don't try to track into superclasses
526 if (TreeInfo.name(invoke.meth) == names._super)
527 return;
528
529 // "Invoke" the method
530 invoke(invoke, sym, invoke.args, receiverRefs);
531 }
532
533 private void invoke(JCTree site, Symbol sym, List<JCExpression> args, RefSet<?> receiverRefs) {
534
535 // Skip if ignoring warnings for a constructor invoked via 'this()'
536 if (suppressed.contains(sym))
537 return;
538
539 // Ignore final methods in java.lang.Object (getClass(), notify(), etc.)
540 if (sym != null &&
541 sym.owner.kind == TYP &&
542 sym.owner.type.tsym == syms.objectType.tsym &&
543 sym.isFinal()) {
544 return;
545 }
546
547 // Analyze method if possible, otherwise assume nothing
548 MethodInfo methodInfo = methodMap.get(sym);
549 if (methodInfo != null && methodInfo.invokable())
550 invokeInvokable(site, args, receiverRefs, methodInfo);
551 else
552 invokeUnknown(site, args, receiverRefs);
553 }
757 refs.remove(ExprRef.direct(depth));
758 boolean indirectRef = refs.remove(ExprRef.indirect(depth));
759 scan(tree.index);
760 refs.discardExprs(depth);
761 if (indirectRef) {
762 refs.add(ExprRef.direct(depth));
763 refs.add(ExprRef.indirect(depth));
764 }
765 }
766
767 @Override
768 public void visitSelect(JCFieldAccess tree) {
769
770 // Scan the selected thing
771 scan(tree.selected);
772 boolean selectedDirectRef = refs.remove(ExprRef.direct(depth));
773 boolean selectedIndirectRef = refs.remove(ExprRef.indirect(depth));
774
775 // Explicit 'this' reference?
776 Type.ClassType currentClassType = (Type.ClassType)methodClass.sym.type;
777 if (TreeInfo.isExplicitThisReference(types, currentClassType, tree)) {
778 refs.mapInto(refs, ThisRef.class, direct -> new ExprRef(depth, direct));
779 return;
780 }
781
782 // Explicit outer 'this' reference?
783 Type selectedType = types.erasure(tree.selected.type);
784 if (selectedType.hasTag(CLASS)) {
785 ClassSymbol currentClassSym = (ClassSymbol)currentClassType.tsym;
786 ClassSymbol selectedTypeSym = (ClassSymbol)selectedType.tsym;
787 if (tree.name == names._this &&
788 selectedTypeSym != currentClassSym &&
789 currentClassSym.isEnclosedBy(selectedTypeSym)) {
790 refs.mapInto(refs, OuterRef.class, direct -> new ExprRef(depth, direct));
791 return;
792 }
793 }
794
795 // Methods - the "value" of a non-static method is a reference to its instance
796 Symbol sym = tree.sym;
797 if (sym.kind == MTH) {
1141 pendingWarning = callStack.toArray(new DiagnosticPosition[0]);
1142 callStack.pop();
1143 }
1144
1145 // Copy pending warning, if any, to the warning list and reset
1146 private boolean copyPendingWarning() {
1147 if (pendingWarning == null)
1148 return false;
1149 warningList.add(pendingWarning);
1150 pendingWarning = null;
1151 return true;
1152 }
1153
1154 // Does the symbol correspond to a parameter or local variable (not a field)?
1155 private boolean isParamOrVar(Symbol sym) {
1156 return sym != null &&
1157 sym.kind == VAR &&
1158 (sym.owner.kind == MTH || sym.owner.kind == VAR);
1159 }
1160
1161 // When scanning nodes we can be in one of two modes:
1162 // (a) Looking for constructors - we do not recurse into any code blocks
1163 // (b) Analyzing a constructor - we are tracing its possible execution paths
1164 private boolean isAnalyzing() {
1165 return targetClass != null;
1166 }
1167
1168 // Debugging
1169
1170 // Invariant checks
1171 private boolean checkInvariants(boolean analyzing, boolean allowExpr) {
1172 Assert.check(analyzing == isAnalyzing());
1173 if (isAnalyzing()) {
1174 Assert.check(methodClass != null);
1175 Assert.check(targetClass != null);
1176 Assert.check(refs != null);
1177 Assert.check(depth >= 0);
1178 Assert.check(refs.stream().noneMatch(ref -> ref.getDepth() > depth));
1179 Assert.check(allowExpr || !refs.contains(ExprRef.direct(depth)));
1180 Assert.check(allowExpr || !refs.contains(ExprRef.indirect(depth)));
|