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
35
36
37
38
39
40
41
42
43
44
45
46 package org.codehaus.groovy.classgen;
47
48 import groovy.lang.Closure;
49 import groovy.lang.GString;
50 import groovy.lang.GroovyObject;
51 import groovy.lang.MetaClass;
52
53 import java.lang.reflect.Modifier;
54 import java.util.ArrayList;
55 import java.util.Iterator;
56 import java.util.List;
57
58 import org.codehaus.groovy.ast.ClassNode;
59 import org.codehaus.groovy.ast.ConstructorNode;
60 import org.codehaus.groovy.ast.FieldNode;
61 import org.codehaus.groovy.ast.GroovyClassVisitor;
62 import org.codehaus.groovy.ast.InnerClassNode;
63 import org.codehaus.groovy.ast.MethodNode;
64 import org.codehaus.groovy.ast.Parameter;
65 import org.codehaus.groovy.ast.PropertyNode;
66 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
67 import org.codehaus.groovy.ast.expr.BinaryExpression;
68 import org.codehaus.groovy.ast.expr.BooleanExpression;
69 import org.codehaus.groovy.ast.expr.ClosureExpression;
70 import org.codehaus.groovy.ast.expr.ConstantExpression;
71 import org.codehaus.groovy.ast.expr.Expression;
72 import org.codehaus.groovy.ast.expr.FieldExpression;
73 import org.codehaus.groovy.ast.expr.MethodCallExpression;
74 import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
75 import org.codehaus.groovy.ast.expr.VariableExpression;
76 import org.codehaus.groovy.ast.stmt.BlockStatement;
77 import org.codehaus.groovy.ast.stmt.EmptyStatement;
78 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
79 import org.codehaus.groovy.ast.stmt.IfStatement;
80 import org.codehaus.groovy.ast.stmt.ReturnStatement;
81 import org.codehaus.groovy.ast.stmt.Statement;
82 import org.codehaus.groovy.runtime.InvokerHelper;
83 import org.codehaus.groovy.syntax.Types;
84 import org.codehaus.groovy.syntax.Token;
85 import org.codehaus.groovy.syntax.parser.RuntimeParserException;
86 import org.objectweb.asm.Constants;
87
88 /***
89 * Verifies the AST node and adds any defaulted AST code before
90 * bytecode generation occurs.
91 *
92 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
93 * @version $Revision: 1.32 $
94 */
95 public class Verifier implements GroovyClassVisitor, Constants {
96
97 public static final String __TIMESTAMP = "__timeStamp";
98 private ClassNode classNode;
99 private MethodNode methodNode;
100
101 public ClassNode getClassNode() {
102 return classNode;
103 }
104
105 public MethodNode getMethodNode() {
106 return methodNode;
107 }
108
109 /***
110 * add code to implement GroovyObject
111 * @param node
112 */
113 public void visitClass(ClassNode node) {
114 this.classNode = node;
115
116 addDefaultParameterMethods(node);
117
118 if (!node.isDerivedFromGroovyObject()) {
119 node.addInterface(GroovyObject.class.getName());
120
121
122 StaticMethodCallExpression initMetaClassCall =
123 new StaticMethodCallExpression(
124 InvokerHelper.class.getName(),
125 "getMetaClass",
126 VariableExpression.THIS_EXPRESSION);
127
128 PropertyNode metaClassProperty =
129 node.addProperty("metaClass", ACC_PUBLIC, MetaClass.class.getName(), initMetaClassCall, null, null);
130 metaClassProperty.setSynthetic(true);
131 FieldNode metaClassField = metaClassProperty.getField();
132 metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
133
134 FieldExpression metaClassVar = new FieldExpression(metaClassField);
135 IfStatement initMetaClassField =
136 new IfStatement(
137 new BooleanExpression(
138 new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
139 new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
140 EmptyStatement.INSTANCE);
141
142 node.addSyntheticMethod(
143 "getMetaClass",
144 ACC_PUBLIC,
145 MetaClass.class.getName(),
146 Parameter.EMPTY_ARRAY,
147 new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)}));
148
149
150
151
152 String superClass = node.getSuperClass();
153 boolean addDelegateObject =
154 (node instanceof InnerClassNode && superClass.equals(Closure.class.getName()))
155 || superClass.equals(GString.class.getName());
156
157
158 if (!addDelegateObject) {
159 node.addSyntheticMethod(
160 "invokeMethod",
161 ACC_PUBLIC,
162 Object.class.getName(),
163 new Parameter[] {
164 new Parameter(String.class.getName(), "method"),
165 new Parameter(Object.class.getName(), "arguments")},
166 new BlockStatement(
167 new Statement[] {
168 initMetaClassField,
169 new ReturnStatement(
170 new MethodCallExpression(
171 metaClassVar,
172 "invokeMethod",
173 new ArgumentListExpression(
174 new Expression[] {
175 VariableExpression.THIS_EXPRESSION,
176 new VariableExpression("method"),
177 new VariableExpression("arguments")})))
178 }));
179
180 if (!node.isScript()) {
181 node.addSyntheticMethod(
182 "getProperty",
183 ACC_PUBLIC,
184 Object.class.getName(),
185 new Parameter[] { new Parameter(String.class.getName(), "property")},
186 new BlockStatement(
187 new Statement[] {
188 initMetaClassField,
189 new ReturnStatement(
190 new MethodCallExpression(
191 metaClassVar,
192 "getProperty",
193 new ArgumentListExpression(
194 new Expression[] {
195 VariableExpression.THIS_EXPRESSION,
196 new VariableExpression("property")})))
197 }));
198
199 node.addSyntheticMethod(
200 "setProperty",
201 ACC_PUBLIC,
202 "void",
203 new Parameter[] {
204 new Parameter(String.class.getName(), "property"),
205 new Parameter(Object.class.getName(), "value")},
206 new BlockStatement(
207 new Statement[] {
208 initMetaClassField,
209 new ExpressionStatement(
210 new MethodCallExpression(
211 metaClassVar,
212 "setProperty",
213 new ArgumentListExpression(
214 new Expression[] {
215 VariableExpression.THIS_EXPRESSION,
216 new VariableExpression("property"),
217 new VariableExpression("value")})))
218 }));
219 }
220 }
221 }
222
223 if (node.getDeclaredConstructors().isEmpty()) {
224 ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
225 constructor.setSynthetic(true);
226 node.addConstructor(constructor);
227 }
228
229 if (!(node instanceof InnerClassNode)) {
230 FieldNode timeTagField = new FieldNode(
231 Verifier.__TIMESTAMP,
232 Modifier.PUBLIC | Modifier.STATIC,
233 "java.lang.Long",
234
235 node.getName(),
236 new ConstantExpression(new Long(System.currentTimeMillis())));
237
238 timeTagField.setSynthetic(true);
239 node.addField(timeTagField);
240 }
241
242 addFieldInitialization(node);
243
244 node.visitContents(this);
245 }
246
247 public void visitConstructor(ConstructorNode node) {
248 }
249
250 public void visitMethod(MethodNode node) {
251 this.methodNode = node;
252
253 Statement statement = node.getCode();
254 if (!node.isVoidMethod()) {
255 if (statement instanceof ExpressionStatement) {
256 ExpressionStatement expStmt = (ExpressionStatement) statement;
257 node.setCode(new ReturnStatement(expStmt.getExpression()));
258 }
259 else if (statement instanceof BlockStatement) {
260 BlockStatement block = (BlockStatement) statement;
261
262
263 List list = new ArrayList(block.getStatements());
264 if (!list.isEmpty()) {
265 int idx = list.size() - 1;
266 Statement last = (Statement) list.get(idx);
267 if (last instanceof ExpressionStatement) {
268 ExpressionStatement expStmt = (ExpressionStatement) last;
269 list.set(idx, new ReturnStatement(expStmt.getExpression()));
270 }
271 else if (!(last instanceof ReturnStatement)) {
272 list.add(new ReturnStatement(ConstantExpression.NULL));
273 }
274 }
275 else {
276 list.add(new ReturnStatement(ConstantExpression.NULL));
277 }
278
279 node.setCode(new BlockStatement(filterStatements(list)));
280 }
281 }
282 else {
283 BlockStatement newBlock = new BlockStatement();
284 if (statement instanceof BlockStatement) {
285 newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
286 }
287 else {
288 newBlock.addStatement(filterStatement(statement));
289 }
290 newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
291 node.setCode(newBlock);
292 }
293 if (node.getName().equals("main") && node.isStatic()) {
294 Parameter[] params = node.getParameters();
295 if (params.length == 1) {
296 Parameter param = params[0];
297 if (param.getType() == null || param.getType().equals("java.lang.Object")) {
298 param.setType("java.lang.String[]");
299 }
300 }
301 }
302 node.getCode().visit(new VerifierCodeVisitor(this));
303 }
304
305 public void visitField(FieldNode node) {
306 }
307
308 public void visitProperty(PropertyNode node) {
309 String name = node.getName();
310 FieldNode field = node.getField();
311
312
313 String getterPrefix = "get";
314 if ("boolean".equals(node.getType())) {
315 getterPrefix = "is";
316 }
317 String getterName = getterPrefix + capitalize(name);
318 String setterName = "set" + capitalize(name);
319
320 Statement getterBlock = node.getGetterBlock();
321 if (getterBlock == null) {
322 if (!node.isPrivate() && classNode.getGetterMethod(getterName) == null) {
323 getterBlock = createGetterBlock(node, field);
324 }
325 }
326 Statement setterBlock = node.getGetterBlock();
327 if (setterBlock == null) {
328 if (!node.isPrivate() && classNode.getSetterMethod(setterName) == null) {
329 setterBlock = createSetterBlock(node, field);
330 }
331 }
332
333 if (getterBlock != null) {
334 MethodNode getter =
335 new MethodNode(getterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
336 getter.setSynthetic(true);
337 classNode.addMethod(getter);
338 visitMethod(getter);
339
340 if ("java.lang.Boolean".equals(node.getType())) {
341 String secondGetterName = "is" + capitalize(name);
342 MethodNode secondGetter =
343 new MethodNode(secondGetterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
344 secondGetter.setSynthetic(true);
345 classNode.addMethod(secondGetter);
346 visitMethod(secondGetter);
347 }
348 }
349 if (setterBlock != null) {
350 Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
351 MethodNode setter =
352 new MethodNode(setterName, node.getModifiers(), "void", setterParameterTypes, setterBlock);
353 setter.setSynthetic(true);
354 classNode.addMethod(setter);
355 visitMethod(setter);
356 }
357 }
358
359
360
361
362 /***
363 * Creates a new helper method for each combination of default parameter expressions
364 */
365 protected void addDefaultParameterMethods(ClassNode node) {
366 List methods = new ArrayList(node.getMethods());
367 for (Iterator iter = methods.iterator(); iter.hasNext();) {
368 MethodNode method = (MethodNode) iter.next();
369 Parameter[] parameters = method.getParameters();
370 int size = parameters.length;
371 for (int i = 0; i < size; i++) {
372 Parameter parameter = parameters[i];
373 Expression exp = parameter.getDefaultValue();
374 if (exp != null) {
375 addDefaultParameterMethod(node, method, parameters, i);
376 }
377 }
378 }
379 }
380
381 /***
382 * Adds a new method which defaults the values for all the parameters starting
383 * from and including the given index
384 *
385 * @param node the class to add the method
386 * @param method the given method to add a helper of
387 * @param parameters the parameters of the method to add a helper for
388 * @param index the index of the first default value expression parameter to use
389 */
390 protected void addDefaultParameterMethod(ClassNode node, MethodNode method, Parameter[] parameters, int index) {
391
392 Parameter[] newParams = new Parameter[index];
393 System.arraycopy(parameters, 0, newParams, 0, index);
394
395 ArgumentListExpression arguments = new ArgumentListExpression();
396 int size = parameters.length;
397 for (int i = 0; i < size; i++) {
398 if (i < index) {
399 arguments.addExpression(new VariableExpression(parameters[i].getName()));
400 }
401 else {
402 Expression defaultValue = parameters[i].getDefaultValue();
403 if (defaultValue == null) {
404 throw new RuntimeParserException(
405 "The " + parameters[i].getName() + " parameter must have a default value",
406 method);
407 }
408 else {
409 arguments.addExpression(defaultValue);
410 }
411 }
412 }
413
414 MethodCallExpression expression =
415 new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
416 Statement code = null;
417 if (method.isVoidMethod()) {
418 code = new ExpressionStatement(expression);
419 }
420 else {
421 code = new ReturnStatement(expression);
422 }
423
424 node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
425 }
426
427 protected void addClosureCode(InnerClassNode node) {
428
429 }
430
431 protected void addFieldInitialization(ClassNode node) {
432 for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
433 addFieldInitialization(node, (ConstructorNode) iter.next());
434 }
435 }
436
437 protected void addFieldInitialization(ClassNode node, ConstructorNode constructorNode) {
438 List statements = new ArrayList();
439 List staticStatements = new ArrayList();
440 for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
441 addFieldInitialization(statements, staticStatements, constructorNode, (FieldNode) iter.next());
442 }
443 if (!statements.isEmpty()) {
444 Statement code = constructorNode.getCode();
445 List otherStatements = new ArrayList();
446 if (code instanceof BlockStatement) {
447 BlockStatement block = (BlockStatement) code;
448 otherStatements.addAll(block.getStatements());
449 }
450 else if (code != null) {
451 otherStatements.add(code);
452 }
453 if (!otherStatements.isEmpty()) {
454 Statement first = (Statement) otherStatements.get(0);
455 if (isSuperMethodCall(first)) {
456 otherStatements.remove(0);
457 statements.add(0, first);
458 }
459 statements.addAll(otherStatements);
460 }
461 constructorNode.setCode(new BlockStatement(statements));
462 }
463
464 if (!staticStatements.isEmpty()) {
465 node.addStaticInitializerStatements(staticStatements);
466 }
467 }
468
469 protected void addFieldInitialization(
470 List list,
471 List staticList,
472 ConstructorNode constructorNode,
473 FieldNode fieldNode) {
474 Expression expression = fieldNode.getInitialValueExpression();
475 if (expression != null) {
476 ExpressionStatement statement =
477 new ExpressionStatement(
478 new BinaryExpression(
479 new FieldExpression(fieldNode),
480 Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
481 expression));
482 if (fieldNode.isStatic()) {
483 staticList.add(statement);
484 }
485 else {
486 list.add(statement);
487 }
488 }
489 }
490
491 protected boolean isSuperMethodCall(Statement first) {
492 if (first instanceof ExpressionStatement) {
493 ExpressionStatement exprStmt = (ExpressionStatement) first;
494 Expression expr = exprStmt.getExpression();
495 if (expr instanceof MethodCallExpression) {
496 return MethodCallExpression.isSuperMethodCall((MethodCallExpression) expr);
497 }
498 }
499 return false;
500 }
501
502 /***
503 * Capitalizes the start of the given bean property name
504 */
505 public static String capitalize(String name) {
506 return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
507 }
508
509 protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
510 Expression expression = new FieldExpression(field);
511 return new ReturnStatement(expression);
512 }
513
514 protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
515 Expression expression = new FieldExpression(field);
516 return new ExpressionStatement(
517 new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
518 }
519
520 /***
521 * Filters the given statements
522 */
523 protected List filterStatements(List list) {
524 List answer = new ArrayList(list.size());
525 for (Iterator iter = list.iterator(); iter.hasNext();) {
526 answer.add(filterStatement((Statement) iter.next()));
527 }
528 return answer;
529 }
530
531 protected Statement filterStatement(Statement statement) {
532 if (statement instanceof ExpressionStatement) {
533 ExpressionStatement expStmt = (ExpressionStatement) statement;
534 Expression expression = expStmt.getExpression();
535 if (expression instanceof ClosureExpression) {
536 ClosureExpression closureExp = (ClosureExpression) expression;
537 if (!closureExp.isParameterSpecified()) {
538 return closureExp.getCode();
539 }
540 }
541 }
542 return statement;
543 }
544 }