Refactor GWT compiler driver.
This patch removes the remnants of Library compiler from
the compiler drivers and does some other minor cleanups.
Change-Id: I9d8a830ccaeb60d46b6026375f8be38f4af5b1f4
diff --git a/dev/core/src/com/google/gwt/dev/CompileOnePerm.java b/dev/core/src/com/google/gwt/dev/CompileOnePerm.java
index a31149d..f7230de 100644
--- a/dev/core/src/com/google/gwt/dev/CompileOnePerm.java
+++ b/dev/core/src/com/google/gwt/dev/CompileOnePerm.java
@@ -20,6 +20,7 @@
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.ModuleDefLoader;
+import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.util.PerfCounter;
import com.google.gwt.dev.util.arg.ArgHandlerPerm;
@@ -149,7 +150,8 @@
assert subPerms.length == 1;
PermutationResult permResult =
- precompilation.getUnifiedAst().compilePermutation(logger, compilerContext, subPerms[0]);
+ JavaToJavaScriptCompiler.compilePermutation(precompilation.getUnifiedAst(), logger,
+ compilerContext, subPerms[0]);
Link.linkOnePermutationToJar(logger, module, compilerContext.getPublicResourceOracle(),
precompilation.getGeneratedArtifacts(), permResult, makePermFilename(
compilerWorkDir, permId), precompilationOptions);
diff --git a/dev/core/src/com/google/gwt/dev/CompilePerms.java b/dev/core/src/com/google/gwt/dev/CompilePerms.java
index fad0dc0..964ac20 100644
--- a/dev/core/src/com/google/gwt/dev/CompilePerms.java
+++ b/dev/core/src/com/google/gwt/dev/CompilePerms.java
@@ -21,6 +21,7 @@
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.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.PermutationResult;
import com.google.gwt.dev.jjs.UnifiedAst;
import com.google.gwt.dev.util.FileBackedObject;
@@ -194,7 +195,8 @@
*/
public static PermutationResult compile(TreeLogger logger, CompilerContext compilerContext,
Permutation permutation, UnifiedAst unifiedAst) throws UnableToCompleteException {
- return unifiedAst.compilePermutation(logger, compilerContext, permutation);
+ return JavaToJavaScriptCompiler.compilePermutation(unifiedAst, logger, compilerContext,
+ permutation);
}
/**
diff --git a/dev/core/src/com/google/gwt/dev/Precompile.java b/dev/core/src/com/google/gwt/dev/Precompile.java
index d5cafde..ce835f5 100644
--- a/dev/core/src/com/google/gwt/dev/Precompile.java
+++ b/dev/core/src/com/google/gwt/dev/Precompile.java
@@ -24,14 +24,12 @@
import com.google.gwt.core.ext.linker.impl.StandardLinkerContext;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.dev.CompileTaskRunner.CompileTask;
-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.javac.CompilationState;
import com.google.gwt.dev.javac.CompilationUnit;
-import com.google.gwt.dev.jjs.AbstractCompiler;
-import com.google.gwt.dev.jjs.JavaScriptCompiler;
+import com.google.gwt.dev.jjs.JavaToJavaScriptCompiler;
import com.google.gwt.dev.jjs.UnifiedAst;
import com.google.gwt.dev.shell.CheckForUpdates;
import com.google.gwt.dev.shell.CheckForUpdates.UpdateResult;
@@ -180,7 +178,7 @@
compilationState = null;
// Never optimize on a validation run.
jjsOptions.setOptimizePrecompile(false);
- getCompiler(module).precompile(
+ JavaToJavaScriptCompiler.precompile(
logger, compilerContext, rpo, declEntryPts, additionalRootTypes, true, null);
return true;
} catch (UnableToCompleteException e) {
@@ -202,28 +200,6 @@
return collapsedPermutations;
}
- static AbstractCompiler getCompiler(ModuleDef module) {
- ConfigurationProperty compilerClassProp =
- module.getProperties().findConfigProp("x.compiler.class");
- String compilerClassName = compilerClassProp != null ? compilerClassProp.getValue() : null;
- 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);
- }
-
static Precompilation precompile(TreeLogger logger, CompilerContext compilerContext,
int permutationBase, PropertyPermutations allPermutations) {
return precompile(logger, compilerContext, permutationBase,
@@ -277,7 +253,7 @@
jjsOptions.isCompilerMetricsEnabled()
? new PrecompilationMetricsArtifact(permutationBase) : null;
UnifiedAst unifiedAst =
- getCompiler(module).precompile(logger, compilerContext, rpo, declEntryPts, null,
+ JavaToJavaScriptCompiler.precompile(logger, compilerContext, rpo, declEntryPts, null,
rpo.getPermutationCount() == 1, precompilationMetrics);
if (jjsOptions.isCompilerMetricsEnabled()) {
diff --git a/dev/core/src/com/google/gwt/dev/cfg/ConfigProps.java b/dev/core/src/com/google/gwt/dev/cfg/ConfigProps.java
index cc52757..7015ed4 100644
--- a/dev/core/src/com/google/gwt/dev/cfg/ConfigProps.java
+++ b/dev/core/src/com/google/gwt/dev/cfg/ConfigProps.java
@@ -116,6 +116,18 @@
}
/**
+ * Returns a single-valued property as a string if possible.
+ * If not set or not single-valued, returns the default value.
+ */
+ public String getString(String key, String defaultValue) {
+ List<String> values = getStrings(key);
+ if (values.size() != 1 || values.get(0) == null) {
+ return defaultValue;
+ }
+ return values.get(0);
+ }
+
+ /**
* Returns all the values of a multi-valued configuration property, or an empty list
* if not found.
*
diff --git a/dev/core/src/com/google/gwt/dev/jjs/AbstractCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/AbstractCompiler.java
deleted file mode 100644
index b94d585..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/AbstractCompiler.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright 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.gwt.dev.jjs;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact;
-import com.google.gwt.dev.CompilerContext;
-import com.google.gwt.dev.jdt.RebindPermutationOracle;
-
-/**
- * A Compiler used to compile a GWT project into artifacts.
- */
-public interface AbstractCompiler {
- /**
- * Performs a precompilation, returning an object that can then be used to
- * compile individual permutations..
- *
- * @param logger the logger to use
- * @param compilerContext shared read only compiler state
- * @param rpo the RebindPermutationOracle
- * @param declEntryPts the set of entry classes declared in a GWT module;
- * these will be automatically rebound
- * @param additionalRootTypes additional classes that should serve as code
- * roots; will not be rebound; may be <code>null</code>
- * @param singlePermutation if true, do not pre-optimize the resulting AST or
- * allow serialization of the result
- * @param precompilationMetrics if not null, the precompile with gather some
- * diagnostics for the compile.
- * @return the unified AST used to drive permutation compiles
- * @throws UnableToCompleteException if an error other than
- * {@link OutOfMemoryError} occurs
- */
- UnifiedAst precompile(TreeLogger logger, CompilerContext compilerContext,
- RebindPermutationOracle rpo, String[] declEntryPts, String[] additionalRootTypes,
- boolean singlePermutation, PrecompilationMetricsArtifact precompilationMetrics)
- throws UnableToCompleteException;
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaScriptCompiler.java
deleted file mode 100644
index aa647ec..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaScriptCompiler.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 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.gwt.dev.jjs;
-
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact;
-import com.google.gwt.dev.CompilerContext;
-import com.google.gwt.dev.jdt.RebindPermutationOracle;
-
-/**
- * Uses the default compiler {@link JavaToJavaScriptCompiler}.
- */
-public class JavaScriptCompiler implements AbstractCompiler {
-
- @Override
- public UnifiedAst precompile(TreeLogger logger, CompilerContext compilerContext,
- RebindPermutationOracle rpo, String[] declEntryPts, String[] additionalRootTypes,
- boolean singlePermutation, PrecompilationMetricsArtifact precompilationMetrics)
- throws UnableToCompleteException {
- JavaToJavaScriptCompiler javaToJavaScriptCompiler =
- new MonolithicJavaToJavaScriptCompiler(logger, compilerContext);
- return javaToJavaScriptCompiler.precompile(
- rpo, declEntryPts, additionalRootTypes, singlePermutation, precompilationMetrics);
- }
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
index 6c6337a..5956fce 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/JavaToJavaScriptCompiler.java
@@ -29,6 +29,7 @@
import com.google.gwt.core.ext.soyc.SourceMapRecorder;
import com.google.gwt.core.ext.soyc.coderef.DependencyGraphRecorder;
import com.google.gwt.core.ext.soyc.coderef.EntityRecorder;
+import com.google.gwt.core.ext.soyc.impl.DependencyRecorder;
import com.google.gwt.core.ext.soyc.impl.SizeMapRecorder;
import com.google.gwt.core.ext.soyc.impl.SplitPointRecorder;
import com.google.gwt.core.ext.soyc.impl.StoryRecorder;
@@ -59,42 +60,67 @@
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JTypeOracle.StandardTypes;
import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
import com.google.gwt.dev.jjs.impl.AssertionNormalizer;
import com.google.gwt.dev.jjs.impl.AssertionRemover;
import com.google.gwt.dev.jjs.impl.AstDumper;
+import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
import com.google.gwt.dev.jjs.impl.CompileTimeConstantsReplacer;
+import com.google.gwt.dev.jjs.impl.ComputeCastabilityInformation;
+import com.google.gwt.dev.jjs.impl.ComputeExhaustiveCastabilityInformation;
+import com.google.gwt.dev.jjs.impl.ComputeInstantiatedJsoInterfaces;
+import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer;
import com.google.gwt.dev.jjs.impl.ControlFlowRecorder;
import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
+import com.google.gwt.dev.jjs.impl.Devirtualizer;
+import com.google.gwt.dev.jjs.impl.EnumNameObfuscator;
import com.google.gwt.dev.jjs.impl.EnumOrdinalizer;
+import com.google.gwt.dev.jjs.impl.EqualityNormalizer;
import com.google.gwt.dev.jjs.impl.Finalizer;
import com.google.gwt.dev.jjs.impl.FixAssignmentsToUnboxOrCast;
import com.google.gwt.dev.jjs.impl.FullOptimizerContext;
import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
+import com.google.gwt.dev.jjs.impl.HandleCrossFragmentReferences;
+import com.google.gwt.dev.jjs.impl.ImplementCastsAndTypeChecks;
import com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields;
import com.google.gwt.dev.jjs.impl.JavaAstVerifier;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
+import com.google.gwt.dev.jjs.impl.JjsUtils;
import com.google.gwt.dev.jjs.impl.JsAbstractTextTransformer;
import com.google.gwt.dev.jjs.impl.JsFunctionClusterer;
import com.google.gwt.dev.jjs.impl.JsInteropRestrictionChecker;
import com.google.gwt.dev.jjs.impl.JsNoopTransformer;
import com.google.gwt.dev.jjs.impl.JsTypeLinker;
import com.google.gwt.dev.jjs.impl.JsniRestrictionChecker;
+import com.google.gwt.dev.jjs.impl.LongCastNormalizer;
+import com.google.gwt.dev.jjs.impl.LongEmulationNormalizer;
import com.google.gwt.dev.jjs.impl.MakeCallsStatic;
import com.google.gwt.dev.jjs.impl.MethodCallSpecializer;
import com.google.gwt.dev.jjs.impl.MethodCallTightener;
import com.google.gwt.dev.jjs.impl.MethodInliner;
import com.google.gwt.dev.jjs.impl.OptimizerContext;
import com.google.gwt.dev.jjs.impl.OptimizerStats;
+import com.google.gwt.dev.jjs.impl.PostOptimizationCompoundAssignmentNormalizer;
import com.google.gwt.dev.jjs.impl.Pruner;
import com.google.gwt.dev.jjs.impl.RecordRebinds;
+import com.google.gwt.dev.jjs.impl.RemoveEmptySuperCalls;
+import com.google.gwt.dev.jjs.impl.RemoveSpecializations;
import com.google.gwt.dev.jjs.impl.ReplaceDefenderMethodReferences;
+import com.google.gwt.dev.jjs.impl.ReplaceGetClassOverrides;
import com.google.gwt.dev.jjs.impl.ResolveRebinds;
+import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences;
+import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.ClosureUniqueIdTypeMapper;
+import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.IntTypeMapper;
+import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.StringTypeMapper;
import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeMapper;
+import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeOrder;
import com.google.gwt.dev.jjs.impl.SameParameterValueOptimizer;
import com.google.gwt.dev.jjs.impl.SourceInfoCorrelator;
+import com.google.gwt.dev.jjs.impl.TypeCoercionNormalizer;
import com.google.gwt.dev.jjs.impl.TypeReferencesRecorder;
import com.google.gwt.dev.jjs.impl.TypeTightener;
import com.google.gwt.dev.jjs.impl.UnifyAst;
+import com.google.gwt.dev.jjs.impl.codesplitter.CodeSplitter;
import com.google.gwt.dev.jjs.impl.codesplitter.CodeSplitters;
import com.google.gwt.dev.jjs.impl.codesplitter.MultipleDependencyGraphRecorder;
import com.google.gwt.dev.jjs.impl.codesplitter.ReplaceRunAsyncs;
@@ -106,6 +132,7 @@
import com.google.gwt.dev.js.EvalFunctionsAtTopScope;
import com.google.gwt.dev.js.FreshNameGenerator;
import com.google.gwt.dev.js.JsBreakUpLargeVarStatements;
+import com.google.gwt.dev.js.JsDuplicateCaseFolder;
import com.google.gwt.dev.js.JsDuplicateFunctionRemover;
import com.google.gwt.dev.js.JsIncrementalNamer;
import com.google.gwt.dev.js.JsInliner;
@@ -121,6 +148,7 @@
import com.google.gwt.dev.js.JsStaticEval;
import com.google.gwt.dev.js.JsSymbolResolver;
import com.google.gwt.dev.js.JsUnusedFunctionRemover;
+import com.google.gwt.dev.js.JsVerboseNamer;
import com.google.gwt.dev.js.SizeBreakdown;
import com.google.gwt.dev.js.ast.JsContext;
import com.google.gwt.dev.js.ast.JsForIn;
@@ -147,7 +175,6 @@
import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
import com.google.gwt.soyc.SoycDashboard;
import com.google.gwt.soyc.io.ArtifactsOutputDirectory;
-import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableMap;
import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import com.google.gwt.thirdparty.guava.common.collect.Lists;
@@ -160,6 +187,7 @@
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
@@ -170,7 +198,6 @@
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
-import java.util.TreeSet;
import java.util.zip.GZIPInputStream;
import javax.xml.parsers.ParserConfigurationException;
@@ -183,1063 +210,1332 @@
* which is called once per permutation. This allow build systems the option of distributing and
* parallelizing some of the work.
*/
-public abstract class JavaToJavaScriptCompiler {
+public final class JavaToJavaScriptCompiler {
/**
- * Compile a permutation.
+ * Ending optimization passes when the rate of change has reached this value results in gaining
+ * nearly all of the impact while avoiding the long tail of costly but low-impact passes.
*/
- protected abstract class PermutationCompiler {
+ private static final float EFFICIENT_CHANGE_RATE = 0.01f;
+ /**
+ * Continuing to apply optimizations till the rate of change reaches this value causes the AST to
+ * reach a fixed point.
+ */
+ private static final int FIXED_POINT_CHANGE_RATE = 0;
+ /**
+ * Limits the number of optimization passes against the possible danger of an AST that does not
+ * converge.
+ */
+ private static final int MAX_PASSES = 100;
- protected Permutation permutation;
+ static {
+ // Preload the internal compiler exception just in case we run out of memory?.
+ InternalCompilerException.preload();
+ }
- public PermutationCompiler(Permutation permutation) {
- this.permutation = permutation;
- }
+ private final CompilerContext compilerContext;
+ private final TreeLogger logger;
+ private final ModuleDef module;
+ private final PrecompileTaskOptions options;
+ private JsProgram jsProgram;
+ private JProgram jprogram;
- /**
- * Takes as input an unresolved Java AST (a Java AST wherein all rebind result classes are
- * available and have not yet been pruned down to the set applicable for a particular
- * permutation) that was previously constructed by the Precompiler and from that constructs
- * output Js source code and related information. This Js source and related information is
- * packaged into a Permutation instance and then returned.
- *
- * Permutation compilation is INTENDED to progress as a series of stages:
- *
- * <pre>
- * 1. initialize local state
- * 2. transform unresolved Java AST to resolved Java AST
- * 3. normalize the resolved Java AST
- * 4. optimize the resolved Java AST
- * 5. construct the Js AST
- * 6. normalize the Js AST
- * 7. optimize the Js AST
- * 8. generate Js source
- * 9. construct and return a value
- * </pre>
- *
- * There are some other types of work here (mostly metrics and data gathering) which do not
- * serve the goal of output program construction. This work should really be moved into
- * subclasses or some sort of callback or plugin system so as not to visually pollute the real
- * compile logic.<br />
- *
- * Significant amounts of visitors implementing the intended above stages are triggered here but
- * in the wrong order. They have been noted for future cleanup.
+ public JavaToJavaScriptCompiler(TreeLogger logger, CompilerContext compilerContext) {
+ this.logger = logger;
+ this.compilerContext = compilerContext;
+ this.module = compilerContext.getModule();
+ this.options = compilerContext.getOptions();
+ }
+
+ public static UnifiedAst precompile(TreeLogger logger, CompilerContext compilerContext,
+ RebindPermutationOracle rpo, String[] declEntryPts, String[] additionalRootTypes,
+ boolean singlePermutation, PrecompilationMetricsArtifact precompilationMetrics)
+ throws UnableToCompleteException {
+ return new JavaToJavaScriptCompiler(logger, compilerContext).precompile(
+ rpo, declEntryPts, additionalRootTypes, singlePermutation, precompilationMetrics);
+ }
+
+ /**
+ * Compiles a particular permutation.
+ *
+ * @param logger the logger to use
+ * @param compilerContext shared read only compiler state
+ * @param permutation the permutation to compile
+ * @return the permutation result
+ * @throws UnableToCompleteException if an error other than {@link OutOfMemoryError} occurs
+ */
+ public static PermutationResult compilePermutation(UnifiedAst unifiedAst,
+ TreeLogger logger, CompilerContext compilerContext, Permutation permutation)
+ throws UnableToCompleteException {
+ JavaToJavaScriptCompiler javaToJavaScriptCompiler =
+ new JavaToJavaScriptCompiler(logger, compilerContext);
+ return javaToJavaScriptCompiler.compilePermutation(permutation, unifiedAst);
+ }
+
+ /**
+ * Takes as input an unresolved Java AST (a Java AST wherein all rebind result classes are
+ * available and have not yet been pruned down to the set applicable for a particular permutation)
+ * that was previously constructed by the Precompiler and from that constructs output Js source
+ * code and related information. This Js source and related information is packaged into a
+ * Permutation instance and then returned.
+ *
+ * Permutation compilation is INTENDED to progress as a series of stages:
+ *
+ * <pre>
+ * 1. initialize local state
+ * 2. transform unresolved Java AST to resolved Java AST
+ * 3. normalize the resolved Java AST
+ * 4. optimize the resolved Java AST
+ * 5. construct the Js AST
+ * 6. normalize the Js AST
+ * 7. optimize the Js AST
+ * 8. generate Js source
+ * 9. construct and return a value
+ * </pre>
+ *
+ * There are some other types of work here (mostly metrics and data gathering) which do not serve
+ * the goal of output program construction. This work should really be moved into subclasses or
+ * some sort of callback or plugin system so as not to visually pollute the real compile logic.<br
+ * />
+ *
+ * Significant amounts of visitors implementing the intended above stages are triggered here but
+ * in the wrong order. They have been noted for future cleanup.
+ */
+ private PermutationResult compilePermutation(Permutation permutation, UnifiedAst unifiedAst)
+ throws UnableToCompleteException {
+ Event jjsCompilePermutationEvent = SpeedTracerLogger.start(
+ CompilerEventType.JJS_COMPILE_PERMUTATION, "name", permutation.getProps().prettyPrint()
+ );
+ /*
+ * Do not introduce any new pass here unless it is logically a part of one of the 9 defined
+ * stages and is physically located in that stage.
*/
- public PermutationResult compilePermutation(UnifiedAst unifiedAst)
- throws UnableToCompleteException {
- Event jjsCompilePermutationEvent = SpeedTracerLogger.start(
- CompilerEventType.JJS_COMPILE_PERMUTATION, "name", permutation.getProps().prettyPrint()
- );
- /*
- * Do not introduce any new pass here unless it is logically a part of one of the 9 defined
- * stages and is physically located in that stage.
- */
- long permStartMs = System.currentTimeMillis();
- try {
- Event javaEvent = SpeedTracerLogger.start(CompilerEventType.PERMUTATION_JAVA);
+ long permStartMs = System.currentTimeMillis();
+ try {
+ Event javaEvent = SpeedTracerLogger.start(CompilerEventType.PERMUTATION_JAVA);
- // (1) Initialize local state.
- long startTimeMs = System.currentTimeMillis();
- PermProps props = permutation.getProps();
- int permutationId = permutation.getId();
- AST ast = unifiedAst.getFreshAst();
- jprogram = ast.getJProgram();
- jsProgram = ast.getJsProgram();
- Map<StandardSymbolData, JsName> symbolTable =
- new TreeMap<StandardSymbolData, JsName>(new SymbolData.ClassIdentComparator());
+ // (1) Initialize local state.
+ long startTimeMs = System.currentTimeMillis();
+ PermProps props = permutation.getProps();
+ int permutationId = permutation.getId();
+ AST ast = unifiedAst.getFreshAst();
+ jprogram = ast.getJProgram();
+ jsProgram = ast.getJsProgram();
+ Map<StandardSymbolData, JsName> symbolTable =
+ new TreeMap<StandardSymbolData, JsName>(new SymbolData.ClassIdentComparator());
- // TODO(stalcup): hide metrics gathering in a callback or subclass
- logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "...");
+ // TODO(stalcup): hide metrics gathering in a callback or subclass
+ logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "...");
- // (2) Transform unresolved Java AST to resolved Java AST
- ResolveRebinds.exec(jprogram, permutation.getGwtCreateAnswers());
+ // (2) Transform unresolved Java AST to resolved Java AST
+ ResolveRebinds.exec(jprogram, permutation.getGwtCreateAnswers());
- // TODO(stalcup): hide metrics gathering in a callback or subclass
- // This has to happen before optimizations because functions might
- // be optimized out; we want those marked as "not executed", not "not
- // instrumentable".
- Multimap<String, Integer> instrumentableLines = null;
- if (System.getProperty("gwt.coverage") != null) {
- instrumentableLines = BaselineCoverageGatherer.exec(jprogram);
- }
-
- // TypeOracle needs this to make decisions in several optimization passes
- jprogram.typeOracle.setJsInteropMode(compilerContext.getOptions().getJsInteropMode());
-
- // Record initial set of type->type references.
- // type->type references need to be collected in two phases, 1) before any process to the
- // AST has happened (to record for example reference to types declaring compile-time
- // constants) and 2) after all normalizations to collect synthetic references (e.g. to
- // record references to runtime classes like LongLib).
- maybeRecordReferencesAndControlFlow(false);
-
- // Replace compile time constants by their values.
- // TODO(rluble): eventually move to normizeSemantics.
- CompileTimeConstantsReplacer.exec(jprogram);
-
- // TODO(stalcup): move to after normalize.
- // (3) Optimize the resolved Java AST
- optimizeJava();
-
- // TODO(stalcup): move to before optimize.
- // (4) Normalize the resolved Java AST
- TypeMapper<?> typeMapper = normalizeSemantics();
-
- // TODO(stalcup): this stage shouldn't exist, move into optimize.
- postNormalizationOptimizeJava();
-
- // Now that the AST has stopped mutating update with the final references.
- maybeRecordReferencesAndControlFlow(true);
-
- jprogram.typeOracle.recomputeAfterOptimizations(jprogram.getDeclaredTypes());
-
- javaEvent.end();
-
- Event javaScriptEvent = SpeedTracerLogger.start(CompilerEventType.PERMUTATION_JAVASCRIPT);
-
- // (5) Construct the Js AST
- Pair<? extends JavaToJavaScriptMap, Set<JsNode>> jjsMapAndInlineableFunctions =
- GenerateJavaScriptAST.exec(logger, jprogram, jsProgram,
- compilerContext, typeMapper, symbolTable, props);
- JavaToJavaScriptMap jjsmap = jjsMapAndInlineableFunctions.getLeft();
-
- // TODO(stalcup): hide metrics gathering in a callback or subclass
- if (System.getProperty("gwt.coverage") != null) {
- CoverageInstrumentor.exec(jsProgram, instrumentableLines);
- }
-
- // (6) Normalize the Js AST
- JsNormalizer.exec(jsProgram);
-
- // TODO(stalcup): move to AST construction
- JsSymbolResolver.exec(jsProgram);
- if (options.getNamespace() == JsNamespaceOption.PACKAGE) {
- JsNamespaceChooser.exec(jsProgram, jjsmap);
- }
-
- // TODO(stalcup): move to normalization
- EvalFunctionsAtTopScope.exec(jsProgram, jjsmap);
-
- // (7) Optimize the JS AST.
- final Set<JsNode> inlinableJsFunctions = jjsMapAndInlineableFunctions.getRight();
- optimizeJs(inlinableJsFunctions);
-
- // TODO(stalcup): move to normalization
- // Must run before code splitter and namer.
- JsStackEmulator.exec(jprogram, jsProgram, props, jjsmap);
-
- // TODO(stalcup): move to normalization
- Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder =
- splitJsIntoFragments(props, permutationId, jjsmap);
-
- // TODO(stalcup): move to optimize.
- Map<JsName, JsLiteral> internedLiteralByVariableName = renameJsSymbols(props, jjsmap);
-
- // TODO(stalcup): move to normalization
- JsBreakUpLargeVarStatements.exec(jsProgram, props.getConfigProps());
-
- // (8) Generate Js source
- List<JsSourceMap> sourceInfoMaps = new ArrayList<JsSourceMap>();
- boolean isSourceMapsEnabled = props.isTrueInAnyPermutation("compiler.useSourceMaps");
- String[] jsFragments = new String[jsProgram.getFragmentCount()];
- StatementRanges[] ranges = new StatementRanges[jsFragments.length];
- SizeBreakdown[] sizeBreakdowns = options.isJsonSoycEnabled() || options.isSoycEnabled()
- || options.isCompilerMetricsEnabled() ? new SizeBreakdown[jsFragments.length] : null;
- generateJavaScriptCode(jjsmap, jsFragments, ranges, sizeBreakdowns, sourceInfoMaps,
- isSourceMapsEnabled || options.isJsonSoycEnabled());
-
- javaScriptEvent.end();
-
- // (9) Construct and return a value
- PermutationResult permutationResult =
- new PermutationResultImpl(jsFragments, permutation, makeSymbolMap(symbolTable), ranges);
-
- // TODO(stalcup): hide metrics gathering in a callback or subclass
- addSyntheticArtifacts(unifiedAst, permutation, startTimeMs, permutationId, jjsmap,
- dependenciesAndRecorder, internedLiteralByVariableName, isSourceMapsEnabled, jsFragments,
- sizeBreakdowns, sourceInfoMaps, permutationResult);
- return permutationResult;
- } catch (Throwable e) {
- throw CompilationProblemReporter.logAndTranslateException(logger, e);
- } finally {
- jjsCompilePermutationEvent.end();
- logTrackingStats();
- if (logger.isLoggable(TreeLogger.TRACE)) {
- logger.log(TreeLogger.TRACE,
- "Permutation took " + (System.currentTimeMillis() - permStartMs) + " ms");
- }
+ // TODO(stalcup): hide metrics gathering in a callback or subclass
+ // This has to happen before optimizations because functions might
+ // be optimized out; we want those marked as "not executed", not "not
+ // instrumentable".
+ Multimap<String, Integer> instrumentableLines = null;
+ if (System.getProperty("gwt.coverage") != null) {
+ instrumentableLines = BaselineCoverageGatherer.exec(jprogram);
}
- }
- private void maybeRecordReferencesAndControlFlow(boolean onlyUpdate) {
- if (options.isIncrementalCompileEnabled()) {
- // Per file compilation needs the type reference graph to construct the set of reachable
- // types when linking.
- TypeReferencesRecorder.exec(jprogram, getMinimalRebuildCache(), onlyUpdate);
- ControlFlowRecorder.exec(jprogram, getMinimalRebuildCache().getTypeEnvironment(),
- onlyUpdate);
+ // TypeOracle needs this to make decisions in several optimization passes
+ jprogram.typeOracle.setJsInteropMode(compilerContext.getOptions().getJsInteropMode());
+
+ // Record initial set of type->type references.
+ // type->type references need to be collected in two phases, 1) before any process to the
+ // AST has happened (to record for example reference to types declaring compile-time
+ // constants) and 2) after all normalizations to collect synthetic references (e.g. to
+ // record references to runtime classes like LongLib).
+ maybeRecordReferencesAndControlFlow(false);
+
+ // Replace compile time constants by their values.
+ // TODO(rluble): eventually move to normizeSemantics.
+ CompileTimeConstantsReplacer.exec(jprogram);
+
+ // TODO(stalcup): move to after normalize.
+ // (3) Optimize the resolved Java AST
+ optimizeJava();
+
+ // TODO(stalcup): move to before optimize.
+ // (4) Normalize the resolved Java AST
+ TypeMapper<?> typeMapper = normalizeSemantics();
+
+ // TODO(stalcup): this stage shouldn't exist, move into optimize.
+ postNormalizationOptimizeJava();
+
+ // Now that the AST has stopped mutating update with the final references.
+ maybeRecordReferencesAndControlFlow(true);
+
+ jprogram.typeOracle.recomputeAfterOptimizations(jprogram.getDeclaredTypes());
+
+ javaEvent.end();
+
+ Event javaScriptEvent = SpeedTracerLogger.start(CompilerEventType.PERMUTATION_JAVASCRIPT);
+
+ // (5) Construct the Js AST
+ Pair<? extends JavaToJavaScriptMap, Set<JsNode>> jjsMapAndInlineableFunctions =
+ GenerateJavaScriptAST.exec(logger, jprogram, jsProgram,
+ compilerContext, typeMapper, symbolTable, props);
+ JavaToJavaScriptMap jjsmap = jjsMapAndInlineableFunctions.getLeft();
+
+ // TODO(stalcup): hide metrics gathering in a callback or subclass
+ if (System.getProperty("gwt.coverage") != null) {
+ CoverageInstrumentor.exec(jsProgram, instrumentableLines);
}
- }
- protected abstract void optimizeJs(Set<JsNode> inlinableJsFunctions)
- throws InterruptedException;
+ // (6) Normalize the Js AST
+ JsNormalizer.exec(jsProgram);
- protected abstract void optimizeJava() throws InterruptedException;
-
- protected abstract void postNormalizationOptimizeJava();
-
- protected abstract Map<JsName, JsLiteral> runDetailedNamer(ConfigProps config)
- throws IllegalNameException;
-
- protected abstract Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> splitJsIntoFragments(
- PermProps props, int permutationId, JavaToJavaScriptMap jjsmap);
-
- private CompilationMetricsArtifact addCompilerMetricsArtifact(UnifiedAst unifiedAst,
- Permutation permutation, long startTimeMs, SizeBreakdown[] sizeBreakdowns,
- PermutationResult permutationResult) {
- CompilationMetricsArtifact compilationMetrics = null;
- // TODO: enable this when ClosureCompiler is enabled
- if (options.isCompilerMetricsEnabled()) {
- if (options.isClosureCompilerEnabled()) {
- logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
- + "-XcompilerMetric; ignoring -XcompilerMetric.");
- } else {
- compilationMetrics = new CompilationMetricsArtifact(permutation.getId());
- compilationMetrics.setCompileElapsedMilliseconds(
- System.currentTimeMillis() - startTimeMs);
- compilationMetrics.setElapsedMilliseconds(
- System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime());
- compilationMetrics.setJsSize(sizeBreakdowns);
- compilationMetrics.setPermutationDescription(permutation.getProps().prettyPrint());
- permutationResult.addArtifacts(Lists.newArrayList(
- unifiedAst.getModuleMetrics(), unifiedAst.getPrecompilationMetrics(),
- compilationMetrics));
- }
+ // TODO(stalcup): move to AST construction
+ JsSymbolResolver.exec(jsProgram);
+ if (options.getNamespace() == JsNamespaceOption.PACKAGE) {
+ JsNamespaceChooser.exec(jsProgram, jjsmap);
}
- return compilationMetrics;
- }
- private void addSourceMapArtifacts(int permutationId, JavaToJavaScriptMap jjsmap,
- Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder,
- boolean isSourceMapsEnabled, SizeBreakdown[] sizeBreakdowns,
- List<JsSourceMap> sourceInfoMaps, PermutationResult permutationResult) {
- if (options.isJsonSoycEnabled()) {
- // TODO: enable this when ClosureCompiler is enabled
- if (options.isClosureCompilerEnabled()) {
- logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
- + "-XjsonSoyc; ignoring -XjsonSoyc.");
- } else {
- // Is a super set of SourceMapRecorder.makeSourceMapArtifacts().
- permutationResult.addArtifacts(EntityRecorder.makeSoycArtifacts(
- permutationId, sourceInfoMaps, options.getSourceMapFilePrefix(),
- jjsmap, sizeBreakdowns,
- ((DependencyGraphRecorder) dependenciesAndRecorder.getRight()), jprogram));
- }
- } else if (isSourceMapsEnabled) {
- // TODO: enable this when ClosureCompiler is enabled
- if (options.isClosureCompilerEnabled()) {
- logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
- + "compiler.useSourceMaps=true; ignoring compiler.useSourceMaps=true.");
- } else {
- logger.log(TreeLogger.INFO, "Source Maps Enabled");
- permutationResult.addArtifacts(SourceMapRecorder.exec(permutationId, sourceInfoMaps,
- options.getSourceMapFilePrefix()));
- }
- }
- }
+ // TODO(stalcup): move to normalization
+ EvalFunctionsAtTopScope.exec(jsProgram, jjsmap);
- /**
- * Adds generated artifacts from previous compiles when doing per-file compiles.
- * <p>
- * All generators are run on first compile but only some very small subset are rerun on
- * recompiles. Care must be taken to ensure that all generated artifacts (such as png/html/css
- * files) are still registered for output even when no generators are run in the current
- * compile.
- */
- private void maybeAddGeneratedArtifacts(PermutationResult permutationResult) {
- if (options.isIncrementalCompileEnabled()) {
- permutationResult.addArtifacts(
- compilerContext.getMinimalRebuildCache().getGeneratedArtifacts());
- }
- }
+ // (7) Optimize the JS AST.
+ final Set<JsNode> inlinableJsFunctions = jjsMapAndInlineableFunctions.getRight();
+ optimizeJs(inlinableJsFunctions);
- private void addSoycArtifacts(UnifiedAst unifiedAst, int permutationId,
- JavaToJavaScriptMap jjsmap,
- Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder,
- Map<JsName, JsLiteral> internedLiteralByVariableName, String[] js,
- SizeBreakdown[] sizeBreakdowns,
- List<JsSourceMap> sourceInfoMaps, PermutationResult permutationResult,
- CompilationMetricsArtifact compilationMetrics)
- throws IOException, UnableToCompleteException {
- // TODO: enable this when ClosureCompiler is enabled
- if (options.isClosureCompilerEnabled()) {
- if (options.isSoycEnabled()) {
- logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
- + "-compileReport; ignoring -compileReport.");
- }
- } else {
- permutationResult.addArtifacts(makeSoycArtifacts(permutationId, js, sizeBreakdowns,
- options.isSoycExtra() ? sourceInfoMaps : null, dependenciesAndRecorder.getLeft(),
- jjsmap, internedLiteralByVariableName, unifiedAst.getModuleMetrics(),
- unifiedAst.getPrecompilationMetrics(), compilationMetrics,
- options.isSoycHtmlDisabled()));
- }
- }
+ // TODO(stalcup): move to normalization
+ // Must run before code splitter and namer.
+ JsStackEmulator.exec(jprogram, jsProgram, props, jjsmap);
- private void addSyntheticArtifacts(UnifiedAst unifiedAst, Permutation permutation,
- long startTimeMs, int permutationId, JavaToJavaScriptMap jjsmap,
- Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder,
- Map<JsName, JsLiteral> internedLiteralByVariableName, boolean isSourceMapsEnabled,
- String[] jsFragments, SizeBreakdown[] sizeBreakdowns,
- List<JsSourceMap> sourceInfoMaps, PermutationResult permutationResult)
- throws IOException, UnableToCompleteException {
+ // TODO(stalcup): move to normalization
+ Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder =
+ splitJsIntoFragments(props, permutationId, jjsmap);
- assert internedLiteralByVariableName != null;
+ // TODO(stalcup): move to optimize.
+ Map<JsName, JsLiteral> internedLiteralByVariableName = renameJsSymbols(props, jjsmap);
- Event event = SpeedTracerLogger.start(CompilerEventType.PERMUTATION_ARTIFACTS);
+ // TODO(stalcup): move to normalization
+ JsBreakUpLargeVarStatements.exec(jsProgram, props.getConfigProps());
- CompilationMetricsArtifact compilationMetrics = addCompilerMetricsArtifact(
- unifiedAst, permutation, startTimeMs, sizeBreakdowns, permutationResult);
- addSoycArtifacts(unifiedAst, permutationId, jjsmap, dependenciesAndRecorder,
- internedLiteralByVariableName, jsFragments, sizeBreakdowns, sourceInfoMaps,
- permutationResult, compilationMetrics);
- addSourceMapArtifacts(permutationId, jjsmap, dependenciesAndRecorder, isSourceMapsEnabled,
+ // (8) Generate Js source
+ List<JsSourceMap> sourceInfoMaps = new ArrayList<JsSourceMap>();
+ boolean isSourceMapsEnabled = props.isTrueInAnyPermutation("compiler.useSourceMaps");
+ String[] jsFragments = new String[jsProgram.getFragmentCount()];
+ StatementRanges[] ranges = new StatementRanges[jsFragments.length];
+ SizeBreakdown[] sizeBreakdowns = options.isJsonSoycEnabled() || options.isSoycEnabled()
+ || options.isCompilerMetricsEnabled() ? new SizeBreakdown[jsFragments.length] : null;
+ generateJavaScriptCode(jjsmap, jsFragments, ranges, sizeBreakdowns, sourceInfoMaps,
+ isSourceMapsEnabled || options.isJsonSoycEnabled());
+
+ javaScriptEvent.end();
+
+ // (9) Construct and return a value
+ PermutationResult permutationResult =
+ new PermutationResultImpl(jsFragments, permutation, makeSymbolMap(symbolTable), ranges);
+
+ // TODO(stalcup): hide metrics gathering in a callback or subclass
+ addSyntheticArtifacts(unifiedAst, permutation, startTimeMs, permutationId, jjsmap,
+ dependenciesAndRecorder, internedLiteralByVariableName, isSourceMapsEnabled, jsFragments,
sizeBreakdowns, sourceInfoMaps, permutationResult);
- maybeAddGeneratedArtifacts(permutationResult);
-
- event.end();
- }
-
- /**
- * Generate Js code from the given Js ASTs. Also produces information about that transformation.
- */
- private void generateJavaScriptCode(JavaToJavaScriptMap jjsMap, String[] jsFragments,
- StatementRanges[] ranges, SizeBreakdown[] sizeBreakdowns,
- List<JsSourceMap> sourceInfoMaps, boolean sourceMapsEnabled) {
-
- Event generateJavascriptEvent =
- SpeedTracerLogger.start(CompilerEventType.GENERATE_JAVASCRIPT);
-
- boolean useClosureCompiler = options.isClosureCompilerEnabled();
- if (useClosureCompiler) {
- ClosureJsRunner runner = new ClosureJsRunner();
- runner.compile(jprogram, jsProgram, jsFragments, options.getOutput());
- generateJavascriptEvent.end();
- return;
- }
-
- for (int i = 0; i < jsFragments.length; i++) {
- DefaultTextOutput out = new DefaultTextOutput(options.getOutput().shouldMinimize());
- JsReportGenerationVisitor v = new JsReportGenerationVisitor(out, jjsMap,
- options.isJsonSoycEnabled());
- v.accept(jsProgram.getFragmentBlock(i));
-
- StatementRanges statementRanges = v.getStatementRanges();
- String code = out.toString();
- JsSourceMap infoMap = (sourceInfoMaps != null) ? v.getSourceInfoMap() : null;
-
- JsAbstractTextTransformer transformer =
- new JsNoopTransformer(code, statementRanges, infoMap);
-
- /**
- * Cut generated JS up on class boundaries and re-link the source (possibly making use of
- * source from previous compiles, thus making it possible to perform partial recompiles).
- */
- if (options.isIncrementalCompileEnabled()) {
- transformer = new JsTypeLinker(logger, transformer, v.getClassRanges(),
- v.getProgramClassRange(), getMinimalRebuildCache(), jprogram.typeOracle);
- transformer.exec();
- }
-
- /**
- * Reorder function decls to improve compression ratios. Also restructures the top level
- * blocks into sub-blocks if they exceed 32767 statements.
- */
- Event functionClusterEvent = SpeedTracerLogger.start(CompilerEventType.FUNCTION_CLUSTER);
- // TODO(cromwellian) move to the Js AST optimization, re-enable sourcemaps + clustering
- if (!sourceMapsEnabled && !options.isClosureCompilerFormatEnabled()
- && options.shouldClusterSimilarFunctions()
- && options.getNamespace() == JsNamespaceOption.NONE
- && options.getOutput() == JsOutputOption.OBFUSCATED) {
- transformer = new JsFunctionClusterer(transformer);
- transformer.exec();
- }
- functionClusterEvent.end();
-
- jsFragments[i] = transformer.getJs();
- ranges[i] = transformer.getStatementRanges();
- if (sizeBreakdowns != null) {
- sizeBreakdowns[i] = v.getSizeBreakdown();
- }
- if (sourceInfoMaps != null) {
- sourceInfoMaps.add(transformer.getSourceInfoMap());
- }
- }
-
- generateJavascriptEvent.end();
- }
-
- private Collection<? extends Artifact<?>> makeSoycArtifacts(int permutationId, String[] js,
- SizeBreakdown[] sizeBreakdowns, List<JsSourceMap> sourceInfoMaps,
- SyntheticArtifact dependencies, JavaToJavaScriptMap jjsmap,
- Map<JsName, JsLiteral> internedLiteralByVariableName,
- ModuleMetricsArtifact moduleMetricsArtifact,
- PrecompilationMetricsArtifact precompilationMetricsArtifact,
- CompilationMetricsArtifact compilationMetrics, boolean htmlReportsDisabled)
- throws IOException, UnableToCompleteException {
- Memory.maybeDumpMemory("makeSoycArtifactsStart");
- List<SyntheticArtifact> soycArtifacts = new ArrayList<SyntheticArtifact>();
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- Event soycEvent = SpeedTracerLogger.start(CompilerEventType.MAKE_SOYC_ARTIFACTS);
-
- Event recordSplitPoints = SpeedTracerLogger.start(
- CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "recordSplitPoints");
- SplitPointRecorder.recordSplitPoints(jprogram, baos, logger);
- SyntheticArtifact splitPoints = new SyntheticArtifact(
- SoycReportLinker.class, "splitPoints" + permutationId + ".xml.gz", baos.toByteArray());
- soycArtifacts.add(splitPoints);
- recordSplitPoints.end();
-
- SyntheticArtifact sizeMaps = null;
- if (sizeBreakdowns != null) {
- Event recordSizeMap = SpeedTracerLogger.start(
- CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "recordSizeMap");
- baos.reset();
- SizeMapRecorder.recordMap(logger, baos, sizeBreakdowns, jjsmap,
- internedLiteralByVariableName);
- sizeMaps = new SyntheticArtifact(
- SoycReportLinker.class, "stories" + permutationId + ".xml.gz", baos.toByteArray());
- soycArtifacts.add(sizeMaps);
- recordSizeMap.end();
- }
-
- if (sourceInfoMaps != null) {
- Event recordStories = SpeedTracerLogger.start(
- CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "recordStories");
- baos.reset();
- StoryRecorder.recordStories(logger, baos, sourceInfoMaps, js);
- soycArtifacts.add(new SyntheticArtifact(
- SoycReportLinker.class, "detailedStories" + permutationId + ".xml.gz",
- baos.toByteArray()));
- recordStories.end();
- }
-
- if (dependencies != null) {
- soycArtifacts.add(dependencies);
- }
-
- // Set all of the main SOYC artifacts private.
- for (SyntheticArtifact soycArtifact : soycArtifacts) {
- soycArtifact.setVisibility(Visibility.Private);
- }
-
- if (!htmlReportsDisabled && sizeBreakdowns != null) {
- Event generateCompileReport = SpeedTracerLogger.start(
- CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "generateCompileReport");
- ArtifactsOutputDirectory outDir = new ArtifactsOutputDirectory();
- SoycDashboard dashboard = new SoycDashboard(outDir);
- dashboard.startNewPermutation(Integer.toString(permutationId));
- try {
- dashboard.readSplitPoints(openWithGunzip(splitPoints));
- if (sizeMaps != null) {
- dashboard.readSizeMaps(openWithGunzip(sizeMaps));
- }
- if (dependencies != null) {
- dashboard.readDependencies(openWithGunzip(dependencies));
- }
- Memory.maybeDumpMemory("soycReadDependenciesEnd");
- } catch (ParserConfigurationException e) {
- throw new InternalCompilerException(
- "Error reading compile report information that was just generated", e);
- } catch (SAXException e) {
- throw new InternalCompilerException(
- "Error reading compile report information that was just generated", e);
- }
- dashboard.generateForOnePermutation();
- if (moduleMetricsArtifact != null && precompilationMetricsArtifact != null
- && compilationMetrics != null) {
- dashboard.generateCompilerMetricsForOnePermutation(
- moduleMetricsArtifact, precompilationMetricsArtifact, compilationMetrics);
- }
- soycArtifacts.addAll(outDir.getArtifacts());
- generateCompileReport.end();
- }
-
- soycEvent.end();
-
- return soycArtifacts;
- }
-
- private SymbolData[] makeSymbolMap(Map<StandardSymbolData, JsName> symbolTable) {
- // Keep tracks of a list of referenced name. If it is not used, don't
- // add it to symbol map.
- final Set<String> nameUsed = new HashSet<String>();
- final Map<JsName, Integer> nameToFragment = new HashMap<JsName, Integer>();
-
- for (int i = 0; i < jsProgram.getFragmentCount(); i++) {
- final Integer fragId = i;
- new JsVisitor() {
- @Override
- public void endVisit(JsForIn x, JsContext ctx) {
- if (x.getIterVarName() != null) {
- nameUsed.add(x.getIterVarName().getIdent());
- }
- }
-
- @Override
- public void endVisit(JsFunction x, JsContext ctx) {
- if (x.getName() != null) {
- nameToFragment.put(x.getName(), fragId);
- nameUsed.add(x.getName().getIdent());
- }
- }
-
- @Override
- public void endVisit(JsLabel x, JsContext ctx) {
- nameUsed.add(x.getName().getIdent());
- }
-
- @Override
- public void endVisit(JsNameOf x, JsContext ctx) {
- if (x.getName() != null) {
- nameUsed.add(x.getName().getIdent());
- }
- }
-
- @Override
- public void endVisit(JsNameRef x, JsContext ctx) {
- // Obviously this isn't even that accurate. Some of them are
- // variable names, some of the are property. At least this
- // this give us a safe approximation. Ideally we need
- // the code removal passes to remove stuff in the scope objects.
- if (x.isResolved()) {
- nameUsed.add(x.getName().getIdent());
- }
- }
-
- @Override
- public void endVisit(JsParameter x, JsContext ctx) {
- nameUsed.add(x.getName().getIdent());
- }
-
- @Override
-
- public void endVisit(JsVars.JsVar x, JsContext ctx) {
- nameUsed.add(x.getName().getIdent());
- }
-
- }.accept(jsProgram.getFragmentBlock(i));
- }
-
- // TODO(acleung): This is a temp fix. Once we know this is safe. We
- // new to rewrite it to avoid extra ArrayList creations.
- // Or we should just consider serializing it as an ArrayList if
- // it is that much trouble to determine the true size.
- List<SymbolData> result = new ArrayList<SymbolData>();
-
- for (Map.Entry<StandardSymbolData, JsName> entry : symbolTable.entrySet()) {
- StandardSymbolData symbolData = entry.getKey();
- symbolData.setSymbolName(entry.getValue().getShortIdent());
- Integer fragNum = nameToFragment.get(entry.getValue());
- if (fragNum != null) {
- symbolData.setFragmentNumber(fragNum);
- }
- if (nameUsed.contains(entry.getValue().getIdent()) || entry.getKey().isClass()) {
- result.add(symbolData);
- }
- }
-
- return result.toArray(new SymbolData[result.size()]);
- }
-
- /**
- * Transform patterns that can't be represented in JS (such as multiple catch blocks) into
- * equivalent but compatible patterns and take JVM semantics (such as numeric casts) that are
- * not explicit in the AST and make them explicit.<br />
- *
- * These passes can not be reordering because of subtle interdependencies.
- */
- protected abstract TypeMapper<?> normalizeSemantics();
-
- /**
- * Open an emitted artifact and gunzip its contents.
- */
- private InputStream openWithGunzip(EmittedArtifact artifact)
- throws IOException, UnableToCompleteException {
- return new BufferedInputStream(new GZIPInputStream(artifact.getContents(TreeLogger.NULL)));
- }
-
- protected void optimizeJsLoop(Collection<JsNode> toInline) throws InterruptedException {
- int optimizationLevel = options.getOptimizationLevel();
- List<OptimizerStats> allOptimizerStats = new ArrayList<OptimizerStats>();
- int counter = 0;
- while (true) {
- counter++;
- if (Thread.interrupted()) {
- throw new InterruptedException();
- }
- Event optimizeJsEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE_JS);
-
- OptimizerStats stats = new OptimizerStats("Pass " + counter);
-
- // Remove unused functions if possible.
- stats.add(JsStaticEval.exec(jsProgram));
- // Inline Js function invocations
- stats.add(JsInliner.exec(jsProgram, toInline));
- // Remove unused functions if possible.
- stats.add(JsUnusedFunctionRemover.exec(jsProgram));
-
- // Save the stats to print out after optimizers finish.
- allOptimizerStats.add(stats);
-
- optimizeJsEvent.end();
- if ((optimizationLevel < OptionOptimize.OPTIMIZE_LEVEL_MAX && counter > optimizationLevel)
- || !stats.didChange()) {
- break;
- }
- }
-
- if (optimizationLevel > OptionOptimize.OPTIMIZE_LEVEL_DRAFT) {
- DuplicateExecuteOnceRemover.exec(jsProgram);
+ return permutationResult;
+ } catch (Throwable e) {
+ throw CompilationProblemReporter.logAndTranslateException(logger, e);
+ } finally {
+ jjsCompilePermutationEvent.end();
+ if (logger.isLoggable(TreeLogger.TRACE)) {
+ logger.log(TreeLogger.TRACE,
+ "Permutation took " + (System.currentTimeMillis() - permStartMs) + " ms");
}
}
+ }
- private Map<JsName, JsLiteral> renameJsSymbols(PermProps props, JavaToJavaScriptMap jjsmap)
- throws UnableToCompleteException {
- Map<JsName, JsLiteral> internedLiteralByVariableName;
- try {
- switch (options.getOutput()) {
- case OBFUSCATED:
- internedLiteralByVariableName = runObfuscateNamer(props);
- break;
- case PRETTY:
- internedLiteralByVariableName = runPrettyNamer(props.getConfigProps(), jjsmap);
- break;
- case DETAILED:
- internedLiteralByVariableName = runDetailedNamer(props.getConfigProps());
- break;
- default:
- throw new InternalCompilerException("Unknown output mode");
- }
- } catch (IllegalNameException e) {
- logger.log(TreeLogger.ERROR, e.getMessage(), e);
- throw new UnableToCompleteException();
- }
- return internedLiteralByVariableName == null ?
- ImmutableMap.<JsName, JsLiteral>of() : internedLiteralByVariableName;
- }
-
- private Map<JsName, JsLiteral> runObfuscateNamer(PermProps props) throws IllegalNameException {
- Map<JsName, JsLiteral> internedLiteralByVariableName =
- JsLiteralInterner.exec(jprogram, jsProgram, (byte) (JsLiteralInterner.INTERN_ALL
- & (byte) (jprogram.typeOracle.isJsInteropEnabled()
- ? ~JsLiteralInterner.INTERN_STRINGS : ~0)));
- FreshNameGenerator freshNameGenerator = JsObfuscateNamer.exec(jsProgram,
- props.getConfigProps());
- if (options.shouldRemoveDuplicateFunctions()
- && JsStackEmulator.getStackMode(props) == JsStackEmulator.StackMode.STRIP) {
- JsDuplicateFunctionRemover.exec(jsProgram, freshNameGenerator);
- }
- return internedLiteralByVariableName;
- }
-
- private Map<JsName, JsLiteral> runPrettyNamer(ConfigProps config, JavaToJavaScriptMap jjsmap)
- throws IllegalNameException {
- if (compilerContext.getOptions().isIncrementalCompileEnabled()) {
- JsIncrementalNamer.exec(jsProgram, config,
- compilerContext.getMinimalRebuildCache().getPersistentPrettyNamerState(), jjsmap);
- return null;
- }
-
- // We don't intern strings in pretty mode to improve readability
- Map<JsName, JsLiteral> internedLiteralByVariableName = JsLiteralInterner.exec(
- jprogram, jsProgram,
- (byte) (JsLiteralInterner.INTERN_ALL & ~JsLiteralInterner.INTERN_STRINGS));
-
- JsPrettyNamer.exec(jsProgram, config);
- return internedLiteralByVariableName;
+ private void maybeRecordReferencesAndControlFlow(boolean onlyUpdate) {
+ if (options.isIncrementalCompileEnabled()) {
+ // Per file compilation needs the type reference graph to construct the set of reachable
+ // types when linking.
+ TypeReferencesRecorder.exec(jprogram, getMinimalRebuildCache(), onlyUpdate);
+ ControlFlowRecorder.exec(jprogram, getMinimalRebuildCache().getTypeEnvironment(),
+ onlyUpdate);
}
}
/**
- * Performs precompilation.
+ * Transform patterns that can't be represented in JS (such as multiple catch blocks) into
+ * equivalent but compatible patterns and take JVM semantics (such as numeric casts) that are not
+ * explicit in the AST and make them explicit.<br />
+ *
+ * These passes can not be reordering because of subtle interdependencies.
*/
- protected abstract class Precompiler {
+ protected TypeMapper<?> normalizeSemantics() {
+ Event event = SpeedTracerLogger.start(CompilerEventType.JAVA_NORMALIZERS);
+ try {
+ Devirtualizer.exec(jprogram);
+ CatchBlockNormalizer.exec(jprogram);
+ PostOptimizationCompoundAssignmentNormalizer.exec(jprogram);
+ LongCastNormalizer.exec(jprogram);
+ LongEmulationNormalizer.exec(jprogram);
+ TypeCoercionNormalizer.exec(jprogram);
- protected RebindPermutationOracle rpo;
- protected String[] entryPointTypeNames;
-
- public Precompiler(RebindPermutationOracle rpo, String[] entryPointTypeNames) {
- this.rpo = rpo;
- this.entryPointTypeNames = entryPointTypeNames;
- }
-
- protected abstract void beforeUnifyAst(Set<String> allRootTypes)
- throws UnableToCompleteException;
-
- protected abstract void checkEntryPoints(String[] additionalRootTypes);
-
- protected abstract void createJProgram(CompilerContext compilerContext);
-
- /**
- * Takes as input a CompilationState and transforms that into a unified by not yet resolved Java
- * AST (a Java AST wherein cross-class references have been connected and all rebind result
- * classes are available and have not yet been pruned down to the set applicable for a
- * particular permutation). This AST is packaged into a UnifiedAst instance and then returned.
- *
- * Precompilation is INTENDED to progress as a series of stages:
- *
- * <pre>
- * 1. initialize local state
- * 2. assert preconditions
- * 3. construct and unify the unresolved Java AST
- * 4. normalize the unresolved Java AST // arguably should be removed
- * 5. optimize the unresolved Java AST // arguably should be removed
- * 6. construct and return a value
- * </pre>
- *
- * There are some other types of work here (mostly metrics and data gathering) which do not
- * serve the goal of output program construction. This work should really be moved into
- * subclasses or some sort of callback or plugin system so as not to visually pollute the real
- * compile logic.<br />
- *
- * Significant amounts of visitors implementing the intended above stages are triggered here but
- * in the wrong order. They have been noted for future cleanup.
- */
- protected final UnifiedAst precompile(String[] additionalRootTypes, boolean singlePermutation,
- PrecompilationMetricsArtifact precompilationMetrics) throws UnableToCompleteException {
- try {
- /*
- * Do not introduce any new pass here unless it is logically a part of one of the 6 defined
- * stages and is physically located in that stage.
- */
-
- // (1) Initialize local state
- createJProgram(compilerContext);
- // Synchronize JTypeOracle with compile optimization behavior.
- jprogram.typeOracle.setOptimize(
- options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT);
- jprogram.typeOracle.setJsInteropMode(options.getJsInteropMode());
-
- jsProgram = new JsProgram();
- if (additionalRootTypes == null) {
- additionalRootTypes = Empty.STRINGS;
- }
-
- // (2) Assert preconditions
- checkEntryPoints(additionalRootTypes);
-
- // (3) Construct and unify the unresolved Java AST
- CompilationState compilationState = constructJavaAst(additionalRootTypes);
-
- // TODO(stalcup): hide metrics gathering in a callback or subclass
- JsniRestrictionChecker.exec(logger, jprogram);
- JsInteropRestrictionChecker.exec(logger, jprogram, getMinimalRebuildCache());
- logTypeOracleMetrics(precompilationMetrics, compilationState);
- Memory.maybeDumpMemory("AstOnly");
- AstDumper.maybeDumpAST(jprogram);
-
- // TODO(stalcup): is in wrong place, move to optimization stage
- obfuscateEnums();
-
- // (4) Normalize the unresolved Java AST
- // Replace defender method references
- ReplaceDefenderMethodReferences.exec(jprogram);
-
- FixAssignmentsToUnboxOrCast.exec(jprogram);
- if (options.isEnableAssertions()) {
- AssertionNormalizer.exec(jprogram);
- } else {
- AssertionRemover.exec(jprogram);
- }
- if (module != null && options.isRunAsyncEnabled()) {
- ReplaceRunAsyncs.exec(logger, jprogram);
- ConfigProps config = new ConfigProps(module);
- CodeSplitters.pickInitialLoadSequence(logger, jprogram, config);
- }
- ImplementClassLiteralsAsFields.exec(jprogram);
-
- // (5) Optimize the unresolved Java AST
- optimizeJava(singlePermutation);
-
- // TODO(stalcup): hide metrics gathering in a callback or subclass
- logAstTypeMetrics(precompilationMetrics);
-
- // (6) Construct and return a value.
- Event createUnifiedAstEvent = SpeedTracerLogger.start(CompilerEventType.CREATE_UNIFIED_AST);
- UnifiedAst result = new UnifiedAst(
- options, new AST(jprogram, jsProgram), singlePermutation, RecordRebinds.exec(jprogram));
- createUnifiedAstEvent.end();
- return result;
- } catch (Throwable e) {
- throw CompilationProblemReporter.logAndTranslateException(logger, e);
- } finally {
- logTrackingStats();
- }
- }
-
- /**
- * Creates (and returns the name for) a new class to serve as the container for the invocation
- * of registered entry point methods as part of module bootstrapping.<br />
- *
- * The resulting class will be invoked during bootstrapping like FooEntryMethodHolder.init(). By
- * generating the class on the fly and naming it to match the current module, the resulting
- * holder class can work in both monolithic and separate compilation schemes.
- */
- private String buildEntryMethodHolder(StandardGeneratorContext context,
- Set<String> allRootTypes) throws UnableToCompleteException {
- // If there are no entry points.
- if (entryPointTypeNames.length == 0) {
- // Then there's no need to generate an EntryMethodHolder class to launch them.
- return null;
+ if (options.isIncrementalCompileEnabled()) {
+ // Per file compilation reuses type JS even as references (like casts) in other files
+ // change, which means all legal casts need to be allowed now before they are actually
+ // used later.
+ ComputeExhaustiveCastabilityInformation.exec(jprogram);
+ } else {
+ // If trivial casts are pruned then one can use smaller runtime castmaps.
+ ComputeCastabilityInformation.exec(jprogram, options.isCastCheckingDisabled(),
+ !shouldOptimize() /* recordTrivialCasts */);
}
- EntryMethodHolderGenerator entryMethodHolderGenerator = new EntryMethodHolderGenerator();
- String entryMethodHolderTypeName =
- entryMethodHolderGenerator.generate(logger, context, module.getCanonicalName());
- context.finish(logger);
- // Ensures that unification traverses and keeps the class.
- allRootTypes.add(entryMethodHolderTypeName);
- // Ensures that JProgram knows to index this class's methods so that later bootstrap
- // construction code is able to locate the FooEntryMethodHolder.init() function.
- jprogram.addIndexedTypeName(entryMethodHolderTypeName);
- return entryMethodHolderTypeName;
- }
+ ComputeInstantiatedJsoInterfaces.exec(jprogram);
+ ImplementCastsAndTypeChecks.exec(jprogram, options.isCastCheckingDisabled(),
+ shouldOptimize() /* pruneTrivialCasts */);
+ ArrayNormalizer.exec(jprogram, options.isCastCheckingDisabled());
+ EqualityNormalizer.exec(jprogram);
- private CompilationState constructJavaAst(String[] additionalRootTypes)
- throws UnableToCompleteException {
- Set<String> allRootTypes = new TreeSet<String>();
- CompilationState compilationState = rpo.getCompilationState();
- Memory.maybeDumpMemory("CompStateBuilt");
- recordJsoTypes(compilationState.getTypeOracle());
- populateRootTypes(allRootTypes, additionalRootTypes, compilationState);
- String entryMethodHolderTypeName =
- buildEntryMethodHolder(rpo.getGeneratorContext(), allRootTypes);
- beforeUnifyAst(allRootTypes);
- unifyJavaAst(allRootTypes, entryMethodHolderTypeName);
- if (options.isSoycEnabled() || options.isJsonSoycEnabled()) {
- SourceInfoCorrelator.exec(jprogram);
- }
-
- // Free up memory.
- rpo.clear();
- Set<String> deletedTypeNames = options.isIncrementalCompileEnabled()
- ? getMinimalRebuildCache().computeDeletedTypeNames() : Sets.<String> newHashSet();
- jprogram.typeOracle.computeBeforeAST(StandardTypes.createFrom(jprogram),
- jprogram.getDeclaredTypes(), jprogram.getModuleDeclaredTypes(), deletedTypeNames);
- return compilationState;
- }
-
- /**
- * This method can be used to fetch the list of referenced class.
- *
- * This method is intended to support compiler metrics.
- */
- private String[] getReferencedJavaClasses() {
- class ClassNameVisitor extends JVisitor {
- List<String> classNames = new ArrayList<String>();
- @Override
- public boolean visit(JClassType x, Context ctx) {
- classNames.add(x.getName());
- return true;
- }
- }
- ClassNameVisitor v = new ClassNameVisitor();
- v.accept(jprogram);
- return v.classNames.toArray(new String[v.classNames.size()]);
- }
-
- private void logAstTypeMetrics(PrecompilationMetricsArtifact precompilationMetrics) {
- if (options.isCompilerMetricsEnabled()) {
- precompilationMetrics.setAstTypes(getReferencedJavaClasses());
- }
- }
-
- private void logTypeOracleMetrics(
- PrecompilationMetricsArtifact precompilationMetrics, CompilationState compilationState) {
- if (precompilationMetrics != null) {
- List<String> finalTypeOracleTypes = Lists.newArrayList();
- for (com.google.gwt.core.ext.typeinfo.JClassType type :
- compilationState.getTypeOracle().getTypes()) {
- finalTypeOracleTypes.add(type.getPackage().getName() + "." + type.getName());
- }
- precompilationMetrics.setFinalTypeOracleTypes(finalTypeOracleTypes);
- }
- }
-
- private void obfuscateEnums() {
- // See if we should run the EnumNameObfuscator
- if (module != null) {
- ConfigProps config = new ConfigProps(module);
- List<String> enumObfProps = config.getStrings(ENUM_NAME_OBFUSCATION_PROPERTY);
- String enumObfProp = enumObfProps != null ? enumObfProps.get(0) : null;
- if (!"false".equals(enumObfProp)) {
- EnumNameObfuscator.exec(jprogram, logger,
- config.getCommaSeparatedStrings(
- ENUM_NAME_OBFUSCATION_BLACKLIST_PROPERTY),
- "closure".equals(enumObfProp));
- }
- }
- }
-
- private void optimizeJava(boolean singlePermutation) throws InterruptedException {
- if (options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT
- && !singlePermutation) {
- if (options.isOptimizePrecompile()) {
- /*
- * Go ahead and optimize early, so that each permutation will run faster. This code path
- * is used by the Compiler entry point. We assume that we will not be able to perfectly
- * parallelize the permutation compiles, so let's optimize as much as possible the common
- * AST. In some cases, this might also have the side benefit of reducing the total
- * permutation count.
- */
- optimizeJavaToFixedPoint();
- } else {
- /*
- * Do only minimal early optimizations. This code path is used by the Precompile entry
- * point. The external system might be able to perfectly parallelize the permutation
- * compiles, so let's avoid doing potentially superlinear optimizations on the unified
- * AST.
- */
- optimizeJavaOneTime("Early Optimization", jprogram.getNodeCount(),
- new FullOptimizerContext(jprogram));
- }
- }
- }
-
- private void populateRootTypes(Set<String> allRootTypes, String[] additionalRootTypes,
- CompilationState compilationState) {
- if (jprogram.typeOracle.isJsInteropEnabled()) {
- Iterables.addAll(allRootTypes, compilationState.getQualifiedJsInteropRootTypesNames());
- }
- Collections.addAll(allRootTypes, entryPointTypeNames);
- Collections.addAll(allRootTypes, additionalRootTypes);
- allRootTypes.addAll(JProgram.CODEGEN_TYPES_SET);
- allRootTypes.addAll(jprogram.getTypeNamesToIndex());
- /*
- * Add all SingleJsoImpl types that we know about. It's likely that the concrete types are
- * never explicitly referenced.
- */
- TypeOracle typeOracle = compilationState.getTypeOracle();
- for (com.google.gwt.core.ext.typeinfo.JClassType singleJsoIntf :
- typeOracle.getSingleJsoImplInterfaces()) {
- allRootTypes.add(typeOracle.getSingleJsoImpl(singleJsoIntf).getQualifiedSourceName());
- }
- }
-
- private void recordJsoTypes(TypeOracle typeOracle) {
- if (!options.isIncrementalCompileEnabled()) {
- return;
- }
-
- // Add names of JSO subtypes.
- Set<String> jsoTypeNames = Sets.newHashSet();
- for (com.google.gwt.dev.javac.typemodel.JClassType subtype :
- typeOracle.getJavaScriptObject().getSubtypes()) {
- jsoTypeNames.add(subtype.getQualifiedBinaryName());
- }
-
- // Add names of interfaces that are always of a JSO (aka there are no non-JSO implementors).
- Set<String> singleJsoImplInterfaceNames = Sets.newHashSet();
- for (com.google.gwt.core.ext.typeinfo.JClassType singleJsoImplInterface :
- typeOracle.getSingleJsoImplInterfaces()) {
- singleJsoImplInterfaceNames.add(singleJsoImplInterface.getQualifiedBinaryName());
- }
-
- // Add names of interfaces that are only sometimes a JSO (aka there are both JSO and non-JSO
- // imlementors).
- Set<String> dualJsoImplInterfaceNames = Sets.newHashSet();
- for (com.google.gwt.core.ext.typeinfo.JClassType dualJsoImplInterface :
- typeOracle.getDualJsoImplInterfaces()) {
- dualJsoImplInterfaceNames.add(dualJsoImplInterface.getQualifiedBinaryName());
- }
-
- compilerContext.getMinimalRebuildCache().setJsoTypeNames(jsoTypeNames,
- singleJsoImplInterfaceNames, dualJsoImplInterfaceNames);
- }
-
- private void synthesizeEntryMethodHolderInit(UnifyAst unifyAst,
- String entryMethodHolderTypeName) throws UnableToCompleteException {
- // Get type references.
- JDeclaredType entryMethodHolderType =
- unifyAst.findType(entryMethodHolderTypeName, unifyAst.getSourceNameBasedTypeLocator());
- JDeclaredType gwtType = unifyAst.findType("com.google.gwt.core.client.GWT",
- unifyAst.getSourceNameBasedTypeLocator());
- JDeclaredType entryPointType = unifyAst.findType("com.google.gwt.core.client.EntryPoint",
- unifyAst.getSourceNameBasedTypeLocator());
-
- // Get method references.
- JMethod initMethod = entryMethodHolderType.findMethod("init()V", false);
- JMethod gwtCreateMethod =
- gwtType.findMethod("create(Ljava/lang/Class;)Ljava/lang/Object;", false);
-
- // Synthesize all onModuleLoad() calls.
- JBlock initMethodBlock = ((JMethodBody) initMethod.getBody()).getBlock();
- SourceInfo origin = initMethodBlock.getSourceInfo().makeChild();
- for (String entryPointTypeName : entryPointTypeNames) {
- // Get type and onModuleLoad function for the current entryPointTypeName.
- JDeclaredType specificEntryPointType =
- unifyAst.findType(entryPointTypeName, unifyAst.getSourceNameBasedTypeLocator());
- if (specificEntryPointType == null) {
- logger.log(TreeLogger.ERROR,
- "Could not find module entry point class '" + entryPointTypeName + "'", null);
- throw new UnableToCompleteException();
- }
- JMethod onModuleLoadMethod =
- entryPointType.findMethod("onModuleLoad()V", true);
- JMethod specificOnModuleLoadMethod =
- specificEntryPointType.findMethod("onModuleLoad()V", true);
-
- if (specificOnModuleLoadMethod != null && specificOnModuleLoadMethod.isStatic()) {
- // Synthesize a static invocation FooEntryPoint.onModuleLoad(); call.
- JMethodCall staticOnModuleLoadCall =
- new JMethodCall(origin, null, specificOnModuleLoadMethod);
- initMethodBlock.addStmt(staticOnModuleLoadCall.makeStatement());
- } else {
- // Synthesize ((EntryPoint)GWT.create(FooEntryPoint.class)).onModuleLoad();
- JClassLiteral entryPointTypeClassLiteral =
- new JClassLiteral(origin, specificEntryPointType);
- JMethodCall createInstanceCall =
- new JMethodCall(origin, null, gwtCreateMethod, entryPointTypeClassLiteral);
- JCastOperation castToEntryPoint =
- new JCastOperation(origin, entryPointType, createInstanceCall);
- JMethodCall instanceOnModuleLoadCall =
- new JMethodCall(origin, castToEntryPoint, onModuleLoadMethod);
- initMethodBlock.addStmt(instanceOnModuleLoadCall.makeStatement());
- }
- }
- }
-
- private void unifyJavaAst(Set<String> allRootTypes, String entryMethodHolderTypeName)
- throws UnableToCompleteException {
-
- Event event = SpeedTracerLogger.start(CompilerEventType.UNIFY_AST);
-
- UnifyAst unifyAst = new UnifyAst(logger, compilerContext, jprogram, jsProgram, rpo);
- // Makes JProgram aware of these types so they can be accessed via index.
- unifyAst.addRootTypes(allRootTypes);
- // Must synthesize entryPoint.onModuleLoad() calls because some EntryPoint classes are
- // private.
- if (entryMethodHolderTypeName != null) {
- // Only synthesize the init method in the EntryMethodHolder class, if there is an
- // EntryMethodHolder class.
- synthesizeEntryMethodHolderInit(unifyAst, entryMethodHolderTypeName);
- }
- if (entryMethodHolderTypeName != null) {
- // Only register the init method in the EntryMethodHolder class as an entry method, if there
- // is an EntryMethodHolder class.
- jprogram.addEntryMethod(jprogram.getIndexedMethod(
- SourceName.getShortClassName(entryMethodHolderTypeName) + ".init"));
- }
- unifyAst.exec();
-
+ TypeMapper<?> typeMapper = getTypeMapper();
+ ResolveRuntimeTypeReferences.exec(jprogram, typeMapper, getTypeOrder());
+ return typeMapper;
+ } finally {
event.end();
}
}
+ private void optimizeJava() throws InterruptedException {
+ if (shouldOptimize()) {
+ optimizeJavaToFixedPoint();
+ RemoveEmptySuperCalls.exec(jprogram);
+ }
+ }
+
+ private void optimizeJs(Set<JsNode> inlinableJsFunctions) throws InterruptedException {
+ if (shouldOptimize()) {
+ optimizeJsLoop(inlinableJsFunctions);
+ JsDuplicateCaseFolder.exec(jsProgram);
+ }
+ }
+
+ private void postNormalizationOptimizeJava() {
+ Event event = SpeedTracerLogger.start(CompilerEventType.JAVA_POST_NORMALIZER_OPTIMIZERS);
+ try {
+ if (shouldOptimize()) {
+ RemoveSpecializations.exec(jprogram);
+ Pruner.exec(jprogram, false);
+ }
+ ReplaceGetClassOverrides.exec(jprogram);
+ } finally {
+ event.end();
+ }
+ }
+
+ private Map<JsName, JsLiteral> runDetailedNamer(ConfigProps config)
+ throws IllegalNameException {
+ Map<JsName, JsLiteral> internedTextByVariableName = null;
+ if (shouldOptimize()) {
+ // Only perform the interning optimization when optimizations are enabled.
+ internedTextByVariableName =
+ JsLiteralInterner.exec(jprogram, jsProgram, (byte) (JsLiteralInterner.INTERN_ALL
+ & (byte) (jprogram.typeOracle.isJsInteropEnabled()
+ ? ~JsLiteralInterner.INTERN_STRINGS : ~0)));
+ }
+ JsVerboseNamer.exec(jsProgram, config);
+ return internedTextByVariableName;
+ }
+
+ private Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> splitJsIntoFragments(
+ PermProps props, int permutationId, JavaToJavaScriptMap jjsmap) {
+ Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder;
+ MultipleDependencyGraphRecorder dependencyRecorder = null;
+ SyntheticArtifact dependencies = null;
+ if (options.isRunAsyncEnabled()) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ int expectedFragmentCount = options.getFragmentCount();
+ // -1 is the default value, we trap 0 just in case (0 is not a legal value in any case)
+ if (expectedFragmentCount <= 0) {
+ // Fragment count not set check fragments merge.
+ int numberOfMerges = options.getFragmentsMerge();
+ if (numberOfMerges > 0) {
+ // + 1 for left over, + 1 for initial gave us the total number
+ // of fragments without splitting.
+ expectedFragmentCount =
+ Math.max(0, jprogram.getRunAsyncs().size() + 2 - numberOfMerges);
+ }
+ }
+
+ int minFragmentSize = props.getConfigProps().getInteger(CodeSplitters.MIN_FRAGMENT_SIZE, 0);
+
+ dependencyRecorder = chooseDependencyRecorder(baos);
+ CodeSplitter.exec(logger, jprogram, jsProgram, jjsmap, expectedFragmentCount,
+ minFragmentSize, dependencyRecorder);
+
+ if (baos.size() == 0) {
+ dependencyRecorder = recordNonSplitDependencies(baos);
+ }
+ if (baos.size() > 0) {
+ dependencies = new SyntheticArtifact(
+ SoycReportLinker.class, "dependencies" + permutationId + ".xml.gz",
+ baos.toByteArray());
+ }
+ } else if (options.isSoycEnabled() || options.isJsonSoycEnabled()) {
+ dependencyRecorder = recordNonSplitDependencies(new ByteArrayOutputStream());
+ }
+ dependenciesAndRecorder = Pair.create(
+ dependencies, dependencyRecorder);
+
+ // No new JsNames or references to JSNames can be introduced after this
+ // point.
+ HandleCrossFragmentReferences.exec(jsProgram, props);
+
+ return dependenciesAndRecorder;
+ }
+
+ private MultipleDependencyGraphRecorder chooseDependencyRecorder(OutputStream out) {
+ MultipleDependencyGraphRecorder dependencyRecorder =
+ MultipleDependencyGraphRecorder.NULL_RECORDER;
+ if (options.isSoycEnabled() && options.isJsonSoycEnabled()) {
+ dependencyRecorder = new DependencyGraphRecorder(out, jprogram);
+ } else if (options.isSoycEnabled()) {
+ dependencyRecorder = new DependencyRecorder(out);
+ } else if (options.isJsonSoycEnabled()) {
+ dependencyRecorder = new DependencyGraphRecorder(out, jprogram);
+ }
+ return dependencyRecorder;
+ }
+
+ /**
+ * Dependency information is normally recorded during code splitting, and it results in multiple
+ * dependency graphs. If the code splitter doesn't run, then this method can be used instead to
+ * record a single dependency graph for the whole program.
+ */
+ private DependencyRecorder recordNonSplitDependencies(OutputStream out) {
+ DependencyRecorder deps;
+ if (options.isSoycEnabled() && options.isJsonSoycEnabled()) {
+ deps = new DependencyGraphRecorder(out, jprogram);
+ } else if (options.isSoycEnabled()) {
+ deps = new DependencyRecorder(out);
+ } else if (options.isJsonSoycEnabled()) {
+ deps = new DependencyGraphRecorder(out, jprogram);
+ } else {
+ return null;
+ }
+ deps.open();
+ deps.startDependencyGraph("initial", null);
+
+ ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(jprogram);
+ cfa.setDependencyRecorder(deps);
+ cfa.traverseEntryMethods();
+ deps.endDependencyGraph();
+ deps.close();
+ return deps;
+ }
+
+ private CompilationMetricsArtifact addCompilerMetricsArtifact(UnifiedAst unifiedAst,
+ Permutation permutation, long startTimeMs, SizeBreakdown[] sizeBreakdowns,
+ PermutationResult permutationResult) {
+ CompilationMetricsArtifact compilationMetrics = null;
+ // TODO: enable this when ClosureCompiler is enabled
+ if (options.isCompilerMetricsEnabled()) {
+ if (options.isClosureCompilerEnabled()) {
+ logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
+ + "-XcompilerMetric; ignoring -XcompilerMetric.");
+ } else {
+ compilationMetrics = new CompilationMetricsArtifact(permutation.getId());
+ compilationMetrics.setCompileElapsedMilliseconds(
+ System.currentTimeMillis() - startTimeMs);
+ compilationMetrics.setElapsedMilliseconds(
+ System.currentTimeMillis() - ManagementFactory.getRuntimeMXBean().getStartTime());
+ compilationMetrics.setJsSize(sizeBreakdowns);
+ compilationMetrics.setPermutationDescription(permutation.getProps().prettyPrint());
+ permutationResult.addArtifacts(Lists.newArrayList(
+ unifiedAst.getModuleMetrics(), unifiedAst.getPrecompilationMetrics(),
+ compilationMetrics));
+ }
+ }
+ return compilationMetrics;
+ }
+
+ private void addSourceMapArtifacts(int permutationId, JavaToJavaScriptMap jjsmap,
+ Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder,
+ boolean isSourceMapsEnabled, SizeBreakdown[] sizeBreakdowns,
+ List<JsSourceMap> sourceInfoMaps, PermutationResult permutationResult) {
+ if (options.isJsonSoycEnabled()) {
+ // TODO: enable this when ClosureCompiler is enabled
+ if (options.isClosureCompilerEnabled()) {
+ logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
+ + "-XjsonSoyc; ignoring -XjsonSoyc.");
+ } else {
+ // Is a super set of SourceMapRecorder.makeSourceMapArtifacts().
+ permutationResult.addArtifacts(EntityRecorder.makeSoycArtifacts(
+ permutationId, sourceInfoMaps, options.getSourceMapFilePrefix(),
+ jjsmap, sizeBreakdowns,
+ ((DependencyGraphRecorder) dependenciesAndRecorder.getRight()), jprogram));
+ }
+ } else if (isSourceMapsEnabled) {
+ // TODO: enable this when ClosureCompiler is enabled
+ if (options.isClosureCompilerEnabled()) {
+ logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
+ + "compiler.useSourceMaps=true; ignoring compiler.useSourceMaps=true.");
+ } else {
+ logger.log(TreeLogger.INFO, "Source Maps Enabled");
+ permutationResult.addArtifacts(SourceMapRecorder.exec(permutationId, sourceInfoMaps,
+ options.getSourceMapFilePrefix()));
+ }
+ }
+ }
+
+ /**
+ * Adds generated artifacts from previous compiles when doing per-file compiles. <p> All
+ * generators are run on first compile but only some very small subset are rerun on recompiles.
+ * Care must be taken to ensure that all generated artifacts (such as png/html/css files) are
+ * still registered for output even when no generators are run in the current compile.
+ */
+ private void maybeAddGeneratedArtifacts(PermutationResult permutationResult) {
+ if (options.isIncrementalCompileEnabled()) {
+ permutationResult.addArtifacts(
+ compilerContext.getMinimalRebuildCache().getGeneratedArtifacts());
+ }
+ }
+
+ private void addSoycArtifacts(UnifiedAst unifiedAst, int permutationId,
+ JavaToJavaScriptMap jjsmap,
+ Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder,
+ Map<JsName, JsLiteral> internedLiteralByVariableName, String[] js,
+ SizeBreakdown[] sizeBreakdowns,
+ List<JsSourceMap> sourceInfoMaps, PermutationResult permutationResult,
+ CompilationMetricsArtifact compilationMetrics)
+ throws IOException, UnableToCompleteException {
+ // TODO: enable this when ClosureCompiler is enabled
+ if (options.isClosureCompilerEnabled()) {
+ if (options.isSoycEnabled()) {
+ logger.log(TreeLogger.WARN, "Incompatible options: -XenableClosureCompiler and "
+ + "-compileReport; ignoring -compileReport.");
+ }
+ } else {
+ permutationResult.addArtifacts(makeSoycArtifacts(permutationId, js, sizeBreakdowns,
+ options.isSoycExtra() ? sourceInfoMaps : null, dependenciesAndRecorder.getLeft(),
+ jjsmap, internedLiteralByVariableName, unifiedAst.getModuleMetrics(),
+ unifiedAst.getPrecompilationMetrics(), compilationMetrics,
+ options.isSoycHtmlDisabled()));
+ }
+ }
+
+ private void addSyntheticArtifacts(UnifiedAst unifiedAst, Permutation permutation,
+ long startTimeMs, int permutationId, JavaToJavaScriptMap jjsmap,
+ Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder,
+ Map<JsName, JsLiteral> internedLiteralByVariableName, boolean isSourceMapsEnabled,
+ String[] jsFragments, SizeBreakdown[] sizeBreakdowns,
+ List<JsSourceMap> sourceInfoMaps, PermutationResult permutationResult)
+ throws IOException, UnableToCompleteException {
+
+ assert internedLiteralByVariableName != null;
+
+ Event event = SpeedTracerLogger.start(CompilerEventType.PERMUTATION_ARTIFACTS);
+
+ CompilationMetricsArtifact compilationMetrics = addCompilerMetricsArtifact(
+ unifiedAst, permutation, startTimeMs, sizeBreakdowns, permutationResult);
+ addSoycArtifacts(unifiedAst, permutationId, jjsmap, dependenciesAndRecorder,
+ internedLiteralByVariableName, jsFragments, sizeBreakdowns, sourceInfoMaps,
+ permutationResult, compilationMetrics);
+ addSourceMapArtifacts(permutationId, jjsmap, dependenciesAndRecorder, isSourceMapsEnabled,
+ sizeBreakdowns, sourceInfoMaps, permutationResult);
+ maybeAddGeneratedArtifacts(permutationResult);
+
+ event.end();
+ }
+
+ /**
+ * Generate Js code from the given Js ASTs. Also produces information about that transformation.
+ */
+ private void generateJavaScriptCode(JavaToJavaScriptMap jjsMap, String[] jsFragments,
+ StatementRanges[] ranges, SizeBreakdown[] sizeBreakdowns,
+ List<JsSourceMap> sourceInfoMaps, boolean sourceMapsEnabled) {
+
+ Event generateJavascriptEvent =
+ SpeedTracerLogger.start(CompilerEventType.GENERATE_JAVASCRIPT);
+
+ boolean useClosureCompiler = options.isClosureCompilerEnabled();
+ if (useClosureCompiler) {
+ ClosureJsRunner runner = new ClosureJsRunner();
+ runner.compile(jprogram, jsProgram, jsFragments, options.getOutput());
+ generateJavascriptEvent.end();
+ return;
+ }
+
+ for (int i = 0; i < jsFragments.length; i++) {
+ DefaultTextOutput out = new DefaultTextOutput(options.getOutput().shouldMinimize());
+ JsReportGenerationVisitor v = new JsReportGenerationVisitor(out, jjsMap,
+ options.isJsonSoycEnabled());
+ v.accept(jsProgram.getFragmentBlock(i));
+
+ StatementRanges statementRanges = v.getStatementRanges();
+ String code = out.toString();
+ JsSourceMap infoMap = (sourceInfoMaps != null) ? v.getSourceInfoMap() : null;
+
+ JsAbstractTextTransformer transformer =
+ new JsNoopTransformer(code, statementRanges, infoMap);
+
+ /**
+ * Cut generated JS up on class boundaries and re-link the source (possibly making use of
+ * source from previous compiles, thus making it possible to perform partial recompiles).
+ */
+ if (options.isIncrementalCompileEnabled()) {
+ transformer = new JsTypeLinker(logger, transformer, v.getClassRanges(),
+ v.getProgramClassRange(), getMinimalRebuildCache(), jprogram.typeOracle);
+ transformer.exec();
+ }
+
+ /**
+ * Reorder function decls to improve compression ratios. Also restructures the top level
+ * blocks into sub-blocks if they exceed 32767 statements.
+ */
+ Event functionClusterEvent = SpeedTracerLogger.start(CompilerEventType.FUNCTION_CLUSTER);
+ // TODO(cromwellian) move to the Js AST optimization, re-enable sourcemaps + clustering
+ if (!sourceMapsEnabled && !options.isClosureCompilerFormatEnabled()
+ && options.shouldClusterSimilarFunctions()
+ && options.getNamespace() == JsNamespaceOption.NONE
+ && options.getOutput() == JsOutputOption.OBFUSCATED) {
+ transformer = new JsFunctionClusterer(transformer);
+ transformer.exec();
+ }
+ functionClusterEvent.end();
+
+ jsFragments[i] = transformer.getJs();
+ ranges[i] = transformer.getStatementRanges();
+ if (sizeBreakdowns != null) {
+ sizeBreakdowns[i] = v.getSizeBreakdown();
+ }
+ if (sourceInfoMaps != null) {
+ sourceInfoMaps.add(transformer.getSourceInfoMap());
+ }
+ }
+
+ generateJavascriptEvent.end();
+ }
+
+ private Collection<? extends Artifact<?>> makeSoycArtifacts(int permutationId, String[] js,
+ SizeBreakdown[] sizeBreakdowns, List<JsSourceMap> sourceInfoMaps,
+ SyntheticArtifact dependencies, JavaToJavaScriptMap jjsmap,
+ Map<JsName, JsLiteral> internedLiteralByVariableName,
+ ModuleMetricsArtifact moduleMetricsArtifact,
+ PrecompilationMetricsArtifact precompilationMetricsArtifact,
+ CompilationMetricsArtifact compilationMetrics, boolean htmlReportsDisabled)
+ throws IOException, UnableToCompleteException {
+ Memory.maybeDumpMemory("makeSoycArtifactsStart");
+ List<SyntheticArtifact> soycArtifacts = new ArrayList<SyntheticArtifact>();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ Event soycEvent = SpeedTracerLogger.start(CompilerEventType.MAKE_SOYC_ARTIFACTS);
+
+ Event recordSplitPoints = SpeedTracerLogger.start(
+ CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "recordSplitPoints");
+ SplitPointRecorder.recordSplitPoints(jprogram, baos, logger);
+ SyntheticArtifact splitPoints = new SyntheticArtifact(
+ SoycReportLinker.class, "splitPoints" + permutationId + ".xml.gz", baos.toByteArray());
+ soycArtifacts.add(splitPoints);
+ recordSplitPoints.end();
+
+ SyntheticArtifact sizeMaps = null;
+ if (sizeBreakdowns != null) {
+ Event recordSizeMap = SpeedTracerLogger.start(
+ CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "recordSizeMap");
+ baos.reset();
+ SizeMapRecorder.recordMap(logger, baos, sizeBreakdowns, jjsmap,
+ internedLiteralByVariableName);
+ sizeMaps = new SyntheticArtifact(
+ SoycReportLinker.class, "stories" + permutationId + ".xml.gz", baos.toByteArray());
+ soycArtifacts.add(sizeMaps);
+ recordSizeMap.end();
+ }
+
+ if (sourceInfoMaps != null) {
+ Event recordStories = SpeedTracerLogger.start(
+ CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "recordStories");
+ baos.reset();
+ StoryRecorder.recordStories(logger, baos, sourceInfoMaps, js);
+ soycArtifacts.add(new SyntheticArtifact(
+ SoycReportLinker.class, "detailedStories" + permutationId + ".xml.gz",
+ baos.toByteArray()));
+ recordStories.end();
+ }
+
+ if (dependencies != null) {
+ soycArtifacts.add(dependencies);
+ }
+
+ // Set all of the main SOYC artifacts private.
+ for (SyntheticArtifact soycArtifact : soycArtifacts) {
+ soycArtifact.setVisibility(Visibility.Private);
+ }
+
+ if (!htmlReportsDisabled && sizeBreakdowns != null) {
+ Event generateCompileReport = SpeedTracerLogger.start(
+ CompilerEventType.MAKE_SOYC_ARTIFACTS, "phase", "generateCompileReport");
+ ArtifactsOutputDirectory outDir = new ArtifactsOutputDirectory();
+ SoycDashboard dashboard = new SoycDashboard(outDir);
+ dashboard.startNewPermutation(Integer.toString(permutationId));
+ try {
+ dashboard.readSplitPoints(openWithGunzip(splitPoints));
+ if (sizeMaps != null) {
+ dashboard.readSizeMaps(openWithGunzip(sizeMaps));
+ }
+ if (dependencies != null) {
+ dashboard.readDependencies(openWithGunzip(dependencies));
+ }
+ Memory.maybeDumpMemory("soycReadDependenciesEnd");
+ } catch (ParserConfigurationException e) {
+ throw new InternalCompilerException(
+ "Error reading compile report information that was just generated", e);
+ } catch (SAXException e) {
+ throw new InternalCompilerException(
+ "Error reading compile report information that was just generated", e);
+ }
+ dashboard.generateForOnePermutation();
+ if (moduleMetricsArtifact != null && precompilationMetricsArtifact != null
+ && compilationMetrics != null) {
+ dashboard.generateCompilerMetricsForOnePermutation(
+ moduleMetricsArtifact, precompilationMetricsArtifact, compilationMetrics);
+ }
+ soycArtifacts.addAll(outDir.getArtifacts());
+ generateCompileReport.end();
+ }
+
+ soycEvent.end();
+
+ return soycArtifacts;
+ }
+
+ private SymbolData[] makeSymbolMap(Map<StandardSymbolData, JsName> symbolTable) {
+ // Keep tracks of a list of referenced name. If it is not used, don't
+ // add it to symbol map.
+ final Set<String> nameUsed = new HashSet<String>();
+ final Map<JsName, Integer> nameToFragment = new HashMap<JsName, Integer>();
+
+ for (int i = 0; i < jsProgram.getFragmentCount(); i++) {
+ final Integer fragId = i;
+ new JsVisitor() {
+ @Override
+ public void endVisit(JsForIn x, JsContext ctx) {
+ if (x.getIterVarName() != null) {
+ nameUsed.add(x.getIterVarName().getIdent());
+ }
+ }
+
+ @Override
+ public void endVisit(JsFunction x, JsContext ctx) {
+ if (x.getName() != null) {
+ nameToFragment.put(x.getName(), fragId);
+ nameUsed.add(x.getName().getIdent());
+ }
+ }
+
+ @Override
+ public void endVisit(JsLabel x, JsContext ctx) {
+ nameUsed.add(x.getName().getIdent());
+ }
+
+ @Override
+ public void endVisit(JsNameOf x, JsContext ctx) {
+ if (x.getName() != null) {
+ nameUsed.add(x.getName().getIdent());
+ }
+ }
+
+ @Override
+ public void endVisit(JsNameRef x, JsContext ctx) {
+ // Obviously this isn't even that accurate. Some of them are
+ // variable names, some of the are property. At least this
+ // this give us a safe approximation. Ideally we need
+ // the code removal passes to remove stuff in the scope objects.
+ if (x.isResolved()) {
+ nameUsed.add(x.getName().getIdent());
+ }
+ }
+
+ @Override
+ public void endVisit(JsParameter x, JsContext ctx) {
+ nameUsed.add(x.getName().getIdent());
+ }
+
+ @Override
+
+ public void endVisit(JsVars.JsVar x, JsContext ctx) {
+ nameUsed.add(x.getName().getIdent());
+ }
+ }.accept(jsProgram.getFragmentBlock(i));
+ }
+
+ // TODO(acleung): This is a temp fix. Once we know this is safe. We
+ // new to rewrite it to avoid extra ArrayList creations.
+ // Or we should just consider serializing it as an ArrayList if
+ // it is that much trouble to determine the true size.
+ List<SymbolData> result = new ArrayList<SymbolData>();
+
+ for (Map.Entry<StandardSymbolData, JsName> entry : symbolTable.entrySet()) {
+ StandardSymbolData symbolData = entry.getKey();
+ symbolData.setSymbolName(entry.getValue().getShortIdent());
+ Integer fragNum = nameToFragment.get(entry.getValue());
+ if (fragNum != null) {
+ symbolData.setFragmentNumber(fragNum);
+ }
+ if (nameUsed.contains(entry.getValue().getIdent()) || entry.getKey().isClass()) {
+ result.add(symbolData);
+ }
+ }
+
+ return result.toArray(new SymbolData[result.size()]);
+ }
+
+ /**
+ * Open an emitted artifact and gunzip its contents.
+ */
+ private InputStream openWithGunzip(EmittedArtifact artifact)
+ throws IOException, UnableToCompleteException {
+ return new BufferedInputStream(new GZIPInputStream(artifact.getContents(TreeLogger.NULL)));
+ }
+
+ private void optimizeJsLoop(Collection<JsNode> toInline) throws InterruptedException {
+ int optimizationLevel = options.getOptimizationLevel();
+ List<OptimizerStats> allOptimizerStats = Lists.newArrayList();
+ int counter = 0;
+ while (true) {
+ counter++;
+ if (Thread.interrupted()) {
+ throw new InterruptedException();
+ }
+ Event optimizeJsEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE_JS);
+
+ OptimizerStats stats = new OptimizerStats("Pass " + counter);
+
+ // Remove unused functions if possible.
+ stats.add(JsStaticEval.exec(jsProgram));
+ // Inline Js function invocations
+ stats.add(JsInliner.exec(jsProgram, toInline));
+ // Remove unused functions if possible.
+ stats.add(JsUnusedFunctionRemover.exec(jsProgram));
+
+ // Save the stats to print out after optimizers finish.
+ allOptimizerStats.add(stats);
+
+ optimizeJsEvent.end();
+ if ((optimizationLevel < OptionOptimize.OPTIMIZE_LEVEL_MAX && counter > optimizationLevel)
+ || !stats.didChange()) {
+ break;
+ }
+ }
+
+ if (optimizationLevel > OptionOptimize.OPTIMIZE_LEVEL_DRAFT) {
+ DuplicateExecuteOnceRemover.exec(jsProgram);
+ }
+ }
+
+ private Map<JsName, JsLiteral> renameJsSymbols(PermProps props, JavaToJavaScriptMap jjsmap)
+ throws UnableToCompleteException {
+ Map<JsName, JsLiteral> internedLiteralByVariableName;
+ try {
+ switch (options.getOutput()) {
+ case OBFUSCATED:
+ internedLiteralByVariableName = runObfuscateNamer(props);
+ break;
+ case PRETTY:
+ internedLiteralByVariableName = runPrettyNamer(props.getConfigProps(), jjsmap);
+ break;
+ case DETAILED:
+ internedLiteralByVariableName = runDetailedNamer(props.getConfigProps());
+ break;
+ default:
+ throw new InternalCompilerException("Unknown output mode");
+ }
+ } catch (IllegalNameException e) {
+ logger.log(TreeLogger.ERROR, e.getMessage(), e);
+ throw new UnableToCompleteException();
+ }
+ return internedLiteralByVariableName == null ?
+ ImmutableMap.<JsName, JsLiteral>of() : internedLiteralByVariableName;
+ }
+
+ private Map<JsName, JsLiteral> runObfuscateNamer(PermProps props) throws IllegalNameException {
+ Map<JsName, JsLiteral> internedLiteralByVariableName =
+ JsLiteralInterner.exec(jprogram, jsProgram, (byte) (JsLiteralInterner.INTERN_ALL
+ & (byte) (jprogram.typeOracle.isJsInteropEnabled()
+ ? ~JsLiteralInterner.INTERN_STRINGS : ~0)));
+ FreshNameGenerator freshNameGenerator = JsObfuscateNamer.exec(jsProgram,
+ props.getConfigProps());
+ if (options.shouldRemoveDuplicateFunctions()
+ && JsStackEmulator.getStackMode(props) == JsStackEmulator.StackMode.STRIP) {
+ JsDuplicateFunctionRemover.exec(jsProgram, freshNameGenerator);
+ }
+ return internedLiteralByVariableName;
+ }
+
+ private Map<JsName, JsLiteral> runPrettyNamer(ConfigProps config, JavaToJavaScriptMap jjsmap)
+ throws IllegalNameException {
+ if (compilerContext.getOptions().isIncrementalCompileEnabled()) {
+ JsIncrementalNamer.exec(jsProgram, config,
+ compilerContext.getMinimalRebuildCache().getPersistentPrettyNamerState(), jjsmap);
+ return null;
+ }
+
+ // We don't intern strings in pretty mode to improve readability
+ Map<JsName, JsLiteral> internedLiteralByVariableName = JsLiteralInterner.exec(
+ jprogram, jsProgram,
+ (byte) (JsLiteralInterner.INTERN_ALL & ~JsLiteralInterner.INTERN_STRINGS));
+
+ JsPrettyNamer.exec(jsProgram, config);
+ return internedLiteralByVariableName;
+ }
+
+ /**
+ * Takes as input a CompilationState and transforms that into a unified by not yet resolved Java
+ * AST (a Java AST wherein cross-class references have been connected and all rebind result
+ * classes are available and have not yet been pruned down to the set applicable for a particular
+ * permutation). This AST is packaged into a UnifiedAst instance and then returned.
+ *
+ * Precompilation is INTENDED to progress as a series of stages:
+ *
+ * <pre>
+ * 1. initialize local state
+ * 2. assert preconditions
+ * 3. construct and unify the unresolved Java AST
+ * 4. normalize the unresolved Java AST // arguably should be removed
+ * 5. optimize the unresolved Java AST // arguably should be removed
+ * 6. construct and return a value
+ * </pre>
+ *
+ * There are some other types of work here (mostly metrics and data gathering) which do not serve
+ * the goal of output program construction. This work should really be moved into subclasses or
+ * some sort of callback or plugin system so as not to visually pollute the real compile logic.<br
+ * />
+ *
+ * Significant amounts of visitors implementing the intended above stages are triggered here but
+ * in the wrong order. They have been noted for future cleanup.
+ */
+ private UnifiedAst precompile(RebindPermutationOracle rpo, String[] entryPointTypeNames,
+ String[] additionalRootTypes, boolean singlePermutation,
+ PrecompilationMetricsArtifact precompilationMetrics) throws UnableToCompleteException {
+ try {
+ /*
+ * Do not introduce any new pass here unless it is logically a part of one of the 6 defined
+ * stages and is physically located in that stage.
+ */
+
+ // (1) Initialize local state
+ jprogram = new JProgram(compilerContext.getMinimalRebuildCache());
+ // Synchronize JTypeOracle with compile optimization behavior.
+ jprogram.typeOracle.setOptimize(
+ options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT);
+ jprogram.typeOracle.setJsInteropMode(options.getJsInteropMode());
+
+ jsProgram = new JsProgram();
+ if (additionalRootTypes == null) {
+ additionalRootTypes = Empty.STRINGS;
+ }
+
+ // (2) Assert preconditions
+ if (entryPointTypeNames.length + additionalRootTypes.length == 0) {
+ throw new IllegalArgumentException("entry point(s) required");
+ }
+
+ // (3) Construct and unify the unresolved Java AST
+ CompilationState compilationState =
+ constructJavaAst(rpo, entryPointTypeNames, additionalRootTypes);
+
+ // TODO(stalcup): hide metrics gathering in a callback or subclass
+ JsniRestrictionChecker.exec(logger, jprogram);
+ JsInteropRestrictionChecker.exec(logger, jprogram, getMinimalRebuildCache());
+ logTypeOracleMetrics(precompilationMetrics, compilationState);
+ Memory.maybeDumpMemory("AstOnly");
+ AstDumper.maybeDumpAST(jprogram);
+
+ // TODO(stalcup): is in wrong place, move to optimization stage
+ ConfigProps configProps = new ConfigProps(module);
+ EnumNameObfuscator.exec(jprogram, logger,configProps);
+
+ // (4) Normalize the unresolved Java AST
+ // Replace defender method references
+ ReplaceDefenderMethodReferences.exec(jprogram);
+
+ FixAssignmentsToUnboxOrCast.exec(jprogram);
+ if (options.isEnableAssertions()) {
+ AssertionNormalizer.exec(jprogram);
+ } else {
+ AssertionRemover.exec(jprogram);
+ }
+ if (module != null && options.isRunAsyncEnabled()) {
+ ReplaceRunAsyncs.exec(logger, jprogram);
+ ConfigProps config = new ConfigProps(module);
+ CodeSplitters.pickInitialLoadSequence(logger, jprogram, config);
+ }
+ ImplementClassLiteralsAsFields.exec(jprogram);
+
+ // (5) Optimize the unresolved Java AST
+ optimizeJava(singlePermutation);
+
+ // TODO(stalcup): hide metrics gathering in a callback or subclass
+ logAstTypeMetrics(precompilationMetrics);
+
+ // (6) Construct and return a value.
+ Event createUnifiedAstEvent = SpeedTracerLogger.start(CompilerEventType.CREATE_UNIFIED_AST);
+ UnifiedAst result = new UnifiedAst(
+ options, new AST(jprogram, jsProgram), singlePermutation, RecordRebinds.exec(jprogram));
+ createUnifiedAstEvent.end();
+ return result;
+ } catch (Throwable e) {
+ throw CompilationProblemReporter.logAndTranslateException(logger, e);
+ }
+ }
+
+ /**
+ * Creates (and returns the name for) a new class to serve as the container for the invocation of
+ * registered entry point methods as part of module bootstrapping.<br />
+ *
+ * The resulting class will be invoked during bootstrapping like FooEntryMethodHolder.init(). By
+ * generating the class on the fly and naming it to match the current module, the resulting holder
+ * class can work in both monolithic and separate compilation schemes.
+ */
+ private String buildEntryMethodHolder(StandardGeneratorContext context,
+ String[] entryPointTypeNames, Set<String> allRootTypes)
+ throws UnableToCompleteException {
+ // If there are no entry points.
+ if (entryPointTypeNames.length == 0) {
+ // Then there's no need to generate an EntryMethodHolder class to launch them.
+ return null;
+ }
+
+ EntryMethodHolderGenerator entryMethodHolderGenerator = new EntryMethodHolderGenerator();
+ String entryMethodHolderTypeName =
+ entryMethodHolderGenerator.generate(logger, context, module.getCanonicalName());
+ context.finish(logger);
+ // Ensures that unification traverses and keeps the class.
+ allRootTypes.add(entryMethodHolderTypeName);
+ // Ensures that JProgram knows to index this class's methods so that later bootstrap
+ // construction code is able to locate the FooEntryMethodHolder.init() function.
+ jprogram.addIndexedTypeName(entryMethodHolderTypeName);
+ return entryMethodHolderTypeName;
+ }
+
+ private CompilationState constructJavaAst(RebindPermutationOracle rpo,
+ String[] entryPointTypeNames, String[] additionalRootTypes)
+ throws UnableToCompleteException {
+ Set<String> allRootTypes = Sets.newTreeSet();
+ CompilationState compilationState = rpo.getCompilationState();
+ Memory.maybeDumpMemory("CompStateBuilt");
+ recordJsoTypes(compilationState.getTypeOracle());
+ populateRootTypes(allRootTypes, entryPointTypeNames, additionalRootTypes, compilationState);
+ String entryMethodHolderTypeName =
+ buildEntryMethodHolder(rpo.getGeneratorContext(), entryPointTypeNames, allRootTypes);
+ unifyJavaAst(rpo, entryPointTypeNames, allRootTypes, entryMethodHolderTypeName);
+ if (options.isSoycEnabled() || options.isJsonSoycEnabled()) {
+ SourceInfoCorrelator.exec(jprogram);
+ }
+
+ // Free up memory.
+ rpo.clear();
+ Set<String> deletedTypeNames = options.isIncrementalCompileEnabled()
+ ? getMinimalRebuildCache().computeDeletedTypeNames() : Sets.<String>newHashSet();
+ jprogram.typeOracle.computeBeforeAST(StandardTypes.createFrom(jprogram),
+ jprogram.getDeclaredTypes(), jprogram.getModuleDeclaredTypes(), deletedTypeNames);
+ return compilationState;
+ }
+
+ /**
+ * This method can be used to fetch the list of referenced class.
+ *
+ * This method is intended to support compiler metrics.
+ */
+ private String[] getReferencedJavaClasses() {
+ class ClassNameVisitor extends JVisitor {
+ List<String> classNames = new ArrayList<String>();
+
+ @Override
+ public boolean visit(JClassType x, Context ctx) {
+ classNames.add(x.getName());
+ return true;
+ }
+ }
+ ClassNameVisitor v = new ClassNameVisitor();
+ v.accept(jprogram);
+ return v.classNames.toArray(new String[v.classNames.size()]);
+ }
+
+ private void logAstTypeMetrics(PrecompilationMetricsArtifact precompilationMetrics) {
+ if (options.isCompilerMetricsEnabled()) {
+ precompilationMetrics.setAstTypes(getReferencedJavaClasses());
+ }
+ }
+
+ private void logTypeOracleMetrics(
+ PrecompilationMetricsArtifact precompilationMetrics, CompilationState compilationState) {
+ if (precompilationMetrics != null) {
+ List<String> finalTypeOracleTypes = Lists.newArrayList();
+ for (com.google.gwt.core.ext.typeinfo.JClassType type :
+ compilationState.getTypeOracle().getTypes()) {
+ finalTypeOracleTypes.add(type.getPackage().getName() + "." + type.getName());
+ }
+ precompilationMetrics.setFinalTypeOracleTypes(finalTypeOracleTypes);
+ }
+ }
+
+ private void optimizeJava(boolean singlePermutation) throws InterruptedException {
+ if (options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT
+ && !singlePermutation) {
+ if (options.isOptimizePrecompile()) {
+ /*
+ * Go ahead and optimize early, so that each permutation will run faster. This code path
+ * is used by the Compiler entry point. We assume that we will not be able to perfectly
+ * parallelize the permutation compiles, so let's optimize as much as possible the common
+ * AST. In some cases, this might also have the side benefit of reducing the total
+ * permutation count.
+ */
+ optimizeJavaToFixedPoint();
+ } else {
+ /*
+ * Do only minimal early optimizations. This code path is used by the Precompile entry
+ * point. The external system might be able to perfectly parallelize the permutation
+ * compiles, so let's avoid doing potentially superlinear optimizations on the unified
+ * AST.
+ */
+ optimizeJavaOneTime("Early Optimization", jprogram.getNodeCount(),
+ new FullOptimizerContext(jprogram));
+ }
+ }
+ }
+
+ private void populateRootTypes(Set<String> allRootTypes, String[] entryPointTypeNames,
+ String[] additionalRootTypes, CompilationState compilationState) {
+ if (jprogram.typeOracle.isJsInteropEnabled()) {
+ Iterables.addAll(allRootTypes, compilationState.getQualifiedJsInteropRootTypesNames());
+ }
+ Collections.addAll(allRootTypes, entryPointTypeNames);
+ Collections.addAll(allRootTypes, additionalRootTypes);
+ allRootTypes.addAll(JProgram.CODEGEN_TYPES_SET);
+ allRootTypes.addAll(jprogram.getTypeNamesToIndex());
+ /*
+ * Add all SingleJsoImpl types that we know about. It's likely that the concrete types are
+ * never explicitly referenced.
+ */
+ TypeOracle typeOracle = compilationState.getTypeOracle();
+ for (com.google.gwt.core.ext.typeinfo.JClassType singleJsoIntf :
+ typeOracle.getSingleJsoImplInterfaces()) {
+ allRootTypes.add(typeOracle.getSingleJsoImpl(singleJsoIntf).getQualifiedSourceName());
+ }
+ }
+
+ private void recordJsoTypes(TypeOracle typeOracle) {
+ if (!options.isIncrementalCompileEnabled()) {
+ return;
+ }
+
+ // Add names of JSO subtypes.
+ Set<String> jsoTypeNames = Sets.newHashSet();
+ for (com.google.gwt.dev.javac.typemodel.JClassType subtype :
+ typeOracle.getJavaScriptObject().getSubtypes()) {
+ jsoTypeNames.add(subtype.getQualifiedBinaryName());
+ }
+
+ // Add names of interfaces that are always of a JSO (aka there are no non-JSO implementors).
+ Set<String> singleJsoImplInterfaceNames = Sets.newHashSet();
+ for (com.google.gwt.core.ext.typeinfo.JClassType singleJsoImplInterface :
+ typeOracle.getSingleJsoImplInterfaces()) {
+ singleJsoImplInterfaceNames.add(singleJsoImplInterface.getQualifiedBinaryName());
+ }
+
+ // Add names of interfaces that are only sometimes a JSO (aka there are both JSO and non-JSO
+ // imlementors).
+ Set<String> dualJsoImplInterfaceNames = Sets.newHashSet();
+ for (com.google.gwt.core.ext.typeinfo.JClassType dualJsoImplInterface :
+ typeOracle.getDualJsoImplInterfaces()) {
+ dualJsoImplInterfaceNames.add(dualJsoImplInterface.getQualifiedBinaryName());
+ }
+
+ compilerContext.getMinimalRebuildCache().setJsoTypeNames(jsoTypeNames,
+ singleJsoImplInterfaceNames, dualJsoImplInterfaceNames);
+ }
+
+ private void synthesizeEntryMethodHolderInit(UnifyAst unifyAst, String[] entryPointTypeNames,
+ String entryMethodHolderTypeName) throws UnableToCompleteException {
+ // Get type references.
+ JDeclaredType entryMethodHolderType =
+ unifyAst.findType(entryMethodHolderTypeName, unifyAst.getSourceNameBasedTypeLocator());
+ JDeclaredType gwtType = unifyAst.findType("com.google.gwt.core.client.GWT",
+ unifyAst.getSourceNameBasedTypeLocator());
+ JDeclaredType entryPointType = unifyAst.findType("com.google.gwt.core.client.EntryPoint",
+ unifyAst.getSourceNameBasedTypeLocator());
+
+ // Get method references.
+ JMethod initMethod = entryMethodHolderType.findMethod("init()V", false);
+ JMethod gwtCreateMethod =
+ gwtType.findMethod("create(Ljava/lang/Class;)Ljava/lang/Object;", false);
+
+ // Synthesize all onModuleLoad() calls.
+ JBlock initMethodBlock = ((JMethodBody) initMethod.getBody()).getBlock();
+ SourceInfo origin = initMethodBlock.getSourceInfo().makeChild();
+ for (String entryPointTypeName : entryPointTypeNames) {
+ // Get type and onModuleLoad function for the current entryPointTypeName.
+ JDeclaredType specificEntryPointType =
+ unifyAst.findType(entryPointTypeName, unifyAst.getSourceNameBasedTypeLocator());
+ if (specificEntryPointType == null) {
+ logger.log(TreeLogger.ERROR,
+ "Could not find module entry point class '" + entryPointTypeName + "'", null);
+ throw new UnableToCompleteException();
+ }
+ JMethod onModuleLoadMethod =
+ entryPointType.findMethod("onModuleLoad()V", true);
+ JMethod specificOnModuleLoadMethod =
+ specificEntryPointType.findMethod("onModuleLoad()V", true);
+
+ if (specificOnModuleLoadMethod != null && specificOnModuleLoadMethod.isStatic()) {
+ // Synthesize a static invocation FooEntryPoint.onModuleLoad(); call.
+ JMethodCall staticOnModuleLoadCall =
+ new JMethodCall(origin, null, specificOnModuleLoadMethod);
+ initMethodBlock.addStmt(staticOnModuleLoadCall.makeStatement());
+ } else {
+ // Synthesize ((EntryPoint)GWT.create(FooEntryPoint.class)).onModuleLoad();
+ JClassLiteral entryPointTypeClassLiteral =
+ new JClassLiteral(origin, specificEntryPointType);
+ JMethodCall createInstanceCall =
+ new JMethodCall(origin, null, gwtCreateMethod, entryPointTypeClassLiteral);
+ JCastOperation castToEntryPoint =
+ new JCastOperation(origin, entryPointType, createInstanceCall);
+ JMethodCall instanceOnModuleLoadCall =
+ new JMethodCall(origin, castToEntryPoint, onModuleLoadMethod);
+ initMethodBlock.addStmt(instanceOnModuleLoadCall.makeStatement());
+ }
+ }
+ }
+
+ private void unifyJavaAst(RebindPermutationOracle rpo, String[] entryPointTypeNames,
+ Set<String> allRootTypes, String entryMethodHolderTypeName)
+ throws UnableToCompleteException {
+
+ Event event = SpeedTracerLogger.start(CompilerEventType.UNIFY_AST);
+
+ UnifyAst unifyAst = new UnifyAst(logger, compilerContext, jprogram, jsProgram, rpo);
+ // Makes JProgram aware of these types so they can be accessed via index.
+ unifyAst.addRootTypes(allRootTypes);
+ // Must synthesize entryPoint.onModuleLoad() calls because some EntryPoint classes are
+ // private.
+ if (entryMethodHolderTypeName != null) {
+ // Only synthesize the init method in the EntryMethodHolder class, if there is an
+ // EntryMethodHolder class.
+ synthesizeEntryMethodHolderInit(unifyAst, entryPointTypeNames, entryMethodHolderTypeName);
+ }
+ if (entryMethodHolderTypeName != null) {
+ // Only register the init method in the EntryMethodHolder class as an entry method, if there
+ // is an EntryMethodHolder class.
+ jprogram.addEntryMethod(jprogram.getIndexedMethod(
+ SourceName.getShortClassName(entryMethodHolderTypeName) + ".init"));
+ }
+ unifyAst.exec();
+
+ event.end();
+ }
+
+ private void optimizeJavaToFixedPoint() throws InterruptedException {
+ Event optimizeEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE);
+
+ List<OptimizerStats> allOptimizerStats = Lists.newArrayList();
+ int passCount = 0;
+ int nodeCount = jprogram.getNodeCount();
+ int lastNodeCount;
+
+ boolean atMaxLevel = options.getOptimizationLevel() == OptionOptimize.OPTIMIZE_LEVEL_MAX;
+ int passLimit = atMaxLevel ? MAX_PASSES : options.getOptimizationLevel();
+ float minChangeRate = atMaxLevel ? FIXED_POINT_CHANGE_RATE : EFFICIENT_CHANGE_RATE;
+ OptimizerContext optimizerCtx = new FullOptimizerContext(jprogram);
+ while (true) {
+ passCount++;
+ if (passCount > passLimit) {
+ break;
+ }
+ if (Thread.interrupted()) {
+ optimizeEvent.end();
+ throw new InterruptedException();
+ }
+ AstDumper.maybeDumpAST(jprogram);
+ OptimizerStats stats = optimizeJavaOneTime("Pass " + passCount, nodeCount, optimizerCtx);
+ allOptimizerStats.add(stats);
+ lastNodeCount = nodeCount;
+ nodeCount = jprogram.getNodeCount();
+
+ float nodeChangeRate = stats.getNumMods() / (float) lastNodeCount;
+ float sizeChangeRate = (lastNodeCount - nodeCount) / (float) lastNodeCount;
+ if (nodeChangeRate <= minChangeRate && sizeChangeRate <= minChangeRate) {
+ break;
+ }
+ }
+
+ if (options.shouldOptimizeDataflow()) {
+ // Just run it once, because it is very time consuming
+ allOptimizerStats.add(DataflowOptimizer.exec(jprogram));
+ }
+
+ optimizeEvent.end();
+ }
+
+ private boolean shouldOptimize() {
+ return options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT;
+ }
+
+ private TypeMapper getTypeMapper() {
+
+ // Used to stabilize output for DeltaJS
+ if (JjsUtils.closureStyleLiteralsNeeded(this.options)) {
+ return new ClosureUniqueIdTypeMapper(jprogram);
+ }
+
+ if (this.options.useDetailedTypeIds()) {
+ return new StringTypeMapper();
+ }
+ return this.options.isIncrementalCompileEnabled() ?
+ compilerContext.getMinimalRebuildCache().getTypeMapper() :
+ new IntTypeMapper();
+ }
+
+ private TypeOrder getTypeOrder() {
+
+ // Used to stabilize output for DeltaJS
+ if (JjsUtils.closureStyleLiteralsNeeded(this.options)) {
+ return TypeOrder.ALPHABETICAL;
+ }
+
+ if (this.options.useDetailedTypeIds()) {
+ return TypeOrder.NONE;
+ }
+ return this.options.isIncrementalCompileEnabled() ? TypeOrder.ALPHABETICAL
+ : TypeOrder.FREQUENCY;
+ }
+
+ private OptimizerStats optimizeJavaOneTime(String passName, int numNodes,
+ OptimizerContext optimizerCtx) {
+ Event optimizeEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE, "phase", "loop");
+ // Clinits might have become empty become empty.
+ jprogram.typeOracle.recomputeAfterOptimizations(jprogram.getDeclaredTypes());
+ OptimizerStats stats = new OptimizerStats(passName);
+ JavaAstVerifier.assertProgramIsConsistent(jprogram);
+ stats.add(Pruner.exec(jprogram, true, optimizerCtx).recordVisits(numNodes));
+ stats.add(Finalizer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ stats.add(MakeCallsStatic.exec(jprogram, options.shouldAddRuntimeChecks(), optimizerCtx)
+ .recordVisits(numNodes));
+ stats.add(TypeTightener.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ stats.add(MethodCallTightener.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ // Note: Specialization should be done before inlining.
+ stats.add(MethodCallSpecializer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ stats.add(DeadCodeElimination.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ stats.add(MethodInliner.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ if (options.shouldInlineLiteralParameters()) {
+ stats.add(SameParameterValueOptimizer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ }
+ if (options.shouldOrdinalizeEnums()) {
+ stats.add(EnumOrdinalizer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
+ }
+ optimizeEvent.end();
+ return stats;
+ }
+
+ private MinimalRebuildCache getMinimalRebuildCache() {
+ return compilerContext.getMinimalRebuildCache();
+ }
+
private static class PermutationResultImpl implements PermutationResult {
private final ArtifactSet artifacts = new ArtifactSet();
@@ -1303,147 +1599,4 @@
return statementRanges;
}
}
-
- /**
- * Ending optimization passes when the rate of change has reached this value results in gaining
- * nearly all of the impact while avoiding the long tail of costly but low-impact passes.
- */
- private static final float EFFICIENT_CHANGE_RATE = 0.01f;
-
- private static final String ENUM_NAME_OBFUSCATION_PROPERTY = "compiler.enum.obfuscate.names";
-
- private static final String ENUM_NAME_OBFUSCATION_BLACKLIST_PROPERTY = "compiler.enum.obfuscate.names.blacklist";
-
- /**
- * Continuing to apply optimizations till the rate of change reaches this value causes the AST to
- * reach a fixed point.
- */
- private static final int FIXED_POINT_CHANGE_RATE = 0;
-
- /**
- * Limits the number of optimization passes against the possible danger of an AST that does not
- * converge.
- */
- private static final int MAX_PASSES = 100;
-
- static {
- InternalCompilerException.preload();
- }
-
- protected final CompilerContext compilerContext;
-
- protected JsProgram jsProgram;
-
- protected final TreeLogger logger;
-
- protected final ModuleDef module;
-
- protected final PrecompileTaskOptions options;
-
- @VisibleForTesting
- JProgram jprogram;
-
- public JavaToJavaScriptCompiler(TreeLogger logger, CompilerContext compilerContext) {
- this.logger = logger;
- this.compilerContext = compilerContext;
- this.module = compilerContext.getModule();
- this.options = compilerContext.getOptions();
- }
-
- /**
- * Compiles and returns a particular permutation, based on a precompiled unified AST.
- */
- public abstract PermutationResult compilePermutation(
- UnifiedAst unifiedAst, Permutation permutation) throws UnableToCompleteException;
-
- /**
- * Performs a precompilation, returning a unified AST.
- */
- public abstract UnifiedAst precompile(RebindPermutationOracle rpo, String[] entryPointTypeNames,
- String[] additionalRootTypes, boolean singlePermutation,
- PrecompilationMetricsArtifact precompilationMetrics) throws UnableToCompleteException;
-
- protected final void optimizeJavaToFixedPoint() throws InterruptedException {
- Event optimizeEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE);
-
- List<OptimizerStats> allOptimizerStats = new ArrayList<OptimizerStats>();
- int passCount = 0;
- int nodeCount = jprogram.getNodeCount();
- int lastNodeCount;
-
- boolean atMaxLevel = options.getOptimizationLevel() == OptionOptimize.OPTIMIZE_LEVEL_MAX;
- int passLimit = atMaxLevel ? MAX_PASSES : options.getOptimizationLevel();
- float minChangeRate = atMaxLevel ? FIXED_POINT_CHANGE_RATE : EFFICIENT_CHANGE_RATE;
- OptimizerContext optimizerCtx = new FullOptimizerContext(jprogram);
- while (true) {
- passCount++;
- if (passCount > passLimit) {
- break;
- }
- if (Thread.interrupted()) {
- optimizeEvent.end();
- throw new InterruptedException();
- }
- AstDumper.maybeDumpAST(jprogram);
- OptimizerStats stats = optimizeJavaOneTime("Pass " + passCount, nodeCount, optimizerCtx);
- allOptimizerStats.add(stats);
- lastNodeCount = nodeCount;
- nodeCount = jprogram.getNodeCount();
-
- float nodeChangeRate = stats.getNumMods() / (float) lastNodeCount;
- float sizeChangeRate = (lastNodeCount - nodeCount) / (float) lastNodeCount;
- if (nodeChangeRate <= minChangeRate && sizeChangeRate <= minChangeRate) {
- break;
- }
- }
-
- if (options.shouldOptimizeDataflow()) {
- // Just run it once, because it is very time consuming
- allOptimizerStats.add(DataflowOptimizer.exec(jprogram));
- }
-
- optimizeEvent.end();
- }
-
- /*
- * This method is intended as a central location for producing optional tracking output. This will
- * be called after all optimization/normalization passes have completed.
- */
- private void logTrackingStats() {
- EnumOrdinalizer.Tracker eot = EnumOrdinalizer.getTracker();
- if (eot != null) {
- eot.logResultsDetailed(logger, TreeLogger.WARN);
- }
- }
-
- private OptimizerStats optimizeJavaOneTime(String passName, int numNodes,
- OptimizerContext optimizerCtx) {
- Event optimizeEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE, "phase", "loop");
- // Clinits might have become empty become empty.
- jprogram.typeOracle.recomputeAfterOptimizations(jprogram.getDeclaredTypes());
- OptimizerStats stats = new OptimizerStats(passName);
- JavaAstVerifier.assertProgramIsConsistent(jprogram);
- stats.add(Pruner.exec(jprogram, true, optimizerCtx).recordVisits(numNodes));
- stats.add(Finalizer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- stats.add(MakeCallsStatic.exec(jprogram, options.shouldAddRuntimeChecks(), optimizerCtx)
- .recordVisits(numNodes));
- stats.add(TypeTightener.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- stats.add(MethodCallTightener.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- // Note: Specialization should be done before inlining.
- stats.add(MethodCallSpecializer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- stats.add(DeadCodeElimination.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- stats.add(MethodInliner.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- if (options.shouldInlineLiteralParameters()) {
- stats.add(SameParameterValueOptimizer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- }
- if (options.shouldOrdinalizeEnums()) {
- stats.add(EnumOrdinalizer.exec(jprogram, optimizerCtx).recordVisits(numNodes));
- }
- optimizeEvent.end();
- return stats;
- }
-
- private MinimalRebuildCache getMinimalRebuildCache() {
- return compilerContext.getMinimalRebuildCache();
- }
}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/MonolithicJavaToJavaScriptCompiler.java b/dev/core/src/com/google/gwt/dev/jjs/MonolithicJavaToJavaScriptCompiler.java
deleted file mode 100644
index 6db5b3b..0000000
--- a/dev/core/src/com/google/gwt/dev/jjs/MonolithicJavaToJavaScriptCompiler.java
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * 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.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
-import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact;
-import com.google.gwt.core.ext.linker.SyntheticArtifact;
-import com.google.gwt.core.ext.soyc.coderef.DependencyGraphRecorder;
-import com.google.gwt.core.ext.soyc.impl.DependencyRecorder;
-import com.google.gwt.core.linker.SoycReportLinker;
-import com.google.gwt.dev.CompilerContext;
-import com.google.gwt.dev.Permutation;
-import com.google.gwt.dev.cfg.ConfigProps;
-import com.google.gwt.dev.cfg.PermProps;
-import com.google.gwt.dev.jdt.RebindPermutationOracle;
-import com.google.gwt.dev.jjs.ast.JProgram;
-import com.google.gwt.dev.jjs.impl.ArrayNormalizer;
-import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer;
-import com.google.gwt.dev.jjs.impl.ComputeCastabilityInformation;
-import com.google.gwt.dev.jjs.impl.ComputeExhaustiveCastabilityInformation;
-import com.google.gwt.dev.jjs.impl.ComputeInstantiatedJsoInterfaces;
-import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer;
-import com.google.gwt.dev.jjs.impl.Devirtualizer;
-import com.google.gwt.dev.jjs.impl.EqualityNormalizer;
-import com.google.gwt.dev.jjs.impl.HandleCrossFragmentReferences;
-import com.google.gwt.dev.jjs.impl.ImplementCastsAndTypeChecks;
-import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
-import com.google.gwt.dev.jjs.impl.JjsUtils;
-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.Pruner;
-import com.google.gwt.dev.jjs.impl.RemoveEmptySuperCalls;
-import com.google.gwt.dev.jjs.impl.RemoveSpecializations;
-import com.google.gwt.dev.jjs.impl.ReplaceGetClassOverrides;
-import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences;
-import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.ClosureUniqueIdTypeMapper;
-import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.IntTypeMapper;
-import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.StringTypeMapper;
-import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeMapper;
-import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeOrder;
-import com.google.gwt.dev.jjs.impl.TypeCoercionNormalizer;
-import com.google.gwt.dev.jjs.impl.codesplitter.CodeSplitter;
-import com.google.gwt.dev.jjs.impl.codesplitter.CodeSplitters;
-import com.google.gwt.dev.jjs.impl.codesplitter.MultipleDependencyGraphRecorder;
-import com.google.gwt.dev.js.JsDuplicateCaseFolder;
-import com.google.gwt.dev.js.JsLiteralInterner;
-import com.google.gwt.dev.js.JsNamer.IllegalNameException;
-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.util.Pair;
-import com.google.gwt.dev.util.arg.OptionOptimize;
-import com.google.gwt.dev.util.log.speedtracer.CompilerEventType;
-import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger;
-import com.google.gwt.dev.util.log.speedtracer.SpeedTracerLogger.Event;
-
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Compiles the Java <code>JProgram</code> representation into its corresponding Js source.
- */
-public class MonolithicJavaToJavaScriptCompiler extends JavaToJavaScriptCompiler {
-
- private class MonolithicPermutationCompiler extends PermutationCompiler {
-
- public MonolithicPermutationCompiler(Permutation permutation) {
- super(permutation);
- }
-
- @Override
- protected TypeMapper<?> normalizeSemantics() {
- Event event = SpeedTracerLogger.start(CompilerEventType.JAVA_NORMALIZERS);
- try {
- Devirtualizer.exec(jprogram);
- CatchBlockNormalizer.exec(jprogram);
- PostOptimizationCompoundAssignmentNormalizer.exec(jprogram);
- LongCastNormalizer.exec(jprogram);
- LongEmulationNormalizer.exec(jprogram);
- TypeCoercionNormalizer.exec(jprogram);
-
- if (options.isIncrementalCompileEnabled()) {
- // Per file compilation reuses type JS even as references (like casts) in other files
- // change, which means all legal casts need to be allowed now before they are actually
- // used later.
- ComputeExhaustiveCastabilityInformation.exec(jprogram);
- } else {
- // If trivial casts are pruned then one can use smaller runtime castmaps.
- ComputeCastabilityInformation.exec(jprogram, options.isCastCheckingDisabled(),
- !shouldOptimize() /* recordTrivialCasts */);
- }
-
- ComputeInstantiatedJsoInterfaces.exec(jprogram);
- ImplementCastsAndTypeChecks.exec(jprogram, options.isCastCheckingDisabled(),
- shouldOptimize() /* pruneTrivialCasts */);
- ArrayNormalizer.exec(jprogram, options.isCastCheckingDisabled());
- EqualityNormalizer.exec(jprogram);
-
- TypeMapper<?> typeMapper = getTypeMapper();
- ResolveRuntimeTypeReferences.exec(jprogram, typeMapper, getTypeOrder());
- return typeMapper;
- } finally {
- event.end();
- }
- }
-
- @Override
- protected void optimizeJava() throws InterruptedException {
- if (shouldOptimize()) {
- optimizeJavaToFixedPoint();
- RemoveEmptySuperCalls.exec(jprogram);
- }
- }
-
- @Override
- protected void optimizeJs(Set<JsNode> inlinableJsFunctions) throws InterruptedException {
- if (shouldOptimize()) {
- optimizeJsLoop(inlinableJsFunctions);
- JsDuplicateCaseFolder.exec(jsProgram);
- }
- }
-
- @Override
- protected void postNormalizationOptimizeJava() {
- Event event = SpeedTracerLogger.start(CompilerEventType.JAVA_POST_NORMALIZER_OPTIMIZERS);
- try {
- if (shouldOptimize()) {
- RemoveSpecializations.exec(jprogram);
- Pruner.exec(jprogram, false);
- }
- ReplaceGetClassOverrides.exec(jprogram);
- } finally {
- event.end();
- }
- }
-
- @Override
- protected Map<JsName, JsLiteral> runDetailedNamer(ConfigProps config)
- throws IllegalNameException {
- Map<JsName, JsLiteral> internedTextByVariableName = null;
- if (shouldOptimize()) {
- // Only perform the interning optimization when optimizations are enabled.
- internedTextByVariableName =
- JsLiteralInterner.exec(jprogram, jsProgram, (byte) (JsLiteralInterner.INTERN_ALL
- & (byte) (jprogram.typeOracle.isJsInteropEnabled()
- ? ~JsLiteralInterner.INTERN_STRINGS : ~0)));
- }
- JsVerboseNamer.exec(jsProgram, config);
- return internedTextByVariableName;
- }
-
- @Override
- protected Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> splitJsIntoFragments(
- PermProps props, int permutationId, JavaToJavaScriptMap jjsmap) {
- Pair<SyntheticArtifact, MultipleDependencyGraphRecorder> dependenciesAndRecorder;
- MultipleDependencyGraphRecorder dependencyRecorder = null;
- SyntheticArtifact dependencies = null;
- if (options.isRunAsyncEnabled()) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
-
- int expectedFragmentCount = options.getFragmentCount();
- // -1 is the default value, we trap 0 just in case (0 is not a legal value in any case)
- if (expectedFragmentCount <= 0) {
- // Fragment count not set check fragments merge.
- int numberOfMerges = options.getFragmentsMerge();
- if (numberOfMerges > 0) {
- // + 1 for left over, + 1 for initial gave us the total number
- // of fragments without splitting.
- expectedFragmentCount =
- Math.max(0, jprogram.getRunAsyncs().size() + 2 - numberOfMerges);
- }
- }
-
- int minFragmentSize = props.getConfigProps().getInteger(CodeSplitters.MIN_FRAGMENT_SIZE, 0);
-
- dependencyRecorder = chooseDependencyRecorder(baos);
- CodeSplitter.exec(logger, jprogram, jsProgram, jjsmap, expectedFragmentCount,
- minFragmentSize, dependencyRecorder);
-
- if (baos.size() == 0) {
- dependencyRecorder = recordNonSplitDependencies(baos);
- }
- if (baos.size() > 0) {
- dependencies = new SyntheticArtifact(
- SoycReportLinker.class, "dependencies" + permutationId + ".xml.gz",
- baos.toByteArray());
- }
- } else if (options.isSoycEnabled() || options.isJsonSoycEnabled()) {
- dependencyRecorder = recordNonSplitDependencies(new ByteArrayOutputStream());
- }
- dependenciesAndRecorder = Pair.<SyntheticArtifact, MultipleDependencyGraphRecorder> create(
- dependencies, dependencyRecorder);
-
- // No new JsNames or references to JSNames can be introduced after this
- // point.
- HandleCrossFragmentReferences.exec(jsProgram, props);
-
- return dependenciesAndRecorder;
- }
-
- private MultipleDependencyGraphRecorder chooseDependencyRecorder(OutputStream out) {
- MultipleDependencyGraphRecorder dependencyRecorder =
- MultipleDependencyGraphRecorder.NULL_RECORDER;
- if (options.isSoycEnabled() && options.isJsonSoycEnabled()) {
- dependencyRecorder = new DependencyGraphRecorder(out, jprogram);
- } else if (options.isSoycEnabled()) {
- dependencyRecorder = new DependencyRecorder(out);
- } else if (options.isJsonSoycEnabled()) {
- dependencyRecorder = new DependencyGraphRecorder(out, jprogram);
- }
- return dependencyRecorder;
- }
-
- /**
- * Dependency information is normally recorded during code splitting, and it results in multiple
- * dependency graphs. If the code splitter doesn't run, then this method can be used instead to
- * record a single dependency graph for the whole program.
- */
- private DependencyRecorder recordNonSplitDependencies(OutputStream out) {
- DependencyRecorder deps;
- if (options.isSoycEnabled() && options.isJsonSoycEnabled()) {
- deps = new DependencyGraphRecorder(out, jprogram);
- } else if (options.isSoycEnabled()) {
- deps = new DependencyRecorder(out);
- } else if (options.isJsonSoycEnabled()) {
- deps = new DependencyGraphRecorder(out, jprogram);
- } else {
- return null;
- }
- deps.open();
- deps.startDependencyGraph("initial", null);
-
- ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(jprogram);
- cfa.setDependencyRecorder(deps);
- cfa.traverseEntryMethods();
- deps.endDependencyGraph();
- deps.close();
- return deps;
- }
- }
-
- private class MonolithicPrecompiler extends Precompiler {
-
- public MonolithicPrecompiler(RebindPermutationOracle rpo, String[] entryPointTypeNames) {
- super(rpo, entryPointTypeNames);
- }
-
- @Override
- protected void beforeUnifyAst(Set<String> allRootTypes)
- throws UnableToCompleteException {
- }
-
- @Override
- protected void checkEntryPoints(String[] additionalRootTypes) {
- if (entryPointTypeNames.length + additionalRootTypes.length == 0) {
- throw new IllegalArgumentException("entry point(s) required");
- }
- }
-
- @Override
- protected void createJProgram(CompilerContext compilerContext) {
- jprogram = new JProgram(compilerContext.getMinimalRebuildCache());
- }
- }
-
- /**
- * Constructs a JavaToJavaScriptCompiler with customizations for compiling independent libraries.
- */
- public MonolithicJavaToJavaScriptCompiler(TreeLogger logger, CompilerContext compilerContext) {
- super(logger, compilerContext);
- }
-
- @Override
- public PermutationResult compilePermutation(UnifiedAst unifiedAst, Permutation permutation)
- throws UnableToCompleteException {
- return new MonolithicPermutationCompiler(permutation).compilePermutation(unifiedAst);
- }
-
- @Override
- public UnifiedAst precompile(RebindPermutationOracle rpo, String[] entryPointTypeNames,
- String[] additionalRootTypes, boolean singlePermutation,
- PrecompilationMetricsArtifact precompilationMetrics) throws UnableToCompleteException {
- return new MonolithicPrecompiler(rpo, entryPointTypeNames).precompile(additionalRootTypes,
- singlePermutation, precompilationMetrics);
- }
-
- private boolean shouldOptimize() {
- return options.getOptimizationLevel() > OptionOptimize.OPTIMIZE_LEVEL_DRAFT;
- }
-
- private TypeMapper getTypeMapper() {
-
- // Used to stabilize output for DeltaJS
- if (JjsUtils.closureStyleLiteralsNeeded(this.options)) {
- return new ClosureUniqueIdTypeMapper(jprogram);
- }
-
- if (this.options.useDetailedTypeIds()) {
- return new StringTypeMapper();
- }
- return this.options.isIncrementalCompileEnabled() ?
- compilerContext.getMinimalRebuildCache().getTypeMapper() :
- new IntTypeMapper();
- }
-
- private TypeOrder getTypeOrder() {
-
- // Used to stabilize output for DeltaJS
- if (JjsUtils.closureStyleLiteralsNeeded(this.options)) {
- return TypeOrder.ALPHABETICAL;
- }
-
- if (this.options.useDetailedTypeIds()) {
- return TypeOrder.NONE;
- }
- return this.options.isIncrementalCompileEnabled() ? TypeOrder.ALPHABETICAL : TypeOrder.FREQUENCY;
- }
-}
diff --git a/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java b/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java
index e21cafd..ae52af2 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/UnifiedAst.java
@@ -15,12 +15,8 @@
*/
package com.google.gwt.dev.jjs;
-import com.google.gwt.core.ext.TreeLogger;
-import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.ModuleMetricsArtifact;
import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact;
-import com.google.gwt.dev.CompilerContext;
-import com.google.gwt.dev.Permutation;
import com.google.gwt.dev.PrecompileTaskOptions;
import com.google.gwt.dev.PrecompileTaskOptionsImpl;
import com.google.gwt.dev.jjs.ast.JProgram;
@@ -113,24 +109,6 @@
}
/**
- * Compiles a particular permutation.
- *
- * @param logger the logger to use
- * @param compilerContext shared read only compiler state
- * @param permutation the permutation to compile
- * @return the permutation result
- * @throws UnableToCompleteException if an error other than
- * {@link OutOfMemoryError} occurs
- */
- public PermutationResult compilePermutation(
- TreeLogger logger, CompilerContext compilerContext, Permutation permutation)
- throws UnableToCompleteException {
- JavaToJavaScriptCompiler javaToJavaScriptCompiler =
- new MonolithicJavaToJavaScriptCompiler(logger, compilerContext);
- return javaToJavaScriptCompiler.compilePermutation(this, permutation);
- }
-
- /**
* Return the current AST so that clients can explicitly walk the Java or
* JavaScript parse trees.
*
diff --git a/dev/core/src/com/google/gwt/dev/jjs/EnumNameObfuscator.java b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumNameObfuscator.java
similarity index 62%
rename from dev/core/src/com/google/gwt/dev/jjs/EnumNameObfuscator.java
rename to dev/core/src/com/google/gwt/dev/jjs/impl/EnumNameObfuscator.java
index 74b30eb..3089112 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/EnumNameObfuscator.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/EnumNameObfuscator.java
@@ -13,9 +13,10 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
-package com.google.gwt.dev.jjs;
+package com.google.gwt.dev.jjs.impl;
import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.dev.cfg.ConfigProps;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JConstructor;
@@ -29,8 +30,8 @@
import com.google.gwt.dev.jjs.ast.JParameter;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JVisitor;
+import com.google.gwt.thirdparty.guava.common.collect.Lists;
-import java.util.ArrayList;
import java.util.List;
/**
@@ -38,6 +39,11 @@
*/
public class EnumNameObfuscator {
+ private static final String ENUM_NAME_OBFUSCATION_PROPERTY =
+ "compiler.enum.obfuscate.names";
+ private static final String ENUM_NAME_OBFUSCATION_BLACKLIST_PROPERTY =
+ "compiler.enum.obfuscate.names.blacklist";
+
private static class EnumNameCallChecker extends JVisitor {
private final JDeclaredType classType;
@@ -49,7 +55,7 @@
private final List<String> blacklistedEnums;
private final JDeclaredType stringType;
- public EnumNameCallChecker(JProgram jprogram, TreeLogger logger,
+ private EnumNameCallChecker(JProgram jprogram, TreeLogger logger,
List<String> blacklistedEnums) {
this.logger = logger;
this.blacklistedEnums = blacklistedEnums;
@@ -73,13 +79,12 @@
JMethod foundMethod = null;
List<JMethod> enumMethods = enumType.getMethods();
for (JMethod enumMethod : enumMethods) {
- if ("valueOf".equals(enumMethod.getName())) {
- List<JParameter> jParameters = enumMethod.getParams();
- if (jParameters.size() == 2 && jParameters.get(0).getType() == classType
- && jParameters.get(1).getType() == stringType) {
- foundMethod = enumMethod;
- break;
- }
+ List<JParameter> params = enumMethod.getParams();
+ if (enumMethod.getName().equals("valueOf") &&
+ params.size() == 2 && params.get(0).getType() == classType
+ && params.get(1).getType() == stringType) {
+ foundMethod = enumMethod;
+ break;
}
}
this.enumValueOfMethod = foundMethod;
@@ -90,27 +95,30 @@
JMethod target = x.getTarget();
JDeclaredType type = target.getEnclosingType();
- if (type instanceof JClassType) {
- JClassType cType = (JClassType) type;
- if (typeInBlacklist(blacklistedEnums, cType)) {
- return;
- }
- if (target == enumNameMethod || target == enumToStringMethod || target == enumValueOfMethod) {
- warn(x);
- } else if (cType.isEnumOrSubclass() != null) {
- if ("valueOf".equals(target.getName())) {
- /*
- * Check for calls to the auto-generated
- * EnumSubType.valueOf(String). Note, the check of the signature for
- * the single String arg version is to avoid flagging user-defined
- * overloaded versions of the method, which are presumably ok.
- */
- List<JParameter> jParameters = target.getParams();
- if (jParameters.size() == 1 && jParameters.get(0).getType() == stringType) {
- warn(x);
- }
- }
- }
+ if (!(type instanceof JClassType)) {
+ return;
+ }
+ JClassType cType = (JClassType) type;
+ if (typeInBlacklist(blacklistedEnums, cType)) {
+ return;
+ }
+ if (target == enumNameMethod || target == enumToStringMethod || target == enumValueOfMethod) {
+ warn(x);
+ return;
+ }
+ if (cType.isEnumOrSubclass() != null) {
+ return;
+ }
+ /*
+ * Check for calls to the auto-generated
+ * EnumSubType.valueOf(String). Note, the check of the signature for
+ * the single String arg version is to avoid flagging user-defined
+ * overloaded versions of the method, which are presumably ok.
+ */
+ List<JParameter> params = target.getParams();
+ if (target.getName().equals("valueOf") &&
+ params.size() == 1 && params.get(0).getType() == stringType) {
+ warn(x);
}
}
@@ -143,7 +151,7 @@
private boolean closureMode;
private final TreeLogger logger;
- public EnumNameReplacer(JProgram jprogram, TreeLogger logger,
+ private EnumNameReplacer(JProgram jprogram, TreeLogger logger,
List<String> blacklistedEnums, boolean closureMode) {
this.logger = logger;
this.jprogram = jprogram;
@@ -152,11 +160,12 @@
this.makeEnumName = jprogram.getIndexedMethod("Util.makeEnumName");
}
- public void exec() {
+ private void exec() {
accept(jprogram);
}
- @Override public boolean visit(JClassType x, Context ctx) {
+ @Override
+ public boolean visit(JClassType x, Context ctx) {
if (x.isEnumOrSubclass() == null || typeInBlacklist(blacklistedEnums, x)) {
return false;
}
@@ -164,31 +173,31 @@
return true;
}
- @Override public void endVisit(JNewInstance x, Context ctx) {
+ @Override
+ public void endVisit(JNewInstance x, Context ctx) {
JConstructor target = x.getTarget();
JClassType enclosingType = target.getEnclosingType();
- if (enclosingType.isEnumOrSubclass() != null) {
- if (!typeInBlacklist(blacklistedEnums, enclosingType)) {
- JNewInstance newEnum = new JNewInstance(x);
- // replace first argument with null
- List<JExpression> args = new ArrayList<JExpression>(x.getArgs());
- if (closureMode) {
- JMethodCall makeEnum = new JMethodCall(x.getSourceInfo(), null, makeEnumName, args.get(0));
- args.set(0, makeEnum);
- } else {
- args.set(0, JNullLiteral.INSTANCE);
- }
- newEnum.addArgs(args);
- ctx.replaceMe(newEnum);
- }
+ if (enclosingType.isEnumOrSubclass() == null) {
+ return;
}
+
+ if (typeInBlacklist(blacklistedEnums, enclosingType)) {
+ return;
+ }
+
+ JNewInstance newEnum = new JNewInstance(x);
+ List<JExpression> args = Lists.newArrayList(x.getArgs());
+ // replace first argument with null or the closure obfuscation function.
+ args.set(0, closureMode ?
+ new JMethodCall(x.getSourceInfo(), null, makeEnumName, args.get(0)) :
+ JNullLiteral.INSTANCE);
+ newEnum.addArgs(args);
+ ctx.replaceMe(newEnum);
}
private void trace(JClassType x) {
if (logger.isLoggable(TreeLogger.TRACE)) {
- logger.log(TreeLogger.TRACE,
- "Obfuscating enum " + x
- + x.getSourceInfo().getFileName() + ":"
+ logger.log(TreeLogger.TRACE, "Obfuscating enum " + x + x.getSourceInfo().getFileName() + ":"
+ x.getSourceInfo().getStartLine());
}
}
@@ -198,8 +207,16 @@
return blacklistedEnums.contains(cType.getName().replace('$', '.'));
}
- public static void exec(JProgram jprogram, TreeLogger logger,
- List<String> blacklistedEnums, boolean closureMode) {
+ public static void exec(JProgram jprogram, TreeLogger logger, ConfigProps configProps) {
+ String enumObfucationProperty = configProps.getString(
+ EnumNameObfuscator.ENUM_NAME_OBFUSCATION_PROPERTY, "false");
+ // TODO(rluble): Replace configuration property by the command line option.
+ boolean closureMode = enumObfucationProperty.equals("closure");
+ if (enumObfucationProperty.equals("false")) {
+ return;
+ }
+ List<String> blacklistedEnums = configProps.getCommaSeparatedStrings(
+ EnumNameObfuscator.ENUM_NAME_OBFUSCATION_BLACKLIST_PROPERTY);
new EnumNameCallChecker(jprogram, logger, blacklistedEnums).accept(jprogram);
new EnumNameReplacer(jprogram, logger, blacklistedEnums, closureMode).exec();
}