View Javadoc

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