EMMA Coverage Report (generated Mon Nov 01 16:48:29 PDT 2010)
[all classes][com.google.caja.ancillary.opt]

COVERAGE SUMMARY FOR SOURCE FILE [JsOptimizer.java]

nameclass, %method, %block, %line, %
JsOptimizer.java100% (1/1)70%  (7/10)51%  (181/357)53%  (40/76)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class JsOptimizer100% (1/1)70%  (7/10)51%  (181/357)53%  (40/76)
JsOptimizer (MessageQueue): void 100% (1/1)100% (9/9)100% (2/2)
addInput (Statement): JsOptimizer 100% (1/1)100% (7/7)100% (2/2)
fromFile (String): CharProducer 0%   (0/1)0%   (0/20)0%   (0/2)
js (CharProducer, MessageQueue): Block 0%   (0/1)0%   (0/5)0%   (0/1)
jsExpr (CharProducer, MessageQueue): Expression 100% (1/1)100% (13/13)100% (4/4)
jsParser (CharProducer, MessageQueue): Parser 100% (1/1)100% (28/28)100% (4/4)
main (String []): void 0%   (0/1)0%   (0/150)0%   (0/32)
optimize (): Statement 100% (1/1)100% (41/41)100% (9/9)
setEnvJson (ObjectConstructor): JsOptimizer 100% (1/1)99%  (78/79)94%  (17/18)
setRename (boolean): JsOptimizer 100% (1/1)100% (5/5)100% (2/2)

1// Copyright (C) 2009 Google Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14 
15package com.google.caja.ancillary.opt;
16 
17import com.google.caja.lexer.CharProducer;
18import com.google.caja.lexer.FilePosition;
19import com.google.caja.lexer.InputSource;
20import com.google.caja.lexer.JsLexer;
21import com.google.caja.lexer.JsTokenQueue;
22import com.google.caja.lexer.ParseException;
23import com.google.caja.parser.js.Block;
24import com.google.caja.parser.js.Expression;
25import com.google.caja.parser.js.Literal;
26import com.google.caja.parser.js.ObjProperty;
27import com.google.caja.parser.js.ObjectConstructor;
28import com.google.caja.parser.js.Parser;
29import com.google.caja.parser.js.Statement;
30import com.google.caja.parser.js.StringLiteral;
31import com.google.caja.parser.js.ValueProperty;
32import com.google.caja.render.Concatenator;
33import com.google.caja.render.JsMinimalPrinter;
34import com.google.caja.reporting.DevNullMessageQueue;
35import com.google.caja.reporting.Message;
36import com.google.caja.reporting.MessageContext;
37import com.google.caja.reporting.MessageQueue;
38import com.google.caja.reporting.RenderContext;
39import com.google.caja.reporting.SimpleMessageQueue;
40import com.google.caja.util.Lists;
41 
42import java.io.File;
43import java.io.FileInputStream;
44import java.io.IOException;
45import java.io.InputStreamReader;
46import java.util.List;
47 
48/**
49 * Optimizes JavaScript code.
50 *
51 * @author mikesamuel@gmail.com
52 */
53public class JsOptimizer {
54  private final List<Statement> compUnits = Lists.newArrayList();
55  private ParseTreeKB optimizer;
56  private boolean rename;
57  private final MessageQueue mq;
58 
59  public JsOptimizer(MessageQueue mq) { this.mq = mq; }
60 
61  /**
62   * Register an input for optimization.
63   */
64  public JsOptimizer addInput(Statement stmt) {
65    compUnits.add(stmt);
66    return this;
67  }
68 
69  /**
70   * Sets the environment file to use during optimization.
71   * The environment file contains facts about the environment in which the
72   * optimized output is expected to run, so allows for interpreter-specific
73   * optimization.
74   */
75  public JsOptimizer setEnvJson(ObjectConstructor envJson) {
76    if (optimizer == null) { optimizer = new ParseTreeKB(); }
77    List<? extends ObjProperty> props = envJson.children();
78    for (ObjProperty prop : props) {
79      // JSON had better not have getters
80      ValueProperty vprop = (ValueProperty) prop;
81      Expression value = vprop.getValueExpr().fold(false); // fold negative nums
82      if (!(value instanceof Literal)) {
83        // True for "*useragent*" property inserted by JSKB.
84        continue;
85      }
86      StringLiteral sl = vprop.getPropertyNameNode();
87      String rawExpr = sl.getValue();
88      rawExpr = " " + rawExpr.substring(1, rawExpr.length() - 1) + " ";
89      CharProducer valueCp = CharProducer.Factory.fromJsString(
90          CharProducer.Factory.fromString(rawExpr, sl.getFilePosition()));
91      try {
92        Expression expr = jsExpr(valueCp, DevNullMessageQueue.singleton());
93        optimizer.addFact(expr, Fact.is((Literal) value));
94      } catch (ParseException ex) {
95        continue;  // Triggered for browser specific extensions such as for each
96      }
97    }
98    return this;
99  }
100 
101  /**
102   * Sets a flag telling the optimizer whether to rename local variables where
103   * doing so would not change semantics on an interpreter that does not allow
104   * aliasing of {@code eval}.
105   */
106  public JsOptimizer setRename(boolean rename) {
107    this.rename = rename;
108    return this;
109  }
110 
111  /**
112   * Returns an optimized version of the concatenation of the programs
113   * registered via {@link #addInput}.
114   */
115  public Statement optimize() {
116    // TODO(mikesamuel): use the message queue passed to the ctor once Scope no
117    // longer complains about cajita.js defining cajita.
118    SimpleMessageQueue optMq = new SimpleMessageQueue();
119    Block block = new Block(FilePosition.UNKNOWN, compUnits);
120    // Do first since this improves the performance of the ConstVarInliner.
121    VarCollector.optimize(block);
122    if (optimizer != null) {
123      block = optimizer.optimize(block, optMq);
124    }
125    if (rename) {
126      // We pool after the ConstLocalOptimizer invoked by optimizer has run.
127      block = ConstantPooler.optimize(block);
128      // Now we shorten any long names introduced by the constant pooler.
129      block = new LocalVarRenamer(optMq).optimize(block);
130    }
131    // Finally we rearrange statements and convert conditionals to expressions
132    // where it will make things shorter.
133    return (Statement) StatementSimplifier.optimize(block, mq);
134  }
135 
136  public static void main(String... args) throws IOException {
137    MessageQueue mq = new SimpleMessageQueue();
138    MessageContext mc = new MessageContext();
139    JsOptimizer opt = new JsOptimizer(mq);
140    opt.setRename(true);
141    opt.setEnvJson(new ObjectConstructor(FilePosition.UNKNOWN));
142    try {
143      for (int i = 0, n = args.length; i < n; ++i) {
144        String arg = args[i];
145        if ("--norename".equals(arg)) {
146          opt.setRename(false);
147        } else if (arg.startsWith("--envjson=")) {
148          String jsonfile = arg.substring(arg.indexOf('=') + 1);
149          opt.setEnvJson((ObjectConstructor) jsExpr(fromFile(jsonfile), mq));
150        } else {
151          if ("--".equals(arg)) { ++i; }
152          for (;i < n; ++i) {
153            CharProducer cp = fromFile(args[i]);
154            mc.addInputSource(cp.getCurrentPosition().source());
155            opt.addInput(js(cp, mq));
156          }
157        }
158      }
159    } catch (ParseException ex) {
160      ex.toMessageQueue(mq);
161    }
162    Statement out = opt.optimize();
163    for (Message msg : mq.getMessages()) {
164      msg.format(mc, System.err);
165      System.err.println();
166    }
167    JsMinimalPrinter printer = new JsMinimalPrinter(
168        new Concatenator(System.out, null));
169    RenderContext rc = new RenderContext(printer).withRawObjKeys(true);
170    if (out instanceof Block) {
171      ((Block) out).renderBody(rc);
172    } else {
173      out.render(rc);
174    }
175    printer.noMoreTokens();
176  }
177 
178  private static Block js(CharProducer cp, MessageQueue mq)
179      throws ParseException {
180    return jsParser(cp, mq).parse();
181  }
182 
183  private static Expression jsExpr(CharProducer cp, MessageQueue mq)
184      throws ParseException {
185    Parser p = jsParser(cp, mq);
186    Expression e = p.parseExpression(true);
187    p.getTokenQueue().expectEmpty();
188    return e;
189  }
190 
191  private static Parser jsParser(CharProducer cp, MessageQueue mq) {
192    JsLexer lexer = new JsLexer(cp, false);
193    JsTokenQueue tq = new JsTokenQueue(lexer, cp.getCurrentPosition().source());
194    tq.setInputRange(cp.filePositionForOffsets(cp.getOffset(), cp.getLimit()));
195    return new Parser(tq, mq);
196  }
197 
198  private static CharProducer fromFile(String path) throws IOException {
199    File f = new File(path);
200    return CharProducer.Factory.create(
201        new InputStreamReader(new FileInputStream(f), "UTF-8"),
202        new InputSource(f.toURI()));
203  }
204}

[all classes][com.google.caja.ancillary.opt]
EMMA 2.0.5312 (C) Vladimir Roubtsov