Precompile.java :  » Ajax » GWT » com » google » gwt » dev » Java Open Source

Java Open Source » Ajax » GWT 
GWT » com » google » gwt » dev » Precompile.java
/*
 * Copyright 2008 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.gwt.dev;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ConfigurationProperty;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
import com.google.gwt.dev.cfg.PropertyPermutations;
import com.google.gwt.dev.cfg.Rules;
import com.google.gwt.dev.cfg.StaticPropertyOracle;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompilationUnit;
import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd;
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.jjs.JJSOptionsImpl;
import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.JsOutputOption;
import com.google.gwt.dev.jjs.UnifiedAst;
import com.google.gwt.dev.shell.CheckForUpdates;
import com.google.gwt.dev.shell.PlatformSpecific;
import com.google.gwt.dev.shell.StandardRebindOracle;
import com.google.gwt.dev.shell.CheckForUpdates.UpdateResult;
import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
import com.google.gwt.dev.util.arg.ArgHandlerDisableUpdateCheck;
import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
import com.google.gwt.dev.util.arg.ArgHandlerValidateOnlyFlag;
import com.google.gwt.dev.util.arg.OptionDisableUpdateCheck;
import com.google.gwt.dev.util.arg.OptionGenDir;
import com.google.gwt.dev.util.arg.OptionValidateOnly;

import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.concurrent.FutureTask;

/**
 * Performs the first phase of compilation, generating the set of permutations
 * to compile, and a ready-to-compile AST.
 */
public class Precompile {

  /**
   * The set of options for the precompiler.
   */
  public interface PrecompileOptions extends JJSOptions, CompileTaskOptions,
      OptionGenDir, OptionValidateOnly, OptionDisableUpdateCheck {
  }

  static class ArgProcessor extends CompileArgProcessor {
    public ArgProcessor(PrecompileOptions options) {
      super(options);
      registerHandler(new ArgHandlerGenDir(options));
      registerHandler(new ArgHandlerScriptStyle(options));
      registerHandler(new ArgHandlerEnableAssertions(options));
      registerHandler(new ArgHandlerDisableAggressiveOptimization(options));
      registerHandler(new ArgHandlerValidateOnlyFlag(options));
      registerHandler(new ArgHandlerDisableUpdateCheck(options));
    }

    @Override
    protected String getName() {
      return Precompile.class.getName();
    }
  }

  static class PrecompileOptionsImpl extends CompileTaskOptionsImpl implements
      PrecompileOptions {
    private boolean disableUpdateCheck;
    private File genDir;
    private final JJSOptionsImpl jjsOptions = new JJSOptionsImpl();
    private boolean validateOnly;

    public PrecompileOptionsImpl() {
    }

    public PrecompileOptionsImpl(PrecompileOptions other) {
      copyFrom(other);
    }

    public void copyFrom(PrecompileOptions other) {
      super.copyFrom(other);

      jjsOptions.copyFrom(other);

      setDisableUpdateCheck(other.isUpdateCheckDisabled());
      setGenDir(other.getGenDir());
      setValidateOnly(other.isValidateOnly());
    }

    public File getGenDir() {
      return genDir;
    }

    public JsOutputOption getOutput() {
      return jjsOptions.getOutput();
    }

    public boolean isAggressivelyOptimize() {
      return jjsOptions.isAggressivelyOptimize();
    }

    public boolean isEnableAssertions() {
      return jjsOptions.isEnableAssertions();
    }

    public boolean isUpdateCheckDisabled() {
      return disableUpdateCheck;
    }

    public boolean isValidateOnly() {
      return validateOnly;
    }

    public void setAggressivelyOptimize(boolean aggressivelyOptimize) {
      jjsOptions.setAggressivelyOptimize(aggressivelyOptimize);
    }

    public void setDisableUpdateCheck(boolean disabled) {
      disableUpdateCheck = disabled;
    }

    public void setEnableAssertions(boolean enableAssertions) {
      jjsOptions.setEnableAssertions(enableAssertions);
    }

    public void setGenDir(File genDir) {
      this.genDir = genDir;
    }

    public void setOutput(JsOutputOption output) {
      jjsOptions.setOutput(output);
    }

    public void setValidateOnly(boolean validateOnly) {
      this.validateOnly = validateOnly;
    }
  }

  private static class DistillerRebindPermutationOracle implements
      RebindPermutationOracle {

    private Permutation[] permutations;
    private StaticPropertyOracle[] propertyOracles;
    private RebindOracle[] rebindOracles;

    public DistillerRebindPermutationOracle(ModuleDef module,
        CompilationState compilationState, ArtifactSet generatorArtifacts,
        PropertyPermutations perms, File genDir, File generatorResourcesDir) {
      permutations = new Permutation[perms.size()];
      propertyOracles = new StaticPropertyOracle[perms.size()];
      rebindOracles = new RebindOracle[perms.size()];
      BindingProperty[] orderedProps = perms.getOrderedProperties();
      SortedSet<ConfigurationProperty> configPropSet = module.getProperties().getConfigurationProperties();
      ConfigurationProperty[] configProps = configPropSet.toArray(new ConfigurationProperty[configPropSet.size()]);
      Rules rules = module.getRules();
      for (int i = 0; i < rebindOracles.length; ++i) {
        String[] orderedPropValues = perms.getOrderedPropertyValues(i);
        propertyOracles[i] = new StaticPropertyOracle(orderedProps,
            orderedPropValues, configProps);
        rebindOracles[i] = new StandardRebindOracle(compilationState,
            propertyOracles[i], module, rules, genDir, generatorResourcesDir,
            generatorArtifacts);
        permutations[i] = new Permutation(i, propertyOracles[i]);
      }
    }

    public String[] getAllPossibleRebindAnswers(TreeLogger logger,
        String requestTypeName) throws UnableToCompleteException {

      String msg = "Computing all possible rebind results for '"
          + requestTypeName + "'";
      logger = logger.branch(TreeLogger.DEBUG, msg, null);

      Set<String> answers = new HashSet<String>();

      for (int i = 0; i < getPermuationCount(); ++i) {
        String resultTypeName = rebindOracles[i].rebind(logger, requestTypeName);
        answers.add(resultTypeName);
        // Record the correct answer into each permutation.
        permutations[i].putRebindAnswer(requestTypeName, resultTypeName);
      }
      return Util.toArray(String.class, answers);
    }

    public int getPermuationCount() {
      return rebindOracles.length;
    }

    public Permutation[] getPermutations() {
      return permutations;
    }

    public StaticPropertyOracle getPropertyOracle(int permNumber) {
      return propertyOracles[permNumber];
    }

    public RebindOracle getRebindOracle(int permNumber) {
      return rebindOracles[permNumber];
    }
  }

  static final String PERM_COUNT_FILENAME = "permCount.txt";

  static final String PRECOMPILATION_FILENAME = "precompilation.ser";

  /**
   * Performs a command-line precompile.
   */
  public static void main(String[] args) {
    /*
     * NOTE: main always exits with a call to System.exit to terminate any
     * non-daemon threads that were started in Generators. Typically, this is to
     * shutdown AWT related threads, since the contract for their termination is
     * still implementation-dependent.
     */
    final PrecompileOptions options = new PrecompileOptionsImpl();
    if (new ArgProcessor(options).processArgs(args)) {
      CompileTask task = new CompileTask() {
        public boolean run(TreeLogger logger) throws UnableToCompleteException {
          FutureTask<UpdateResult> updater = null;
          if (!options.isUpdateCheckDisabled()) {
            updater = PlatformSpecific.checkForUpdatesInBackgroundThread(
                logger, CheckForUpdates.ONE_DAY);
          }
          boolean success = new Precompile(options).run(logger);
          if (success) {
            PlatformSpecific.logUpdateAvailable(logger, updater);
          }
          return success;
        }
      };
      if (CompileTaskRunner.runWithAppropriateLogger(options, task)) {
        // Exit w/ success code.
        System.exit(0);
      }
    }
    // Exit w/ non-success code.
    System.exit(1);
  }

  /**
   * Precompiles the given module.
   * 
   * @param logger a logger to use
   * @param jjsOptions a set of compiler options
   * @param module the module to compile
   * @param genDir optional directory to dump generated source, may be
   *          <code>null</code>
   * @param generatorResourcesDir required directory to dump generator resources
   * @return the precompilation
   */
  public static Precompilation precompile(TreeLogger logger,
      JJSOptions jjsOptions, ModuleDef module, File genDir,
      File generatorResourcesDir) {
    try {
      CompilationState compilationState = module.getCompilationState(logger);

      String[] declEntryPts = module.getEntryPointTypeNames();
      if (declEntryPts.length == 0) {
        logger.log(TreeLogger.ERROR, "Module has no entry points defined", null);
        throw new UnableToCompleteException();
      }

      ArtifactSet generatedArtifacts = new ArtifactSet();
      DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
          module, compilationState, generatedArtifacts,
          new PropertyPermutations(module.getProperties()), genDir,
          generatorResourcesDir);

      WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd(
          compilationState, rpo);
      PerfLogger.start("Precompile");
      UnifiedAst unifiedAst = JavaToJavaScriptCompiler.precompile(logger,
          frontEnd, declEntryPts, null, jjsOptions,
          rpo.getPermuationCount() == 1);
      PerfLogger.end();

      // Merge all identical permutations together.
      Permutation[] permutations = rpo.getPermutations();
      // Sort the permutations by an ordered key to ensure determinism.
      SortedMap<String, Permutation> merged = new TreeMap<String, Permutation>();
      for (Permutation permutation : permutations) {
        permutation.reduceRebindAnswers(unifiedAst.getRebindRequests());
        // Arbitrarily choose as a key the stringified map of rebind answers.
        String rebindResultsString = permutation.getRebindAnswers().toString();
        if (merged.containsKey(rebindResultsString)) {
          Permutation existing = merged.get(rebindResultsString);
          existing.mergeFrom(permutation);
        } else {
          merged.put(rebindResultsString, permutation);
        }
      }
      return new Precompilation(unifiedAst, merged.values(), generatedArtifacts);
    } catch (UnableToCompleteException e) {
      // We intentionally don't pass in the exception here since the real
      // cause has been logged.
      return null;
    }
  }

  /**
   * Validates the given module can be compiled.
   * 
   * @param logger a logger to use
   * @param jjsOptions a set of compiler options
   * @param module the module to compile
   * @param genDir optional directory to dump generated source, may be
   *          <code>null</code>
   * @param generatorResourcesDir required directory to dump generator resources
   */
  public static boolean validate(TreeLogger logger, JJSOptions jjsOptions,
      ModuleDef module, File genDir, File generatorResourcesDir) {
    try {
      CompilationState compilationState = module.getCompilationState(logger);

      String[] declEntryPts = module.getEntryPointTypeNames();
      String[] additionalRootTypes = null;
      if (declEntryPts.length == 0) {
        // No declared entry points, just validate all visible classes.
        Set<CompilationUnit> compilationUnits = compilationState.getCompilationUnits();
        additionalRootTypes = new String[compilationUnits.size()];
        int i = 0;
        for (CompilationUnit unit : compilationUnits) {
          additionalRootTypes[i++] = unit.getTypeName();
        }
      }

      ArtifactSet generatorArtifacts = new ArtifactSet();
      DistillerRebindPermutationOracle rpo = new DistillerRebindPermutationOracle(
          module, compilationState, generatorArtifacts,
          new PropertyPermutations(module.getProperties()), genDir,
          generatorResourcesDir);

      WebModeCompilerFrontEnd frontEnd = new WebModeCompilerFrontEnd(
          compilationState, rpo);
      JavaToJavaScriptCompiler.precompile(logger, frontEnd, declEntryPts,
          additionalRootTypes, jjsOptions, true);
      return true;
    } catch (UnableToCompleteException e) {
      // Already logged.
      return false;
    }
  }

  private ModuleDef module;

  private final PrecompileOptionsImpl options;

  public Precompile(PrecompileOptions options) {
    this.options = new PrecompileOptionsImpl(options);
  }

  public boolean run(TreeLogger logger) throws UnableToCompleteException {
    for (String moduleName : options.getModuleNames()) {
      File compilerWorkDir = options.getCompilerWorkDir(moduleName);
      Util.recursiveDelete(compilerWorkDir, true);
      compilerWorkDir.mkdirs();

      this.module = ModuleDefLoader.loadFromClassPath(logger, moduleName);

      // TODO: All JDT checks now before even building TypeOracle?
      module.getCompilationState(logger);

      if (options.isValidateOnly()) {
        TreeLogger branch = logger.branch(TreeLogger.INFO,
            "Validating compilation " + module.getName());
        if (!validate(branch, options, module, options.getGenDir(),
            compilerWorkDir)) {
          branch.log(TreeLogger.ERROR, "Validation failed");
          return false;
        }
        branch.log(TreeLogger.INFO, "Validation succeeded");
      } else {
        TreeLogger branch = logger.branch(TreeLogger.INFO,
            "Precompiling module " + module.getName());
        Precompilation precompilation = precompile(branch, options, module,
            options.getGenDir(), compilerWorkDir);
        if (precompilation == null) {
          branch.log(TreeLogger.ERROR, "Precompilation failed");
          return false;
        }
        Util.writeObjectAsFile(branch, new File(compilerWorkDir,
            PRECOMPILATION_FILENAME), precompilation);
        Util.writeStringAsFile(branch, new File(compilerWorkDir,
            PERM_COUNT_FILENAME),
            String.valueOf(precompilation.getPermutations().length));
        branch.log(TreeLogger.INFO,
            "Precompilation succeeded, number of permutations: "
                + precompilation.getPermutations().length);
      }
    }
    return true;
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.