001 /* 002 * $Id: JSRVariableScopeCodeVisitor.java,v 1.16 2005/06/13 20:50:50 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 416 throw new AssertionError(false); 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 checkClassForOverwritingFinal(ClassNode cn) { 431 ClassNode superCN = cn.getSuperClassNode(); 432 if (superCN==null) return; 433 if (!Modifier.isFinal(superCN.getModifiers())) return; 434 StringBuffer msg = new StringBuffer(); 435 msg.append("you are not allowed to overwrite the final class "); 436 msg.append(superCN.getName()); 437 msg.append("."); 438 addError(msg.toString(),cn); 439 440 } 441 442 private void checkMethodsForOverwritingFinal(ClassNode cn) { 443 List l = cn.getMethods(); 444 for (Iterator cnIter = l.iterator(); cnIter.hasNext();) { 445 MethodNode method =(MethodNode) cnIter.next(); 446 Parameter[] parameters = method.getParameters(); 447 for (ClassNode superCN = cn.getSuperClassNode(); superCN!=null; superCN=superCN.getSuperClassNode()){ 448 List methods = superCN.getMethods(method.getName()); 449 for (Iterator iter = methods.iterator(); iter.hasNext();) { 450 MethodNode m = (MethodNode) iter.next(); 451 Parameter[] np = m.getParameters(); 452 if (!hasEqualParameterTypes(parameters,np)) continue; 453 if (!Modifier.isFinal(m.getModifiers())) return; 454 455 StringBuffer msg = new StringBuffer(); 456 msg.append("you are not allowed to overwrite the final method ").append(method.getName()); 457 msg.append("("); 458 boolean semi = false; 459 for (int i=0; i<parameters.length;i++) { 460 if (semi) { 461 msg.append(","); 462 } else { 463 semi = true; 464 } 465 msg.append(parameters[i].getType()); 466 } 467 msg.append(")"); 468 msg.append(" from class ").append(superCN.getName()); 469 msg.append("."); 470 addError(msg.toString(),method); 471 return; 472 } 473 } 474 } 475 } 476 477 private void checkVariableContextAccess(Var v, Expression expr) { 478 if (v.isInStaticContext || !currentScope.isInStaticContext) return; 479 480 String msg = v.name+ 481 " is declared in a dynamic context, but you tried to"+ 482 " access it from a static context."; 483 addError(msg,expr); 484 485 // decalre a static variable to be able to continue the check 486 Var v2 = new Var(v); 487 v2.isInStaticContext = true; 488 currentScope.declares.put(v2.name,v2); 489 } 490 491 private Var checkVariableNameForDeclaration(VariableExpression expression) { 492 if (expression == VariableExpression.THIS_EXPRESSION) return null; 493 String name = expression.getVariable(); 494 return checkVariableNameForDeclaration(name,expression); 495 } 496 497 private Var checkVariableNameForDeclaration(String name, Expression expression) { 498 Var var = new Var(name); 499 500 // TODO: this line is not working 501 // if (expression==VariableExpression.SUPER_EXPRESSION) return; 502 if ("super".equals(var.name) || "this".equals(var.name)) return null; 503 504 VarScope scope = currentScope; 505 while (scope != null) { 506 if (scope.declares.get(var.name)!=null) { 507 var = (Var) scope.declares.get(var.name); 508 break; 509 } 510 if (scope.visibles.get(var.name)!=null) { 511 var = (Var) scope.visibles.get(var.name); 512 break; 513 } 514 // scope.getReferencedVariables().add(name); 515 scope = scope.parent; 516 } 517 518 VarScope end = scope; 519 520 if (scope == null) { 521 //TODO add a check to be on the lhs! 522 ClassNode vn = unit.getClass(var.name); 523 // vn==null means there is no class of that name 524 // note: we need to do this check because it's possible in groovy to access 525 // Classes without the .class known from Java. Example: def type = String; 526 if (vn==null) { 527 declare(var,expression); 528 // don't create an error when inside a script body 529 if (!scriptMode) addError("The variable " + var.name + 530 " is undefined in the current scope", expression); 531 } 532 } else { 533 scope = currentScope; 534 while (scope != end) { 535 scope.visibles.put(var.name,var); 536 scope = scope.parent; 537 } 538 } 539 540 return var; 541 } 542 543 public void visitClosureExpression(ClosureExpression expression) { 544 VarScope scope = currentScope; 545 currentScope = new VarScope(false,currentScope,scope.isInStaticContext); 546 547 // TODO: set scope 548 // expression.setVarScope(currentScope); 549 550 if (expression.isParameterSpecified()) { 551 Parameter[] parameters = expression.getParameters(); 552 for (int i = 0; i < parameters.length; i++) { 553 declare(new Var(parameters[i],scope.isInStaticContext),expression); 554 } 555 } else { 556 Var var = new Var("it"); 557 var.isInStaticContext = scope.isInStaticContext; 558 // TODO: when to add "it" and when not? 559 // John's rule is to add it only to the closures using 'it' 560 // and only to the closure itself, not to subclosures 561 if (jroseRule) { 562 JRoseCheck check = new JRoseCheck(); 563 expression.visit(check); 564 if (check.itUsed) declare(var,expression); 565 } else { 566 currentScope.declares.put("it",var); 567 } 568 } 569 570 // currentScope = new VarScope(currentScope); 571 super.visitClosureExpression(expression); 572 currentScope = scope; 573 } 574 575 public void visitClass(ClassNode node) { 576 checkClassForOverwritingFinal(node); 577 checkMethodsForOverwritingFinal(node); 578 VarScope scope = currentScope; 579 currentScope = new VarScope(true,currentScope,false); 580 boolean scriptModeBackup = scriptMode; 581 scriptMode = node.isScript(); 582 ClassNode classBackup = currentClass; 583 currentClass = node; 584 585 HashMap declares = currentScope.declares; 586 // first pass, add all possible variable names (properies and fields) 587 // TODO: handle interfaces 588 // TODO: handle static imports 589 try { 590 addVarNames(node); 591 addVarNames(node.getOuterClass(), currentScope.visibles, true); 592 addVarNames(node.getSuperClass(), currentScope.visibles, true); 593 } catch (ClassNotFoundException cnfe) { 594 //TODO: handle this case properly 595 // throw new GroovyRuntimeException("couldn't find super 596 // class",cnfe); 597 cnfe.printStackTrace(); 598 } 599 600 // second pass, check contents 601 node.visitContents(this); 602 603 currentClass = classBackup; 604 currentScope = scope; 605 scriptMode = scriptModeBackup; 606 } 607 608 private void addVarNames(Class c, HashMap refs, boolean visitParent) 609 throws ClassNotFoundException 610 { 611 if (c == null) return; 612 // to prefer compiled code try to get a ClassNode via name first 613 addVarNames(c.getName(), refs, visitParent); 614 } 615 616 private void addVarNames(ClassNode cn) { 617 //TODO: change test for currentScope.declares 618 //TODO: handle indexed properties 619 if (cn == null) return; 620 List l = cn.getFields(); 621 Set fields = new HashSet(); 622 for (Iterator iter = l.iterator(); iter.hasNext();) { 623 FieldNode f = (FieldNode) iter.next(); 624 Var var = new Var(f); 625 if (fields.contains(var)) { 626 declare(var,f); 627 } else { 628 fields.add(var); 629 currentScope.declares.put(var.name,var); 630 } 631 } 632 633 //TODO: ignore double delcaration of methods for the moment 634 l = cn.getMethods(); 635 Set setter = new HashSet(); 636 Set getter = new HashSet(); 637 for (Iterator iter = l.iterator(); iter.hasNext();) { 638 MethodNode f =(MethodNode) iter.next(); 639 String methodName = f.getName(); 640 String pName = getPropertyName(methodName); 641 if (pName == null) continue; 642 Var var = new Var(pName,f); 643 currentScope.declares.put(var.name,var); 644 } 645 646 l = cn.getProperties(); 647 Set props = new HashSet(); 648 for (Iterator iter = l.iterator(); iter.hasNext();) { 649 PropertyNode f = (PropertyNode) iter.next(); 650 Var var = new Var(f); 651 if (props.contains(var)) { 652 declare(var,f); 653 } else { 654 props.add(var); 655 currentScope.declares.put(var.name,var); 656 } 657 } 658 } 659 660 private void addVarNames(ClassNode cn, HashMap refs, boolean visitParent) 661 throws ClassNotFoundException { 662 if (cn == null) return; 663 List l = cn.getFields(); 664 for (Iterator iter = l.iterator(); iter.hasNext();) { 665 FieldNode f = (FieldNode) iter.next(); 666 if (visitParent && Modifier.isPrivate(f.getModifiers())) 667 continue; 668 refs.put(f.getName(),new Var(f)); 669 } 670 l = cn.getMethods(); 671 for (Iterator iter = l.iterator(); iter.hasNext();) { 672 MethodNode f = (MethodNode) iter.next(); 673 if (visitParent && Modifier.isPrivate(f.getModifiers())) 674 continue; 675 String name = getPropertyName(f.getName()); 676 if (name == null) continue; 677 refs.put(name, new Var(name,f)); 678 } 679 680 l = cn.getProperties(); 681 for (Iterator iter = l.iterator(); iter.hasNext();) { 682 PropertyNode f = (PropertyNode) iter.next(); 683 if (visitParent && Modifier.isPrivate(f.getModifiers())) 684 continue; 685 refs.put(f.getName(),new Var(f)); 686 } 687 688 if (!visitParent) return; 689 690 addVarNames(cn.getSuperClass(), refs, visitParent); 691 MethodNode enclosingMethod = cn.getEnclosingMethod(); 692 693 if (enclosingMethod == null) return; 694 695 Parameter[] params = enclosingMethod.getParameters(); 696 for (int i = 0; i < params.length; i++) { 697 refs.put(params[i].getName(),new Var(params[i],enclosingMethod.isStatic())); 698 } 699 700 if (visitParent) 701 addVarNames(enclosingMethod.getDeclaringClass(), refs, visitParent); 702 703 addVarNames(cn.getOuterClass(), refs, visitParent); 704 } 705 706 private void addVarNames(String superclassName, HashMap refs, boolean visitParent) 707 throws ClassNotFoundException 708 { 709 710 if (superclassName == null) return; 711 712 ClassNode cn = unit.getClass(superclassName); 713 if (cn != null) { 714 addVarNames(cn, refs, visitParent); 715 return; 716 } 717 718 Class c = unit.getClassLoader().loadClass(superclassName); 719 Field[] fields = c.getFields(); 720 for (int i = 0; i < fields.length; i++) { 721 Field f = fields[i]; 722 if (visitParent && Modifier.isPrivate(f.getModifiers())) 723 continue; 724 refs.put(f.getName(),new Var(f)); 725 } 726 727 Method[] methods = c.getMethods(); 728 for (int i = 0; i < methods.length; i++) { 729 Method m = methods[i]; 730 if (visitParent && Modifier.isPrivate(m.getModifiers())) 731 continue; 732 String name = getPropertyName(m.getName()); 733 if (name == null) continue; 734 refs.put(name,new Var(name,m)); 735 } 736 737 if (!visitParent) return; 738 739 addVarNames(c.getSuperclass(), refs, visitParent); 740 741 // it's not possible to know the variable names used for an enclosing 742 // method 743 744 // addVarNames(c.getEnclosingClass(),refs,visitParent); 745 } 746 747 private String getPropertyName(String name) { 748 if (!(name.startsWith("set") || name.startsWith("get"))) return null; 749 String pname = name.substring(3); 750 if (pname.length() == 0) return null; 751 String s = pname.substring(0, 1).toLowerCase(); 752 String rest = pname.substring(1); 753 return s + rest; 754 } 755 756 public void visitConstructor(ConstructorNode node) { 757 VarScope scope = currentScope; 758 currentScope = new VarScope(currentScope); 759 760 // TODO: set scope 761 // node.setVarScope(currentScope); 762 763 HashMap declares = currentScope.declares; 764 Parameter[] parameters = node.getParameters(); 765 for (int i = 0; i < parameters.length; i++) { 766 // a constructor is never static 767 declare(new Var(parameters[i],false),node); 768 } 769 currentScope = new VarScope(currentScope); 770 Statement code = node.getCode(); 771 if (code != null) code.visit(this); 772 currentScope = scope; 773 } 774 775 public void visitMethod(MethodNode node) { 776 checkAbstractDeclaration(node); 777 778 VarScope scope = currentScope; 779 currentScope = new VarScope(currentScope,node.isStatic()); 780 781 // TODO: set scope 782 // node.setVarScope(currentScope); 783 784 HashMap declares = currentScope.declares; 785 Parameter[] parameters = node.getParameters(); 786 for (int i = 0; i < parameters.length; i++) { 787 declares.put(parameters[i].getName(),new Var(parameters[i],node.isStatic())); 788 } 789 790 currentScope = new VarScope(currentScope); 791 Statement code = node.getCode(); 792 if (code!=null) code.visit(this); 793 currentScope = scope; 794 } 795 796 public void visitField(FieldNode node) { 797 Expression init = node.getInitialValueExpression(); 798 if (init != null) init.visit(this); 799 } 800 801 public void visitProperty(PropertyNode node) { 802 Statement statement = node.getGetterBlock(); 803 if (statement != null) statement.visit(this); 804 805 statement = node.getSetterBlock(); 806 if (statement != null) statement.visit(this); 807 808 Expression init = node.getInitialValueExpression(); 809 if (init != null) init.visit(this); 810 } 811 812 public void visitPropertyExpression(PropertyExpression expression) { 813 814 } 815 816 public void visitCatchStatement(CatchStatement statement) { 817 VarScope scope = currentScope; 818 currentScope = new VarScope(currentScope); 819 declare(new Var(statement.getVariable()), statement); 820 super.visitCatchStatement(statement); 821 currentScope = scope; 822 } 823 }