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 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
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
511
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
525 scope = scope.parent;
526 }
527
528 VarScope end = scope;
529
530 if (scope == null) {
531
532 ClassNode vn = unit.getClass(var.name);
533
534
535
536 if (vn==null) {
537 declare(var,expression);
538
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
558
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
569
570
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
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
598
599
600 try {
601 addVarNames(node);
602 addVarNames(node.getOuterClass(), currentScope.visibles, true);
603 addVarNames(node.getSuperClass(), currentScope.visibles, true);
604 } catch (ClassNotFoundException cnfe) {
605
606
607
608 cnfe.printStackTrace();
609 }
610
611
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
624 addVarNames(c.getName(), refs, visitParent);
625 }
626
627 private void addVarNames(ClassNode cn) {
628
629
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
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
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
755
756
757
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
774
775
776 HashMap declares = currentScope.declares;
777 Parameter[] parameters = node.getParameters();
778 for (int i = 0; i < parameters.length; i++) {
779
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
795
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 }