View Javadoc

1   /*
2    $Id: ReflectorGenerator.java,v 1.8 2004/12/14 16:18:14 russel Exp $
3   
4    Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5   
6    Redistribution and use of this software and associated documentation
7    ("Software"), with or without modification, are permitted provided
8    that the following conditions are met:
9   
10   1. Redistributions of source code must retain copyright
11      statements and notices.  Redistributions must also contain a
12      copy of this document.
13  
14   2. Redistributions in binary form must reproduce the
15      above copyright notice, this list of conditions and the
16      following disclaimer in the documentation and/or other
17      materials provided with the distribution.
18  
19   3. The name "groovy" must not be used to endorse or promote
20      products derived from this Software without prior written
21      permission of The Codehaus.  For written permission,
22      please contact info@codehaus.org.
23  
24   4. Products derived from this Software may not be called "groovy"
25      nor may "groovy" appear in their names without prior written
26      permission of The Codehaus. "groovy" is a registered
27      trademark of The Codehaus.
28  
29   5. Due credit should be given to The Codehaus -
30      http://groovy.codehaus.org/
31  
32   THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33   ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34   NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35   FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
36   THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43   OF THE POSSIBILITY OF SUCH DAMAGE.
44  
45   */
46  package org.codehaus.groovy.classgen;
47  
48  import groovy.lang.MetaMethod;
49  
50  import java.util.List;
51  
52  import org.objectweb.asm.ClassVisitor;
53  import org.objectweb.asm.CodeVisitor;
54  import org.objectweb.asm.Constants;
55  import org.objectweb.asm.Label;
56  
57  /***
58   * Code generates a Reflector 
59   * 
60   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
61   * @version $Revision: 1.8 $
62   */
63  public class ReflectorGenerator implements Constants {
64  
65      private List methods;
66      private ClassVisitor cw;
67      private CodeVisitor cv;
68      private BytecodeHelper helper = new BytecodeHelper(null);
69      private String classInternalName;
70  
71      public ReflectorGenerator(List methods) {
72          this.methods = methods;
73      }
74  
75      public void generate(ClassVisitor cw, String className) {
76          this.cw = cw;
77          String fileName = className;
78          int idx = className.lastIndexOf('.');
79          if (idx > 0) {
80              fileName = className.substring(idx + 1);
81          }
82          fileName += ".java";
83  
84          classInternalName = BytecodeHelper.getClassInternalName(className);
85          cw.visit(ClassGenerator.asmJDKVersion, ACC_PUBLIC + ACC_SUPER, classInternalName, "org/codehaus/groovy/runtime/Reflector", null, fileName);
86  
87          cv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
88          cv.visitVarInsn(ALOAD, 0);
89          cv.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/Reflector", "<init>", "()V");
90          cv.visitInsn(RETURN);
91          cv.visitMaxs(1, 1);
92  
93          generateInvokeMethod();
94  
95          cw.visitEnd();
96      }
97  
98      protected void generateInvokeMethod() {
99          int methodCount = methods.size();
100 
101         cv =
102             cw.visitMethod(
103                 ACC_PUBLIC,
104                 "invoke",
105                 "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
106                 null,
107                 null);
108         helper = new BytecodeHelper(cv);
109 
110         cv.visitVarInsn(ALOAD, 1);
111         cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/MetaMethod", "getMethodIndex", "()I");
112         Label defaultLabel = new Label();
113         Label[] labels = new Label[methodCount];
114         int[] indices = new int[methodCount];
115         for (int i = 0; i < methodCount; i++) {
116             labels[i] = new Label();
117 
118             MetaMethod method = (MetaMethod) methods.get(i);
119             method.setMethodIndex(i + 1);
120             indices[i] = method.getMethodIndex();
121 
122             //System.out.println("Index: " + method.getMethodIndex() + " for: " + method);
123         }
124 
125         cv.visitLookupSwitchInsn(defaultLabel, indices, labels);
126         //cv.visitTableSwitchInsn(minMethodIndex, maxMethodIndex, defaultLabel, labels);
127 
128         for (int i = 0; i < methodCount; i++) {
129             cv.visitLabel(labels[i]);
130 
131             MetaMethod method = (MetaMethod) methods.get(i);
132             invokeMethod(method);
133             if (method.getReturnType() == void.class) {
134                 cv.visitInsn(ACONST_NULL);
135             }
136             cv.visitInsn(ARETURN);
137         }
138 
139         cv.visitLabel(defaultLabel);
140         cv.visitVarInsn(ALOAD, 0);
141         cv.visitVarInsn(ALOAD, 1);
142         cv.visitVarInsn(ALOAD, 2);
143         cv.visitVarInsn(ALOAD, 3);
144         cv.visitMethodInsn(
145             INVOKEVIRTUAL,
146             classInternalName,
147             "noSuchMethod",
148             "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
149         cv.visitInsn(ARETURN);
150         cv.visitMaxs(4, 4);
151     }
152 
153     protected void invokeMethod(MetaMethod method) {
154         /*** simple
155         cv.visitVarInsn(ALOAD, 2);
156         cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
157         */
158         Class ownerClass = method.getInterfaceClass();
159         boolean useInterface = false;
160         if (ownerClass == null) {
161             ownerClass = method.getDeclaringClass();
162         }
163         else {
164             useInterface = true;
165         }
166         String type = BytecodeHelper.getClassInternalName(ownerClass.getName());
167         String descriptor = BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes());
168 
169         //        System.out.println("Method: " + method);
170         //        System.out.println("Descriptor: " + descriptor);
171 
172         if (method.isStatic()) {
173             loadParameters(method, 3);
174             cv.visitMethodInsn(INVOKESTATIC, type, method.getName(), descriptor);
175         }
176         else {
177             cv.visitVarInsn(ALOAD, 2);
178             helper.doCast(ownerClass);
179             loadParameters(method, 3);
180             cv.visitMethodInsn((useInterface) ? INVOKEINTERFACE : INVOKEVIRTUAL, type, method.getName(), descriptor);
181         }
182 
183         helper.box(method.getReturnType());
184     }
185 
186     /*
187     protected void generateInvokeSuperMethod() {
188         List superMethods = new ArrayList(methods);
189         for (Iterator iter = methods.iterator(); iter.hasNext();) {
190             MetaMethod method = (MetaMethod) iter.next();
191             if (!validSuperMethod(method)) {
192                 superMethods.remove(method);
193             }
194         }
195         int methodCount = superMethods.size();
196         if (methodCount == 0) {
197             return;
198         }
199         cv =
200             cw.visitMethod(
201                 ACC_PUBLIC,
202                 "invokeSuper",
203                 "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
204                 null,
205                 null);
206         helper = new BytecodeHelper(cv);
207 
208         cv.visitVarInsn(ALOAD, 1);
209         cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/MetaMethod", "getMethodIndex", "()I");
210         Label defaultLabel = new Label();
211         Label[] labels = new Label[methodCount];
212         int[] indices = new int[methodCount];
213         for (int i = 0; i < methodCount; i++) {
214             labels[i] = new Label();
215 
216             MetaMethod method = (MetaMethod) superMethods.get(i);
217             method.setMethodIndex(i + 1);
218             indices[i] = method.getMethodIndex();
219 
220             //System.out.println("Index: " + method.getMethodIndex() + " for: " + method);
221         }
222 
223         cv.visitLookupSwitchInsn(defaultLabel, indices, labels);
224         //cv.visitTableSwitchInsn(minMethodIndex, maxMethodIndex, defaultLabel, labels);
225 
226         for (int i = 0; i < methodCount; i++) {
227             MetaMethod method = (MetaMethod) superMethods.get(i);
228             cv.visitLabel(labels[i]);
229 
230             invokeSuperMethod(method);
231             if (method.getReturnType() == void.class) {
232                 cv.visitInsn(ACONST_NULL);
233             }
234             cv.visitInsn(ARETURN);
235         }
236 
237         cv.visitLabel(defaultLabel);
238         cv.visitVarInsn(ALOAD, 0);
239         cv.visitVarInsn(ALOAD, 1);
240         cv.visitVarInsn(ALOAD, 2);
241         cv.visitVarInsn(ALOAD, 3);
242         cv.visitMethodInsn(
243             INVOKEVIRTUAL,
244             classInternalName,
245             "noSuchMethod",
246             "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
247         cv.visitInsn(ARETURN);
248         cv.visitMaxs(4, 4);
249     }
250 
251     protected boolean validSuperMethod(MetaMethod method) {
252         return !method.isStatic() && (method.getModifiers() & (Modifier.FINAL | Modifier.ABSTRACT)) == 0 && theClass == method.getDeclaringClass();
253     }
254 
255     protected void invokeSuperMethod(MetaMethod method) {
256         Class ownerClass = method.getDeclaringClass();
257         String type = helper.getClassInternalName(ownerClass.getName());
258         String descriptor = helper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes());
259 
260 //        System.out.println("Method: " + method.getName());
261 //        System.out.println("Descriptor: " + descriptor);
262 
263         cv.visitVarInsn(ALOAD, 2);
264         //helper.doCast(ownerClass);
265         loadParameters(method, 3);
266         cv.visitMethodInsn(INVOKESPECIAL, type, method.getName(), descriptor);
267 
268         helper.box(method.getReturnType());
269     }
270 */
271     
272     protected void loadParameters(MetaMethod method, int argumentIndex) {
273         Class[] parameters = method.getParameterTypes();
274         int size = parameters.length;
275         for (int i = 0; i < size; i++) {
276             cv.visitVarInsn(ALOAD, argumentIndex);
277             helper.pushConstant(i);
278             cv.visitInsn(AALOAD);
279 
280             // we should cast to something
281             Class type = parameters[i];
282             if (type.isPrimitive()) {
283                 helper.unbox(type);
284             }
285             else {
286                 helper.doCast(type.getName());
287             }
288         }
289     }
290 }