View Javadoc

    1   /*
    2    * $Id: DummyClassGenerator.java,v 1.2 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 that the
    8    * following conditions are met: 1. Redistributions of source code must retain
    9    * copyright statements and notices. Redistributions must also contain a copy
    10   * of this document. 2. Redistributions in binary form must reproduce the above
    11   * copyright notice, this list of conditions and the following disclaimer in
    12   * the documentation and/or other materials provided with the distribution. 3.
    13   * The name "groovy" must not be used to endorse or promote products derived
    14   * from this Software without prior written permission of The Codehaus. For
    15   * written permission, please contact info@codehaus.org. 4. Products derived
    16   * from this Software may not be called "groovy" nor may "groovy" appear in
    17   * their names without prior written permission of The Codehaus. "groovy" is a
    18   * registered trademark of The Codehaus. 5. Due credit should be given to The
    19   * Codehaus - http://groovy.codehaus.org/
    20   *
    21   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
    22   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    23   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    24   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
    25   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    27   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
    28   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
    31   * DAMAGE.
    32   *
    33   */
    34  package org.codehaus.groovy.classgen;
    35
    36  import groovy.lang.GroovyRuntimeException;
    37  import groovy.lang.MissingClassException;
    38  import org.codehaus.groovy.ast.*;
    39  import org.objectweb.asm.ClassVisitor;
    40  import org.objectweb.asm.CodeVisitor;
    41
    42  import java.util.*;
    43
    44  /***
    45   * To generate a class that has all the fields and methods, except that fields are not initilized
    46   * and methods are empty. It's intended for being used as a place holder during code generation
    47   * of reference to the "this" class itself.
    48   *
    49   * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
    50   * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
    51   *
    52   * @version $Revision: 1.2 $
    53   */
    54  public class DummyClassGenerator extends ClassGenerator {
    55
    56      private ClassVisitor cw;
    57      private CodeVisitor cv;
    58      private GeneratorContext context;
    59
    60      private String sourceFile;
    61
    62      // current class details
    63      private ClassNode classNode;
    64      private String internalClassName;
    65      private String internalBaseClassName;
    66
    67
    68      public DummyClassGenerator(
    69          GeneratorContext context,
    70          ClassVisitor classVisitor,
    71          ClassLoader classLoader,
    72          String sourceFile) {
    73          super(classLoader);
    74          this.context = context;
    75          this.cw = classVisitor;
    76          this.sourceFile = sourceFile;
    77      }
    78
    79      // GroovyClassVisitor interface
    80      //-------------------------------------------------------------------------
    81      public void visitClass(ClassNode classNode) {
    82          try {
    83              this.classNode = classNode;
    84              this.internalClassName = BytecodeHelper.getClassInternalName(classNode.getName());
    85
    86              //System.out.println("Generating class: " + classNode.getName());
    87
    88              // lets check that the classes are all valid
    89              classNode.setSuperClass(checkValidType(classNode.getSuperClass(), classNode, "Must be a valid base class"));
    90              String[] interfaces = classNode.getInterfaces();
    91              for (int i = 0; i < interfaces.length; i++ ) {
    92                  interfaces[i] = checkValidType(interfaces[i], classNode, "Must be a valid interface name");
    93              }
    94
    95              this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
    96
    97              cw.visit(
    98                  asmJDKVersion,
    99                  classNode.getModifiers(),
    100                 internalClassName,
    101                 internalBaseClassName,
    102                 BytecodeHelper.getClassInternalNames(classNode.getInterfaces()),
    103                 sourceFile);
    104
    105             classNode.visitContents(this);
    106
    107             for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
    108                 ClassNode innerClass = (ClassNode) iter.next();
    109                 String innerClassName = innerClass.getName();
    110                 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
    111                 String outerClassName = internalClassName; // default for inner classes
    112                 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
    113                 if (enclosingMethod != null) {
    114                     // local inner classes do not specify the outer class name
    115                     outerClassName = null;
    116                 }
    117                 cw.visitInnerClass(
    118                     innerClassInternalName,
    119                     outerClassName,
    120                     innerClassName,
    121                     innerClass.getModifiers());
    122             }
    123             cw.visitEnd();
    124         }
    125         catch (GroovyRuntimeException e) {
    126             e.setModule(classNode.getModule());
    127             throw e;
    128         }
    129     }
    130
    131     public void visitConstructor(ConstructorNode node) {
    132
    133         visitParameters(node, node.getParameters());
    134
    135         String methodType = BytecodeHelper.getMethodDescriptor("void", node.getParameters());
    136         cv = cw.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
    137         cv.visitTypeInsn(NEW, "java/lang/RuntimeException");
    138         cv.visitInsn(DUP);
    139         cv.visitLdcInsn("not intended for execution");
    140         cv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
    141         cv.visitInsn(ATHROW);
    142         cv.visitMaxs(0, 0);
    143     }
    144
    145     public void visitMethod(MethodNode node) {
    146
    147         visitParameters(node, node.getParameters());
    148         node.setReturnType(checkValidType(node.getReturnType(), node, "Must be a valid return type"));
    149
    150         String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
    151         cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
    152
    153         cv.visitTypeInsn(NEW, "java/lang/RuntimeException");
    154         cv.visitInsn(DUP);
    155         cv.visitLdcInsn("not intended for execution");
    156         cv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
    157         cv.visitInsn(ATHROW);
    158
    159         cv.visitMaxs(0, 0);
    160     }
    161
    162     public void visitField(FieldNode fieldNode) {
    163
    164         // lets check that the classes are all valid
    165         fieldNode.setType(checkValidType(fieldNode.getType(), fieldNode, "Must be a valid field class for field: " + fieldNode.getName()));
    166
    167         cw.visitField(
    168             fieldNode.getModifiers(),
    169             fieldNode.getName(),
    170             BytecodeHelper.getTypeDescription(fieldNode.getType()),
    171             null, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
    172             null);
    173     }
    174
    175     /***
    176      * Creates a getter, setter and field
    177      */
    178     public void visitProperty(PropertyNode statement) {
    179     }
    180
    181
    182     protected String checkValidType(String type, ASTNode node, String message) {
    183         if (type!= null && type.length() == 0)
    184             return "java.lang.Object";
    185         if (type.endsWith("[]")) {
    186             String postfix = "[]";
    187             String prefix = type.substring(0, type.length() - 2);
    188             return checkValidType(prefix, node, message) + postfix;
    189         }
    190         int idx = type.indexOf('$');
    191         if (idx > 0) {
    192             String postfix = type.substring(idx);
    193             String prefix = type.substring(0, idx);
    194             return checkValidType(prefix, node, message) + postfix;
    195         }
    196         if (BytecodeHelper.isPrimitiveType(type) || "void".equals(type)) {
    197             return type;
    198         }
    199         String original = type;
    200         type = resolveClassName(type);
    201         if (type != null) {
    202             return type;
    203         }
    204
    205         throw new MissingClassException(original, node, message + " for class: " + classNode.getName());
    206     }
    207     protected String resolveClassName(String type) {
    208         return classNode.resolveClassName(type);
    209     }
    210
    211     protected static boolean isPrimitiveFieldType(String type) {
    212         return type.equals("java.lang.String")
    213             || type.equals("java.lang.Integer")
    214             || type.equals("java.lang.Double")
    215             || type.equals("java.lang.Long")
    216             || type.equals("java.lang.Float");
    217     }
    218     protected Class loadClass(String name) {
    219         if (name.equals(this.classNode.getName())) {
    220             return Object.class;
    221         }
    222
    223         if (name == null) {
    224             return null;
    225         }
    226         else if (name.length() == 0) {
    227             return Object.class;
    228         }
    229
    230         else if ("void".equals(name)) {
    231             return void.class;
    232         }
    233         else if ("boolean".equals(name)) {
    234             return boolean.class;
    235         }
    236         else if ("byte".equals(name)) {
    237             return byte.class;
    238         }
    239         else if ("short".equals(name)) {
    240             return short.class;
    241         }
    242         else if ("char".equals(name)) {
    243             return char.class;
    244         }
    245         else if ("int".equals(name)) {
    246             return int.class;
    247         }
    248         else if ("long".equals(name)) {
    249             return long.class;
    250         }
    251         else if ("float".equals(name)) {
    252             return float.class;
    253         }
    254         else if ("double".equals(name)) {
    255             return double.class;
    256         }
    257
    258         name = BytecodeHelper.formatNameForClassLoading(name);
    259
    260     	try {
    261     		Class cls = (Class)classCache.get(name);
    262     		if (cls != null)
    263     			return cls;
    264
    265     		CompileUnit compileUnit = getCompileUnit();
    266             if (compileUnit != null) {
    267             	cls = compileUnit.loadClass(name);
    268                 classCache.put(name, cls);
    269             	return cls;
    270             }
    271             else {
    272                 throw new ClassGeneratorException("Could not load class: " + name);
    273             }
    274         }
    275         catch (ClassNotFoundException e) {
    276             throw new ClassGeneratorException("Error when compiling class: " + classNode.getName() + ". Reason: could not load class: " + name + " reason: " + e, e);
    277         }
    278     }
    279
    280     Map classCache = new HashMap();
    281     {
    282         classCache.put("int", Integer.TYPE);
    283         classCache.put("byte", Byte.TYPE);
    284         classCache.put("short", Short.TYPE);
    285         classCache.put("char", Character.TYPE);
    286         classCache.put("boolean", Boolean.TYPE);
    287         classCache.put("long", Long.TYPE);
    288         classCache.put("double", Double.TYPE);
    289         classCache.put("float", Float.TYPE);
    290     }
    291     protected CompileUnit getCompileUnit() {
    292         CompileUnit answer = classNode.getCompileUnit();
    293         if (answer == null) {
    294             answer = context.getCompileUnit();
    295         }
    296         return answer;
    297     }
    298
    299     protected void visitParameters(ASTNode node, Parameter[] parameters) {
    300         for (int i = 0, size = parameters.length; i < size; i++ ) {
    301             visitParameter(node, parameters[i]);
    302         }
    303     }
    304
    305     protected void visitParameter(ASTNode node, Parameter parameter) {
    306         if (! parameter.isDynamicType()) {
    307             parameter.setType(checkValidType(parameter.getType(), node, "Must be a valid parameter class"));
    308         }
    309     }
    310
    311 }