/****************************************************************************** * Copyright (c) 2002 - 2006 IBM Corporation. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *****************************************************************************/ /* * Created on Oct 3, 2005 */ package com.ibm.wala.cast.java.test; import java.io.File; import java.io.IOException; import java.util.*; import java.util.jar.JarFile; import com.ibm.wala.util.debug.*; import com.ibm.wala.cast.ir.ssa.AstIRFactory; import com.ibm.wala.cast.java.client.EclipseProjectSourceAnalysisEngine; import com.ibm.wala.cast.java.ipa.callgraph.JavaSourceAnalysisScope; import com.ibm.wala.cast.java.loader.JavaSourceLoaderImpl; import com.ibm.wala.cast.java.translator.polyglot.IRTranslatorExtension; import com.ibm.wala.cast.java.translator.polyglot.PolyglotClassLoaderFactory; import com.ibm.wala.classLoader.ClassLoaderFactory; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IClassLoader; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.classLoader.JarFileModule; import com.ibm.wala.classLoader.SourceFileModule; import com.ibm.wala.client.AnalysisEngine; import com.ibm.wala.client.impl.AbstractAnalysisEngine; import com.ibm.wala.core.tests.util.WalaTestCase; import com.ibm.wala.ipa.callgraph.AnalysisOptions; import com.ibm.wala.ipa.callgraph.AnalysisScope; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; import com.ibm.wala.ipa.callgraph.CallGraphBuilder; import com.ibm.wala.ipa.callgraph.Entrypoints; import com.ibm.wala.ipa.callgraph.impl.Everywhere; import com.ibm.wala.ipa.callgraph.impl.Util; import com.ibm.wala.ipa.cha.ClassHierarchy; import com.ibm.wala.ipa.cha.ClassHierarchyException; import com.ibm.wala.ssa.*; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.Atom; import com.ibm.wala.util.collections.*; import com.ibm.wala.util.warnings.WarningSet; import junit.framework.*; public abstract class IRTests extends WalaTestCase { public IRTests(String name) { super(name); } protected static String javaHomePath= System.getProperty("java.home"); protected static String testSrcPath= "." + File.separator + "testSrc"; protected static List/**/ rtJar; static { rtJar = new LinkedList(); rtJar.add(javaHomePath + File.separator + "lib" + File.separator + "rt.jar"); rtJar.add(javaHomePath + File.separator + "lib" + File.separator + "core.jar"); rtJar.add(javaHomePath + File.separator + "lib" + File.separator + "vm.jar"); } protected static class EdgeAssertions { public final String srcDescriptor; public final List/**/ tgtDescriptors= new ArrayList(); public EdgeAssertions(String srcDescriptor) { this.srcDescriptor= srcDescriptor; } public static EdgeAssertions make(String srcDescriptor, String tgtDescriptor) { EdgeAssertions ea= new EdgeAssertions(srcDescriptor); ea.tgtDescriptors.add(tgtDescriptor); return ea; } public static EdgeAssertions make(String srcDescriptor, String tgtDescriptor1, String tgtDescriptor2) { EdgeAssertions ea= new EdgeAssertions(srcDescriptor); ea.tgtDescriptors.add(tgtDescriptor1); ea.tgtDescriptors.add(tgtDescriptor2); return ea; } public static EdgeAssertions make(String srcDescriptor, String tgtDescriptor1, String tgtDescriptor2, String tgtDescriptor3) { EdgeAssertions ea= new EdgeAssertions(srcDescriptor); ea.tgtDescriptors.add(tgtDescriptor1); ea.tgtDescriptors.add(tgtDescriptor2); ea.tgtDescriptors.add(tgtDescriptor3); return ea; } public static EdgeAssertions make(String srcDescriptor, String tgtDescriptor1, String tgtDescriptor2, String tgtDescriptor3, String tgtDescriptor4) { EdgeAssertions ea= new EdgeAssertions(srcDescriptor); ea.tgtDescriptors.add(tgtDescriptor1); ea.tgtDescriptors.add(tgtDescriptor2); ea.tgtDescriptors.add(tgtDescriptor3); ea.tgtDescriptors.add(tgtDescriptor4); return ea; } } protected static class GraphAssertions { public final Set/**/ nodeAssertions= new HashSet(); public GraphAssertions() { } public GraphAssertions(EdgeAssertions ea1) { nodeAssertions.add(ea1); } public GraphAssertions(EdgeAssertions ea1, EdgeAssertions ea2) { nodeAssertions.add(ea1); nodeAssertions.add(ea2); } public GraphAssertions(EdgeAssertions ea1, EdgeAssertions ea2, EdgeAssertions ea3) { nodeAssertions.add(ea1); nodeAssertions.add(ea2); nodeAssertions.add(ea3); } public GraphAssertions(EdgeAssertions ea1, EdgeAssertions ea2, EdgeAssertions ea3, EdgeAssertions ea4) { nodeAssertions.add(ea1); nodeAssertions.add(ea2); nodeAssertions.add(ea3); nodeAssertions.add(ea4); } } protected static class SourceMapAssertion { private final String variableName; private final int definingLineNumber; protected SourceMapAssertion(String variableName, int definingLineNumber) { this.variableName = variableName; this.definingLineNumber = definingLineNumber; } boolean check(IMethod m, IR ir) { Trace.println( "check for " + variableName + " defined at " + definingLineNumber); SSAInstruction[] insts = ir.getInstructions(); for(int i = 0; i < insts.length; i++) { if (insts[i] != null) { int ln = m.getLineNumber( i ); if (ln == definingLineNumber) { Trace.println(" found " + insts[i] + " at " + ln); for(int j = 0; j < insts[i].getNumberOfDefs(); j++) { int def = insts[i].getDef(j); Trace.println(" looking at def " + j + ": " + def); String[] names = ir.getLocalNames(i, def); if (names != null) { for(int n = 0; n < names.length; n++) { Trace.println(" looking at name " + names[n]); if (names[n].equals(variableName)) { return true; } } } } } } } return false; } } protected static class SourceMapAssertions { private final Map methodAssertions = new HashMap(); protected void addAssertion(String method, SourceMapAssertion a) { Set x = MapUtil.findOrCreateSet(methodAssertions, method); x.add(a); } void check(CallGraph CG) { WarningSet ws = new WarningSet(); for(Iterator ms = methodAssertions.entrySet().iterator(); ms.hasNext(); ) { Map.Entry entry = (Map.Entry)ms.next(); Set s = (Set) entry.getValue(); String method = (String) entry.getKey(); MethodReference mref = descriptorToMethodRef(method, CG.getClassHierarchy()); for(Iterator ns = CG.getNodes(mref).iterator(); ns.hasNext(); ) { CGNode n = (CGNode) ns.next(); for(Iterator as = s.iterator(); as.hasNext(); ) { SourceMapAssertion a = (SourceMapAssertion) as.next(); Assert.assertTrue( "failed for " + a.variableName + " in " + n, a.check(n.getMethod(), CG.getInterpreter(n).getIR(n, ws))); } } } } } protected abstract String singleInputForTest(); protected abstract String singlePkgInputForTest(String pkgName); protected Collection singleTestSrc() { return Collections.singletonList(testSrcPath + File.separator + singleInputForTest()); } protected Collection singlePkgTestSrc(String pkgName) { return Collections.singletonList(testSrcPath + File.separator + singlePkgInputForTest(pkgName)); } protected String[] simpleTestEntryPoint() { return new String[] { "L" + getName().substring(4) }; } protected String[] simplePkgTestEntryPoint(String pkgName) { return new String[] { "L" + pkgName + "/" + getName().substring(4) }; } protected abstract EclipseProjectSourceAnalysisEngine getAnalysisEngine(String[] mainClassDescriptors); public void runTest(Collection/**/ sources, List/**/ libs, String[] mainClassDescriptors, GraphAssertions ga, SourceMapAssertions sa) { try { EclipseProjectSourceAnalysisEngine engine= getAnalysisEngine(mainClassDescriptors); populateScope(engine, sources, libs); CallGraph callGraph = engine.buildDefaultCallGraph(); // If we've gotten this far, IR has been produced. dumpIR(callGraph); // Now check any assertions as to source mapping if (sa != null) { sa.check(callGraph); } // Now check any assertions as to call-graph shape. checkCallGraphShape(callGraph, ga); } catch (IOException e) { e.printStackTrace(); } } private static void dumpIR(CallGraph cg) throws IOException { WarningSet warnings= new WarningSet(); ClassHierarchy cha = cg.getClassHierarchy(); IClassLoader sourceLoader = cha.getLoader(JavaSourceAnalysisScope.SOURCE_REF); for(Iterator iter= sourceLoader.iterateAllClasses(); iter.hasNext(); ) { IClass clazz= (IClass) iter.next(); System.out.println(clazz); if (clazz.isInterface()) continue; for(Iterator iterator= clazz.getDeclaredMethods().iterator(); iterator.hasNext(); ) { IMethod m= (IMethod) iterator.next(); if (m.isAbstract()) System.out.println(m); else { Iterator nodeIter= cg.getNodes(m.getReference()).iterator(); if (!nodeIter.hasNext()) { System.err.println("Source method " + m.getReference() + " not reachable?"); continue; } CGNode node= (CGNode) nodeIter.next(); System.out.println(cg.getInterpreter(node).getIR(node, warnings)); } } } } private static void checkCallGraphShape(CallGraph callGraph, GraphAssertions ga) throws IOException { Trace.println(callGraph.toString()); for(Iterator nodeIter= ga.nodeAssertions.iterator(); nodeIter.hasNext(); ) { EdgeAssertions ea= (EdgeAssertions) nodeIter.next(); MethodReference srcMethod= descriptorToMethodRef(ea.srcDescriptor, callGraph.getClassHierarchy()); Set/**/ srcNodes= callGraph.getNodes(srcMethod); if (srcNodes.size() == 0) { System.err.println("Unreachable/non-existent method: " + srcMethod); continue; } if (srcNodes.size() > 1) { System.err.println("Context-sensitive call graph?"); } // Assume only one node for src method CGNode srcNode= (CGNode) srcNodes.iterator().next(); for(Iterator edgeIter= ea.tgtDescriptors.iterator(); edgeIter.hasNext(); ) { String target= (String) edgeIter.next(); MethodReference tgtMethod= descriptorToMethodRef(target, callGraph.getClassHierarchy()); // Assume only one node for target method Set tgtNodes= callGraph.getNodes(tgtMethod); if (tgtNodes.size() == 0) { System.err.println("Unreachable/non-existent method: " + tgtMethod); continue; } CGNode tgtNode= (CGNode) tgtNodes.iterator().next(); boolean found= false; for(Iterator succIter= callGraph.getSuccNodes(srcNode); succIter.hasNext(); ) { CGNode succ= (CGNode) succIter.next(); if (tgtNode == succ) { found= true; break; } } if (!found) System.err.println("Missing edge: " + srcMethod + " -> " + tgtMethod); } } } private static MethodReference descriptorToMethodRef(String descrip, ClassHierarchy cha) { String srcDescriptor= descrip; // ldr#type#methName#methSig String[] ldrTypeMeth= srcDescriptor.split("\\#"); String loaderName= ldrTypeMeth[0]; String typeStr= ldrTypeMeth[1]; String methName= ldrTypeMeth[2]; String methSig= ldrTypeMeth[3]; ClassLoaderReference clr= findLoader(loaderName, cha); TypeName typeName= TypeName.string2TypeName("L" + typeStr); TypeReference typeRef= TypeReference.findOrCreate(clr, typeName); MethodReference methodRef= MethodReference.findOrCreate(typeRef, methName, methSig); return methodRef; } private static ClassLoaderReference findLoader(String loaderName, ClassHierarchy cha) { Atom loaderAtom = Atom.findOrCreateUnicodeAtom(loaderName); IClassLoader[] loaders = cha.getLoaders(); for(int i = 0; i < loaders.length; i++) { if (loaders[i].getName() == loaderAtom) return loaders[i].getReference(); } Assertions.UNREACHABLE(); return null; } private static void populateScope(EclipseProjectSourceAnalysisEngine engine, Collection/**/ sources, List/**/ libs) throws IOException { JavaSourceAnalysisScope scope= new JavaSourceAnalysisScope(); boolean foundLib = false; for(Iterator iter= libs.iterator(); iter.hasNext(); ) { String lib= (String) iter.next(); File libFile= new File(lib); if (libFile.exists()) { foundLib = true; engine.addSystemModule(new JarFileModule(new JarFile(libFile))); } } Assertions._assert(foundLib); for(Iterator iter= sources.iterator(); iter.hasNext(); ) { String srcFilePath= (String) iter.next(); String srcFileName= srcFilePath.substring(srcFilePath.lastIndexOf(File.separator)+1); engine.addSourceModule(new SourceFileModule(new File(srcFilePath), srcFileName)); } } }