Package com.google.gwt.dev.jjs

Source Code of com.google.gwt.dev.jjs.LibraryJavaToJavaScriptCompiler$LibraryPrecompiler

/*
* Copyright 2014 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.jjs;

import com.google.gwt.core.ext.PropertyOracle;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.impl.ResourceGeneratorUtilImpl;
import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact;
import com.google.gwt.core.ext.linker.SyntheticArtifact;
import com.google.gwt.dev.CompilerContext;
import com.google.gwt.dev.Permutation;
import com.google.gwt.dev.cfg.BindingProperty;
import com.google.gwt.dev.cfg.ConfigurationProperty;
import com.google.gwt.dev.cfg.PropertyProviderRegistratorGenerator;
import com.google.gwt.dev.cfg.Rule;
import com.google.gwt.dev.cfg.RuleGenerateWith;
import com.google.gwt.dev.cfg.RuleReplaceWithFallback;
import com.google.gwt.dev.cfg.Rules;
import com.google.gwt.dev.cfg.RuntimeRebindRegistratorGenerator;
import com.google.gwt.dev.cfg.RuntimeRebindRuleGenerator;
import com.google.gwt.dev.javac.CompilationUnit;
import com.google.gwt.dev.javac.CompiledClass;
import com.google.gwt.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JLiteral;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JType;
import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
import com.google.gwt.dev.jjs.impl.ComputeExhaustiveCastabilityInformation;
import com.google.gwt.dev.jjs.impl.ComputeInstantiatedJsoInterfaces;
import com.google.gwt.dev.jjs.impl.Devirtualizer;
import com.google.gwt.dev.jjs.impl.EqualityNormalizer;
import com.google.gwt.dev.jjs.impl.ImplementCastsAndTypeChecks;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.jjs.impl.LongCastNormalizer;
import com.google.gwt.dev.jjs.impl.LongEmulationNormalizer;
import com.google.gwt.dev.jjs.impl.PostOptimizationCompoundAssignmentNormalizer;
import com.google.gwt.dev.jjs.impl.ReboundTypeRecorder;
import com.google.gwt.dev.jjs.impl.ReplaceGetClassOverrides;
import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences;
import com.google.gwt.dev.jjs.impl.TypeCoercionNormalizer;
import com.google.gwt.dev.jjs.impl.codesplitter.MultipleDependencyGraphRecorder;
import com.google.gwt.dev.js.JsVerboseNamer;
import com.google.gwt.dev.js.ast.JsLiteral;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNode;
import com.google.gwt.dev.resource.impl.FileResource;
import com.google.gwt.dev.util.Name.BinaryName;
import com.google.gwt.dev.util.Pair;
import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.Sets;

import java.io.File;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;

/**
* Compiles the Java <code>JProgram</code> representation into its corresponding library Js source.
* <br />
*
* Care is taken to ensure that the resulting Js source will be valid for runtime linking, such as
* performing only local optimizations, running only local stages of Generators, gathering and
* enqueueing rebind information for runtime usage and outputting Js source with names that are
* stable across libraries.
*/
public class LibraryJavaToJavaScriptCompiler extends JavaToJavaScriptCompiler {

  @VisibleForTesting
  class LibraryPermutationCompiler extends PermutationCompiler {

    public LibraryPermutationCompiler(Permutation permutation) {
      super(permutation);
    }

    @Override
    protected Map<JType, JLiteral> normalizeSemantics() {
      Devirtualizer.exec(jprogram);
      CatchBlockNormalizer.exec(jprogram);
      PostOptimizationCompoundAssignmentNormalizer.exec(jprogram);
      LongCastNormalizer.exec(jprogram);
      LongEmulationNormalizer.exec(jprogram);
      TypeCoercionNormalizer.exec(jprogram);
      ComputeExhaustiveCastabilityInformation.exec(jprogram, options.isCastCheckingDisabled());
      ComputeInstantiatedJsoInterfaces.exec(jprogram);
      ImplementCastsAndTypeChecks.exec(jprogram, options.isCastCheckingDisabled());
      ArrayNormalizer.exec(jprogram, options.isCastCheckingDisabled());
      EqualityNormalizer.exec(jprogram);
      return ResolveRuntimeTypeReferences.IntoStringLiterals.exec(jprogram);
    }

    @Override
    protected void optimizeJava() {
    }

    @Override
    protected void optimizeJs(Set<JsNode> inlinableJsFunctions)throws InterruptedException {
    }

    @Override
    protected void postNormalizationOptimizeJava() {
      // Does not prune types and functions when constructing a library since final runtime usage
      // can not be predicted.
      ReplaceGetClassOverrides.exec(jprogram);
    }

    @Override
    protected Map<JsName, JsLiteral> runDetailedNamer(PropertyOracle[] propertyOracles) {
      JsVerboseNamer.exec(jsProgram, propertyOracles);
      return null;
    }

    @Override
    protected Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> splitJsIntoFragments(
        PropertyOracle[] propertyOracles, int permutationId, JavaToJavaScriptMap jjsmap) {
      // Local control flow knowledge and the local list of RunAsyncs is not enough information to
      // be able to accurately split program fragments.
      return Pair.<SyntheticArtifact, MultipleDependencyGraphRecorder>create(null, null);
    }
  }

  @VisibleForTesting
  class LibraryPrecompiler extends Precompiler {

    public LibraryPrecompiler(RebindPermutationOracle rpo, String[] entryPointTypeNames) {
      super(rpo, entryPointTypeNames);
    }

    @Override
    protected void beforeUnifyAst(Set<String> allRootTypes)
        throws UnableToCompleteException {
      runGeneratorsToFixedPoint(rpo);

      Set<JDeclaredType> reboundTypes = gatherReboundTypes(rpo);
      buildFallbackRuntimeRebindRules(reboundTypes);
      buildSimpleRuntimeRebindRules(module.getRules());

      buildRuntimeRebindRegistrator(allRootTypes);
      buildPropertyProviderRegistrator(allRootTypes, module.getProperties().getBindingProperties(),
          module.getProperties().getConfigurationProperties());
    }

    @Override
    protected void checkEntryPoints(String[] additionalRootTypes) {
      // Library construction does not need to care whether their are or are not any entry points.
    }

    @Override
    protected void createJProgram() {
      jprogram = new JProgram(false);
    }

    @VisibleForTesting
    protected JDeclaredType ensureFullTypeLoaded(JDeclaredType type) {
      return findTypeBySourceName(BinaryName.toSourceName(type.getName()));
    }

    private JDeclaredType findType(List<JDeclaredType> types, String typeName) {
      for (JDeclaredType type : types) {
        if (BinaryName.toSourceName(type.getName()).equals(typeName)) {
          return type;
        }
      }
      return null;
    }

    // TODO(stalcup): performs much the same load logic as UnifyAst.findType(), but is necessary
    // much earlier. Replace with some single mechanism. This logic only exists to support the
    // ability to analyze whether a type is instantiable prior to creating a rebind rule that
    // attempts to instantiate it. It would be very nice to not be duplicating an instantiability
    // check here that JDT already does quite well during its own compile.
    private JDeclaredType findTypeBySourceName(String sourceTypeName) {
      Map<String, CompiledClass> compiledClassesBySourceName =
          rpo.getCompilationState().getClassFileMapBySource();
      // If the type is available as compiled source.
      if (compiledClassesBySourceName.containsKey(sourceTypeName)) {
        // Get and return it.
        CompiledClass compiledClass = compiledClassesBySourceName.get(sourceTypeName);
        return findType(compiledClass.getUnit().getTypes(), sourceTypeName);
      }
      // Otherwise if the type is available in a loaded library.
      CompilationUnit compilationUnit =
          compilerContext.getLibraryGroup().getCompilationUnitByTypeSourceName(sourceTypeName);
      if (compilationUnit != null) {
        // Get and return it.
        compilerContext.getUnitCache().add(compilationUnit);
        return findType(compilationUnit.getTypes(), sourceTypeName);
      }
      return null;
    }

    @VisibleForTesting
    protected Set<JDeclaredType> gatherReboundTypes(RebindPermutationOracle rpo) {
      Collection<CompilationUnit> compilationUnits =
          rpo.getCompilationState().getCompilationUnits();
      Set<JDeclaredType> reboundTypes = Sets.newLinkedHashSet();
      // EntryPoints are rebound but the rebind synthetisation has not occurred yet. Gather them as
      // rebound types anyway.
      for (String entryPointTypeName : entryPointTypeNames) {
        reboundTypes.add(findTypeBySourceName(entryPointTypeName));
      }

      for (CompilationUnit compilationUnit : compilationUnits) {
        for (JDeclaredType type : compilationUnit.getTypes()) {
          ReboundTypeRecorder.exec(type, reboundTypes);
        }
      }
      return reboundTypes;
    }

    protected StandardGeneratorContext getGeneratorContext() {
      return rpo.getGeneratorContext();
    }

    @VisibleForTesting
    protected Set<String> getTypeNames(Set<JDeclaredType> types) {
      Set<String> typeNames = Sets.newHashSet();
      for (JDeclaredType type : types) {
        typeNames.add(type.getName());
      }
      return typeNames;
    }

    /**
     * Runs a particular generator on the provided set of rebound types. Takes care to guard against
     * duplicate work during reruns as generation approaches a fixed point.
     */
    @VisibleForTesting
    protected void runGenerator(RuleGenerateWith generatorRule, Set<String> reboundTypeNames)
        throws UnableToCompleteException {
      for (String reboundTypeName : reboundTypeNames) {
        generatorRule.generate(logger, module.getProperties(), getGeneratorContext(),
            BinaryName.toSourceName(reboundTypeName));
      }
    }

    @VisibleForTesting
    protected void runGeneratorsToFixedPoint(RebindPermutationOracle rpo)
        throws UnableToCompleteException {
      boolean fixedPoint;
      do {
        compilerContext.getLibraryWriter()
            .markReboundTypesProcessed(getTypeNames(gatherReboundTypes(rpo)));

        fixedPoint = runGenerators();
      } while (!fixedPoint);

      // This is a horribly dirty hack to work around the fact that CssResourceGenerator uses a
      // completely nonstandard resource creation and caching mechanism that ignores the
      // GeneratorContext infrastructure. It and GenerateCssAst need to be fixed.
      for (Entry<String, File> entry :
          ResourceGeneratorUtilImpl.getGeneratedFilesByName().entrySet()) {
        String resourcePath = entry.getKey();
        File resourceFile = entry.getValue();
        compilerContext.getLibraryWriter()
            .addBuildResource(new FileResource(null, resourcePath, resourceFile));
      }
    }

    @VisibleForTesting
    void buildFallbackRuntimeRebindRules(Set<JDeclaredType> reboundTypes)
        throws UnableToCompleteException {
      // Create fallback rebinds.
      for (JDeclaredType reboundType : reboundTypes) {
        // It's possible for module A to declare rebind rules about types that were defined in
        // module B. While processing module A these types might not be loaded in their full form,
        // which would cause their instantiability analysis to be wrong. So, make sure the full
        // version of each such type has been loaded.
        // TODO(stalcup) find a way to check if a type is instantiable without having to have the
        // full version of the type loaded.
        reboundType = ensureFullTypeLoaded(reboundType);
        if (!reboundType.isInstantiable()) {
          continue;
        }
        RuleReplaceWithFallback fallbackRule =
            new RuleReplaceWithFallback(reboundType.getName().replace("$", "."));
        fallbackRule.generateRuntimeRebindClasses(logger, module, getGeneratorContext());
      }
    }

    @VisibleForTesting
    void buildPropertyProviderRegistrator(Set<String> allRootTypes,
        SortedSet<BindingProperty> bindingProperties,
        SortedSet<ConfigurationProperty> configurationProperties) throws UnableToCompleteException {
      PropertyProviderRegistratorGenerator propertyProviderRegistratorGenerator =
          new PropertyProviderRegistratorGenerator(bindingProperties, configurationProperties);
      StandardGeneratorContext generatorContext = getGeneratorContext();
      // Name based on module canonical name, to avoid collisions resulting from multiple modules
      // with the same rename.
      String propertyProviderRegistratorTypeName = propertyProviderRegistratorGenerator.generate(
          logger, generatorContext, module.getCanonicalName());
      // Ensures that unification traverses and keeps the class.
      allRootTypes.add(propertyProviderRegistratorTypeName);
      // Ensures that JProgram knows to index this class's methods so that later bootstrap
      // construction code is able to locate the FooPropertyProviderRegistrator.register() function.
      jprogram.addIndexedTypeName(propertyProviderRegistratorTypeName);
      jprogram.setPropertyProviderRegistratorTypeSourceName(propertyProviderRegistratorTypeName);
      generatorContext.finish(logger);
    }

    @VisibleForTesting
    void buildRuntimeRebindRegistrator(Set<String> allRootTypes) throws UnableToCompleteException {
      // If no runtime rebind rules were created for this library.
      if (RuntimeRebindRuleGenerator.RUNTIME_REBIND_RULE_SOURCES_BY_SHORT_NAME.isEmpty()) {
        // Then there's no need to generate a registrator to attach them to the runtime registry.
        return;
      }

      RuntimeRebindRegistratorGenerator runtimeRebindRegistratorGenerator =
          new RuntimeRebindRegistratorGenerator();
      StandardGeneratorContext generatorContext = getGeneratorContext();
      // Name based on module canonical name, to avoid collisions resulting from multiple modules
      // with the same rename.
      String runtimeRebindRegistratorTypeName = runtimeRebindRegistratorGenerator.generate(logger,
          generatorContext, module.getCanonicalName());
      // Ensures that unification traverses and keeps the class.
      allRootTypes.add(runtimeRebindRegistratorTypeName);
      // Ensures that JProgram knows to index this class's methods so that later bootstrap
      // construction code is able to locate the FooRuntimeRebindRegistrator.register() function.
      jprogram.addIndexedTypeName(runtimeRebindRegistratorTypeName);
      jprogram.setRuntimeRebindRegistratorTypeName(runtimeRebindRegistratorTypeName);
      generatorContext.finish(logger);
    }

    @VisibleForTesting
    void buildSimpleRuntimeRebindRules(Rules rules) throws UnableToCompleteException {
      // Create rebinders for rules specified in the module.
      Iterator<Rule> iterator = rules.iterator();
      while (iterator.hasNext()) {
        Rule rule = iterator.next();
        if (rule instanceof RuleGenerateWith) {
          continue;
        }
        rule.generateRuntimeRebindClasses(logger, module, getGeneratorContext());
      }
    }

    /**
     * Figures out which generators should run based on the current state and runs them. Generator
     * execution can create new opportunities for further generator execution so this function
     * should be invoked repeatedly till a fixed point is reached.<br />
     *
     * Returns whether a fixed point was reached.
     */
    private boolean runGenerators() throws UnableToCompleteException {
      boolean globalCompile = compilerContext.getOptions().shouldLink();
      Set<Rule> generatorRules = Sets.newHashSet(module.getGeneratorRules());

      TreeLogger branch = logger.branch(TreeLogger.SPAM, "running generators");

      for (Rule rule : generatorRules) {
        RuleGenerateWith generatorRule = (RuleGenerateWith) rule;
        String generatorName = generatorRule.getName();

        if (generatorRule.contentDependsOnTypes() && !globalCompile) {
          // Type unstable generators can only be safely run in the global phase.
          // TODO(stalcup): modify type unstable generators such that their output is no longer
          // unstable.
          branch.log(TreeLogger.SPAM,
              "skipping generator " + generatorName + " since it can only run in the global phase");
          continue;
        }

        if (!generatorRule.relevantPropertiesAreFinal(module.getProperties(),
            options.getFinalProperties())) {
          // Some property(s) that this generator cares about have not yet reached their final
          // value. Running the generator now would be wasted effort as it would just need to be run
          // again later anyway.
          branch.log(TreeLogger.SPAM, "skipping generator " + generatorName
              + " since properties it cares about have not reached their final values.");
          continue;
        }

        Set<String> reboundTypes = Sets.newHashSet(compilerContext.getReboundTypeSourceNames());
        Set<String> processedReboundTypeSourceNamesForGenerator =
            compilerContext.getProcessedReboundTypeSourceNames(generatorName);

        Set<String> unprocessedReboundTypeSourceNames = Sets.newHashSet(reboundTypes);
        unprocessedReboundTypeSourceNames.removeAll(processedReboundTypeSourceNamesForGenerator);
        if (unprocessedReboundTypeSourceNames.isEmpty()) {
          // All the requested rebound types have already been processed by this generator.
          branch.log(TreeLogger.SPAM, "skipping generator " + generatorName
              + " since it has already processed all requested rebound types.");
          continue;
        }

        branch.log(TreeLogger.SPAM, "running generator " + generatorName + " on "
            + unprocessedReboundTypeSourceNames.size() + " not yet processed rebound types");
        runGenerator(generatorRule, unprocessedReboundTypeSourceNames);

        // Marks the previously unprocessed types as processed.
        for (String unprocessedReboundTypeSourceName : unprocessedReboundTypeSourceNames) {
          compilerContext.getLibraryWriter().markReboundTypeProcessed(
              unprocessedReboundTypeSourceName, generatorName);
        }
      }

      // If there is output.
      if (getGeneratorContext().isDirty()) {
        // Compile and assimilate it.
        getGeneratorContext().finish(logger);
        return false;
      }

      return true;
    }
  }

  /**
   * Constructs a JavaToJavaScriptCompiler with customizations for compiling independent libraries.
   */
  public LibraryJavaToJavaScriptCompiler(TreeLogger logger, CompilerContext compilerContext) {
    super(logger, compilerContext);
  }

  @Override
  public PermutationResult compilePermutation(UnifiedAst unifiedAst, Permutation permutation)
      throws UnableToCompleteException {
    return new LibraryPermutationCompiler(permutation).compilePermutation(unifiedAst);
  }

  @Override
  public UnifiedAst precompile(RebindPermutationOracle rpo, String[] entryPointTypeNames,
      String[] additionalRootTypes, boolean singlePermutation,
      PrecompilationMetricsArtifact precompilationMetrics) throws UnableToCompleteException {
    return new LibraryPrecompiler(rpo, entryPointTypeNames).precompile(
        additionalRootTypes, singlePermutation, precompilationMetrics);
  }
}
TOP

Related Classes of com.google.gwt.dev.jjs.LibraryJavaToJavaScriptCompiler$LibraryPrecompiler

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.