/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2010, Stefan Hepp (stefan@stefant.org). * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.common.graphutils; import com.jopdesign.common.ClassInfo; import com.jopdesign.common.FieldInfo; import com.jopdesign.common.MemberInfo; import com.jopdesign.common.MethodCode; import com.jopdesign.common.MethodInfo; import com.jopdesign.common.bcel.AnnotationAttribute; import com.jopdesign.common.bcel.CustomAttribute; import com.jopdesign.common.bcel.EnclosingMethod; import com.jopdesign.common.bcel.ParameterAnnotationAttribute; import com.jopdesign.common.bcel.StackMapTable; import com.jopdesign.common.logger.LogConfig; import com.jopdesign.common.misc.JavaClassFormatError; import org.apache.bcel.classfile.Attribute; import org.apache.bcel.classfile.Code; import org.apache.bcel.classfile.CodeException; import org.apache.bcel.classfile.Constant; import org.apache.bcel.classfile.ConstantClass; import org.apache.bcel.classfile.ConstantDouble; import org.apache.bcel.classfile.ConstantFieldref; import org.apache.bcel.classfile.ConstantFloat; import org.apache.bcel.classfile.ConstantInteger; import org.apache.bcel.classfile.ConstantInterfaceMethodref; import org.apache.bcel.classfile.ConstantLong; import org.apache.bcel.classfile.ConstantMethodref; import org.apache.bcel.classfile.ConstantNameAndType; import org.apache.bcel.classfile.ConstantPool; import org.apache.bcel.classfile.ConstantString; import org.apache.bcel.classfile.ConstantUtf8; import org.apache.bcel.classfile.ConstantValue; import org.apache.bcel.classfile.ExceptionTable; 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.Signature; import org.apache.bcel.classfile.SourceFile; import org.apache.bcel.classfile.StackMap; import org.apache.bcel.classfile.StackMapEntry; import org.apache.bcel.classfile.Synthetic; import org.apache.bcel.classfile.Unknown; import org.apache.bcel.classfile.Visitor; import org.apache.bcel.generic.CodeExceptionGen; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.LineNumberGen; import org.apache.bcel.generic.LocalVariableGen; import org.apache.log4j.Logger; /** * A class visitor which traverses all elements of a classInfo. Similar to BCELs DescendingVisitor. * * @author Stefan Hepp (stefan@stefant.org) */ public class DescendingClassTraverser implements ClassVisitor { /////////////////////////////////////////////////////////////////// // Private BCEL visitor delegator /////////////////////////////////////////////////////////////////// private class BcelVisitor implements Visitor { private ClassInfo classInfo; private FieldInfo fieldInfo; private MethodInfo methodInfo; private boolean code = false; private BcelVisitor() { } public void setClassInfo(ClassInfo classInfo) { this.classInfo = classInfo; fieldInfo = null; methodInfo = null; } public void setFieldInfo(FieldInfo fieldInfo) { this.fieldInfo = fieldInfo; methodInfo = null; classInfo = null; } public void setMethodInfo(MethodInfo methodInfo) { this.methodInfo = methodInfo; fieldInfo = null; classInfo = null; } public void setMemberInfo(MemberInfo member) { if (member instanceof MethodInfo) { setMethodInfo((MethodInfo) member); } else if (member instanceof FieldInfo) { setFieldInfo((FieldInfo) member); } else if (member instanceof ClassInfo) { setClassInfo((ClassInfo) member); } else { throw new AssertionError("A member which is neither class, field nor method?? " + member); } } public boolean isCode() { return code; } public void setCode(boolean code) { this.code = code; } public MemberInfo getMemberInfo() { if (classInfo != null) return classInfo; if (methodInfo != null) return methodInfo; return fieldInfo; } public ClassInfo getClassInfo() { return classInfo; } public MethodInfo getMethodInfo() { return methodInfo; } public void visitCode(Code obj) { visitor.visitCode(methodInfo, obj); } public void visitCodeException(CodeException obj) { logger.warn("Visiting CodeException attribute, but MethodInfo should not have one. Skipping."); } public void visitConstantClass(ConstantClass obj) { visitor.visitConstantClass(classInfo, obj); } public void visitConstantDouble(ConstantDouble obj) { visitor.visitConstantDouble(classInfo, obj); } public void visitConstantFieldref(ConstantFieldref obj) { visitor.visitConstantField(classInfo, obj); } public void visitConstantFloat(ConstantFloat obj) { visitor.visitConstantFloat(classInfo, obj); } public void visitConstantInteger(ConstantInteger obj) { visitor.visitConstantInteger(classInfo, obj); } public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj) { visitor.visitConstantInterfaceMethod(classInfo, obj); } public void visitConstantLong(ConstantLong obj) { visitor.visitConstantLong(classInfo, obj); } public void visitConstantMethodref(ConstantMethodref obj) { visitor.visitConstantMethod(classInfo, obj); } public void visitConstantNameAndType(ConstantNameAndType obj) { visitor.visitConstantNameAndType(classInfo, obj); } public void visitConstantPool(ConstantPool obj) { throw new JavaClassFormatError("Visiting ConstantPool, but this should not happen."); } public void visitConstantString(ConstantString obj) { visitor.visitConstantString(classInfo, obj); } public void visitConstantUtf8(ConstantUtf8 obj) { visitor.visitConstantUtf8(classInfo, obj); } public void visitConstantValue(ConstantValue obj) { visitor.visitConstantValue(fieldInfo, obj); } public void visitDeprecated(org.apache.bcel.classfile.Deprecated obj) { visitor.visitDeprecated(getMemberInfo(), obj); } public void visitExceptionTable(ExceptionTable obj) { visitor.visitExceptionTable(methodInfo, obj); } public void visitField(Field obj) { throw new JavaClassFormatError("Visiting Field, but this should not happen."); } public void visitInnerClass(InnerClass obj) { throw new JavaClassFormatError("Visiting InnerClass, but we do not call this.."); } public void visitInnerClasses(InnerClasses obj) { visitor.visitInnerClasses(classInfo, obj); } public void visitJavaClass(JavaClass obj) { throw new JavaClassFormatError("Visiting JavaClass, but this should not happen."); } public void visitLineNumber(LineNumber obj) { throw new JavaClassFormatError("Visiting LineNumber, but this should not happen."); } public void visitLineNumberTable(LineNumberTable obj) { visitor.visitLineNumberTable(methodInfo, obj); } public void visitLocalVariable(LocalVariable obj) { throw new JavaClassFormatError("Visiting LocalVariable, but this should not happen."); } public void visitLocalVariableTable(LocalVariableTable obj) { visitor.visitLocalVariableTable(methodInfo, obj); } public void visitMethod(Method obj) { throw new JavaClassFormatError("Visiting Method, but this should not happen."); } public void visitSignature(Signature obj) { visitor.visitSignature(getMemberInfo(), obj); } public void visitSourceFile(SourceFile obj) { visitor.visitSourceFile(classInfo, obj); } public void visitSynthetic(Synthetic obj) { visitor.visitSynthetic(getMemberInfo(), obj); } public void visitUnknown(Unknown obj) { visitor.visitUnknown(getMemberInfo(), obj, code); } public void visitStackMap(StackMap obj) { visitor.visitStackMap(methodInfo, obj); } public void visitStackMapEntry(StackMapEntry obj) { throw new JavaClassFormatError("Visiting StackMapEntry, but we do not call this.."); } } /////////////////////////////////////////////////////////////////// // Constructor, ClassVisitor implementation /////////////////////////////////////////////////////////////////// private static final Logger logger = Logger.getLogger(LogConfig.LOG_STRUCT + ".DescendingClassTraverser"); private final ClassElementVisitor visitor; private final BcelVisitor bcelVisitor; private boolean returnOnSkipClass = true; public DescendingClassTraverser(ClassElementVisitor visitor) { this.visitor = visitor; bcelVisitor = new BcelVisitor(); } public ClassElementVisitor getVisitor() { return visitor; } /** * Do we want to terminate iteration over classes when the ClassElementVisitor wants to terminate iteration * for a class? Default is not to terminate. * @param terminate if true, return the same return value in {@link #visitClass(ClassInfo)} as the visitor, * else only skip the current class and continue with the next class. */ public void setTerminateAfterClassSkipped(boolean terminate) { returnOnSkipClass = !terminate; } /** * @return True if we terminate the traversion of all classes when the ClassElementVisitor terminates * iteration for a class. Default is false. */ public boolean doTerminateAfterClassSkipped() { return !returnOnSkipClass; } public boolean visitClass(ClassInfo classInfo) { if (!visitor.visitClass(classInfo)) { return returnOnSkipClass; } visitConstantPool(classInfo); // methods and fields are final, no need to call accept() for (FieldInfo f : classInfo.getFields()) { if (!visitor.visitField(f)) { continue; } bcelVisitor.setFieldInfo(f); visitAttributes(f.getAttributes()); visitor.finishField(f); } for (MethodInfo m : classInfo.getMethods()) { if (!visitor.visitMethod(m)) { continue; } bcelVisitor.setMethodInfo(m); visitMethodCode(m); visitAttributes(m.getAttributes()); visitor.finishMethod(m); } bcelVisitor.setClassInfo(classInfo); visitAttributes(classInfo.getAttributes()); visitor.finishClass(classInfo); return true; } public void finishClass(ClassInfo classInfo) { } /////////////////////////////////////////////////////////////////// // Other methods to visit only parts of a class /////////////////////////////////////////////////////////////////// public void visitConstantPool(ClassInfo classInfo) { ConstantPoolGen cpg = classInfo.getConstantPoolGen(); if (visitor.visitConstantPoolGen(classInfo, cpg)) { bcelVisitor.setClassInfo(classInfo); for (int i = 1; i < cpg.getSize(); i++) { Constant c = cpg.getConstant(i); // Some entries might be null (continuation of previous entry) if (c == null) continue; c.accept(bcelVisitor); } visitor.finishConstantPoolGen(classInfo, cpg); } } public void visitConstant(ClassInfo classInfo, int index) { ConstantPoolGen cpg = classInfo.getConstantPoolGen(); visitConstant(classInfo, cpg.getConstant(index)); } public void visitConstant(ClassInfo classInfo, Constant constant) { if (constant == null) return; bcelVisitor.setClassInfo(classInfo); constant.accept(bcelVisitor); } public void visitMethodCode(MethodInfo methodInfo) { if (methodInfo.hasCode()) { bcelVisitor.setCode(true); MethodCode code = methodInfo.getCode(); visitor.visitMethodCode(code); for (CodeExceptionGen ex : code.getExceptionHandlers()) { visitor.visitCodeException(methodInfo, ex); } for (LineNumberGen lng : code.getLineNumbers()) { visitor.visitLineNumber(methodInfo, lng); } for (LocalVariableGen lvg : code.getLocalVariables()) { visitor.visitLocalVariable(methodInfo, lvg); } visitAttributes(code.getAttributes()); bcelVisitor.setCode(false); } } public void visitAttributes(MemberInfo member, Attribute[] attributes) { bcelVisitor.setMemberInfo(member); visitAttributes(attributes); } /////////////////////////////////////////////////////////////////// // Private stuff /////////////////////////////////////////////////////////////////// private void visitAttributes(Attribute[] attributes) { for (Attribute a : attributes) { if (a instanceof EnclosingMethod) { visitor.visitEnclosingMethod(bcelVisitor.getClassInfo(), (EnclosingMethod) a); } else if (a instanceof AnnotationAttribute) { visitor.visitAnnotation(bcelVisitor.getMemberInfo(), (AnnotationAttribute) a); } else if (a instanceof ParameterAnnotationAttribute) { visitor.visitParameterAnnotation(bcelVisitor.getMemberInfo(), (ParameterAnnotationAttribute) a); } else if (a instanceof StackMapTable) { visitor.visitStackMapTable(bcelVisitor.getMethodInfo(), (StackMapTable) a); } else if (a instanceof CustomAttribute) { visitor.visitCustomAttribute(bcelVisitor.getMemberInfo(), (CustomAttribute) a, bcelVisitor.isCode()); } else { a.accept(bcelVisitor); } } } }