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