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 }