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