/******************************************************************************* * 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.types; import java.util.HashMap; import com.ibm.wala.classLoader.Language; import com.ibm.wala.util.collections.HashMapFactory; import com.ibm.wala.util.shrike.ShrikeUtil; import com.ibm.wala.util.strings.Atom; /** * A class to represent the reference in a class file to a method. */ public final class MethodReference extends MemberReference { /** * Used to canonicalize MethodReferences a mapping from Key -> MethodReference */ final private static HashMap<Key, MethodReference> dictionary = HashMapFactory.make(); public final static Atom newInstanceAtom = Atom.findOrCreateUnicodeAtom("newInstance"); private final static Descriptor newInstanceDesc = Descriptor.findOrCreateUTF8(Language.JAVA, "()Ljava/lang/Object;"); public final static MethodReference JavaLangClassNewInstance = findOrCreate(TypeReference.JavaLangClass, newInstanceAtom, newInstanceDesc); private final static Atom ctorNewInstanceAtom = Atom.findOrCreateUnicodeAtom("newInstance"); private final static Descriptor ctorNewInstanceDesc = Descriptor.findOrCreateUTF8(Language.JAVA, "([Ljava/lang/Object;)Ljava/lang/Object;"); public final static MemberReference JavaLangReflectCtorNewInstance = findOrCreate(TypeReference.JavaLangReflectConstructor, ctorNewInstanceAtom, ctorNewInstanceDesc); public final static Atom forNameAtom = Atom.findOrCreateUnicodeAtom("forName"); private final static Descriptor forNameDesc = Descriptor.findOrCreateUTF8(Language.JAVA, "(Ljava/lang/String;)Ljava/lang/Class;"); public final static MethodReference JavaLangClassForName = findOrCreate(TypeReference.JavaLangClass, forNameAtom, forNameDesc); public final static Atom initAtom = Atom.findOrCreateUnicodeAtom("<init>"); public final static Descriptor defaultInitDesc = Descriptor.findOrCreateUTF8(Language.JAVA, "()V"); public final static Selector initSelector = new Selector(initAtom, defaultInitDesc); public final static Atom clinitName = Atom.findOrCreateUnicodeAtom("<clinit>"); public final static Selector clinitSelector = new Selector(clinitName, defaultInitDesc); public final static Atom runAtom = Atom.findOrCreateUnicodeAtom("run"); public final static Descriptor runDesc = Descriptor.findOrCreateUTF8(Language.JAVA, "()Ljava/lang/Object;"); public final static Selector runSelector = new Selector(runAtom, runDesc); public final static Atom equalsAtom = Atom.findOrCreateUnicodeAtom("equals"); public final static Descriptor equalsDesc = Descriptor.findOrCreateUTF8(Language.JAVA, "(Ljava/lang/Object;)Z"); public final static Selector equalsSelector = new Selector(equalsAtom, equalsDesc); /** * types of parameters to this method. */ final private TypeReference[] parameterTypes; /** * return type. */ private final TypeReference returnType; /** * Name and descriptor */ private final Selector selector; /** * Find or create the canonical MethodReference instance for the given tuple. * * @param mn the name of the member * @param md the descriptor of the member */ public static synchronized MethodReference findOrCreate(TypeReference tref, Atom mn, Descriptor md) { if (tref == null) { throw new IllegalArgumentException("null tref"); } Key key = new Key(tref, mn, md); MethodReference val = dictionary.get(key); if (val != null) return val; val = new MethodReference(key); dictionary.put(key, val); return val; } /** * Find or create the canonical MethodReference instance for the given tuple. * * @param tref the type reference * @param selector the selector for the method * @throws IllegalArgumentException if selector is null */ public static synchronized MethodReference findOrCreate(TypeReference tref, Selector selector) { if (selector == null) { throw new IllegalArgumentException("selector is null"); } return findOrCreate(tref, selector.getName(), selector.getDescriptor()); } public static MethodReference findOrCreate(TypeReference t, String methodName, String descriptor) throws IllegalArgumentException { return findOrCreate(Language.JAVA, t, methodName, descriptor); } public static MethodReference findOrCreate(Language l, TypeReference t, String methodName, String descriptor) throws IllegalArgumentException { Descriptor d = Descriptor.findOrCreateUTF8(l, descriptor); return findOrCreate(t, Atom.findOrCreateUnicodeAtom(methodName), d); } public static MethodReference findOrCreate(ClassLoaderReference loader, String methodClass, String methodName, String methodSignature) throws IllegalArgumentException { return findOrCreate(Language.JAVA, loader, methodClass, methodName, methodSignature); } public static MethodReference findOrCreate(Language l, ClassLoaderReference loader, String methodClass, String methodName, String methodSignature) throws IllegalArgumentException { TypeReference t = ShrikeUtil.makeTypeReference(loader, methodClass); Atom name = Atom.findOrCreateUnicodeAtom(methodName); Descriptor d = Descriptor.findOrCreateUTF8(l, methodSignature); return findOrCreate(t, name, d); } /** * @return the descriptor component of this member reference */ public final Descriptor getDescriptor() { return selector.getDescriptor(); } @Override public final String toString() { return "< " + getDeclaringClass().getClassLoader().getName() + ", " + getDeclaringClass().getName() + ", " + selector + " >"; } MethodReference(Key key) { super(key.type, key.name, key.hashCode()); selector = new Selector(key.name, key.descriptor); TypeName[] parameterNames = key.descriptor.getParameters(); if (parameterNames != null) { parameterTypes = new TypeReference[parameterNames.length]; } else { parameterTypes = null; } ClassLoaderReference loader = getDeclaringClass().getClassLoader(); if (parameterNames != null) { for (int i = 0; i < parameterNames.length; i++) { parameterTypes[i] = TypeReference.findOrCreate(loader, parameterNames[i]); } } TypeName r = key.descriptor.getReturnType(); returnType = TypeReference.findOrCreate(getDeclaringClass().getClassLoader(), r); } /** * @return return type of the method */ public final TypeReference getReturnType() { return returnType; } /** * @return ith parameter to the method. This does NOT include the implicit "this" pointer. */ public final TypeReference getParameterType(int i) throws IllegalArgumentException { if (parameterTypes == null || i >= parameterTypes.length) { throw new IllegalArgumentException("illegal parameter number " + i + " for " + this); } return parameterTypes[i]; } public boolean isInit() { return getName().equals(MethodReference.initAtom); } /** * @return something like: com.foo.bar.createLargeOrder(IILjava/lang/String;Ljava/sql/Date;)Ljava/lang/Integer; */ @Override public String getSignature() { // TODO: check that we're not calling this often. String s = getDeclaringClass().getName().toString().substring(1).replace('/', '.') + "." + getName() + getDescriptor(); return s; } /** * @return something like: createLargeOrder(IILjava.lang.String;SLjava.sql.Date;)Ljava.lang.Integer; */ public Selector getSelector() { return selector; } /** * This method does NOT include the implicit "this" parameter */ public int getNumberOfParameters() { return parameterTypes == null ? 0 : parameterTypes.length; } /** * An identifier/selector for methods. */ protected static class Key { private final TypeReference type; private final Atom name; private final Descriptor descriptor; Key(TypeReference type, Atom name, Descriptor descriptor) { this.type = type; this.name = name; this.descriptor = descriptor; } @Override public final int hashCode() { return 7001 * type.hashCode() + 7013 * name.hashCode() + descriptor.hashCode(); } @Override public final boolean equals(Object other) { assert other != null && this.getClass().equals(other.getClass()); Key that = (Key) other; return type.equals(that.type) && name.equals(that.name) && descriptor.equals(that.descriptor); } } }