001: /*
002: $Id: ReflectorGenerator.java 4120 2006-10-17 12:05:59Z blackdrag $
003:
004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005:
006: Redistribution and use of this software and associated documentation
007: ("Software"), with or without modification, are permitted provided
008: that the following conditions are met:
009:
010: 1. Redistributions of source code must retain copyright
011: statements and notices. Redistributions must also contain a
012: copy of this document.
013:
014: 2. Redistributions in binary form must reproduce the
015: above copyright notice, this list of conditions and the
016: following disclaimer in the documentation and/or other
017: materials provided with the distribution.
018:
019: 3. The name "groovy" must not be used to endorse or promote
020: products derived from this Software without prior written
021: permission of The Codehaus. For written permission,
022: please contact info@codehaus.org.
023:
024: 4. Products derived from this Software may not be called "groovy"
025: nor may "groovy" appear in their names without prior written
026: permission of The Codehaus. "groovy" is a registered
027: trademark of The Codehaus.
028:
029: 5. Due credit should be given to The Codehaus -
030: http://groovy.codehaus.org/
031:
032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043: OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: */
046: package org.codehaus.groovy.classgen;
047:
048: import groovy.lang.MetaMethod;
049:
050: import java.util.List;
051:
052: import org.objectweb.asm.ClassVisitor;
053: import org.objectweb.asm.MethodVisitor;
054: import org.objectweb.asm.Opcodes;
055: import org.objectweb.asm.Label;
056:
057: /**
058: * Code generates a Reflector
059: *
060: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
061: * @version $Revision: 4120 $
062: */
063: public class ReflectorGenerator implements Opcodes {
064:
065: private List methods;
066: private ClassVisitor cw;
067: private MethodVisitor cv;
068: private BytecodeHelper helper = new BytecodeHelper(null);
069: private String classInternalName;
070:
071: public ReflectorGenerator(List methods) {
072: this .methods = methods;
073: }
074:
075: public void generate(ClassVisitor cw, String className) {
076: this .cw = cw;
077:
078: classInternalName = BytecodeHelper
079: .getClassInternalName(className);
080: cw.visit(ClassGenerator.asmJDKVersion, ACC_PUBLIC + ACC_SUPER,
081: classInternalName, (String) null,
082: "org/codehaus/groovy/runtime/Reflector", null);
083:
084: cv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
085: cv.visitVarInsn(ALOAD, 0);
086: cv.visitMethodInsn(INVOKESPECIAL,
087: "org/codehaus/groovy/runtime/Reflector", "<init>",
088: "()V");
089: cv.visitInsn(RETURN);
090: cv.visitMaxs(1, 1);
091:
092: generateInvokeMethod();
093:
094: cw.visitEnd();
095: }
096:
097: protected void generateInvokeMethod() {
098: int methodCount = methods.size();
099:
100: cv = cw
101: .visitMethod(
102: ACC_PUBLIC,
103: "invoke",
104: "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
105: null, null);
106: helper = new BytecodeHelper(cv);
107:
108: cv.visitVarInsn(ALOAD, 1);
109: cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/MetaMethod",
110: "getMethodIndex", "()I");
111: Label defaultLabel = new Label();
112: Label[] labels = new Label[methodCount];
113: int[] indices = new int[methodCount];
114: for (int i = 0; i < methodCount; i++) {
115: labels[i] = new Label();
116:
117: MetaMethod method = (MetaMethod) methods.get(i);
118: method.setMethodIndex(i + 1);
119: indices[i] = method.getMethodIndex();
120:
121: //System.out.println("Index: " + method.getMethodIndex() + " for: " + method);
122: }
123:
124: cv.visitLookupSwitchInsn(defaultLabel, indices, labels);
125: //cv.visitTableSwitchInsn(minMethodIndex, maxMethodIndex, defaultLabel, labels);
126:
127: for (int i = 0; i < methodCount; i++) {
128: cv.visitLabel(labels[i]);
129:
130: MetaMethod method = (MetaMethod) methods.get(i);
131: invokeMethod(method);
132: if (method.getReturnType() == void.class) {
133: cv.visitInsn(ACONST_NULL);
134: }
135: cv.visitInsn(ARETURN);
136: }
137:
138: cv.visitLabel(defaultLabel);
139: cv.visitVarInsn(ALOAD, 0);
140: cv.visitVarInsn(ALOAD, 1);
141: cv.visitVarInsn(ALOAD, 2);
142: cv.visitVarInsn(ALOAD, 3);
143: cv
144: .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 callClass = method.getInterfaceClass();
159: boolean useInterface = false;
160: if (callClass == null) {
161: callClass = method.getCallClass();
162: } else {
163: useInterface = true;
164: }
165: String type = BytecodeHelper.getClassInternalName(callClass
166: .getName());
167: String descriptor = BytecodeHelper.getMethodDescriptor(method
168: .getReturnType(), method.getParameterTypes());
169:
170: // System.out.println("Method: " + method);
171: // System.out.println("Descriptor: " + descriptor);
172:
173: if (method.isStatic()) {
174: loadParameters(method, 3);
175: cv.visitMethodInsn(INVOKESTATIC, type, method.getName(),
176: descriptor);
177: } else {
178: cv.visitVarInsn(ALOAD, 2);
179: helper.doCast(callClass);
180: loadParameters(method, 3);
181: cv
182: .visitMethodInsn((useInterface) ? INVOKEINTERFACE
183: : INVOKEVIRTUAL, type, method.getName(),
184: descriptor);
185: }
186:
187: helper.box(method.getReturnType());
188: }
189:
190: /*
191: protected void generateInvokeSuperMethod() {
192: List superMethods = new ArrayList(methods);
193: for (Iterator iter = methods.iterator(); iter.hasNext();) {
194: MetaMethod method = (MetaMethod) iter.next();
195: if (!validSuperMethod(method)) {
196: superMethods.remove(method);
197: }
198: }
199: int methodCount = superMethods.size();
200: if (methodCount == 0) {
201: return;
202: }
203: cv =
204: cw.visitMethod(
205: ACC_PUBLIC,
206: "invokeSuper",
207: "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
208: null,
209: null);
210: helper = new BytecodeHelper(cv);
211:
212: cv.visitVarInsn(ALOAD, 1);
213: cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/MetaMethod", "getMethodIndex", "()I");
214: Label defaultLabel = new Label();
215: Label[] labels = new Label[methodCount];
216: int[] indices = new int[methodCount];
217: for (int i = 0; i < methodCount; i++) {
218: labels[i] = new Label();
219:
220: MetaMethod method = (MetaMethod) superMethods.get(i);
221: method.setMethodIndex(i + 1);
222: indices[i] = method.getMethodIndex();
223:
224: //System.out.println("Index: " + method.getMethodIndex() + " for: " + method);
225: }
226:
227: cv.visitLookupSwitchInsn(defaultLabel, indices, labels);
228: //cv.visitTableSwitchInsn(minMethodIndex, maxMethodIndex, defaultLabel, labels);
229:
230: for (int i = 0; i < methodCount; i++) {
231: MetaMethod method = (MetaMethod) superMethods.get(i);
232: cv.visitLabel(labels[i]);
233:
234: invokeSuperMethod(method);
235: if (method.getReturnType() == void.class) {
236: cv.visitInsn(ACONST_NULL);
237: }
238: cv.visitInsn(ARETURN);
239: }
240:
241: cv.visitLabel(defaultLabel);
242: cv.visitVarInsn(ALOAD, 0);
243: cv.visitVarInsn(ALOAD, 1);
244: cv.visitVarInsn(ALOAD, 2);
245: cv.visitVarInsn(ALOAD, 3);
246: cv.visitMethodInsn(
247: INVOKEVIRTUAL,
248: classInternalName,
249: "noSuchMethod",
250: "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
251: cv.visitInsn(ARETURN);
252: cv.visitMaxs(4, 4);
253: }
254:
255: protected boolean validSuperMethod(MetaMethod method) {
256: return !method.isStatic() && (method.getModifiers() & (Modifier.FINAL | Modifier.ABSTRACT)) == 0 && theClass == method.getDeclaringClass();
257: }
258:
259: protected void invokeSuperMethod(MetaMethod method) {
260: Class ownerClass = method.getDeclaringClass();
261: String type = helper.getClassInternalName(ownerClass.getName());
262: String descriptor = helper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes());
263:
264: // System.out.println("Method: " + method.getName());
265: // System.out.println("Descriptor: " + descriptor);
266:
267: cv.visitVarInsn(ALOAD, 2);
268: //helper.doCast(ownerClass);
269: loadParameters(method, 3);
270: cv.visitMethodInsn(INVOKESPECIAL, type, method.getName(), descriptor);
271:
272: helper.box(method.getReturnType());
273: }
274: */
275:
276: protected void loadParameters(MetaMethod method, int argumentIndex) {
277: Class[] parameters = method.getParameterTypes();
278: int size = parameters.length;
279: for (int i = 0; i < size; i++) {
280: cv.visitVarInsn(ALOAD, argumentIndex);
281: helper.pushConstant(i);
282: cv.visitInsn(AALOAD);
283:
284: // we should cast to something
285: Class type = parameters[i];
286: if (type.isPrimitive()) {
287: helper.unbox(type);
288: } else {
289: helper.doCast(type);
290: }
291: }
292: }
293: }
|