001 /*
002 * $Id: DummyClassGenerator.java,v 1.3 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 that the
008 * following conditions are met: 1. Redistributions of source code must retain
009 * copyright statements and notices. Redistributions must also contain a copy
010 * of this document. 2. Redistributions in binary form must reproduce the above
011 * copyright notice, this list of conditions and the following disclaimer in
012 * the documentation and/or other materials provided with the distribution. 3.
013 * The name "groovy" must not be used to endorse or promote products derived
014 * from this Software without prior written permission of The Codehaus. For
015 * written permission, please contact info@codehaus.org. 4. Products derived
016 * from this Software may not be called "groovy" nor may "groovy" appear in
017 * their names without prior written permission of The Codehaus. "groovy" is a
018 * registered trademark of The Codehaus. 5. Due credit should be given to The
019 * Codehaus - http://groovy.codehaus.org/
020 *
021 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
022 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
023 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
024 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
025 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
026 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
027 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
028 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
029 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
030 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
031 * DAMAGE.
032 *
033 */
034 package org.codehaus.groovy.classgen;
035
036 import groovy.lang.GroovyRuntimeException;
037 import groovy.lang.MissingClassException;
038 import org.codehaus.groovy.ast.*;
039 import org.objectweb.asm.ClassVisitor;
040 import org.objectweb.asm.MethodVisitor;
041
042 import java.util.*;
043
044 /**
045 * To generate a class that has all the fields and methods, except that fields are not initilized
046 * and methods are empty. It's intended for being used as a place holder during code generation
047 * of reference to the "this" class itself.
048 *
049 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
050 * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
051 *
052 * @version $Revision: 1.3 $
053 */
054 public class DummyClassGenerator extends ClassGenerator {
055
056 private ClassVisitor cw;
057 private MethodVisitor cv;
058 private GeneratorContext context;
059
060 private String sourceFile;
061
062 // current class details
063 private ClassNode classNode;
064 private String internalClassName;
065 private String internalBaseClassName;
066
067
068 public DummyClassGenerator(
069 GeneratorContext context,
070 ClassVisitor classVisitor,
071 ClassLoader classLoader,
072 String sourceFile) {
073 super(classLoader);
074 this.context = context;
075 this.cw = classVisitor;
076 this.sourceFile = sourceFile;
077 }
078
079 // GroovyClassVisitor interface
080 //-------------------------------------------------------------------------
081 public void visitClass(ClassNode classNode) {
082 try {
083 this.classNode = classNode;
084 this.internalClassName = BytecodeHelper.getClassInternalName(classNode.getName());
085
086 //System.out.println("Generating class: " + classNode.getName());
087
088 // lets check that the classes are all valid
089 classNode.setSuperClass(checkValidType(classNode.getSuperClass(), classNode, "Must be a valid base class"));
090 String[] interfaces = classNode.getInterfaces();
091 for (int i = 0; i < interfaces.length; i++ ) {
092 interfaces[i] = checkValidType(interfaces[i], classNode, "Must be a valid interface name");
093 }
094
095 this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
096
097 cw.visit(
098 asmJDKVersion,
099 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 }