View Javadoc

1   /*
2    * $Id: DummyClassGenerator.java,v 1.1 2004/07/10 03:31:39 bran 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.1 $
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                  classNode.getModifiers(),
99                  internalClassName,
100                 internalBaseClassName,
101                 BytecodeHelper.getClassInternalNames(classNode.getInterfaces()),
102                 sourceFile);
103 
104             classNode.visitContents(this);
105 
106             for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
107                 ClassNode innerClass = (ClassNode) iter.next();
108                 String innerClassName = innerClass.getName();
109                 String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
110                 String outerClassName = internalClassName; // default for inner classes
111                 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
112                 if (enclosingMethod != null) {
113                     // local inner classes do not specify the outer class name
114                     outerClassName = null;
115                 }
116                 cw.visitInnerClass(
117                     innerClassInternalName,
118                     outerClassName,
119                     innerClassName,
120                     innerClass.getModifiers());
121             }
122             cw.visitEnd();
123         }
124         catch (GroovyRuntimeException e) {
125             e.setModule(classNode.getModule());
126             throw e;
127         }
128     }
129 
130     public void visitConstructor(ConstructorNode node) {
131 
132         visitParameters(node, node.getParameters());
133 
134         String methodType = BytecodeHelper.getMethodDescriptor("void", node.getParameters());
135         cv = cw.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
136         cv.visitTypeInsn(NEW, "java/lang/RuntimeException");
137         cv.visitInsn(DUP);
138         cv.visitLdcInsn("not intended for execution");
139         cv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
140         cv.visitInsn(ATHROW);
141         cv.visitMaxs(0, 0);
142     }
143 
144     public void visitMethod(MethodNode node) {
145 
146         visitParameters(node, node.getParameters());
147         node.setReturnType(checkValidType(node.getReturnType(), node, "Must be a valid return type"));
148 
149         String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
150         cv = cw.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
151 
152         cv.visitTypeInsn(NEW, "java/lang/RuntimeException");
153         cv.visitInsn(DUP);
154         cv.visitLdcInsn("not intended for execution");
155         cv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
156         cv.visitInsn(ATHROW);
157 
158         cv.visitMaxs(0, 0);
159     }
160 
161     public void visitField(FieldNode fieldNode) {
162 
163         // lets check that the classes are all valid
164         fieldNode.setType(checkValidType(fieldNode.getType(), fieldNode, "Must be a valid field class for field: " + fieldNode.getName()));
165 
166         cw.visitField(
167             fieldNode.getModifiers(),
168             fieldNode.getName(),
169             BytecodeHelper.getTypeDescription(fieldNode.getType()),
170             null, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
171             null);
172     }
173 
174     /***
175      * Creates a getter, setter and field
176      */
177     public void visitProperty(PropertyNode statement) {
178     }
179 
180 
181     protected String checkValidType(String type, ASTNode node, String message) {
182         if (type!= null && type.length() == 0)
183             return "java.lang.Object";
184         if (type.endsWith("[]")) {
185             String postfix = "[]";
186             String prefix = type.substring(0, type.length() - 2);
187             return checkValidType(prefix, node, message) + postfix;
188         }
189         int idx = type.indexOf('$');
190         if (idx > 0) {
191             String postfix = type.substring(idx);
192             String prefix = type.substring(0, idx);
193             return checkValidType(prefix, node, message) + postfix;
194         }
195         if (BytecodeHelper.isPrimitiveType(type) || "void".equals(type)) {
196             return type;
197         }
198         String original = type;
199         type = resolveClassName(type);
200         if (type != null) {
201             return type;
202         }
203 
204         throw new MissingClassException(original, node, message + " for class: " + classNode.getName());
205     }
206     protected String resolveClassName(String type) {
207         return classNode.resolveClassName(type);
208     }
209 
210     protected static boolean isPrimitiveFieldType(String type) {
211         return type.equals("java.lang.String")
212             || type.equals("java.lang.Integer")
213             || type.equals("java.lang.Double")
214             || type.equals("java.lang.Long")
215             || type.equals("java.lang.Float");
216     }
217     protected Class loadClass(String name) {
218         if (name.equals(this.classNode.getName())) {
219             return Object.class;
220         }
221 
222         if (name == null) {
223             return null;
224         }
225         else if (name.length() == 0) {
226             return Object.class;
227         }
228 
229         else if ("void".equals(name)) {
230             return void.class;
231         }
232         else if ("boolean".equals(name)) {
233             return boolean.class;
234         }
235         else if ("byte".equals(name)) {
236             return byte.class;
237         }
238         else if ("short".equals(name)) {
239             return short.class;
240         }
241         else if ("char".equals(name)) {
242             return char.class;
243         }
244         else if ("int".equals(name)) {
245             return int.class;
246         }
247         else if ("long".equals(name)) {
248             return long.class;
249         }
250         else if ("float".equals(name)) {
251             return float.class;
252         }
253         else if ("double".equals(name)) {
254             return double.class;
255         }
256 
257         name = BytecodeHelper.formatNameForClassLoading(name);
258 
259     	try {
260     		Class cls = (Class)classCache.get(name);
261     		if (cls != null)
262     			return cls;
263 
264     		CompileUnit compileUnit = getCompileUnit();
265             if (compileUnit != null) {
266             	cls = compileUnit.loadClass(name);
267                 classCache.put(name, cls);
268             	return cls;
269             }
270             else {
271                 throw new ClassGeneratorException("Could not load class: " + name);
272             }
273         }
274         catch (ClassNotFoundException e) {
275             throw new ClassGeneratorException("Error when compiling class: " + classNode.getName() + ". Reason: could not load class: " + name + " reason: " + e, e);
276         }
277     }
278 
279     Map classCache = new HashMap();
280     {
281         classCache.put("int", Integer.TYPE);
282         classCache.put("byte", Byte.TYPE);
283         classCache.put("short", Short.TYPE);
284         classCache.put("char", Character.TYPE);
285         classCache.put("boolean", Boolean.TYPE);
286         classCache.put("long", Long.TYPE);
287         classCache.put("double", Double.TYPE);
288         classCache.put("float", Float.TYPE);
289     }
290     protected CompileUnit getCompileUnit() {
291         CompileUnit answer = classNode.getCompileUnit();
292         if (answer == null) {
293             answer = context.getCompileUnit();
294         }
295         return answer;
296     }
297 
298     protected void visitParameters(ASTNode node, Parameter[] parameters) {
299         for (int i = 0, size = parameters.length; i < size; i++ ) {
300             visitParameter(node, parameters[i]);
301         }
302     }
303 
304     protected void visitParameter(ASTNode node, Parameter parameter) {
305         if (! parameter.isDynamicType()) {
306             parameter.setType(checkValidType(parameter.getType(), node, "Must be a valid parameter class"));
307         }
308     }
309 
310 }