/* * 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) 2012 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.List; import libcore.reflect.AnnotationAccess; import libcore.reflect.GenericSignatureParser; import libcore.reflect.ListOfTypes; import libcore.reflect.Types; import libcore.util.EmptyArray; /** * This class represents an abstract method. Abstract methods are either methods or constructors. * @hide */ public abstract class AbstractMethod extends AccessibleObject { /** Bits encoding access (e.g. public, private) as well as other runtime specific flags */ protected int accessFlags; /** * The ArtMethod associated with this Method, requried for dispatching due to entrypoints * Classloader is held live by the declaring class. * Hidden to workaround b/16828157. * @hide */ protected long artMethod; /** Method's declaring class */ protected Class<?> declaringClass; /** Overriden method's declaring class (same as declaringClass unless declaringClass * is a proxy class) */ protected Class<?> declaringClassOfOverriddenMethod; /** The method index of this method within its defining dex file */ protected int dexMethodIndex; /** * Hidden to workaround b/16828157. * @hide */ protected AbstractMethod() { } public <T extends Annotation> T getAnnotation(Class<T> annotationClass) { return super.getAnnotation(annotationClass); } /** * We insert native method stubs for abstract methods so we don't have to * check the access flags at the time of the method call. This results in * "native abstract" methods, which can't exist. If we see the "abstract" * flag set, clear the "native" flag. * * We also move the DECLARED_SYNCHRONIZED flag into the SYNCHRONIZED * position, because the callers of this function are trying to convey * the "traditional" meaning of the flags to their callers. */ private static int fixMethodFlags(int flags) { if ((flags & Modifier.ABSTRACT) != 0) { flags &= ~Modifier.NATIVE; } flags &= ~Modifier.SYNCHRONIZED; int ACC_DECLARED_SYNCHRONIZED = 0x00020000; if ((flags & ACC_DECLARED_SYNCHRONIZED) != 0) { flags |= Modifier.SYNCHRONIZED; } return flags & 0xffff; // mask out bits not used by Java } int getModifiers() { return fixMethodFlags(accessFlags); } boolean isVarArgs() { return (accessFlags & Modifier.VARARGS) != 0; } boolean isBridge() { return (accessFlags & Modifier.BRIDGE) != 0; } boolean isSynthetic() { return (accessFlags & Modifier.SYNTHETIC) != 0; } /** * @hide */ public final int getAccessFlags() { return accessFlags; } /** * Returns the class that declares this constructor or method. */ Class<?> getDeclaringClass() { return declaringClass; } /** * Returns the index of this method's ID in its dex file. * * @hide */ public final int getDexMethodIndex() { return dexMethodIndex; } /** * Returns the name of the method or constructor represented by this * instance. * * @return the name of this method */ abstract public String getName(); /** * Returns an array of {@code Class} objects associated with the parameter types of this * abstract method. If the method was declared with no parameters, an * empty array will be returned. * * @return the parameter types */ Class<?>[] getParameterTypes() { Dex dex = declaringClassOfOverriddenMethod.getDex(); short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); if (types.length == 0) { return EmptyArray.CLASS; } Class<?>[] parametersArray = new Class[types.length]; for (int i = 0; i < types.length; i++) { // Note, in the case of a Proxy the dex cache types are equal. parametersArray[i] = declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]); } return parametersArray; } /** * Returns true if {@code other} has the same declaring class, name, * parameters and return type as this method. */ @Override public boolean equals(Object other) { if (!(other instanceof AbstractMethod)) { return false; } // Exactly one instance of each member in this runtime, todo, does this work for proxies? AbstractMethod otherMethod = (AbstractMethod) other; return this.declaringClass == otherMethod.declaringClass && this.dexMethodIndex == otherMethod.dexMethodIndex; } String toGenericString() { return toGenericStringHelper(); } Type[] getGenericParameterTypes() { return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericParameterTypes, false); } Type[] getGenericExceptionTypes() { return Types.getTypeArray(getMethodOrConstructorGenericInfo().genericExceptionTypes, false); } @Override public Annotation[] getDeclaredAnnotations() { List<Annotation> result = AnnotationAccess.getDeclaredAnnotations(this); return result.toArray(new Annotation[result.size()]); } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { if (annotationType == null) { throw new NullPointerException("annotationType == null"); } return AnnotationAccess.isDeclaredAnnotationPresent(this, annotationType); } public Annotation[] getAnnotations() { return super.getAnnotations(); } /** * 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 abstract Annotation[][] getParameterAnnotations(); /** * 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") abstract String getSignature(); static final class GenericInfo { final ListOfTypes genericExceptionTypes; final ListOfTypes genericParameterTypes; final Type genericReturnType; final TypeVariable<?>[] formalTypeParameters; GenericInfo(ListOfTypes exceptions, ListOfTypes parameters, Type ret, TypeVariable<?>[] formal) { genericExceptionTypes = exceptions; genericParameterTypes = parameters; genericReturnType = ret; formalTypeParameters = formal; } } /** * Returns generic information associated with this method/constructor member. */ final GenericInfo getMethodOrConstructorGenericInfo() { String signatureAttribute = AnnotationAccess.getSignature(this); Member member; Class<?>[] exceptionTypes; boolean method = this instanceof Method; if (method) { Method m = (Method) this; member = m; exceptionTypes = m.getExceptionTypes(); } else { Constructor<?> c = (Constructor<?>) this; member = c; exceptionTypes = c.getExceptionTypes(); } GenericSignatureParser parser = new GenericSignatureParser(member.getDeclaringClass().getClassLoader()); if (method) { parser.parseForMethod((GenericDeclaration) this, signatureAttribute, exceptionTypes); } else { parser.parseForConstructor((GenericDeclaration) this, signatureAttribute, exceptionTypes); } return new GenericInfo(parser.exceptionTypes, parser.parameterTypes, parser.returnType, parser.formalTypeParameters); } protected boolean equalMethodParameters(Class<?>[] params) { Dex dex = declaringClassOfOverriddenMethod.getDex(); short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); if (types.length != params.length) { return false; } for (int i = 0; i < types.length; i++) { if (declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]) != params[i]) { return false; } } return true; } protected int compareParameters(Class<?>[] params) { Dex dex = declaringClassOfOverriddenMethod.getDex(); short[] types = dex.parameterTypeIndicesFromMethodIndex(dexMethodIndex); int length = Math.min(types.length, params.length); for (int i = 0; i < length; i++) { Class<?> aType = declaringClassOfOverriddenMethod.getDexCacheType(dex, types[i]); Class<?> bType = params[i]; if (aType != bType) { int comparison = aType.getName().compareTo(bType.getName()); if (comparison != 0) { return comparison; } } } return types.length - params.length; } /** * Helper for Method and Constructor for toGenericString */ final String toGenericStringHelper() { StringBuilder sb = new StringBuilder(80); GenericInfo info = getMethodOrConstructorGenericInfo(); int modifiers = ((Member)this).getModifiers(); // append modifiers if any if (modifiers != 0) { sb.append(Modifier.toString(modifiers & ~Modifier.VARARGS)).append(' '); } // append type parameters if (info.formalTypeParameters != null && info.formalTypeParameters.length > 0) { sb.append('<'); for (int i = 0; i < info.formalTypeParameters.length; i++) { Types.appendGenericType(sb, info.formalTypeParameters[i]); if (i < info.formalTypeParameters.length - 1) { sb.append(","); } } sb.append("> "); } Class<?> declaringClass = ((Member) this).getDeclaringClass(); if (this instanceof Constructor) { // append constructor name Types.appendTypeName(sb, declaringClass); } else { // append return type Types.appendGenericType(sb, Types.getType(info.genericReturnType)); sb.append(' '); // append method name Types.appendTypeName(sb, declaringClass); sb.append(".").append(((Method) this).getName()); } // append parameters sb.append('('); Types.appendArrayGenericType(sb, info.genericParameterTypes.getResolvedTypes()); sb.append(')'); // append exceptions if any Type[] genericExceptionTypeArray = Types.getTypeArray(info.genericExceptionTypes, false); if (genericExceptionTypeArray.length > 0) { sb.append(" throws "); Types.appendArrayGenericType(sb, genericExceptionTypeArray); } return sb.toString(); } }