1 34 35 package org.codehaus.groovy.classgen; 36 37 import java.lang.reflect.Modifier ; 38 import java.lang.reflect.Field ; 39 import java.lang.reflect.Method ; 40 import java.util.HashMap ; 41 import java.util.HashSet ; 42 import java.util.Iterator ; 43 import java.util.Set ; 44 import java.util.List ; 45 import org.codehaus.groovy.ast.ASTNode; 46 import org.codehaus.groovy.ast.ClassNode; 47 import org.codehaus.groovy.ast.CodeVisitorSupport; 48 import org.codehaus.groovy.ast.CompileUnit; 49 import org.codehaus.groovy.ast.ConstructorNode; 50 import org.codehaus.groovy.ast.FieldNode; 51 import org.codehaus.groovy.ast.GroovyClassVisitor; 52 import org.codehaus.groovy.ast.MethodNode; 53 import org.codehaus.groovy.ast.Parameter; 54 import org.codehaus.groovy.ast.PropertyNode; 55 import org.codehaus.groovy.ast.expr.ClosureExpression; 56 import org.codehaus.groovy.ast.expr.DeclarationExpression; 57 import org.codehaus.groovy.ast.expr.Expression; 58 import org.codehaus.groovy.ast.expr.FieldExpression; 59 import org.codehaus.groovy.ast.expr.PropertyExpression; 60 import org.codehaus.groovy.ast.expr.VariableExpression; 61 import org.codehaus.groovy.ast.stmt.BlockStatement; 62 import org.codehaus.groovy.ast.stmt.CatchStatement; 63 import org.codehaus.groovy.ast.stmt.DoWhileStatement; 64 import org.codehaus.groovy.ast.stmt.ForStatement; 65 import org.codehaus.groovy.ast.stmt.Statement; 66 import org.codehaus.groovy.ast.stmt.WhileStatement; 67 import org.codehaus.groovy.control.SourceUnit; 68 import org.codehaus.groovy.control.messages.SyntaxErrorMessage; 69 import org.codehaus.groovy.syntax.SyntaxException; 70 71 public class JSRVariableScopeCodeVisitor extends CodeVisitorSupport implements GroovyClassVisitor { 72 73 private static class Var { 74 boolean isStatic=false, isFinal=false, isDynamicTyped=false; 76 String name, type=null; 77 Class typeClass=null; 78 boolean isInStaticContext=false; 79 80 public boolean equals(Object o){ 81 Var v = (Var) o; 82 return v.name.equals(name); 83 } 84 85 public int hashCode() { 86 return name.hashCode(); 87 } 88 89 public Var(String name) { 90 this.name=name; 93 } 94 95 public Var(VariableExpression ve, VarScope scope) { 96 name = ve.getVariable(); 97 if(ve.isDynamic()) { 98 isDynamicTyped=true; 99 } 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 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 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 if ("true".equals(System.getProperty("groovy.jsr.check.rule.jrose"))) { 234 jroseRule=true; 235 } 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 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 VarScope scope = currentScope; 262 currentScope = new VarScope(currentScope); 263 super.visitWhileLoop(loop); 264 currentScope = scope; 265 } 266 267 public void visitDoWhileLoop(DoWhileStatement loop) { 268 VarScope scope = currentScope; 270 currentScope = new VarScope(currentScope); 271 super.visitDoWhileLoop(loop); 272 currentScope = scope; 273 } 274 275 public void visitDeclarationExpression(DeclarationExpression expression) { 276 expression.getRightExpression().visit(this); 279 VariableExpression vex = expression.getVariableExpression(); 281 if (!jroseRule && "it".equals(vex.getVariable())) { 282 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 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 addError(msg.toString(), expr); 335 break; 336 } 337 } 338 currentScope.declares.put(var.name,var); 340 var.isInStaticContext = currentScope.isInStaticContext; 341 } 342 343 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 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 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 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 = scope.parent; 516 } 517 518 VarScope end = scope; 519 520 if (scope == null) { 521 ClassNode vn = unit.getClass(var.name); 523 if (vn==null) { 527 declare(var,expression); 528 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 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 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 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 try { 590 addVarNames(node); 591 addVarNames(node.getOuterClass(), currentScope.visibles, true); 592 addVarNames(node.getSuperClass(), currentScope.visibles, true); 593 } catch (ClassNotFoundException cnfe) { 594 cnfe.printStackTrace(); 598 } 599 600 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 addVarNames(c.getName(), refs, visitParent); 614 } 615 616 private void addVarNames(ClassNode cn) { 617 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 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 744 } 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 763 HashMap declares = currentScope.declares; 764 Parameter[] parameters = node.getParameters(); 765 for (int i = 0; i < parameters.length; i++) { 766 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 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 } 824 | Popular Tags |