001: /* -*- mode: Java; c-basic-offset: 2; -*- */
002:
003: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
004: * Copyright 2001-2004 Laszlo Systems, Inc. All Rights Reserved. *
005: * Use is subject to license terms. *
006: * J_LZ_COPYRIGHT_END *********************************************************/
007:
008: package org.openlaszlo.sc;
009:
010: import java.util.*;
011: import org.openlaszlo.sc.parser.SimpleNode;
012: import org.openlaszlo.sc.parser.*;
013:
014: public class VariableAnalyzer {
015: // A ref will cause an auto_reg to be declared
016: // compiler magic variables (e.g., $flasm) are always "available"
017: static Set AVAILABLE = new HashSet(Instructions.Register.AUTO_REG);
018: static {
019: AVAILABLE.add("$flasm");
020: }
021:
022: // _Must_ be in order
023: public LinkedHashSet parameters;
024: // Kept in order for deterministic code generation
025: public LinkedHashSet variables;
026: public LinkedHashMap fundefs;
027: // Order unimportant for the rest
028: public Map used;
029: public Set closed;
030: public Set free;
031:
032: boolean ignoreFlasm;
033: Set locals;
034:
035: public VariableAnalyzer(SimpleNode params, boolean ignoreFlasm) {
036: // Parameter order is significant
037: this .parameters = new LinkedHashSet();
038: for (int i = 0, len = params.size(); i < len; i++) {
039: parameters.add(((ASTIdentifier) params.get(i)).getName());
040: }
041: this .locals = new LinkedHashSet(parameters);
042: closed = new HashSet();
043: fundefs = new LinkedHashMap();
044: used = new HashMap();
045: this .ignoreFlasm = ignoreFlasm;
046: }
047:
048: public void incrementUsed(String variable) {
049: String var = variable.intern();
050: if (used.containsKey(var)) {
051: // Gaak!
052: used.put(var, new Integer(((Integer) used.get(var))
053: .intValue() + 1));
054: } else {
055: used.put(var, new Integer(1));
056: }
057: }
058:
059: // Computes parameters, variables, fundefs, used, closed, free
060: public void computeReferences() {
061: // variables (locals - parameters)
062: variables = new LinkedHashSet(locals);
063: variables.removeAll(parameters);
064: // available (locals + AVAILABLE)
065: Set available = new HashSet(locals);
066: available.addAll(AVAILABLE);
067: // Closing over a variable counts as a use
068: for (Iterator i = closed.iterator(); i.hasNext();) {
069: incrementUsed((String) i.next());
070: }
071: // Calculate actual closed (closed & available)
072: closed.retainAll(available);
073: // Calculate free references (used - available)
074: free = new HashSet(used.keySet());
075: free.removeAll(available);
076: }
077:
078: public void visit(SimpleNode node) {
079: SimpleNode[] children;
080: if (node instanceof ASTPropertyIdentifierReference) {
081: // Only the base is a reference
082: SimpleNode[] c = { node.get(0) };
083: children = c;
084: } else if (node instanceof ASTIfStatement) {
085: // Don't analyze flasm
086: SimpleNode test = node.get(0);
087: if (test instanceof ASTIdentifier
088: && ("$flasm".equals(((ASTIdentifier) test)
089: .getName())) && ignoreFlasm) {
090: SimpleNode[] c = { test };
091: children = c;
092: } else {
093: children = node.getChildren();
094: }
095: } else {
096: children = node.getChildren();
097: }
098: // ForVar has a VariableDeclaration as a child, so we don"t
099: // need to handle it specially, but ForVarIn does not.
100: if (node instanceof ASTVariableDeclaration
101: || node instanceof ASTForVarInStatement) {
102: String v = ((ASTIdentifier) children[0]).getName();
103: // In ECMAscript you can re-declare variables and
104: // parameters and not shadow them
105: if (!locals.contains(v)) {
106: locals.add(v);
107: }
108: } else if (node instanceof ASTFunctionDeclaration) {
109: String v = ((ASTIdentifier) children[0]).getName();
110: fundefs.put(v, node);
111: if (!locals.contains(v)) {
112: locals.add(v);
113: }
114: } else if (node instanceof ASTThisReference
115: || node instanceof ASTIdentifier) {
116: if (node instanceof ASTThisReference) {
117: incrementUsed("this");
118: } else {
119: incrementUsed(((ASTIdentifier) node).getName());
120: }
121: }
122: if (node instanceof ASTFunctionDeclaration
123: || node instanceof ASTFunctionExpression) {
124: SimpleNode params = children[children.length - 2];
125: SimpleNode stmts = children[children.length - 1];
126: VariableAnalyzer analyzer = new VariableAnalyzer(params,
127: ignoreFlasm);
128: for (int i = 0, len = stmts.size(); i < len; i++) {
129: SimpleNode stmt = stmts.get(i);
130: analyzer.visit(stmt);
131: }
132: analyzer.computeReferences();
133: for (Iterator i = analyzer.free.iterator(); i.hasNext();) {
134: String v = (String) i.next();
135: if (!closed.contains(v)) {
136: closed.add(v);
137: }
138: }
139: return;
140: }
141: // TODO: [2005-03-09 ptw] Don't need this when callInherited
142: // goes away. For now add implicit reference to this and
143: // arguments.
144: if (node instanceof ASTSuperCallExpression) {
145: incrementUsed("this");
146: incrementUsed("arguments");
147: }
148: for (int i = 0; i < children.length; i++) {
149: visit(children[i]);
150: }
151: }
152: }
|