001    /*
002     $Id: Verifier.java 4598 2006-12-22 20:21:21Z blackdrag $
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.GroovyObject;
049    import groovy.lang.MetaClass;
050    
051    import java.lang.reflect.Modifier;
052    import java.util.ArrayList;
053    import java.util.Iterator;
054    import java.util.List;
055    
056    import org.codehaus.groovy.ast.ClassHelper;
057    import org.codehaus.groovy.ast.ClassNode;
058    import org.codehaus.groovy.ast.CodeVisitorSupport;
059    import org.codehaus.groovy.ast.ConstructorNode;
060    import org.codehaus.groovy.ast.FieldNode;
061    import org.codehaus.groovy.ast.GroovyClassVisitor;
062    import org.codehaus.groovy.ast.InnerClassNode;
063    import org.codehaus.groovy.ast.MethodNode;
064    import org.codehaus.groovy.ast.Parameter;
065    import org.codehaus.groovy.ast.PropertyNode;
066    import org.codehaus.groovy.ast.VariableScope;
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.ConstructorCallExpression;
073    import org.codehaus.groovy.ast.expr.Expression;
074    import org.codehaus.groovy.ast.expr.FieldExpression;
075    import org.codehaus.groovy.ast.expr.MethodCallExpression;
076    import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
077    import org.codehaus.groovy.ast.expr.VariableExpression;
078    import org.codehaus.groovy.ast.stmt.BlockStatement;
079    import org.codehaus.groovy.ast.stmt.EmptyStatement;
080    import org.codehaus.groovy.ast.stmt.ExpressionStatement;
081    import org.codehaus.groovy.ast.stmt.IfStatement;
082    import org.codehaus.groovy.ast.stmt.ReturnStatement;
083    import org.codehaus.groovy.ast.stmt.Statement;
084    import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
085    import org.codehaus.groovy.syntax.Types;
086    import org.codehaus.groovy.syntax.Token;
087    import org.codehaus.groovy.syntax.RuntimeParserException;
088    import org.objectweb.asm.Opcodes;
089    
090    /**
091     * Verifies the AST node and adds any defaulted AST code before
092     * bytecode generation occurs.
093     * 
094     * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
095     * @version $Revision: 4598 $
096     */
097    public class Verifier implements GroovyClassVisitor, Opcodes {
098    
099        public static final String __TIMESTAMP = "__timeStamp";
100            private ClassNode classNode;
101        private MethodNode methodNode;
102    
103        public ClassNode getClassNode() {
104            return classNode;
105        }
106    
107        public MethodNode getMethodNode() {
108            return methodNode;
109        }
110    
111        /**
112         * add code to implement GroovyObject
113         * @param node
114         */
115        public void visitClass(ClassNode node) {
116            this.classNode = node;
117                    
118            if ((classNode.getModifiers() & Opcodes.ACC_INTERFACE) >0) {
119                //interfaces have no construcotrs, but this code expects one, 
120                //so creta a dummy and don't add it to the class node
121                ConstructorNode dummy = new ConstructorNode(0,null);
122                addInitialization(node, dummy);
123                node.visitContents(this);
124                return;
125            }
126            
127            addDefaultParameterMethods(node);
128            addDefaultParameterConstructors(node);
129    
130            if (!node.isDerivedFromGroovyObject()) {
131                node.addInterface(ClassHelper.make(GroovyObject.class));
132    
133                // lets add a new field for the metaclass
134                StaticMethodCallExpression initMetaClassCall =
135                    new StaticMethodCallExpression(
136                        ClassHelper.make(ScriptBytecodeAdapter.class),
137                        "initMetaClass",
138                        VariableExpression.THIS_EXPRESSION);
139    
140                PropertyNode metaClassProperty =
141                    node.addProperty("metaClass", ACC_PUBLIC, ClassHelper.make(MetaClass.class), initMetaClassCall, null, null);
142                metaClassProperty.setSynthetic(true);
143                FieldNode metaClassField = metaClassProperty.getField();
144                metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
145    
146                FieldExpression metaClassVar = new FieldExpression(metaClassField);
147                IfStatement initMetaClassField =
148                    new IfStatement(
149                        new BooleanExpression(
150                            new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
151                        new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
152                        EmptyStatement.INSTANCE);
153    
154                node.addSyntheticMethod(
155                    "getMetaClass",
156                    ACC_PUBLIC,
157                    ClassHelper.make(MetaClass.class),
158                    Parameter.EMPTY_ARRAY,
159                    ClassNode.EMPTY_ARRAY,
160                    new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)}, new VariableScope())
161                );
162    
163                // @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(ClassHelper.CLOSURE_TYPE))
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.getMethodAsString();
296                    if (!name.equals("super") && !name.equals("this")) return;
297                    type=name;
298                    call.getArguments().visit(this);
299                    type=null;
300                }
301                public void visitVariableExpression(VariableExpression expression) {
302                    if (type==null) return;
303                    String name = expression.getName();
304                    if (!name.equals("this") && !name.equals("super")) return;
305                    throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
306                }            
307            };
308            Statement s = node.getCode();
309            //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        private interface DefaultArgsAction {
422            public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method);
423        }
424        
425        /**
426         * Creates a new helper method for each combination of default parameter expressions 
427         */
428        protected void addDefaultParameterMethods(final ClassNode node) {
429            List methods = new ArrayList(node.getMethods());
430            addDefaultParameters(methods, new DefaultArgsAction(){
431                public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
432                    MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
433                    expression.setImplicitThis(true);
434                    Statement code = null;
435                    if (method.isVoidMethod()) {
436                        code = new ExpressionStatement(expression);
437                    } else {
438                        code = new ReturnStatement(expression);
439                    }
440                    node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, method.getExceptions(), code);
441                }
442            });
443        }
444        
445        protected void addDefaultParameterConstructors(final ClassNode node) {
446            List methods = new ArrayList(node.getDeclaredConstructors());
447            addDefaultParameters(methods, new DefaultArgsAction(){
448                public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
449                    ConstructorNode ctor = (ConstructorNode) method;
450                    ConstructorCallExpression expression = new ConstructorCallExpression(ClassNode.THIS, arguments);
451                    Statement code = new ExpressionStatement(expression);
452                    node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code);
453                }
454            });
455        }
456    
457        /**
458         * Creates a new helper method for each combination of default parameter expressions 
459         */
460        protected void addDefaultParameters(List methods, DefaultArgsAction action) {
461            for (Iterator iter = methods.iterator(); iter.hasNext();) {
462                MethodNode method = (MethodNode) iter.next();
463                if (method.hasDefaultValue()) {
464                    Parameter[] parameters = method.getParameters();
465                    int counter = 0;
466                    ArrayList paramValues = new ArrayList();
467                    int size = parameters.length;
468                    for (int i = size - 1; i >= 0; i--) {
469                        Parameter parameter = parameters[i];
470                        if (parameter != null && parameter.hasInitialExpression()) {
471                            paramValues.add(new Integer(i));
472                            paramValues.add(parameter.getInitialExpression());
473                            counter++;
474                        }
475                    }
476    
477                    for (int j = 1; j <= counter; j++) {
478                        Parameter[] newParams =  new Parameter[parameters.length - j];
479                        ArgumentListExpression arguments = new ArgumentListExpression();
480                        int index = 0;
481                        int k = 1;
482                        for (int i = 0; i < parameters.length; i++) {
483                            if (k > counter - j && parameters[i] != null && parameters[i].hasInitialExpression()) {
484                                arguments.addExpression(parameters[i].getInitialExpression());
485                                k++;
486                            }
487                            else if (parameters[i] != null && parameters[i].hasInitialExpression()) {
488                                newParams[index++] = parameters[i];
489                                arguments.addExpression(new VariableExpression(parameters[i].getName()));
490                                k++;
491                            }
492                            else {
493                                newParams[index++] = parameters[i];
494                                arguments.addExpression(new VariableExpression(parameters[i].getName()));
495                            }
496                        }
497                        action.call(arguments,newParams,method);
498                    }
499                }
500            }
501        }
502    
503        protected void addClosureCode(InnerClassNode node) {
504            // add a new invoke
505        }
506    
507        protected void addInitialization(ClassNode node) {
508            for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
509                addInitialization(node, (ConstructorNode) iter.next());
510            }
511        }
512    
513        protected void addInitialization(ClassNode node, ConstructorNode constructorNode) {
514            Statement firstStatement = constructorNode.getFirstStatement();
515            ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement);
516            
517            // in case of this(...) let the other constructor do the intit
518            if (first!=null && first.isThisCall()) return;
519            
520            List statements = new ArrayList();
521            List staticStatements = new ArrayList();
522            for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
523                addFieldInitialization(statements, staticStatements, (FieldNode) iter.next());
524            }
525            statements.addAll(node.getObjectInitializerStatements());
526            if (!statements.isEmpty()) {
527                Statement code = constructorNode.getCode();
528                BlockStatement block = new BlockStatement();
529                List otherStatements = block.getStatements();
530                if (code instanceof BlockStatement) {
531                    block = (BlockStatement) code;
532                    otherStatements=block.getStatements();
533                }
534                else if (code != null) {
535                    otherStatements.add(code);
536                }
537                if (!otherStatements.isEmpty()) {
538                    if (first!=null) {
539                        // it is super(..) since this(..) is already covered
540                        otherStatements.remove(0);
541                        statements.add(0, firstStatement);
542                    } 
543                    statements.addAll(otherStatements);
544                }
545                constructorNode.setCode(new BlockStatement(statements, block.getVariableScope()));
546            }
547    
548            if (!staticStatements.isEmpty()) {
549                node.addStaticInitializerStatements(staticStatements,true);
550            }
551        }
552    
553        private ConstructorCallExpression getFirstIfSpecialConstructorCall(Statement code) {
554            if (code == null || !(code instanceof ExpressionStatement)) return null;
555    
556            Expression expression = ((ExpressionStatement)code).getExpression();
557            if (!(expression instanceof ConstructorCallExpression)) return null;
558            ConstructorCallExpression cce = (ConstructorCallExpression) expression;
559            if (cce.isSpecialCall()) return cce;
560            return null;
561        }
562    
563        protected void addFieldInitialization(
564            List list,
565            List staticList,
566            FieldNode fieldNode) {
567            Expression expression = fieldNode.getInitialExpression();
568            if (expression != null) {
569                ExpressionStatement statement =
570                    new ExpressionStatement(
571                        new BinaryExpression(
572                            new FieldExpression(fieldNode),
573                            Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
574                            expression));
575                if (fieldNode.isStatic()) {
576                    staticList.add(statement);
577                }
578                else {
579                    list.add(statement);
580                }
581            }
582        }
583    
584        /**
585         * Capitalizes the start of the given bean property name
586         */
587        public static String capitalize(String name) {
588            return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
589        }
590    
591        protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
592            Expression expression = new FieldExpression(field);
593            return new ReturnStatement(expression);
594        }
595    
596        protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
597            Expression expression = new FieldExpression(field);
598            return new ExpressionStatement(
599                new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
600        }
601    
602        /**
603         * Filters the given statements
604         */
605        protected List filterStatements(List list) {
606            List answer = new ArrayList(list.size());
607            for (Iterator iter = list.iterator(); iter.hasNext();) {
608                answer.add(filterStatement((Statement) iter.next()));
609            }
610            return answer;
611        }
612    
613        protected Statement filterStatement(Statement statement) {
614            if (statement instanceof ExpressionStatement) {
615                ExpressionStatement expStmt = (ExpressionStatement) statement;
616                Expression expression = expStmt.getExpression();
617                if (expression instanceof ClosureExpression) {
618                    ClosureExpression closureExp = (ClosureExpression) expression;
619                    if (!closureExp.isParameterSpecified()) {
620                        return closureExp.getCode();
621                    }
622                }
623            }
624            return statement;
625        }
626    
627    }