001 /*
002 * $Id: JSRVariableScopeCodeVisitor.java,v 1.24 2005/11/13 16:42:11 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.ClassHelper;
047 import org.codehaus.groovy.ast.ClassNode;
048 import org.codehaus.groovy.ast.CodeVisitorSupport;
049 import org.codehaus.groovy.ast.CompileUnit;
050 import org.codehaus.groovy.ast.ConstructorNode;
051 import org.codehaus.groovy.ast.FieldNode;
052 import org.codehaus.groovy.ast.GroovyClassVisitor;
053 import org.codehaus.groovy.ast.MethodNode;
054 import org.codehaus.groovy.ast.Parameter;
055 import org.codehaus.groovy.ast.PropertyNode;
056 import org.codehaus.groovy.ast.expr.ClosureExpression;
057 import org.codehaus.groovy.ast.expr.DeclarationExpression;
058 import org.codehaus.groovy.ast.expr.Expression;
059 import org.codehaus.groovy.ast.expr.FieldExpression;
060 import org.codehaus.groovy.ast.expr.PropertyExpression;
061 import org.codehaus.groovy.ast.expr.VariableExpression;
062 import org.codehaus.groovy.ast.stmt.BlockStatement;
063 import org.codehaus.groovy.ast.stmt.CatchStatement;
064 import org.codehaus.groovy.ast.stmt.DoWhileStatement;
065 import org.codehaus.groovy.ast.stmt.ForStatement;
066 import org.codehaus.groovy.ast.stmt.Statement;
067 import org.codehaus.groovy.ast.stmt.WhileStatement;
068 import org.codehaus.groovy.control.SourceUnit;
069 import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
070 import org.codehaus.groovy.syntax.SyntaxException;
071
072 import org.codehaus.groovy.ast.Variable;
073
074 public class JSRVariableScopeCodeVisitor extends CodeVisitorSupport implements GroovyClassVisitor {
075
076 private static class Var implements Variable{
077 //TODO: support final and native
078 String name;
079 ClassNode type=null;
080 boolean isInStaticContext=false;
081 boolean isDynamicTyped;
082
083 public Var(String name,VarScope scope) {
084 // a Variable without type and other modifiers
085 // make it dynamic type, non final and non static
086 this.name=name;
087 setType(ClassHelper.DYNAMIC_TYPE);
088 isInStaticContext = scope.isInStaticContext;
089 }
090
091 public Var(String pName, MethodNode f) {
092 name = pName;
093 setType(f.getReturnType());
094 isInStaticContext=f.isStatic();
095 }
096
097 public Var(String pName, Method m) {
098 name = pName;
099 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 // don't visit subclosures if already in a closure
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 //System.out.println("scope check enabled");
200 if ("true".equals(System.getProperty("groovy.jsr.check.rule.jrose"))) {
201 jroseRule=true;
202 //System.out.println("jrose check enabled");
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 // TODO: always define a variable here? What about type?
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 //TODO: check while loop variables
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 //TODO: still existant?
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 // visit right side first to avoid the usage of a
244 // variable before its declaration
245 expression.getRightExpression().visit(this);
246 // no need to visit left side, just get the variable name
247 VariableExpression vex = expression.getVariableExpression();
248 vex.setInStaticContext(currentScope.isInStaticContext);
249 if (!jroseRule && "it".equals(vex.getName())) {
250 // we are not in jrose mode, so don't allow variables
251 // of the name 'it'
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 //TODO: this case is not visited I think
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 // variable already declared
302 addError(msg.toString(), expr);
303 break;
304 }
305 }
306 // declare the variable even if there was an error to allow more checks
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 //TODO: change that to get the correct scope
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 // decalre a static variable to be able to continue the check
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 // TODO: this line is not working
424 // if (expression==VariableExpression.SUPER_EXPRESSION) return;
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 // scope.getReferencedVariables().add(name);
438 scope = scope.parent;
439 }
440
441 VarScope end = scope;
442
443 if (scope == null) {
444 //TODO add a check to be on the lhs!
445 ClassNode vn = unit.getClass(var.getName());
446 // vn==null means there is no class of that name
447 // note: we need to do this check because it's possible in groovy to access
448 // Classes without the .class known from Java. Example: def type = String;
449 if (vn==null) {
450 declare(var,expression);
451 // don't create an error when inside a script body
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 // TODO: set scope
471 // expression.setVarScope(currentScope);
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 // TODO: when to add "it" and when not?
482 // John's rule is to add it only to the closures using 'it'
483 // and only to the closure itself, not to subclosures
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 // currentScope = new VarScope(currentScope);
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 // first pass, add all possible variable names (properies and fields)
511 // TODO: handle interfaces
512 // TODO: handle static imports
513 addVarNames(node);
514 addVarNames(node.getOuterClass(), currentScope.visibles, true);
515 addVarNames(node.getSuperClass(), currentScope.visibles, true);
516 // second pass, check contents
517 node.visitContents(this);
518
519 currentClass = classBackup;
520 currentScope = scope;
521 scriptMode = scriptModeBackup;
522 }
523
524 private void addVarNames(ClassNode cn) {
525 //TODO: change test for currentScope.declares
526 //TODO: handle indexed properties
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 //TODO: ignore double delcaration of methods for the moment
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 // note this method is only called for parent classes
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 /*private void addVarNames(ClassNode superclassType, HashMap refs, boolean visitParent)
614 throws ClassNotFoundException
615 {
616
617 if (superclassType == null) return;
618 String superclassName = superclassType.getName();
619
620 ClassNode cn = unit.getClass(superclassName);
621 if (cn != null) {
622 addVarNames(cn, refs, visitParent);
623 return;
624 }
625
626 Class c = superclassType.getTypeClass();
627 if (c==null) c = unit.getClassLoader().loadClass(superclassName);
628 Field[] fields = c.getFields();
629 for (int i = 0; i < fields.length; i++) {
630 Field f = fields[i];
631 if (visitParent && Modifier.isPrivate(f.getModifiers()))
632 continue;
633 refs.put(f.getName(),new Var(f));
634 }
635
636 Method[] methods = c.getMethods();
637 for (int i = 0; i < methods.length; i++) {
638 Method m = methods[i];
639 if (visitParent && Modifier.isPrivate(m.getModifiers()))
640 continue;
641 String name = getPropertyName(m.getName());
642 if (name == null) continue;
643 refs.put(name,new Var(name,m));
644 }
645
646 if (!visitParent) return;
647
648 addVarNames(c.getSuperclass(), refs, visitParent);
649
650 // it's not possible to know the variable names used for an enclosing
651 // method
652
653 // addVarNames(c.getEnclosingClass(),refs,visitParent);
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 // TODO: set scope
670 // node.setVarScope(currentScope);
671
672 HashMap declares = currentScope.declares;
673 Parameter[] parameters = node.getParameters();
674 for (int i = 0; i < parameters.length; i++) {
675 // a constructor is never static
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 // TODO: set scope
691 // node.setVarScope(currentScope);
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 }