PageRenderTime 54ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/pmd-4.2.5/src/net/sourceforge/pmd/rules/ConstructorCallsOverridableMethod.java

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