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