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 }