All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.umd.cs.findbugs.visitclass.PreorderVisitor Maven / Gradle / Ivy

The newest version!
/*
 * FindBugs - Find bugs in Java programs
 * Copyright (C) 2003,2004 University of Maryland
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.umd.cs.findbugs.visitclass;

import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import edu.umd.cs.findbugs.util.ClassName;
import org.apache.bcel.Const;
import org.apache.bcel.classfile.AnnotationDefault;
import org.apache.bcel.classfile.AnnotationEntry;
import org.apache.bcel.classfile.Annotations;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.BootstrapMethods;
import org.apache.bcel.classfile.Code;
import org.apache.bcel.classfile.CodeException;
import org.apache.bcel.classfile.Constant;
import org.apache.bcel.classfile.ConstantCP;
import org.apache.bcel.classfile.ConstantClass;
import org.apache.bcel.classfile.ConstantInterfaceMethodref;
import org.apache.bcel.classfile.ConstantInvokeDynamic;
import org.apache.bcel.classfile.ConstantMethodHandle;
import org.apache.bcel.classfile.ConstantMethodType;
import org.apache.bcel.classfile.ConstantMethodref;
import org.apache.bcel.classfile.ConstantModule;
import org.apache.bcel.classfile.ConstantNameAndType;
import org.apache.bcel.classfile.ConstantPackage;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ConstantUtf8;
import org.apache.bcel.classfile.EnclosingMethod;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.InnerClass;
import org.apache.bcel.classfile.InnerClasses;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LineNumber;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.LocalVariable;
import org.apache.bcel.classfile.LocalVariableTable;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.classfile.MethodParameters;
import org.apache.bcel.classfile.ParameterAnnotationEntry;
import org.apache.bcel.classfile.ParameterAnnotations;
import org.apache.bcel.classfile.StackMap;
import org.apache.bcel.classfile.StackMapEntry;

import edu.umd.cs.findbugs.FindBugs;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.XClass;
import edu.umd.cs.findbugs.ba.XField;
import edu.umd.cs.findbugs.ba.XMethod;
import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
import edu.umd.cs.findbugs.classfile.ClassDescriptor;
import edu.umd.cs.findbugs.classfile.DescriptorFactory;
import edu.umd.cs.findbugs.classfile.FieldDescriptor;
import edu.umd.cs.findbugs.classfile.FieldOrMethodDescriptor;
import edu.umd.cs.findbugs.classfile.Global;
import edu.umd.cs.findbugs.classfile.IAnalysisCache;
import edu.umd.cs.findbugs.classfile.MethodDescriptor;
import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
import edu.umd.cs.findbugs.classfile.analysis.FieldInfo;
import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;

/**
 * 

Interface to make the use of a visitor pattern programming style possible. * I.e. a class that implements this interface can traverse the contents of a * Java class just by calling the `accept' method which all classes have. *

*

Implemented by wish of Boris * Bokowski.

*

* If don't like it, blame him. If you do like it thank me 8-) *

* @author M. Dahm * @version 970819 */ public class PreorderVisitor extends BetterVisitor { // Available when visiting a class private ConstantPool constantPool; private JavaClass thisClass; private ClassInfo thisClassInfo; private MethodInfo thisMethodInfo; private FieldInfo thisFieldInfo; private String className = "none"; private String dottedClassName = "none"; private String packageName = "none"; private String sourceFile = "none"; private String superclassName = "none"; private String dottedSuperclassName = "none"; // Available when visiting a method private boolean visitingMethod = false; private String methodSig = "none"; private String dottedMethodSig = "none"; private Method method = null; private String methodName = "none"; private String fullyQualifiedMethodName = "none"; // Available when visiting a field private Field field; private boolean visitingField = false; private String fullyQualifiedFieldName = "none"; private String fieldName = "none"; private String fieldSig = "none"; private String dottedFieldSig = "none"; private boolean fieldIsStatic; // Available when visiting a Code private Code code; protected String getStringFromIndex(int i) { ConstantUtf8 name = (ConstantUtf8) constantPool.getConstant(i); return name.getBytes(); } protected int asUnsignedByte(byte b) { return 0xff & b; } /** * Return the current Code attribute; assuming one is being visited * * @return current code attribute */ public Code getCode() { if (code == null) { throw new IllegalStateException("Not visiting Code"); } return code; } public Set getSurroundingCaughtExceptions(int pc) { return getSurroundingCaughtExceptions(pc, Integer.MAX_VALUE); } public Set getSurroundingCaughtExceptions(int pc, int maxTryBlockSize) { return getSurroundingCaughtExceptionTypes(pc, maxTryBlockSize).stream() .map(ex -> "C" + ex) .collect(Collectors.toSet()); } public Set getSurroundingCaughtExceptionTypes(int pc, int maxTryBlockSize) { HashSet result = new HashSet<>(); if (code == null) { throw new IllegalStateException("Not visiting Code"); } int size = maxTryBlockSize; if (code.getExceptionTable() == null) { return result; } for (CodeException catchBlock : code.getExceptionTable()) { int startPC = catchBlock.getStartPC(); int endPC = catchBlock.getEndPC(); if (pc >= startPC && pc <= endPC) { int thisSize = endPC - startPC; if (size > thisSize) { result.clear(); size = thisSize; result.add(catchBlock.getCatchType()); } else if (size == thisSize) { result.add(catchBlock.getCatchType()); } } } return result; } /** * Get lines of code in try block that surround pc * * @param pc * @return number of lines of code in try block */ public int getSizeOfSurroundingTryBlock(int pc) { return getSizeOfSurroundingTryBlock(null, pc); } /** * Get lines of code in try block that surround pc * * @param pc * @return number of lines of code in try block */ public int getSizeOfSurroundingTryBlock(String vmNameOfExceptionClass, int pc) { if (code == null) { throw new IllegalStateException("Not visiting Code"); } return Util.getSizeOfSurroundingTryBlock(constantPool, code, vmNameOfExceptionClass, pc); } public CodeException getSurroundingTryBlock(int pc) { return getSurroundingTryBlock(null, pc); } public CodeException getSurroundingTryBlock(String vmNameOfExceptionClass, int pc) { if (code == null) { throw new IllegalStateException("Not visiting Code"); } return Util.getSurroundingTryBlock(constantPool, code, vmNameOfExceptionClass, pc); } // Attributes @Override public void visitCode(Code obj) { code = obj; super.visitCode(obj); CodeException[] exceptions = obj.getExceptionTable(); for (CodeException exception : exceptions) { exception.accept(this); } Attribute[] attributes = obj.getAttributes(); for (Attribute attribute : attributes) { attribute.accept(this); } visitAfter(obj); code = null; } /** * Called after visiting a code attribute * * @param obj * Code that was just visited */ public void visitAfter(Code obj) { } // Constants @Override public void visitConstantPool(ConstantPool obj) { super.visitConstantPool(obj); Constant[] constant_pool = obj.getConstantPool(); for (int i = 1; i < constant_pool.length; i++) { constant_pool[i].accept(this); byte tag = constant_pool[i].getTag(); if ((tag == Const.CONSTANT_Double) || (tag == Const.CONSTANT_Long)) { i++; } } } private void doVisitField(Field field) { if (visitingField) { throw new IllegalStateException("visitField called when already visiting a field"); } visitingField = true; this.field = field; try { fieldName = fieldSig = dottedFieldSig = fullyQualifiedFieldName = null; thisFieldInfo = (FieldInfo) thisClassInfo.findField(getFieldName(), getFieldSig(), field.isStatic()); assert thisFieldInfo != null : "Can't get field info for " + getFullyQualifiedFieldName(); fieldIsStatic = field.isStatic(); field.accept(this); Attribute[] attributes = field.getAttributes(); for (Attribute attribute : attributes) { attribute.accept(this); } } finally { visitingField = false; this.field = null; this.thisFieldInfo = null; } } public void doVisitMethod(Method method) { if (visitingMethod) { throw new IllegalStateException("doVisitMethod called when already visiting a method"); } visitingMethod = true; try { this.method = method; methodName = methodSig = dottedMethodSig = fullyQualifiedMethodName = null; thisMethodInfo = (MethodInfo) thisClassInfo.findMethod(getMethodName(), getMethodSig(), method.isStatic()); assert thisMethodInfo != null : "Can't get method info for " + getFullyQualifiedMethodName(); this.method.accept(this); Attribute[] attributes = method.getAttributes(); for (Attribute attribute : attributes) { attribute.accept(this); } } finally { visitingMethod = false; this.method = null; this.thisMethodInfo = null; } } public boolean amVisitingMainMethod() { if (!visitingMethod) { throw new IllegalStateException("Not visiting a method"); } return method.isStatic() && "main".equals(getMethodName()) && "([Ljava/lang/String;)V".equals(getMethodSig()); } // Extra classes (i.e. leaves in this context) @Override public void visitInnerClasses(InnerClasses obj) { super.visitInnerClasses(obj); InnerClass[] inner_classes = obj.getInnerClasses(); for (InnerClass inner_class : inner_classes) { inner_class.accept(this); } } public void visitAfter(JavaClass obj) { } public boolean shouldVisit(JavaClass obj) { return true; } boolean visitMethodsInCallOrder; protected boolean isVisitMethodsInCallOrder() { return visitMethodsInCallOrder; } protected void setVisitMethodsInCallOrder(boolean visitMethodsInCallOrder) { this.visitMethodsInCallOrder = visitMethodsInCallOrder; } protected Iterable getMethodVisitOrder(JavaClass obj) { return Arrays.asList(obj.getMethods()); } // General classes @Override public void visitJavaClass(JavaClass obj) { setupVisitorForClass(obj); if (shouldVisit(obj)) { constantPool.accept(this); Field[] fields = obj.getFields(); Attribute[] attributes = obj.getAttributes(); for (Field field : fields) { doVisitField(field); } boolean didInCallOrder = false; if (visitMethodsInCallOrder) { try { IAnalysisCache analysisCache = Global.getAnalysisCache(); ClassDescriptor c = DescriptorFactory.createClassDescriptor(obj); ClassContext classContext = analysisCache.getClassAnalysis(ClassContext.class, c); didInCallOrder = true; for (Method m : classContext.getMethodsInCallOrder()) { doVisitMethod(m); } } catch (CheckedAnalysisException e) { AnalysisContext.logError("Error trying to visit methods in order", e); } } if (!didInCallOrder) { for (Method m : getMethodVisitOrder(obj)) { doVisitMethod(m); } } for (Attribute attribute : attributes) { attribute.accept(this); } visitAfter(obj); } } public void setupVisitorForClass(JavaClass obj) { constantPool = obj.getConstantPool(); thisClass = obj; ConstantClass c = (ConstantClass) constantPool.getConstant(obj.getClassNameIndex()); className = getStringFromIndex(c.getNameIndex()); dottedClassName = ClassName.toDottedClassName(className); packageName = obj.getPackageName(); sourceFile = obj.getSourceFileName(); dottedSuperclassName = obj.getSuperclassName(); superclassName = ClassName.toSlashedClassName(dottedSuperclassName); ClassDescriptor cDesc = DescriptorFactory.createClassDescriptor(className); if (!FindBugs.isNoAnalysis()) { try { thisClassInfo = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, cDesc); } catch (CheckedAnalysisException e) { throw new AssertionError("Can't find ClassInfo for " + cDesc); } } super.visitJavaClass(obj); } @Override public void visitLineNumberTable(LineNumberTable obj) { super.visitLineNumberTable(obj); LineNumber[] line_number_table = obj.getLineNumberTable(); for (LineNumber aLine_number_table : line_number_table) { aLine_number_table.accept(this); } } @Override public void visitLocalVariableTable(LocalVariableTable obj) { super.visitLocalVariableTable(obj); LocalVariable[] local_variable_table = obj.getLocalVariableTable(); for (LocalVariable aLocal_variable_table : local_variable_table) { aLocal_variable_table.accept(this); } } // Accessors public XClass getXClass() { if (thisClassInfo == null) { throw new AssertionError("XClass information not set"); } return thisClassInfo; } public ClassDescriptor getClassDescriptor() { return thisClassInfo; } public XMethod getXMethod() { return thisMethodInfo; } public MethodDescriptor getMethodDescriptor() { return thisMethodInfo; } public XField getXField() { return thisFieldInfo; } public FieldDescriptor getFieldDescriptor() { return thisFieldInfo; } /** Get the constant pool for the current or most recently visited class */ public ConstantPool getConstantPool() { return constantPool; } /** * Get the slash-formatted class name for the current or most recently * visited class */ public @SlashedClassName String getClassName() { return className; } /** Get the dotted class name for the current or most recently visited class */ public @DottedClassName String getDottedClassName() { return dottedClassName; } /** * Get the (slash-formatted?) package name for the current or most recently * visited class */ public String getPackageName() { return packageName; } /** Get the source file name for the current or most recently visited class */ public String getSourceFile() { return sourceFile; } /** * Get the slash-formatted superclass name for the current or most recently * visited class */ public @SlashedClassName String getSuperclassName() { return superclassName; } /** * Get the dotted superclass name for the current or most recently visited * class */ public @DottedClassName String getDottedSuperclassName() { return dottedSuperclassName; } /** Get the JavaClass object for the current or most recently visited class */ public JavaClass getThisClass() { return thisClass; } /** If currently visiting a method, get the method's fully qualified name */ public String getFullyQualifiedMethodName() { if (!visitingMethod) { throw new IllegalStateException("getFullyQualifiedMethodName called while not visiting method"); } if (fullyQualifiedMethodName == null) { getMethodName(); getDottedMethodSig(); StringBuilder ref = new StringBuilder(5 + dottedClassName.length() + methodName.length() + dottedMethodSig.length()); ref.append(dottedClassName).append(".").append(methodName).append(" : ").append(dottedMethodSig); fullyQualifiedMethodName = ref.toString(); } return fullyQualifiedMethodName; } /** * is the visitor currently visiting a method? */ public boolean visitingMethod() { return visitingMethod; } /** * is the visitor currently visiting a field? */ public boolean visitingField() { return visitingField; } /** If currently visiting a field, get the field's Field object */ public Field getField() { if (!visitingField) { throw new IllegalStateException("getField called while not visiting field"); } return field; } /** If currently visiting a method, get the method's Method object */ public Method getMethod() { if (!visitingMethod) { throw new IllegalStateException("getMethod called while not visiting method"); } return method; } /** If currently visiting a method, get the method's name */ public String getMethodName() { if (!visitingMethod) { throw new IllegalStateException("getMethodName called while not visiting method"); } if (methodName == null) { methodName = getStringFromIndex(method.getNameIndex()); } return methodName; } static Pattern argumentSignature = Pattern.compile("\\[*([BCDFIJSZ]|L[^;]*;)"); public static int getNumberArguments(String signature) { int count = 0; int pos = 1; boolean inArray = false; while (true) { switch (signature.charAt(pos++)) { case ')': return count; case '[': if (!inArray) { count++; } inArray = true; break; case 'L': if (!inArray) { count++; } while (signature.charAt(pos) != ';') { pos++; } pos++; inArray = false; break; default: if (!inArray) { count++; } inArray = false; break; } } } /** * Returns true if given constant pool probably has a reference to any of supplied methods * Useful to exclude from analysis uninteresting classes * @param cp constant pool * @param methods methods collection * @return true if method is found */ public static boolean hasInterestingMethod(ConstantPool cp, Collection methods) { for (Constant c : cp.getConstantPool()) { if (c instanceof ConstantMethodref || c instanceof ConstantInterfaceMethodref) { ConstantCP desc = (ConstantCP) c; ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(desc.getNameAndTypeIndex()); String className = cp.getConstantString(desc.getClassIndex(), Const.CONSTANT_Class); String name = ((ConstantUtf8) cp.getConstant(nameAndType.getNameIndex())).getBytes(); String signature = ((ConstantUtf8) cp.getConstant(nameAndType.getSignatureIndex())).getBytes(); // We don't know whether method is static thus cannot use equals int hash = FieldOrMethodDescriptor.getNameSigHashCode(name, signature); for (MethodDescriptor method : methods) { if (method.getNameSigHashCode() == hash && (method.getSlashedClassName().isEmpty() || method.getSlashedClassName().equals(className)) && method.getName().equals(name) && method.getSignature().equals(signature)) { return true; } } } } return false; } public static boolean hasInterestingClass(ConstantPool cp, Collection classes) { for (Constant c : cp.getConstantPool()) { if (c instanceof ConstantClass) { String className = ((ConstantUtf8) cp.getConstant(((ConstantClass) c).getNameIndex())).getBytes(); if (classes.contains(className)) { return true; } } } return false; } public int getNumberMethodArguments() { return getNumberArguments(getMethodSig()); } /** * If currently visiting a method, get the method's slash-formatted * signature */ public String getMethodSig() { if (!visitingMethod) { throw new IllegalStateException("getMethodSig called while not visiting method"); } if (methodSig == null) { methodSig = getStringFromIndex(method.getSignatureIndex()); } return methodSig; } /** If currently visiting a method, get the method's dotted method signature */ public String getDottedMethodSig() { if (!visitingMethod) { throw new IllegalStateException("getDottedMethodSig called while not visiting method"); } if (dottedMethodSig == null) { dottedMethodSig = ClassName.toDottedClassName(getMethodSig()); } return dottedMethodSig; } /** If currently visiting a field, get the field's name */ public String getFieldName() { if (!visitingField) { throw new IllegalStateException("getFieldName called while not visiting field"); } if (fieldName == null) { fieldName = getStringFromIndex(field.getNameIndex()); } return fieldName; } /** If currently visiting a field, get the field's slash-formatted signature */ public String getFieldSig() { if (!visitingField) { throw new IllegalStateException("getFieldSig called while not visiting field"); } if (fieldSig == null) { fieldSig = getStringFromIndex(field.getSignatureIndex()); } return fieldSig; } /** If currently visiting a field, return whether or not the field is static */ public boolean getFieldIsStatic() { if (!visitingField) { throw new IllegalStateException("getFieldIsStatic called while not visiting field"); } return fieldIsStatic; } /** If currently visiting a field, get the field's fully qualified name */ public String getFullyQualifiedFieldName() { if (!visitingField) { throw new IllegalStateException("getFullyQualifiedFieldName called while not visiting field"); } if (fullyQualifiedFieldName == null) { fullyQualifiedFieldName = getDottedClassName() + "." + getFieldName() + " : " + getFieldSig(); } return fullyQualifiedFieldName; } /** If currently visiting a field, get the field's dot-formatted signature */ @Deprecated public String getDottedFieldSig() { if (!visitingField) { throw new IllegalStateException("getDottedFieldSig called while not visiting field"); } if (dottedFieldSig == null) { dottedFieldSig = ClassName.toDottedClassName(fieldSig); } return dottedFieldSig; } @Override public String toString() { if (visitingMethod) { return this.getClass().getSimpleName() + " analyzing " + getClassName() + "." + getMethodName() + getMethodSig(); } else if (visitingField) { return this.getClass().getSimpleName() + " analyzing " + getClassName() + "." + getFieldName(); } return this.getClass().getSimpleName() + " analyzing " + getClassName(); } /* * (non-Javadoc) * * @see * org.apache.bcel.classfile.Visitor#visitAnnotation(org.apache.bcel.classfile * .Annotations) */ @Override public void visitAnnotation(Annotations arg0) { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see * org.apache.bcel.classfile.Visitor#visitAnnotationDefault(org.apache.bcel * .classfile.AnnotationDefault) */ @Override public void visitAnnotationDefault(AnnotationDefault arg0) { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see * org.apache.bcel.classfile.Visitor#visitAnnotationEntry(org.apache.bcel * .classfile.AnnotationEntry) */ @Override public void visitAnnotationEntry(AnnotationEntry arg0) { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see * org.apache.bcel.classfile.Visitor#visitEnclosingMethod(org.apache.bcel * .classfile.EnclosingMethod) */ @Override public void visitEnclosingMethod(EnclosingMethod arg0) { // TODO Auto-generated method stub } /* * (non-Javadoc) * * @see * org.apache.bcel.classfile.Visitor#visitParameterAnnotation(org.apache * .bcel.classfile.ParameterAnnotations) */ @Override public void visitParameterAnnotation(ParameterAnnotations arg0) { } @Override public void visitStackMap(StackMap arg0) { // TODO Auto-generated method stub } @Override public void visitStackMapEntry(StackMapEntry arg0) { // TODO Auto-generated method stub } @Override public void visitConstantInvokeDynamic(ConstantInvokeDynamic obj) { // TODO Auto-generated method stub } @Override public void visitBootstrapMethods(BootstrapMethods obj) { // TODO Auto-generated method stub } @Override public void visitMethodParameters(MethodParameters obj) { // TODO Auto-generated method stub } @Override public void visitConstantMethodType(ConstantMethodType obj) { // TODO Auto-generated method stub } @Override public void visitConstantMethodHandle(ConstantMethodHandle obj) { // TODO Auto-generated method stub } @Override public void visitParameterAnnotationEntry(ParameterAnnotationEntry obj) { // TODO Auto-generated method stub } @Override public void visitConstantModule(ConstantModule obj) { // TODO Auto-generated method stub } @Override public void visitConstantPackage(ConstantPackage obj) { // TODO Auto-generated method stub } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy