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 }