001: /*
002: * FindBugs - Find Bugs in Java programs
003: * Copyright (C) 2003-2007 University of Maryland
004: *
005: * This library is free software; you can redistribute it and/or
006: * modify it under the terms of the GNU Lesser General Public
007: * License as published by the Free Software Foundation; either
008: * version 2.1 of the License, or (at your option) any later version.
009: *
010: * This library is distributed in the hope that it will be useful,
011: * but WITHOUT ANY WARRANTY; without even the implied warranty of
012: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013: * Lesser General Public License for more details.
014: *
015: * You should have received a copy of the GNU Lesser General Public
016: * License along with this library; if not, write to the Free Software
017: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
018: */
019:
020: package edu.umd.cs.findbugs.classfile.engine;
021:
022: import java.util.BitSet;
023: import java.util.HashSet;
024: import java.util.Iterator;
025: import java.util.TreeSet;
026:
027: import org.apache.bcel.Constants;
028: import org.objectweb.asm.Attribute;
029: import org.objectweb.asm.ClassReader;
030: import org.objectweb.asm.ClassVisitor;
031: import org.objectweb.asm.FieldVisitor;
032: import org.objectweb.asm.Label;
033: import org.objectweb.asm.MethodVisitor;
034: import org.objectweb.asm.Opcodes;
035:
036: import edu.umd.cs.findbugs.annotations.CheckForNull;
037: import edu.umd.cs.findbugs.ba.SignatureParser;
038: import edu.umd.cs.findbugs.classfile.ClassDescriptor;
039: import edu.umd.cs.findbugs.classfile.DescriptorFactory;
040: import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
041: import edu.umd.cs.findbugs.classfile.InvalidClassFileFormatException;
042: import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue;
043: import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
044: import edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo;
045: import edu.umd.cs.findbugs.classfile.analysis.FieldInfo;
046: import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
047: import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
048: import edu.umd.cs.findbugs.util.ClassName;
049:
050: /**
051: * @author William Pugh
052: */
053: public class ClassParserUsingASM implements ClassParserInterface {
054:
055: private static final BitSet RETURN_OPCODE_SET = new BitSet();
056: static {
057: RETURN_OPCODE_SET.set(Constants.ARETURN);
058: RETURN_OPCODE_SET.set(Constants.IRETURN);
059: RETURN_OPCODE_SET.set(Constants.LRETURN);
060: RETURN_OPCODE_SET.set(Constants.DRETURN);
061: RETURN_OPCODE_SET.set(Constants.FRETURN);
062: RETURN_OPCODE_SET.set(Constants.RETURN);
063: }
064:
065: private final ClassReader classReader;
066: private @SlashedClassName
067: String slashedClassName;
068: private final ClassDescriptor expectedClassDescriptor;
069: private final ICodeBaseEntry codeBaseEntry;
070:
071: enum State {
072: INITIAL, THIS_LOADED, VARIABLE_LOADED, AFTER_METHOD_CALL
073: };
074:
075: public ClassParserUsingASM(ClassReader classReader, @CheckForNull
076: ClassDescriptor expectedClassDescriptor,
077: ICodeBaseEntry codeBaseEntry) {
078: this .classReader = classReader;
079: this .expectedClassDescriptor = expectedClassDescriptor;
080: this .codeBaseEntry = codeBaseEntry;
081: }
082:
083: /* (non-Javadoc)
084: * @see edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder)
085: */
086: public void parse(final ClassNameAndSuperclassInfo.Builder cBuilder)
087: throws InvalidClassFileFormatException {
088:
089: cBuilder.setCodeBaseEntry(codeBaseEntry);
090:
091: final TreeSet<ClassDescriptor> calledClassSet = new TreeSet<ClassDescriptor>();
092:
093: classReader.accept(new ClassVisitor() {
094:
095: boolean isInnerClass = false;
096:
097: public void visit(int version, int access, String name,
098: String signature, String super Name,
099: String[] interfaces) {
100: ClassParserUsingASM.this .slashedClassName = name;
101: cBuilder.setClassfileVersion(version >>> 16,
102: version & 0xffff);
103: cBuilder.setAccessFlags(access);
104: cBuilder.setClassDescriptor(DescriptorFactory
105: .createClassDescriptor(name));
106: cBuilder.setInterfaceDescriptorList(DescriptorFactory
107: .createClassDescriptor(interfaces));
108: if (super Name != null)
109: cBuilder.setSuperclassDescriptor(DescriptorFactory
110: .createClassDescriptor(super Name));
111: if (cBuilder instanceof ClassInfo.Builder) {
112: ((ClassInfo.Builder) cBuilder)
113: .setSourceSignature(signature);
114: }
115: }
116:
117: public org.objectweb.asm.AnnotationVisitor visitAnnotation(
118: String desc, boolean isVisible) {
119: if (cBuilder instanceof ClassInfo.Builder) {
120: AnnotationValue value = new AnnotationValue(desc);
121: ((ClassInfo.Builder) cBuilder).addAnnotation(desc,
122: value);
123: return value.getAnnotationVisitor();
124: }
125: return null;
126: }
127:
128: public void visitAttribute(Attribute arg0) {
129: // TODO Auto-generated method stub
130:
131: }
132:
133: public void visitEnd() {
134: // TODO Auto-generated method stub
135:
136: }
137:
138: public FieldVisitor visitField(int access, String name,
139: String desc, String signature, Object value) {
140: if (name.equals("this$0"))
141: isInnerClass = true;
142: if (cBuilder instanceof ClassInfo.Builder) {
143: final FieldInfo.Builder fBuilder = new FieldInfo.Builder(
144: slashedClassName, name, desc, access);
145: fBuilder.setSourceSignature(signature);
146: return new AbstractFieldAnnotationVisitor() {
147:
148: public org.objectweb.asm.AnnotationVisitor visitAnnotation(
149: final String desc, boolean visible) {
150: AnnotationValue value = new AnnotationValue(
151: desc);
152: fBuilder.addAnnotation(desc, value);
153: return value.getAnnotationVisitor();
154: }
155:
156: public void visitEnd() {
157: ((ClassInfo.Builder) cBuilder)
158: .addFieldDescriptor(fBuilder
159: .build());
160:
161: }
162:
163: };
164:
165: }
166: return null;
167: }
168:
169: public void visitInnerClass(String name, String outerName,
170: String innerName, int access) {
171: if (name.equals(slashedClassName) && outerName != null) {
172: if (cBuilder instanceof ClassInfo.Builder) {
173: ClassDescriptor outerClassDescriptor = DescriptorFactory
174: .createClassDescriptor(outerName);
175: ((ClassInfo.Builder) cBuilder)
176: .setImmediateEnclosingClass(outerClassDescriptor);
177: }
178:
179: }
180:
181: }
182:
183: public MethodVisitor visitMethod(final int access,
184: final String methodName, final String methodDesc,
185: String signature, String[] exceptions) {
186: if (cBuilder instanceof ClassInfo.Builder) {
187: final MethodInfo.Builder mBuilder = new MethodInfo.Builder(
188: slashedClassName, methodName, methodDesc,
189: access);
190: mBuilder.setSourceSignature(signature);
191: mBuilder.setThrownExceptions(exceptions);
192:
193: return new AbstractMethodVisitor() {
194:
195: int variable;
196: boolean sawReturn = (access & Opcodes.ACC_NATIVE) != 0;
197: boolean sawThrow = false;
198: boolean sawSystemExit = false;
199: boolean sawBranch = false;
200: State state = State.INITIAL;
201:
202: @Override
203: public void visitInsn(int opcode) {
204: if (RETURN_OPCODE_SET.get(opcode))
205: sawReturn = true;
206: else if (opcode == Opcodes.ATHROW)
207: sawThrow = true;
208: resetState();
209: }
210:
211: public void resetState() {
212: if (state != State.AFTER_METHOD_CALL)
213: state = State.INITIAL;
214: }
215:
216: @Override
217: public void visitSomeInsn() {
218: resetState();
219: }
220:
221: @Override
222: public void visitVarInsn(int opcode, int var) {
223: if (opcode == Opcodes.ALOAD && var == 0)
224: state = State.THIS_LOADED;
225: else if (state == State.THIS_LOADED)
226: switch (opcode) {
227: case Opcodes.ALOAD:
228: case Opcodes.ILOAD:
229: case Opcodes.LLOAD:
230: case Opcodes.DLOAD:
231: case Opcodes.FLOAD:
232: state = State.VARIABLE_LOADED;
233: variable = var;
234: }
235: else
236: visitSomeInsn();
237: }
238:
239: @Override
240: public void visitFieldInsn(int opcode,
241: String owner, String name, String desc) {
242: if (false && state == State.VARIABLE_LOADED
243: && methodName.equals("<init>")
244: && owner.equals(slashedClassName)
245: && name.indexOf('$') >= 0) {
246:
247: System.out.println("Parameter "
248: + (variable - 1) + " to new "
249: + slashedClassName + methodDesc
250: + " is synthetic");
251: }
252: }
253:
254: public org.objectweb.asm.AnnotationVisitor visitAnnotation(
255: final String desc, boolean visible) {
256: AnnotationValue value = new AnnotationValue(
257: desc);
258: mBuilder.addAnnotation(desc, value);
259: return value.getAnnotationVisitor();
260: }
261:
262: @Override
263: public void visitMethodInsn(int opcode,
264: String owner, String name, String desc) {
265: if (opcode == Opcodes.INVOKEINTERFACE)
266: return;
267: if (owner.charAt(0) == '['
268: && owner.charAt(owner.length() - 1) != ';') {
269: // primitive array
270: return;
271: }
272: owner = ClassName.fromSignature(owner);
273: if (opcode == Opcodes.INVOKESTATIC
274: && owner.equals("java/lang/System")
275: && name.equals("exit"))
276: sawSystemExit = true;
277: // System.out.println("Call from " + ClassParserUsingASM.this.slashedClassName + " to " + owner + " : " + desc);
278: if (desc.indexOf('[') == -1
279: && desc.indexOf('L') == -1)
280: return;
281: if (ClassParserUsingASM.this .slashedClassName
282: .equals(owner))
283: return;
284: ClassDescriptor classDescriptor = DescriptorFactory
285: .instance().getClassDescriptor(
286: owner);
287: calledClassSet.add(classDescriptor);
288: // System.out.println("Added call from " + ClassParserUsingASM.this.slashedClassName + " to " + owner);
289: state = State.AFTER_METHOD_CALL;
290: }
291:
292: @Override
293: public void visitJumpInsn(int opcode,
294: Label label) {
295: sawBranch = true;
296: super .visitJumpInsn(opcode, label);
297:
298: }
299:
300: public void visitEnd() {
301: if (sawThrow && !sawReturn || sawSystemExit
302: && !sawBranch)
303: mBuilder.setIsUnconditionalThrower();
304: MethodInfo methodInfo = mBuilder.build();
305: ((ClassInfo.Builder) cBuilder)
306: .addMethodDescriptor(methodInfo);
307: }
308:
309: public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(
310: int parameter, String desc,
311: boolean visible) {
312: if (false)
313: for (Iterator<String> i = new SignatureParser(
314: methodDesc)
315: .parameterSignatureIterator(); i
316: .hasNext();)
317: System.out
318: .println(" " + i.next());
319: AnnotationValue value = new AnnotationValue(
320: desc);
321: if (isInnerClass
322: && methodName.equals("<init>")) {
323: parameter++;
324: }
325: // System.out.println(isInnerClass + " parameter " + parameter + " of " + slashedClassName+"." + methodName +methodDesc + " is annotated " + desc);
326: mBuilder.addParameterAnnotation(parameter,
327: desc, value);
328: return value.getAnnotationVisitor();
329: }
330: };
331:
332: }
333: return null;
334: }
335:
336: public void visitOuterClass(String owner, String name,
337: String desc) {
338:
339: }
340:
341: public void visitSource(String arg0, String arg1) {
342: if (cBuilder instanceof ClassInfo.Builder) {
343: ((ClassInfo.Builder) cBuilder).setSource(arg0);
344: }
345:
346: }
347: }, ClassReader.SKIP_FRAMES);
348: HashSet<ClassDescriptor> referencedClassSet = new HashSet<ClassDescriptor>();
349:
350: // collect class references
351:
352: int constantPoolCount = classReader.readUnsignedShort(8);
353: int offset = 10;
354: char[] buf = new char[1024];
355: // System.out.println("constant pool count: " + constantPoolCount);
356: for (int count = 1; count < constantPoolCount; count++) {
357: int tag = classReader.readByte(offset);
358:
359: int size;
360: switch (tag) {
361: case Constants.CONSTANT_Methodref:
362: case Constants.CONSTANT_InterfaceMethodref:
363: case Constants.CONSTANT_Fieldref:
364: case Constants.CONSTANT_Integer:
365: case Constants.CONSTANT_Float:
366: case Constants.CONSTANT_NameAndType:
367: size = 5;
368: break;
369: case Constants.CONSTANT_Long:
370: case Constants.CONSTANT_Double:
371: size = 9;
372: count++;
373: break;
374: case Constants.CONSTANT_Utf8:
375: size = 3 + classReader.readUnsignedShort(offset + 1);
376: break;
377: case Constants.CONSTANT_Class:
378: @SlashedClassName
379: String className = classReader
380: .readUTF8(offset + 1, buf);
381: if (className.indexOf('[') >= 0) {
382: ClassParser.extractReferencedClassesFromSignature(
383: referencedClassSet, className);
384: } else if (ClassName.isValidClassName(className)) {
385: ClassDescriptor classDescriptor = DescriptorFactory
386: .instance().getClassDescriptor(className);
387: referencedClassSet.add(classDescriptor);
388: }
389: size = 3;
390: break;
391: // case ClassWriter.CLASS:
392: // case ClassWriter.STR:
393: case Constants.CONSTANT_String:
394: size = 3;
395: break;
396: default:
397: throw new IllegalStateException("Unexpected tag of "
398: + tag + " at offset " + offset
399: + " while parsing " + slashedClassName
400: + " from " + codeBaseEntry);
401: }
402: // System.out.println(count + "@" + offset + " : [" + tag +"] size="+size);
403: offset += size;
404: }
405: cBuilder.setCalledClassDescriptors(calledClassSet);
406: cBuilder.setReferencedClassDescriptors(referencedClassSet);
407: }
408:
409: public void parse(ClassInfo.Builder builder)
410: throws InvalidClassFileFormatException {
411: parse((ClassNameAndSuperclassInfo.Builder) builder);
412:
413: }
414: }
|