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 }