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.40 $
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 Parameter[] parameters = method.getParameters();
398 int size = parameters.length;
399 for (int i = 0; i < size; i++) {
400 Parameter parameter = parameters[i];
401 Expression exp = parameter.getDefaultValue();
402 if (exp != null) {
403 addDefaultParameterMethod(node, method, parameters, i);
404 }
405 }
406 }
407 }
408
409 /***
410 * Adds a new method which defaults the values for all the parameters starting
411 * from and including the given index
412 *
413 * @param node the class to add the method
414 * @param method the given method to add a helper of
415 * @param parameters the parameters of the method to add a helper for
416 * @param index the index of the first default value expression parameter to use
417 */
418 protected void addDefaultParameterMethod(ClassNode node, MethodNode method, Parameter[] parameters, int index) {
419
420 Parameter[] newParams = new Parameter[index];
421 System.arraycopy(parameters, 0, newParams, 0, index);
422
423 ArgumentListExpression arguments = new ArgumentListExpression();
424 int size = parameters.length;
425 for (int i = 0; i < size; i++) {
426 if (i < index) {
427 arguments.addExpression(new VariableExpression(parameters[i].getName()));
428 }
429 else {
430 Expression defaultValue = parameters[i].getDefaultValue();
431 if (defaultValue == null) {
432 throw new RuntimeParserException(
433 "The " + parameters[i].getName() + " parameter must have a default value",
434 method);
435 }
436 else {
437 arguments.addExpression(defaultValue);
438 }
439 }
440 }
441
442 MethodCallExpression expression =
443 new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
444 Statement code = null;
445 if (method.isVoidMethod()) {
446 code = new ExpressionStatement(expression);
447 }
448 else {
449 code = new ReturnStatement(expression);
450 }
451
452 node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
453 }
454
455 protected void addClosureCode(InnerClassNode node) {
456
457 }
458
459 protected void addFieldInitialization(ClassNode node) {
460 for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
461 addFieldInitialization(node, (ConstructorNode) iter.next());
462 }
463 }
464
465 protected void addFieldInitialization(ClassNode node, ConstructorNode constructorNode) {
466 List statements = new ArrayList();
467 List staticStatements = new ArrayList();
468 for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
469 addFieldInitialization(statements, staticStatements, constructorNode, (FieldNode) iter.next());
470 }
471 if (!statements.isEmpty()) {
472 Statement code = constructorNode.getCode();
473 List otherStatements = new ArrayList();
474 if (code instanceof BlockStatement) {
475 BlockStatement block = (BlockStatement) code;
476 otherStatements.addAll(block.getStatements());
477 }
478 else if (code != null) {
479 otherStatements.add(code);
480 }
481 if (!otherStatements.isEmpty()) {
482 Statement first = (Statement) otherStatements.get(0);
483 if (isSuperMethodCall(first)) {
484 otherStatements.remove(0);
485 statements.add(0, first);
486 }
487 statements.addAll(otherStatements);
488 }
489 constructorNode.setCode(new BlockStatement(statements));
490 }
491
492 if (!staticStatements.isEmpty()) {
493 node.addStaticInitializerStatements(staticStatements);
494 }
495 }
496
497 protected void addFieldInitialization(
498 List list,
499 List staticList,
500 ConstructorNode constructorNode,
501 FieldNode fieldNode) {
502 Expression expression = fieldNode.getInitialValueExpression();
503 if (expression != null) {
504 ExpressionStatement statement =
505 new ExpressionStatement(
506 new BinaryExpression(
507 new FieldExpression(fieldNode),
508 Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
509 expression));
510 if (fieldNode.isStatic()) {
511 staticList.add(statement);
512 }
513 else {
514 list.add(statement);
515 }
516 }
517 }
518
519 protected boolean isSuperMethodCall(Statement first) {
520 if (first instanceof ExpressionStatement) {
521 ExpressionStatement exprStmt = (ExpressionStatement) first;
522 Expression expr = exprStmt.getExpression();
523 if (expr instanceof MethodCallExpression) {
524 return MethodCallExpression.isSuperMethodCall((MethodCallExpression) expr);
525 }
526 }
527 return false;
528 }
529
530 /***
531 * Capitalizes the start of the given bean property name
532 */
533 public static String capitalize(String name) {
534 return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
535 }
536
537 protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
538 Expression expression = new FieldExpression(field);
539 return new ReturnStatement(expression);
540 }
541
542 protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
543 Expression expression = new FieldExpression(field);
544 return new ExpressionStatement(
545 new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
546 }
547
548 /***
549 * Filters the given statements
550 */
551 protected List filterStatements(List list) {
552 List answer = new ArrayList(list.size());
553 for (Iterator iter = list.iterator(); iter.hasNext();) {
554 answer.add(filterStatement((Statement) iter.next()));
555 }
556 return answer;
557 }
558
559 protected Statement filterStatement(Statement statement) {
560 if (statement instanceof ExpressionStatement) {
561 ExpressionStatement expStmt = (ExpressionStatement) statement;
562 Expression expression = expStmt.getExpression();
563 if (expression instanceof ClosureExpression) {
564 ClosureExpression closureExp = (ClosureExpression) expression;
565 if (!closureExp.isParameterSpecified()) {
566 return closureExp.getCode();
567 }
568 }
569 }
570 return statement;
571 }
572 }