Package com.google.gwt.dev

Source Code of com.google.gwt.dev.Precompile$DistillerRebindPermutationOracle

/*
* 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.javac.StandardGeneratorContext;
import com.google.gwt.dev.jdt.RebindOracle;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jjs.AbstractCompiler;
import com.google.gwt.dev.jjs.JJSOptions;
import com.google.gwt.dev.jjs.JJSOptionsImpl;
import com.google.gwt.dev.jjs.JavaScriptCompiler;
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.StandardRebindOracle;
import com.google.gwt.dev.shell.CheckForUpdates.UpdateResult;
import com.google.gwt.dev.util.Memory;
import com.google.gwt.dev.util.PerfLogger;
import com.google.gwt.dev.util.Util;
import com.google.gwt.dev.util.arg.ArgHandlerCompileReport;
import com.google.gwt.dev.util.arg.ArgHandlerDisableAggressiveOptimization;
import com.google.gwt.dev.util.arg.ArgHandlerDisableCastChecking;
import com.google.gwt.dev.util.arg.ArgHandlerDisableClassMetadata;
import com.google.gwt.dev.util.arg.ArgHandlerDisableRunAsync;
import com.google.gwt.dev.util.arg.ArgHandlerDisableUpdateCheck;
import com.google.gwt.dev.util.arg.ArgHandlerDraftCompile;
import com.google.gwt.dev.util.arg.ArgHandlerDumpSignatures;
import com.google.gwt.dev.util.arg.ArgHandlerEnableAssertions;
import com.google.gwt.dev.util.arg.ArgHandlerGenDir;
import com.google.gwt.dev.util.arg.ArgHandlerMaxPermsPerPrecompile;
import com.google.gwt.dev.util.arg.ArgHandlerScriptStyle;
import com.google.gwt.dev.util.arg.ArgHandlerSoyc;
import com.google.gwt.dev.util.arg.ArgHandlerSoycDetailed;
import com.google.gwt.dev.util.arg.ArgHandlerValidateOnlyFlag;
import com.google.gwt.dev.util.arg.OptionDisableUpdateCheck;
import com.google.gwt.dev.util.arg.OptionDumpSignatures;
import com.google.gwt.dev.util.arg.OptionGenDir;
import com.google.gwt.dev.util.arg.OptionMaxPermsPerPrecompile;
import com.google.gwt.dev.util.arg.OptionValidateOnly;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
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;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;

/**
* 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,
      OptionDumpSignatures, OptionMaxPermsPerPrecompile {
  }

  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 ArgHandlerDisableClassMetadata(options));
      registerHandler(new ArgHandlerDisableCastChecking(options));
      registerHandler(new ArgHandlerValidateOnlyFlag(options));
      registerHandler(new ArgHandlerDisableRunAsync(options));
      registerHandler(new ArgHandlerDraftCompile(options));
      registerHandler(new ArgHandlerDisableUpdateCheck(options));
      registerHandler(new ArgHandlerDumpSignatures(options));
      registerHandler(new ArgHandlerMaxPermsPerPrecompile(options));
      registerHandler(new ArgHandlerCompileReport(options));
      registerHandler(new ArgHandlerSoyc(options));
      registerHandler(new ArgHandlerSoycDetailed(options));
    }

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

  static class PrecompileOptionsImpl extends CompileTaskOptionsImpl implements
      PrecompileOptions {
    private boolean disableUpdateCheck;
    private File dumpFile;
    private File genDir;
    private final JJSOptionsImpl jjsOptions = new JJSOptionsImpl();
    private int maxPermsPerPrecompile;
    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());
      setDumpSignatureFile(other.getDumpSignatureFile());
      setGenDir(other.getGenDir());
      setMaxPermsPerPrecompile(other.getMaxPermsPerPrecompile());
      setValidateOnly(other.isValidateOnly());
    }

    public File getDumpSignatureFile() {
      return dumpFile;
    }

    public File getGenDir() {
      return genDir;
    }

    public int getMaxPermsPerPrecompile() {
      return maxPermsPerPrecompile;
    }

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

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

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

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

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

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

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

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

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

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

    public boolean isUpdateCheckDisabled() {
      return disableUpdateCheck;
    }

    public boolean isValidateOnly() {
      return validateOnly;
    }

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

    public void setCastCheckingDisabled(boolean disabled) {
      jjsOptions.setCastCheckingDisabled(disabled);
    }

    public void setClassMetadataDisabled(boolean disabled) {
      jjsOptions.setClassMetadataDisabled(disabled);
    }

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

    public void setDraftCompile(boolean draft) {
      jjsOptions.setDraftCompile(draft);
    }

    public void setDumpSignatureFile(File dumpFile) {
      this.dumpFile = dumpFile;
    }

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

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

    public void setMaxPermsPerPrecompile(int maxPermsPerPrecompile) {
      this.maxPermsPerPrecompile = maxPermsPerPrecompile;
    }

    public void setOptimizePrecompile(boolean optimize) {
      jjsOptions.setOptimizePrecompile(optimize);
    }

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

    public void setRunAsyncEnabled(boolean enabled) {
      jjsOptions.setRunAsyncEnabled(enabled);
    }

    public void setSoycEnabled(boolean enabled) {
      jjsOptions.setSoycEnabled(enabled);
    }

    public void setSoycExtra(boolean soycExtra) {
      jjsOptions.setSoycExtra(soycExtra);
    }

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

  private static class DistillerRebindPermutationOracle implements
      RebindPermutationOracle {

    private CompilationState compilationState;
    private StandardGeneratorContext generatorContext;
    private final Permutation[] permutations;
    private final StaticPropertyOracle[] propertyOracles;
    private final RebindOracle[] rebindOracles;

    public DistillerRebindPermutationOracle(ModuleDef module,
        CompilationState compilationState, ArtifactSet generatorArtifacts,
        PropertyPermutations perms, File genDir, File generatorResourcesDir) {
      this.compilationState = compilationState;
      permutations = new Permutation[perms.size()];
      propertyOracles = new StaticPropertyOracle[perms.size()];
      rebindOracles = new RebindOracle[perms.size()];
      generatorContext = new StandardGeneratorContext(compilationState, module,
          genDir, generatorResourcesDir, generatorArtifacts);
      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(propertyOracles[i], rules,
            generatorContext);
        permutations[i] = new Permutation(i, propertyOracles[i]);
      }
    }

    public void clear() {
      generatorContext.clear();
      compilationState = null;
      generatorContext = null;
    }

    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 CompilationState getCompilationState() {
      return compilationState;
    }

    public StandardGeneratorContext getGeneratorContext() {
      return generatorContext;
    }

    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];
    }
  }

  /**
   * The file name for the result of Precompile.
   */
  public static final String PRECOMPILE_FILENAME = "precompilation.ser";

  static final String PERM_COUNT_FILENAME = "permCount.txt";

  /**
   * Performs a command-line precompile.
   */
  public static void main(String[] args) {
    Memory.initialize();
    if (System.getProperty("gwt.jjs.dumpAst") != null) {
      System.out.println("Will dump AST to: "
          + System.getProperty("gwt.jjs.dumpAst"));
    }

    /*
     * 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 = CheckForUpdates.checkForUpdatesInBackgroundThread(logger,
                CheckForUpdates.ONE_DAY);
          }
          boolean success = new Precompile(options).run(logger);
          if (success) {
            CheckForUpdates.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, File dumpSignatureFile) {
    PropertyPermutations allPermutations = new PropertyPermutations(
        module.getProperties());
    return precompile(logger, jjsOptions, module, 0, allPermutations, genDir,
        generatorResourcesDir, dumpSignatureFile);
  }

  /**
   * 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,
      File dumpSignatureFile) {
    try {
      CompilationState compilationState = module.getCompilationState(logger);
      if (dumpSignatureFile != null) {
        // Dump early to avoid generated types.
        SignatureDumper.dumpSignatures(logger,
            compilationState.getTypeOracle(), dumpSignatureFile);
      }

      String[] declEntryPts = module.getEntryPointTypeNames();
      String[] additionalRootTypes = null;
      if (declEntryPts.length == 0) {
        // No declared entry points, just validate all visible classes.
        Collection<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);
      // Allow GC later.
      compilationState = null;
      if (dumpSignatureFile != null) {
        // Dump early to avoid generated types.
        SignatureDumper.dumpSignatures(logger,
            compilationState.getTypeOracle(), dumpSignatureFile);
      }
      // Never optimize on a validation run.
      jjsOptions.setOptimizePrecompile(false);
      getCompiler(module).precompile(logger, module, rpo, declEntryPts,
          additionalRootTypes, jjsOptions, true);
      return true;
    } catch (UnableToCompleteException e) {
      // Already logged.
      return false;
    }
  }

  private static AbstractCompiler getCompiler(ModuleDef module) {
    ConfigurationProperty compilerClassProp = module.getProperties().createConfiguration(
        "x.compiler.class", false);
    String compilerClassName = compilerClassProp.getValue();
    if (compilerClassName == null || compilerClassName.length() == 0) {
      return new JavaScriptCompiler();
    }
    Throwable caught;
    try {
      Class<?> compilerClass = Class.forName(compilerClassName);
      return (AbstractCompiler) compilerClass.newInstance();
    } catch (ClassNotFoundException e) {
      caught = e;
    } catch (InstantiationException e) {
      caught = e;
    } catch (IllegalAccessException e) {
      caught = e;
    }
    throw new RuntimeException("Unable to instantiate compiler class '"
        + compilerClassName + "'", caught);
  }

  private static Precompilation precompile(TreeLogger logger,
      JJSOptions jjsOptions, ModuleDef module, int permutationBase,
      PropertyPermutations allPermutations, File genDir,
      File generatorResourcesDir, File dumpSignatureFile) {

    try {
      CompilationState compilationState = module.getCompilationState(logger);
      if (dumpSignatureFile != null) {
        // Dump early to avoid generated types.
        SignatureDumper.dumpSignatures(logger,
            compilationState.getTypeOracle(), dumpSignatureFile);
      }

      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, allPermutations,
          genDir, generatorResourcesDir);
      // Allow GC later.
      compilationState = null;
      PerfLogger.start("Precompile");
      UnifiedAst unifiedAst = getCompiler(module).precompile(logger,
          module, rpo, 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>();
      SortedSet<String> liveRebindRequests = unifiedAst.getRebindRequests();
      for (Permutation permutation : permutations) {
        // Construct a key from the stringified map of live rebind answers.
        SortedMap<String, String> rebindAnswers = new TreeMap<String, String>(
            permutation.getRebindAnswers());
        rebindAnswers.keySet().retainAll(liveRebindRequests);
        String key = rebindAnswers.toString();
        if (merged.containsKey(key)) {
          Permutation existing = merged.get(key);
          existing.mergeFrom(permutation, liveRebindRequests);
        } else {
          merged.put(key, permutation);
        }
      }
      return new Precompilation(unifiedAst, merged.values(), permutationBase,
          generatedArtifacts);
    } catch (UnableToCompleteException e) {
      // We intentionally don't pass in the exception here since the real
      // cause has been logged.
      return null;
    }
  }

  private final PrecompileOptionsImpl options;

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

  public boolean run(TreeLogger logger) throws UnableToCompleteException {
    // Avoid early optimizations since permutation compiles will run separately.
    options.setOptimizePrecompile(false);

    for (String moduleName : options.getModuleNames()) {
      File compilerWorkDir = options.getCompilerWorkDir(moduleName);
      Util.recursiveDelete(compilerWorkDir, true);
      // No need to check mkdirs result because an IOException will occur anyway
      compilerWorkDir.mkdirs();

      JarOutputStream precompilationJar;
      try {
        precompilationJar = new JarOutputStream(new FileOutputStream(new File(
            compilerWorkDir, PRECOMPILE_FILENAME)));
      } catch (IOException e) {
        logger.log(TreeLogger.ERROR, "Could not create " + PRECOMPILE_FILENAME,
            e);
        return false;
      }

      ModuleDef 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, options.getDumpSignatureFile())) {
          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());
        PropertyPermutations allPermutations = new PropertyPermutations(
            module.getProperties());
        int potentialPermutations = allPermutations.size();
        int permutationsPerIteration = options.getMaxPermsPerPrecompile();

        if (permutationsPerIteration <= 0) {
          permutationsPerIteration = potentialPermutations;
        }
        /*
         * The potential number of permutations to precompile >= the actual
         * number of permutations that end up being precompiled, because some of
         * the permutations might collapse due to identical rebind results. So
         * we have to track these two counts and ids separately.
         */
        int actualPermutations = 0;
        for (int potentialFirstPerm = 0; potentialFirstPerm < potentialPermutations; potentialFirstPerm += permutationsPerIteration) {
          int numPermsToPrecompile = Math.min(potentialPermutations
              - potentialFirstPerm, permutationsPerIteration);

          // Select only the range of property permutations that we want
          PropertyPermutations localPermutations = new PropertyPermutations(
              allPermutations, potentialFirstPerm, numPermsToPrecompile);

          Precompilation precompilation = precompile(branch, options, module,
              actualPermutations, localPermutations, options.getGenDir(),
              compilerWorkDir, options.getDumpSignatureFile());
          if (precompilation == null) {
            branch.log(TreeLogger.ERROR, "Precompilation failed");
            return false;
          }
          int actualNumPermsPrecompiled = precompilation.getPermutations().length;
          String precompilationFilename = PrecompilationFile.fileNameForPermutations(
              actualPermutations, actualNumPermsPrecompiled);
          try {
            precompilationJar.putNextEntry(new ZipEntry(precompilationFilename));
            Util.writeObjectToStream(precompilationJar, precompilation);
          } catch (IOException e) {
            branch.log(TreeLogger.ERROR,
                "Failed to write a precompilation result", e);
            return false;
          }

          actualPermutations += actualNumPermsPrecompiled;
          branch.log(TreeLogger.DEBUG, "Compiled " + actualNumPermsPrecompiled
              + " permutations starting from " + potentialFirstPerm);
        }

        try {
          precompilationJar.close();
        } catch (IOException e) {
          branch.log(TreeLogger.ERROR, "Failed to finalize "
              + PRECOMPILE_FILENAME, e);
          return false;
        }

        Util.writeStringAsFile(branch, new File(compilerWorkDir,
            PERM_COUNT_FILENAME), String.valueOf(actualPermutations));
        branch.log(TreeLogger.INFO,
            "Precompilation succeeded, number of permutations: "
                + actualPermutations);
      }
    }
    return true;
  }
}
TOP

Related Classes of com.google.gwt.dev.Precompile$DistillerRebindPermutationOracle

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.