001    /*
002     $Id: Verifier.java,v 1.48 2005/11/13 16:42:11 blackdrag Exp $
003    
004     Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005    
006     Redistribution and use of this software and associated documentation
007     ("Software"), with or without modification, are permitted provided
008     that the following conditions are met:
009    
010     1. Redistributions of source code must retain copyright
011        statements and notices.  Redistributions must also contain a
012        copy of this document.
013    
014     2. Redistributions in binary form must reproduce the
015        above copyright notice, this list of conditions and the
016        following disclaimer in the documentation and/or other
017        materials provided with the distribution.
018    
019     3. The name "groovy" must not be used to endorse or promote
020        products derived from this Software without prior written
021        permission of The Codehaus.  For written permission,
022        please contact info@codehaus.org.
023    
024     4. Products derived from this Software may not be called "groovy"
025        nor may "groovy" appear in their names without prior written
026        permission of The Codehaus. "groovy" is a registered
027        trademark of The Codehaus.
028    
029     5. Due credit should be given to The Codehaus -
030        http://groovy.codehaus.org/
031    
032     THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033     ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034     NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
036     THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039     SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040     HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041     STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043     OF THE POSSIBILITY OF SUCH DAMAGE.
044    
045     */
046    package org.codehaus.groovy.classgen;
047    
048    import groovy.lang.Closure;
049    import groovy.lang.GroovyObject;
050    import groovy.lang.MetaClass;
051    
052    import java.lang.reflect.Modifier;
053    import java.util.ArrayList;
054    import java.util.Iterator;
055    import java.util.List;
056    
057    import org.codehaus.groovy.ast.ClassHelper;
058    import org.codehaus.groovy.ast.ClassNode;
059    import org.codehaus.groovy.ast.CodeVisitorSupport;
060    import org.codehaus.groovy.ast.ConstructorNode;
061    import org.codehaus.groovy.ast.FieldNode;
062    import org.codehaus.groovy.ast.GroovyClassVisitor;
063    import org.codehaus.groovy.ast.InnerClassNode;
064    import org.codehaus.groovy.ast.MethodNode;
065    import org.codehaus.groovy.ast.Parameter;
066    import org.codehaus.groovy.ast.PropertyNode;
067    import org.codehaus.groovy.ast.expr.ArgumentListExpression;
068    import org.codehaus.groovy.ast.expr.BinaryExpression;
069    import org.codehaus.groovy.ast.expr.BooleanExpression;
070    import org.codehaus.groovy.ast.expr.ClosureExpression;
071    import org.codehaus.groovy.ast.expr.ConstantExpression;
072    import org.codehaus.groovy.ast.expr.Expression;
073    import org.codehaus.groovy.ast.expr.FieldExpression;
074    import org.codehaus.groovy.ast.expr.MethodCallExpression;
075    import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
076    import org.codehaus.groovy.ast.expr.VariableExpression;
077    import org.codehaus.groovy.ast.stmt.BlockStatement;
078    import org.codehaus.groovy.ast.stmt.EmptyStatement;
079    import org.codehaus.groovy.ast.stmt.ExpressionStatement;
080    import org.codehaus.groovy.ast.stmt.IfStatement;
081    import org.codehaus.groovy.ast.stmt.ReturnStatement;
082    import org.codehaus.groovy.ast.stmt.Statement;
083    import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
084    import org.codehaus.groovy.syntax.Types;
085    import org.codehaus.groovy.syntax.Token;
086    import org.codehaus.groovy.syntax.RuntimeParserException;
087    import org.objectweb.asm.Opcodes;
088    
089    /**
090     * Verifies the AST node and adds any defaulted AST code before
091     * bytecode generation occurs.
092     * 
093     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
094     * @version $Revision: 1.48 $
095     */
096    public class Verifier implements GroovyClassVisitor, Opcodes {
097    
098        public static final String __TIMESTAMP = "__timeStamp";
099            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                //interfaces have no construcotrs, but this code expects one, 
119                //so creta a dummy and don't add it to the class node
120                ConstructorNode dummy = new ConstructorNode(0,null);
121                addFieldInitialization(node, dummy);
122                node.visitContents(this);
123                return;
124            }
125            
126            addDefaultParameterMethods(node);
127    
128            if (!node.isDerivedFromGroovyObject()) {
129                node.addInterface(ClassHelper.make(GroovyObject.class));
130    
131                // lets add a new field for the metaclass
132                StaticMethodCallExpression initMetaClassCall =
133                    new StaticMethodCallExpression(
134                        ClassHelper.make(ScriptBytecodeAdapter.class),
135                        "getMetaClass",
136                        VariableExpression.THIS_EXPRESSION);
137    
138                PropertyNode metaClassProperty =
139                    node.addProperty("metaClass", ACC_PUBLIC, ClassHelper.make(MetaClass.class), initMetaClassCall, null, null);
140                metaClassProperty.setSynthetic(true);
141                FieldNode metaClassField = metaClassProperty.getField();
142                metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
143    
144                FieldExpression metaClassVar = new FieldExpression(metaClassField);
145                IfStatement initMetaClassField =
146                    new IfStatement(
147                        new BooleanExpression(
148                            new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
149                        new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
150                        EmptyStatement.INSTANCE);
151    
152                node.addSyntheticMethod(
153                    "getMetaClass",
154                    ACC_PUBLIC,
155                    ClassHelper.make(MetaClass.class),
156                    Parameter.EMPTY_ARRAY,
157                    new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)})
158                );
159    
160                // @todo we should check if the base class implements the invokeMethod method
161    
162                // lets add the invokeMethod implementation
163                ClassNode superClass = node.getSuperClass();
164                boolean addDelegateObject =
165                    (node instanceof InnerClassNode && superClass.equals(Closure.class.getName()))
166                        || superClass.equals(ClassHelper.GSTRING_TYPE);
167    
168                // don't do anything as the base class implements the invokeMethod
169                if (!addDelegateObject) {
170                    node.addSyntheticMethod(
171                        "invokeMethod",
172                        ACC_PUBLIC,
173                        ClassHelper.OBJECT_TYPE,
174                        new Parameter[] {
175                            new Parameter(ClassHelper.STRING_TYPE, "method"),
176                            new Parameter(ClassHelper.OBJECT_TYPE, "arguments")},
177                        new BlockStatement(
178                            new Statement[] {
179                                initMetaClassField,
180                                new ReturnStatement(
181                                    new MethodCallExpression(
182                                        metaClassVar,
183                                        "invokeMethod",
184                                        new ArgumentListExpression(
185                                            new Expression[] {
186                                                VariableExpression.THIS_EXPRESSION,
187                                                new VariableExpression("method"),
188                                                new VariableExpression("arguments")})))
189                    }));
190    
191                    if (!node.isScript()) {
192                        node.addSyntheticMethod(
193                            "getProperty",
194                            ACC_PUBLIC,
195                            ClassHelper.OBJECT_TYPE,
196                            new Parameter[] { new Parameter(ClassHelper.STRING_TYPE, "property")},
197                            new BlockStatement(
198                                new Statement[] {
199                                    initMetaClassField,
200                                    new ReturnStatement(
201                                        new MethodCallExpression(
202                                            metaClassVar,
203                                            "getProperty",
204                                            new ArgumentListExpression(
205                                                new Expression[] {
206                                                    VariableExpression.THIS_EXPRESSION,
207                                                    new VariableExpression("property")})))
208                        }));
209    
210                        node.addSyntheticMethod(
211                            "setProperty",
212                            ACC_PUBLIC,
213                            ClassHelper.VOID_TYPE,
214                            new Parameter[] {
215                                new Parameter(ClassHelper.STRING_TYPE, "property"),
216                                new Parameter(ClassHelper.OBJECT_TYPE, "value")},
217                            new BlockStatement(
218                                new Statement[] {
219                                    initMetaClassField,
220                                    new ExpressionStatement(
221                                        new MethodCallExpression(
222                                            metaClassVar,
223                                            "setProperty",
224                                            new ArgumentListExpression(
225                                                new Expression[] {
226                                                    VariableExpression.THIS_EXPRESSION,
227                                                    new VariableExpression("property"),
228                                                    new VariableExpression("value")})))
229                        }));
230                    }
231                }
232            }
233    
234            if (node.getDeclaredConstructors().isEmpty()) {
235                ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
236                constructor.setSynthetic(true);
237                node.addConstructor(constructor);
238            }
239            
240            if (!(node instanceof InnerClassNode)) {// add a static timestamp field to the class
241                FieldNode timeTagField = new FieldNode(
242                        Verifier.__TIMESTAMP,
243                        Modifier.PUBLIC | Modifier.STATIC,
244                        ClassHelper.Long_TYPE,
245                        //"",
246                        node,
247                        new ConstantExpression(new Long(System.currentTimeMillis())));
248                // alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
249                timeTagField.setSynthetic(true);
250                node.addField(timeTagField);
251            }
252    
253            addFieldInitialization(node);
254    
255            node.visitContents(this);
256        }
257        public void visitConstructor(ConstructorNode node) {
258            CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
259                boolean firstMethodCall = true;
260                String type=null;
261                public void visitMethodCallExpression(MethodCallExpression call) {
262                    if (!firstMethodCall) return;
263                    firstMethodCall = false;
264                    String name = call.getMethod();
265                    if (!name.equals("super") && !name.equals("this")) return;
266                    type=name;
267                    call.getArguments().visit(this);
268                    type=null;
269                }
270                public void visitVariableExpression(VariableExpression expression) {
271                    if (type==null) return;
272                    String name = expression.getName();
273                    if (!name.equals("this") && !name.equals("super")) return;
274                    throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
275                }            
276            };
277            Statement s = node.getCode();
278            //todo why can a statement can be null?
279            if (s == null) return;
280            s.visit(checkSuper);
281        }
282    
283        public void visitMethod(MethodNode node) {
284            this.methodNode = node;
285            Statement statement = node.getCode();
286            if (!node.isVoidMethod()) {
287                if (statement instanceof ExpressionStatement) {
288                    ExpressionStatement expStmt = (ExpressionStatement) statement;
289                    node.setCode(new ReturnStatement(expStmt.getExpression()));
290                }
291                else if (statement instanceof BlockStatement) {
292                    BlockStatement block = (BlockStatement) statement;
293    
294                    // lets copy the list so we create a new block
295                    List list = new ArrayList(block.getStatements());
296                    if (!list.isEmpty()) {
297                        int idx = list.size() - 1;
298                        Statement last = (Statement) list.get(idx);
299                        if (last instanceof ExpressionStatement) {
300                            ExpressionStatement expStmt = (ExpressionStatement) last;
301                            list.set(idx, new ReturnStatement(expStmt.getExpression()));
302                        }
303                        else if (!(last instanceof ReturnStatement)) {
304                            list.add(new ReturnStatement(ConstantExpression.NULL));
305                        }
306                    }
307                    else {
308                        list.add(new ReturnStatement(ConstantExpression.NULL));
309                    }
310    
311                    node.setCode(new BlockStatement(filterStatements(list)));
312                }
313            }
314            else if (!node.isAbstract()) {
315                    BlockStatement newBlock = new BlockStatement();
316                if (statement instanceof BlockStatement) {
317                    newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
318                }
319                else {
320                    newBlock.addStatement(filterStatement(statement));
321                }
322                newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
323                node.setCode(newBlock);
324            }
325            if (node.getName().equals("main") && node.isStatic()) {
326                Parameter[] params = node.getParameters();
327                if (params.length == 1) {
328                    Parameter param = params[0];
329                    if (param.getType() == null || param.getType()==ClassHelper.OBJECT_TYPE) {
330                        param.setType(ClassHelper.STRING_TYPE.makeArray());
331                    }
332                }
333            }
334            statement = node.getCode();
335            if (statement!=null) statement.visit(new VerifierCodeVisitor(this));
336        }
337    
338        public void visitField(FieldNode node) {
339        }
340    
341        public void visitProperty(PropertyNode node) {
342            String name = node.getName();
343            FieldNode field = node.getField();
344    
345            String getterName = "get" + 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.getSetterBlock();
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 (ClassHelper.boolean_TYPE==node.getType() || ClassHelper.Boolean_TYPE==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(), ClassHelper.VOID_TYPE, setterParameterTypes, setterBlock);
381                setter.setSynthetic(true);
382                classNode.addMethod(setter);
383                visitMethod(setter);
384            }
385        }
386    
387        // Implementation methods
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                if (method.hasDefaultValue()) {
398                    Parameter[] parameters = method.getParameters();
399                    int counter = 0;
400                    ArrayList paramValues = new ArrayList();
401                    int size = parameters.length;
402                    for (int i = size - 1; i >= 0; i--) {
403                        Parameter parameter = parameters[i];
404                        if (parameter != null && parameter.hasInitialExpression()) {
405                            paramValues.add(new Integer(i));
406                            paramValues.add(parameter.getInitialExpression());
407                            counter++;
408                        }
409                    }
410    
411                    for (int j = 1; j <= counter; j++) {
412                        Parameter[] newParams =  new Parameter[parameters.length - j];
413                        ArgumentListExpression arguments = new ArgumentListExpression();
414                        int index = 0;
415                        int k = 1;
416                        for (int i = 0; i < parameters.length; i++) {
417                            if (k > counter - j && parameters[i] != null && parameters[i].hasInitialExpression()) {
418                                arguments.addExpression(parameters[i].getInitialExpression());
419                                k++;
420                            }
421                            else if (parameters[i] != null && parameters[i].hasInitialExpression()) {
422                                newParams[index++] = parameters[i];
423                                arguments.addExpression(new VariableExpression(parameters[i].getName()));
424                                k++;
425                            }
426                            else {
427                                newParams[index++] = parameters[i];
428                                arguments.addExpression(new VariableExpression(parameters[i].getName()));
429                            }
430                        }
431    
432                        MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
433                        Statement code = null;
434                        if (method.isVoidMethod()) {
435                            code = new ExpressionStatement(expression);
436                        }
437                        else {
438                            code = new ReturnStatement(expression);
439                        }
440                        node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
441                    }
442                }
443            }
444        }
445    
446        /**
447         * Adds a new method which defaults the values for all the parameters starting 
448         * from and including the given index
449         * 
450         * @param node the class to add the method
451         * @param method the given method to add a helper of
452         * @param parameters the parameters of the method to add a helper for
453         * @param index the index of the first default value expression parameter to use
454         */
455        protected void addDefaultParameterMethod(ClassNode node, MethodNode method, Parameter[] parameters, int depth, ArrayList values) {
456            // lets create a method using this expression
457            Parameter[] newParams = new Parameter[parameters.length - depth];
458            int index = 0;
459            ArgumentListExpression arguments = new ArgumentListExpression();
460            for (int i = 0; i < parameters.length; i++) {
461                if (parameters[i] != null && parameters[i].hasInitialExpression()) {
462                    newParams[index++] = parameters[i];
463                    arguments.addExpression(new VariableExpression(parameters[i].getName()));
464                }
465                else {
466                    arguments.addExpression(parameters[i].getInitialExpression());
467                }
468            }
469    
470            MethodCallExpression expression =
471                new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
472            Statement code = null;
473            if (method.isVoidMethod()) {
474                code = new ExpressionStatement(expression);
475            }
476            else {
477                code = new ReturnStatement(expression);
478            }
479    
480            node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
481        }
482    
483        /**
484         * Adds a new method which defaults the values for all the parameters starting 
485         * from and including the given index
486         * 
487         * @param node the class to add the method
488         * @param method the given method to add a helper of
489         * @param parameters the parameters of the method to add a helper for
490         * @param index the index of the first default value expression parameter to use
491         */
492        protected void addDefaultParameterMethod(ClassNode node, MethodNode method, Parameter[] parameters, int index) {
493            // lets create a method using this expression
494            Parameter[] newParams = new Parameter[index];
495            System.arraycopy(parameters, 0, newParams, 0, index);
496    
497            ArgumentListExpression arguments = new ArgumentListExpression();
498            int size = parameters.length;
499            for (int i = 0; i < size; i++) {
500                if (i < index) {
501                    arguments.addExpression(new VariableExpression(parameters[i].getName()));
502                }
503                else {
504                    Expression defaultValue = parameters[i].getInitialExpression();
505                    if (defaultValue == null) {
506                        throw new RuntimeParserException(
507                            "The " + parameters[i].getName() + " parameter must have a default value",
508                            method);
509                    }
510                    else {
511                        arguments.addExpression(defaultValue);
512                    }
513                }
514            }
515    
516            MethodCallExpression expression =
517                new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
518            Statement code = null;
519            if (method.isVoidMethod()) {
520                code = new ExpressionStatement(expression);
521                }
522            else {
523                code = new ReturnStatement(expression);
524            }
525    
526            node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, code);
527        }
528    
529        protected void addClosureCode(InnerClassNode node) {
530            // add a new invoke
531        }
532    
533        protected void addFieldInitialization(ClassNode node) {
534            for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
535                addFieldInitialization(node, (ConstructorNode) iter.next());
536            }
537        }
538    
539        protected void addFieldInitialization(ClassNode node, ConstructorNode constructorNode) {
540            List statements = new ArrayList();
541            List staticStatements = new ArrayList();
542            for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
543                addFieldInitialization(statements, staticStatements, constructorNode, (FieldNode) iter.next());
544            }
545            if (!statements.isEmpty()) {
546                Statement code = constructorNode.getCode();
547                List otherStatements = new ArrayList();
548                if (code instanceof BlockStatement) {
549                    BlockStatement block = (BlockStatement) code;
550                    otherStatements.addAll(block.getStatements());
551                }
552                else if (code != null) {
553                    otherStatements.add(code);
554                }
555                if (!otherStatements.isEmpty()) {
556                    Statement first = (Statement) otherStatements.get(0);
557                    if (isSuperMethodCall(first)) {
558                        otherStatements.remove(0);
559                        statements.add(0, first);
560                    }
561                    statements.addAll(otherStatements);
562                }
563                constructorNode.setCode(new BlockStatement(statements));
564            }
565    
566            if (!staticStatements.isEmpty()) {
567                node.addStaticInitializerStatements(staticStatements);
568            }
569        }
570    
571        protected void addFieldInitialization(
572            List list,
573            List staticList,
574            ConstructorNode constructorNode,
575            FieldNode fieldNode) {
576            Expression expression = fieldNode.getInitialExpression();
577            if (expression != null) {
578                ExpressionStatement statement =
579                    new ExpressionStatement(
580                        new BinaryExpression(
581                            new FieldExpression(fieldNode),
582                            Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
583                            expression));
584                if (fieldNode.isStatic()) {
585                    staticList.add(statement);
586                }
587                else {
588                    list.add(statement);
589                }
590            }
591        }
592    
593        protected boolean isSuperMethodCall(Statement first) {
594            if (first instanceof ExpressionStatement) {
595                ExpressionStatement exprStmt = (ExpressionStatement) first;
596                Expression expr = exprStmt.getExpression();
597                if (expr instanceof MethodCallExpression) {
598                    return MethodCallExpression.isSuperMethodCall((MethodCallExpression) expr);
599                }
600            }
601            return false;
602        }
603    
604        /**
605         * Capitalizes the start of the given bean property name
606         */
607        public static String capitalize(String name) {
608            return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
609        }
610    
611        protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
612            Expression expression = new FieldExpression(field);
613            return new ReturnStatement(expression);
614        }
615    
616        protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
617            Expression expression = new FieldExpression(field);
618            return new ExpressionStatement(
619                new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
620        }
621    
622        /**
623         * Filters the given statements
624         */
625        protected List filterStatements(List list) {
626            List answer = new ArrayList(list.size());
627            for (Iterator iter = list.iterator(); iter.hasNext();) {
628                answer.add(filterStatement((Statement) iter.next()));
629            }
630            return answer;
631        }
632    
633        protected Statement filterStatement(Statement statement) {
634            if (statement instanceof ExpressionStatement) {
635                ExpressionStatement expStmt = (ExpressionStatement) statement;
636                Expression expression = expStmt.getExpression();
637                if (expression instanceof ClosureExpression) {
638                    ClosureExpression closureExp = (ClosureExpression) expression;
639                    if (!closureExp.isParameterSpecified()) {
640                        return closureExp.getCode();
641                    }
642                }
643            }
644            return statement;
645        }
646    
647    }