001: /* -*- mode: Java; c-basic-offset: 2; -*- */
002:
003: /* J_LZ_COPYRIGHT_BEGIN *******************************************************
004: * Copyright 2001-2007 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 ReferenceCollector {
015: static public boolean DebugConstraints = false;
016: // The LFC guarantees that these won't change.
017: static Set immutableProperties = new HashSet();
018: static {
019: immutableProperties.add("parent");
020: immutableProperties.add("immediateParent");
021: immutableProperties.add("classroot");
022: immutableProperties.add("canvas");
023: }
024:
025: boolean computeMetaReferences = false;
026: Set references;
027: Set functions;
028: Set metareferences;
029: Set metafunctions;
030: String depth = "";
031:
032: public ReferenceCollector() {
033: this (false);
034: }
035:
036: public ReferenceCollector(boolean computeMetaReferences) {
037: this .computeMetaReferences = computeMetaReferences;
038: // Linked used for determinism in binary
039: this .references = new LinkedHashSet();
040: this .functions = new LinkedHashSet();
041: if (computeMetaReferences) {
042: this .metareferences = new LinkedHashSet();
043: this .metafunctions = new LinkedHashSet();
044: }
045: }
046:
047: // Convert a list of PropertyReference's to an ArrayLiteral of
048: // alternating reference/property
049: private SimpleNode rsubst(Set r) {
050: List l = new ArrayList();
051: Set added = new HashSet();
052: ParseTreePrinter ptp = new ParseTreePrinter();
053: for (Iterator i = r.iterator(); i.hasNext();) {
054: SimpleNode n = (SimpleNode) i.next();
055: String s = ptp.visit(n);
056: // Eliminate redundant constraints
057: if (!added.contains(s)) {
058: added.add(s);
059: l.add(n.get(0));
060: l.add(new ASTLiteral(((ASTIdentifier) n.get(1))
061: .getName()));
062: }
063: }
064: SimpleNode s = new ASTArrayLiteral(0);
065: s.setChildren((SimpleNode[]) l.toArray(new SimpleNode[0]));
066: return s;
067: }
068:
069: // f(args...) -> f["dependencies"](undefined, args...)
070: // a.f(args...) -> f["dependencies"](a, args...)
071: private SimpleNode fsubst(SimpleNode node) {
072: SimpleNode fn = node.get(0);
073: SimpleNode callee;
074: if (fn instanceof ASTPropertyIdentifierReference) {
075: callee = fn.get(0);
076: } else {
077: callee = new ASTUnaryExpression(0);
078: ASTOperator voidOp = new ASTOperator(0);
079: voidOp.setOperator(ParserConstants.VOID);
080: callee.set(0, voidOp);
081: callee.set(1, new ASTLiteral(0));
082: }
083: // the function uses #pragma "warnUndefinedReferences=false"
084: // to avoid warnings for non-existent dependencies
085: Map map = new HashMap();
086: map.put("_1", fn);
087: map.put("_2", callee);
088: map.put("_3", new Compiler.Splice(node.get(1).getChildren()));
089: // TODO: [2006-01-03 ptw] Do we really need a new Parser each time?
090: return (new Compiler.Parser())
091: .substitute(
092: "_1.hasOwnProperty('dependencies') ? _1.dependencies(this, _2, _3) : []",
093: map);
094: }
095:
096: // Concatenate references array with any results from dependeny
097: // functions
098: private SimpleNode build(Set references, Set functions) {
099: SimpleNode a = rsubst(references);
100: Map map = new HashMap();
101: Set added = new HashSet();
102: ParseTreePrinter ptp = new ParseTreePrinter();
103: for (Iterator i = functions.iterator(); i.hasNext();) {
104: SimpleNode n = (SimpleNode) i.next();
105: String s = ptp.visit(n);
106: // Eliminate redundant constraints
107: if (!added.contains(s)) {
108: added.add(s);
109: SimpleNode b = fsubst(n);
110:
111: map.put("_1", a);
112: map.put("_2", b);
113: // TODO: [2006-01-03 ptw] Do we really need a new Parser each time?
114: a = (new Compiler.Parser()).substitute("_1.concat(_2)",
115: map);
116: }
117: }
118: return a;
119: }
120:
121: public SimpleNode computeReferences(String name) {
122: // Sanitize the name
123: name = name.replace('#', '_').replace(' ', '_').replace('/',
124: '_').replace('.', '_');
125: SimpleNode d = build(references, functions);
126: if (computeMetaReferences) {
127: for (Iterator i = metareferences.iterator(); i.hasNext();) {
128: SimpleNode r = (SimpleNode) i.next();
129: if (r instanceof ASTPropertyIdentifierReference
130: && r.get(1) instanceof ASTIdentifier
131: && immutableProperties
132: .contains(((ASTIdentifier) r.get(1))
133: .getName())) {
134: metareferences.remove(r);
135: }
136: }
137: SimpleNode md = build(metareferences, metafunctions);
138: // store metadependencies as a property of deps
139: Map map = new HashMap();
140: map.put("_1", d);
141: map.put("_2", md);
142: // TODO: [2006-01-03 ptw] Do we really need a new Parser each time?
143: return (new Compiler.Parser())
144: .substitute(
145: // TODO: [2003-06-19 ptw] (krank) Have to use sanitized
146: // name here, so that substitute does not try to name
147: // the function "x"
148: // Note: Since substitute does not enforce macro
149: // hygiene, arguments.callee.d is used as a temp var
150: // (rather than declaring a var) so as not to shadow
151: // any free references in the dependencies expression
152: "function "
153: + name
154: + "_dependencies () {\n#pragma \"warnUndefinedReferences=false\"\nwith (this) { arguments.callee.d = _1; arguments.callee.d.metadependencies = _2; return arguments.callee.d;}}",
155: map);
156: } else {
157: Map map = new HashMap();
158: map.put("_1", d);
159: // TODO: [2006-01-03 ptw] Do we really need a new Parser each time?
160: return (new Compiler.Parser())
161: .substitute(
162: // TODO: [2003-06-19 ptw] (krank) Have to use sanitized
163: // name here, so that substitute does not try to name
164: // the function "x"
165: "function "
166: + name
167: + "_dependencies () {\n#pragma \"warnUndefinedReferences=false\"\nwith (this) return _1;}",
168: map);
169: }
170: }
171:
172: public void visit(SimpleNode node) {
173: visitInternal(node, references, functions);
174: }
175:
176: // Imagine how easy this would be to write with generic functions!
177: private void visitInternal(SimpleNode node, Set references,
178: Set functions) {
179: if (DebugConstraints) {
180: System.out.println(depth + node);
181: }
182: try {
183: if (DebugConstraints) {
184: depth = depth + " ";
185: }
186: if (node instanceof ASTFunctionDeclaration
187: || node instanceof ASTFunctionExpression) {
188: // Don't traverse inside nested functions
189: ;
190: } else if (node instanceof ASTIdentifier) {
191: SimpleNode p = new ASTPropertyIdentifierReference(0);
192: SimpleNode[] c = { new ASTThisReference(0), node };
193: p.setChildren(c);
194: references.add(p);
195: } else if (node instanceof ASTPropertyIdentifierReference) {
196: SimpleNode base = node.get(0);
197: if (computeMetaReferences) {
198: // Visit the base for meta-dependencies
199: visitInternal(base, metareferences, metafunctions);
200: }
201: // If the base is a function, collect its dependency function
202: if (base instanceof ASTCallExpression) {
203: functions.add(base);
204: }
205: references.add(node);
206: } else if (node instanceof ASTCallExpression) {
207: // Optimization: ignore setAttribute(...),
208: // since it's used in every constraint expression
209: // and doesn't have a dependency function (and if
210: // it did, it would always return null).
211: SimpleNode base = node.get(0);
212: if (base instanceof ASTPropertyIdentifierReference
213: && base.get(0) instanceof ASTThisReference
214: && base.get(1) instanceof ASTIdentifier
215: && "setAttribute".equals(((ASTIdentifier) base
216: .get(1)).getName())) {
217: ;
218: } else {
219: if (computeMetaReferences) {
220: // Visit the function for meta-dependencies
221: visitInternal(base, metareferences,
222: metafunctions);
223: }
224: // Collect the function's dependency function
225: functions.add(node);
226: }
227: // Visit the arguments.
228: visitInternal(node.get(1), references, functions);
229: } else {
230: for (int i = 0, len = node.size(); i < len; i++) {
231: visitInternal(node.get(i), references, functions);
232: }
233: }
234: } finally {
235: if (DebugConstraints) {
236: depth = depth.substring(0, depth.length() - 4);
237: System.out.println(depth + "references: " + references);
238: System.out.println(depth + "functions: " + functions);
239: if (computeMetaReferences) {
240: System.out.println(depth + "metareferences: "
241: + metareferences);
242: System.out.println(depth + "metafunctions: "
243: + metafunctions);
244: }
245: }
246: }
247: }
248: }
|