PageRenderTime 109ms CodeModel.GetById 89ms app.highlight 17ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/XML/sidekick/ecmascript/parser/GlobalDeclCollector.java

#
Java | 298 lines | 195 code | 54 blank | 49 comment | 49 complexity | b364c863f5e46f7429a187328f42e7da MD5 | raw file
  1/*
  2 Copyright (c) 2004-2005, The Dojo Foundation
  3 All Rights Reserved.
  4
  5 Licensed under the Academic Free License version 2.1 or above OR the
  6 modified BSD license. For more information on Dojo licensing, see:
  7
  8 http://dojotoolkit.org/community/licensing.shtml <http://dojotoolkit.org/community/licensing.shtml>
  9
 10 Code donated to the Dojo Foundation by AOL LLC under the terms of
 11 the Dojo CCLA (http://dojotoolkit.org/ccla.txt).
 12
 13 */
 14package sidekick.ecmascript.parser;
 15
 16import java.util.*;
 17
 18import sidekick.ecmascript.parser.ASTAssignmentExpression;
 19import sidekick.ecmascript.parser.ASTExpressionStatement;
 20import sidekick.ecmascript.parser.ASTForVarInStatement;
 21import sidekick.ecmascript.parser.ASTForVarStatement;
 22import sidekick.ecmascript.parser.ASTFormalParameterList;
 23import sidekick.ecmascript.parser.ASTVariableDeclaration;
 24import sidekick.ecmascript.parser.EcmaScriptVisitor;
 25
 26/**
 27 * Visitor that collects the global identifiers and string literals in the code
 28 * base and also decorates the function declaration nodes with symbol table
 29 * information of local variables
 30 *
 31 *
 32 * @since JDK 1.4
 33 */
 34public class GlobalDeclCollector extends EcmaScriptVisitorAdapter implements
 35        EcmaScriptVisitor {
 36
 37    // globals
 38    private Set declarations;
 39
 40    private LinkedList declarationNodes;
 41
 42    private boolean collectForvarDeclarations;
 43
 44    // stacks of local variables
 45    private LinkedList localDeclarations;
 46
 47    private LinkedList loopDeclarations;
 48
 49    public GlobalDeclCollector(Set declarations, LinkedList declarationNodes,
 50            EcmaScriptVisitorDelegate visitorDelegate) {
 51        super(visitorDelegate);
 52        this.declarations = declarations;
 53        this.declarationNodes = declarationNodes;
 54
 55        localDeclarations = new LinkedList();
 56        loopDeclarations = new LinkedList();
 57    }
 58
 59    public GlobalDeclCollector(Set declarations, LinkedList declarationNodes) {
 60        this(declarations, declarationNodes, null);
 61    }
 62
 63    public GlobalDeclCollector(Set declarations) {
 64        this(declarations, null);
 65    }
 66
 67    private boolean isLocal(String identifierName) {
 68        ListIterator iter = loopDeclarations.listIterator(loopDeclarations
 69                .size());
 70
 71        while (iter.hasPrevious()) {
 72            Map decls = (Map) iter.previous();
 73
 74            if (decls.containsKey(identifierName)) {
 75                return true;
 76            }
 77        }
 78
 79        iter = localDeclarations.listIterator(localDeclarations.size());
 80
 81        while (iter.hasPrevious()) {
 82            Map decls = (Map) iter.previous();
 83
 84            if (decls.containsKey(identifierName)) {
 85                return true;
 86            }
 87        }
 88
 89        return false;
 90    }
 91
 92    @Override
 93    public Object visit(ASTVariableDeclaration node, Object data) {
 94        String name = ((ASTIdentifier) node.jjtGetChild(0)).getName();
 95
 96        if (collectForvarDeclarations) {
 97            Map forvarDeclarations = (Map) loopDeclarations.getLast();
 98
 99            if (!forvarDeclarations.containsKey(name)) {
100                forvarDeclarations.put(name, name);
101            }
102        } else if (localDeclarations.size() > 0) {
103            Map functionDecls = (Map) localDeclarations.getLast();
104
105            if (!functionDecls.containsKey(name)) {
106                functionDecls.put(name, name);
107            }
108        } else {
109            declarations.add(name);
110            if (declarationNodes != null) {
111                declarationNodes.add(node);
112            }
113        }
114        return super.visit(node, data);
115    }
116
117    @Override
118    public Object visit(ASTExpressionStatement node, Object data) {
119        // see if we have an assignment expression with a composite reference on
120        // the lhs
121        if (node.jjtGetNumChildren() > 0) {
122            SimpleNode exprNode = (SimpleNode) node.jjtGetChild(0);
123
124            if ((exprNode instanceof ASTAssignmentExpression)
125                    && (exprNode.jjtGetNumChildren() > 0)) {
126                SimpleNode lhsNode = (SimpleNode) exprNode.jjtGetChild(0);
127
128                if (lhsNode instanceof ASTCompositeReference) {
129                    // determine if it is a global declaration
130                    String firstRefPart = null;
131                    SimpleNode cNode = (SimpleNode) lhsNode.jjtGetChild(0);
132
133                    if (cNode instanceof ASTIdentifier) {
134                        firstRefPart = ((ASTIdentifier) cNode).getName();
135                    }
136
137                    if ((firstRefPart != null) && (!isLocal(firstRefPart))) {
138                        String compositeName = ((ASTCompositeReference) lhsNode)
139                                .getCompositeName();
140                        if (compositeName != null) {
141                            declarations.add(compositeName);
142                            if (declarationNodes != null) {
143                                declarationNodes.add(exprNode);
144                            }
145                        }
146                    }
147                } else if (lhsNode instanceof ASTIdentifier) {
148                    String lhsName = ((ASTIdentifier) lhsNode).getName();
149
150                    if ((lhsName != null) && (!isLocal(lhsName))) {
151                        // PENDING(uwe): I cannot really have this as a global
152                        // declaration
153                        // because I cannot distinguish between an actual
154                        // declaration
155                        // and an assignment
156                        // i.e. location = foo;
157                        // in this case it's window.location which would be bad
158                        // if I consider it
159                        // a declaration (that is something we own and is a
160                        // potential target for janitor/jabber)
161
162                        // so I look for the @constructor tag in comment
163                        // associated with this assignment
164                        // if it exists it's one of ours
165
166                        Comment comment = node.getComment();
167
168                        if ((comment != null)
169                                && comment.containsTag("constructor")) {
170                            declarations.add(lhsName);
171                            if (declarationNodes != null) {
172                                declarationNodes.add(exprNode);
173                            }
174                        }
175                    }
176                }
177            }
178        }
179
180        return super.visit(node, data);
181    }
182
183    @Override
184    public Object visit(ASTForVarStatement node, Object data) {
185        HashMap forvarDeclarations = new HashMap();
186        loopDeclarations.add(forvarDeclarations);
187
188        pre(node, data);
189        collectForvarDeclarations = true;
190        data = node.jjtGetChild(0).jjtAccept(this, data);
191        collectForvarDeclarations = false;
192
193        // it's weird but in javascript loop variables are visible afterwards
194        // outside the loop
195        // so we dump them into the last local variables map
196        if (localDeclarations.size() > 0) {
197            Map functionDecls = (Map) localDeclarations.getLast();
198
199            Iterator keyIter = forvarDeclarations.keySet().iterator();
200
201            while (keyIter.hasNext()) {
202                String name = (String) keyIter.next();
203                if (!functionDecls.containsKey(name)) {
204                    functionDecls.put(name, name);
205                }
206            }
207        }
208
209        int n = node.jjtGetNumChildren();
210        for (int i = 1; i < n; i++) {
211            data = node.jjtGetChild(i).jjtAccept(this, data);
212        }
213        loopDeclarations.removeLast();
214        post(node, data);
215        return data;
216    }
217
218    @Override
219    public Object visit(ASTForVarInStatement node, Object data) {
220        HashMap forvarInDecls = new HashMap();
221        String forVarInName = ((ASTIdentifier) node.jjtGetChild(0)).getName();
222        forvarInDecls.put(forVarInName, forVarInName);
223
224        // it's weird but in javascript loop variables are visible afterwards
225        // outside the loop
226        // so we dump them into the last local variables map
227        if (localDeclarations.size() > 0) {
228            Map functionDecls = (Map) localDeclarations.getLast();
229
230            if (!functionDecls.containsKey(forVarInName)) {
231                functionDecls.put(forVarInName, forVarInName);
232            }
233        }
234
235        loopDeclarations.add(forvarInDecls);
236        data = super.visit(node, data);
237        loopDeclarations.removeLast();
238        return data;
239    }
240
241    @Override
242    public Object visit(ASTFunctionDeclaration node, Object data) {
243        int index = 0;
244        pre(node, data);
245        if (node.jjtGetNumChildren() == 3) {
246            SimpleNode firstChild = (SimpleNode) node.jjtGetChild(0);
247            String name = null;
248
249            if (firstChild instanceof ASTIdentifier) {
250                name = ((ASTIdentifier) firstChild).getName();
251            } else {
252                name = ((ASTIdentifier) firstChild.jjtGetChild(1)).getName();
253            }
254            // if scope is global record it as a global declaration
255            if (getScope() instanceof ASTProgram) {
256                declarations.add(name);
257                if (declarationNodes != null) {
258                    declarationNodes.add(node);
259                }
260            }
261
262            // visit function name identifier
263            data = node.jjtGetChild(0).jjtAccept(this, data);
264            index = 1;
265        }
266
267        ASTFormalParameterList paramList = (ASTFormalParameterList) node
268                .jjtGetChild(index);
269        ASTBlock body = (ASTBlock) node.jjtGetChild(index + 1);
270
271        HashMap functionVars = new HashMap();
272        if (paramList.jjtGetNumChildren() > 0) {
273            int n = paramList.jjtGetNumChildren();
274
275            for (int i = 0; i < n; i++) {
276                ASTIdentifier param = (ASTIdentifier) paramList.jjtGetChild(i);
277                String paramName = param.getName();
278
279                functionVars.put(paramName, paramName);
280            }
281        }
282
283        localDeclarations.add(functionVars);
284        data = body.jjtAccept(this, data);
285
286        // here we decorate the function declaration node
287        // with all the local variables visible to it
288        node.setLocals(localDeclarations);
289
290        localDeclarations.removeLast();
291        post(node, data);
292        return data;
293    }
294
295    public Object visit(ASTRequireStatement node, Object data) {
296        return data;
297    }
298}