1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
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
80
81 public void visitClass(ClassNode classNode) {
82 try {
83 this.classNode = classNode;
84 this.internalClassName = BytecodeHelper.getClassInternalName(classNode.getName());
85
86
87
88
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;
112 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
113 if (enclosingMethod != null) {
114
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
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,
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 }