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