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.GroovyObject;
50 import groovy.lang.MetaClass;
51
52 import java.lang.reflect.Modifier;
53 import java.util.ArrayList;
54 import java.util.Iterator;
55 import java.util.List;
56
57 import org.codehaus.groovy.ast.ClassHelper;
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.48 $
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
119
120 ConstructorNode dummy = new ConstructorNode(0,null);
121 addFieldInitialization(node, dummy);
122 node.visitContents(this);
123 return;
124 }
125
126 addDefaultParameterMethods(node);
127
128 if (!node.isDerivedFromGroovyObject()) {
129 node.addInterface(ClassHelper.make(GroovyObject.class));
130
131
132 StaticMethodCallExpression initMetaClassCall =
133 new StaticMethodCallExpression(
134 ClassHelper.make(ScriptBytecodeAdapter.class),
135 "getMetaClass",
136 VariableExpression.THIS_EXPRESSION);
137
138 PropertyNode metaClassProperty =
139 node.addProperty("metaClass", ACC_PUBLIC, ClassHelper.make(MetaClass.class), initMetaClassCall, null, null);
140 metaClassProperty.setSynthetic(true);
141 FieldNode metaClassField = metaClassProperty.getField();
142 metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
143
144 FieldExpression metaClassVar = new FieldExpression(metaClassField);
145 IfStatement initMetaClassField =
146 new IfStatement(
147 new BooleanExpression(
148 new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
149 new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
150 EmptyStatement.INSTANCE);
151
152 node.addSyntheticMethod(
153 "getMetaClass",
154 ACC_PUBLIC,
155 ClassHelper.make(MetaClass.class),
156 Parameter.EMPTY_ARRAY,
157 new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)})
158 );
159
160
161
162
163 ClassNode superClass = node.getSuperClass();
164 boolean addDelegateObject =
165 (node instanceof InnerClassNode && superClass.equals(Closure.class.getName()))
166 || superClass.equals(ClassHelper.GSTRING_TYPE);
167
168
169 if (!addDelegateObject) {
170 node.addSyntheticMethod(
171 "invokeMethod",
172 ACC_PUBLIC,
173 ClassHelper.OBJECT_TYPE,
174 new Parameter[] {
175 new Parameter(ClassHelper.STRING_TYPE, "method"),
176 new Parameter(ClassHelper.OBJECT_TYPE, "arguments")},
177 new BlockStatement(
178 new Statement[] {
179 initMetaClassField,
180 new ReturnStatement(
181 new MethodCallExpression(
182 metaClassVar,
183 "invokeMethod",
184 new ArgumentListExpression(
185 new Expression[] {
186 VariableExpression.THIS_EXPRESSION,
187 new VariableExpression("method"),
188 new VariableExpression("arguments")})))
189 }));
190
191 if (!node.isScript()) {
192 node.addSyntheticMethod(
193 "getProperty",
194 ACC_PUBLIC,
195 ClassHelper.OBJECT_TYPE,
196 new Parameter[] { new Parameter(ClassHelper.STRING_TYPE, "property")},
197 new BlockStatement(
198 new Statement[] {
199 initMetaClassField,
200 new ReturnStatement(
201 new MethodCallExpression(
202 metaClassVar,
203 "getProperty",
204 new ArgumentListExpression(
205 new Expression[] {
206 VariableExpression.THIS_EXPRESSION,
207 new VariableExpression("property")})))
208 }));
209
210 node.addSyntheticMethod(
211 "setProperty",
212 ACC_PUBLIC,
213 ClassHelper.VOID_TYPE,
214 new Parameter[] {
215 new Parameter(ClassHelper.STRING_TYPE, "property"),
216 new Parameter(ClassHelper.OBJECT_TYPE, "value")},
217 new BlockStatement(
218 new Statement[] {
219 initMetaClassField,
220 new ExpressionStatement(
221 new MethodCallExpression(
222 metaClassVar,
223 "setProperty",
224 new ArgumentListExpression(
225 new Expression[] {
226 VariableExpression.THIS_EXPRESSION,
227 new VariableExpression("property"),
228 new VariableExpression("value")})))
229 }));
230 }
231 }
232 }
233
234 if (node.getDeclaredConstructors().isEmpty()) {
235 ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
236 constructor.setSynthetic(true);
237 node.addConstructor(constructor);
238 }
239
240 if (!(node instanceof InnerClassNode)) {
241 FieldNode timeTagField = new FieldNode(
242 Verifier.__TIMESTAMP,
243 Modifier.PUBLIC | Modifier.STATIC,
244 ClassHelper.Long_TYPE,
245
246 node,
247 new ConstantExpression(new Long(System.currentTimeMillis())));
248
249 timeTagField.setSynthetic(true);
250 node.addField(timeTagField);
251 }
252
253 addFieldInitialization(node);
254
255 node.visitContents(this);
256 }
257 public void visitConstructor(ConstructorNode node) {
258 CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
259 boolean firstMethodCall = true;
260 String type=null;
261 public void visitMethodCallExpression(MethodCallExpression call) {
262 if (!firstMethodCall) return;
263 firstMethodCall = false;
264 String name = call.getMethod();
265 if (!name.equals("super") && !name.equals("this")) return;
266 type=name;
267 call.getArguments().visit(this);
268 type=null;
269 }
270 public void visitVariableExpression(VariableExpression expression) {
271 if (type==null) return;
272 String name = expression.getName();
273 if (!name.equals("this") && !name.equals("super")) return;
274 throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
275 }
276 };
277 Statement s = node.getCode();
278
279 if (s == null) return;
280 s.visit(checkSuper);
281 }
282
283 public void visitMethod(MethodNode node) {
284 this.methodNode = node;
285 Statement statement = node.getCode();
286 if (!node.isVoidMethod()) {
287 if (statement instanceof ExpressionStatement) {
288 ExpressionStatement expStmt = (ExpressionStatement) statement;
289 node.setCode(new ReturnStatement(expStmt.getExpression()));
290 }
291 else if (statement instanceof BlockStatement) {
292 BlockStatement block = (BlockStatement) statement;
293
294
295 List list = new ArrayList(block.getStatements());
296 if (!list.isEmpty()) {
297 int idx = list.size() - 1;
298 Statement last = (Statement) list.get(idx);
299 if (last instanceof ExpressionStatement) {
300 ExpressionStatement expStmt = (ExpressionStatement) last;
301 list.set(idx, new ReturnStatement(expStmt.getExpression()));
302 }
303 else if (!(last instanceof ReturnStatement)) {
304 list.add(new ReturnStatement(ConstantExpression.NULL));
305 }
306 }
307 else {
308 list.add(new ReturnStatement(ConstantExpression.NULL));
309 }
310
311 node.setCode(new BlockStatement(filterStatements(list)));
312 }
313 }
314 else if (!node.isAbstract()) {
315 BlockStatement newBlock = new BlockStatement();
316 if (statement instanceof BlockStatement) {
317 newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
318 }
319 else {
320 newBlock.addStatement(filterStatement(statement));
321 }
322 newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
323 node.setCode(newBlock);
324 }
325 if (node.getName().equals("main") && node.isStatic()) {
326 Parameter[] params = node.getParameters();
327 if (params.length == 1) {
328 Parameter param = params[0];
329 if (param.getType() == null || param.getType()==ClassHelper.OBJECT_TYPE) {
330 param.setType(ClassHelper.STRING_TYPE.makeArray());
331 }
332 }
333 }
334 statement = node.getCode();
335 if (statement!=null) statement.visit(new VerifierCodeVisitor(this));
336 }
337
338 public void visitField(FieldNode node) {
339 }
340
341 public void visitProperty(PropertyNode node) {
342 String name = node.getName();
343 FieldNode field = node.getField();
344
345 String getterName = "get" + 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.getSetterBlock();
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 (ClassHelper.boolean_TYPE==node.getType() || ClassHelper.Boolean_TYPE==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(), ClassHelper.VOID_TYPE, 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.hasInitialExpression()) {
405 paramValues.add(new Integer(i));
406 paramValues.add(parameter.getInitialExpression());
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].hasInitialExpression()) {
418 arguments.addExpression(parameters[i].getInitialExpression());
419 k++;
420 }
421 else if (parameters[i] != null && parameters[i].hasInitialExpression()) {
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].hasInitialExpression()) {
462 newParams[index++] = parameters[i];
463 arguments.addExpression(new VariableExpression(parameters[i].getName()));
464 }
465 else {
466 arguments.addExpression(parameters[i].getInitialExpression());
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].getInitialExpression();
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.getInitialExpression();
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
647 }