View Javadoc

1   /*
2    * $Id: JSRVariableScopeCodeVisitor.java,v 1.24 2005/11/13 16:42:11 blackdrag Exp $
3    *
4    * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
5    *
6    * Redistribution and use of this software and associated documentation
7    * ("Software"), with or without modification, are permitted provided that the
8    * following conditions are met: 1. Redistributions of source code must retain
9    * copyright statements and notices. Redistributions must also contain a copy
10   * of this document. 2. Redistributions in binary form must reproduce the above
11   * copyright notice, this list of conditions and the following disclaimer in
12   * the documentation and/or other materials provided with the distribution. 3.
13   * The name "groovy" must not be used to endorse or promote products derived
14   * from this Software without prior written permission of The Codehaus. For
15   * written permission, please contact info@codehaus.org. 4. Products derived
16   * from this Software may not be called "groovy" nor may "groovy" appear in
17   * their names without prior written permission of The Codehaus. "groovy" is a
18   * registered trademark of The Codehaus. 5. Due credit should be given to The
19   * Codehaus - http://groovy.codehaus.org/
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
22   * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24   * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
25   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29   * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30   * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31   * DAMAGE.
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          //TODO: support final and native
78          String name;
79          ClassNode type=null;
80          boolean isInStaticContext=false;
81          boolean isDynamicTyped;
82          
83          public Var(String name,VarScope scope) {
84              // a Variable without type and other modifiers
85              // make it dynamic type, non final and non static
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             // 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 }