KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > codehaus > groovy > classgen > Verifier


1 /*
2  $Id: Verifier.java,v 1.32 2004/07/10 03:31:39 bran Exp $
3
4  Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5
6  Redistribution and use of this software and associated documentation
7  ("Software"), with or without modification, are permitted provided
8  that the following conditions are met:
9
10  1. Redistributions of source code must retain copyright
11     statements and notices. Redistributions must also contain a
12     copy of this document.
13
14  2. Redistributions in binary form must reproduce the
15     above copyright notice, this list of conditions and the
16     following disclaimer in the documentation and/or other
17     materials provided with the distribution.
18
19  3. The name "groovy" must not be used to endorse or promote
20     products derived from this Software without prior written
21     permission of The Codehaus. For written permission,
22     please contact info@codehaus.org.
23
24  4. Products derived from this Software may not be called "groovy"
25     nor may "groovy" appear in their names without prior written
26     permission of The Codehaus. "groovy" is a registered
27     trademark of The Codehaus.
28
29  5. Due credit should be given to The Codehaus -
30     http://groovy.codehaus.org/
31
32  THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
33  ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
34  NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
35  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
36  THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
37  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
38  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
39  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
40  HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
41  STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
43  OF THE POSSIBILITY OF SUCH DAMAGE.
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 JavaDoc;
54 import java.util.ArrayList JavaDoc;
55 import java.util.Iterator JavaDoc;
56 import java.util.List JavaDoc;
57
58 import org.codehaus.groovy.ast.ClassNode;
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.expr.ArgumentListExpression;
67 import org.codehaus.groovy.ast.expr.BinaryExpression;
68 import org.codehaus.groovy.ast.expr.BooleanExpression;
69 import org.codehaus.groovy.ast.expr.ClosureExpression;
70 import org.codehaus.groovy.ast.expr.ConstantExpression;
71 import org.codehaus.groovy.ast.expr.Expression;
72 import org.codehaus.groovy.ast.expr.FieldExpression;
73 import org.codehaus.groovy.ast.expr.MethodCallExpression;
74 import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
75 import org.codehaus.groovy.ast.expr.VariableExpression;
76 import org.codehaus.groovy.ast.stmt.BlockStatement;
77 import org.codehaus.groovy.ast.stmt.EmptyStatement;
78 import org.codehaus.groovy.ast.stmt.ExpressionStatement;
79 import org.codehaus.groovy.ast.stmt.IfStatement;
80 import org.codehaus.groovy.ast.stmt.ReturnStatement;
81 import org.codehaus.groovy.ast.stmt.Statement;
82 import org.codehaus.groovy.runtime.InvokerHelper;
83 import org.codehaus.groovy.syntax.Types;
84 import org.codehaus.groovy.syntax.Token;
85 import org.codehaus.groovy.syntax.parser.RuntimeParserException;
86 import org.objectweb.asm.Constants;
87
88 /**
89  * Verifies the AST node and adds any defaulted AST code before
90  * bytecode generation occurs.
91  *
92  * @author <a HREF="mailto:james@coredevelopers.net">James Strachan</a>
93  * @version $Revision: 1.32 $
94  */

95 public class Verifier implements GroovyClassVisitor, Constants {
96
97     public static final String JavaDoc __TIMESTAMP = "__timeStamp";
98     private ClassNode classNode;
99     private MethodNode methodNode;
100
101     public ClassNode getClassNode() {
102         return classNode;
103     }
104
105     public MethodNode getMethodNode() {
106         return methodNode;
107     }
108
109     /**
110      * add code to implement GroovyObject
111      * @param node
112      */

113     public void visitClass(ClassNode node) {
114         this.classNode = node;
115
116         addDefaultParameterMethods(node);
117
118         if (!node.isDerivedFromGroovyObject()) {
119             node.addInterface(GroovyObject.class.getName());
120
121             // lets add a new field for the metaclass
122
StaticMethodCallExpression initMetaClassCall =
123                 new StaticMethodCallExpression(
124                     InvokerHelper.class.getName(),
125                     "getMetaClass",
126                     VariableExpression.THIS_EXPRESSION);
127
128             PropertyNode metaClassProperty =
129                 node.addProperty("metaClass", ACC_PUBLIC, MetaClass.class.getName(), initMetaClassCall, null, null);
130             metaClassProperty.setSynthetic(true);
131             FieldNode metaClassField = metaClassProperty.getField();
132             metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
133
134             FieldExpression metaClassVar = new FieldExpression(metaClassField);
135             IfStatement initMetaClassField =
136                 new IfStatement(
137                     new BooleanExpression(
138                         new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
139                     new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
140                     EmptyStatement.INSTANCE);
141
142             node.addSyntheticMethod(
143                 "getMetaClass",
144                 ACC_PUBLIC,
145                 MetaClass.class.getName(),
146                 Parameter.EMPTY_ARRAY,
147                 new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)}));
148
149             // @todo we should check if the base class implements the invokeMethod method
150

151             // lets add the invokeMethod implementation
152
String JavaDoc superClass = node.getSuperClass();
153             boolean addDelegateObject =
154                 (node instanceof InnerClassNode && superClass.equals(Closure.class.getName()))
155                     || superClass.equals(GString.class.getName());
156
157             // don't do anything as the base class implements the invokeMethod
158
if (!addDelegateObject) {
159                 node.addSyntheticMethod(
160                     "invokeMethod",
161                     ACC_PUBLIC,
162                     Object JavaDoc.class.getName(),
163                     new Parameter[] {
164                         new Parameter(String JavaDoc.class.getName(), "method"),
165                         new Parameter(Object JavaDoc.class.getName(), "arguments")},
166                     new BlockStatement(
167                         new Statement[] {
168                             initMetaClassField,
169                             new ReturnStatement(
170                                 new MethodCallExpression(
171                                     metaClassVar,
172                                     "invokeMethod",
173                                     new ArgumentListExpression(
174                                         new Expression[] {
175                                             VariableExpression.THIS_EXPRESSION,
176                                             new VariableExpression("method"),
177                                             new VariableExpression("arguments")})))
178                 }));
179
180                 if (!node.isScript()) {
181                     node.addSyntheticMethod(
182                         "getProperty",
183                         ACC_PUBLIC,
184                         Object JavaDoc.class.getName(),
185                         new Parameter[] { new Parameter(String JavaDoc.class.getName(), "property")},
186                         new BlockStatement(
187                             new Statement[] {
188                                 initMetaClassField,
189                                 new ReturnStatement(
190                                     new MethodCallExpression(
191                                         metaClassVar,
192                                         "getProperty",
193                                         new ArgumentListExpression(
194                                             new Expression[] {
195                                                 VariableExpression.THIS_EXPRESSION,
196                                                 new VariableExpression("property")})))
197                     }));
198
199                     node.addSyntheticMethod(
200                         "setProperty",
201                         ACC_PUBLIC,
202                         "void",
203                         new Parameter[] {
204                             new Parameter(String JavaDoc.class.getName(), "property"),
205                             new Parameter(Object JavaDoc.class.getName(), "value")},
206                         new BlockStatement(
207                             new Statement[] {
208                                 initMetaClassField,
209                                 new ExpressionStatement(
210                                     new MethodCallExpression(
211                                         metaClassVar,
212                                         "setProperty",
213                                         new ArgumentListExpression(
214                                             new Expression[] {
215                                                 VariableExpression.THIS_EXPRESSION,
216                                                 new VariableExpression("property"),
217                                                 new VariableExpression("value")})))
218                     }));
219                 }
220             }
221         }
222
223         if (node.getDeclaredConstructors().isEmpty()) {
224             ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
225             constructor.setSynthetic(true);
226             node.addConstructor(constructor);
227         }
228         
229         if (!(node instanceof InnerClassNode)) {// add a static timestamp field to the class
230
FieldNode timeTagField = new FieldNode(
231                     Verifier.__TIMESTAMP,
232                     Modifier.PUBLIC | Modifier.STATIC,
233                     "java.lang.Long",
234                     //"",
235
node.getName(),
236                     new ConstantExpression(new Long JavaDoc(System.currentTimeMillis())));
237             // alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
238
timeTagField.setSynthetic(true);
239             node.addField(timeTagField);
240         }
241
242         addFieldInitialization(node);
243
244         node.visitContents(this);
245     }
246
247     public void visitConstructor(ConstructorNode node) {
248     }
249
250     public void visitMethod(MethodNode node) {
251         this.methodNode = node;
252
253         Statement statement = node.getCode();
254         if (!node.isVoidMethod()) {
255             if (statement instanceof ExpressionStatement) {
256                 ExpressionStatement expStmt = (ExpressionStatement) statement;
257                 node.setCode(new ReturnStatement(expStmt.getExpression()));
258             }
259             else if (statement instanceof BlockStatement) {
260                 BlockStatement block = (BlockStatement) statement;
261
262                 // lets copy the list so we create a new block
263
List JavaDoc list = new ArrayList JavaDoc(block.getStatements());
264                 if (!list.isEmpty()) {
265                     int idx = list.size() - 1;
266                     Statement last = (Statement) list.get(idx);
267                     if (last instanceof ExpressionStatement) {
268                         ExpressionStatement expStmt = (ExpressionStatement) last;
269                         list.set(idx, new ReturnStatement(expStmt.getExpression()));
270                     }
271                     else if (!(last instanceof ReturnStatement)) {
272                         list.add(new ReturnStatement(ConstantExpression.NULL));
273                     }
274                 }
275                 else {
276                     list.add(new ReturnStatement(ConstantExpression.NULL));
277                 }
278
279                 node.setCode(new BlockStatement(filterStatements(list)));
280             }
281         }
282         else {
283             BlockStatement newBlock = new BlockStatement();
284             if (statement instanceof BlockStatement) {
285                 newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
286             }
287             else {
288                 newBlock.addStatement(filterStatement(statement));
289             }
290             newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
291             node.setCode(newBlock);
292         }
293         if (node.getName().equals("main") && node.isStatic()) {
294             Parameter[] params = node.getParameters();
295             if (params.length == 1) {
296                 Parameter param = params[0];
297                 if (param.getType() == null || param.getType().equals("java.lang.Object")) {
298                     param.setType("java.lang.String[]");
299                 }
300             }
301         }
302         node.getCode().visit(new VerifierCodeVisitor(this));
303     }
304
305     public void visitField(FieldNode node) {
306     }
307
308     public void visitProperty(PropertyNode node) {
309         String JavaDoc name = node.getName();
310         FieldNode field = node.getField();
311
312         
313         String JavaDoc getterPrefix = "get";
314         if ("boolean".equals(node.getType())) {
315             getterPrefix = "is";
316         }
317         String JavaDoc getterName = getterPrefix + capitalize(name);
318         String JavaDoc setterName = "set" + capitalize(name);
319
320         Statement getterBlock = node.getGetterBlock();
321         if (getterBlock == null) {
322             if (!node.isPrivate() && classNode.getGetterMethod(getterName) == null) {
323                 getterBlock = createGetterBlock(node, field);
324             }
325         }
326         Statement setterBlock = node.getGetterBlock();
327         if (setterBlock == null) {
328             if (!node.isPrivate() && classNode.getSetterMethod(setterName) == null) {
329                 setterBlock = createSetterBlock(node, field);
330             }
331         }
332
333         if (getterBlock != null) {
334             MethodNode getter =
335                 new MethodNode(getterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
336             getter.setSynthetic(true);
337             classNode.addMethod(getter);
338             visitMethod(getter);
339
340             if ("java.lang.Boolean".equals(node.getType())) {
341                 String JavaDoc secondGetterName = "is" + capitalize(name);
342                 MethodNode secondGetter =
343                     new MethodNode(secondGetterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
344                 secondGetter.setSynthetic(true);
345                 classNode.addMethod(secondGetter);
346                 visitMethod(secondGetter);
347             }
348         }
349         if (setterBlock != null) {
350             Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
351             MethodNode setter =
352                 new MethodNode(setterName, node.getModifiers(), "void", setterParameterTypes, setterBlock);
353             setter.setSynthetic(true);
354             classNode.addMethod(setter);
355             visitMethod(setter);
356         }
357     }
358
359     // Implementation methods
360
//-------------------------------------------------------------------------
361

362     /**
363      * Creates a new helper method for each combination of default parameter expressions
364      */

365     protected void addDefaultParameterMethods(ClassNode node) {
366         List JavaDoc methods = new ArrayList JavaDoc(node.getMethods());
367         for (Iterator JavaDoc iter = methods.iterator(); iter.hasNext();) {
368             MethodNode method = (MethodNode) iter.next();
369             Parameter[] parameters = method.getParameters();
370             int size = parameters.length;
371             for (int i = 0; i < size; i++) {
372                 Parameter parameter = parameters[i];
373                 Expression exp = parameter.getDefaultValue();
374                 if (exp != null) {
375                     addDefaultParameterMethod(node, method, parameters, i);
376                 }
377             }
378         }
379     }
380
381     /**
382      * Adds a new method which defaults the values for all the parameters starting
383      * from and including the given index
384      *
385      * @param node the class to add the method
386      * @param method the given method to add a helper of
387      * @param parameters the parameters of the method to add a helper for
388      * @param index the index of the first default value expression parameter to use
389      */

390     protected void addDefaultParameterMethod(ClassNode node, MethodNode method, Parameter[] parameters, int index) {
391         // lets create a method using this expression
392
Parameter[] newParams = new Parameter[index];
393         System.arraycopy(parameters, 0, newParams, 0, index);
394
395         ArgumentListExpression arguments = new ArgumentListExpression();
396         int size = parameters.length;
397         for (int i = 0; i < size; i++) {
398             if (i < index) {
399                 arguments.addExpression(new VariableExpression(parameters[i].getName()));
400             }
401             else {
402                 Expression defaultValue = parameters[i].getDefaultValue();
403                 if (defaultValue == null) {
404                     throw new RuntimeParserException(
405                         "The " + parameters[i].getName() + " parameter must have a default value",
406                         method);
407                 }
408                 else {
409                     arguments.addExpression(defaultValue);
410                 }
411             }
412         }
413
414         MethodCallExpression expression =
415             new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
416         Statement code = null;
417         if (method.isVoidMethod()) {
418             code = new ExpressionStatement(expression);
419             }
420         else {
421             code = new ReturnStatement(expression);
422         }
423
424         node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
425     }
426
427     protected void addClosureCode(InnerClassNode node) {
428         // add a new invoke
429
}
430
431     protected void addFieldInitialization(ClassNode node) {
432         for (Iterator JavaDoc iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
433             addFieldInitialization(node, (ConstructorNode) iter.next());
434         }
435     }
436
437     protected void addFieldInitialization(ClassNode node, ConstructorNode constructorNode) {
438         List JavaDoc statements = new ArrayList JavaDoc();
439         List JavaDoc staticStatements = new ArrayList JavaDoc();
440         for (Iterator JavaDoc iter = node.getFields().iterator(); iter.hasNext();) {
441             addFieldInitialization(statements, staticStatements, constructorNode, (FieldNode) iter.next());
442         }
443         if (!statements.isEmpty()) {
444             Statement code = constructorNode.getCode();
445             List JavaDoc otherStatements = new ArrayList JavaDoc();
446             if (code instanceof BlockStatement) {
447                 BlockStatement block = (BlockStatement) code;
448                 otherStatements.addAll(block.getStatements());
449             }
450             else if (code != null) {
451                 otherStatements.add(code);
452             }
453             if (!otherStatements.isEmpty()) {
454                 Statement first = (Statement) otherStatements.get(0);
455                 if (isSuperMethodCall(first)) {
456                     otherStatements.remove(0);
457                     statements.add(0, first);
458                 }
459                 statements.addAll(otherStatements);
460             }
461             constructorNode.setCode(new BlockStatement(statements));
462         }
463
464         if (!staticStatements.isEmpty()) {
465             node.addStaticInitializerStatements(staticStatements);
466         }
467     }
468
469     protected void addFieldInitialization(
470         List JavaDoc list,
471         List JavaDoc staticList,
472         ConstructorNode constructorNode,
473         FieldNode fieldNode) {
474         Expression expression = fieldNode.getInitialValueExpression();
475         if (expression != null) {
476             ExpressionStatement statement =
477                 new ExpressionStatement(
478                     new BinaryExpression(
479                         new FieldExpression(fieldNode),
480                         Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
481                         expression));
482             if (fieldNode.isStatic()) {
483                 staticList.add(statement);
484             }
485             else {
486                 list.add(statement);
487             }
488         }
489     }
490
491     protected boolean isSuperMethodCall(Statement first) {
492         if (first instanceof ExpressionStatement) {
493             ExpressionStatement exprStmt = (ExpressionStatement) first;
494             Expression expr = exprStmt.getExpression();
495             if (expr instanceof MethodCallExpression) {
496                 return MethodCallExpression.isSuperMethodCall((MethodCallExpression) expr);
497             }
498         }
499         return false;
500     }
501
502     /**
503      * Capitalizes the start of the given bean property name
504      */

505     public static String JavaDoc capitalize(String JavaDoc name) {
506         return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
507     }
508
509     protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
510         Expression expression = new FieldExpression(field);
511         return new ReturnStatement(expression);
512     }
513
514     protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
515         Expression expression = new FieldExpression(field);
516         return new ExpressionStatement(
517             new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
518     }
519
520     /**
521      * Filters the given statements
522      */

523     protected List JavaDoc filterStatements(List JavaDoc list) {
524         List JavaDoc answer = new ArrayList JavaDoc(list.size());
525         for (Iterator JavaDoc iter = list.iterator(); iter.hasNext();) {
526             answer.add(filterStatement((Statement) iter.next()));
527         }
528         return answer;
529     }
530
531     protected Statement filterStatement(Statement statement) {
532         if (statement instanceof ExpressionStatement) {
533             ExpressionStatement expStmt = (ExpressionStatement) statement;
534             Expression expression = expStmt.getExpression();
535             if (expression instanceof ClosureExpression) {
536                 ClosureExpression closureExp = (ClosureExpression) expression;
537                 if (!closureExp.isParameterSpecified()) {
538                     return closureExp.getCode();
539                 }
540             }
541         }
542         return statement;
543     }
544 }
545
Popular Tags