| /* |
| * 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.BadPropertyValueException; |
| import com.google.gwt.core.ext.PropertyOracle; |
| import com.google.gwt.core.ext.SelectionProperty; |
| 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.Range; |
| 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; |
| import com.google.gwt.core.linker.SoycReportLinker; |
| import com.google.gwt.dev.Permutation; |
| import com.google.gwt.dev.cfg.ConfigurationProperty; |
| import com.google.gwt.dev.cfg.ModuleDef; |
| import com.google.gwt.dev.javac.CompilationProblemReporter; |
| import com.google.gwt.dev.javac.CompilationProblemReporter.SourceFetcher; |
| import com.google.gwt.dev.javac.typemodel.TypeOracle; |
| import com.google.gwt.dev.jdt.RebindPermutationOracle; |
| import com.google.gwt.dev.jdt.WebModeCompilerFrontEnd; |
| import com.google.gwt.dev.jjs.CorrelationFactory.DummyCorrelationFactory; |
| import com.google.gwt.dev.jjs.CorrelationFactory.RealCorrelationFactory; |
| import com.google.gwt.dev.jjs.UnifiedAst.AST; |
| import com.google.gwt.dev.jjs.ast.Context; |
| import com.google.gwt.dev.jjs.ast.JBinaryOperation; |
| import com.google.gwt.dev.jjs.ast.JBinaryOperator; |
| import com.google.gwt.dev.jjs.ast.JBlock; |
| import com.google.gwt.dev.jjs.ast.JClassType; |
| import com.google.gwt.dev.jjs.ast.JDeclaredType; |
| import com.google.gwt.dev.jjs.ast.JExpression; |
| import com.google.gwt.dev.jjs.ast.JGwtCreate; |
| 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.JNode; |
| import com.google.gwt.dev.jjs.ast.JProgram; |
| import com.google.gwt.dev.jjs.ast.JReboundEntryPoint; |
| import com.google.gwt.dev.jjs.ast.JStatement; |
| 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.BuildTypeMap; |
| import com.google.gwt.dev.jjs.impl.CastNormalizer; |
| import com.google.gwt.dev.jjs.impl.CatchBlockNormalizer; |
| import com.google.gwt.dev.jjs.impl.CodeSplitter; |
| import com.google.gwt.dev.jjs.impl.CodeSplitter.MultipleDependencyGraphRecorder; |
| import com.google.gwt.dev.jjs.impl.ControlFlowAnalyzer; |
| import com.google.gwt.dev.jjs.impl.DeadCodeElimination; |
| 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.FixAssignmentToUnbox; |
| import com.google.gwt.dev.jjs.impl.GenerateJavaAST; |
| import com.google.gwt.dev.jjs.impl.GenerateJavaScriptAST; |
| import com.google.gwt.dev.jjs.impl.GwtAstBuilder; |
| import com.google.gwt.dev.jjs.impl.HandleCrossFragmentReferences; |
| 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.JsIEBlockTextTransformer; |
| import com.google.gwt.dev.jjs.impl.JsoDevirtualizer; |
| 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.MethodCallTightener; |
| import com.google.gwt.dev.jjs.impl.MethodInliner; |
| 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.ReplaceRebinds; |
| import com.google.gwt.dev.jjs.impl.ReplaceRunAsyncs; |
| import com.google.gwt.dev.jjs.impl.ResolveRebinds; |
| import com.google.gwt.dev.jjs.impl.SameParameterValueOptimizer; |
| import com.google.gwt.dev.jjs.impl.SourceInfoCorrelator; |
| import com.google.gwt.dev.jjs.impl.TypeLinker; |
| import com.google.gwt.dev.jjs.impl.TypeMap; |
| import com.google.gwt.dev.jjs.impl.TypeTightener; |
| import com.google.gwt.dev.jjs.impl.UnifyAst; |
| import com.google.gwt.dev.jjs.impl.gflow.DataflowOptimizer; |
| import com.google.gwt.dev.js.EvalFunctionsAtTopScope; |
| import com.google.gwt.dev.js.JsBreakUpLargeVarStatements; |
| import com.google.gwt.dev.js.JsCoerceIntShift; |
| import com.google.gwt.dev.js.JsDuplicateCaseFolder; |
| import com.google.gwt.dev.js.JsDuplicateFunctionRemover; |
| import com.google.gwt.dev.js.JsIEBlockSizeVisitor; |
| import com.google.gwt.dev.js.JsInliner; |
| import com.google.gwt.dev.js.JsNormalizer; |
| import com.google.gwt.dev.js.JsObfuscateNamer; |
| import com.google.gwt.dev.js.JsPrettyNamer; |
| import com.google.gwt.dev.js.JsReportGenerationVisitor; |
| import com.google.gwt.dev.js.JsSourceGenerationVisitorWithSizeBreakdown; |
| import com.google.gwt.dev.js.JsStackEmulator; |
| import com.google.gwt.dev.js.JsStaticEval; |
| import com.google.gwt.dev.js.JsStringInterner; |
| 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.JsBlock; |
| import com.google.gwt.dev.js.ast.JsName; |
| import com.google.gwt.dev.js.ast.JsProgram; |
| 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.Util; |
| import com.google.gwt.dev.util.arg.OptionOptimize; |
| import com.google.gwt.dev.util.collect.Lists; |
| import com.google.gwt.dev.util.collect.Maps; |
| 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 org.eclipse.jdt.internal.compiler.CompilationResult; |
| import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
| import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; |
| import org.xml.sax.SAXException; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.lang.management.ManagementFactory; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| 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; |
| |
| /** |
| * Compiles the Java <code>JProgram</code> representation into its corresponding |
| * JavaScript source. |
| */ |
| public class JavaToJavaScriptCompiler { |
| |
| private static class PermutationResultImpl implements PermutationResult { |
| private final ArtifactSet artifacts = new ArtifactSet(); |
| private final byte[][] js; |
| private final Permutation permutation; |
| private final byte[] serializedSymbolMap; |
| private final StatementRanges[] statementRanges; |
| |
| public PermutationResultImpl(String[] js, Permutation permutation, SymbolData[] symbolMap, |
| StatementRanges[] statementRanges) { |
| byte[][] bytes = new byte[js.length][]; |
| for (int i = 0; i < js.length; ++i) { |
| bytes[i] = Util.getBytes(js[i]); |
| } |
| this.js = 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; |
| } |
| |
| public void addArtifacts(Collection<? extends Artifact<?>> newArtifacts) { |
| this.artifacts.addAll(newArtifacts); |
| } |
| |
| public ArtifactSet getArtifacts() { |
| return artifacts; |
| } |
| |
| public byte[][] getJs() { |
| return js; |
| } |
| |
| public Permutation getPermutation() { |
| return permutation; |
| } |
| |
| public byte[] getSerializedSymbolMap() { |
| return serializedSymbolMap; |
| } |
| |
| public StatementRanges[] getStatementRanges() { |
| return statementRanges; |
| } |
| } |
| |
| private static class TreeStatistics extends JVisitor { |
| private int nodeCount = 0; |
| |
| public int getNodeCount() { |
| return nodeCount; |
| } |
| |
| @Override |
| public boolean visit(JNode x, Context ctx) { |
| nodeCount++; |
| return true; |
| } |
| } |
| |
| private static final String ENUM_NAME_OBFUSCATION_PROPERTY = "compiler.enum.obfuscate.names"; |
| |
| /** |
| * Compiles a particular permutation, based on a precompiled unified AST. |
| * |
| * @param logger the logger to use |
| * @param unifiedAst the result of a |
| * {@link #precompile(TreeLogger, ModuleDef, RebindPermutationOracle, String[], String[], JJSOptions, boolean, PrecompilationMetricsArtifact)} |
| * @param permutation the permutation to compile |
| * @return the output JavaScript |
| * @throws UnableToCompleteException if an error other than |
| * {@link OutOfMemoryError} occurs |
| */ |
| public static PermutationResult compilePermutation(TreeLogger logger, UnifiedAst unifiedAst, |
| Permutation permutation) throws UnableToCompleteException { |
| JJSOptions options = unifiedAst.getOptions(); |
| long startTimeMilliseconds = System.currentTimeMillis(); |
| |
| Event jjsCompilePermutationEvent = |
| SpeedTracerLogger.start(CompilerEventType.JJS_COMPILE_PERMUTATION, "name", permutation |
| .prettyPrint()); |
| |
| InternalCompilerException.preload(); |
| PropertyOracle[] propertyOracles = permutation.getPropertyOracles(); |
| int permutationId = permutation.getId(); |
| if (logger.isLoggable(TreeLogger.INFO)) { |
| logger.log(TreeLogger.INFO, "Compiling permutation " + permutationId + "..."); |
| } |
| long permStart = System.currentTimeMillis(); |
| try { |
| if (JProgram.isTracingEnabled()) { |
| System.out.println("------------------------------------------------------------"); |
| System.out.println("| (new permuation) |"); |
| System.out.println("------------------------------------------------------------"); |
| System.out.println("Properties: " + permutation.prettyPrint()); |
| } |
| |
| AST ast = unifiedAst.getFreshAst(); |
| JProgram jprogram = ast.getJProgram(); |
| JsProgram jsProgram = ast.getJsProgram(); |
| |
| Map<StandardSymbolData, JsName> symbolTable = |
| new TreeMap<StandardSymbolData, JsName>(new SymbolData.ClassIdentComparator()); |
| |
| ResolveRebinds.exec(jprogram, permutation.getOrderedRebindAnswers()); |
| |
| // (4) Optimize the normalized Java AST for each permutation. |
| int optimizationLevel = options.getOptimizationLevel(); |
| if (optimizationLevel == OptionOptimize.OPTIMIZE_LEVEL_DRAFT) { |
| draftOptimize(jprogram); |
| } else { |
| optimize(options, jprogram); |
| } |
| |
| RemoveEmptySuperCalls.exec(jprogram); |
| |
| // (5) "Normalize" the high-level Java tree into a lower-level tree more |
| // suited for JavaScript code generation. Don't go reordering these |
| // willy-nilly because there are some subtle interdependencies. |
| JsoDevirtualizer.exec(jprogram); |
| CatchBlockNormalizer.exec(jprogram); |
| PostOptimizationCompoundAssignmentNormalizer.exec(jprogram); |
| LongCastNormalizer.exec(jprogram); |
| LongEmulationNormalizer.exec(jprogram); |
| CastNormalizer.exec(jprogram, options.isCastCheckingDisabled()); |
| ArrayNormalizer.exec(jprogram); |
| EqualityNormalizer.exec(jprogram); |
| |
| // (6) Perform further post-normalization optimizations |
| // Prune everything |
| Pruner.exec(jprogram, false); |
| |
| // (7) Generate a JavaScript code DOM from the Java type declarations |
| jprogram.typeOracle.recomputeAfterOptimizations(); |
| JavaToJavaScriptMap jjsmap = |
| GenerateJavaScriptAST.exec(jprogram, jsProgram, options.getOutput(), symbolTable, |
| propertyOracles); |
| |
| // (8) Normalize the JS AST. |
| // Fix invalid constructs created during JS AST gen. |
| JsNormalizer.exec(jsProgram); |
| // Resolve all unresolved JsNameRefs. |
| JsSymbolResolver.exec(jsProgram); |
| // Move all function definitions to a top-level scope, to reduce weirdness |
| EvalFunctionsAtTopScope.exec(jsProgram, jjsmap); |
| |
| // (9) Optimize the JS AST. |
| if (optimizationLevel > OptionOptimize.OPTIMIZE_LEVEL_DRAFT) { |
| optimizeJs(options, jsProgram); |
| |
| /* |
| * Coalesce redundant labels in switch statements. |
| */ |
| JsDuplicateCaseFolder.exec(jsProgram); |
| } |
| |
| /* |
| * Creates new variables, must run before code splitter and namer. |
| */ |
| JsStackEmulator.exec(jsProgram, propertyOracles); |
| |
| /* |
| * Work around Safari 5 bug by rewriting a >> b as ~~a >> b. |
| * |
| * No shifts may be generated after this point. |
| */ |
| JsCoerceIntShift.exec(jsProgram, logger, propertyOracles); |
| |
| // (10) Split up the program into fragments |
| SyntheticArtifact dependencies = null; |
| if (options.isRunAsyncEnabled()) { |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| CodeSplitter.exec(logger, jprogram, jsProgram, jjsmap, chooseDependencyRecorder(options |
| .isSoycEnabled(), baos)); |
| if (baos.size() == 0 && options.isSoycEnabled()) { |
| recordNonSplitDependencies(jprogram, baos); |
| } |
| if (baos.size() > 0) { |
| dependencies = |
| new SyntheticArtifact(SoycReportLinker.class, "dependencies" + permutationId |
| + ".xml.gz", baos.toByteArray()); |
| } |
| } |
| |
| // detect if browser is ie6 or not known |
| boolean isIE6orUnknown = false; |
| for (PropertyOracle oracle : propertyOracles) { |
| try { |
| SelectionProperty userAgentProperty = oracle.getSelectionProperty(logger, "user.agent"); |
| if ("ie6".equals(userAgentProperty.getCurrentValue())) { |
| isIE6orUnknown = true; |
| break; |
| } |
| } catch (BadPropertyValueException e) { |
| // user agent unknown; play it safe |
| isIE6orUnknown = true; |
| break; |
| } |
| } |
| |
| // (10.5) Obfuscate |
| Map<JsName, String> obfuscateMap = Maps.create(); |
| switch (options.getOutput()) { |
| case OBFUSCATED: |
| obfuscateMap = JsStringInterner.exec(jprogram, jsProgram, isIE6orUnknown); |
| JsObfuscateNamer.exec(jsProgram); |
| if (options.isAggressivelyOptimize()) { |
| if (JsStackEmulator.getStackMode(propertyOracles) == JsStackEmulator.StackMode.STRIP) { |
| boolean changed = false; |
| for (int i = 0; i < jsProgram.getFragmentCount(); i++) { |
| JsBlock fragment = jsProgram.getFragmentBlock(i); |
| changed = JsDuplicateFunctionRemover.exec(jsProgram, fragment) || changed; |
| } |
| if (changed) { |
| JsUnusedFunctionRemover.exec(jsProgram); |
| } |
| } |
| } |
| break; |
| case PRETTY: |
| // We don't intern strings in pretty mode to improve readability |
| JsPrettyNamer.exec(jsProgram); |
| break; |
| case DETAILED: |
| obfuscateMap = JsStringInterner.exec(jprogram, jsProgram, isIE6orUnknown); |
| JsVerboseNamer.exec(jsProgram); |
| break; |
| default: |
| throw new InternalCompilerException("Unknown output mode"); |
| } |
| |
| // (10.8) Handle cross-island references. |
| // No new JsNames or references to JSNames can be introduced after this |
| // point. |
| HandleCrossFragmentReferences.exec(logger, jsProgram, propertyOracles); |
| |
| // (11) Perform any post-obfuscation normalizations. |
| |
| // Work around an IE7 bug, |
| // http://code.google.com/p/google-web-toolkit/issues/detail?id=1440 |
| // note, JsIEBlockTextTransformer now handles restructuring top level |
| // blocks, this class now handles non-top level blocks only. |
| boolean splitBlocks = isIE6orUnknown; |
| |
| if (splitBlocks) { |
| JsIEBlockSizeVisitor.exec(jsProgram); |
| } |
| JsBreakUpLargeVarStatements.exec(jsProgram, propertyOracles); |
| |
| // (12) Generate the final output text. |
| String[] js = new String[jsProgram.getFragmentCount()]; |
| StatementRanges[] ranges = new StatementRanges[js.length]; |
| SizeBreakdown[] sizeBreakdowns = |
| options.isSoycEnabled() || options.isCompilerMetricsEnabled() |
| ? new SizeBreakdown[js.length] : null; |
| List<Map<Range, SourceInfo>> sourceInfoMaps = |
| options.isSoycExtra() ? new ArrayList<Map<Range, SourceInfo>>() : null; |
| generateJavaScriptCode(options, jsProgram, jjsmap, js, ranges, sizeBreakdowns, |
| sourceInfoMaps, splitBlocks); |
| |
| PermutationResult toReturn = |
| new PermutationResultImpl(js, permutation, makeSymbolMap(symbolTable), ranges); |
| CompilationMetricsArtifact compilationMetrics = null; |
| if (options.isCompilerMetricsEnabled()) { |
| compilationMetrics = new CompilationMetricsArtifact(permutation.getId()); |
| compilationMetrics.setCompileElapsedMilliseconds(System.currentTimeMillis() |
| - startTimeMilliseconds); |
| compilationMetrics.setElapsedMilliseconds(System.currentTimeMillis() |
| - ManagementFactory.getRuntimeMXBean().getStartTime()); |
| compilationMetrics.setJsSize(sizeBreakdowns); |
| compilationMetrics.setPermutationDescription(permutation.prettyPrint()); |
| toReturn.addArtifacts(Lists.create(unifiedAst.getModuleMetrics(), unifiedAst |
| .getPrecompilationMetrics(), compilationMetrics)); |
| } |
| toReturn.addArtifacts(makeSoycArtifacts(logger, permutationId, jprogram, js, sizeBreakdowns, |
| sourceInfoMaps, dependencies, jjsmap, obfuscateMap, unifiedAst.getModuleMetrics(), |
| unifiedAst.getPrecompilationMetrics(), compilationMetrics, options.isSoycHtmlDisabled())); |
| |
| logTrackingStats(logger); |
| if (logger.isLoggable(TreeLogger.TRACE)) { |
| logger.log(TreeLogger.TRACE, "Permutation took " + (System.currentTimeMillis() - permStart) |
| + " ms"); |
| } |
| return toReturn; |
| } catch (Throwable e) { |
| throw CompilationProblemReporter.logAndTranslateException(logger, e); |
| } finally { |
| jjsCompilePermutationEvent.end(); |
| } |
| } |
| |
| public static UnifiedAst precompile(TreeLogger logger, ModuleDef module, |
| RebindPermutationOracle rpo, String[] declEntryPts, String[] additionalRootTypes, |
| JJSOptions options, boolean singlePermutation) throws UnableToCompleteException { |
| return precompile(logger, module, rpo, declEntryPts, additionalRootTypes, options, |
| singlePermutation, null); |
| } |
| |
| /** |
| * Performs a precompilation, returning a unified AST. |
| * |
| * @param logger the logger to use |
| * @param module the module to compile |
| * @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 options the compiler options |
| * @param singlePermutation if true, do not pre-optimize the resulting AST or |
| * allow serialization of the result |
| * @param precompilationMetrics if not null, gather diagnostic information |
| * from this build for a report. |
| * @return the unified AST used to drive permutation compiles |
| * @throws UnableToCompleteException if an error other than |
| * {@link OutOfMemoryError} occurs |
| */ |
| public static UnifiedAst precompile(TreeLogger logger, ModuleDef module, |
| RebindPermutationOracle rpo, String[] declEntryPts, String[] additionalRootTypes, |
| JJSOptions options, boolean singlePermutation, |
| PrecompilationMetricsArtifact precompilationMetrics) throws UnableToCompleteException { |
| |
| InternalCompilerException.preload(); |
| |
| if (additionalRootTypes == null) { |
| additionalRootTypes = Empty.STRINGS; |
| } |
| if (declEntryPts.length + additionalRootTypes.length == 0) { |
| throw new IllegalArgumentException("entry point(s) required"); |
| } |
| |
| Set<String> allRootTypes = new TreeSet<String>(); |
| |
| // Find all the possible rebinds for declared entry point types. |
| for (String element : declEntryPts) { |
| String[] all = rpo.getAllPossibleRebindAnswers(logger, element); |
| Collections.addAll(allRootTypes, all); |
| } |
| rpo.getGeneratorContext().finish(logger); |
| Collections.addAll(allRootTypes, additionalRootTypes); |
| allRootTypes.addAll(JProgram.CODEGEN_TYPES_SET); |
| allRootTypes.addAll(JProgram.INDEX_TYPES_SET); |
| /* |
| * Add all SingleJsoImpl types that we know about. It's likely that the |
| * concrete types are never explicitly referenced. |
| */ |
| TypeOracle typeOracle = rpo.getCompilationState().getTypeOracle(); |
| for (com.google.gwt.core.ext.typeinfo.JClassType singleJsoIntf : typeOracle |
| .getSingleJsoImplInterfaces()) { |
| allRootTypes.add(typeOracle.getSingleJsoImpl(singleJsoIntf).getQualifiedSourceName()); |
| } |
| |
| Memory.maybeDumpMemory("CompStateBuilt"); |
| |
| CorrelationFactory correlator = |
| options.isSoycExtra() ? RealCorrelationFactory.INSTANCE : DummyCorrelationFactory.INSTANCE; |
| JProgram jprogram = new JProgram(correlator); |
| JsProgram jsProgram = new JsProgram(correlator); |
| |
| try { |
| if (GwtAstBuilder.ENABLED) { |
| UnifyAst unifyAst = new UnifyAst(jprogram, jsProgram, options, rpo); |
| unifyAst.addRootTypes(allRootTypes); |
| // TODO: errors. |
| // TODO: move this into UnifyAst. |
| findEntryPoints(logger, rpo, declEntryPts, jprogram); |
| unifyAst.exec(logger); |
| // TODO: errors. |
| |
| List<String> finalTypeOracleTypes = Lists.create(); |
| if (precompilationMetrics != null) { |
| for (com.google.gwt.core.ext.typeinfo.JClassType type : typeOracle.getTypes()) { |
| finalTypeOracleTypes = |
| Lists.add(finalTypeOracleTypes, type.getPackage().getName() + "." + type.getName()); |
| } |
| precompilationMetrics.setFinalTypeOracleTypes(finalTypeOracleTypes); |
| } |
| |
| // Free up memory. |
| rpo.clear(); |
| |
| if (options.isSoycEnabled()) { |
| SourceInfoCorrelator.exec(jprogram); |
| } |
| |
| // Compute all super type/sub type info |
| jprogram.typeOracle.computeBeforeAST(); |
| } else { |
| |
| // Compile the source and get the compiler so we can get the parse tree |
| CompilationUnitDeclaration[] goldenCuds = |
| WebModeCompilerFrontEnd.getCompilationUnitDeclarations(logger, allRootTypes |
| .toArray(new String[allRootTypes.size()]), rpo, TypeLinker.NULL_TYPE_LINKER).compiledUnits; |
| |
| List<String> finalTypeOracleTypes = Lists.create(); |
| if (precompilationMetrics != null) { |
| for (com.google.gwt.core.ext.typeinfo.JClassType type : typeOracle.getTypes()) { |
| finalTypeOracleTypes = |
| Lists.add(finalTypeOracleTypes, type.getPackage().getName() + "." + type.getName()); |
| } |
| precompilationMetrics.setFinalTypeOracleTypes(finalTypeOracleTypes); |
| } |
| |
| // Free up memory. |
| rpo.clear(); |
| Memory.maybeDumpMemory("GoldenCudsBuilt"); |
| |
| /* |
| * Check for compilation problems. We don't log here because any |
| * problems found here will have already been logged by |
| * AbstractCompiler. |
| */ |
| checkForErrors(logger, goldenCuds, false); |
| |
| /* |
| * (1) Build a flattened map of TypeDeclarations => JType. The resulting |
| * map contains entries for all reference types. BuildTypeMap also |
| * parses all JSNI. |
| */ |
| TypeMap typeMap = new TypeMap(jprogram); |
| TypeDeclaration[] allTypeDeclarations = BuildTypeMap.exec(typeMap, goldenCuds, jsProgram); |
| |
| // BuildTypeMap can uncover syntactic JSNI errors; report & abort |
| checkForErrors(logger, goldenCuds, true); |
| |
| // Compute all super type/sub type info |
| jprogram.typeOracle.computeBeforeAST(); |
| |
| // (2) Create our own Java AST from the JDT AST. |
| GenerateJavaAST.exec(allTypeDeclarations, typeMap, jprogram, options); |
| |
| // GenerateJavaAST can uncover semantic JSNI errors; report & abort |
| checkForErrors(logger, goldenCuds, true); |
| |
| Memory.maybeDumpMemory("AstBuilt"); |
| |
| // Allow GC |
| goldenCuds = null; |
| typeMap = null; |
| allTypeDeclarations = null; |
| } |
| |
| Memory.maybeDumpMemory("AstOnly"); |
| AstDumper.maybeDumpAST(jprogram); |
| |
| // See if we should run the EnumNameObfuscator |
| if (module != null) { |
| ConfigurationProperty enumNameObfuscationProp = |
| (ConfigurationProperty) module.getProperties().find(ENUM_NAME_OBFUSCATION_PROPERTY); |
| if (enumNameObfuscationProp != null |
| && Boolean.parseBoolean(enumNameObfuscationProp.getValue())) { |
| EnumNameObfuscator.exec(jprogram, logger); |
| } |
| } |
| |
| // (3) Perform Java AST normalizations. |
| FixAssignmentToUnbox.exec(jprogram); |
| |
| /* |
| * TODO: If we defer this until later, we could maybe use the results of |
| * the assertions to enable more optimizations. |
| */ |
| if (options.isEnableAssertions()) { |
| // Turn into assertion checking calls. |
| AssertionNormalizer.exec(jprogram); |
| } else { |
| // Remove all assert statements. |
| AssertionRemover.exec(jprogram); |
| } |
| |
| if (!GwtAstBuilder.ENABLED) { |
| // Replace GWT.create calls with JGwtCreate nodes. |
| ReplaceRebinds.exec(logger, jprogram, rpo); |
| } |
| |
| // Fix up GWT.runAsync() |
| if (module != null && options.isRunAsyncEnabled()) { |
| ReplaceRunAsyncs.exec(logger, jprogram); |
| CodeSplitter.pickInitialLoadSequence(logger, jprogram, module.getProperties()); |
| } |
| |
| if (!GwtAstBuilder.ENABLED) { |
| // Resolve entry points, rebinding non-static entry points. |
| findEntryPoints(logger, rpo, declEntryPts, jprogram); |
| } |
| |
| ImplementClassLiteralsAsFields.exec(jprogram); |
| |
| /* |
| * 4) Possibly optimize some. |
| * |
| * Don't optimize early if this is a draft compile, or if there's only one |
| * permutation. |
| */ |
| 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. |
| */ |
| optimize(options, jprogram); |
| } 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. |
| */ |
| optimizeLoop("Early Optimization", jprogram, false); |
| } |
| } |
| |
| Set<String> rebindRequests = new HashSet<String>(); |
| RecordRebinds.exec(jprogram, rebindRequests); |
| |
| if (options.isCompilerMetricsEnabled()) { |
| precompilationMetrics.setAstTypes(getReferencedJavaClasses(jprogram)); |
| } |
| |
| logTrackingStats(logger); |
| |
| Event createUnifiedAstEvent = SpeedTracerLogger.start(CompilerEventType.CREATE_UNIFIED_AST); |
| UnifiedAst result = |
| new UnifiedAst(options, new AST(jprogram, jsProgram), singlePermutation, rebindRequests); |
| createUnifiedAstEvent.end(); |
| return result; |
| } catch (Throwable e) { |
| throw CompilationProblemReporter.logAndTranslateException(logger, e); |
| } finally { |
| } |
| } |
| |
| /** |
| * Perform the minimal amount of optimization to make sure the compile |
| * succeeds. |
| */ |
| protected static void draftOptimize(JProgram jprogram) { |
| Event draftOptimizeEvent = SpeedTracerLogger.start(CompilerEventType.DRAFT_OPTIMIZE); |
| Finalizer.exec(jprogram); |
| MakeCallsStatic.exec(jprogram); |
| jprogram.typeOracle.recomputeAfterOptimizations(); |
| DeadCodeElimination.exec(jprogram); |
| draftOptimizeEvent.end(); |
| } |
| |
| protected static void optimize(JJSOptions options, JProgram jprogram) throws InterruptedException { |
| Event optimizeEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE); |
| |
| List<OptimizerStats> allOptimizerStats = new ArrayList<OptimizerStats>(); |
| int counter = 0; |
| int optimizationLevel = options.getOptimizationLevel(); |
| while (true) { |
| counter++; |
| if (optimizationLevel < OptionOptimize.OPTIMIZE_LEVEL_MAX && counter > optimizationLevel) { |
| break; |
| } |
| if (Thread.interrupted()) { |
| optimizeEvent.end(); |
| throw new InterruptedException(); |
| } |
| AstDumper.maybeDumpAST(jprogram); |
| OptimizerStats stats = |
| optimizeLoop("Pass " + counter, jprogram, options.isAggressivelyOptimize()); |
| allOptimizerStats.add(stats); |
| if (!stats.didChange()) { |
| break; |
| } |
| } |
| |
| if (options.isAggressivelyOptimize()) { |
| // Just run it once, because it is very time consuming |
| allOptimizerStats.add(DataflowOptimizer.exec(jprogram)); |
| } |
| |
| if (JProgram.isTracingEnabled()) { |
| System.out.println(""); |
| System.out.println(" Java Optimization Stats"); |
| System.out.println(""); |
| for (OptimizerStats stats : allOptimizerStats) { |
| System.out.println(stats.prettyPrint()); |
| } |
| } |
| |
| optimizeEvent.end(); |
| } |
| |
| protected static void optimizeJs(JJSOptions options, JsProgram jsProgram) |
| throws InterruptedException { |
| 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, possible |
| stats.add(JsStaticEval.exec(jsProgram)); |
| // Inline JavaScript function invocations |
| stats.add(JsInliner.exec(jsProgram)); |
| // Remove unused functions, possible |
| stats.add(JsUnusedFunctionRemover.exec(jsProgram)); |
| |
| // Save the stats to print out after optimizers finish. |
| allOptimizerStats.add(stats); |
| |
| optimizeJsEvent.end(); |
| int optimizationLevel = options.getOptimizationLevel(); |
| if ((optimizationLevel < OptionOptimize.OPTIMIZE_LEVEL_MAX && counter > optimizationLevel) |
| || !stats.didChange()) { |
| break; |
| } |
| } |
| |
| if (JProgram.isTracingEnabled()) { |
| System.out.println(""); |
| System.out.println(" JavaScript Optimization Stats"); |
| System.out.println(""); |
| for (OptimizerStats stats : allOptimizerStats) { |
| System.out.println(stats.prettyPrint()); |
| } |
| } |
| } |
| |
| protected static OptimizerStats optimizeLoop(String passName, JProgram jprogram, |
| boolean isAggressivelyOptimize) { |
| Event optimizeEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE, "phase", "loop"); |
| |
| // Count the number of nodes in the AST so we can measure the efficiency of |
| // the optimizers. |
| Event countEvent = SpeedTracerLogger.start(CompilerEventType.OPTIMIZE, "phase", "countNodes"); |
| TreeStatistics treeStats = new TreeStatistics(); |
| treeStats.accept(jprogram); |
| int numNodes = treeStats.getNodeCount(); |
| countEvent.end(); |
| |
| // Recompute clinits each time, they can become empty. |
| jprogram.typeOracle.recomputeAfterOptimizations(); |
| // jprogram.methodOracle = |
| // MethodOracleBuilder.buildMethodOracle(jprogram); |
| OptimizerStats stats = new OptimizerStats(passName); |
| |
| // Remove unreferenced types, fields, methods, [params, locals] |
| stats.add(Pruner.exec(jprogram, true).recordVisits(numNodes)); |
| |
| // finalize locals, params, fields, methods, classes |
| stats.add(Finalizer.exec(jprogram).recordVisits(numNodes)); |
| |
| // rewrite non-polymorphic calls as static calls; update all call sites |
| stats.add(MakeCallsStatic.exec(jprogram).recordVisits(numNodes)); |
| |
| // type flow tightening |
| // - fields, locals based on assignment |
| // - params based on assignment and call sites |
| // - method bodies based on return statements |
| // - polymorphic methods based on return types of all implementors |
| // - optimize casts and instance of |
| stats.add(TypeTightener.exec(jprogram).recordVisits(numNodes)); |
| |
| // tighten method call bindings |
| stats.add(MethodCallTightener.exec(jprogram).recordVisits(numNodes)); |
| |
| // dead code removal?? |
| stats.add(DeadCodeElimination.exec(jprogram).recordVisits(numNodes)); |
| |
| // inlining |
| stats.add(MethodInliner.exec(jprogram).recordVisits(numNodes)); |
| |
| if (isAggressivelyOptimize) { |
| // remove same parameters value |
| stats.add(SameParameterValueOptimizer.exec(jprogram).recordVisits(numNodes)); |
| |
| /* |
| * Enum ordinalization. |
| * |
| * TODO(jbrosenberg): graduate this out of the 'isAggressivelyOptimize' |
| * block, over time. |
| */ |
| stats.add(EnumOrdinalizer.exec(jprogram).recordVisits(numNodes)); |
| } |
| |
| // prove that any types that have been culled from the main tree are |
| // unreferenced due to type tightening? |
| |
| optimizeEvent.end(); |
| return stats; |
| } |
| |
| /** |
| * Look through the list of compiled units for errors and log them to the |
| * console. |
| * |
| * @param logger logger to use for compilation errors |
| * @param cuds compiled units to analyze for errors. |
| * @param itemizeErrors log each error or simply log one message if the build |
| * failed. |
| * @throws UnableToCompleteException if a compilation error is found in the |
| * cuds argument. |
| */ |
| static void checkForErrors(TreeLogger logger, CompilationUnitDeclaration[] cuds, |
| boolean itemizeErrors) throws UnableToCompleteException { |
| Event checkForErrorsEvent = SpeedTracerLogger.start(CompilerEventType.CHECK_FOR_ERRORS); |
| boolean compilationFailed = false; |
| if (cuds.length == 0) { |
| compilationFailed = true; |
| } |
| for (CompilationUnitDeclaration cud : cuds) { |
| final CompilationResult result = cud.compilationResult(); |
| if (result.hasErrors()) { |
| compilationFailed = true; |
| // Early out if we don't need to itemize. |
| if (!itemizeErrors) { |
| break; |
| } |
| String typeName = new String(cud.getMainTypeName()); |
| CompilationProblemReporter.reportErrors(logger, result.getErrors(), new String(cud |
| .getFileName()), true, new SourceFetcher() { |
| public String getSource() { |
| return new String(result.getCompilationUnit().getContents()); |
| } |
| }, typeName, false); |
| } |
| } |
| checkForErrorsEvent.end(); |
| if (compilationFailed) { |
| logger.log(TreeLogger.ERROR, "Cannot proceed due to previous errors", null); |
| throw new UnableToCompleteException(); |
| } |
| } |
| |
| private static MultipleDependencyGraphRecorder chooseDependencyRecorder(boolean soycEnabled, |
| OutputStream out) { |
| MultipleDependencyGraphRecorder dependencyRecorder = CodeSplitter.NULL_RECORDER; |
| if (soycEnabled) { |
| dependencyRecorder = new DependencyRecorder(out); |
| } |
| return dependencyRecorder; |
| } |
| |
| private static JMethodCall createReboundModuleLoad(TreeLogger logger, SourceInfo info, |
| JDeclaredType reboundEntryType, String originalMainClassName, JDeclaredType enclosingType) |
| throws UnableToCompleteException { |
| if (!(reboundEntryType instanceof JClassType)) { |
| logger.log(TreeLogger.ERROR, "Module entry point class '" + originalMainClassName |
| + "' must be a class", null); |
| throw new UnableToCompleteException(); |
| } |
| |
| JClassType entryClass = (JClassType) reboundEntryType; |
| if (entryClass.isAbstract()) { |
| logger.log(TreeLogger.ERROR, "Module entry point class '" + originalMainClassName |
| + "' must not be abstract", null); |
| throw new UnableToCompleteException(); |
| } |
| |
| JMethod entryMethod = findMainMethodRecurse(entryClass); |
| if (entryMethod == null) { |
| logger.log(TreeLogger.ERROR, |
| "Could not find entry method 'onModuleLoad()' method in entry point class '" |
| + originalMainClassName + "'", null); |
| throw new UnableToCompleteException(); |
| } |
| |
| if (entryMethod.isAbstract()) { |
| logger.log(TreeLogger.ERROR, "Entry method 'onModuleLoad' in entry point class '" |
| + originalMainClassName + "' must not be abstract", null); |
| throw new UnableToCompleteException(); |
| } |
| |
| JExpression qualifier = null; |
| if (!entryMethod.isStatic()) { |
| qualifier = JGwtCreate.createInstantiationExpression(info, entryClass, enclosingType); |
| |
| if (qualifier == null) { |
| logger.log(TreeLogger.ERROR, |
| "No default (zero argument) constructor could be found in entry point class '" |
| + originalMainClassName |
| + "' to qualify a call to non-static entry method 'onModuleLoad'", null); |
| throw new UnableToCompleteException(); |
| } |
| } |
| return new JMethodCall(info, qualifier, entryMethod); |
| } |
| |
| private static void findEntryPoints(TreeLogger logger, RebindPermutationOracle rpo, |
| String[] mainClassNames, JProgram program) throws UnableToCompleteException { |
| Event findEntryPointsEvent = SpeedTracerLogger.start(CompilerEventType.FIND_ENTRY_POINTS); |
| JMethod bootStrapMethod = program.getIndexedMethod("EntryMethodHolder.init"); |
| |
| JMethodBody body = (JMethodBody) bootStrapMethod.getBody(); |
| JBlock block = body.getBlock(); |
| SourceInfo info = block.getSourceInfo().makeChild(); |
| |
| // Also remember $entry, which we'll handle specially in GenerateJsAst |
| JMethod registerEntry = program.getIndexedMethod("Impl.registerEntry"); |
| program.addEntryMethod(registerEntry); |
| |
| for (String mainClassName : mainClassNames) { |
| block.addStmt(makeStatsCalls(info, program, mainClassName)); |
| JDeclaredType mainType = program.getFromTypeMap(mainClassName); |
| |
| if (mainType == null) { |
| logger.log(TreeLogger.ERROR, "Could not find module entry point class '" + mainClassName |
| + "'", null); |
| throw new UnableToCompleteException(); |
| } |
| |
| JMethod mainMethod = findMainMethod(mainType); |
| if (mainMethod != null && mainMethod.isStatic()) { |
| JMethodCall onModuleLoadCall = new JMethodCall(info, null, mainMethod); |
| block.addStmt(onModuleLoadCall.makeStatement()); |
| continue; |
| } |
| |
| // Couldn't find a static main method; must rebind the class |
| String[] resultTypeNames = rpo.getAllPossibleRebindAnswers(logger, mainClassName); |
| List<JClassType> resultTypes = new ArrayList<JClassType>(); |
| List<JExpression> entryCalls = new ArrayList<JExpression>(); |
| for (String resultTypeName : resultTypeNames) { |
| JDeclaredType resultType = program.getFromTypeMap(resultTypeName); |
| if (resultType == null) { |
| logger.log(TreeLogger.ERROR, "Could not find module entry point class '" + resultTypeName |
| + "' after rebinding from '" + mainClassName + "'", null); |
| throw new UnableToCompleteException(); |
| } |
| |
| JMethodCall onModuleLoadCall = |
| createReboundModuleLoad(logger, info, resultType, mainClassName, bootStrapMethod |
| .getEnclosingType()); |
| resultTypes.add((JClassType) resultType); |
| entryCalls.add(onModuleLoadCall); |
| } |
| if (resultTypes.size() == 1) { |
| block.addStmt(entryCalls.get(0).makeStatement()); |
| } else { |
| JReboundEntryPoint reboundEntryPoint = |
| new JReboundEntryPoint(info, mainType, resultTypes, entryCalls); |
| block.addStmt(reboundEntryPoint); |
| } |
| } |
| program.addEntryMethod(bootStrapMethod); |
| findEntryPointsEvent.end(); |
| } |
| |
| private static JMethod findMainMethod(JDeclaredType declaredType) { |
| for (JMethod method : declaredType.getMethods()) { |
| if (method.getName().equals("onModuleLoad")) { |
| if (method.getParams().size() == 0) { |
| return method; |
| } |
| } |
| } |
| return null; |
| } |
| |
| private static JMethod findMainMethodRecurse(JDeclaredType declaredType) { |
| for (JDeclaredType it = declaredType; it != null; it = it.getSuperClass()) { |
| JMethod result = findMainMethod(it); |
| if (result != null) { |
| return result; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Generate JavaScript code from the given JavaScript ASTs. Also produces |
| * information about that transformation. |
| * |
| * @param options The options this compiler instance is running with |
| * @param jsProgram The AST to convert to source code |
| * @param jjsMap A map between the JavaScript AST and the Java AST it came |
| * from |
| * @param js An array to hold the output JavaScript |
| * @param ranges An array to hold the statement ranges for that JavaScript |
| * @param sizeBreakdowns An array to hold the size breakdowns for that |
| * JavaScript |
| * @param sourceInfoMaps An array to hold the source info maps for that |
| * JavaScript |
| * @param splitBlocks true if current permutation is for IE6 or unknown |
| */ |
| private static void generateJavaScriptCode(JJSOptions options, JsProgram jsProgram, |
| JavaToJavaScriptMap jjsMap, String[] js, StatementRanges[] ranges, |
| SizeBreakdown[] sizeBreakdowns, List<Map<Range, SourceInfo>> sourceInfoMaps, |
| boolean splitBlocks) { |
| for (int i = 0; i < js.length; i++) { |
| DefaultTextOutput out = new DefaultTextOutput(options.getOutput().shouldMinimize()); |
| JsSourceGenerationVisitorWithSizeBreakdown v; |
| |
| if (sourceInfoMaps != null) { |
| v = new JsReportGenerationVisitor(out, jjsMap); |
| } else { |
| v = new JsSourceGenerationVisitorWithSizeBreakdown(out, jjsMap); |
| } |
| v.accept(jsProgram.getFragmentBlock(i)); |
| |
| StatementRanges statementRanges = v.getStatementRanges(); |
| String code = out.toString(); |
| Map<Range, SourceInfo> infoMap = (sourceInfoMaps != null) ? v.getSourceInfoMap() : null; |
| |
| JsAbstractTextTransformer transformer = |
| new JsAbstractTextTransformer(code, statementRanges, infoMap) { |
| @Override |
| public void exec() { |
| } |
| |
| @Override |
| protected void updateSourceInfoMap() { |
| } |
| }; |
| |
| /** |
| * 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); |
| // only cluster for obfuscated mode |
| if (options.isAggressivelyOptimize() && options.getOutput() == JsOutputOption.OBFUSCATED) { |
| transformer = new JsFunctionClusterer(transformer); |
| transformer.exec(); |
| } |
| functionClusterEvent.end(); |
| |
| // rewrite top-level blocks to limit the number of statements |
| if (splitBlocks) { |
| transformer = new JsIEBlockTextTransformer(transformer); |
| transformer.exec(); |
| } |
| |
| js[i] = transformer.getJs(); |
| ranges[i] = transformer.getStatementRanges(); |
| if (sizeBreakdowns != null) { |
| sizeBreakdowns[i] = v.getSizeBreakdown(); |
| } |
| if (sourceInfoMaps != null) { |
| sourceInfoMaps.add(transformer.getSourceInfoMap()); |
| } |
| } |
| } |
| |
| /** |
| * This method can be used to fetch the list of referenced classs. |
| * |
| * This method is intended to support compiler metrics in the precompile |
| * phase. |
| */ |
| private static String[] getReferencedJavaClasses(JProgram jprogram) { |
| 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()]); |
| } |
| |
| /* |
| * 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 static void logTrackingStats(TreeLogger logger) { |
| EnumOrdinalizer.Tracker eot = EnumOrdinalizer.getTracker(); |
| if (eot != null) { |
| eot.logResultsDetailed(logger, TreeLogger.WARN); |
| } |
| } |
| |
| private static Collection<? extends Artifact<?>> makeSoycArtifacts(TreeLogger logger, |
| int permutationId, JProgram jprogram, String[] js, SizeBreakdown[] sizeBreakdowns, |
| List<Map<Range, SourceInfo>> sourceInfoMaps, SyntheticArtifact dependencies, |
| JavaToJavaScriptMap jjsmap, Map<JsName, String> obfuscateMap, |
| 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, obfuscateMap); |
| 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.generateCompilerMetricsForOnePermuation(moduleMetricsArtifact, |
| precompilationMetricsArtifact, compilationMetrics); |
| } |
| soycArtifacts.addAll(outDir.getArtifacts()); |
| generateCompileReport.end(); |
| } |
| |
| soycEvent.end(); |
| |
| return soycArtifacts; |
| } |
| |
| /** |
| * Create a variable assignment to invoke a call to the statistics collector. |
| * |
| * <pre> |
| * Stats.isStatsAvailable() && |
| * Stats.onModuleStart("mainClassName"); |
| * </pre> |
| */ |
| private static JStatement makeStatsCalls(SourceInfo info, JProgram program, String mainClassName) { |
| JMethod isStatsAvailableMethod = program.getIndexedMethod("Stats.isStatsAvailable"); |
| JMethod onModuleStartMethod = program.getIndexedMethod("Stats.onModuleStart"); |
| |
| JMethodCall availableCall = new JMethodCall(info, null, isStatsAvailableMethod); |
| JMethodCall onModuleStartCall = new JMethodCall(info, null, onModuleStartMethod); |
| onModuleStartCall.addArg(program.getLiteralString(info, mainClassName)); |
| |
| JBinaryOperation amp = |
| new JBinaryOperation(info, program.getTypePrimitiveBoolean(), JBinaryOperator.AND, |
| availableCall, onModuleStartCall); |
| |
| return amp.makeStatement(); |
| } |
| |
| private static SymbolData[] makeSymbolMap(Map<StandardSymbolData, JsName> symbolTable) { |
| |
| SymbolData[] result = new SymbolData[symbolTable.size()]; |
| int i = 0; |
| for (Map.Entry<StandardSymbolData, JsName> entry : symbolTable.entrySet()) { |
| StandardSymbolData symbolData = entry.getKey(); |
| symbolData.setSymbolName(entry.getValue().getShortIdent()); |
| result[i++] = symbolData; |
| } |
| return result; |
| } |
| |
| /** |
| * Open an emitted artifact and gunzip its contents. |
| */ |
| private static GZIPInputStream openWithGunzip(EmittedArtifact artifact) throws IOException, |
| UnableToCompleteException { |
| return new GZIPInputStream(artifact.getContents(TreeLogger.NULL)); |
| } |
| |
| /** |
| * 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 static void recordNonSplitDependencies(JProgram program, OutputStream out) { |
| DependencyRecorder deps = new DependencyRecorder(out); |
| deps.open(); |
| deps.startDependencyGraph("initial", null); |
| |
| ControlFlowAnalyzer cfa = new ControlFlowAnalyzer(program); |
| cfa.setDependencyRecorder(deps); |
| cfa.traverseEntryMethods(); |
| deps.endDependencyGraph(); |
| deps.close(); |
| } |
| } |