001    /*
002     $Id: Verifier.java,v 1.41 2005/07/16 20:00:26 phk 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.GString;
050    import groovy.lang.GroovyObject;
051    import groovy.lang.MetaClass;
052    
053    import java.lang.reflect.Modifier;
054    import java.util.ArrayList;
055    import java.util.Iterator;
056    import java.util.List;
057    
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.41 $
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                node.visitContents(this);
119                return;
120            }
121            
122            addDefaultParameterMethods(node);
123    
124            if (!node.isDerivedFromGroovyObject()) {
125                node.addInterface(GroovyObject.class.getName());
126    
127                // lets add a new field for the metaclass
128                StaticMethodCallExpression initMetaClassCall =
129                    new StaticMethodCallExpression(
130                        ScriptBytecodeAdapter.class.getName(),
131                        "getMetaClass",
132                        VariableExpression.THIS_EXPRESSION);
133    
134                PropertyNode metaClassProperty =
135                    node.addProperty("metaClass", ACC_PUBLIC, MetaClass.class.getName(), initMetaClassCall, null, null);
136                metaClassProperty.setSynthetic(true);
137                FieldNode metaClassField = metaClassProperty.getField();
138                metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
139    
140                FieldExpression metaClassVar = new FieldExpression(metaClassField);
141                IfStatement initMetaClassField =
142                    new IfStatement(
143                        new BooleanExpression(
144                            new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
145                        new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
146                        EmptyStatement.INSTANCE);
147    
148                node.addSyntheticMethod(
149                    "getMetaClass",
150                    ACC_PUBLIC,
151                    MetaClass.class.getName(),
152                    Parameter.EMPTY_ARRAY,
153                    new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)}));
154    
155                // @todo we should check if the base class implements the invokeMethod method
156    
157                // lets add the invokeMethod implementation
158                String superClass = node.getSuperClass();
159                boolean addDelegateObject =
160                    (node instanceof InnerClassNode && superClass.equals(Closure.class.getName()))
161                        || superClass.equals(GString.class.getName());
162    
163                // don't do anything as the base class implements the invokeMethod
164                if (!addDelegateObject) {
165                    node.addSyntheticMethod(
166                        "invokeMethod",
167                        ACC_PUBLIC,
168                        Object.class.getName(),
169                        new Parameter[] {
170                            new Parameter(String.class.getName(), "method"),
171                            new Parameter(Object.class.getName(), "arguments")},
172                        new BlockStatement(
173                            new Statement[] {
174                                initMetaClassField,
175                                new ReturnStatement(
176                                    new MethodCallExpression(
177                                        metaClassVar,
178                                        "invokeMethod",
179                                        new ArgumentListExpression(
180                                            new Expression[] {
181                                                VariableExpression.THIS_EXPRESSION,
182                                                new VariableExpression("method"),
183                                                new VariableExpression("arguments")})))
184                    }));
185    
186                    if (!node.isScript()) {
187                        node.addSyntheticMethod(
188                            "getProperty",
189                            ACC_PUBLIC,
190                            Object.class.getName(),
191                            new Parameter[] { new Parameter(String.class.getName(), "property")},
192                            new BlockStatement(
193                                new Statement[] {
194                                    initMetaClassField,
195                                    new ReturnStatement(
196                                        new MethodCallExpression(
197                                            metaClassVar,
198                                            "getProperty",
199                                            new ArgumentListExpression(
200                                                new Expression[] {
201                                                    VariableExpression.THIS_EXPRESSION,
202                                                    new VariableExpression("property")})))
203                        }));
204    
205                        node.addSyntheticMethod(
206                            "setProperty",
207                            ACC_PUBLIC,
208                            "void",
209                            new Parameter[] {
210                                new Parameter(String.class.getName(), "property"),
211                                new Parameter(Object.class.getName(), "value")},
212                            new BlockStatement(
213                                new Statement[] {
214                                    initMetaClassField,
215                                    new ExpressionStatement(
216                                        new MethodCallExpression(
217                                            metaClassVar,
218                                            "setProperty",
219                                            new ArgumentListExpression(
220                                                new Expression[] {
221                                                    VariableExpression.THIS_EXPRESSION,
222                                                    new VariableExpression("property"),
223                                                    new VariableExpression("value")})))
224                        }));
225                    }
226                }
227            }
228    
229            if (node.getDeclaredConstructors().isEmpty()) {
230                ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
231                constructor.setSynthetic(true);
232                node.addConstructor(constructor);
233            }
234            
235            if (!(node instanceof InnerClassNode)) {// add a static timestamp field to the class
236                FieldNode timeTagField = new FieldNode(
237                        Verifier.__TIMESTAMP,
238                        Modifier.PUBLIC | Modifier.STATIC,
239                        "java.lang.Long",
240                        //"",
241                        node.getName(),
242                        new ConstantExpression(new Long(System.currentTimeMillis())));
243                // alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
244                timeTagField.setSynthetic(true);
245                node.addField(timeTagField);
246            }
247    
248            addFieldInitialization(node);
249    
250            node.visitContents(this);
251        }
252        public void visitConstructor(ConstructorNode node) {
253            CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
254                boolean firstMethodCall = true;
255                String type=null;
256                public void visitMethodCallExpression(MethodCallExpression call) {
257                    if (!firstMethodCall) return;
258                    firstMethodCall = false;
259                    String name = call.getMethod();
260                    if (!name.equals("super") && !name.equals("this")) return;
261                    type=name;
262                    call.getArguments().visit(this);
263                    type=null;
264                }
265                public void visitVariableExpression(VariableExpression expression) {
266                    if (type==null) return;
267                    String name = expression.getVariable();
268                    if (!name.equals("this") && !name.equals("super")) return;
269                    throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
270                }            
271            };
272            Statement s = node.getCode();
273            //todo why can a statement can be null?
274            if (s == null) return;
275            s.visit(checkSuper);
276        }
277    
278        public void visitMethod(MethodNode node) {
279            this.methodNode = node;
280            Statement statement = node.getCode();
281            if (!node.isVoidMethod()) {
282                if (statement instanceof ExpressionStatement) {
283                    ExpressionStatement expStmt = (ExpressionStatement) statement;
284                    node.setCode(new ReturnStatement(expStmt.getExpression()));
285                }
286                else if (statement instanceof BlockStatement) {
287                    BlockStatement block = (BlockStatement) statement;
288    
289                    // lets copy the list so we create a new block
290                    List list = new ArrayList(block.getStatements());
291                    if (!list.isEmpty()) {
292                        int idx = list.size() - 1;
293                        Statement last = (Statement) list.get(idx);
294                        if (last instanceof ExpressionStatement) {
295                            ExpressionStatement expStmt = (ExpressionStatement) last;
296                            list.set(idx, new ReturnStatement(expStmt.getExpression()));
297                        }
298                        else if (!(last instanceof ReturnStatement)) {
299                            list.add(new ReturnStatement(ConstantExpression.NULL));
300                        }
301                    }
302                    else {
303                        list.add(new ReturnStatement(ConstantExpression.NULL));
304                    }
305    
306                    node.setCode(new BlockStatement(filterStatements(list)));
307                }
308            }
309            else if (!node.isAbstract()) {
310                    BlockStatement newBlock = new BlockStatement();
311                if (statement instanceof BlockStatement) {
312                    newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
313                }
314                else {
315                    newBlock.addStatement(filterStatement(statement));
316                }
317                newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
318                node.setCode(newBlock);
319            }
320            if (node.getName().equals("main") && node.isStatic()) {
321                Parameter[] params = node.getParameters();
322                if (params.length == 1) {
323                    Parameter param = params[0];
324                    if (param.getType() == null || param.getType().equals("java.lang.Object")) {
325                        param.setType("java.lang.String[]");
326                    }
327                }
328            }
329            statement = node.getCode();
330            if (statement!=null) statement.visit(new VerifierCodeVisitor(this));
331        }
332    
333        public void visitField(FieldNode node) {
334        }
335    
336        public void visitProperty(PropertyNode node) {
337            String name = node.getName();
338            FieldNode field = node.getField();
339    
340            
341            String getterPrefix = "get";
342            if ("boolean".equals(node.getType())) {
343                getterPrefix = "is";
344            }
345            String getterName = getterPrefix + capitalize(name);
346            String setterName = "set" + capitalize(name);
347    
348            Statement getterBlock = node.getGetterBlock();
349            if (getterBlock == null) {
350                if (!node.isPrivate() && classNode.getGetterMethod(getterName) == null) {
351                    getterBlock = createGetterBlock(node, field);
352                }
353            }
354            Statement setterBlock = node.getGetterBlock();
355            if (setterBlock == null) {
356                if (!node.isPrivate() && classNode.getSetterMethod(setterName) == null) {
357                    setterBlock = createSetterBlock(node, field);
358                }
359            }
360    
361            if (getterBlock != null) {
362                MethodNode getter =
363                    new MethodNode(getterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
364                getter.setSynthetic(true);
365                classNode.addMethod(getter);
366                visitMethod(getter);
367    
368                if ("java.lang.Boolean".equals(node.getType())) {
369                    String secondGetterName = "is" + capitalize(name);
370                    MethodNode secondGetter =
371                        new MethodNode(secondGetterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, getterBlock);
372                    secondGetter.setSynthetic(true);
373                    classNode.addMethod(secondGetter);
374                    visitMethod(secondGetter);
375                }
376            }
377            if (setterBlock != null) {
378                Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
379                MethodNode setter =
380                    new MethodNode(setterName, node.getModifiers(), "void", setterParameterTypes, setterBlock);
381                setter.setSynthetic(true);
382                classNode.addMethod(setter);
383                visitMethod(setter);
384            }
385        }
386    
387        // 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.hasDefaultValue()) {
405                            paramValues.add(new Integer(i));
406                            paramValues.add(parameter.getDefaultValue());
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].hasDefaultValue()) {
418                                arguments.addExpression(parameters[i].getDefaultValue());
419                                k++;
420                            }
421                            else if (parameters[i] != null && parameters[i].hasDefaultValue()) {
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].hasDefaultValue()) {
462                    newParams[index++] = parameters[i];
463                    arguments.addExpression(new VariableExpression(parameters[i].getName()));
464                }
465                else {
466                    arguments.addExpression(parameters[i].getDefaultValue());
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].getDefaultValue();
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.getInitialValueExpression();
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    }