View Javadoc
1 /* 2 * ConstructorCallsOverridableMethodRule.java 3 * dnoyeb@users.sourceforge.net 4 * Created on February 5, 2003, 1:54 PM 5 */ 6 7 package net.sourceforge.pmd.rules; 8 9 import net.sourceforge.pmd.RuleContext; 10 import net.sourceforge.pmd.ast.ASTArguments; 11 import net.sourceforge.pmd.ast.ASTClassDeclaration; 12 import net.sourceforge.pmd.ast.ASTCompilationUnit; 13 import net.sourceforge.pmd.ast.ASTConstructorDeclaration; 14 import net.sourceforge.pmd.ast.ASTExplicitConstructorInvocation; 15 import net.sourceforge.pmd.ast.ASTInterfaceDeclaration; 16 import net.sourceforge.pmd.ast.ASTLiteral; 17 import net.sourceforge.pmd.ast.ASTMethodDeclarator; 18 import net.sourceforge.pmd.ast.ASTName; 19 import net.sourceforge.pmd.ast.ASTNestedClassDeclaration; 20 import net.sourceforge.pmd.ast.ASTNestedInterfaceDeclaration; 21 import net.sourceforge.pmd.ast.ASTPrimaryExpression; 22 import net.sourceforge.pmd.ast.ASTPrimaryPrefix; 23 import net.sourceforge.pmd.ast.ASTPrimarySuffix; 24 import net.sourceforge.pmd.ast.ASTUnmodifiedClassDeclaration; 25 import net.sourceforge.pmd.ast.AccessNode; 26 import net.sourceforge.pmd.ast.Node; 27 import net.sourceforge.pmd.ast.SimpleNode; 28 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 37 /*** 38 * Searches through all methods and constructors called from constructors. It 39 * marks as dangerous any call to overridable methods from non-private 40 * constructors. It marks as dangerous any calls to dangerous private constructors 41 * from non-private constructors. 42 * 43 * 44 * @todo match parameter types. Agressive strips off any package names. Normal 45 * compares the names as is. 46 * 47 * @todo What about interface declarations which can have internal classes 48 * 49 * @author CL Gilbert (dnoyeb@users.sourceforge.net) 50 */ 51 public final class ConstructorCallsOverridableMethodRule extends net.sourceforge.pmd.AbstractRule { 52 /*** 53 * 2: method(); 54 * ASTPrimaryPrefix 55 * ASTName image = "method" 56 * ASTPrimarySuffix 57 * *ASTArguments 58 * 3: a.method(); 59 * ASTPrimaryPrefix -> 60 * ASTName image = "a.method" ??? 61 * ASTPrimarySuffix -> () 62 * ASTArguments 63 * 3: this.method(); 64 * ASTPrimaryPrefix -> this image=null 65 * ASTPrimarySuffix -> method 66 * ASTPrimarySuffix -> () 67 * ASTArguments 68 * 69 * super.method(); 70 * ASTPrimaryPrefix -> image = "method" 71 * ASTPrimarySuffix -> image = null 72 * ASTArguments -> 73 * 74 * super.a.method(); 75 * ASTPrimaryPrefix -> image = "a" 76 * ASTPrimarySuffix -> image = "method" 77 * ASTPrimarySuffix -> image = null 78 * ASTArguments -> 79 80 * 81 * 4: this.a.method(); 82 * ASTPrimaryPrefix -> image = null 83 * ASTPrimarySuffix -> image = "a" 84 * ASTPrimarySuffix -> image = "method" 85 * ASTPrimarySuffix -> 86 * ASTArguments 87 * 88 * 4: ClassName.this.method(); 89 * ASTPrimaryPrefix 90 * ASTName image = "ClassName" 91 * ASTPrimarySuffix -> this image=null 92 * ASTPrimarySuffix -> image = "method" 93 * ASTPrimarySuffix -> () 94 * ASTArguments 95 * 5: ClassName.this.a.method(); 96 * ASTPrimaryPrefix 97 * ASTName image = "ClassName" 98 * ASTPrimarySuffix -> this image=null 99 * ASTPrimarySuffix -> image="a" 100 * ASTPrimarySuffix -> image="method" 101 * ASTPrimarySuffix -> () 102 * ASTArguments 103 * 5: Package.ClassName.this.method(); 104 * ASTPrimaryPrefix 105 * ASTName image ="Package.ClassName" 106 * ASTPrimarySuffix -> this image=null 107 * ASTPrimarySuffix -> image="method" 108 * ASTPrimarySuffix -> () 109 * ASTArguments 110 * 6: Package.ClassName.this.a.method(); 111 * ASTPrimaryPrefix 112 * ASTName image ="Package.ClassName" 113 * ASTPrimarySuffix -> this image=null 114 * ASTPrimarySuffix -> a 115 * ASTPrimarySuffix -> method 116 * ASTPrimarySuffix -> () 117 * ASTArguments 118 * 5: OuterClass.InnerClass.this.method(); 119 * ASTPrimaryPrefix 120 * ASTName image = "OuterClass.InnerClass" 121 * ASTPrimarySuffix -> this image=null 122 * ASTPrimarySuffix -> method 123 * ASTPrimarySuffix -> () 124 * ASTArguments 125 * 6: OuterClass.InnerClass.this.a.method(); 126 * ASTPrimaryPrefix 127 * ASTName image = "OuterClass.InnerClass" 128 * ASTPrimarySuffix -> this image=null 129 * ASTPrimarySuffix -> a 130 * ASTPrimarySuffix -> method 131 * ASTPrimarySuffix -> () 132 * ASTArguments 133 * 134 * OuterClass.InnerClass.this.a.method().method().method(); 135 * ASTPrimaryPrefix 136 * ASTName image = "OuterClass.InnerClass" 137 * ASTPrimarySuffix -> this image=null 138 * ASTPrimarySuffix -> a image='a' 139 * ASTPrimarySuffix -> method image='method' 140 * ASTPrimarySuffix -> () image=null 141 * ASTArguments 142 * ASTPrimarySuffix -> method image='method' 143 * ASTPrimarySuffix -> () image=null 144 * ASTArguments 145 * ASTPrimarySuffix -> method image='method' 146 * ASTPrimarySuffix -> () image=null 147 * ASTArguments 148 * 149 * 3..n: Class.InnerClass[0].InnerClass[n].this.method(); 150 * ASTPrimaryPrefix 151 * ASTName image = "Class[0]..InnerClass[n]" 152 * ASTPrimarySuffix -> image=null 153 * ASTPrimarySuffix -> method 154 * ASTPrimarySuffix -> () 155 * ASTArguments 156 * 157 * super.aMethod(); 158 * ASTPrimaryPrefix -> aMethod 159 * ASTPrimarySuffix -> () 160 * 161 * Evaluate right to left 162 * 163 */ 164 private static class MethodInvocation { 165 private String m_Name; 166 private ASTPrimaryExpression m_Ape; 167 private List m_ReferenceNames; 168 private List m_QualifierNames; 169 private int m_ArgumentSize; 170 private boolean m_Super; 171 172 private MethodInvocation(ASTPrimaryExpression ape, List qualifierNames, List referenceNames, String name, int argumentSize, boolean superCall) { 173 m_Ape = ape; 174 m_QualifierNames = qualifierNames; 175 m_ReferenceNames = referenceNames; 176 m_Name = name; 177 m_ArgumentSize = argumentSize; 178 m_Super = superCall; 179 } 180 181 public boolean isSuper() { 182 return m_Super; 183 } 184 185 public String getName() { 186 return m_Name; 187 } 188 189 public int getArgumentCount() { 190 return m_ArgumentSize; 191 } 192 193 public List getReferenceNames() { 194 return m_ReferenceNames;//new ArrayList(variableNames); 195 } 196 197 public List getQualifierNames() { 198 return m_QualifierNames; 199 } 200 201 public ASTPrimaryExpression getASTPrimaryExpression() { 202 return m_Ape; 203 } 204 205 public static MethodInvocation getMethod(ASTPrimaryExpression node) { 206 MethodInvocation meth = null; 207 int i = node.jjtGetNumChildren(); 208 if (i > 1) {//should always be at least 2, probably can eliminate this check 209 //start at end which is guaranteed, work backwards 210 Node lastNode = node.jjtGetChild(i - 1); 211 if ((lastNode.jjtGetNumChildren() == 1) && (lastNode.jjtGetChild(0) instanceof ASTArguments)) { //could be ASTExpression for instance 'a[4] = 5'; 212 //start putting method together 213 // System.out.println("Putting method together now"); 214 List varNames = new ArrayList(); 215 List packagesAndClasses = new ArrayList(); //look in JLS for better name here; 216 String methodName = null; 217 ASTArguments args = (ASTArguments) lastNode.jjtGetChild(0); 218 int numOfArguments = args.getArgumentCount(); 219 boolean superFirst = false; 220 int thisIndex = -1; 221 222 FIND_SUPER_OR_THIS: { 223 //search all nodes except last for 'this' or 'super'. will be at: (node 0 | node 1 | nowhere) 224 //this is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments) 225 //this is an ASTPrimaryPrefix with a null image and an ASTName that has a null image 226 //super is an ASTPrimarySuffix with a null image and does not have child (which will be of type ASTArguments) 227 //super is an ASTPrimaryPrefix with a non-null image 228 for (int x = 0; x < i - 1; x++) { 229 Node child = node.jjtGetChild(x); 230 if (child instanceof ASTPrimarySuffix) { //check suffix type match 231 ASTPrimarySuffix child2 = (ASTPrimarySuffix) child; 232 // String name = getNameFromSuffix((ASTPrimarySuffix)child); 233 // System.out.println("found name suffix of : " + name); 234 if (child2.getImage() == null && child2.jjtGetNumChildren() == 0) { 235 thisIndex = x; 236 break; 237 } 238 //could be super, could be this. currently we cant tell difference so we miss super when 239 //XYZ.ClassName.super.method(); 240 //still works though. 241 } else if (child instanceof ASTPrimaryPrefix) { //check prefix type match 242 ASTPrimaryPrefix child2 = (ASTPrimaryPrefix) child; 243 if (getNameFromPrefix(child2) == null) { 244 if (child2.getImage() == null) { 245 thisIndex = x; 246 break; 247 } else {//happens when super is used [super.method(): image = 'method'] 248 superFirst = true; 249 thisIndex = x; 250 //the true super is at an unusable index because super.method() has only 2 nodes [method=0,()=1] 251 //as opposed to the 3 you might expect and which this.method() actually has. [this=0,method=1.()=2] 252 break; 253 } 254 } 255 } 256 // else{ 257 // System.err.println("Bad Format error"); //throw exception, quit evaluating this compilation node 258 // } 259 } 260 } 261 262 if (thisIndex != -1) { 263 // System.out.println("Found this or super: " + thisIndex); 264 //Hack that must be removed if and when the patters of super.method() begins to logically match the rest of the patterns !!! 265 if (superFirst) { //this is when super is the first node of statement. no qualifiers, all variables or method 266 // System.out.println("super first"); 267 FIRSTNODE:{ 268 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); 269 String name = child.getImage();//special case 270 if (i == 2) { //last named node = method name 271 methodName = name; 272 } else { //not the last named node so its only var name 273 varNames.add(name); 274 } 275 } 276 OTHERNODES:{ //variables 277 for (int x = 1; x < i - 1; x++) { 278 Node child = node.jjtGetChild(x); 279 ASTPrimarySuffix ps = (ASTPrimarySuffix) child; 280 if (ps.isArguments() == false) { 281 String name = ((ASTPrimarySuffix) child).getImage(); 282 if (x == i - 2) {//last node 283 methodName = name; 284 } else {//not the last named node so its only var name 285 varNames.add(name); 286 } 287 } 288 } 289 } 290 } else {//not super call 291 FIRSTNODE:{ 292 if (thisIndex == 1) {//qualifiers in node 0 293 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); 294 String toParse = getNameFromPrefix(child); 295 // System.out.println("parsing for class/package names in : " + toParse); 296 java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, "."); 297 while (st.hasMoreTokens()) { 298 packagesAndClasses.add(st.nextToken()); 299 } 300 } 301 } 302 OTHERNODES:{ //other methods called in this statement are grabbed here 303 //this is at 0, then no Qualifiers 304 //this is at 1, the node 0 contains qualifiers 305 for (int x = thisIndex + 1; x < i - 1; x++) {//everything after this is var name or method name 306 ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x); 307 if (child.isArguments() == false) { //skip the () of method calls 308 String name = getNameFromSuffix(child); 309 // System.out.println("Found suffix: " + suffixName); 310 if (x == i - 2) { 311 methodName = name; 312 } else { 313 varNames.add(name); 314 } 315 } 316 } 317 } 318 } 319 } else { //if no this or super found, everything is method name or variable 320 //System.out.println("no this found:"); 321 FIRSTNODE:{ //variable names are in the prefix + the first method call [a.b.c.x()] 322 ASTPrimaryPrefix child = (ASTPrimaryPrefix) node.jjtGetChild(0); 323 String toParse = getNameFromPrefix(child); 324 // System.out.println("parsing for var names in : " + toParse); 325 java.util.StringTokenizer st = new java.util.StringTokenizer(toParse, "."); 326 while (st.hasMoreTokens()) { 327 String value = st.nextToken(); 328 if (!st.hasMoreTokens()) { 329 if (i == 2) {//if this expression is 2 nodes long, then the last part of prefix is method name 330 methodName = value; 331 } else { 332 varNames.add(value); 333 } 334 } else { //variable name 335 varNames.add(value); 336 } 337 } 338 } 339 OTHERNODES:{ //other methods called in this statement are grabbed here 340 for (int x = 1; x < i - 1; x++) { 341 ASTPrimarySuffix child = (ASTPrimarySuffix) node.jjtGetChild(x); 342 if (child.isArguments() == false) { 343 String name = getNameFromSuffix(child); 344 if (x == i - 2) { 345 methodName = name; 346 } else { 347 varNames.add(name); 348 } 349 } 350 } 351 } 352 } 353 meth = new MethodInvocation(node, packagesAndClasses, varNames, methodName, numOfArguments, superFirst); 354 } 355 } 356 return meth; 357 } 358 359 public void show() { 360 System.out.println("<MethodInvocation>"); 361 List pkg = getQualifierNames(); 362 System.out.println(" <Qualifiers>"); 363 for (Iterator it = pkg.iterator(); it.hasNext();) { 364 String name = (String) it.next(); 365 System.out.println(" " + name); 366 } 367 System.out.println(" </Qualifiers>"); 368 System.out.println(" <Super>" + isSuper() + "</Super>"); 369 List vars = getReferenceNames(); 370 System.out.println(" <References>"); 371 for (Iterator it = vars.iterator(); it.hasNext();) { 372 String name = (String) it.next(); 373 System.out.println(" " + name); 374 } 375 System.out.println(" </References>"); 376 System.out.println(" <Name>" + getName() + "</Name>"); 377 System.out.println("</MethodInvocation>"); 378 } 379 } 380 381 private final class ConstructorInvocation { 382 private ASTExplicitConstructorInvocation m_Eci; 383 private String name; 384 private int count = 0; 385 386 public ConstructorInvocation(ASTExplicitConstructorInvocation eci) { 387 m_Eci = eci; 388 List l = new ArrayList(); 389 eci.findChildrenOfType(ASTArguments.class, l); 390 if (l.size() > 0) { 391 ASTArguments aa = (ASTArguments) l.get(0); 392 count = aa.getArgumentCount(); 393 } 394 name = eci.getImage(); 395 } 396 397 public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() { 398 return m_Eci; 399 } 400 401 public int getArgumentCount() { 402 return count; 403 } 404 405 public String getName() { 406 return name; 407 } 408 } 409 410 private final class MethodHolder { 411 private ASTMethodDeclarator m_Amd; 412 private boolean m_Dangerous = false; 413 414 public MethodHolder(ASTMethodDeclarator amd) { 415 m_Amd = amd; 416 } 417 418 public ASTMethodDeclarator getASTMethodDeclarator() { 419 return m_Amd; 420 } 421 422 public boolean isDangerous() { 423 return m_Dangerous; 424 } 425 426 public void setDangerous(boolean dangerous) { 427 m_Dangerous = dangerous; 428 } 429 } 430 431 private final class ConstructorHolder { 432 private ASTConstructorDeclaration m_Cd; 433 private boolean m_Dangerous = false; 434 private ConstructorInvocation m_Ci; 435 private boolean m_CiInitialized = false; 436 437 public ConstructorHolder(ASTConstructorDeclaration cd) { 438 m_Cd = cd; 439 } 440 441 public ASTConstructorDeclaration getASTConstructorDeclaration() { 442 return m_Cd; 443 } 444 445 public ConstructorInvocation getCalledConstructor() { 446 if (m_CiInitialized == false) { 447 initCI(); 448 } 449 return m_Ci; 450 } 451 452 public ASTExplicitConstructorInvocation getASTExplicitConstructorInvocation() { 453 ASTExplicitConstructorInvocation eci = null; 454 if (m_CiInitialized == false) { 455 initCI(); 456 } 457 if (m_Ci != null) { 458 eci = m_Ci.getASTExplicitConstructorInvocation(); 459 } 460 return eci; 461 } 462 463 private void initCI() { 464 List expressions = new ArrayList(); 465 m_Cd.findChildrenOfType(ASTExplicitConstructorInvocation.class, expressions); //only 1... 466 if (expressions.size() > 0) { 467 ASTExplicitConstructorInvocation eci = (ASTExplicitConstructorInvocation) expressions.get(0); 468 m_Ci = new ConstructorInvocation(eci); 469 //System.out.println("Const call " + eci.getImage()); //super or this??? 470 } 471 m_CiInitialized = true; 472 } 473 474 public boolean isDangerous() { 475 return m_Dangerous; 476 } 477 478 public void setDangerous(boolean dangerous) { 479 m_Dangerous = dangerous; 480 } 481 } 482 483 /*** 484 * 1 package per class. holds info for evaluating a single class. 485 */ 486 private static class EvalPackage { 487 public EvalPackage() { 488 } 489 490 public EvalPackage(String className) { 491 m_ClassName = className; 492 calledMethods = new ArrayList();//meths called from constructor 493 allMethodsOfClass = new HashMap(); 494 calledConstructors = new ArrayList();//all constructors called from constructor 495 allPrivateConstructorsOfClass = new HashMap(); 496 } 497 498 public String m_ClassName; 499 public List calledMethods; 500 public Map allMethodsOfClass; 501 502 public List calledConstructors; 503 public Map allPrivateConstructorsOfClass; 504 } 505 506 private static final class NullEvalPackage extends EvalPackage { 507 public NullEvalPackage() { 508 m_ClassName = ""; 509 calledMethods = Collections.EMPTY_LIST; 510 allMethodsOfClass = Collections.EMPTY_MAP; 511 calledConstructors = Collections.EMPTY_LIST; 512 allPrivateConstructorsOfClass = Collections.EMPTY_MAP; 513 } 514 } 515 516 private static final NullEvalPackage nullEvalPackage = new NullEvalPackage(); 517 518 519 /*** 520 * 1 package per class. 521 */ 522 private final List evalPackages = new ArrayList();//could use java.util.Stack 523 524 private EvalPackage getCurrentEvalPackage() { 525 return (EvalPackage) evalPackages.get(evalPackages.size() - 1); 526 } 527 528 /*** 529 * Adds and evaluation package and makes it current 530 */ 531 private void putEvalPackage(EvalPackage ep) { 532 evalPackages.add(ep); 533 } 534 535 private void removeCurrentEvalPackage() { 536 evalPackages.remove(evalPackages.size() - 1); 537 } 538 539 private void clearEvalPackages() { 540 evalPackages.clear(); 541 } 542 543 /*** 544 * This check must be evaluated independelty for each class. Inner classses 545 * get their own EvalPackage in order to perform independent evaluation. 546 */ 547 private Object visitClassDec(AccessNode node, Object data) { 548 String className = ((ASTUnmodifiedClassDeclaration) node.jjtGetChild(0)).getImage(); 549 // System.out.println("Class is " + className); 550 //evaluate each level independently 551 if (!node.isFinal() && !node.isStatic()) { 552 putEvalPackage(new EvalPackage(className)); 553 } else { 554 putEvalPackage(nullEvalPackage); 555 } 556 //store any errors caught from other passes. 557 if (node instanceof ASTClassDeclaration) { 558 super.visit((ASTClassDeclaration) node, data); 559 } else { 560 super.visit((ASTNestedClassDeclaration) node, data); 561 } 562 //skip this class if it has no evaluation package 563 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) { 564 //evaluate danger of all methods in class 565 while (evaluateDangerOfMethods(getCurrentEvalPackage().allMethodsOfClass) == true) 566 ; 567 //evaluate danger of constructors 568 evaluateDangerOfConstructors1(getCurrentEvalPackage().allPrivateConstructorsOfClass, getCurrentEvalPackage().allMethodsOfClass.keySet()); 569 while (evaluateDangerOfConstructors2(getCurrentEvalPackage().allPrivateConstructorsOfClass) == true) 570 ; 571 572 //get each method called on this object from a non-private constructor, if its dangerous flag it 573 for (Iterator it = getCurrentEvalPackage().calledMethods.iterator(); it.hasNext();) { 574 MethodInvocation meth = (MethodInvocation) it.next(); 575 //check against each dangerous method in class 576 for (Iterator it2 = getCurrentEvalPackage().allMethodsOfClass.keySet().iterator(); it2.hasNext();) { 577 MethodHolder h = (MethodHolder) it2.next(); 578 if (h.isDangerous()) { 579 String methName = h.getASTMethodDeclarator().getImage(); 580 int count = h.getASTMethodDeclarator().getParameterCount(); 581 if (meth.getName().equals(methName) && (meth.getArgumentCount() == count)) { 582 //bad call 583 RuleContext ctx = (RuleContext) data; 584 ctx.getReport().addRuleViolation(createRuleViolation(ctx, meth.getASTPrimaryExpression().getBeginLine())); 585 } 586 } 587 } 588 } 589 //get each unsafe private constructor, and check if its called from any non private constructors 590 for (Iterator privConstIter = getCurrentEvalPackage().allPrivateConstructorsOfClass.keySet().iterator(); privConstIter.hasNext();) { 591 ConstructorHolder ch = (ConstructorHolder) privConstIter.next(); 592 if (ch.isDangerous()) { //if its dangerous check if its called from any non-private constructors 593 //System.out.println("visitClassDec Evaluating dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params"); 594 int paramCount = ch.getASTConstructorDeclaration().getParameterCount(); 595 for (Iterator calledConstIter = getCurrentEvalPackage().calledConstructors.iterator(); calledConstIter.hasNext();) { 596 ConstructorInvocation ci = (ConstructorInvocation) calledConstIter.next(); 597 if (ci.getArgumentCount() == paramCount) { 598 //match name super / this !? 599 RuleContext ctx = (RuleContext) data; 600 ctx.getReport().addRuleViolation(createRuleViolation(ctx, ci.getASTExplicitConstructorInvocation().getBeginLine())); 601 } 602 } 603 } 604 } 605 } 606 //finished evaluating this class, move up a level 607 removeCurrentEvalPackage(); 608 return data; 609 } 610 611 /*** 612 * Check the methods called on this class by each of the methods on this 613 * class. If a method calls an unsafe method, mark the calling method as 614 * unsafe. This changes the list of unsafe methods which necessitates 615 * another pass. Keep passing until you make a clean pass in which no 616 * methods are changed to unsafe. 617 * For speed it is possible to limit the number of passes. 618 * 619 * Impossible to tell type of arguments to method, so forget method matching 620 * on types. just use name and num of arguments. will be some false hits, 621 * but oh well. 622 * 623 * @todo investigate limiting the number of passes through config. 624 */ 625 private boolean evaluateDangerOfMethods(Map classMethodMap) { 626 //check each method if it calls overridable method 627 boolean found = false; 628 for (Iterator methodsIter = classMethodMap.keySet().iterator(); methodsIter.hasNext();) { 629 MethodHolder h = (MethodHolder) methodsIter.next(); 630 List calledMeths = (List) classMethodMap.get(h); 631 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && (h.isDangerous() == false);) { 632 //if this method matches one of our dangerous methods, mark it dangerous 633 MethodInvocation meth = (MethodInvocation) calledMethsIter.next(); 634 //System.out.println("Called meth is " + meth); 635 for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) { //need to skip self here h == h3 636 MethodHolder h3 = (MethodHolder) innerMethsIter.next(); 637 if (h3.isDangerous()) { 638 String matchMethodName = h3.getASTMethodDeclarator().getImage(); 639 int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount(); 640 //System.out.println("matchint " + matchMethodName + " to " + methName); 641 if (matchMethodName.equals(meth.getName()) && (matchMethodParamCount == meth.getArgumentCount())) { 642 h.setDangerous(true); 643 found = true; 644 break; 645 } 646 } 647 } 648 } 649 } 650 return found; 651 } 652 653 /*** 654 * marks constructors dangerous if they call any dangerous methods 655 * Requires only a single pass as methods are already marked 656 * @todo optimize by having methods already evaluated somehow!? 657 */ 658 private void evaluateDangerOfConstructors1(Map classConstructorMap, Set evaluatedMethods) { 659 //check each constructor in the class 660 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) { 661 ConstructorHolder ch = (ConstructorHolder) constIter.next(); 662 if (!ch.isDangerous()) {//if its not dangerous then evaluate if it should be 663 //if it calls dangerous method mark it as dangerous 664 List calledMeths = (List) classConstructorMap.get(ch); 665 //check each method it calls 666 for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && !ch.isDangerous();) {//but thee are diff objects which represent same thing but were never evaluated, they need reevaluation 667 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();//CCE 668 String methName = meth.getName(); 669 int methArgCount = meth.getArgumentCount(); 670 //check each of the already evaluated methods: need to optimize this out 671 for (Iterator evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) { 672 MethodHolder h = (MethodHolder) evaldMethsIter.next(); 673 if (h.isDangerous()) { 674 String matchName = h.getASTMethodDeclarator().getImage(); 675 int matchParamCount = h.getASTMethodDeclarator().getParameterCount(); 676 if (methName.equals(matchName) && (methArgCount == matchParamCount)) { 677 ch.setDangerous(true); 678 //System.out.println("evaluateDangerOfConstructors1 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params"); 679 break; 680 } 681 } 682 683 } 684 } 685 } 686 } 687 } 688 689 /*** 690 * Constructor map should contain a key for each private constructor, and 691 * maps to a List which contains all called constructors of that key. 692 * marks dangerous if call dangerous private constructor 693 * we ignore all non-private constructors here. That is, the map passed in 694 * should not contain any non-private constructors. 695 * we return boolean in order to limit the number of passes through this method 696 * but it seems as if we can forgo that and just process it till its done. 697 */ 698 private boolean evaluateDangerOfConstructors2(Map classConstructorMap) { 699 boolean found = false;//triggers on danger state change 700 //check each constructor in the class 701 for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) { 702 ConstructorHolder ch = (ConstructorHolder) constIter.next(); 703 ConstructorInvocation calledC = ch.getCalledConstructor(); 704 if (calledC == null || ch.isDangerous()) { 705 continue; 706 } 707 //if its not dangerous then evaluate if it should be 708 //if it calls dangerous constructor mark it as dangerous 709 int cCount = calledC.getArgumentCount(); 710 for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) { //forget skipping self because that introduces another check for each, but only 1 hit 711 ConstructorHolder h2 = (ConstructorHolder) innerConstIter.next(); 712 if (h2.isDangerous()) { 713 int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount(); 714 if (matchConstArgCount == cCount) { 715 ch.setDangerous(true); 716 found = true; 717 //System.out.println("evaluateDangerOfConstructors2 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params"); 718 } 719 } 720 } 721 } 722 return found; 723 } 724 725 //////////////////////////////////////////////////////////////////////////////// 726 //////////////////////////////////////////////////////////////////////////////// 727 //////////////////////////////////////////////////////////////////////////////// 728 //The Visited Methods 729 730 /*** 731 * Work on each file independently. 732 */ 733 public Object visit(ASTCompilationUnit node, Object data) { 734 clearEvalPackages(); 735 // try { 736 return super.visit(node, data); 737 // } 738 // catch(Exception e){ 739 // e.printStackTrace(); 740 // } 741 } 742 //for testing only 743 // public Object visit(ASTPackageDeclaration node, Object data){ 744 // System.out.println("package= " + ((ASTName)node.jjtGetChild(0)).getImage()); 745 // return super.visit(node,data); 746 // } 747 748 /*** 749 * This check must be evaluated independelty for each class. Inner classses 750 * get their own EvalPackage in order to perform independent evaluation. 751 */ 752 public Object visit(ASTClassDeclaration node, Object data) { 753 return visitClassDec(node, data); 754 } 755 756 public Object visit(ASTNestedClassDeclaration node, Object data) { 757 return visitClassDec(node, data); 758 } 759 760 public Object visit(ASTInterfaceDeclaration node, Object data) { 761 putEvalPackage(nullEvalPackage); 762 Object o = super.visit(node, data);//interface may have inner classes, possible? if not just skip whole interface 763 removeCurrentEvalPackage(); 764 return o; 765 } 766 767 public Object visit(ASTNestedInterfaceDeclaration node, Object data) { 768 putEvalPackage(nullEvalPackage); 769 Object o = super.visit(node, data);//interface may have inner classes, possible? if not just skip whole interface 770 removeCurrentEvalPackage(); 771 return o; 772 } 773 774 775 /*** 776 * Non-private constructor's methods are added to a list for later safety 777 * evaluation. Non-private constructor's calls on private constructors 778 * are added to a list for later safety evaluation. Private constructors 779 * are added to a list so their safety to be called can be later evaluated. 780 * 781 * Note: We are not checking private constructor's calls on non-private 782 * constructors because all non-private constructors will be evaluated for 783 * safety anyway. This means we wont flag a private constructor as unsafe 784 * just because it calls an unsafe public constructor. We want to show only 785 * 1 instance of an error, and this would be 2 instances of the same error. 786 * 787 * @todo eliminate the redundency 788 */ 789 public Object visit(ASTConstructorDeclaration node, Object data) { 790 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class 791 List calledMethodsOfConstructor = new ArrayList(); 792 ConstructorHolder ch = new ConstructorHolder(node); 793 addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName); 794 if (!node.isPrivate()) { 795 //these calledMethods are what we will evaluate for being called badly 796 getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor); 797 //these called private constructors are what we will evaluate for being called badly 798 //we add all constructors invoked by non-private constructors 799 //but we are only interested in the private ones. We just can't tell the difference here 800 ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation(); 801 if (eci != null && eci.isThis()) { 802 getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor()); 803 } 804 } else { 805 //add all private constructors to list for later evaluation on if they are safe to call from another constructor 806 //store this constructorHolder for later evaluation 807 getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor); 808 } 809 } 810 return super.visit(node, data); 811 } 812 813 /*** 814 * Create a MethodHolder to hold the method. 815 * Store the MethodHolder in the Map as the key 816 * Store each method called by the current method as a List in the Map as the Object 817 */ 818 public Object visit(ASTMethodDeclarator node, Object data) { 819 if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class 820 AccessNode parent = (AccessNode) node.jjtGetParent(); 821 MethodHolder h = new MethodHolder(node); 822 if (!parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) { 823 h.setDangerous(true);//this method is overridable 824 } 825 List l = new ArrayList(); 826 addCalledMethodsOfNode((SimpleNode) parent, l, getCurrentEvalPackage().m_ClassName); 827 getCurrentEvalPackage().allMethodsOfClass.put(h, l); 828 } 829 return super.visit(node, data); 830 } 831 832 833 834 //////////////////////////////////////////////////////////////////////////////// 835 //////////////////////////////////////////////////////////////////////////////// 836 //////////////////////////////////////////////////////////////////////////////// 837 //Helper methods to process visits 838 839 private static void addCalledMethodsOfNode(AccessNode node, List calledMethods, String className) { 840 List expressions = new ArrayList(); 841 node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false); 842 addCalledMethodsOfNodeImpl(expressions, calledMethods, className); 843 } 844 845 /*** 846 * Adds all methods called on this instance from within this Node. 847 */ 848 private static void addCalledMethodsOfNode(SimpleNode node, List calledMethods, String className) { 849 List expressions = new ArrayList(); 850 node.findChildrenOfType(ASTPrimaryExpression.class, expressions); 851 addCalledMethodsOfNodeImpl(expressions, calledMethods, className); 852 } 853 854 private static void addCalledMethodsOfNodeImpl(List expressions, List calledMethods, String className) { 855 for (Iterator it = expressions.iterator(); it.hasNext();) { 856 ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next(); 857 MethodInvocation meth = findMethod(ape, className); 858 if (meth != null) { 859 //System.out.println("Adding call " + methName); 860 calledMethods.add(meth); 861 } 862 } 863 } 864 865 /*** 866 * @todo Need a better way to match the class and package name to the actual 867 * method being called. 868 * @return A method call on the class passed in, or null if no method call 869 * is found. 870 */ 871 private static MethodInvocation findMethod(ASTPrimaryExpression node, String className) { 872 if (node.jjtGetNumChildren() > 0 873 && node.jjtGetChild(0).jjtGetNumChildren() > 0 874 && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral) { 875 return null; 876 } 877 MethodInvocation meth = MethodInvocation.getMethod(node); 878 boolean found = false; 879 // if(meth != null){ 880 // meth.show(); 881 // } 882 if (meth != null) { 883 //if it's a call on a variable, or on its superclass ignore it. 884 if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) { 885 //if this list does not contain our class name, then its not referencing our class 886 //this is a cheezy test... but it errs on the side of less false hits. 887 List packClass = meth.getQualifierNames(); 888 if (packClass.size() > 0) { 889 for (Iterator it = packClass.iterator(); it.hasNext();) { 890 String name = (String) it.next(); 891 if (name.equals(className)) { 892 found = true; 893 break; 894 } 895 } 896 } else { 897 found = true; 898 } 899 } 900 } 901 if (found) { 902 return meth; 903 } else { 904 return null; 905 } 906 } 907 908 /*** 909 * ASTPrimaryPrefix has name in child node of ASTName 910 */ 911 private static String getNameFromPrefix(ASTPrimaryPrefix node) { 912 String name = null; 913 //should only be 1 child, if more I need more knowledge 914 if (node.jjtGetNumChildren() == 1) { //safety check 915 Node nnode = node.jjtGetChild(0); 916 if (nnode instanceof ASTName) { //just as easy as null check and it should be an ASTName anyway 917 name = ((ASTName) nnode).getImage(); 918 } 919 } 920 return name; 921 } 922 923 /*** 924 * ASTPrimarySuffix has name in itself 925 */ 926 private static String getNameFromSuffix(ASTPrimarySuffix node) { 927 return node.getImage(); 928 } 929 }

This page was automatically generated by Maven