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.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
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 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;
111 MethodNode enclosingMethod = innerClass.getEnclosingMethod();
112 if (enclosingMethod != null) {
113
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
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,
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 }