View Javadoc

    1   /**
    2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
    3    */
    4   package net.sourceforge.pmd.dcd.graph;
    5
    6   import java.io.IOException;
    7   import java.io.InputStream;
    8   import java.util.ArrayList;
    9   import java.util.Arrays;
    10  import java.util.List;
    11
    12  import net.sourceforge.pmd.dcd.asm.PrintVisitor;
    13  import net.sourceforge.pmd.dcd.asm.TypeSignatureVisitor;
    14  import net.sourceforge.pmd.util.filter.Filter;
    15
    16  import org.objectweb.asm.AnnotationVisitor;
    17  import org.objectweb.asm.Attribute;
    18  import org.objectweb.asm.ClassReader;
    19  import org.objectweb.asm.ClassVisitor;
    20  import org.objectweb.asm.FieldVisitor;
    21  import org.objectweb.asm.Label;
    22  import org.objectweb.asm.MethodVisitor;
    23  import org.objectweb.asm.signature.SignatureReader;
    24
    25  /**
    26   * Utility class used to build a UsageGraph.
    27   */
    28  public class UsageGraphBuilder {
    29
    30  	/**
    31  	 * Should trace level logging be enabled.  This is a development debugging
    32  	 * option.
    33  	 */
    34  	private static final boolean TRACE = false;
    35  	private static final boolean INDEX = true;
    36
    37  	protected final UsageGraph usageGraph;
    38  	protected final Filter<String> classFilter;
    39
    40  	public UsageGraphBuilder(Filter<String> classFilter) {
    41  		this.classFilter = classFilter;
    42  		this.usageGraph = new UsageGraph(classFilter);
    43  	}
    44
    45  	public void index(String name) {
    46  		try {
    47  			String className = getClassName(name);
    48  			String classResourceName = getResourceName(name);
    49  			if (classFilter.filter(className)) {
    50  				if (!usageGraph.isClass(className)) {
    51  					ClassNode classNode = usageGraph.defineClass(className);
    52  					InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(
    53  							classResourceName + ".class");
    54  					ClassReader classReader = new ClassReader(inputStream);
    55  					classReader.accept(getNewClassVisitor(), 0);
    56  				}
    57  			}
    58  		} catch (IOException e) {
    59  			throw new RuntimeException("For " + name + ": " + e.getMessage(), e);
    60  		}
    61  	}
    62
    63  	public UsageGraph getUsageGraph() {
    64  		return usageGraph;
    65  	}
    66
    67  	private ClassVisitor getNewClassVisitor() {
    68  		return new MyClassVisitor();
    69  	}
    70
    71  	// ASM visitor to on Class files to build a UsageGraph
    72  	class MyClassVisitor extends PrintVisitor implements ClassVisitor {
    73  		private String className;
    74
    75  		public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
    76  			if (TRACE) {
    77  				println("visit:");
    78  				printlnIndent("version: " + version);
    79  				printlnIndent("access: " + access);
    80  				printlnIndent("name: " + name);
    81  				printlnIndent("signature: " + signature);
    82  				printlnIndent("superName: " + superName);
    83  				printlnIndent("interfaces: " + asList(interfaces));
    84  			}
    85  			this.className = getClassName(name);
    86  		}
    87
    88  		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
    89  			if (TRACE) {
    90  				println("visitAnnotation:");
    91  				printlnIndent("desc: " + desc);
    92  				printlnIndent("visible: " + visible);
    93  			}
    94  			return null;
    95  		}
    96
    97  		public void visitAttribute(Attribute attr) {
    98  			if (TRACE) {
    99  				println("visitAttribute:");
    100 				printlnIndent("attr: " + attr);
    101 			}
    102 		}
    103
    104 		public void visitEnd() {
    105 			if (TRACE) {
    106 				println("visitEnd:");
    107 			}
    108 		}
    109
    110 		public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
    111 			if (TRACE) {
    112 				println("visitField:");
    113 				printlnIndent("access: " + access);
    114 				printlnIndent("name: " + name);
    115 				printlnIndent("desc: " + desc);
    116 				printlnIndent("signature: " + signature);
    117 				printlnIndent("value: " + value);
    118 			}
    119 			if (INDEX) {
    120 				SignatureReader signatureReader = new SignatureReader(desc);
    121 				TypeSignatureVisitor visitor = new TypeSignatureVisitor(this);
    122 				signatureReader.acceptType(visitor);
    123 				if (TRACE) {
    124 					printlnIndent("fieldType: " + visitor.getFieldType());
    125 				}
    126
    127 				usageGraph.defineField(className, name, desc);
    128 			}
    129 			return null;
    130 		}
    131
    132 		public void visitInnerClass(String name, String outerName, String innerName, int access) {
    133 			if (TRACE) {
    134 				println("visitInnerClass:");
    135 				printlnIndent("name: " + name);
    136 				printlnIndent("outerName: " + outerName);
    137 				printlnIndent("innerName: " + innerName);
    138 				printlnIndent("access: " + access);
    139 			}
    140 			index(name);
    141 		}
    142
    143 		public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
    144 			MemberNode memberNode = null;
    145 			if (TRACE) {
    146 				println("visitMethod:");
    147 				printlnIndent("access: " + access);
    148 				printlnIndent("name: " + name);
    149 				printlnIndent("desc: " + desc);
    150 				printlnIndent("signature: " + signature);
    151 				printlnIndent("exceptions: " + asList(exceptions));
    152 			}
    153 			if (INDEX) {
    154 				memberNode = usageGraph.defineMethod(className, name, desc);
    155 			}
    156 			return getNewMethodVisitor(this, memberNode);
    157 		}
    158
    159 		public void visitOuterClass(String owner, String name, String desc) {
    160 			if (TRACE) {
    161 				println("visitOuterClass:");
    162 				printlnIndent("owner: " + owner);
    163 				printlnIndent("name: " + name);
    164 				printlnIndent("desc: " + desc);
    165 			}
    166 		}
    167
    168 		public void visitSource(String source, String debug) {
    169 			if (TRACE) {
    170 				println("visitSource:");
    171 				printlnIndent("source: " + source);
    172 				printlnIndent("debug: " + debug);
    173 			}
    174 		}
    175 	}
    176
    177 	protected MethodVisitor getNewMethodVisitor(PrintVisitor parent, MemberNode usingMemberNode) {
    178 		return new MyMethodVisitor(parent, usingMemberNode);
    179 	}
    180
    181 	protected class MyMethodVisitor extends PrintVisitor implements MethodVisitor {
    182
    183 		private final MemberNode usingMemberNode;
    184
    185 		public MyMethodVisitor(PrintVisitor parent, MemberNode usingMemberNode) {
    186 			super(parent);
    187 			this.usingMemberNode = usingMemberNode;
    188 		}
    189
    190 		public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
    191 			if (TRACE) {
    192 				println("visitAnnotation:");
    193 				printlnIndent("desc: " + desc);
    194 				printlnIndent("visible: " + visible);
    195 			}
    196 			return null;
    197 		}
    198
    199 		public AnnotationVisitor visitAnnotationDefault() {
    200 			if (TRACE) {
    201 				println("visitAnnotationDefault:");
    202 			}
    203 			return null;
    204 		}
    205
    206 		public void visitAttribute(Attribute attr) {
    207 			if (TRACE) {
    208 				println("visitAttribute:");
    209 				printlnIndent("attr: " + attr);
    210 			}
    211 		}
    212
    213 		public void visitCode() {
    214 			if (TRACE) {
    215 				println("visitCode:");
    216 			}
    217 		}
    218
    219 		public void visitEnd() {
    220 			if (TRACE) {
    221 				println("visitEnd:");
    222 			}
    223 		}
    224
    225 		public void visitFieldInsn(int opcode, String owner, String name, String desc) {
    226 			if (TRACE) {
    227 				println("visitFieldInsn:");
    228 				printlnIndent("opcode: " + opcode);
    229 				printlnIndent("owner: " + owner);
    230 				printlnIndent("name: " + name);
    231 				printlnIndent("desc: " + desc);
    232 			}
    233 			if (INDEX) {
    234 				String className = getClassName(owner);
    235 				usageGraph.usageField(className, name, desc, usingMemberNode);
    236 			}
    237 		}
    238
    239 		public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
    240 			if (TRACE) {
    241 				println("visitFrame:");
    242 				printlnIndent("type: " + type);
    243 				printlnIndent("local: " + local);
    244 				printlnIndent("local2: " + asList(local2));
    245 				printlnIndent("stack: " + stack);
    246 				printlnIndent("stack2: " + asList(stack2));
    247 			}
    248 		}
    249
    250 		public void visitIincInsn(int var, int increment) {
    251 			if (TRACE) {
    252 				println("visitIincInsn:");
    253 				printlnIndent("var: " + var);
    254 				printlnIndent("increment: " + increment);
    255 			}
    256 		}
    257
    258 		public void visitInsn(int opcode) {
    259 			if (TRACE) {
    260 				println("visitInsn:");
    261 				printlnIndent("opcode: " + opcode);
    262 			}
    263 		}
    264
    265 		public void visitIntInsn(int opcode, int operand) {
    266 			if (TRACE) {
    267 				println("visitIntInsn:");
    268 				printlnIndent("opcode: " + opcode);
    269 				printlnIndent("operand: " + operand);
    270 			}
    271 		}
    272
    273 		public void visitJumpInsn(int opcode, Label label) {
    274 			if (TRACE) {
    275 				println("visitJumpInsn:");
    276 				printlnIndent("opcode: " + opcode);
    277 				printlnIndent("label: " + label);
    278 			}
    279 		}
    280
    281 		public void visitLabel(Label label) {
    282 			if (TRACE) {
    283 				println("visitLabel:");
    284 				printlnIndent("label: " + label);
    285 			}
    286 		}
    287
    288 		public void visitLdcInsn(Object cst) {
    289 			if (TRACE) {
    290 				println("visitLdcInsn:");
    291 				printlnIndent("cst: " + cst);
    292 			}
    293 		}
    294
    295 		public void visitLineNumber(int line, Label start) {
    296 			if (TRACE) {
    297 				println("visitLineNumber:");
    298 				printlnIndent("line: " + line);
    299 				printlnIndent("start: " + start);
    300 			}
    301 		}
    302
    303 		public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
    304 			if (TRACE) {
    305 				println("visitLocalVariable:");
    306 				printlnIndent("name: " + name);
    307 				printlnIndent("desc: " + desc);
    308 				printlnIndent("signature: " + signature);
    309 				printlnIndent("start: " + start);
    310 				printlnIndent("end: " + end);
    311 				printlnIndent("index: " + index);
    312 			}
    313 		}
    314
    315 		public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
    316 			if (TRACE) {
    317 				println("visitLookupSwitchInsn:");
    318 				printlnIndent("dflt: " + dflt);
    319 				printlnIndent("keys: " + asList(keys));
    320 				printlnIndent("labels: " + asList(labels));
    321 			}
    322 		}
    323
    324 		public void visitMaxs(int maxStack, int maxLocals) {
    325 			if (TRACE) {
    326 				println("visitMaxs:");
    327 				printlnIndent("maxStack: " + maxStack);
    328 				printlnIndent("maxLocals: " + maxLocals);
    329 			}
    330 		}
    331
    332 		public void visitMethodInsn(int opcode, String owner, String name, String desc) {
    333 			if (TRACE) {
    334 				println("visitMethodInsn:");
    335 				printlnIndent("opcode: " + opcode);
    336 				printlnIndent("owner: " + owner);
    337 				printlnIndent("name: " + name);
    338 				printlnIndent("desc: " + desc);
    339 			}
    340 			if (INDEX) {
    341 				String className = getClassName(owner);
    342 				usageGraph.usageMethod(className, name, desc, usingMemberNode);
    343 			}
    344 		}
    345
    346 		public void visitMultiANewArrayInsn(String desc, int dims) {
    347 			if (TRACE) {
    348 				println("visitMultiANewArrayInsn:");
    349 				printlnIndent("desc: " + desc);
    350 				printlnIndent("dims: " + dims);
    351 			}
    352 		}
    353
    354 		public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
    355 			if (TRACE) {
    356 				println("visitParameterAnnotation:");
    357 				printlnIndent("parameter: " + parameter);
    358 				printlnIndent("desc: " + desc);
    359 				printlnIndent("visible: " + visible);
    360 			}
    361 			return null;
    362 		}
    363
    364 		public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
    365 			if (TRACE) {
    366 				println("visitTableSwitchInsn:");
    367 				printlnIndent("min: " + min);
    368 				printlnIndent("max: " + max);
    369 				printlnIndent("dflt: " + dflt);
    370 				printlnIndent("labels: " + asList(labels));
    371 			}
    372 		}
    373
    374 		public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
    375 			if (TRACE) {
    376 				println("visitTryCatchBlock:");
    377 				printlnIndent("start: " + start);
    378 				printlnIndent("end: " + end);
    379 				printlnIndent("handler: " + handler);
    380 				printlnIndent("type: " + type);
    381 			}
    382 		}
    383
    384 		public void visitTypeInsn(int opcode, String desc) {
    385 			if (TRACE) {
    386 				println("visitTypeInsn:");
    387 				printlnIndent("opcode: " + opcode);
    388 				printlnIndent("desc: " + desc);
    389 			}
    390 		}
    391
    392 		public void visitVarInsn(int opcode, int var) {
    393 			if (TRACE) {
    394 				println("visitVarInsn:");
    395 				printlnIndent("opcode: " + opcode);
    396 				printlnIndent("var: " + var);
    397 			}
    398 		}
    399 	}
    400
    401 	private static String getResourceName(String name) {
    402 		return name.replace('.', '/');
    403 	}
    404
    405 	static String getClassName(String name) {
    406 		return name.replace('/', '.');
    407 	}
    408
    409 	private static List<Integer> asList(int[] array) {
    410 		List<Integer> list = null;
    411 		if (array != null) {
    412 			list = new ArrayList<Integer>(array.length);
    413 			for (int i : array) {
    414 				list.add(i);
    415 			}
    416 		}
    417 		return list;
    418 	}
    419
    420 	private static List<Object> asList(Object[] array) {
    421 		return array != null ? Arrays.asList(array) : null;
    422 	}
    423 }