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.ClassHelper;
47 import org.codehaus.groovy.ast.ClassNode;
48 import org.codehaus.groovy.ast.CodeVisitorSupport;
49 import org.codehaus.groovy.ast.CompileUnit;
50 import org.codehaus.groovy.ast.ConstructorNode;
51 import org.codehaus.groovy.ast.FieldNode;
52 import org.codehaus.groovy.ast.GroovyClassVisitor;
53 import org.codehaus.groovy.ast.MethodNode;
54 import org.codehaus.groovy.ast.Parameter;
55 import org.codehaus.groovy.ast.PropertyNode;
56 import org.codehaus.groovy.ast.expr.ClosureExpression;
57 import org.codehaus.groovy.ast.expr.DeclarationExpression;
58 import org.codehaus.groovy.ast.expr.Expression;
59 import org.codehaus.groovy.ast.expr.FieldExpression;
60 import org.codehaus.groovy.ast.expr.PropertyExpression;
61 import org.codehaus.groovy.ast.expr.VariableExpression;
62 import org.codehaus.groovy.ast.stmt.BlockStatement;
63 import org.codehaus.groovy.ast.stmt.CatchStatement;
64 import org.codehaus.groovy.ast.stmt.DoWhileStatement;
65 import org.codehaus.groovy.ast.stmt.ForStatement;
66 import org.codehaus.groovy.ast.stmt.Statement;
67 import org.codehaus.groovy.ast.stmt.WhileStatement;
68 import org.codehaus.groovy.control.SourceUnit;
69 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
70 import org.codehaus.groovy.syntax.SyntaxException;
71
72 import org.codehaus.groovy.ast.Variable;
73
74 public class JSRVariableScopeCodeVisitor extends CodeVisitorSupport implements GroovyClassVisitor {
75
76 private static class Var implements Variable{
77
78 String name;
79 ClassNode type=null;
80 boolean isInStaticContext=false;
81 boolean isDynamicTyped;
82
83 public Var(String name,VarScope scope) {
84
85
86 this.name=name;
87 setType(ClassHelper.DYNAMIC_TYPE);
88 isInStaticContext = scope.isInStaticContext;
89 }
90
91 public Var(String pName, MethodNode f) {
92 name = pName;
93 setType(f.getReturnType());
94 isInStaticContext=f.isStatic();
95 }
96
97 public Var(String pName, Method m) {
98 name = pName;
99 type = ClassHelper.make(m.getReturnType());
100 isInStaticContext=Modifier.isStatic(m.getModifiers());
101 }
102
103 public Var(Field f) {
104 name = f.getName();
105 type = ClassHelper.make(f.getType());
106 isInStaticContext=Modifier.isStatic(f.getModifiers());
107 }
108
109 public Var(Variable v) {
110 name=v.getName();
111 type=v.getType();
112 isInStaticContext=v.isInStaticContext();
113 isDynamicTyped=v.isDynamicTyped();
114 }
115
116 public void setType(ClassNode cn) {
117 type = cn;
118 isDynamicTyped |= cn==ClassHelper.DYNAMIC_TYPE;
119 }
120
121 public ClassNode getType() {
122 return type;
123 }
124
125 public String getName() {
126 return name;
127 }
128
129 public Expression getInitialExpression() {
130 return null;
131 }
132
133 public boolean hasInitialExpression() {
134 return false;
135 }
136
137 public boolean isInStaticContext() {
138 return isInStaticContext;
139 }
140
141 public boolean isDynamicTyped() {
142 return isDynamicTyped;
143 }
144 }
145
146 private static class VarScope {
147 boolean isClass=true;
148 boolean isInStaticContext = false;
149
150 VarScope parent;
151 HashMap declares = new HashMap();
152 HashMap visibles = new HashMap();
153
154 public VarScope(boolean isClass, VarScope parent, boolean staticContext) {
155 this.isClass=isClass;
156 this.parent = parent;
157 isInStaticContext = staticContext;
158 }
159
160 public VarScope(VarScope parent, boolean staticContext) {
161 this(false,parent,staticContext);
162 }
163
164 public VarScope(VarScope parent) {
165 this(false,parent,parent!=null?parent.isInStaticContext:false);
166 }
167 }
168
169 private static class JRoseCheck extends CodeVisitorSupport{
170 boolean closureStarted=false;
171 boolean itUsed=false;
172
173 public void visitClosureExpression(ClosureExpression expression) {
174
175 if (closureStarted) return;
176 closureStarted=true;
177 Parameter[] param = expression.getParameters();
178 for (int i=0; i<param.length; i++) {
179 itUsed = (param[i].getName().equals("it")) && closureStarted || itUsed;
180 }
181 super.visitClosureExpression(expression);
182 }
183
184 public void visitVariableExpression(VariableExpression expression) {
185 itUsed = (expression.getName().equals("it")) && closureStarted || itUsed;
186 }
187
188 }
189
190 private VarScope currentScope = null;
191 private CompileUnit unit;
192 private SourceUnit source;
193 private boolean scriptMode=false;
194 private ClassNode currentClass=null;
195
196 private boolean jroseRule=false;
197
198 public JSRVariableScopeCodeVisitor(VarScope scope, SourceUnit source) {
199
200 if ("true".equals(System.getProperty("groovy.jsr.check.rule.jrose"))) {
201 jroseRule=true;
202
203 }
204 currentScope = scope;
205 this.source = source;
206 if (source.getAST() == null) return;
207 this.unit = source.getAST().getUnit();
208 }
209
210 public void visitBlockStatement(BlockStatement block) {
211 VarScope scope = currentScope;
212 currentScope = new VarScope(currentScope);
213 super.visitBlockStatement(block);
214 currentScope = scope;
215 }
216
217 public void visitForLoop(ForStatement forLoop) {
218 VarScope scope = currentScope;
219
220 currentScope = new VarScope(currentScope);
221 declare(new Var(forLoop.getVariable(),currentScope), forLoop);
222 super.visitForLoop(forLoop);
223 currentScope = scope;
224 }
225
226 public void visitWhileLoop(WhileStatement loop) {
227
228 VarScope scope = currentScope;
229 currentScope = new VarScope(currentScope);
230 super.visitWhileLoop(loop);
231 currentScope = scope;
232 }
233
234 public void visitDoWhileLoop(DoWhileStatement loop) {
235
236 VarScope scope = currentScope;
237 currentScope = new VarScope(currentScope);
238 super.visitDoWhileLoop(loop);
239 currentScope = scope;
240 }
241
242 public void visitDeclarationExpression(DeclarationExpression expression) {
243
244
245 expression.getRightExpression().visit(this);
246
247 VariableExpression vex = expression.getVariableExpression();
248 vex.setInStaticContext(currentScope.isInStaticContext);
249 if (!jroseRule && "it".equals(vex.getName())) {
250
251
252 addError("'it' is a keyword in this mode.",vex);
253 } else {
254 declare(vex);
255 }
256 }
257
258 private void addError(String msg, ASTNode expr) {
259 int line = expr.getLineNumber();
260 int col = expr.getColumnNumber();
261 source.getErrorCollector().addErrorAndContinue(
262 new SyntaxErrorMessage(new SyntaxException(msg + '\n', line, col), source)
263 );
264 }
265
266 private void declare(VariableExpression expr) {
267 declare(expr,expr);
268 }
269
270 private void declare(Variable var, ASTNode expr) {
271 String scopeType = "scope";
272 String variableType = "variable";
273
274 if (expr.getClass()==FieldNode.class){
275 scopeType = "class";
276 variableType = "field";
277 } else if (expr.getClass()==PropertyNode.class){
278 scopeType = "class";
279 variableType = "property";
280 }
281
282 StringBuffer msg = new StringBuffer();
283 msg.append("The current ").append(scopeType);
284 msg.append(" does already contain a ").append(variableType);
285 msg.append(" of the name ").append(var.getName());
286
287 if (currentScope.declares.get(var.getName())!=null) {
288 addError(msg.toString(),expr);
289 return;
290 }
291
292
293 if (currentScope.isClass) {
294 currentScope.declares.put(var.getName(),var);
295 }
296
297 for (VarScope scope = currentScope.parent; scope!=null; scope = scope.parent) {
298 HashMap declares = scope.declares;
299 if (scope.isClass) break;
300 if (declares.get(var.getName())!=null) {
301
302 addError(msg.toString(), expr);
303 break;
304 }
305 }
306
307 currentScope.declares.put(var.getName(),var);
308 }
309
310 public void visitVariableExpression(VariableExpression expression) {
311 String name = expression.getName();
312 Variable v = checkVariableNameForDeclaration(name,expression);
313 if (v==null) return;
314 checkVariableContextAccess(v,expression);
315 }
316
317 public void visitFieldExpression(FieldExpression expression) {
318 String name = expression.getFieldName();
319
320 Variable v = checkVariableNameForDeclaration(name,expression);
321 checkVariableContextAccess(v,expression);
322 }
323
324 private void checkAbstractDeclaration(MethodNode methodNode) {
325 if (!Modifier.isAbstract(methodNode.getModifiers())) return;
326 if (Modifier.isAbstract(currentClass.getModifiers())) return;
327 addError("Can't have an abstract method in a non abstract class." +
328 " The class '" + currentClass.getName() + "' must be declared abstract or the method '" +
329 methodNode.getName() + "' must not be abstract.",methodNode);
330 }
331
332 private boolean hasEqualParameterTypes(Parameter[] first, Parameter[] second) {
333 if (first.length!=second.length) return false;
334 for (int i=0; i<first.length; i++) {
335 String ft = first[i].getType().getName();
336 String st = second[i].getType().getName();
337 if (ft.equals(st)) continue;
338 return false;
339 }
340 return true;
341 }
342
343 private void checkImplementsAndExtends(ClassNode node) {
344 ClassNode cn = node.getSuperClass();
345 if (cn.isInterface()) addError("you are not allowed to extend the Interface "+cn.getName()+", use implements instead", node);
346 ClassNode[] interfaces = node.getInterfaces();
347 for (int i = 0; i < interfaces.length; i++) {
348 cn = interfaces[i];
349 if (!cn.isInterface()) addError ("you are not allowed to implement the Class "+cn.getName()+", use extends instead", node);
350 }
351 }
352
353 private void checkClassForOverwritingFinal(ClassNode cn) {
354 ClassNode superCN = cn.getSuperClass();
355 if (superCN==null) return;
356 if (!Modifier.isFinal(superCN.getModifiers())) return;
357 StringBuffer msg = new StringBuffer();
358 msg.append("you are not allowed to overwrite the final class ");
359 msg.append(superCN.getName());
360 msg.append(".");
361 addError(msg.toString(),cn);
362
363 }
364
365 private void checkMethodsForOverwritingFinal(ClassNode cn) {
366 List l = cn.getMethods();
367 for (Iterator cnIter = l.iterator(); cnIter.hasNext();) {
368 MethodNode method =(MethodNode) cnIter.next();
369 Parameter[] parameters = method.getParameters();
370 for (ClassNode superCN = cn.getSuperClass(); superCN!=null; superCN=superCN.getSuperClass()){
371 List methods = superCN.getMethods(method.getName());
372 for (Iterator iter = methods.iterator(); iter.hasNext();) {
373 MethodNode m = (MethodNode) iter.next();
374 Parameter[] np = m.getParameters();
375 if (!hasEqualParameterTypes(parameters,np)) continue;
376 if (!Modifier.isFinal(m.getModifiers())) return;
377
378 StringBuffer msg = new StringBuffer();
379 msg.append("you are not allowed to overwrite the final method ").append(method.getName());
380 msg.append("(");
381 boolean semi = false;
382 for (int i=0; i<parameters.length;i++) {
383 if (semi) {
384 msg.append(",");
385 } else {
386 semi = true;
387 }
388 msg.append(parameters[i].getType());
389 }
390 msg.append(")");
391 msg.append(" from class ").append(superCN.getName());
392 msg.append(".");
393 addError(msg.toString(),method);
394 return;
395 }
396 }
397 }
398 }
399
400 private void checkVariableContextAccess(Variable v, Expression expr) {
401 if (v.isInStaticContext() || !currentScope.isInStaticContext) return;
402
403 String msg = v.getName()+
404 " is declared in a dynamic context, but you tried to"+
405 " access it from a static context.";
406 addError(msg,expr);
407
408
409 Var v2 = new Var(v);
410 v2.isInStaticContext = true;
411 currentScope.declares.put(v2.name,v2);
412 }
413
414 private Variable checkVariableNameForDeclaration(VariableExpression expression) {
415 if (expression == VariableExpression.THIS_EXPRESSION) return null;
416 String name = expression.getName();
417 return checkVariableNameForDeclaration(name,expression);
418 }
419
420 private Variable checkVariableNameForDeclaration(String name, Expression expression) {
421 Variable var = new Var(name,currentScope);
422
423
424
425 if ("super".equals(var.getName()) || "this".equals(var.getName())) return null;
426
427 VarScope scope = currentScope;
428 while (scope != null) {
429 if (scope.declares.get(var.getName())!=null) {
430 var = (Variable) scope.declares.get(var.getName());
431 break;
432 }
433 if (scope.visibles.get(var.getName())!=null) {
434 var = (Variable) scope.visibles.get(var.getName());
435 break;
436 }
437
438 scope = scope.parent;
439 }
440
441 VarScope end = scope;
442
443 if (scope == null) {
444
445 ClassNode vn = unit.getClass(var.getName());
446
447
448
449 if (vn==null) {
450 declare(var,expression);
451
452 if (!scriptMode) addError("The variable " + var.getName() +
453 " is undefined in the current scope", expression);
454 }
455 } else {
456 scope = currentScope;
457 while (scope != end) {
458 scope.visibles.put(var.getName(),var);
459 scope = scope.parent;
460 }
461 }
462
463 return var;
464 }
465
466 public void visitClosureExpression(ClosureExpression expression) {
467 VarScope scope = currentScope;
468 currentScope = new VarScope(false,currentScope,scope.isInStaticContext);
469
470
471
472
473 if (expression.isParameterSpecified()) {
474 Parameter[] parameters = expression.getParameters();
475 for (int i = 0; i < parameters.length; i++) {
476 parameters[i].setInStaticContext(currentScope.isInStaticContext);
477 declare(parameters[i],expression);
478 }
479 } else {
480 Var var = new Var("it",scope);
481
482
483
484 if (jroseRule) {
485 JRoseCheck check = new JRoseCheck();
486 expression.visit(check);
487 if (check.itUsed) declare(var,expression);
488 } else {
489 currentScope.declares.put("it",var);
490 }
491 }
492
493
494 super.visitClosureExpression(expression);
495 currentScope = scope;
496 }
497
498 public void visitClass(ClassNode node) {
499 checkImplementsAndExtends(node);
500 checkClassForOverwritingFinal(node);
501 checkMethodsForOverwritingFinal(node);
502 VarScope scope = currentScope;
503 currentScope = new VarScope(true,currentScope,false);
504 boolean scriptModeBackup = scriptMode;
505 scriptMode = node.isScript();
506 ClassNode classBackup = currentClass;
507 currentClass = node;
508
509 HashMap declares = currentScope.declares;
510
511
512
513 addVarNames(node);
514 addVarNames(node.getOuterClass(), currentScope.visibles, true);
515 addVarNames(node.getSuperClass(), currentScope.visibles, true);
516
517 node.visitContents(this);
518
519 currentClass = classBackup;
520 currentScope = scope;
521 scriptMode = scriptModeBackup;
522 }
523
524 private void addVarNames(ClassNode cn) {
525
526
527 if (cn == null) return;
528 List l = cn.getFields();
529 Set fields = new HashSet();
530 for (Iterator iter = l.iterator(); iter.hasNext();) {
531 FieldNode f = (FieldNode) iter.next();
532 if (fields.contains(f)) {
533 declare(f,f);
534 } else {
535 fields.add(f);
536 currentScope.declares.put(f.getName(),f);
537 }
538 }
539
540
541 l = cn.getMethods();
542 Set setter = new HashSet();
543 Set getter = new HashSet();
544 for (Iterator iter = l.iterator(); iter.hasNext();) {
545 MethodNode f =(MethodNode) iter.next();
546 String methodName = f.getName();
547 String pName = getPropertyName(methodName);
548 if (pName == null) continue;
549 Var var = new Var(pName,f);
550 currentScope.declares.put(var.name,var);
551 }
552
553 l = cn.getProperties();
554 Set props = new HashSet();
555 for (Iterator iter = l.iterator(); iter.hasNext();) {
556 PropertyNode f = (PropertyNode) iter.next();
557 if (props.contains(f)) {
558 declare(f,f);
559 } else {
560 props.add(f);
561 currentScope.declares.put(f.getName(),f);
562 }
563 }
564 }
565
566 private void addVarNames(ClassNode cn, HashMap refs, boolean visitParent){
567
568
569 if (cn == null) return;
570 List l = cn.getFields();
571 for (Iterator iter = l.iterator(); iter.hasNext();) {
572 FieldNode f = (FieldNode) iter.next();
573 if (visitParent && Modifier.isPrivate(f.getModifiers()))
574 continue;
575 refs.put(f.getName(),f);
576 }
577 l = cn.getMethods();
578 for (Iterator iter = l.iterator(); iter.hasNext();) {
579 MethodNode f = (MethodNode) iter.next();
580 if (visitParent && Modifier.isPrivate(f.getModifiers()))
581 continue;
582 String name = getPropertyName(f.getName());
583 if (name == null) continue;
584 refs.put(name, new Var(name,f));
585 }
586
587 l = cn.getProperties();
588 for (Iterator iter = l.iterator(); iter.hasNext();) {
589 PropertyNode f = (PropertyNode) iter.next();
590 if (visitParent && Modifier.isPrivate(f.getModifiers()))
591 continue;
592 refs.put(f.getName(),f);
593 }
594
595 if (!visitParent) return;
596
597 addVarNames(cn.getSuperClass(), refs, visitParent);
598 MethodNode enclosingMethod = cn.getEnclosingMethod();
599
600 if (enclosingMethod == null) return;
601
602 Parameter[] params = enclosingMethod.getParameters();
603 for (int i = 0; i < params.length; i++) {
604 refs.put(params[i].getName(),params[i]);
605 }
606
607 if (visitParent)
608 addVarNames(enclosingMethod.getDeclaringClass(), refs, visitParent);
609
610 addVarNames(cn.getOuterClass(), refs, visitParent);
611 }
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656 private String getPropertyName(String name) {
657 if (!(name.startsWith("set") || name.startsWith("get"))) return null;
658 String pname = name.substring(3);
659 if (pname.length() == 0) return null;
660 String s = pname.substring(0, 1).toLowerCase();
661 String rest = pname.substring(1);
662 return s + rest;
663 }
664
665 public void visitConstructor(ConstructorNode node) {
666 VarScope scope = currentScope;
667 currentScope = new VarScope(currentScope);
668
669
670
671
672 HashMap declares = currentScope.declares;
673 Parameter[] parameters = node.getParameters();
674 for (int i = 0; i < parameters.length; i++) {
675
676 declare(parameters[i],node);
677 }
678 currentScope = new VarScope(currentScope);
679 Statement code = node.getCode();
680 if (code != null) code.visit(this);
681 currentScope = scope;
682 }
683
684 public void visitMethod(MethodNode node) {
685 checkAbstractDeclaration(node);
686
687 VarScope scope = currentScope;
688 currentScope = new VarScope(currentScope,node.isStatic());
689
690
691
692
693 HashMap declares = currentScope.declares;
694 Parameter[] parameters = node.getParameters();
695 for (int i = 0; i < parameters.length; i++) {
696 declares.put(parameters[i].getName(),parameters[i]);
697 }
698
699 currentScope = new VarScope(currentScope);
700 Statement code = node.getCode();
701 if (code!=null) code.visit(this);
702 currentScope = scope;
703 }
704
705 public void visitField(FieldNode node) {
706 Expression init = node.getInitialExpression();
707 if (init != null) init.visit(this);
708 }
709
710 public void visitProperty(PropertyNode node) {
711 Statement statement = node.getGetterBlock();
712 if (statement != null) statement.visit(this);
713
714 statement = node.getSetterBlock();
715 if (statement != null) statement.visit(this);
716
717 Expression init = node.getInitialExpression();
718 if (init != null) init.visit(this);
719 }
720
721 public void visitPropertyExpression(PropertyExpression expression) {}
722
723 public void visitCatchStatement(CatchStatement statement) {
724 VarScope scope = currentScope;
725 currentScope = new VarScope(currentScope);
726 declare(new Var(statement.getVariable(),currentScope), statement);
727 super.visitCatchStatement(statement);
728 currentScope = scope;
729 }
730
731 }