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);
// prune all Object.getClass() overrides and replace with inline field ref
ReplaceGetClassOverrides.exec(jprogram);
// (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(jprogram, jsProgram, propertyOracles, jjsmap);
/*
* 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();
int fragmentsMerge = options.getFragmentsMerge();
// Pick and choose which code splitter to use. Only use the experimental
// one when the user explicitly decides the project needs fragment
// merging.
if (fragmentsMerge > 0) {
CodeSplitter2.exec(logger, jprogram, jsProgram, jjsmap, fragmentsMerge,
chooseDependencyRecorder(options.isSoycEnabled(), baos));
} else {
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 = findBooleanProperty(propertyOracles, logger, "user.agent", "ie6",
true, false, true);
boolean isSourceMapsEnabled = findBooleanProperty(propertyOracles, logger,
"compiler.useSourceMaps", "true", true, false, false);
// (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 imprmakeSouove 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 = new ArrayList<Map<Range, SourceInfo>>();
generateJavaScriptCode(options, jprogram, jsProgram, jjsmap, js, ranges,
sizeBreakdowns, sourceInfoMaps, splitBlocks, isSourceMapsEnabled);
PermutationResult toReturn =
new PermutationResultImpl(js, permutation, makeSymbolMap(symbolTable, jsProgram), ranges);
CompilationMetricsArtifact compilationMetrics = null;
// TODO: enable this when ClosureCompiler is enabled
if (!options.isClosureCompilerEnabled() && 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));
}
// TODO: enable this when ClosureCompiler is enabled
if (!options.isClosureCompilerEnabled()) {
toReturn.addArtifacts(makeSoycArtifacts(logger, permutationId, jprogram, js, sizeBreakdowns,
options.isSoycExtra() ? sourceInfoMaps : null, dependencies, jjsmap, obfuscateMap,
unifiedAst.getModuleMetrics(), unifiedAst.getPrecompilationMetrics(), compilationMetrics,
options.isSoycHtmlDisabled()));
}
// TODO: enable this when ClosureCompiler is enabled
if (!options.isClosureCompilerEnabled() && isSourceMapsEnabled) {
logger.log(TreeLogger.INFO, "Source Maps Enabled");
toReturn.addArtifacts(SourceMapRecorder.makeSourceMapArtifacts(sourceInfoMaps,
permutationId));
}
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();
}
}