001    /*
002     $Id: ReflectorGenerator.java,v 1.9 2005/05/27 10:13:09 russel Exp $
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: 1.9 $
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.getClassInternalName(className);
079            cw.visit(ClassGenerator.asmJDKVersion, ACC_PUBLIC + ACC_SUPER, classInternalName, (String)null, "org/codehaus/groovy/runtime/Reflector", null);
080    
081            cv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
082            cv.visitVarInsn(ALOAD, 0);
083            cv.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/Reflector", "<init>", "()V");
084            cv.visitInsn(RETURN);
085            cv.visitMaxs(1, 1);
086    
087            generateInvokeMethod();
088    
089            cw.visitEnd();
090        }
091    
092        protected void generateInvokeMethod() {
093            int methodCount = methods.size();
094    
095            cv =
096                cw.visitMethod(
097                    ACC_PUBLIC,
098                    "invoke",
099                    "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
100                    null,
101                    null);
102            helper = new BytecodeHelper(cv);
103    
104            cv.visitVarInsn(ALOAD, 1);
105            cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/MetaMethod", "getMethodIndex", "()I");
106            Label defaultLabel = new Label();
107            Label[] labels = new Label[methodCount];
108            int[] indices = new int[methodCount];
109            for (int i = 0; i < methodCount; i++) {
110                labels[i] = new Label();
111    
112                MetaMethod method = (MetaMethod) methods.get(i);
113                method.setMethodIndex(i + 1);
114                indices[i] = method.getMethodIndex();
115    
116                //System.out.println("Index: " + method.getMethodIndex() + " for: " + method);
117            }
118    
119            cv.visitLookupSwitchInsn(defaultLabel, indices, labels);
120            //cv.visitTableSwitchInsn(minMethodIndex, maxMethodIndex, defaultLabel, labels);
121    
122            for (int i = 0; i < methodCount; i++) {
123                cv.visitLabel(labels[i]);
124    
125                MetaMethod method = (MetaMethod) methods.get(i);
126                invokeMethod(method);
127                if (method.getReturnType() == void.class) {
128                    cv.visitInsn(ACONST_NULL);
129                }
130                cv.visitInsn(ARETURN);
131            }
132    
133            cv.visitLabel(defaultLabel);
134            cv.visitVarInsn(ALOAD, 0);
135            cv.visitVarInsn(ALOAD, 1);
136            cv.visitVarInsn(ALOAD, 2);
137            cv.visitVarInsn(ALOAD, 3);
138            cv.visitMethodInsn(
139                INVOKEVIRTUAL,
140                classInternalName,
141                "noSuchMethod",
142                "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
143            cv.visitInsn(ARETURN);
144            cv.visitMaxs(4, 4);
145        }
146    
147        protected void invokeMethod(MetaMethod method) {
148            /** simple
149            cv.visitVarInsn(ALOAD, 2);
150            cv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;");
151            */
152            Class ownerClass = method.getInterfaceClass();
153            boolean useInterface = false;
154            if (ownerClass == null) {
155                ownerClass = method.getDeclaringClass();
156            }
157            else {
158                useInterface = true;
159            }
160            String type = BytecodeHelper.getClassInternalName(ownerClass.getName());
161            String descriptor = BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes());
162    
163            //        System.out.println("Method: " + method);
164            //        System.out.println("Descriptor: " + descriptor);
165    
166            if (method.isStatic()) {
167                loadParameters(method, 3);
168                cv.visitMethodInsn(INVOKESTATIC, type, method.getName(), descriptor);
169            }
170            else {
171                cv.visitVarInsn(ALOAD, 2);
172                helper.doCast(ownerClass);
173                loadParameters(method, 3);
174                cv.visitMethodInsn((useInterface) ? INVOKEINTERFACE : INVOKEVIRTUAL, type, method.getName(), descriptor);
175            }
176    
177            helper.box(method.getReturnType());
178        }
179    
180        /*
181        protected void generateInvokeSuperMethod() {
182            List superMethods = new ArrayList(methods);
183            for (Iterator iter = methods.iterator(); iter.hasNext();) {
184                MetaMethod method = (MetaMethod) iter.next();
185                if (!validSuperMethod(method)) {
186                    superMethods.remove(method);
187                }
188            }
189            int methodCount = superMethods.size();
190            if (methodCount == 0) {
191                return;
192            }
193            cv =
194                cw.visitMethod(
195                    ACC_PUBLIC,
196                    "invokeSuper",
197                    "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
198                    null,
199                    null);
200            helper = new BytecodeHelper(cv);
201    
202            cv.visitVarInsn(ALOAD, 1);
203            cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/MetaMethod", "getMethodIndex", "()I");
204            Label defaultLabel = new Label();
205            Label[] labels = new Label[methodCount];
206            int[] indices = new int[methodCount];
207            for (int i = 0; i < methodCount; i++) {
208                labels[i] = new Label();
209    
210                MetaMethod method = (MetaMethod) superMethods.get(i);
211                method.setMethodIndex(i + 1);
212                indices[i] = method.getMethodIndex();
213    
214                //System.out.println("Index: " + method.getMethodIndex() + " for: " + method);
215            }
216    
217            cv.visitLookupSwitchInsn(defaultLabel, indices, labels);
218            //cv.visitTableSwitchInsn(minMethodIndex, maxMethodIndex, defaultLabel, labels);
219    
220            for (int i = 0; i < methodCount; i++) {
221                MetaMethod method = (MetaMethod) superMethods.get(i);
222                cv.visitLabel(labels[i]);
223    
224                invokeSuperMethod(method);
225                if (method.getReturnType() == void.class) {
226                    cv.visitInsn(ACONST_NULL);
227                }
228                cv.visitInsn(ARETURN);
229            }
230    
231            cv.visitLabel(defaultLabel);
232            cv.visitVarInsn(ALOAD, 0);
233            cv.visitVarInsn(ALOAD, 1);
234            cv.visitVarInsn(ALOAD, 2);
235            cv.visitVarInsn(ALOAD, 3);
236            cv.visitMethodInsn(
237                INVOKEVIRTUAL,
238                classInternalName,
239                "noSuchMethod",
240                "(Lgroovy/lang/MetaMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
241            cv.visitInsn(ARETURN);
242            cv.visitMaxs(4, 4);
243        }
244    
245        protected boolean validSuperMethod(MetaMethod method) {
246            return !method.isStatic() && (method.getModifiers() & (Modifier.FINAL | Modifier.ABSTRACT)) == 0 && theClass == method.getDeclaringClass();
247        }
248    
249        protected void invokeSuperMethod(MetaMethod method) {
250            Class ownerClass = method.getDeclaringClass();
251            String type = helper.getClassInternalName(ownerClass.getName());
252            String descriptor = helper.getMethodDescriptor(method.getReturnType(), method.getParameterTypes());
253    
254    //        System.out.println("Method: " + method.getName());
255    //        System.out.println("Descriptor: " + descriptor);
256    
257            cv.visitVarInsn(ALOAD, 2);
258            //helper.doCast(ownerClass);
259            loadParameters(method, 3);
260            cv.visitMethodInsn(INVOKESPECIAL, type, method.getName(), descriptor);
261    
262            helper.box(method.getReturnType());
263        }
264    */
265        
266        protected void loadParameters(MetaMethod method, int argumentIndex) {
267            Class[] parameters = method.getParameterTypes();
268            int size = parameters.length;
269            for (int i = 0; i < size; i++) {
270                cv.visitVarInsn(ALOAD, argumentIndex);
271                helper.pushConstant(i);
272                cv.visitInsn(AALOAD);
273    
274                // we should cast to something
275                Class type = parameters[i];
276                if (type.isPrimitive()) {
277                    helper.unbox(type);
278                }
279                else {
280                    helper.doCast(type.getName());
281                }
282            }
283        }
284    }