📄 constructorcallsoverridablemethodrule.java
字号:
* * @todo investigate limiting the number of passes through config. */ private boolean evaluateDangerOfMethods(Map classMethodMap) { //check each method if it calls overridable method boolean found = false; for (Iterator methodsIter = classMethodMap.keySet().iterator(); methodsIter.hasNext();) { MethodHolder h = (MethodHolder) methodsIter.next(); List calledMeths = (List) classMethodMap.get(h); for (Iterator calledMethsIter = calledMeths.iterator(); calledMethsIter.hasNext() && (h.isDangerous() == false);) { //if this method matches one of our dangerous methods, mark it dangerous MethodInvocation meth = (MethodInvocation) calledMethsIter.next(); //System.out.println("Called meth is " + meth); for (Iterator innerMethsIter = classMethodMap.keySet().iterator(); innerMethsIter.hasNext();) { //need to skip self here h == h3 MethodHolder h3 = (MethodHolder) innerMethsIter.next(); if (h3.isDangerous()) { String matchMethodName = h3.getASTMethodDeclarator().getImage(); int matchMethodParamCount = h3.getASTMethodDeclarator().getParameterCount(); //System.out.println("matchint " + matchMethodName + " to " + methName); if (matchMethodName.equals(meth.getName()) && (matchMethodParamCount == meth.getArgumentCount())) { h.setDangerous(true); found = true; break; } } } } } return found; } /** * marks constructors dangerous if they call any dangerous methods * Requires only a single pass as methods are already marked * @todo optimize by having methods already evaluated somehow!? */ private void evaluateDangerOfConstructors1(Map classConstructorMap, Set evaluatedMethods) { //check each constructor in the class for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) { ConstructorHolder ch = (ConstructorHolder) constIter.next(); if (!ch.isDangerous()) {//if its not dangerous then evaluate if it should be //if it calls dangerous method mark it as dangerous List calledMeths = (List) classConstructorMap.get(ch); //check each method it calls 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 MethodInvocation meth = (MethodInvocation) calledMethsIter.next();//CCE String methName = meth.getName(); int methArgCount = meth.getArgumentCount(); //check each of the already evaluated methods: need to optimize this out for (Iterator evaldMethsIter = evaluatedMethods.iterator(); evaldMethsIter.hasNext();) { MethodHolder h = (MethodHolder) evaldMethsIter.next(); if (h.isDangerous()) { String matchName = h.getASTMethodDeclarator().getImage(); int matchParamCount = h.getASTMethodDeclarator().getParameterCount(); if (methName.equals(matchName) && (methArgCount == matchParamCount)) { ch.setDangerous(true); //System.out.println("evaluateDangerOfConstructors1 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params"); break; } } } } } } } /** * Constructor map should contain a key for each private constructor, and * maps to a List which contains all called constructors of that key. * marks dangerous if call dangerous private constructor * we ignore all non-private constructors here. That is, the map passed in * should not contain any non-private constructors. * we return boolean in order to limit the number of passes through this method * but it seems as if we can forgo that and just process it till its done. */ private boolean evaluateDangerOfConstructors2(Map classConstructorMap) { boolean found = false;//triggers on danger state change //check each constructor in the class for (Iterator constIter = classConstructorMap.keySet().iterator(); constIter.hasNext();) { ConstructorHolder ch = (ConstructorHolder) constIter.next(); ConstructorInvocation calledC = ch.getCalledConstructor(); if (calledC == null || ch.isDangerous()) { continue; } //if its not dangerous then evaluate if it should be //if it calls dangerous constructor mark it as dangerous int cCount = calledC.getArgumentCount(); for (Iterator innerConstIter = classConstructorMap.keySet().iterator(); innerConstIter.hasNext() && !ch.isDangerous();) { //forget skipping self because that introduces another check for each, but only 1 hit ConstructorHolder h2 = (ConstructorHolder) innerConstIter.next(); if (h2.isDangerous()) { int matchConstArgCount = h2.getASTConstructorDeclaration().getParameterCount(); if (matchConstArgCount == cCount) { ch.setDangerous(true); found = true; //System.out.println("evaluateDangerOfConstructors2 setting dangerous constructor with " + ch.getASTConstructorDeclaration().getParameterCount() + " params"); } } } } return found; } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //The Visited Methods /** * Work on each file independently. */ public Object visit(ASTCompilationUnit node, Object data) { clearEvalPackages(); // try { return super.visit(node, data); // } // catch(Exception e){ // e.printStackTrace(); // } } //for testing only // public Object visit(ASTPackageDeclaration node, Object data){ // System.out.println("package= " + ((ASTName)node.jjtGetChild(0)).getImage()); // return super.visit(node,data); // } /** * This check must be evaluated independelty for each class. Inner classses * get their own EvalPackage in order to perform independent evaluation. */ public Object visit(ASTClassDeclaration node, Object data) { return visitClassDec(node, data); } public Object visit(ASTNestedClassDeclaration node, Object data) { return visitClassDec(node, data); } public Object visit(ASTInterfaceDeclaration node, Object data) { putEvalPackage(nullEvalPackage); Object o = super.visit(node, data);//interface may have inner classes, possible? if not just skip whole interface removeCurrentEvalPackage(); return o; } public Object visit(ASTNestedInterfaceDeclaration node, Object data) { putEvalPackage(nullEvalPackage); Object o = super.visit(node, data);//interface may have inner classes, possible? if not just skip whole interface removeCurrentEvalPackage(); return o; } /** * Non-private constructor's methods are added to a list for later safety * evaluation. Non-private constructor's calls on private constructors * are added to a list for later safety evaluation. Private constructors * are added to a list so their safety to be called can be later evaluated. * * Note: We are not checking private constructor's calls on non-private * constructors because all non-private constructors will be evaluated for * safety anyway. This means we wont flag a private constructor as unsafe * just because it calls an unsafe public constructor. We want to show only * 1 instance of an error, and this would be 2 instances of the same error. * * @todo eliminate the redundency */ public Object visit(ASTConstructorDeclaration node, Object data) { if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class List calledMethodsOfConstructor = new ArrayList(); ConstructorHolder ch = new ConstructorHolder(node); addCalledMethodsOfNode(node, calledMethodsOfConstructor, getCurrentEvalPackage().m_ClassName); if (!node.isPrivate()) { //these calledMethods are what we will evaluate for being called badly getCurrentEvalPackage().calledMethods.addAll(calledMethodsOfConstructor); //these called private constructors are what we will evaluate for being called badly //we add all constructors invoked by non-private constructors //but we are only interested in the private ones. We just can't tell the difference here ASTExplicitConstructorInvocation eci = ch.getASTExplicitConstructorInvocation(); if (eci != null && eci.isThis()) { getCurrentEvalPackage().calledConstructors.add(ch.getCalledConstructor()); } } else { //add all private constructors to list for later evaluation on if they are safe to call from another constructor //store this constructorHolder for later evaluation getCurrentEvalPackage().allPrivateConstructorsOfClass.put(ch, calledMethodsOfConstructor); } } return super.visit(node, data); } /** * Create a MethodHolder to hold the method. * Store the MethodHolder in the Map as the key * Store each method called by the current method as a List in the Map as the Object */ public Object visit(ASTMethodDeclarator node, Object data) { if (!(getCurrentEvalPackage() instanceof NullEvalPackage)) {//only evaluate if we have an eval package for this class AccessNode parent = (AccessNode) node.jjtGetParent(); MethodHolder h = new MethodHolder(node); if (!parent.isPrivate() && !parent.isStatic() && !parent.isFinal()) { h.setDangerous(true);//this method is overridable } List l = new ArrayList(); addCalledMethodsOfNode((SimpleNode) parent, l, getCurrentEvalPackage().m_ClassName); getCurrentEvalPackage().allMethodsOfClass.put(h, l); } return super.visit(node, data); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //Helper methods to process visits private static void addCalledMethodsOfNode(AccessNode node, List calledMethods, String className) { List expressions = new ArrayList(); node.findChildrenOfType(ASTPrimaryExpression.class, expressions, false); addCalledMethodsOfNodeImpl(expressions, calledMethods, className); } /** * Adds all methods called on this instance from within this Node. */ private static void addCalledMethodsOfNode(SimpleNode node, List calledMethods, String className) { List expressions = new ArrayList(); node.findChildrenOfType(ASTPrimaryExpression.class, expressions); addCalledMethodsOfNodeImpl(expressions, calledMethods, className); } private static void addCalledMethodsOfNodeImpl(List expressions, List calledMethods, String className) { for (Iterator it = expressions.iterator(); it.hasNext();) { ASTPrimaryExpression ape = (ASTPrimaryExpression) it.next(); MethodInvocation meth = findMethod(ape, className); if (meth != null) { //System.out.println("Adding call " + methName); calledMethods.add(meth); } } } /** * @todo Need a better way to match the class and package name to the actual * method being called. * @return A method call on the class passed in, or null if no method call * is found. */ private static MethodInvocation findMethod(ASTPrimaryExpression node, String className) { if (node.jjtGetNumChildren() > 0 && node.jjtGetChild(0).jjtGetNumChildren() > 0 && node.jjtGetChild(0).jjtGetChild(0) instanceof ASTLiteral) { return null; } MethodInvocation meth = MethodInvocation.getMethod(node); boolean found = false; // if(meth != null){ // meth.show(); // } if (meth != null) { //if it's a call on a variable, or on its superclass ignore it. if ((meth.getReferenceNames().size() == 0) && !meth.isSuper()) { //if this list does not contain our class name, then its not referencing our class //this is a cheezy test... but it errs on the side of less false hits. List packClass = meth.getQualifierNames(); if (packClass.size() > 0) { for (Iterator it = packClass.iterator(); it.hasNext();) { String name = (String) it.next(); if (name.equals(className)) { found = true; break; } } } else { found = true; } } } if (found) { return meth; } else { return null; } } /** * ASTPrimaryPrefix has name in child node of ASTName */ private static String getNameFromPrefix(ASTPrimaryPrefix node) { String name = null; //should only be 1 child, if more I need more knowledge if (node.jjtGetNumChildren() == 1) { //safety check Node nnode = node.jjtGetChild(0); if (nnode instanceof ASTName) { //just as easy as null check and it should be an ASTName anyway name = ((ASTName) nnode).getImage(); } } return name; } /** * ASTPrimarySuffix has name in itself */ private static String getNameFromSuffix(ASTPrimarySuffix node) { return node.getImage(); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -