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 }