blob: 6db5b3b23e65a092eadba1cd27ab1a46cf1d2e7f [file] [log] [blame]
/*
* 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;
}
}