/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.lang.reflect; import com.android.dex.Dex; import java.lang.annotation.Annotation; import java.util.Comparator; import java.util.List; import libcore.reflect.AnnotationAccess; import libcore.reflect.Types; /** * This class represents a method. Information about the method can be accessed, * and the method can be invoked dynamically. */ public final class Method extends AbstractMethod implements GenericDeclaration, Member { /** * Orders methods by their name, parameters and return type. * * @hide */ public static final Comparator ORDER_BY_SIGNATURE = new Comparator() { @Override public int compare(Method a, Method b) { if (a == b) { return 0; } int comparison = a.getName().compareTo(b.getName()); if (comparison == 0) { comparison = a.artMethod.findOverriddenMethodIfProxy().compareParameters( b.getParameterTypes()); if (comparison == 0) { // This is necessary for methods that have covariant return types. Class aReturnType = a.getReturnType(); Class bReturnType = b.getReturnType(); if (aReturnType == bReturnType) { comparison = 0; } else { comparison = aReturnType.getName().compareTo(bReturnType.getName()); } } } return comparison; } }; /** * @hide */ public Method(ArtMethod artMethod) { super(artMethod); } ArtMethod getArtMethod() { return artMethod; } public Annotation[] getAnnotations() { return super.getAnnotations(); } /** * Returns the modifiers for this method. The {@link Modifier} class should * be used to decode the result. * * @return the modifiers for this method * * @see Modifier */ @Override public int getModifiers() { return super.getModifiers(); } /** * Indicates whether or not this method takes a variable number argument. * * @return {@code true} if a vararg is declared, {@code false} otherwise */ public boolean isVarArgs() { return super.isVarArgs(); } /** * Indicates whether or not this method is a bridge. * * @return {@code true} if this method is a bridge, {@code false} otherwise */ public boolean isBridge() { return super.isBridge(); } /** * Indicates whether or not this method is synthetic. * * @return {@code true} if this method is synthetic, {@code false} otherwise */ @Override public boolean isSynthetic() { return super.isSynthetic(); } /** * Returns the name of the method represented by this {@code Method} * instance. * * @return the name of this method */ @Override public String getName() { return ArtMethod.getMethodName(artMethod); } /** * Returns the class that declares this method. */ @Override public Class getDeclaringClass() { return super.getDeclaringClass(); } /** * Returns the exception types as an array of {@code Class} instances. If * this method has no declared exceptions, an empty array is returned. * * @return the declared exception classes */ public Class[] getExceptionTypes() { if (getDeclaringClass().isProxy()) { return getExceptionTypesNative(); } else { // TODO: use dex cache to speed looking up class return AnnotationAccess.getExceptions(this); } } private native Class[] getExceptionTypesNative(); /** * Returns an array of {@code Class} objects associated with the parameter * types of this method. If the method was declared with no parameters, an * empty array will be returned. * * @return the parameter types */ @Override public Class[] getParameterTypes() { return artMethod.findOverriddenMethodIfProxy().getParameterTypes(); } /** * Returns the {@code Class} associated with the return type of this * method. * * @return the return type */ public Class getReturnType() { return artMethod.findOverriddenMethodIfProxy().getReturnType(); } /** * {@inheritDoc} * *

Equivalent to {@code getDeclaringClass().getName().hashCode() ^ getName().hashCode()}. */ @Override public int hashCode() { return getDeclaringClass().getName().hashCode() ^ getName().hashCode(); } /** * Returns true if {@code other} has the same declaring class, name, * parameters and return type as this method. */ @Override public boolean equals(Object other) { return super.equals(other); } /** * Returns true if this and {@code method} have the same name and the same * parameters in the same order. Such methods can share implementation if * one method's return types is assignable to the other. * * @hide needed by Proxy */ boolean equalNameAndParameters(Method m) { return getName().equals(m.getName()) && ArtMethod.equalMethodParameters(artMethod,m.getParameterTypes()); } /** * Returns the string representation of the method's declaration, including * the type parameters. * * @return the string representation of this method */ public String toGenericString() { return super.toGenericString(); } @Override public TypeVariable[] getTypeParameters() { GenericInfo info = getMethodOrConstructorGenericInfo(); return (TypeVariable[]) info.formalTypeParameters.clone(); } /** * Returns the parameter types as an array of {@code Type} instances, in * declaration order. If this method has no parameters, an empty array is * returned. * * @return the parameter types * * @throws GenericSignatureFormatError * if the generic method signature is invalid * @throws TypeNotPresentException * if any parameter type points to a missing type * @throws MalformedParameterizedTypeException * if any parameter type points to a type that cannot be * instantiated for some reason */ public Type[] getGenericParameterTypes() { return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericParameterTypes, false); } @Override public boolean isAnnotationPresent(Class annotationType) { if (annotationType == null) { throw new NullPointerException("annotationType == null"); } return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType); } /** * Returns the exception types as an array of {@code Type} instances. If * this method has no declared exceptions, an empty array will be returned. * * @return an array of generic exception types * * @throws GenericSignatureFormatError * if the generic method signature is invalid * @throws TypeNotPresentException * if any exception type points to a missing type * @throws MalformedParameterizedTypeException * if any exception type points to a type that cannot be * instantiated for some reason */ public Type[] getGenericExceptionTypes() { return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericExceptionTypes, false); } /** * Returns the return type of this method as a {@code Type} instance. * * @return the return type of this method * * @throws GenericSignatureFormatError * if the generic method signature is invalid * @throws TypeNotPresentException * if the return type points to a missing type * @throws MalformedParameterizedTypeException * if the return type points to a type that cannot be * instantiated for some reason */ public Type getGenericReturnType() { return Types.getType(getMethodOrConstructorGenericInfo().genericReturnType); } @Override public Annotation[] getDeclaredAnnotations() { List result = AnnotationAccess.getDeclaredAnnotations(this); return result.toArray(new Annotation[result.size()]); } @Override public A getAnnotation(Class annotationType) { if (annotationType == null) { throw new NullPointerException("annotationType == null"); } return AnnotationAccess.getDeclaredAnnotation(this, annotationType); } /** * Returns an array of arrays that represent the annotations of the formal * parameters of this method. If there are no parameters on this method, * then an empty array is returned. If there are no annotations set, then * and array of empty arrays is returned. * * @return an array of arrays of {@code Annotation} instances */ public Annotation[][] getParameterAnnotations() { return artMethod.findOverriddenMethodIfProxy().getParameterAnnotations(); } /** * Returns the default value for the annotation member represented by this * method. * * @return the default value, or {@code null} if none * * @throws TypeNotPresentException * if this annotation member is of type {@code Class} and no * definition can be found */ public Object getDefaultValue() { return AnnotationAccess.getDefaultValue(this); } /** * Returns the result of dynamically invoking this method. Equivalent to * {@code receiver.methodName(arg1, arg2, ... , argN)}. * *

If the method is static, the receiver argument is ignored (and may be null). * *

If the method takes no arguments, you can pass {@code (Object[]) null} instead of * allocating an empty array. * *

If you're calling a varargs method, you need to pass an {@code Object[]} for the * varargs parameter: that conversion is usually done in {@code javac}, not the VM, and * the reflection machinery does not do this for you. (It couldn't, because it would be * ambiguous.) * *

Reflective method invocation follows the usual process for method lookup. * *

If an exception is thrown during the invocation it is caught and * wrapped in an InvocationTargetException. This exception is then thrown. * *

If the invocation completes normally, the return value itself is * returned. If the method is declared to return a primitive type, the * return value is boxed. If the return type is void, null is returned. * * @param receiver * the object on which to call this method (or null for static methods) * @param args * the arguments to the method * @return the result * * @throws NullPointerException * if {@code receiver == null} for a non-static method * @throws IllegalAccessException * if this method is not accessible (see {@link AccessibleObject}) * @throws IllegalArgumentException * if the number of arguments doesn't match the number of parameters, the receiver * is incompatible with the declaring class, or an argument could not be unboxed * or converted by a widening conversion to the corresponding parameter type * @throws InvocationTargetException * if an exception was thrown by the invoked method */ public Object invoke(Object receiver, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { return invoke(receiver, args, isAccessible()); } private native Object invoke(Object receiver, Object[] args, boolean accessible) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException; /** * Returns a string containing a concise, human-readable description of this * method. The format of the string is: * *

    *
  1. modifiers (if any) *
  2. return type or 'void' *
  3. declaring class name *
  4. '(' *
  5. parameter types, separated by ',' (if any) *
  6. ')' *
  7. 'throws' plus exception types, separated by ',' (if any) *
* * For example: {@code public native Object * java.lang.Method.invoke(Object,Object) throws * IllegalAccessException,IllegalArgumentException * ,InvocationTargetException} * * @return a printable representation for this method */ @Override public String toString() { StringBuilder result = new StringBuilder(Modifier.toString(getModifiers())); if (result.length() != 0) { result.append(' '); } result.append(getReturnType().getName()); result.append(' '); result.append(getDeclaringClass().getName()); result.append('.'); result.append(getName()); result.append("("); Class[] parameterTypes = getParameterTypes(); result.append(Types.toString(parameterTypes)); result.append(")"); Class[] exceptionTypes = getExceptionTypes(); if (exceptionTypes.length != 0) { result.append(" throws "); result.append(Types.toString(exceptionTypes)); } return result.toString(); } /** * Returns the constructor's signature in non-printable form. This is called * (only) from IO native code and needed for deriving the serialVersionUID * of the class * * @return The constructor's signature. */ @SuppressWarnings("unused") String getSignature() { StringBuilder result = new StringBuilder(); result.append('('); Class[] parameterTypes = getParameterTypes(); for (Class parameterType : parameterTypes) { result.append(Types.getSignature(parameterType)); } result.append(')'); result.append(Types.getSignature(getReturnType())); return result.toString(); } }