View Javadoc

1   /*
2    $Id: Verifier.java,v 1.55 2006/06/25 19:58:43 blackdrag 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.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             //interfaces have no construcotrs, but this code expects one, 
121             //so creta a dummy and don't add it to the class node
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             // lets add a new field for the metaclass
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             // @todo we should check if the base class implements the invokeMethod method
164 
165             // lets add the invokeMethod implementation
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             // don't do anything as the base class implements the invokeMethod
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)) {// add a static timestamp field to the class
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             // alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
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         //todo why can a statement can be null?
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                 // lets copy the list so we create a new block
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     // Implementation methods
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         // lets create a method using this expression
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         // lets create a method using this expression
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         // add a new invoke
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         // in case of this(...) let the other constructor do the intit
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                     // it is super(..) since this(..) is already covered
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 }