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.CodeVisitorSupport;
60 import org.codehaus.groovy.ast.ConstructorNode;
61 import org.codehaus.groovy.ast.FieldNode;
62 import org.codehaus.groovy.ast.GroovyClassVisitor;
63 import org.codehaus.groovy.ast.InnerClassNode;
64 import org.codehaus.groovy.ast.MethodNode;
65 import org.codehaus.groovy.ast.Parameter;
66 import org.codehaus.groovy.ast.PropertyNode;
67 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
68 import org.codehaus.groovy.ast.expr.BinaryExpression;
69 import org.codehaus.groovy.ast.expr.BooleanExpression;
70 import org.codehaus.groovy.ast.expr.ClosureExpression;
71 import org.codehaus.groovy.ast.expr.ConstantExpression;
72 import org.codehaus.groovy.ast.expr.Expression;
73 import org.codehaus.groovy.ast.expr.FieldExpression;
74 import org.codehaus.groovy.ast.expr.MethodCallExpression;
75 import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
76 import org.codehaus.groovy.ast.expr.VariableExpression;
77 import org.codehaus.groovy.ast.stmt.BlockStatement;
78 import org.codehaus.groovy.ast.stmt.EmptyStatement;
79 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
80 import org.codehaus.groovy.ast.stmt.IfStatement;
81 import org.codehaus.groovy.ast.stmt.ReturnStatement;
82 import org.codehaus.groovy.ast.stmt.Statement;
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.RuntimeParserException;
87 import org.objectweb.asm.Opcodes;
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.41 $
95 */
96 public class Verifier implements GroovyClassVisitor, Opcodes {
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 if ((classNode.getModifiers() & Opcodes.ACC_INTERFACE) >0) {
118 node.visitContents(this);
119 return;
120 }
121
122 addDefaultParameterMethods(node);
123
124 if (!node.isDerivedFromGroovyObject()) {
125 node.addInterface(GroovyObject.class.getName());
126
127
128 StaticMethodCallExpression initMetaClassCall =
129 new StaticMethodCallExpression(
130 ScriptBytecodeAdapter.class.getName(),
131 "getMetaClass",
132 VariableExpression.THIS_EXPRESSION);
133
134 PropertyNode metaClassProperty =
135 node.addProperty("metaClass", ACC_PUBLIC, MetaClass.class.getName(), initMetaClassCall, null, null);
136 metaClassProperty.setSynthetic(true);
137 FieldNode metaClassField = metaClassProperty.getField();
138 metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
139
140 FieldExpression metaClassVar = new FieldExpression(metaClassField);
141 IfStatement initMetaClassField =
142 new IfStatement(
143 new BooleanExpression(
144 new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
145 new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
146 EmptyStatement.INSTANCE);
147
148 node.addSyntheticMethod(
149 "getMetaClass",
150 ACC_PUBLIC,
151 MetaClass.class.getName(),
152 Parameter.EMPTY_ARRAY,
153 new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)}));
154
155
156
157
158 String superClass = node.getSuperClass();
159 boolean addDelegateObject =
160 (node instanceof InnerClassNode && superClass.equals(Closure.class.getName()))
161 || superClass.equals(GString.class.getName());
162
163
164 if (!addDelegateObject) {
165 node.addSyntheticMethod(
166 "invokeMethod",
167 ACC_PUBLIC,
168 Object.class.getName(),
169 new Parameter[] {
170 new Parameter(String.class.getName(), "method"),
171 new Parameter(Object.class.getName(), "arguments")},
172 new BlockStatement(
173 new Statement[] {
174 initMetaClassField,
175 new ReturnStatement(
176 new MethodCallExpression(
177 metaClassVar,
178 "invokeMethod",
179 new ArgumentListExpression(
180 new Expression[] {
181 VariableExpression.THIS_EXPRESSION,
182 new VariableExpression("method"),
183 new VariableExpression("arguments")})))
184 }));
185
186 if (!node.isScript()) {
187 node.addSyntheticMethod(
188 "getProperty",
189 ACC_PUBLIC,
190 Object.class.getName(),
191 new Parameter[] { new Parameter(String.class.getName(), "property")},
192 new BlockStatement(
193 new Statement[] {
194 initMetaClassField,
195 new ReturnStatement(
196 new MethodCallExpression(
197 metaClassVar,
198 "getProperty",
199 new ArgumentListExpression(
200 new Expression[] {
201 VariableExpression.THIS_EXPRESSION,
202 new VariableExpression("property")})))
203 }));
204
205 node.addSyntheticMethod(
206 "setProperty",
207 ACC_PUBLIC,
208 "void",
209 new Parameter[] {
210 new Parameter(String.class.getName(), "property"),
211 new Parameter(Object.class.getName(), "value")},
212 new BlockStatement(
213 new Statement[] {
214 initMetaClassField,
215 new ExpressionStatement(
216 new MethodCallExpression(
217 metaClassVar,
218 "setProperty",
219 new ArgumentListExpression(
220 new Expression[] {
221 VariableExpression.THIS_EXPRESSION,
222 new VariableExpression("property"),
223 new VariableExpression("value")})))
224 }));
225 }
226 }
227 }
228
229 if (node.getDeclaredConstructors().isEmpty()) {
230 ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
231 constructor.setSynthetic(true);
232 node.addConstructor(constructor);
233 }
234
235 if (!(node instanceof InnerClassNode)) {
236 FieldNode timeTagField = new FieldNode(
237 Verifier.__TIMESTAMP,
238 Modifier.PUBLIC | Modifier.STATIC,
239 "java.lang.Long",
240
241 node.getName(),
242 new ConstantExpression(new Long(System.currentTimeMillis())));
243
244 timeTagField.setSynthetic(true);
245 node.addField(timeTagField);
246 }
247
248 addFieldInitialization(node);
249
250 node.visitContents(this);
251 }
252 public void visitConstructor(ConstructorNode node) {
253 CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
254 boolean firstMethodCall = true;
255 String type=null;
256 public void visitMethodCallExpression(MethodCallExpression call) {
257 if (!firstMethodCall) return;
258 firstMethodCall = false;
259 String name = call.getMethod();
260 if (!name.equals("super") && !name.equals("this")) return;
261 type=name;
262 call.getArguments().visit(this);
263 type=null;
264 }
265 public void visitVariableExpression(VariableExpression expression) {
266 if (type==null) return;
267 String name = expression.getVariable();
268 if (!name.equals("this") && !name.equals("super")) return;
269 throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
270 }
271 };
272 Statement s = node.getCode();
273
274 if (s == null) return;
275 s.visit(checkSuper);
276 }
277
278 public void visitMethod(MethodNode node) {
279 this.methodNode = node;
280 Statement statement = node.getCode();
281 if (!node.isVoidMethod()) {
282 if (statement instanceof ExpressionStatement) {
283 ExpressionStatement expStmt = (ExpressionStatement) statement;
284 node.setCode(new ReturnStatement(expStmt.getExpression()));
285 }
286 else if (statement instanceof BlockStatement) {
287 BlockStatement block = (BlockStatement) statement;
288
289
290 List list = new ArrayList(block.getStatements());
291 if (!list.isEmpty()) {
292 int idx = list.size() - 1;
293 Statement last = (Statement) list.get(idx);
294 if (last instanceof ExpressionStatement) {
295 ExpressionStatement expStmt = (ExpressionStatement) last;
296 list.set(idx, new ReturnStatement(expStmt.getExpression()));
297 }
298 else if (!(last instanceof ReturnStatement)) {
299 list.add(new ReturnStatement(ConstantExpression.NULL));
300 }
301 }
302 else {
303 list.add(new ReturnStatement(ConstantExpression.NULL));
304 }
305
306 node.setCode(new BlockStatement(filterStatements(list)));
307 }
308 }
309 else if (!node.isAbstract()) {
310 BlockStatement newBlock = new BlockStatement();
311 if (statement instanceof BlockStatement) {
312 newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
313 }
314 else {
315 newBlock.addStatement(filterStatement(statement));
316 }
317 newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
318 node.setCode(newBlock);
319 }
320 if (node.getName().equals("main") && node.isStatic()) {
321 Parameter[] params = node.getParameters();
322 if (params.length == 1) {
323 Parameter param = params[0];
324 if (param.getType() == null || param.getType().equals("java.lang.Object")) {
325 param.setType("java.lang.String[]");
326 }
327 }
328 }
329 statement = node.getCode();
330 if (statement!=null) statement.visit(new VerifierCodeVisitor(this));
331 }
332
333 public void visitField(FieldNode node) {
334 }
335
336 public void visitProperty(PropertyNode node) {
337 String name = node.getName();
338 FieldNode field = node.getField();
339
340
341 String getterPrefix = "get";
342 if ("boolean".equals(node.getType())) {
343 getterPrefix = "is";
344 }
345 String getterName = getterPrefix + capitalize(name);
346 String setterName = "set" + capitalize(name);
347
348 Statement getterBlock = node.getGetterBlock();
349 if (getterBlock == null) {
350 if (!node.isPrivate() && classNode.getGetterMethod(getterName) == null) {
351 getterBlock = createGetterBlock(node, field);
352 }
353 }
354 Statement setterBlock = node.getGetterBlock();
355 if (setterBlock == null) {
356 if (!node.isPrivate() && classNode.getSetterMethod(setterName) == null) {
357 setterBlock = createSetterBlock(node, field);
358 }
359 }
360
361 if (getterBlock != null) {
362 MethodNode getter =
363 new MethodNode(getterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
364 getter.setSynthetic(true);
365 classNode.addMethod(getter);
366 visitMethod(getter);
367
368 if ("java.lang.Boolean".equals(node.getType())) {
369 String secondGetterName = "is" + capitalize(name);
370 MethodNode secondGetter =
371 new MethodNode(secondGetterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
372 secondGetter.setSynthetic(true);
373 classNode.addMethod(secondGetter);
374 visitMethod(secondGetter);
375 }
376 }
377 if (setterBlock != null) {
378 Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
379 MethodNode setter =
380 new MethodNode(setterName, node.getModifiers(), "void", setterParameterTypes, setterBlock);
381 setter.setSynthetic(true);
382 classNode.addMethod(setter);
383 visitMethod(setter);
384 }
385 }
386
387
388
389
390 /***
391 * Creates a new helper method for each combination of default parameter expressions
392 */
393 protected void addDefaultParameterMethods(ClassNode node) {
394 List methods = new ArrayList(node.getMethods());
395 for (Iterator iter = methods.iterator(); iter.hasNext();) {
396 MethodNode method = (MethodNode) iter.next();
397 if (method.hasDefaultValue()) {
398 Parameter[] parameters = method.getParameters();
399 int counter = 0;
400 ArrayList paramValues = new ArrayList();
401 int size = parameters.length;
402 for (int i = size - 1; i >= 0; i--) {
403 Parameter parameter = parameters[i];
404 if (parameter != null && parameter.hasDefaultValue()) {
405 paramValues.add(new Integer(i));
406 paramValues.add(parameter.getDefaultValue());
407 counter++;
408 }
409 }
410
411 for (int j = 1; j <= counter; j++) {
412 Parameter[] newParams = new Parameter[parameters.length - j];
413 ArgumentListExpression arguments = new ArgumentListExpression();
414 int index = 0;
415 int k = 1;
416 for (int i = 0; i < parameters.length; i++) {
417 if (k > counter - j && parameters[i] != null && parameters[i].hasDefaultValue()) {
418 arguments.addExpression(parameters[i].getDefaultValue());
419 k++;
420 }
421 else if (parameters[i] != null && parameters[i].hasDefaultValue()) {
422 newParams[index++] = parameters[i];
423 arguments.addExpression(new VariableExpression(parameters[i].getName()));
424 k++;
425 }
426 else {
427 newParams[index++] = parameters[i];
428 arguments.addExpression(new VariableExpression(parameters[i].getName()));
429 }
430 }
431
432 MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
433 Statement code = null;
434 if (method.isVoidMethod()) {
435 code = new ExpressionStatement(expression);
436 }
437 else {
438 code = new ReturnStatement(expression);
439 }
440 node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
441 }
442 }
443 }
444 }
445
446 /***
447 * Adds a new method which defaults the values for all the parameters starting
448 * from and including the given index
449 *
450 * @param node the class to add the method
451 * @param method the given method to add a helper of
452 * @param parameters the parameters of the method to add a helper for
453 * @param index the index of the first default value expression parameter to use
454 */
455 protected void addDefaultParameterMethod(ClassNode node, MethodNode method, Parameter[] parameters, int depth, ArrayList values) {
456
457 Parameter[] newParams = new Parameter[parameters.length - depth];
458 int index = 0;
459 ArgumentListExpression arguments = new ArgumentListExpression();
460 for (int i = 0; i < parameters.length; i++) {
461 if (parameters[i] != null && parameters[i].hasDefaultValue()) {
462 newParams[index++] = parameters[i];
463 arguments.addExpression(new VariableExpression(parameters[i].getName()));
464 }
465 else {
466 arguments.addExpression(parameters[i].getDefaultValue());
467 }
468 }
469
470 MethodCallExpression expression =
471 new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
472 Statement code = null;
473 if (method.isVoidMethod()) {
474 code = new ExpressionStatement(expression);
475 }
476 else {
477 code = new ReturnStatement(expression);
478 }
479
480 node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
481 }
482
483 /***
484 * Adds a new method which defaults the values for all the parameters starting
485 * from and including the given index
486 *
487 * @param node the class to add the method
488 * @param method the given method to add a helper of
489 * @param parameters the parameters of the method to add a helper for
490 * @param index the index of the first default value expression parameter to use
491 */
492 protected void addDefaultParameterMethod(ClassNode node, MethodNode method, Parameter[] parameters, int index) {
493
494 Parameter[] newParams = new Parameter[index];
495 System.arraycopy(parameters, 0, newParams, 0, index);
496
497 ArgumentListExpression arguments = new ArgumentListExpression();
498 int size = parameters.length;
499 for (int i = 0; i < size; i++) {
500 if (i < index) {
501 arguments.addExpression(new VariableExpression(parameters[i].getName()));
502 }
503 else {
504 Expression defaultValue = parameters[i].getDefaultValue();
505 if (defaultValue == null) {
506 throw new RuntimeParserException(
507 "The " + parameters[i].getName() + " parameter must have a default value",
508 method);
509 }
510 else {
511 arguments.addExpression(defaultValue);
512 }
513 }
514 }
515
516 MethodCallExpression expression =
517 new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
518 Statement code = null;
519 if (method.isVoidMethod()) {
520 code = new ExpressionStatement(expression);
521 }
522 else {
523 code = new ReturnStatement(expression);
524 }
525
526 node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
527 }
528
529 protected void addClosureCode(InnerClassNode node) {
530
531 }
532
533 protected void addFieldInitialization(ClassNode node) {
534 for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
535 addFieldInitialization(node, (ConstructorNode) iter.next());
536 }
537 }
538
539 protected void addFieldInitialization(ClassNode node, ConstructorNode constructorNode) {
540 List statements = new ArrayList();
541 List staticStatements = new ArrayList();
542 for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
543 addFieldInitialization(statements, staticStatements, constructorNode, (FieldNode) iter.next());
544 }
545 if (!statements.isEmpty()) {
546 Statement code = constructorNode.getCode();
547 List otherStatements = new ArrayList();
548 if (code instanceof BlockStatement) {
549 BlockStatement block = (BlockStatement) code;
550 otherStatements.addAll(block.getStatements());
551 }
552 else if (code != null) {
553 otherStatements.add(code);
554 }
555 if (!otherStatements.isEmpty()) {
556 Statement first = (Statement) otherStatements.get(0);
557 if (isSuperMethodCall(first)) {
558 otherStatements.remove(0);
559 statements.add(0, first);
560 }
561 statements.addAll(otherStatements);
562 }
563 constructorNode.setCode(new BlockStatement(statements));
564 }
565
566 if (!staticStatements.isEmpty()) {
567 node.addStaticInitializerStatements(staticStatements);
568 }
569 }
570
571 protected void addFieldInitialization(
572 List list,
573 List staticList,
574 ConstructorNode constructorNode,
575 FieldNode fieldNode) {
576 Expression expression = fieldNode.getInitialValueExpression();
577 if (expression != null) {
578 ExpressionStatement statement =
579 new ExpressionStatement(
580 new BinaryExpression(
581 new FieldExpression(fieldNode),
582 Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
583 expression));
584 if (fieldNode.isStatic()) {
585 staticList.add(statement);
586 }
587 else {
588 list.add(statement);
589 }
590 }
591 }
592
593 protected boolean isSuperMethodCall(Statement first) {
594 if (first instanceof ExpressionStatement) {
595 ExpressionStatement exprStmt = (ExpressionStatement) first;
596 Expression expr = exprStmt.getExpression();
597 if (expr instanceof MethodCallExpression) {
598 return MethodCallExpression.isSuperMethodCall((MethodCallExpression) expr);
599 }
600 }
601 return false;
602 }
603
604 /***
605 * Capitalizes the start of the given bean property name
606 */
607 public static String capitalize(String name) {
608 return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
609 }
610
611 protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
612 Expression expression = new FieldExpression(field);
613 return new ReturnStatement(expression);
614 }
615
616 protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
617 Expression expression = new FieldExpression(field);
618 return new ExpressionStatement(
619 new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
620 }
621
622 /***
623 * Filters the given statements
624 */
625 protected List filterStatements(List list) {
626 List answer = new ArrayList(list.size());
627 for (Iterator iter = list.iterator(); iter.hasNext();) {
628 answer.add(filterStatement((Statement) iter.next()));
629 }
630 return answer;
631 }
632
633 protected Statement filterStatement(Statement statement) {
634 if (statement instanceof ExpressionStatement) {
635 ExpressionStatement expStmt = (ExpressionStatement) statement;
636 Expression expression = expStmt.getExpression();
637 if (expression instanceof ClosureExpression) {
638 ClosureExpression closureExp = (ClosureExpression) expression;
639 if (!closureExp.isParameterSpecified()) {
640 return closureExp.getCode();
641 }
642 }
643 }
644 return statement;
645 }
646 }