/** * @param m a method reference * @return a SyntheticMethod corresponding to m; or null if none is available. */ protected SyntheticMethod findOrCreateSyntheticMethod(MethodReference m, boolean isStatic) { if (syntheticMethods.containsKey(m)) { return syntheticMethods.get(m); } else { MethodSummary summ = null; if (canIgnore(m)) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { // did not load class; don't try to create a synthetic method syntheticMethods.put(m, null); return null; } summ = generateNoOp(m, isStatic); } else { summ = findSummary(m); } if (summ != null) { TypeReference T = m.getDeclaringClass(); IClass C = cha.lookupClass(T); if (C == null) { syntheticMethods.put(m, null); return null; } SummarizedMethod n = new SummarizedMethod(m, summ, C); syntheticMethods.put(m, n); return n; } else { syntheticMethods.put(m, null); return null; } } }
@Test public void testClassAnnotations3() throws Exception { TypeReference typeRef = TypeReference.findOrCreate( ClassLoaderReference.Application, "Lannotations/AnnotatedClass3"); IClass klass = cha.lookupClass(typeRef); Assert.assertNotNull(klass); ShrikeClass shrikeClass = (ShrikeClass) klass; Collection<Annotation> classAnnotations = shrikeClass.getAnnotations(true); Assert.assertEquals( "[Annotation type <Application,Lannotations/AnnotationWithParams> {strParam=classStrParam}]", classAnnotations.toString()); MethodReference methodRefUnderTest = MethodReference.findOrCreate(typeRef, Selector.make("foo()V")); IMethod methodUnderTest = cha.resolveMethod(methodRefUnderTest); Assert.assertNotNull(methodRefUnderTest.toString() + " not found", methodUnderTest); Assert.assertTrue(methodUnderTest instanceof ShrikeCTMethod); ShrikeCTMethod shrikeCTMethodUnderTest = (ShrikeCTMethod) methodUnderTest; Collection<Annotation> runtimeInvisibleAnnotations = shrikeCTMethodUnderTest.getAnnotations(true); Assert.assertEquals( "[Annotation type <Application,Lannotations/AnnotationWithParams> {enumParam=EnumElementValue [type=Lannotations/AnnotationEnum;, val=VAL1], strArrParam=ArrayElementValue [vals=[biz, boz]], annotParam=AnnotationElementValue [type=Lannotations/AnnotationWithSingleParam;, elementValues={value=sdfevs}], strParam=sdfsevs, intParam=25, klassParam=Ljava/lang/Integer;}]", runtimeInvisibleAnnotations.toString()); }
/** * @see * com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode) */ @Override public boolean understands(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } if (!(node.getContext() instanceof GetMethodContext)) { return false; } MethodReference mRef = node.getMethod().getReference(); return mRef.equals(GET_METHOD) || mRef.equals(GET_DECLARED_METHOD); }
/* * @see com.ibm.wala.classLoader.IMethod#getParameterType(int) */ public TypeReference getParameterType(int i) { if (isStatic()) { return method.getParameterType(i); } else { if (i == 0) { return method.getDeclaringClass(); } else { return method.getParameterType(i - 1); } } }
/** * Create statements for methods like getMethod() and getDeclaredMethod(), which return a single * method. This creates a return statement for each possible return value, each of which is a * {@link ConstantValue} for an {@link IMethod}. * * @param returnValues the possible return values for this method * @return the statements */ private SSAInstruction[] getParticularMethodStatements( MethodReference ref, Collection<IMethod> returnValues, GetMethodContext context, Map<Integer, ConstantValue> constants) { ArrayList<SSAInstruction> statements = new ArrayList<SSAInstruction>(); int nextLocal = ref.getNumberOfParameters() + 2; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), c, false); statements.add(R); } } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator<SSAInstruction> it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; }
/** A {@link ContextSelector} to intercept calls to Object.getClass() */ class GetClassContextSelector implements ContextSelector { public static final MethodReference GET_CLASS = MethodReference.findOrCreate(TypeReference.JavaLangObject, "getClass", "()Ljava/lang/Class;"); public GetClassContextSelector() {} /* * @see com.ibm.wala.ipa.callgraph.ContextSelector#getCalleeTarget(com.ibm.wala.ipa.callgraph.CGNode, * com.ibm.wala.classLoader.CallSiteReference, com.ibm.wala.classLoader.IMethod, * com.ibm.wala.ipa.callgraph.propagation.InstanceKey) */ public Context getCalleeTarget( CGNode caller, CallSiteReference site, IMethod callee, InstanceKey[] receiver) { if (callee.getReference().equals(GET_CLASS)) { return new JavaTypeContext(new PointType(receiver[0].getConcreteType())); } return null; } private static final IntSet thisParameter = IntSetUtil.make(new int[] {0}); public IntSet getRelevantParameters(CGNode caller, CallSiteReference site) { if (site.isDispatch() || site.getDeclaredTarget().getNumberOfParameters() > 0) { return thisParameter; } else { return EmptyIntSet.instance; } } }
@Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((declaringClass == null) ? 0 : declaringClass.hashCode()); result = prime * result + ((method == null) ? 0 : method.hashCode()); return result; }
@Override public String toString() { StringBuffer s = new StringBuffer("synthetic "); if (isFactoryMethod()) { s.append(" factory "); } s.append(method.toString()); return s.toString(); }
/** make main entrypoints, even in the primordial loader. */ public static Iterable<Entrypoint> makePrimordialMainEntrypoints( AnalysisScope scope, ClassHierarchy cha) { final Atom mainMethod = Atom.findOrCreateAsciiAtom("main"); final HashSet<Entrypoint> result = HashSetFactory.make(); for (IClass klass : cha) { MethodReference mainRef = MethodReference.findOrCreate( klass.getReference(), mainMethod, Descriptor.findOrCreateUTF8("([Ljava/lang/String;)V")); IMethod m = klass.getMethod(mainRef.getSelector()); if (m != null) { result.add(new DefaultEntrypoint(m, cha)); } } return new Iterable<Entrypoint>() { @Override public Iterator<Entrypoint> iterator() { return result.iterator(); } }; }
/** * Add an instruction to invoke the default constructor on the object of value number alloc of * type t. */ protected void addCtorInvokeInstruction(final TypeReference t, int alloc) { MethodReference init = MethodReference.findOrCreate( t, MethodReference.initAtom, MethodReference.defaultInitDesc); CallSiteReference site = CallSiteReference.make(getCallSiteForType(t), init, IInvokeInstruction.Dispatch.SPECIAL); int[] params = new int[1]; params[0] = alloc; int exc = getExceptionsForType(t); SSAInvokeInstruction s = insts.InvokeInstruction(params, exc, site); calls.add(s); allInstructions.add(s); }
@Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final SyntheticMethod other = (SyntheticMethod) obj; if (declaringClass == null) { if (other.declaringClass != null) return false; } else if (!declaringClass.equals(other.declaringClass)) return false; if (method == null) { if (other.method != null) return false; } else if (!method.equals(other.method)) return false; return true; }
/** * Add any exceptional edges generated by the last instruction in a basic block. * * @param last the last instruction in a basic block. */ protected void addExceptionalEdges(IInstruction last) { IClassHierarchy cha = getMethod().getClassHierarchy(); if (last.isPEI()) { Collection<TypeReference> exceptionTypes = null; boolean goToAllHandlers = false; ExceptionHandler[] hs = getExceptionHandlers(); if (last instanceof ThrowInstruction) { // this class does not have the type information needed // to determine what the athrow throws. So, add an // edge to all reachable handlers. Better information can // be obtained later with SSA type propagation. // TODO: consider pruning to only the exception types that // this method either catches or allocates, since these are // the only types that can flow to an athrow. goToAllHandlers = true; } else { if (hs != null && hs.length > 0) { IClassLoader loader = getMethod().getDeclaringClass().getClassLoader(); BytecodeLanguage l = (BytecodeLanguage) loader.getLanguage(); exceptionTypes = l.getImplicitExceptionTypes(last); if (last instanceof IInvokeInstruction) { IInvokeInstruction call = (IInvokeInstruction) last; exceptionTypes = HashSetFactory.make(exceptionTypes); MethodReference target = MethodReference.findOrCreate( l, loader.getReference(), call.getClassType(), call.getMethodName(), call.getMethodSignature()); try { exceptionTypes.addAll(l.inferInvokeExceptions(target, cha)); } catch (InvalidClassFileException e) { e.printStackTrace(); Assertions.UNREACHABLE(); } } } } if (hs != null && hs.length > 0) { // found a handler for this PEI // create a mutable copy if (!goToAllHandlers) { exceptionTypes = HashSetFactory.make(exceptionTypes); } // this var gets set to false if goToAllHandlers is true but some enclosing exception // handler catches all // exceptions. in such a case, we need not add an exceptional edge to the method exit boolean needEdgeToExitForAllHandlers = true; for (int j = 0; j < hs.length; j++) { if (DEBUG) { System.err.println(" handler " + hs[j]); } BasicBlock b = getBlockForInstruction(hs[j].getHandler()); if (DEBUG) { System.err.println(" target " + b); } if (goToAllHandlers) { // add an edge to the catch block. if (DEBUG) { System.err.println(" gotoAllHandlers " + b); } addExceptionalEdgeTo(b); // if the handler catches all exceptions, we don't need to add an edge to the exit or // any other handlers if (hs[j].getCatchClass() == null) { needEdgeToExitForAllHandlers = false; break; } } else { TypeReference caughtException = null; if (hs[j].getCatchClass() != null) { ClassLoaderReference loader = ShrikeCFG.this.getMethod().getDeclaringClass().getReference().getClassLoader(); caughtException = ShrikeUtil.makeTypeReference(loader, hs[j].getCatchClass()); if (DEBUG) { System.err.println(" caughtException " + caughtException); } IClass caughtClass = cha.lookupClass(caughtException); if (caughtClass == null) { // conservatively add the edge, and raise a warning addExceptionalEdgeTo(b); Warnings.add(FailedExceptionResolutionWarning.create(caughtException)); // null out caughtException, to avoid attempting to process it caughtException = null; } } else { if (DEBUG) { System.err.println(" catchClass() == null"); } // hs[j].getCatchClass() == null. // this means that the handler catches all exceptions. // add the edge and null out all types if (!exceptionTypes.isEmpty()) { addExceptionalEdgeTo(b); exceptionTypes.clear(); caughtException = null; } } if (caughtException != null) { IClass caughtClass = cha.lookupClass(caughtException); // the set "caught" should be the set of exceptions that MUST // have been caught by the handlers in scope ArrayList<TypeReference> caught = new ArrayList<TypeReference>(exceptionTypes.size()); // check if we should add an edge to the catch block. for (TypeReference t : exceptionTypes) { if (t != null) { IClass klass = cha.lookupClass(t); if (klass == null) { Warnings.add(FailedExceptionResolutionWarning.create(caughtException)); // conservatively add an edge addExceptionalEdgeTo(b); } else { boolean subtype1 = cha.isSubclassOf(klass, caughtClass); if (subtype1 || cha.isSubclassOf(caughtClass, klass)) { // add the edge and null out the type from the array addExceptionalEdgeTo(b); if (subtype1) { caught.add(t); } } } } } exceptionTypes.removeAll(caught); } } } // if needed, add an edge to the exit block. if ((exceptionTypes == null && needEdgeToExitForAllHandlers) || (exceptionTypes != null && !exceptionTypes.isEmpty())) { BasicBlock exit = exit(); addExceptionalEdgeTo(exit); } } else { // found no handler for this PEI ... link to the exit block. BasicBlock exit = exit(); addExceptionalEdgeTo(exit); } } }
/** @see com.ibm.wala.classLoader.IMethod#isInit() */ public boolean isInit() { return method.getSelector().equals(MethodReference.initSelector); }
public Descriptor getDescriptor() { return method.getSelector().getDescriptor(); }
public Atom getName() { return method.getSelector().getName(); }
/** @see com.ibm.wala.classLoader.IMethod#getNumberOfParameters() */ public int getNumberOfParameters() { int n = method.getNumberOfParameters(); return isStatic() ? n : n + 1; }
public static boolean isRemoteProcedure(MethodReference method, IClassHierarchy cha) { return isCapsuleInterface(cha.lookupClass(method.getDeclaringClass())); }
/** * Understands {@link com.ibm.wala.analysis.reflection.GetMethodContext}. * * @author Michael Heilmann * @see com.ibm.wala.analysis.reflection.GetMethodContext * @see com.ibm.wala.analysis.reflection.GetMethodContextSelector */ public class GetMethodContextInterpreter implements SSAContextInterpreter { /** TODO MH: Maybe hard-code those in {@link com.ibm.wala.types.MethodReference}? */ public static final MethodReference GET_METHOD = MethodReference.findOrCreate( TypeReference.JavaLangClass, "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); /** TODO MH: Maybe hard-code those in {@link com.ibm.wala.types.MethodReference}? */ public static final MethodReference GET_DECLARED_METHOD = MethodReference.findOrCreate( TypeReference.JavaLangClass, "getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;"); private static final boolean DEBUG = false; /** * @see * com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getIR(com.ibm.wala.ipa.callgraph.CGNode) */ @Override public IR getIR(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); if (DEBUG) { System.err.println("generating IR for " + node); } IMethod method = node.getMethod(); GetMethodContext context = (GetMethodContext) node.getContext(); Map<Integer, ConstantValue> constants = HashMapFactory.make(); if (method.getReference().equals(GET_METHOD)) { Atom name = Atom.findOrCreateAsciiAtom(context.getName()); SSAInstruction instrs[] = makeGetMethodStatements(context, constants, name); return new SyntheticIR( method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } if (method.getReference().equals(GET_DECLARED_METHOD)) { Atom name = Atom.findOrCreateAsciiAtom(context.getName()); SSAInstruction instrs[] = makeGetDeclaredMethodStatements(context, constants, name); return new SyntheticIR( method, context, new InducedCFG(instrs, method, context), instrs, SSAOptions.defaultOptions(), constants); } Assertions.UNREACHABLE("Unexpected method " + node); return null; } /** * @see * com.ibm.wala.ipa.callgraph.propagation.SSAContextInterpreter#getNumberOfStatements(com.ibm.wala.ipa.callgraph.CGNode) */ @Override public int getNumberOfStatements(CGNode node) { assert understands(node); return getIR(node).getInstructions().length; } /** * @see * com.ibm.wala.ipa.callgraph.propagation.rta.RTAContextInterpreter#understands(com.ibm.wala.ipa.callgraph.CGNode) */ @Override public boolean understands(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } if (!(node.getContext() instanceof GetMethodContext)) { return false; } MethodReference mRef = node.getMethod().getReference(); return mRef.equals(GET_METHOD) || mRef.equals(GET_DECLARED_METHOD); } @Override public Iterator<NewSiteReference> iterateNewSites(CGNode node) { if (node == null) { throw new IllegalArgumentException("node is null"); } assert understands(node); GetMethodContext context = (GetMethodContext) node.getContext(); TypeReference tr = context.getType().getTypeReference(); if (tr != null) { return new NonNullSingletonIterator<NewSiteReference>(NewSiteReference.make(0, tr)); } return EmptyIterator.instance(); } @Override public Iterator<CallSiteReference> iterateCallSites(CGNode node) { assert understands(node); return EmptyIterator.instance(); } /** * Get all non-constructor, non-class-initializer methods declared by a class if their name is * equal to the specified name. * * @param cls the class * @param name the name */ private Collection<IMethod> getDeclaredNormalMethods(IClass cls, Atom name) { Collection<IMethod> result = HashSetFactory.make(); for (IMethod m : cls.getDeclaredMethods()) { if (!m.isInit() && !m.isClinit() && m.getSelector().getName().equals(name)) { result.add(m); } } return result; } /** * Get all non-constructor, non-class-initializer methods declared by a class and all its * superclasses if their name is equal to the specified name. * * @param cls the class * @param name the name */ private Collection<IMethod> getAllNormalPublicMethods(IClass cls, Atom name) { Collection<IMethod> result = HashSetFactory.make(); Collection<IMethod> allMethods = null; allMethods = cls.getAllMethods(); for (IMethod m : allMethods) { if (!m.isInit() && !m.isClinit() && m.isPublic() && m.getSelector().getName().equals(name)) { result.add(m); } } return result; } /** * Create statements for methods like getMethod() and getDeclaredMethod(), which return a single * method. This creates a return statement for each possible return value, each of which is a * {@link ConstantValue} for an {@link IMethod}. * * @param returnValues the possible return values for this method * @return the statements */ private SSAInstruction[] getParticularMethodStatements( MethodReference ref, Collection<IMethod> returnValues, GetMethodContext context, Map<Integer, ConstantValue> constants) { ArrayList<SSAInstruction> statements = new ArrayList<SSAInstruction>(); int nextLocal = ref.getNumberOfParameters() + 2; IClass cls = context.getType().getType(); SSAInstructionFactory insts = context.getType().getType().getClassLoader().getInstructionFactory(); if (cls != null) { for (IMethod m : returnValues) { int c = nextLocal++; constants.put(c, new ConstantValue(m)); SSAReturnInstruction R = insts.ReturnInstruction(statements.size(), c, false); statements.add(R); } } else { // SJF: This is incorrect. TODO: fix and enable. // SSAThrowInstruction t = insts.ThrowInstruction(retValue); // statements.add(t); } SSAInstruction[] result = new SSAInstruction[statements.size()]; Iterator<SSAInstruction> it = statements.iterator(); for (int i = 0; i < result.length; i++) { result[i] = it.next(); } return result; } private SSAInstruction[] makeGetMethodStatements( GetMethodContext context, Map<Integer, ConstantValue> constants, Atom name) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_METHOD, null, context, constants); } else { return getParticularMethodStatements( GET_METHOD, getAllNormalPublicMethods(cls, name), context, constants); } } /** Create statements for {@link Class#getDeclaredMethod(String, Class...)}. */ private SSAInstruction[] makeGetDeclaredMethodStatements( GetMethodContext context, Map<Integer, ConstantValue> constants, Atom name) { IClass cls = context.getType().getType(); if (cls == null) { return getParticularMethodStatements(GET_DECLARED_METHOD, null, context, constants); } else { return getParticularMethodStatements( GET_DECLARED_METHOD, getDeclaredNormalMethods(cls, name), context, constants); } } @Override public boolean recordFactoryType(CGNode node, IClass klass) { return false; } @Override public Iterator<FieldReference> iterateFieldsRead(CGNode node) { return EmptyIterator.instance(); } @Override public Iterator<FieldReference> iterateFieldsWritten(CGNode node) { return EmptyIterator.instance(); } @Override public ControlFlowGraph<SSAInstruction, ISSABasicBlock> getCFG(CGNode N) { return getIR(N).getControlFlowGraph(); } @Override public DefUse getDU(CGNode node) { return new DefUse(getIR(node)); } }