All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.gwt.dev.jjs.JavaToJavaScriptCompiler Maven / Gradle / Ivy

There is a newer version: 2.7.0.vaadin7
Show newest version
/*
 * Copyright 2008 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package com.google.gwt.dev.jjs;

import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.linker.Artifact;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.CompilationMetricsArtifact;
import com.google.gwt.core.ext.linker.EmittedArtifact;
import com.google.gwt.core.ext.linker.EmittedArtifact.Visibility;
import com.google.gwt.core.ext.linker.ModuleMetricsArtifact;
import com.google.gwt.core.ext.linker.PrecompilationMetricsArtifact;
import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.core.ext.linker.SymbolData;
import com.google.gwt.core.ext.linker.SyntheticArtifact;
import com.google.gwt.core.ext.linker.impl.StandardSymbolData;
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.SizeMapRecorder;
import com.google.gwt.core.ext.soyc.impl.SplitPointRecorder;
import com.google.gwt.core.ext.soyc.impl.StoryRecorder;
import com.google.gwt.core.linker.SoycReportLinker;
import com.google.gwt.dev.CompilerContext;
import com.google.gwt.dev.MinimalRebuildCache;
import com.google.gwt.dev.Permutation;
import com.google.gwt.dev.PrecompileTaskOptions;
import com.google.gwt.dev.cfg.ConfigProps;
import com.google.gwt.dev.cfg.EntryMethodHolderGenerator;
import com.google.gwt.dev.cfg.LibraryGroup.CollidingCompilationUnitException;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.cfg.PermProps;
import com.google.gwt.dev.javac.CompilationProblemReporter;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.StandardGeneratorContext;
import com.google.gwt.dev.javac.typemodel.TypeOracle;
import com.google.gwt.dev.jdt.RebindPermutationOracle;
import com.google.gwt.dev.jjs.UnifiedAst.AST;
import com.google.gwt.dev.jjs.ast.Context;
import com.google.gwt.dev.jjs.ast.JBlock;
import com.google.gwt.dev.jjs.ast.JCastOperation;
import com.google.gwt.dev.jjs.ast.JClassLiteral;
import com.google.gwt.dev.jjs.ast.JClassType;
import com.google.gwt.dev.jjs.ast.JDeclaredType;
import com.google.gwt.dev.jjs.ast.JMethod;
import com.google.gwt.dev.jjs.ast.JMethodBody;
import com.google.gwt.dev.jjs.ast.JMethodCall;
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.AssertionNormalizer;
import com.google.gwt.dev.jjs.impl.AssertionRemover;
import com.google.gwt.dev.jjs.impl.AstDumper;
import com.google.gwt.dev.jjs.impl.CompileTimeConstantsReplacer;
import com.google.gwt.dev.jjs.impl.DeadCodeElimination;
import com.google.gwt.dev.jjs.impl.EnumOrdinalizer;
import com.google.gwt.dev.jjs.impl.Finalizer;
import com.google.gwt.dev.jjs.impl.FixAssignmentsToUnboxOrCast;
import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST;
import com.google.gwt.dev.jjs.impl.ImplementClassLiteralsAsFields;
import com.google.gwt.dev.jjs.impl.JavaToJavaScriptMap;
import com.google.gwt.dev.jjs.impl.JsAbstractTextTransformer;
import com.google.gwt.dev.jjs.impl.JsFunctionClusterer;
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.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.OptimizerStats;
import com.google.gwt.dev.jjs.impl.Pruner;
import com.google.gwt.dev.jjs.impl.RecordRebinds;
import com.google.gwt.dev.jjs.impl.ResolveRebinds;
import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.TypeMapper;
import com.google.gwt.dev.jjs.impl.SameParameterValueOptimizer;
import com.google.gwt.dev.jjs.impl.SourceInfoCorrelator;
import com.google.gwt.dev.jjs.impl.TypeRefDepsChecker;
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.CodeSplitters;
import com.google.gwt.dev.jjs.impl.codesplitter.MultipleDependencyGraphRecorder;
import com.google.gwt.dev.jjs.impl.codesplitter.ReplaceRunAsyncs;
import com.google.gwt.dev.jjs.impl.gflow.DataflowOptimizer;
import com.google.gwt.dev.js.BaselineCoverageGatherer;
import com.google.gwt.dev.js.ClosureJsRunner;
import com.google.gwt.dev.js.CoverageInstrumentor;
import com.google.gwt.dev.js.DuplicateExecuteOnceRemover;
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.JsDuplicateFunctionRemover;
import com.google.gwt.dev.js.JsInliner;
import com.google.gwt.dev.js.JsLiteralInterner;
import com.google.gwt.dev.js.JsNamer.IllegalNameException;
import com.google.gwt.dev.js.JsNamespaceChooser;
import com.google.gwt.dev.js.JsNamespaceOption;
import com.google.gwt.dev.js.JsNormalizer;
import com.google.gwt.dev.js.JsObfuscateNamer;
import com.google.gwt.dev.js.JsPersistentPrettyNamer;
import com.google.gwt.dev.js.JsPrettyNamer;
import com.google.gwt.dev.js.JsReportGenerationVisitor;
import com.google.gwt.dev.js.JsStackEmulator;
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.SizeBreakdown;
import com.google.gwt.dev.js.ast.JsContext;
import com.google.gwt.dev.js.ast.JsForIn;
import com.google.gwt.dev.js.ast.JsFunction;
import com.google.gwt.dev.js.ast.JsLabel;
import com.google.gwt.dev.js.ast.JsLiteral;
import com.google.gwt.dev.js.ast.JsName;
import com.google.gwt.dev.js.ast.JsNameOf;
import com.google.gwt.dev.js.ast.JsNameRef;
import com.google.gwt.dev.js.ast.JsNode;
import com.google.gwt.dev.js.ast.JsParameter;
import com.google.gwt.dev.js.ast.JsProgram;
import com.google.gwt.dev.js.ast.JsVars;
import com.google.gwt.dev.js.ast.JsVisitor;
import com.google.gwt.dev.util.DefaultTextOutput;
import com.google.gwt.dev.util.Empty;
import com.google.gwt.dev.util.Memory;
import com.google.gwt.dev.util.Name.SourceName;
import com.google.gwt.dev.util.Pair;
import com.google.gwt.dev.util.TinyCompileSummary;
import com.google.gwt.dev.util.Util;
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 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.Lists;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import com.google.gwt.thirdparty.guava.common.collect.Sets;

import org.xml.sax.SAXException;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.management.ManagementFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
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;

/**
 * A base for classes that compile Java JProgram representations into corresponding Js
 * source.
* * Work is split between a precompile() stage which is only called once and compilePerms() stage * which is called once per permutation. This allow build systems the option of distributing and * parallelizing some of the work. */ public abstract class JavaToJavaScriptCompiler { /** * Compile a permutation. */ protected abstract class PermutationCompiler { private Permutation permutation; public PermutationCompiler(Permutation permutation) { this.permutation = permutation; } /** * 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: * *
     * 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
     * 
* * 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.
* * 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 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); // (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 symbolTable = new TreeMap(new SymbolData.ClassIdentComparator()); // TODO(stalcup): hide metrics gathering in a callback or subclass if (compilerContext.shouldCompileMonolithic() && logger.isLoggable(TreeLogger.INFO)) { logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "..."); } printPermutationTrace(permutation); // (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 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). maybeRecordTypeReferences(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. maybeRecordTypeReferences(true); jprogram.typeOracle.recomputeAfterOptimizations(jprogram.getDeclaredTypes()); javaEvent.end(); Event javaScriptEvent = SpeedTracerLogger.start(CompilerEventType.PERMUTATION_JAVASCRIPT); // (5) Construct the Js AST Pair> 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.BY_JAVA_PACKAGE) { JsNamespaceChooser.exec(jsProgram, jjsmap); } // TODO(stalcup): move to normalization EvalFunctionsAtTopScope.exec(jsProgram, jjsmap); // (7) Optimize the JS AST. final Set 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 dependenciesAndRecorder = splitJsIntoFragments(props, permutationId, jjsmap); // TODO(stalcup): move to optimize. Map internedLiteralByVariableName = renameJsSymbols(props); // TODO(stalcup): move to normalization JsBreakUpLargeVarStatements.exec(jsProgram, props.getConfigProps()); // (8) Generate Js source List sourceInfoMaps = new ArrayList(); 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"); } } } private void maybeRecordTypeReferences(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); } } protected abstract void optimizeJs(Set inlinableJsFunctions) throws InterruptedException; protected abstract void optimizeJava() throws InterruptedException; protected abstract void postNormalizationOptimizeJava(); protected abstract Map runDetailedNamer(ConfigProps config) throws IllegalNameException; protected abstract Pair 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)); } } return compilationMetrics; } private void addSourceMapArtifacts(int permutationId, JavaToJavaScriptMap jjsmap, Pair dependenciesAndRecorder, boolean isSourceMapsEnabled, SizeBreakdown[] sizeBreakdowns, List 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. *

* 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 dependenciesAndRecorder, Map internedLiteralByVariableName, String[] js, SizeBreakdown[] sizeBreakdowns, List 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 dependenciesAndRecorder, Map internedLiteralByVariableName, boolean isSourceMapsEnabled, String[] jsFragments, SizeBreakdown[] sizeBreakdowns, List 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 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.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> makeSoycArtifacts(int permutationId, String[] js, SizeBreakdown[] sizeBreakdowns, List sourceInfoMaps, SyntheticArtifact dependencies, JavaToJavaScriptMap jjsmap, Map internedLiteralByVariableName, ModuleMetricsArtifact moduleMetricsArtifact, PrecompilationMetricsArtifact precompilationMetricsArtifact, CompilationMetricsArtifact compilationMetrics, boolean htmlReportsDisabled) throws IOException, UnableToCompleteException { Memory.maybeDumpMemory("makeSoycArtifactsStart"); List soycArtifacts = new ArrayList(); 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 symbolTable) { // Keep tracks of a list of referenced name. If it is not used, don't // add it to symbol map. final Set nameUsed = new HashSet(); final Map nameToFragment = new HashMap(); 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 result = new ArrayList(); for (Map.Entry 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.
* * These passes can not be reordering because of subtle interdependencies. */ protected abstract TypeMapper normalizeSemantics(); /** * Open an emitted artifact and gunzip its contents. */ private GZIPInputStream openWithGunzip(EmittedArtifact artifact) throws IOException, UnableToCompleteException { return new GZIPInputStream(artifact.getContents(TreeLogger.NULL)); } protected void optimizeJsLoop(Collection toInline) throws InterruptedException { int optimizationLevel = options.getOptimizationLevel(); List allOptimizerStats = new ArrayList(); 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); } printJsOptimizeTrace(allOptimizerStats); } private void printJsOptimizeTrace(List allOptimizerStats) { if (JProgram.isTracingEnabled()) { System.out.println(""); System.out.println(" Js Optimization Stats"); System.out.println(""); for (OptimizerStats stats : allOptimizerStats) { System.out.println(stats.prettyPrint()); } } } private void printPermutationTrace(Permutation permutation) { if (JProgram.isTracingEnabled()) { System.out.println("-------------------------------------------------------------"); System.out.println("| (new permutation) |"); System.out.println("-------------------------------------------------------------"); System.out.println("Properties: " + permutation.getProps().prettyPrint()); } } private Map renameJsSymbols(PermProps props) throws UnableToCompleteException { Map internedLiteralByVariableName; try { switch (options.getOutput()) { case OBFUSCATED: internedLiteralByVariableName = runObfuscateNamer(props); break; case PRETTY: internedLiteralByVariableName = runPrettyNamer(props.getConfigProps()); 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.of() : internedLiteralByVariableName; } private Map runObfuscateNamer(PermProps props) throws IllegalNameException { Map internedLiteralByVariableName = JsLiteralInterner.exec(jprogram, jsProgram, (byte) (JsLiteralInterner.INTERN_ALL & (byte) (jprogram.typeOracle.isInteropEnabled() ? ~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 runPrettyNamer(ConfigProps config) throws IllegalNameException { if (compilerContext.getOptions().isIncrementalCompileEnabled()) { JsPersistentPrettyNamer.exec(jsProgram, config, compilerContext.getMinimalRebuildCache().getPersistentPrettyNamerState()); return null; } // We don't intern strings in pretty mode to improve readability Map internedLiteralByVariableName = JsLiteralInterner.exec( jprogram, jsProgram, (byte) (JsLiteralInterner.INTERN_ALL & ~JsLiteralInterner.INTERN_STRINGS)); JsPrettyNamer.exec(jsProgram, config); return internedLiteralByVariableName; } } /** * Performs precompilation. */ protected abstract class Precompiler { protected RebindPermutationOracle rpo; protected String[] entryPointTypeNames; private static final String JS_EXPORT_ANN = "com.google.gwt.core.client.js.JsExport"; private static final String JS_TYPE_ANN = "com.google.gwt.core.client.js.JsType"; public Precompiler(RebindPermutationOracle rpo, String[] entryPointTypeNames) { this.rpo = rpo; this.entryPointTypeNames = entryPointTypeNames; } protected abstract void beforeUnifyAst(Set 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: * *

     * 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
     * 
* * 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.
* * 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); TypeRefDepsChecker.exec(logger, jprogram, module, options.warnMissingDeps(), options.getMissingDepsFile()); 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 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.
* * 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 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(String[] additionalRootTypes) throws UnableToCompleteException { Set allRootTypes = new TreeSet(); CompilationState compilationState = rpo.getCompilationState(); Memory.maybeDumpMemory("CompStateBuilt"); recordJsoTypes(compilationState.getTypeOracle()); populateRootTypes(allRootTypes, additionalRootTypes, compilationState.getTypeOracle()); String entryMethodHolderTypeName = buildEntryMethodHolder(rpo.getGeneratorContext(), allRootTypes); beforeUnifyAst(allRootTypes); unifyJavaAst(allRootTypes, entryMethodHolderTypeName); if (options.isSoycEnabled() || options.isJsonSoycEnabled()) { SourceInfoCorrelator.exec(jprogram); } // Gathers simple metrics that can highlight overly-large modules in an incremental compile. TinyCompileSummary tinyCompileSummary = compilerContext.getTinyCompileSummary(); tinyCompileSummary.setTypesForGeneratorsCount( rpo.getGeneratorContext().getTypeOracle().getTypes().length); tinyCompileSummary.setTypesForAstCount(jprogram.getDeclaredTypes().size()); tinyCompileSummary.setStaticSourceFilesCount(compilationState.getStaticSourceCount()); tinyCompileSummary.setGeneratedSourceFilesCount(compilationState.getGeneratedSourceCount()); tinyCompileSummary.setCachedStaticSourceFilesCount( compilationState.getCachedStaticSourceCount()); tinyCompileSummary.setCachedGeneratedSourceFilesCount( compilationState.getCachedGeneratedSourceCount()); // Free up memory. rpo.clear(); Set deletedTypeNames = options.isIncrementalCompileEnabled() ? getMinimalRebuildCache().computeDeletedTypeNames() : Sets. 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 classNames = new ArrayList(); @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 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 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()); } } } private void populateRootTypes(Set allRootTypes, String[] additionalRootTypes, TypeOracle typeOracle) { 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. */ for (com.google.gwt.core.ext.typeinfo.JClassType singleJsoIntf : typeOracle.getSingleJsoImplInterfaces()) { allRootTypes.add(typeOracle.getSingleJsoImpl(singleJsoIntf).getQualifiedSourceName()); } if (jprogram.typeOracle.isInteropEnabled()) { // find any types with @JsExport could be entry points as well nextType: for (com.google.gwt.dev.javac.typemodel.JClassType type : typeOracle.getTypes()) { for (Annotation ann : type.getAnnotations()) { // If the type immediately exports symbols to JS. if (ann.annotationType().getName().equals(JS_EXPORT_ANN)) { allRootTypes.add(type.getQualifiedSourceName()); continue nextType; } } if (isJsType(type)) { // If the type or any transitive interface is a JS type. allRootTypes.add(type.getQualifiedSourceName()); } } } } /** * Returns true if the type, or any super-interface has a JsType * annotation. */ private boolean isJsType(com.google.gwt.dev.javac.typemodel.JClassType type) { for (Annotation ann : type.getAnnotations()) { if (ann.annotationType().getName().equals(JS_TYPE_ANN)) { return true; } } for (com.google.gwt.dev.javac.typemodel.JClassType intf : type .getImplementedInterfaces()) { if (isJsType(intf)) { return true; } } return false; } private void recordJsoTypes(TypeOracle typeOracle) { if (!options.isIncrementalCompileEnabled()) { return; } // Add names of JSO subtypes. Set 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 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 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 allRootTypes, String entryMethodHolderTypeName) throws UnableToCompleteException { Event event = SpeedTracerLogger.start(CompilerEventType.UNIFY_AST); UnifyAst unifyAst; try { unifyAst = new UnifyAst(logger, compilerContext, jprogram, jsProgram, rpo); } catch (CollidingCompilationUnitException e) { logger.log(TreeLogger.ERROR, e.getMessage()); throw new UnableToCompleteException(); } // 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(); event.end(); } } private static class PermutationResultImpl implements PermutationResult { private final ArtifactSet artifacts = new ArtifactSet(); private final byte[][] js; private final String jsStrongName; private final Permutation permutation; private final byte[] serializedSymbolMap; private final StatementRanges[] statementRanges; public PermutationResultImpl(String[] jsFragments, Permutation permutation, SymbolData[] symbolMap, StatementRanges[] statementRanges) { byte[][] bytes = new byte[jsFragments.length][]; for (int i = 0; i < jsFragments.length; ++i) { bytes[i] = Util.getBytes(jsFragments[i]); } this.js = bytes; this.jsStrongName = Util.computeStrongName(bytes); this.permutation = permutation; try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); Util.writeObjectToStream(baos, (Object) symbolMap); this.serializedSymbolMap = baos.toByteArray(); } catch (IOException e) { throw new RuntimeException("Should never happen with in-memory stream", e); } this.statementRanges = statementRanges; } @Override public void addArtifacts(Collection> newArtifacts) { this.artifacts.addAll(newArtifacts); } @Override public ArtifactSet getArtifacts() { return artifacts; } @Override public byte[][] getJs() { return js; } @Override public String getJsStrongName() { return jsStrongName; } @Override public Permutation getPermutation() { return permutation; } @Override public byte[] getSerializedSymbolMap() { return serializedSymbolMap; } @Override public StatementRanges[] getStatementRanges() { 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 UnifiedAst precompile(RebindPermutationOracle rpo, String[] entryPointTypeNames, String[] additionalRootTypes, boolean singlePermutation) throws UnableToCompleteException { return precompile(rpo, entryPointTypeNames, additionalRootTypes, singlePermutation, null); } /** * 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 allOptimizerStats = new ArrayList(); 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; while (true) { passCount++; if (passCount > passLimit) { break; } if (Thread.interrupted()) { optimizeEvent.end(); throw new InterruptedException(); } AstDumper.maybeDumpAST(jprogram); OptimizerStats stats = optimizeJavaOneTime("Pass " + passCount, nodeCount); 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)); } printJavaOptimizeTrace(allOptimizerStats); 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) { 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); stats.add(Pruner.exec(jprogram, true).recordVisits(numNodes)); stats.add(Finalizer.exec(jprogram).recordVisits(numNodes)); stats.add(MakeCallsStatic.exec(jprogram, options.shouldAddRuntimeChecks()) .recordVisits(numNodes)); stats.add(TypeTightener.exec(jprogram).recordVisits(numNodes)); stats.add(MethodCallTightener.exec(jprogram).recordVisits(numNodes)); // Note: Specialization should be done before inlining. stats.add(MethodCallSpecializer.exec(jprogram).recordVisits(numNodes)); stats.add(DeadCodeElimination.exec(jprogram).recordVisits(numNodes)); stats.add(MethodInliner.exec(jprogram).recordVisits(numNodes)); if (options.shouldInlineLiteralParameters()) { stats.add(SameParameterValueOptimizer.exec(jprogram).recordVisits(numNodes)); } if (options.shouldOrdinalizeEnums()) { stats.add(EnumOrdinalizer.exec(jprogram).recordVisits(numNodes)); } optimizeEvent.end(); return stats; } private void printJavaOptimizeTrace(List allOptimizerStats) { if (JProgram.isTracingEnabled()) { System.out.println(""); System.out.println(" Java Optimization Stats"); System.out.println(""); for (OptimizerStats stats : allOptimizerStats) { System.out.println(stats.prettyPrint()); } } } private MinimalRebuildCache getMinimalRebuildCache() { return compilerContext.getMinimalRebuildCache(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy