001 /* 002 * $Id: JSRVariableScopeCodeVisitor.java,v 1.18 2005/07/06 13:44:32 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 that the 008 * following conditions are met: 1. Redistributions of source code must retain 009 * copyright statements and notices. Redistributions must also contain a copy 010 * of this document. 2. Redistributions in binary form must reproduce the above 011 * copyright notice, this list of conditions and the following disclaimer in 012 * the documentation and/or other materials provided with the distribution. 3. 013 * The name "groovy" must not be used to endorse or promote products derived 014 * from this Software without prior written permission of The Codehaus. For 015 * written permission, please contact info@codehaus.org. 4. Products derived 016 * from this Software may not be called "groovy" nor may "groovy" appear in 017 * their names without prior written permission of The Codehaus. "groovy" is a 018 * registered trademark of The Codehaus. 5. Due credit should be given to The 019 * Codehaus - http://groovy.codehaus.org/ 020 * 021 * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY 022 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 023 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 024 * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR 025 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 026 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 027 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 028 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 029 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 030 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 031 * DAMAGE. 032 * 033 */ 034 035 package org.codehaus.groovy.classgen; 036 037 import java.lang.reflect.Modifier; 038 import java.lang.reflect.Field; 039 import java.lang.reflect.Method; 040 import java.util.HashMap; 041 import java.util.HashSet; 042 import java.util.Iterator; 043 import java.util.Set; 044 import java.util.List; 045 import org.codehaus.groovy.ast.ASTNode; 046 import org.codehaus.groovy.ast.ClassNode; 047 import org.codehaus.groovy.ast.CodeVisitorSupport; 048 import org.codehaus.groovy.ast.CompileUnit; 049 import org.codehaus.groovy.ast.ConstructorNode; 050 import org.codehaus.groovy.ast.FieldNode; 051 import org.codehaus.groovy.ast.GroovyClassVisitor; 052 import org.codehaus.groovy.ast.MethodNode; 053 import org.codehaus.groovy.ast.Parameter; 054 import org.codehaus.groovy.ast.PropertyNode; 055 import org.codehaus.groovy.ast.expr.ClosureExpression; 056 import org.codehaus.groovy.ast.expr.DeclarationExpression; 057 import org.codehaus.groovy.ast.expr.Expression; 058 import org.codehaus.groovy.ast.expr.FieldExpression; 059 import org.codehaus.groovy.ast.expr.PropertyExpression; 060 import org.codehaus.groovy.ast.expr.VariableExpression; 061 import org.codehaus.groovy.ast.stmt.BlockStatement; 062 import org.codehaus.groovy.ast.stmt.CatchStatement; 063 import org.codehaus.groovy.ast.stmt.DoWhileStatement; 064 import org.codehaus.groovy.ast.stmt.ForStatement; 065 import org.codehaus.groovy.ast.stmt.Statement; 066 import org.codehaus.groovy.ast.stmt.WhileStatement; 067 import org.codehaus.groovy.control.SourceUnit; 068 import org.codehaus.groovy.control.messages.SyntaxErrorMessage; 069 import org.codehaus.groovy.syntax.SyntaxException; 070 071 public class JSRVariableScopeCodeVisitor extends CodeVisitorSupport implements GroovyClassVisitor { 072 073 private static class Var { 074 //TODO: support final and native 075 boolean isStatic=false, isFinal=false, isDynamicTyped=false; 076 String name, type=null; 077 Class typeClass=null; 078 boolean isInStaticContext=false; 079 080 public boolean equals(Object o){ 081 Var v = (Var) o; 082 return v.name.equals(name); 083 } 084 085 public int hashCode() { 086 return name.hashCode(); 087 } 088 089 public Var(String name) { 090 // a Variable without type and other modifiers 091 // make it dynamic type, non final and non static 092 this.name=name; 093 } 094 095 public Var(VariableExpression ve, VarScope scope) { 096 name = ve.getVariable(); 097 if(ve.isDynamic()) { 098 isDynamicTyped=true; 099 } else { 100 type = ve.getType(); 101 typeClass = ve.getTypeClass(); 102 } 103 isInStaticContext = scope.isInStaticContext; 104 } 105 106 public Var(Parameter par, boolean staticContext) { 107 name = par.getName(); 108 if (par.isDynamicType()) { 109 isDynamicTyped=true; 110 } else { 111 type = par.getType(); 112 } 113 isInStaticContext = staticContext; 114 } 115 116 public Var(FieldNode f) { 117 name = f.getName(); 118 if (f.isDynamicType()) { 119 isDynamicTyped=true; 120 } else { 121 type = f.getType(); 122 } 123 isStatic=f.isStatic(); 124 isInStaticContext = isStatic; 125 } 126 127 public Var(String pName, MethodNode f) { 128 name = pName; 129 if (f.isDynamicReturnType()) { 130 isDynamicTyped=true; 131 } else { 132 type = f.getReturnType(); 133 } 134 isStatic=f.isStatic(); 135 isInStaticContext = isStatic; 136 } 137 138 public Var(String pName, Method m) { 139 name = pName; 140 typeClass = m.getReturnType(); 141 isStatic=Modifier.isStatic(m.getModifiers()); 142 isFinal=Modifier.isFinal(m.getModifiers()); 143 isInStaticContext = isStatic; 144 } 145 146 public Var(PropertyNode f) { 147 //TODO: no static? What about read-/write-only? abstract? 148 isInStaticContext = false; 149 name = f.getName(); 150 if (f.isDynamicType()) { 151 isDynamicTyped=true; 152 } else { 153 type = f.getType(); 154 } 155 isInStaticContext = false; 156 } 157 158 public Var(Field f) { 159 name = f.getName(); 160 typeClass = f.getType(); 161 isStatic=Modifier.isStatic(f.getModifiers()); 162 isInStaticContext = isStatic; 163 isFinal=Modifier.isFinal(f.getModifiers()); 164 isInStaticContext = isStatic; 165 } 166 167 public Var(Var v) { 168 isStatic=v.isStatic; 169 isFinal=v.isFinal; 170 isDynamicTyped=v.isDynamicTyped; 171 name=v.name; 172 type=v.type; 173 typeClass=v.typeClass; 174 isInStaticContext=v.isInStaticContext; 175 } 176 } 177 178 private static class VarScope { 179 boolean isClass=true; 180 boolean isInStaticContext = false; 181 182 VarScope parent; 183 HashMap declares = new HashMap(); 184 HashMap visibles = new HashMap(); 185 186 public VarScope(boolean isClass, VarScope parent, boolean staticContext) { 187 this.isClass=isClass; 188 this.parent = parent; 189 isInStaticContext = staticContext; 190 } 191 192 public VarScope(VarScope parent, boolean staticContext) { 193 this(false,parent,staticContext); 194 } 195 196 public VarScope(VarScope parent) { 197 this(false,parent,parent!=null?parent.isInStaticContext:false); 198 } 199 } 200 201 private static class JRoseCheck extends CodeVisitorSupport{ 202 boolean closureStarted=false; 203 boolean itUsed=false; 204 205 public void visitClosureExpression(ClosureExpression expression) { 206 // don't visit subclosures if already in a closure 207 if (closureStarted) return; 208 closureStarted=true; 209 Parameter[] param = expression.getParameters(); 210 for (int i=0; i<param.length; i++) { 211 itUsed = (param[i].getName().equals("it")) && closureStarted || itUsed; 212 } 213 super.visitClosureExpression(expression); 214 } 215 216 public void visitVariableExpression(VariableExpression expression) { 217 itUsed = (expression.getVariable().equals("it")) && closureStarted || itUsed; 218 } 219 220 } 221 222 223 private VarScope currentScope = null; 224 private CompileUnit unit; 225 private SourceUnit source; 226 private boolean scriptMode=false; 227 private ClassNode currentClass=null; 228 229 private boolean jroseRule=false; 230 231 public JSRVariableScopeCodeVisitor(VarScope scope, SourceUnit source) { 232 //System.out.println("scope check enabled"); 233 if ("true".equals(System.getProperty("groovy.jsr.check.rule.jrose"))) { 234 jroseRule=true; 235 //System.out.println("jrose check enabled"); 236 } 237 currentScope = scope; 238 this.source = source; 239 if (source.getAST() == null) return; 240 this.unit = source.getAST().getUnit(); 241 } 242 243 public void visitBlockStatement(BlockStatement block) { 244 VarScope scope = currentScope; 245 currentScope = new VarScope(currentScope); 246 super.visitBlockStatement(block); 247 currentScope = scope; 248 } 249 250 public void visitForLoop(ForStatement forLoop) { 251 VarScope scope = currentScope; 252 // TODO: always define a variable here? What about type? 253 currentScope = new VarScope(currentScope); 254 declare(new Var(forLoop.getVariable()), forLoop); 255 super.visitForLoop(forLoop); 256 currentScope = scope; 257 } 258 259 public void visitWhileLoop(WhileStatement loop) { 260 //TODO: check while loop variables 261 VarScope scope = currentScope; 262 currentScope = new VarScope(currentScope); 263 super.visitWhileLoop(loop); 264 currentScope = scope; 265 } 266 267 public void visitDoWhileLoop(DoWhileStatement loop) { 268 //TODO: still existant? 269 VarScope scope = currentScope; 270 currentScope = new VarScope(currentScope); 271 super.visitDoWhileLoop(loop); 272 currentScope = scope; 273 } 274 275 public void visitDeclarationExpression(DeclarationExpression expression) { 276 // visit right side first to avoid the usage of a 277 // variable before its declaration 278 expression.getRightExpression().visit(this); 279 // no need to visit left side, just get the variable name 280 VariableExpression vex = expression.getVariableExpression(); 281 if (!jroseRule && "it".equals(vex.getVariable())) { 282 // we are not in jrose mode, so don't allow variables 283 // of the name 'it' 284 addError("'it' is a keyword in this mode.",vex); 285 } else { 286 declare(vex); 287 } 288 } 289 290 private void addError(String msg, ASTNode expr) { 291 int line = expr.getLineNumber(); 292 int col = expr.getColumnNumber(); 293 source.getErrorCollector().addErrorAndContinue( 294 new SyntaxErrorMessage(new SyntaxException(msg + '\n', line, col), source) 295 ); 296 } 297 298 private void declare(VariableExpression expr) { 299 declare(new Var(expr,currentScope),expr); 300 } 301 302 private void declare(Var var, ASTNode expr) { 303 String scopeType = "scope"; 304 String variableType = "variable"; 305 306 if (expr.getClass()==FieldNode.class){ 307 scopeType = "class"; 308 variableType = "field"; 309 } else if (expr.getClass()==PropertyNode.class){ 310 scopeType = "class"; 311 variableType = "property"; 312 } 313 314 StringBuffer msg = new StringBuffer(); 315 msg.append("The current ").append(scopeType); 316 msg.append(" does already contain a ").append(variableType); 317 msg.append(" of the name ").append(var.name); 318 319 if (currentScope.declares.get(var.name)!=null) { 320 addError(msg.toString(),expr); 321 return; 322 } 323 324 //TODO: this case is not visited I think 325 if (currentScope.isClass) { 326 currentScope.declares.put(var.name,var); 327 } 328 329 for (VarScope scope = currentScope.parent; scope!=null; scope = scope.parent) { 330 HashMap declares = scope.declares; 331 if (scope.isClass) break; 332 if (declares.get(var.name)!=null) { 333 // variable already declared 334 addError(msg.toString(), expr); 335 break; 336 } 337 } 338 // declare the variable even if there was an error to allow more checks 339 currentScope.declares.put(var.name,var); 340 var.isInStaticContext = currentScope.isInStaticContext; 341 } 342 343 /* 344 * public void visitBinaryExpression(BinaryExpression expression) { 345 * // evaluate right first because for an expression like "def a = a" 346 * // we need first to know if the a on the rhs is defined, before 347 * // defining a new a for the lhs 348 * 349 * Expression right = expression.getRightExpression(); 350 * 351 * right.visit(this); 352 * 353 * Expression left = expression.getLeftExpression(); 354 * 355 * left.visit(this); 356 * } 357 */ 358 359 public void visitVariableExpression(VariableExpression expression) { 360 String name = expression.getVariable(); 361 Var v = checkVariableNameForDeclaration(name,expression); 362 if (v==null) return; 363 checkVariableContextAccess(v,expression); 364 } 365 366 367 public void visitFieldExpression(FieldExpression expression) { 368 String name = expression.getFieldName(); 369 //TODO: change that to get the correct scope 370 Var v = checkVariableNameForDeclaration(name,expression); 371 checkVariableContextAccess(v,expression); 372 } 373 374 private void checkAbstractDeclaration(MethodNode methodNode) { 375 if (!Modifier.isAbstract(methodNode.getModifiers())) return; 376 if (Modifier.isAbstract(currentClass.getModifiers())) return; 377 addError("Can't have an abstract method in a non abstract class." + 378 " The class '" + currentClass.getName() + "' must be declared abstract or the method '" + 379 methodNode.getName() + "' must not be abstract.",methodNode); 380 } 381 382 private String getTypeName(String name) { 383 if (!name.endsWith("[]")) return name; 384 385 String prefix = ""; 386 while (name.endsWith("[]")) { 387 name = name.substring(0,name.length()-2); 388 prefix = "["; 389 } 390 391 if (name.equals("int")) { 392 return prefix + "I"; 393 } 394 else if (name.equals("long")) { 395 return prefix + "J"; 396 } 397 else if (name.equals("short")) { 398 return prefix + "S"; 399 } 400 else if (name.equals("float")) { 401 return prefix + "F"; 402 } 403 else if (name.equals("double")) { 404 return prefix + "D"; 405 } 406 else if (name.equals("byte")) { 407 return prefix + "B"; 408 } 409 else if (name.equals("char")) { 410 return prefix + "C"; 411 } 412 else if (name.equals("boolean")) { 413 return prefix + "Z"; 414 } 415 // no primitive 416 return prefix+"L"+name+";"; 417 } 418 419 private boolean hasEqualParameterTypes(Parameter[] first, Parameter[] second) { 420 if (first.length!=second.length) return false; 421 for (int i=0; i<first.length; i++) { 422 String ft = getTypeName(first[i].getType()); 423 String st = getTypeName(second[i].getType()); 424 if (ft.equals(st)) continue; 425 return false; 426 } 427 return true; 428 } 429 430 private void checkImplementsAndExtends(ClassNode node) { 431 ClassNode cn = node.getSuperClassNode(); 432 if (cn.isInterface()) addError("you are not allowed to extend the Interface "+cn.getName()+", use implements instead", node); 433 String[] interfaces = node.getInterfaces(); 434 for (int i = 0; i < interfaces.length; i++) { 435 cn = node.findClassNode(interfaces[i]); 436 if (!cn.isInterface()) addError ("you are not allowed to implement the Class "+cn.getName()+", use extends instead", node); 437 } 438 } 439 440 private void checkClassForOverwritingFinal(ClassNode cn) { 441 ClassNode superCN = cn.getSuperClassNode(); 442 if (superCN==null) return; 443 if (!Modifier.isFinal(superCN.getModifiers())) return; 444 StringBuffer msg = new StringBuffer(); 445 msg.append("you are not allowed to overwrite the final class "); 446 msg.append(superCN.getName()); 447 msg.append("."); 448 addError(msg.toString(),cn); 449 450 } 451 452 private void checkMethodsForOverwritingFinal(ClassNode cn) { 453 List l = cn.getMethods(); 454 for (Iterator cnIter = l.iterator(); cnIter.hasNext();) { 455 MethodNode method =(MethodNode) cnIter.next(); 456 Parameter[] parameters = method.getParameters(); 457 for (ClassNode superCN = cn.getSuperClassNode(); superCN!=null; superCN=superCN.getSuperClassNode()){ 458 List methods = superCN.getMethods(method.getName()); 459 for (Iterator iter = methods.iterator(); iter.hasNext();) { 460 MethodNode m = (MethodNode) iter.next(); 461 Parameter[] np = m.getParameters(); 462 if (!hasEqualParameterTypes(parameters,np)) continue; 463 if (!Modifier.isFinal(m.getModifiers())) return; 464 465 StringBuffer msg = new StringBuffer(); 466 msg.append("you are not allowed to overwrite the final method ").append(method.getName()); 467 msg.append("("); 468 boolean semi = false; 469 for (int i=0; i<parameters.length;i++) { 470 if (semi) { 471 msg.append(","); 472 } else { 473 semi = true; 474 } 475 msg.append(parameters[i].getType()); 476 } 477 msg.append(")"); 478 msg.append(" from class ").append(superCN.getName()); 479 msg.append("."); 480 addError(msg.toString(),method); 481 return; 482 } 483 } 484 } 485 } 486 487 private void checkVariableContextAccess(Var v, Expression expr) { 488 if (v.isInStaticContext || !currentScope.isInStaticContext) return; 489 490 String msg = v.name+ 491 " is declared in a dynamic context, but you tried to"+ 492 " access it from a static context."; 493 addError(msg,expr); 494 495 // decalre a static variable to be able to continue the check 496 Var v2 = new Var(v); 497 v2.isInStaticContext = true; 498 currentScope.declares.put(v2.name,v2); 499 } 500 501 private Var checkVariableNameForDeclaration(VariableExpression expression) { 502 if (expression == VariableExpression.THIS_EXPRESSION) return null; 503 String name = expression.getVariable(); 504 return checkVariableNameForDeclaration(name,expression); 505 } 506 507 private Var checkVariableNameForDeclaration(String name, Expression expression) { 508 Var var = new Var(name); 509 510 // TODO: this line is not working 511 // if (expression==VariableExpression.SUPER_EXPRESSION) return; 512 if ("super".equals(var.name) || "this".equals(var.name)) return null; 513 514 VarScope scope = currentScope; 515 while (scope != null) { 516 if (scope.declares.get(var.name)!=null) { 517 var = (Var) scope.declares.get(var.name); 518 break; 519 } 520 if (scope.visibles.get(var.name)!=null) { 521 var = (Var) scope.visibles.get(var.name); 522 break; 523 } 524 // scope.getReferencedVariables().add(name); 525 scope = scope.parent; 526 } 527 528 VarScope end = scope; 529 530 if (scope == null) { 531 //TODO add a check to be on the lhs! 532 ClassNode vn = unit.getClass(var.name); 533 // vn==null means there is no class of that name 534 // note: we need to do this check because it's possible in groovy to access 535 // Classes without the .class known from Java. Example: def type = String; 536 if (vn==null) { 537 declare(var,expression); 538 // don't create an error when inside a script body 539 if (!scriptMode) addError("The variable " + var.name + 540 " is undefined in the current scope", expression); 541 } 542 } else { 543 scope = currentScope; 544 while (scope != end) { 545 scope.visibles.put(var.name,var); 546 scope = scope.parent; 547 } 548 } 549 550 return var; 551 } 552 553 public void visitClosureExpression(ClosureExpression expression) { 554 VarScope scope = currentScope; 555 currentScope = new VarScope(false,currentScope,scope.isInStaticContext); 556 557 // TODO: set scope 558 // expression.setVarScope(currentScope); 559 560 if (expression.isParameterSpecified()) { 561 Parameter[] parameters = expression.getParameters(); 562 for (int i = 0; i < parameters.length; i++) { 563 declare(new Var(parameters[i],scope.isInStaticContext),expression); 564 } 565 } else { 566 Var var = new Var("it"); 567 var.isInStaticContext = scope.isInStaticContext; 568 // TODO: when to add "it" and when not? 569 // John's rule is to add it only to the closures using 'it' 570 // and only to the closure itself, not to subclosures 571 if (jroseRule) { 572 JRoseCheck check = new JRoseCheck(); 573 expression.visit(check); 574 if (check.itUsed) declare(var,expression); 575 } else { 576 currentScope.declares.put("it",var); 577 } 578 } 579 580 // currentScope = new VarScope(currentScope); 581 super.visitClosureExpression(expression); 582 currentScope = scope; 583 } 584 585 public void visitClass(ClassNode node) { 586 checkImplementsAndExtends(node); 587 checkClassForOverwritingFinal(node); 588 checkMethodsForOverwritingFinal(node); 589 VarScope scope = currentScope; 590 currentScope = new VarScope(true,currentScope,false); 591 boolean scriptModeBackup = scriptMode; 592 scriptMode = node.isScript(); 593 ClassNode classBackup = currentClass; 594 currentClass = node; 595 596 HashMap declares = currentScope.declares; 597 // first pass, add all possible variable names (properies and fields) 598 // TODO: handle interfaces 599 // TODO: handle static imports 600 try { 601 addVarNames(node); 602 addVarNames(node.getOuterClass(), currentScope.visibles, true); 603 addVarNames(node.getSuperClass(), currentScope.visibles, true); 604 } catch (ClassNotFoundException cnfe) { 605 //TODO: handle this case properly 606 // throw new GroovyRuntimeException("couldn't find super 607 // class",cnfe); 608 cnfe.printStackTrace(); 609 } 610 611 // second pass, check contents 612 node.visitContents(this); 613 614 currentClass = classBackup; 615 currentScope = scope; 616 scriptMode = scriptModeBackup; 617 } 618 619 private void addVarNames(Class c, HashMap refs, boolean visitParent) 620 throws ClassNotFoundException 621 { 622 if (c == null) return; 623 // to prefer compiled code try to get a ClassNode via name first 624 addVarNames(c.getName(), refs, visitParent); 625 } 626 627 private void addVarNames(ClassNode cn) { 628 //TODO: change test for currentScope.declares 629 //TODO: handle indexed properties 630 if (cn == null) return; 631 List l = cn.getFields(); 632 Set fields = new HashSet(); 633 for (Iterator iter = l.iterator(); iter.hasNext();) { 634 FieldNode f = (FieldNode) iter.next(); 635 Var var = new Var(f); 636 if (fields.contains(var)) { 637 declare(var,f); 638 } else { 639 fields.add(var); 640 currentScope.declares.put(var.name,var); 641 } 642 } 643 644 //TODO: ignore double delcaration of methods for the moment 645 l = cn.getMethods(); 646 Set setter = new HashSet(); 647 Set getter = new HashSet(); 648 for (Iterator iter = l.iterator(); iter.hasNext();) { 649 MethodNode f =(MethodNode) iter.next(); 650 String methodName = f.getName(); 651 String pName = getPropertyName(methodName); 652 if (pName == null) continue; 653 Var var = new Var(pName,f); 654 currentScope.declares.put(var.name,var); 655 } 656 657 l = cn.getProperties(); 658 Set props = new HashSet(); 659 for (Iterator iter = l.iterator(); iter.hasNext();) { 660 PropertyNode f = (PropertyNode) iter.next(); 661 Var var = new Var(f); 662 if (props.contains(var)) { 663 declare(var,f); 664 } else { 665 props.add(var); 666 currentScope.declares.put(var.name,var); 667 } 668 } 669 } 670 671 private void addVarNames(ClassNode cn, HashMap refs, boolean visitParent) 672 throws ClassNotFoundException { 673 // note this method is only called for parent classes 674 675 if (cn == null) return; 676 List l = cn.getFields(); 677 for (Iterator iter = l.iterator(); iter.hasNext();) { 678 FieldNode f = (FieldNode) iter.next(); 679 if (visitParent && Modifier.isPrivate(f.getModifiers())) 680 continue; 681 refs.put(f.getName(),new Var(f)); 682 } 683 l = cn.getMethods(); 684 for (Iterator iter = l.iterator(); iter.hasNext();) { 685 MethodNode f = (MethodNode) iter.next(); 686 if (visitParent && Modifier.isPrivate(f.getModifiers())) 687 continue; 688 String name = getPropertyName(f.getName()); 689 if (name == null) continue; 690 refs.put(name, new Var(name,f)); 691 } 692 693 l = cn.getProperties(); 694 for (Iterator iter = l.iterator(); iter.hasNext();) { 695 PropertyNode f = (PropertyNode) iter.next(); 696 if (visitParent && Modifier.isPrivate(f.getModifiers())) 697 continue; 698 refs.put(f.getName(),new Var(f)); 699 } 700 701 if (!visitParent) return; 702 703 addVarNames(cn.getSuperClass(), refs, visitParent); 704 MethodNode enclosingMethod = cn.getEnclosingMethod(); 705 706 if (enclosingMethod == null) return; 707 708 Parameter[] params = enclosingMethod.getParameters(); 709 for (int i = 0; i < params.length; i++) { 710 refs.put(params[i].getName(),new Var(params[i],enclosingMethod.isStatic())); 711 } 712 713 if (visitParent) 714 addVarNames(enclosingMethod.getDeclaringClass(), refs, visitParent); 715 716 addVarNames(cn.getOuterClass(), refs, visitParent); 717 } 718 719 private void addVarNames(String superclassName, HashMap refs, boolean visitParent) 720 throws ClassNotFoundException 721 { 722 723 if (superclassName == null) return; 724 725 ClassNode cn = unit.getClass(superclassName); 726 if (cn != null) { 727 addVarNames(cn, refs, visitParent); 728 return; 729 } 730 731 Class c = unit.getClassLoader().loadClass(superclassName); 732 Field[] fields = c.getFields(); 733 for (int i = 0; i < fields.length; i++) { 734 Field f = fields[i]; 735 if (visitParent && Modifier.isPrivate(f.getModifiers())) 736 continue; 737 refs.put(f.getName(),new Var(f)); 738 } 739 740 Method[] methods = c.getMethods(); 741 for (int i = 0; i < methods.length; i++) { 742 Method m = methods[i]; 743 if (visitParent && Modifier.isPrivate(m.getModifiers())) 744 continue; 745 String name = getPropertyName(m.getName()); 746 if (name == null) continue; 747 refs.put(name,new Var(name,m)); 748 } 749 750 if (!visitParent) return; 751 752 addVarNames(c.getSuperclass(), refs, visitParent); 753 754 // it's not possible to know the variable names used for an enclosing 755 // method 756 757 // addVarNames(c.getEnclosingClass(),refs,visitParent); 758 } 759 760 private String getPropertyName(String name) { 761 if (!(name.startsWith("set") || name.startsWith("get"))) return null; 762 String pname = name.substring(3); 763 if (pname.length() == 0) return null; 764 String s = pname.substring(0, 1).toLowerCase(); 765 String rest = pname.substring(1); 766 return s + rest; 767 } 768 769 public void visitConstructor(ConstructorNode node) { 770 VarScope scope = currentScope; 771 currentScope = new VarScope(currentScope); 772 773 // TODO: set scope 774 // node.setVarScope(currentScope); 775 776 HashMap declares = currentScope.declares; 777 Parameter[] parameters = node.getParameters(); 778 for (int i = 0; i < parameters.length; i++) { 779 // a constructor is never static 780 declare(new Var(parameters[i],false),node); 781 } 782 currentScope = new VarScope(currentScope); 783 Statement code = node.getCode(); 784 if (code != null) code.visit(this); 785 currentScope = scope; 786 } 787 788 public void visitMethod(MethodNode node) { 789 checkAbstractDeclaration(node); 790 791 VarScope scope = currentScope; 792 currentScope = new VarScope(currentScope,node.isStatic()); 793 794 // TODO: set scope 795 // node.setVarScope(currentScope); 796 797 HashMap declares = currentScope.declares; 798 Parameter[] parameters = node.getParameters(); 799 for (int i = 0; i < parameters.length; i++) { 800 declares.put(parameters[i].getName(),new Var(parameters[i],node.isStatic())); 801 } 802 803 currentScope = new VarScope(currentScope); 804 Statement code = node.getCode(); 805 if (code!=null) code.visit(this); 806 currentScope = scope; 807 } 808 809 public void visitField(FieldNode node) { 810 Expression init = node.getInitialValueExpression(); 811 if (init != null) init.visit(this); 812 } 813 814 public void visitProperty(PropertyNode node) { 815 Statement statement = node.getGetterBlock(); 816 if (statement != null) statement.visit(this); 817 818 statement = node.getSetterBlock(); 819 if (statement != null) statement.visit(this); 820 821 Expression init = node.getInitialValueExpression(); 822 if (init != null) init.visit(this); 823 } 824 825 public void visitPropertyExpression(PropertyExpression expression) { 826 827 } 828 829 public void visitCatchStatement(CatchStatement statement) { 830 VarScope scope = currentScope; 831 currentScope = new VarScope(currentScope); 832 declare(new Var(statement.getVariable()), statement); 833 super.visitCatchStatement(statement); 834 currentScope = scope; 835 } 836 }