in compiler-jx/src/main/java/com/google/javascript/jscomp/RoyaleClosurePassConfig.java [528:924]
protected List<PassFactory> getOptimizations() {
List<PassFactory> passes = new ArrayList<PassFactory>();
if (options.skipNonTranspilationPasses) {
return passes;
}
passes.add(removeWeakSources);
passes.add(garbageCollectChecks);
// i18n
// If you want to customize the compiler to use a different i18n pass,
// you can create a PassConfig that calls replacePassFactory
// to replace this.
if (options.replaceMessagesWithChromeI18n) {
passes.add(replaceMessagesForChrome);
} else if (options.messageBundle != null) {
passes.add(replaceMessages);
}
// Defines in code always need to be processed.
passes.add(processDefines);
if (options.getTweakProcessing().shouldStrip()
|| !options.stripTypes.isEmpty()
|| !options.stripNameSuffixes.isEmpty()
|| !options.stripTypePrefixes.isEmpty()
|| !options.stripNamePrefixes.isEmpty()) {
passes.add(stripCode);
}
passes.add(normalize);
// Create extern exports after the normalize because externExports depends on unique names.
if (options.isExternExportsEnabled() || options.externExportsPath != null) {
passes.add(externExports);
}
// Gather property names in externs so they can be queried by the
// optimizing passes.
passes.add(gatherExternProperties);
// Should be run before runtimeTypeCheck and instrumentForCoverage as they rewrite code that
// this pass expects to see.
if (options.j2clPassMode.shouldAddJ2clPasses()) {
passes.add(j2clPass);
passes.add(j2clUtilGetDefineRewriterPass);
}
if (options.instrumentForCoverage) {
passes.add(instrumentForCodeCoverage);
}
if (options.runtimeTypeCheck) {
passes.add(runtimeTypeCheck);
}
passes.add(createEmptyPass(PassNames.BEFORE_STANDARD_OPTIMIZATIONS));
if (options.replaceIdGenerators) {
passes.add(replaceIdGenerators);
}
// Optimizes references to the arguments variable.
if (options.optimizeArgumentsArray) {
passes.add(optimizeArgumentsArray);
}
// Abstract method removal works best on minimally modified code, and also
// only needs to run once.
if (options.closurePass && (options.removeAbstractMethods || options.removeClosureAsserts)) {
passes.add(closureCodeRemoval);
}
if (options.removeJ2clAsserts) {
passes.add(j2clAssertRemovalPass);
}
// Property disambiguation should only run once and needs to be done
// soon after type checking, both so that it can make use of type
// information and so that other passes can take advantage of the renamed
// properties.
if (options.disambiguatePrivateProperties) {
passes.add(disambiguatePrivateProperties);
}
assertAllOneTimePasses(passes);
// Inline aliases so that following optimizations don't have to understand alias chains.
if (options.shouldCollapseProperties()) {
passes.add(aggressiveInlineAliases);
}
// Inline getters/setters in J2CL classes so that Object.defineProperties() calls (resulting
// from desugaring) don't block class stripping.
if (options.j2clPassMode.shouldAddJ2clPasses() && options.shouldCollapseProperties()) {
// Relies on collapseProperties-triggered aggressive alias inlining.
passes.add(j2clPropertyInlinerPass);
}
// Collapsing properties can undo constant inlining, so we do this before
// the main optimization loop.
if (options.shouldCollapseProperties()) {
passes.add(collapseProperties);
}
if (options.inferConsts) {
passes.add(inferConsts);
}
// TODO(mlourenco): Ideally this would be in getChecks() instead of getOptimizations(). But
// for that it needs to understand constant properties as well. See b/31301233#10.
// Needs to happen after inferConsts and collapseProperties. Detects whether invocations of
// the method goog.string.Const.from are done with an argument which is a string literal.
passes.add(checkConstParams);
// Running RemoveUnusedCode before disambiguate properties allows disambiguate properties to be
// more effective if code that would prevent disambiguation can be removed.
// TODO(b/66971163): Rename options since we're not actually using smartNameRemoval here now.
if (options.extraSmartNameRemoval && options.smartNameRemoval) {
// These passes remove code that is dead because of define flags.
// If the dead code is weakly typed, running these passes before property
// disambiguation results in more code removal.
// The passes are one-time on purpose. (The later runs are loopable.)
if (options.foldConstants && (options.inlineVariables || options.inlineLocalVariables)) {
passes.add(earlyInlineVariables);
passes.add(earlyPeepholeOptimizations);
}
passes.add(removeUnusedCodeOnce);
}
// RewritePolyfills is overly generous in the polyfills it adds. After type
// checking and early smart name removal, we can use the new type information
// to figure out which polyfilled prototype methods are actually called, and
// which were "false alarms" (i.e. calling a method of the same name on a
// user-provided class). We also remove any polyfills added by code that
// was smart-name-removed. This is a one-time pass, since it does not work
// after inlining - we do not attempt to aggressively remove polyfills used
// by code that is only flow-sensitively dead.
if (options.rewritePolyfills) {
passes.add(removeUnusedPolyfills);
}
// Property disambiguation should only run once and needs to be done
// soon after type checking, both so that it can make use of type
// information and so that other passes can take advantage of the renamed
// properties.
if (options.shouldDisambiguateProperties() && options.isTypecheckingEnabled()) {
passes.add(disambiguateProperties);
}
if (options.computeFunctionSideEffects) {
passes.add(markPureFunctions);
} else if (options.markNoSideEffectCalls) {
// TODO(user) The properties that this pass adds to CALL and NEW
// AST nodes increase the AST's in-memory size. Given that we are
// already running close to our memory limits, we could run into
// trouble if we end up using the @nosideeffects annotation a lot
// or compute @nosideeffects annotations by looking at function
// bodies. It should be easy to propagate @nosideeffects
// annotations as part of passes that depend on this property and
// store the result outside the AST (which would allow garbage
// collection once the pass is done).
passes.add(markNoSideEffectCalls);
}
if (options.smartNameRemoval) {
passes.addAll(getCodeRemovingPasses());
// TODO(b/66971163): Remove this early loop or rename the option that enables it
// to something more appropriate.
}
// This needs to come after the inline constants pass, which is run within
// the code removing passes.
if (options.closurePass) {
passes.add(closureOptimizePrimitives);
}
// ReplaceStrings runs after CollapseProperties in order to simplify
// pulling in values of constants defined in enums structures. It also runs
// after disambiguate properties and smart name removal so that it can
// correctly identify logging types and can replace references to string
// expressions.
if (!options.replaceStringsFunctionDescriptions.isEmpty()) {
passes.add(replaceStrings);
}
// TODO(user): This forces a first crack at crossChunkCodeMotion
// before devirtualization. Once certain functions are devirtualized,
// it confuses crossChunkCodeMotion ability to recognized that
// it is recursive.
// TODO(user): This is meant for a temporary quick win.
// In the future, we might want to improve our analysis in
// CrossChunkCodeMotion so we don't need to do this.
if (options.shouldRunCrossChunkCodeMotion()) {
passes.add(crossModuleCodeMotion);
}
// Method devirtualization benefits from property disambiguation so
// it should run after that pass but before passes that do
// optimizations based on global names (like cross module code motion
// and inline functions). Smart Name Removal does better if run before
// this pass.
if (options.devirtualizePrototypeMethods) {
passes.add(devirtualizePrototypeMethods);
}
if (options.customPasses != null) {
passes.add(getCustomPasses(
CustomPassExecutionTime.BEFORE_OPTIMIZATION_LOOP));
}
passes.add(createEmptyPass(PassNames.BEFORE_MAIN_OPTIMIZATIONS));
// Because FlowSensitiveInlineVariables does not operate on the global scope due to compilation
// time, we need to run it once before InlineFunctions so that we don't miss inlining
// opportunities when a function will be inlined into the global scope.
if (options.inlineVariables || options.inlineLocalVariables) {
passes.add(flowSensitiveInlineVariables);
}
passes.addAll(getMainOptimizationLoop());
passes.add(createEmptyPass(PassNames.AFTER_MAIN_OPTIMIZATIONS));
passes.add(createEmptyPass("beforeModuleMotion"));
if (options.shouldRunCrossChunkCodeMotion()) {
passes.add(crossModuleCodeMotion);
}
if (options.shouldRunCrossChunkMethodMotion()) {
passes.add(crossModuleMethodMotion);
}
passes.add(createEmptyPass("afterModuleMotion"));
// Some optimizations belong outside the loop because running them more
// than once would either have no benefit or be incorrect.
if (options.customPasses != null) {
passes.add(getCustomPasses(
CustomPassExecutionTime.AFTER_OPTIMIZATION_LOOP));
}
if (options.inlineVariables || options.inlineLocalVariables) {
passes.add(flowSensitiveInlineVariables);
// After inlining some of the variable uses, some variables are unused.
// Re-run remove unused vars to clean it up.
if (shouldRunRemoveUnusedCode()) {
passes.add(removeUnusedCodeOnce);
}
}
if (options.collapseAnonymousFunctions) {
passes.add(collapseAnonymousFunctions);
}
// Move functions before extracting prototype member declarations.
if (options.moveFunctionDeclarations
// renamePrefixNamescape relies on moveFunctionDeclarations
// to preserve semantics.
|| options.renamePrefixNamespace != null) {
passes.add(moveFunctionDeclarations);
}
if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.MAPPED) {
passes.add(nameMappedAnonymousFunctions);
}
// The mapped name anonymous function pass makes use of information that
// the extract prototype member declarations pass removes so the former
// happens before the latter.
if (options.extractPrototypeMemberDeclarations != ExtractPrototypeMemberDeclarationsMode.OFF) {
passes.add(extractPrototypeMemberDeclarations);
}
if (options.shouldAmbiguateProperties()
&& options.propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED
&& options.isTypecheckingEnabled()) {
passes.add(ambiguateProperties);
}
if (options.propertyRenaming == PropertyRenamingPolicy.ALL_UNQUOTED) {
passes.add(renameProperties);
}
// Reserve global names added to the "windows" object.
if (options.reserveRawExports) {
passes.add(gatherRawExports);
}
// This comes after property renaming because quoted property names must
// not be renamed.
if (options.convertToDottedProperties) {
passes.add(convertToDottedProperties);
}
// Property renaming must happen before this pass runs since this
// pass may convert dotted properties into quoted properties. It
// is beneficial to run before alias strings, alias keywords and
// variable renaming.
if (options.rewriteFunctionExpressions) {
passes.add(rewriteFunctionExpressions);
}
// This comes after converting quoted property accesses to dotted property
// accesses in order to avoid aliasing property names.
if (!options.aliasableStrings.isEmpty() || options.aliasAllStrings) {
passes.add(aliasStrings);
}
if (options.coalesceVariableNames) {
// Passes after this point can no longer depend on normalized AST
// assumptions because the code is marked as un-normalized
passes.add(coalesceVariableNames);
// coalesceVariables creates identity assignments and more redundant code
// that can be removed, rerun the peephole optimizations to clean them
// up.
if (options.foldConstants) {
passes.add(peepholeOptimizationsOnce);
}
}
// Passes after this point can no longer depend on normalized AST assumptions.
passes.add(markUnnormalized);
if (options.collapseVariableDeclarations) {
passes.add(exploitAssign);
passes.add(collapseVariableDeclarations);
}
// This pass works best after collapseVariableDeclarations.
passes.add(denormalize);
if (options.instrumentationTemplate != null) {
passes.add(instrumentFunctions);
}
if (options.variableRenaming != VariableRenamingPolicy.ALL) {
// If we're leaving some (or all) variables with their old names,
// then we need to undo any of the markers we added for distinguishing
// local variables ("x" -> "x$jscomp$1").
passes.add(invertContextualRenaming);
}
if (options.variableRenaming != VariableRenamingPolicy.OFF) {
passes.add(renameVars);
}
// This pass should run after names stop changing.
if (options.processObjectPropertyString) {
passes.add(objectPropertyStringPostprocess);
}
if (options.labelRenaming) {
passes.add(renameLabels);
}
if (options.foldConstants) {
passes.add(latePeepholeOptimizations);
}
if (options.anonymousFunctionNaming == AnonymousFunctionNamingPolicy.UNMAPPED) {
passes.add(nameUnmappedAnonymousFunctions);
}
if (protectHiddenSideEffects) {
passes.add(stripSideEffectProtection);
}
if (options.renamePrefixNamespace != null) {
if (!GLOBAL_SYMBOL_NAMESPACE_PATTERN.matcher(
options.renamePrefixNamespace).matches()) {
throw new IllegalArgumentException(
"Illegal character in renamePrefixNamespace name: "
+ options.renamePrefixNamespace);
}
passes.add(rescopeGlobalSymbols);
}
// Safety checks
passes.add(checkAstValidity);
passes.add(varCheckValidity);
// Raise to ES6, if allowed
if (options.getOutputFeatureSet().contains(ES6)) {
passes.add(optimizeToEs6);
}
assertValidOrderForOptimizations(passes);
return passes;
}