private static Map<String, Set<String>> createStaticFields( String targetClassName) { GetStaticGraph getStaticGraph = GetStaticGraphGenerator .generate(targetClassName); return getStaticGraph.getStaticFields(); }
public static GetStaticGraph generate(String className) { ClassNode targetClass = DependencyAnalysis.getClassNode(className); GetStaticGraph staticUsageTree = new GetStaticGraph(); if (targetClass != null) handle(staticUsageTree, targetClass, 0); if (Properties.INSTRUMENT_PARENT) { handleSuperClasses(staticUsageTree, targetClass); } return staticUsageTree; }
/** * Add all possible calls for a given method * * @param callGraph * @param mn */ @SuppressWarnings("unchecked") private static void handleMethodNode(GetStaticGraph staticUsageTree, ClassNode cn, MethodNode mn, int depth) { InsnList instructions = mn.instructions; Iterator<AbstractInsnNode> iterator = instructions.iterator(); // TODO: This really shouldn't be here but in its own class while (iterator.hasNext()) { AbstractInsnNode insn = iterator.next(); if (insn instanceof MethodInsnNode) { handleMethodInsnNode(staticUsageTree, cn, mn, (MethodInsnNode) insn, depth + 1); } else if (insn instanceof FieldInsnNode) { handleFieldInsnNode(staticUsageTree, cn, mn, (FieldInsnNode) insn, depth + 1); } } }
if (!isOverridden(mn.name + mn.desc)) { handleMethodNode(staticUsageTree, superClass, mn, 0); handleSuperClasses(staticUsageTree, superClass);
/** * Descend into a static method call * */ private static void handleMethodInsnNode(GetStaticGraph staticUsageTree, ClassNode cn, MethodNode mn, MethodInsnNode methodCall, int depth) { // Skip if method call is not static if (methodCall.getOpcode() != Opcodes.INVOKESTATIC) { return; } // Only collect relations for instrumentable classes String calleeClassName = methodCall.owner.replaceAll("/", "."); if (BytecodeInstrumentation.checkIfCanInstrument(calleeClassName)) { logger.debug("Handling method: " + methodCall.name); handleClassInitializer(staticUsageTree, cn, mn, methodCall.owner, depth); if (!staticUsageTree.hasStaticMethodCall(cn.name, mn.name + mn.desc, methodCall.owner, methodCall.name + methodCall.desc)) { // Add call from mn to methodCall to callgraph staticUsageTree.addStaticMethodCall(cn.name, mn.name + mn.desc, methodCall.owner, methodCall.name + methodCall.desc); handle(staticUsageTree, methodCall.owner, methodCall.name + methodCall.desc, depth); } } }
@SuppressWarnings("unchecked") private static void handle(GetStaticGraph staticUsageTree, ClassNode targetClass, String methodName, int depth) { List<MethodNode> methods = targetClass.methods; for (MethodNode mn : methods) { if (methodName.equals(mn.name + mn.desc)) handleMethodNode(staticUsageTree, targetClass, mn, depth); } }
private static void handle(GetStaticGraph staticUsageTree, String className, String methodName, int depth) { ClassNode cn = DependencyAnalysis.getClassNode(className); if (cn == null) return; handle(staticUsageTree, cn, methodName, depth); }
/** * Descend into a static field read * */ private static void handleFieldInsnNode(GetStaticGraph staticUsageTree, ClassNode cn, MethodNode mn, FieldInsnNode insn, int depth) { // Skip field instructions that are not reads to static fields if (insn.getOpcode() != Opcodes.GETSTATIC) { return; } // Only collect relations for instrumentable classes String calleeClassName = insn.owner.replaceAll("/", "."); if (BytecodeInstrumentation.checkIfCanInstrument(calleeClassName)) { logger.debug("Handling field read: " + insn.name); if (!staticUsageTree.hasStaticFieldRead(cn.name, mn.name + mn.desc, insn.owner, insn.name)) { handleClassInitializer(staticUsageTree, cn, mn, insn.owner, depth); // Add static read from mn to insn to static usage graph staticUsageTree.addStaticFieldRead(cn.name, mn.name + mn.desc, insn.owner, insn.name); handle(staticUsageTree, insn.owner, insn.name + insn.desc, depth); } } }
@SuppressWarnings("unchecked") private static void handle(GetStaticGraph staticUsageTree, ClassNode targetClass, int depth) { List<MethodNode> methods = targetClass.methods; for (MethodNode mn : methods) { logger.debug("Method: " + mn.name); handleMethodNode(staticUsageTree, targetClass, mn, depth); } }
/** * Descend into a <clinit> */ private static void handleClassInitializer(GetStaticGraph staticUsageTree, ClassNode cn, MethodNode mn, String owner, int depth) { if (!staticUsageTree.hasStaticMethodCall(cn.name, mn.name + mn.desc, owner, CLASS_INIT_NAME)) { // Add call from mn to methodCall to callgraph staticUsageTree.addStaticMethodCall(cn.name, mn.name + mn.desc, owner, CLASS_INIT_NAME); // handle callee's <clinit> handle(staticUsageTree, owner, CLASS_INIT_NAME, depth); } }
@Test public void testFoo() { String targetClassName = Foo.class.getCanonicalName(); GetStaticGraph graph = GetStaticGraphGenerator .generate(targetClassName); Set<String> expectedClasses = new HashSet<String>(Arrays.asList( Foo.class.getCanonicalName(), Bar1.class.getCanonicalName(), Bar2.class.getCanonicalName(), Bar3.class.getCanonicalName(), Bar4.class.getCanonicalName(), Bar5.class.getCanonicalName(), Bar6.class.getCanonicalName(), Bar7.class.getCanonicalName(), BarBar1.class.getCanonicalName())); Set<String> allClasses = new HashSet<String>(); allClasses.addAll(graph.getSourceClasses()); allClasses.addAll(graph.getTargetClasses()); assertEquals(expectedClasses, allClasses); Map<String, Set<String>> expectedStaticFields = new HashMap<String, Set<String>>(); expectedStaticFields.put(Bar2.class.getCanonicalName(), new HashSet<String>(Arrays.asList("fieldBar2"))); expectedStaticFields.put(Bar6.class.getCanonicalName(), new HashSet<String>(Arrays.asList("fieldBar6"))); expectedStaticFields.put(Bar7.class.getCanonicalName(), new HashSet<String>(Arrays.asList("fieldBar7"))); Map<String, Set<String>> staticFields = graph.getStaticFields(); assertEquals(expectedStaticFields, staticFields); }
@Test public void testCycle() { String targetClassName = Cycle1.class.getCanonicalName(); GetStaticGraph graph = GetStaticGraphGenerator .generate(targetClassName); Set<String> expectedSourceClasses = new HashSet<String>(Arrays.asList( Cycle1.class.getCanonicalName(), Cycle2.class.getCanonicalName())); assertEquals(expectedSourceClasses, graph.getSourceClasses()); Set<String> expectedTargetClasses = expectedSourceClasses; assertEquals(expectedTargetClasses, graph.getTargetClasses()); Map<String, Set<String>> staticFields = graph.getStaticFields(); assertTrue(staticFields.isEmpty()); } }
GetStaticGraph getStaticGraph = GetStaticGraphGenerator.generate(Properties.TARGET_CLASS);