001 package railo.transformer.bytecode.reflection; 002 003 004 import java.lang.reflect.InvocationTargetException; 005 import java.lang.reflect.Member; 006 import java.lang.reflect.Method; 007 import java.lang.reflect.Modifier; 008 009 import railo.commons.lang.ClassUtil; 010 011 /** 012 * A {@code Method} provides information about, and access to, a single method 013 * on a class or interface. The reflected method may be a class method 014 * or an instance method (including an abstract method). 015 * 016 * <p>A {@code Method} permits widening conversions to occur when matching the 017 * actual parameters to invoke with the underlying method's formal 018 * parameters, but it throws an {@code IllegalArgumentException} if a 019 * narrowing conversion would occur. 020 * 021 * @see Member 022 * @see java.lang.Class 023 * @see java.lang.Class#getMethods() 024 * @see java.lang.Class#getMethod(String, Class[]) 025 * @see java.lang.Class#getDeclaredMethods() 026 * @see java.lang.Class#getDeclaredMethod(String, Class[]) 027 */ 028 public abstract class ASMMethod { 029 private Class[] exceptionTypes; 030 private Class returnType; 031 private Class[] parameterTypes; 032 //private int modifiers; 033 private Class clazz; 034 035 036 /** 037 * Package-private constructor used by ReflectAccess to enable 038 * instantiation of these objects in Java code from the java.lang 039 * package via sun.reflect.LangReflectAccess. 040 */ 041 public ASMMethod(Class declaringClass, Class[] parameterTypes) { 042 this.clazz = declaringClass; 043 this.parameterTypes = parameterTypes; 044 } 045 046 047 /** 048 * Returns the {@code Class} object representing the class or interface 049 * that declares the method represented by this {@code Method} object. 050 */ 051 public Class<?> getDeclaringClass() { 052 return clazz; 053 } 054 055 /** 056 * Returns the name of the method represented by this {@code Method} 057 * object, as a {@code String}. 058 */ 059 public abstract String getName(); 060 061 /** 062 * Returns the Java language modifiers for the method represented 063 * by this {@code Method} object, as an integer. The {@code Modifier} class should 064 * be used to decode the modifiers. 065 * 066 * @see Modifier 067 */ 068 public abstract int getModifiers(); 069 070 /** 071 * Returns a {@code Class} object that represents the formal return type 072 * of the method represented by this {@code Method} object. 073 * 074 * @return the return type for the method this object represents 075 */ 076 077 public Class<?> getReturnType() { 078 if(returnType==null) { 079 returnType = ClassUtil.loadClass(getReturnTypeAsString(),null); 080 if(returnType==null) initAddionalParams(); 081 } 082 return returnType; 083 } 084 085 protected Class<?> _getReturnType() { 086 if(returnType==null) initAddionalParams(); 087 return returnType; 088 } 089 090 public abstract String getReturnTypeAsString(); 091 092 /** 093 * Returns an array of {@code Class} objects that represent the formal 094 * parameter types, in declaration order, of the method 095 * represented by this {@code Method} object. Returns an array of length 096 * 0 if the underlying method takes no parameters. 097 * 098 * @return the parameter types for the method this object 099 * represents 100 */ 101 public Class<?>[] getParameterTypes() { 102 return parameterTypes.clone(); 103 } 104 105 106 /** 107 * Returns an array of {@code Class} objects that represent 108 * the types of the exceptions declared to be thrown 109 * by the underlying method 110 * represented by this {@code Method} object. Returns an array of length 111 * 0 if the method declares no exceptions in its {@code throws} clause. 112 * 113 * @return the exception types declared as being thrown by the 114 * method this object represents 115 */ 116 public Class<?>[] getExceptionTypes() { 117 if(exceptionTypes==null) initAddionalParams(); 118 return exceptionTypes.clone(); 119 } 120 121 private void initAddionalParams() { 122 try { 123 Method m = clazz.getMethod(getName(), getParameterTypes()); 124 exceptionTypes=m.getExceptionTypes(); 125 returnType=m.getReturnType(); 126 } 127 catch (SecurityException e) { 128 // TODO remove block 129 e.printStackTrace(); 130 } 131 catch (NoSuchMethodException e) { 132 // TODO remove block 133 e.printStackTrace(); 134 } 135 136 } 137 138 139 /** 140 * Compares this {@code Method} against the specified object. Returns 141 * true if the objects are the same. Two {@code Methods} are the same if 142 * they were declared by the same class and have the same name 143 * and formal parameter types and return type. 144 */ 145 public boolean equals(Object obj) { 146 if(this==obj) return true; 147 if(!(obj instanceof ASMMethod)) return false; 148 149 ASMMethod other=(ASMMethod) obj; 150 151 return 152 this.getModifiers()==other.getModifiers() && 153 this.getName().equals(other.getName()) && 154 this.clazz.equals(other.clazz) && 155 this.returnType.equals(other.returnType) && 156 eq(this.getExceptionTypes(),other.getExceptionTypes()) && 157 eq(this.parameterTypes,other.parameterTypes); 158 } 159 160 private boolean eq(Class[] left, Class[] right) { 161 int l=left==null?0:left.length; 162 int r=right==null?0:right.length; 163 if(l!=r) return false; 164 if(l==0) return true; 165 166 for(int i=0;i<left.length;i++){ 167 if(!left[i].equals(right[i])) return false; 168 } 169 return true; 170 } 171 172 /** 173 * Invokes the underlying method represented by this {@code Method} 174 * object, on the specified object with the specified parameters. 175 * Individual parameters are automatically unwrapped to match 176 * primitive formal parameters, and both primitive and reference 177 * parameters are subject to method invocation conversions as 178 * necessary. 179 * 180 * <p>If the underlying method is static, then the specified {@code obj} 181 * argument is ignored. It may be null. 182 * 183 * <p>If the number of formal parameters required by the underlying method is 184 * 0, the supplied {@code args} array may be of length 0 or null. 185 * 186 * <p>If the underlying method is an instance method, it is invoked 187 * using dynamic method lookup as documented in The Java Language 188 * Specification, Second Edition, section 15.12.4.4; in particular, 189 * overriding based on the runtime type of the target object will occur. 190 * 191 * <p>If the underlying method is static, the class that declared 192 * the method is initialized if it has not already been initialized. 193 * 194 * <p>If the method completes normally, the value it returns is 195 * returned to the caller of invoke; if the value has a primitive 196 * type, it is first appropriately wrapped in an object. However, 197 * if the value has the type of an array of a primitive type, the 198 * elements of the array are <i>not</i> wrapped in objects; in 199 * other words, an array of primitive type is returned. If the 200 * underlying method return type is void, the invocation returns 201 * null. 202 * 203 * @param obj the object the underlying method is invoked from 204 * @param args the arguments used for the method call 205 * @return the result of dispatching the method represented by 206 * this object on {@code obj} with parameters 207 * {@code args} 208 * 209 * @exception IllegalAccessException if this {@code Method} object 210 * enforces Java language access control and the underlying 211 * method is inaccessible. 212 * @exception IllegalArgumentException if the method is an 213 * instance method and the specified object argument 214 * is not an instance of the class or interface 215 * declaring the underlying method (or of a subclass 216 * or implementor thereof); if the number of actual 217 * and formal parameters differ; if an unwrapping 218 * conversion for primitive arguments fails; or if, 219 * after possible unwrapping, a parameter value 220 * cannot be converted to the corresponding formal 221 * parameter type by a method invocation conversion. 222 * @exception InvocationTargetException if the underlying method 223 * throws an exception. 224 * @exception NullPointerException if the specified object is null 225 * and the method is an instance method. 226 * @exception ExceptionInInitializerError if the initialization 227 * provoked by this method fails. 228 */ 229 public abstract Object invoke(Object obj, Object[] args) throws Throwable;//IllegalAccessException, IllegalArgumentException, InvocationTargetException; 230 }