/******************************************************************************* * 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 *******************************************************************************/ package com.ibm.wala.util.warnings; import java.util.Iterator; import java.util.Set; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.classLoader.SyntheticMethod; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.callgraph.CallGraph; import com.ibm.wala.ipa.callgraph.impl.UnresolvedReflectionWarning; import com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter; 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.HashSetFactory; import com.ibm.wala.util.collections.IteratorUtil; /** * * Support to walk a call graph and generate warnings. * * @author sfink */ public class CallGraphWarnings { private static final TypeReference[] ignoredTypeArray = { TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Ljava/lang/Object"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/apache/xerces/dom3/UserDataHandler"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/w3c/dom/events/EventListener"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/w3c/dom/ls/DOMBuilderFilter"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/apache/xerces/dom/PSVIAttrNSImpl"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/apache/xerces/dom/PSVIElementNSImpl"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/apache/xerces/dom/NodeIteratorImpl"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/apache/xml/serializer/SerializerTrace"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/xml/sax/DocumentHandler"), TypeReference.findOrCreate(ClassLoaderReference.Primordial, "Lorg/apache/xerces/dom/RangeImpl") }; /** * Set of TypeReferences representing types which we do not report unreachable * warnings for. */ private static Set ignoredTypes = HashSetFactory.make(); static { for (int i = 0; i < ignoredTypeArray.length; i++) { ignoredTypes.add(ignoredTypeArray[i]); } } /** * Set of Atoms representing package names whose methods should be treated as * no-ops */ private static Set ignoredPackages = HashSetFactory.make(); /** * Register a package names whose methods should be treated as no-ops; don't * report unreachable warnings for these */ public static synchronized void ignorePackage(Atom p) { ignoredPackages.add(p); } /** * @param m * @return true iff we can ignore unreachable warnings for m */ private static boolean canIgnore(MethodReference m) { TypeReference T = m.getDeclaringClass(); TypeName n = T.getName(); Atom p = n.getPackage(); return (ignoredPackages.contains(p) || ignoredTypes.contains(m.getDeclaringClass())); } /** * @param cg * Call Graph * @return set of warnings inferred from the call graph. */ public static WarningSet getWarnings(CallGraph cg) { WarningSet result = new WarningSet(); if (cg.getNumberOfNodes() == 1) { result.add(NoEntrypointsFailure.INSTANCE); } else { for (Iterator it = cg.iterateNodes(); it.hasNext();) { CGNode n = (CGNode) it.next(); RTAContextInterpreter interp = cg.getInterpreter(n); addWarningsForNode(result, n, interp); } } return result; } /** * @author sfink */ private static class NoEntrypointsFailure extends Warning { private static NoEntrypointsFailure INSTANCE = new NoEntrypointsFailure(); NoEntrypointsFailure() { super(Warning.SEVERE); } public String getMsg() { return getClass().toString(); } } private static void addWarningsForNode(WarningSet warnings, CGNode n, RTAContextInterpreter interp) { IMethod m = n.getMethod(); if (m == null) { return; } if (m.isSynthetic()) { SyntheticMethod s = (SyntheticMethod) m; if (s.hasPoison()) { warnings.add(new PoisonWarning(s.getPoisonLevel(),n)); } if (s.isFactoryMethod()) { int count = IteratorUtil.count(interp.iterateNewSites(n, new WarningSet())); if (count == 1) { warnings.add(new UnresolvedReflectionWarning(n)); } } } if (m.isNative()) { warnings.add(new NativeWarning(n)); } // check that every call site in node has at least one successor for (Iterator it = interp.iterateCallSites(n, warnings); it.hasNext();) { CallSiteReference site = (CallSiteReference) it.next(); Iterator targets = n.getPossibleTargets(site).iterator(); if (!targets.hasNext()) { if (!canIgnore(site.getDeclaredTarget())) { warnings.add(new NoCalleeWarning(n, site)); } } } } /** * @author sfink * */ private static class PoisonWarning extends MethodWarning { final String poison; public PoisonWarning(byte level, CGNode node) { super(level, node.getMethod().getReference()); poison = ((SyntheticMethod)node.getMethod()).getPoison(); } public String getMsg() { return getClass() + " " + poison; } } /** * @author sfink * * A warning generated by reaching an unmodelled native method */ private static class NativeWarning extends MethodWarning { /** * @param node */ public NativeWarning(CGNode node) { super(SEVERE, node.getMethod().getReference()); } /* * (non-Javadoc) * * @see com.ibm.wala.util.Warning#getMsg() */ public String getMsg() { return "Native method " + getMethod(); } } /** * @author sfink * * A warning generated by reaching an call site with no callees found. */ private static class NoCalleeWarning extends MethodWarning { private CallSiteReference site; /** * @param node */ public NoCalleeWarning(CGNode node, CallSiteReference site) { super(node.getMethod().getReference()); this.site = site; ClassLoaderReference cl = node.getMethod().getDeclaringClass().getClassLoader().getReference(); if (cl.equals(ClassLoaderReference.Primordial)) { setLevel(CLIENT_MILD); } else if (cl.equals(ClassLoaderReference.Extension)) { setLevel(CLIENT_MODERATE); } else { setLevel(CLIENT_SEVERE); } } /* * (non-Javadoc) * * @see com.ibm.wala.util.Warning#getMsg() */ public String getMsg() { return "No callee for " + site + " in node " + getMethod(); } /* * (non-Javadoc) * * @see com.ibm.wala.util.warnings.Warning#severityString() */ protected String severityString() { return "Unreachable call"; } } }