View Javadoc

1   /***************************************************************************************
2    * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved.                 *
3    * http://aspectwerkz.codehaus.org                                                    *
4    * ---------------------------------------------------------------------------------- *
5    * The software in this package is published under the terms of the LGPL license      *
6    * a copy of which has been included with this distribution in the license.txt file.  *
7    **************************************************************************************/
8   package org.codehaus.aspectwerkz.reflect.impl.asm;
9   
10  import org.codehaus.aspectwerkz.reflect.ClassInfo;
11  import org.codehaus.aspectwerkz.reflect.MethodInfo;
12  import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
13  import org.codehaus.aspectwerkz.annotation.instrumentation.asm.AsmAnnotationHelper;
14  import org.codehaus.aspectwerkz.exception.DefinitionException;
15  import org.codehaus.aspectwerkz.proxy.ProxyCompiler;
16  import org.objectweb.asm.Type;
17  import org.objectweb.asm.ClassReader;
18  import org.objectweb.asm.attrs.Attributes;
19  
20  import java.util.List;
21  import java.util.ArrayList;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.lang.reflect.Modifier;
25  
26  /***
27   * ASM implementation of the MethodInfo interface.
28   *
29   * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
30   */
31  public class AsmMethodInfo extends AsmMemberInfo implements MethodInfo {
32  
33      /***
34       * The return type name.
35       */
36      private String m_returnTypeName = null;
37  
38      /***
39       * A list with the parameter names as they appear in the source code.
40       * This information may not be available.
41       */
42      protected String[] m_parameterNames = null;
43  
44      /***
45       * A list with the parameter type names.
46       */
47      private String[] m_parameterTypeNames = null;
48  
49      /***
50       * A list with the exception type names.
51       */
52      private String[] m_exceptionTypeNames = null;
53  
54      /***
55       * The return type.
56       */
57      private ClassInfo m_returnType = null;
58  
59      /***
60       * A list with the parameter types.
61       */
62      private ClassInfo[] m_parameterTypes = null;
63  
64      /***
65       * A list with the exception types.
66       */
67      private ClassInfo[] m_exceptionTypes = null;
68  
69      /***
70       * Creates a new method info instance.
71       *
72       * @param method
73       * @param declaringType
74       * @param loader
75       */
76      AsmMethodInfo(final MethodStruct method, final String declaringType, final ClassLoader loader) {
77          super(method, declaringType, loader);
78  
79          m_returnTypeName = Type.getReturnType(method.desc).getClassName();
80          Type[] argTypes = Type.getArgumentTypes(method.desc);
81          m_parameterTypeNames = new String[argTypes.length];
82          for (int i = 0; i < argTypes.length; i++) {
83              m_parameterTypeNames[i] = argTypes[i].getClassName();
84          }
85          // FIXME: how to do exceptions? needed?
86          m_exceptionTypeNames = new String[]{};
87      }
88  
89      /***
90       * Returns the method info for the method specified.
91       *
92       * @param methodName
93       * @param methodDesc
94       * @param bytecode
95       * @param loader
96       * @return the method info
97       */
98      public static MethodInfo getMethodInfo(final String methodName,
99                                             final String methodDesc,
100                                            final byte[] bytecode,
101                                            final ClassLoader loader) {
102         String className = AsmClassInfo.retrieveClassNameFromBytecode(bytecode);
103         AsmClassInfoRepository repository = AsmClassInfoRepository.getRepository(loader);
104         ClassInfo classInfo = repository.getClassInfo(className);
105         if (classInfo == null) {
106             classInfo = AsmClassInfo.getClassInfo(bytecode, loader);
107         }
108         return classInfo.getMethod(AsmHelper.calculateMethodHash(methodName, methodDesc));
109     }
110 
111     /***
112      * Returns the signature for the element.
113      *
114      * @return the signature for the element
115      */
116     public String getSignature() {
117         return AsmHelper.getMethodDescriptor(this);
118     }
119 
120     /***
121      * Returns the return type.
122      *
123      * @return the return type
124      */
125     public ClassInfo getReturnType() {
126         if (m_returnType == null) {
127             m_returnType = AsmClassInfo.getClassInfo(m_returnTypeName, (ClassLoader) m_loaderRef.get());
128         }
129         return m_returnType;
130     }
131 
132     /***
133      * Returns the parameter types.
134      *
135      * @return the parameter types
136      */
137     public ClassInfo[] getParameterTypes() {
138         if (m_parameterTypes == null) {
139             m_parameterTypes = new ClassInfo[m_parameterTypeNames.length];
140             for (int i = 0; i < m_parameterTypeNames.length; i++) {
141                 m_parameterTypes[i] = AsmClassInfo.getClassInfo(
142                         m_parameterTypeNames[i],
143                         (ClassLoader) m_loaderRef.get()
144                 );
145             }
146         }
147         return m_parameterTypes;
148     }
149 
150     /***
151      * Returns the parameter names as they appear in the source code.
152      * This information is available only when class are compiled with javac -g (debug info), but is required
153      * for Aspect that are using args() and target()/this() bindings.
154      * <p/>
155      * It returns null if not available.
156      *
157      * @return
158      */
159     public String[] getParameterNames() {
160         return m_parameterNames;
161     }
162 
163     /***
164      * Returns the exception types.
165      *
166      * @return the exception types
167      */
168     public ClassInfo[] getExceptionTypes() {
169         if (m_exceptionTypes == null) {
170             m_exceptionTypes = new ClassInfo[m_exceptionTypeNames.length];
171             for (int i = 0; i < m_exceptionTypeNames.length; i++) {
172                 m_exceptionTypes[i] = AsmClassInfo.getClassInfo(
173                         m_exceptionTypeNames[i],
174                         (ClassLoader) m_loaderRef.get()
175                 );
176             }
177         }
178         return m_exceptionTypes;
179     }
180 
181     /***
182      * Returns the annotations.
183      *
184      * @return the annotations
185      */
186     public List getAnnotations() {
187         if (m_annotations == null) {
188             try {
189                 InputStream in = null;
190                 ClassReader cr = null;
191                 try {
192                     if ((ClassLoader) m_loaderRef.get() != null) {
193                         in = ((ClassLoader) m_loaderRef.get()).getResourceAsStream(
194                                 m_declaringTypeName.replace('.', '/') + ".class"
195                         );
196                     } else {
197                         in = ClassLoader.getSystemClassLoader().getResourceAsStream(
198                                 m_declaringTypeName.replace('.', '/') + ".class"
199                         );
200                     }
201                     if (in == null) {
202                         in = ProxyCompiler.getProxyResourceAsStream((ClassLoader) m_loaderRef.get(), m_declaringTypeName);
203                     }
204                     cr = new ClassReader(in);
205                 } finally {
206                     try {
207                         in.close();
208                     } catch (Exception e) {
209                         ;
210                     }
211                 }
212                 List annotations = new ArrayList();
213                 cr.accept(
214                         new AsmAnnotationHelper.MethodAnnotationExtractor(
215                                 annotations, m_member.name, m_member.desc, (ClassLoader) m_loaderRef.get()
216                         ),
217                         Attributes.getDefaultAttributes(),
218                         true
219                 );
220                 m_annotations = annotations;
221             } catch (IOException e) {
222                 // unlikely to occur since ClassInfo relies on getResourceAsStream
223                 System.err.println(
224                         "WARN - could not load " + m_declaringTypeName + " as a resource to retrieve annotations"
225                 );
226                 m_annotations = AsmClassInfo.EMPTY_LIST;
227             }
228         }
229         return m_annotations;
230     }
231 
232     public boolean equals(Object o) {
233         if (this == o) {
234             return true;
235         }
236         if (!(o instanceof MethodInfo)) {
237             return false;
238         }
239         MethodInfo methodInfo = (MethodInfo) o;
240         if (!m_declaringTypeName.equals(methodInfo.getDeclaringType().getName())) {
241             return false;
242         }
243         if (!m_member.name.equals(methodInfo.getName())) {
244             return false;
245         }
246         ClassInfo[] parameterTypes = methodInfo.getParameterTypes();
247         if (m_parameterTypeNames.length != parameterTypes.length) {//check on names length for lazyness optim
248             return false;
249         }
250         for (int i = 0; i < m_parameterTypeNames.length; i++) {
251             if (!m_parameterTypeNames[i].equals(parameterTypes[i].getName())) {
252                 return false;
253             }
254         }
255         return true;
256     }
257 
258     public int hashCode() {
259         int result = 29;
260         result = (29 * result) + m_declaringTypeName.hashCode();
261         result = (29 * result) + m_member.name.hashCode();
262         for (int i = 0; i < m_parameterTypeNames.length; i++) {
263             result = (29 * result) + m_parameterTypeNames[i].hashCode();
264         }
265         return result;
266     }
267 
268     public String toString() {
269         StringBuffer sb = new StringBuffer(m_declaringTypeName);
270         sb.append('.').append(m_member.name);
271         sb.append(m_member.desc);
272         return sb.toString();
273     }
274 
275     /***
276      * Update the parameter name given the parameter information
277      * the index is the one from the register ie a long or double will needs 2 register
278      *
279      * @param registerIndex
280      * @param parameterName
281      */
282     public void pushParameterNameFromRegister(int registerIndex, String parameterName) {
283         int registerStart = 1;
284         if (Modifier.isStatic(m_member.modifiers)) {
285             registerStart = 0;
286         }
287         // assume we have a stack starting at the first parameter
288         int registerIndexFrom0 = registerIndex - registerStart;
289         Type[] parameters = Type.getArgumentTypes(m_member.desc);
290         int typeIndex = AsmHelper.getTypeIndexOf(parameters, registerIndexFrom0);
291         if (typeIndex >= 0 && typeIndex < m_parameterNames.length) {
292             m_parameterNames[typeIndex] = parameterName;
293         } else {
294             throw new DefinitionException("Could not register parameter named " + parameterName
295                 + " from register " + registerIndex + " for " + m_member.name + "." + m_member.desc);
296         }
297     }
298 }