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

This page was automatically generated by Maven