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.GroovyObject;
49 import groovy.lang.MetaClass;
50
51 import java.lang.reflect.Modifier;
52 import java.util.ArrayList;
53 import java.util.Iterator;
54 import java.util.List;
55
56 import org.codehaus.groovy.ast.ClassHelper;
57 import org.codehaus.groovy.ast.ClassNode;
58 import org.codehaus.groovy.ast.CodeVisitorSupport;
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.VariableScope;
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.ConstructorCallExpression;
73 import org.codehaus.groovy.ast.expr.Expression;
74 import org.codehaus.groovy.ast.expr.FieldExpression;
75 import org.codehaus.groovy.ast.expr.MethodCallExpression;
76 import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
77 import org.codehaus.groovy.ast.expr.VariableExpression;
78 import org.codehaus.groovy.ast.stmt.BlockStatement;
79 import org.codehaus.groovy.ast.stmt.EmptyStatement;
80 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
81 import org.codehaus.groovy.ast.stmt.IfStatement;
82 import org.codehaus.groovy.ast.stmt.ReturnStatement;
83 import org.codehaus.groovy.ast.stmt.Statement;
84 import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
85 import org.codehaus.groovy.syntax.Types;
86 import org.codehaus.groovy.syntax.Token;
87 import org.codehaus.groovy.syntax.RuntimeParserException;
88 import org.objectweb.asm.Opcodes;
89
90 /***
91 * Verifies the AST node and adds any defaulted AST code before
92 * bytecode generation occurs.
93 *
94 * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
95 * @version $Revision: 4598 $
96 */
97 public class Verifier implements GroovyClassVisitor, Opcodes {
98
99 public static final String __TIMESTAMP = "__timeStamp";
100 private ClassNode classNode;
101 private MethodNode methodNode;
102
103 public ClassNode getClassNode() {
104 return classNode;
105 }
106
107 public MethodNode getMethodNode() {
108 return methodNode;
109 }
110
111 /***
112 * add code to implement GroovyObject
113 * @param node
114 */
115 public void visitClass(ClassNode node) {
116 this.classNode = node;
117
118 if ((classNode.getModifiers() & Opcodes.ACC_INTERFACE) >0) {
119
120
121 ConstructorNode dummy = new ConstructorNode(0,null);
122 addInitialization(node, dummy);
123 node.visitContents(this);
124 return;
125 }
126
127 addDefaultParameterMethods(node);
128 addDefaultParameterConstructors(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 "initMetaClass",
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(ClassHelper.CLOSURE_TYPE))
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.getMethodAsString();
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 private interface DefaultArgsAction {
422 public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method);
423 }
424
425 /***
426 * Creates a new helper method for each combination of default parameter expressions
427 */
428 protected void addDefaultParameterMethods(final ClassNode node) {
429 List methods = new ArrayList(node.getMethods());
430 addDefaultParameters(methods, new DefaultArgsAction(){
431 public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
432 MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
433 expression.setImplicitThis(true);
434 Statement code = null;
435 if (method.isVoidMethod()) {
436 code = new ExpressionStatement(expression);
437 } else {
438 code = new ReturnStatement(expression);
439 }
440 node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, method.getExceptions(), code);
441 }
442 });
443 }
444
445 protected void addDefaultParameterConstructors(final ClassNode node) {
446 List methods = new ArrayList(node.getDeclaredConstructors());
447 addDefaultParameters(methods, new DefaultArgsAction(){
448 public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
449 ConstructorNode ctor = (ConstructorNode) method;
450 ConstructorCallExpression expression = new ConstructorCallExpression(ClassNode.THIS, arguments);
451 Statement code = new ExpressionStatement(expression);
452 node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code);
453 }
454 });
455 }
456
457 /***
458 * Creates a new helper method for each combination of default parameter expressions
459 */
460 protected void addDefaultParameters(List methods, DefaultArgsAction action) {
461 for (Iterator iter = methods.iterator(); iter.hasNext();) {
462 MethodNode method = (MethodNode) iter.next();
463 if (method.hasDefaultValue()) {
464 Parameter[] parameters = method.getParameters();
465 int counter = 0;
466 ArrayList paramValues = new ArrayList();
467 int size = parameters.length;
468 for (int i = size - 1; i >= 0; i--) {
469 Parameter parameter = parameters[i];
470 if (parameter != null && parameter.hasInitialExpression()) {
471 paramValues.add(new Integer(i));
472 paramValues.add(parameter.getInitialExpression());
473 counter++;
474 }
475 }
476
477 for (int j = 1; j <= counter; j++) {
478 Parameter[] newParams = new Parameter[parameters.length - j];
479 ArgumentListExpression arguments = new ArgumentListExpression();
480 int index = 0;
481 int k = 1;
482 for (int i = 0; i < parameters.length; i++) {
483 if (k > counter - j && parameters[i] != null && parameters[i].hasInitialExpression()) {
484 arguments.addExpression(parameters[i].getInitialExpression());
485 k++;
486 }
487 else if (parameters[i] != null && parameters[i].hasInitialExpression()) {
488 newParams[index++] = parameters[i];
489 arguments.addExpression(new VariableExpression(parameters[i].getName()));
490 k++;
491 }
492 else {
493 newParams[index++] = parameters[i];
494 arguments.addExpression(new VariableExpression(parameters[i].getName()));
495 }
496 }
497 action.call(arguments,newParams,method);
498 }
499 }
500 }
501 }
502
503 protected void addClosureCode(InnerClassNode node) {
504
505 }
506
507 protected void addInitialization(ClassNode node) {
508 for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
509 addInitialization(node, (ConstructorNode) iter.next());
510 }
511 }
512
513 protected void addInitialization(ClassNode node, ConstructorNode constructorNode) {
514 Statement firstStatement = constructorNode.getFirstStatement();
515 ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement);
516
517
518 if (first!=null && first.isThisCall()) return;
519
520 List statements = new ArrayList();
521 List staticStatements = new ArrayList();
522 for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
523 addFieldInitialization(statements, staticStatements, (FieldNode) iter.next());
524 }
525 statements.addAll(node.getObjectInitializerStatements());
526 if (!statements.isEmpty()) {
527 Statement code = constructorNode.getCode();
528 BlockStatement block = new BlockStatement();
529 List otherStatements = block.getStatements();
530 if (code instanceof BlockStatement) {
531 block = (BlockStatement) code;
532 otherStatements=block.getStatements();
533 }
534 else if (code != null) {
535 otherStatements.add(code);
536 }
537 if (!otherStatements.isEmpty()) {
538 if (first!=null) {
539
540 otherStatements.remove(0);
541 statements.add(0, firstStatement);
542 }
543 statements.addAll(otherStatements);
544 }
545 constructorNode.setCode(new BlockStatement(statements, block.getVariableScope()));
546 }
547
548 if (!staticStatements.isEmpty()) {
549 node.addStaticInitializerStatements(staticStatements,true);
550 }
551 }
552
553 private ConstructorCallExpression getFirstIfSpecialConstructorCall(Statement code) {
554 if (code == null || !(code instanceof ExpressionStatement)) return null;
555
556 Expression expression = ((ExpressionStatement)code).getExpression();
557 if (!(expression instanceof ConstructorCallExpression)) return null;
558 ConstructorCallExpression cce = (ConstructorCallExpression) expression;
559 if (cce.isSpecialCall()) return cce;
560 return null;
561 }
562
563 protected void addFieldInitialization(
564 List list,
565 List staticList,
566 FieldNode fieldNode) {
567 Expression expression = fieldNode.getInitialExpression();
568 if (expression != null) {
569 ExpressionStatement statement =
570 new ExpressionStatement(
571 new BinaryExpression(
572 new FieldExpression(fieldNode),
573 Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
574 expression));
575 if (fieldNode.isStatic()) {
576 staticList.add(statement);
577 }
578 else {
579 list.add(statement);
580 }
581 }
582 }
583
584 /***
585 * Capitalizes the start of the given bean property name
586 */
587 public static String capitalize(String name) {
588 return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
589 }
590
591 protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
592 Expression expression = new FieldExpression(field);
593 return new ReturnStatement(expression);
594 }
595
596 protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
597 Expression expression = new FieldExpression(field);
598 return new ExpressionStatement(
599 new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
600 }
601
602 /***
603 * Filters the given statements
604 */
605 protected List filterStatements(List list) {
606 List answer = new ArrayList(list.size());
607 for (Iterator iter = list.iterator(); iter.hasNext();) {
608 answer.add(filterStatement((Statement) iter.next()));
609 }
610 return answer;
611 }
612
613 protected Statement filterStatement(Statement statement) {
614 if (statement instanceof ExpressionStatement) {
615 ExpressionStatement expStmt = (ExpressionStatement) statement;
616 Expression expression = expStmt.getExpression();
617 if (expression instanceof ClosureExpression) {
618 ClosureExpression closureExp = (ClosureExpression) expression;
619 if (!closureExp.isParameterSpecified()) {
620 return closureExp.getCode();
621 }
622 }
623 }
624 return statement;
625 }
626
627 }