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}