1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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
75 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
91
92 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
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
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
233 if ("true".equals(System.getProperty("groovy.jsr.check.rule.jrose"))) {
234 jroseRule=true;
235
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
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
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
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
277
278 expression.getRightExpression().visit(this);
279
280 VariableExpression vex = expression.getVariableExpression();
281 if (!jroseRule && "it".equals(vex.getVariable())) {
282
283
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
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
334 addError(msg.toString(), expr);
335 break;
336 }
337 }
338
339 currentScope.declares.put(var.name,var);
340 var.isInStaticContext = currentScope.isInStaticContext;
341 }
342
343
344
345
346
347
348
349
350
351
352
353
354
355
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
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
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
501
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
515 scope = scope.parent;
516 }
517
518 VarScope end = scope;
519
520 if (scope == null) {
521
522 ClassNode vn = unit.getClass(var.name);
523
524
525
526 if (vn==null) {
527 declare(var,expression);
528
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
548
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
559
560
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
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
587
588
589 try {
590 addVarNames(node);
591 addVarNames(node.getOuterClass(), currentScope.visibles, true);
592 addVarNames(node.getSuperClass(), currentScope.visibles, true);
593 } catch (ClassNotFoundException cnfe) {
594
595
596
597 cnfe.printStackTrace();
598 }
599
600
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
613 addVarNames(c.getName(), refs, visitParent);
614 }
615
616 private void addVarNames(ClassNode cn) {
617
618
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
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
742
743
744
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
761
762
763 HashMap declares = currentScope.declares;
764 Parameter[] parameters = node.getParameters();
765 for (int i = 0; i < parameters.length; i++) {
766
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
782
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 }