Package com.google.caja.ancillary.opt

Source Code of com.google.caja.ancillary.opt.JsOptimizer

// Copyright (C) 2009 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.caja.ancillary.opt;

import com.google.caja.lexer.CharProducer;
import com.google.caja.lexer.FilePosition;
import com.google.caja.lexer.InputSource;
import com.google.caja.lexer.JsLexer;
import com.google.caja.lexer.JsTokenQueue;
import com.google.caja.lexer.ParseException;
import com.google.caja.parser.js.Block;
import com.google.caja.parser.js.Expression;
import com.google.caja.parser.js.Literal;
import com.google.caja.parser.js.ObjProperty;
import com.google.caja.parser.js.ObjectConstructor;
import com.google.caja.parser.js.Parser;
import com.google.caja.parser.js.Statement;
import com.google.caja.parser.js.StringLiteral;
import com.google.caja.parser.js.ValueProperty;
import com.google.caja.render.Concatenator;
import com.google.caja.render.JsMinimalPrinter;
import com.google.caja.reporting.DevNullMessageQueue;
import com.google.caja.reporting.Message;
import com.google.caja.reporting.MessageContext;
import com.google.caja.reporting.MessageQueue;
import com.google.caja.reporting.PropertyNameQuotingMode;
import com.google.caja.reporting.RenderContext;
import com.google.caja.reporting.SimpleMessageQueue;
import com.google.caja.util.Lists;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.List;

/**
* Optimizes JavaScript code.
*
* @author mikesamuel@gmail.com
*/
public class JsOptimizer {
  private final List<Statement> compUnits = Lists.newArrayList();
  private ParseTreeKB optimizer;
  private boolean rename;
  private final MessageQueue mq;

  public JsOptimizer(MessageQueue mq) { this.mq = mq; }

  /**
   * Register an input for optimization.
   */
  public JsOptimizer addInput(Statement stmt) {
    compUnits.add(stmt);
    return this;
  }

  /**
   * Sets the environment file to use during optimization.
   * The environment file contains facts about the environment in which the
   * optimized output is expected to run, so allows for interpreter-specific
   * optimization.
   */
  public JsOptimizer setEnvJson(ObjectConstructor envJson) {
    if (optimizer == null) { optimizer = new ParseTreeKB(); }
    List<? extends ObjProperty> props = envJson.children();
    for (ObjProperty prop : props) {
      // JSON had better not have getters
      ValueProperty vprop = (ValueProperty) prop;
      Expression value = vprop.getValueExpr().fold(false); // fold negative nums
      if (!(value instanceof Literal)) {
        // True for "*useragent*" property inserted by JSKB.
        continue;
      }
      StringLiteral sl = vprop.getPropertyNameNode();
      String rawExpr = sl.getValue();
      rawExpr = " " + rawExpr.substring(1, rawExpr.length() - 1) + " ";
      CharProducer valueCp = CharProducer.Factory.fromJsString(
          CharProducer.Factory.fromString(rawExpr, sl.getFilePosition()));
      try {
        Expression expr = jsExpr(valueCp, DevNullMessageQueue.singleton());
        optimizer.addFact(expr, Fact.is((Literal) value));
      } catch (ParseException ex) {
        continue// Triggered for browser specific extensions such as for each
      }
    }
    return this;
  }

  /**
   * Sets a flag telling the optimizer whether to rename local variables where
   * doing so would not change semantics on an interpreter that does not allow
   * aliasing of {@code eval}.
   */
  public JsOptimizer setRename(boolean rename) {
    this.rename = rename;
    return this;
  }

  /**
   * Returns an optimized version of the concatenation of the programs
   * registered via {@link #addInput}.
   */
  public Statement optimize() {
    Block block = new Block(FilePosition.UNKNOWN, compUnits);
    // Do first since this improves the performance of the ConstVarInliner.
    VarCollector.optimize(block);
    if (optimizer != null) {
      block = optimizer.optimize(block, mq);
    }
    if (rename) {
      // We pool after the ConstLocalOptimizer invoked by optimizer has run.
      block = ConstantPooler.optimize(block);
      // Now we shorten any long names introduced by the constant pooler.
      block = new LocalVarRenamer(mq).optimize(block);
    }
    // Finally we rearrange statements and convert conditionals to expressions
    // where it will make things shorter.
    return (Statement) StatementSimplifier.optimize(block, mq);
  }

  public static void main(String... args) throws IOException {
    MessageQueue mq = new SimpleMessageQueue();
    MessageContext mc = new MessageContext();
    JsOptimizer opt = new JsOptimizer(mq);
    opt.setRename(true);
    opt.setEnvJson(new ObjectConstructor(FilePosition.UNKNOWN));
    try {
      for (int i = 0, n = args.length; i < n; ++i) {
        String arg = args[i];
        if ("--norename".equals(arg)) {
          opt.setRename(false);
        } else if (arg.startsWith("--envjson=")) {
          String jsonfile = arg.substring(arg.indexOf('=') + 1);
          opt.setEnvJson((ObjectConstructor) jsExpr(fromFile(jsonfile), mq));
        } else {
          if ("--".equals(arg)) { ++i; }
          for (;i < n; ++i) {
            CharProducer cp = fromFile(args[i]);
            mc.addInputSource(cp.getCurrentPosition().source());
            opt.addInput(js(cp, mq));
          }
        }
      }
    } catch (ParseException ex) {
      ex.toMessageQueue(mq);
    }
    Statement out = opt.optimize();
    for (Message msg : mq.getMessages()) {
      msg.format(mc, System.err);
      System.err.println();
    }
    JsMinimalPrinter printer = new JsMinimalPrinter(
        new Concatenator(System.out, null));
    RenderContext rc = new RenderContext(printer)
        .withPropertyNameQuotingMode(PropertyNameQuotingMode.NO_QUOTES);
    if (out instanceof Block) {
      ((Block) out).renderBody(rc);
    } else {
      out.render(rc);
    }
    printer.noMoreTokens();
  }

  private static Block js(CharProducer cp, MessageQueue mq)
      throws ParseException {
    return jsParser(cp, mq).parse();
  }

  private static Expression jsExpr(CharProducer cp, MessageQueue mq)
      throws ParseException {
    Parser p = jsParser(cp, mq);
    Expression e = p.parseExpression(true);
    p.getTokenQueue().expectEmpty();
    return e;
  }

  private static Parser jsParser(CharProducer cp, MessageQueue mq) {
    JsLexer lexer = new JsLexer(cp, false);
    JsTokenQueue tq = new JsTokenQueue(lexer, cp.getCurrentPosition().source());
    tq.setInputRange(cp.filePositionForOffsets(cp.getOffset(), cp.getLimit()));
    return new Parser(tq, mq);
  }

  private static CharProducer fromFile(String path) throws IOException {
    File f = new File(path);
    return CharProducer.Factory.create(
        new InputStreamReader(new FileInputStream(f), "UTF-8"),
        new InputSource(f.toURI()));
  }
}
TOP

Related Classes of com.google.caja.ancillary.opt.JsOptimizer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.