blob: 01267655a2a7beb70853fc33f077ecc8b9aab94c [file] [log] [blame]
/*
* 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.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;
}
boolean isDefault() {
return (accessFlags & Modifier.DEFAULT) != 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 native Annotation[] getDeclaredAnnotations();
@Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
if (annotationType == null) {
throw new NullPointerException("annotationType == null");
}
return isAnnotationPresentNative(annotationType);
}
private native boolean isAnnotationPresentNative(Class<? extends Annotation> 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 = getSignatureAttribute();
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);
}
private String getSignatureAttribute() {
String[] annotation = getSignatureAnnotation();
if (annotation == null) {
return null;
}
StringBuilder result = new StringBuilder();
for (String s : annotation) {
result.append(s);
}
return result.toString();
}
private native String[] getSignatureAnnotation();
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();
}
}