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

com.google.javascript.jscomp.AbstractCompiler Maven / Gradle / Ivy

There is a newer version: 9.0.8
Show newest version
/*
 * Copyright 2009 The Closure Compiler Authors.
 *
 * 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.javascript.jscomp;

import static com.google.common.base.Preconditions.checkState;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.errorprone.annotations.MustBeClosed;
import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper;
import com.google.javascript.jscomp.ExpressionDecomposer.Workaround;
import com.google.javascript.jscomp.colors.ColorRegistry;
import com.google.javascript.jscomp.deps.ModuleLoader;
import com.google.javascript.jscomp.diagnostic.LogFile;
import com.google.javascript.jscomp.modules.ModuleMap;
import com.google.javascript.jscomp.modules.ModuleMetadataMap;
import com.google.javascript.jscomp.parsing.Config;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.trees.Comment;
import com.google.javascript.jscomp.serialization.ColorPool;
import com.google.javascript.jscomp.type.ReverseAbstractInterpreter;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.StaticScope;
import com.google.javascript.rhino.Token;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import java.io.Serializable;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;

/**
 * An abstract compiler, to help remove the circular dependency of passes on JSCompiler.
 *
 * 

This is an abstract class, so that we can make the methods package-private. */ public abstract class AbstractCompiler implements SourceExcerptProvider, CompilerInputProvider { static final DiagnosticType READ_ERROR = DiagnosticType.error("JSC_READ_ERROR", "Cannot read file {0}: {1}"); private int currentPassIndex = -1; /** Will be called before each pass runs. */ @OverridingMethodsMustInvokeSuper void beforePass(String passName) { this.currentPassIndex++; } /** Will be called after each pass finishes. */ @OverridingMethodsMustInvokeSuper void afterPass(String passName) {} private LifeCycleStage stage = LifeCycleStage.RAW; // TODO(nicksantos): Decide if all of these are really necessary. // Many of them are just accessors that should be passed to the // CompilerPass's constructor. abstract java.util.function.Supplier getTypedAstDeserializer(SourceFile file); /** Looks up an input (possibly an externs input) by input id. May return null. */ @Override public abstract CompilerInput getInput(InputId inputId); /** Looks up a source file by name. May return null. */ @Nullable abstract SourceFile getSourceFileByName(String sourceName); @Nullable public abstract Node getScriptNode(String filename); /** Gets the module graph. */ @Nullable abstract JSChunkGraph getModuleGraph(); /** * Gets the inputs in the order in which they are being processed. Only for use by {@code * AbstractCompilerRunner}. */ abstract Iterable getInputsInOrder(); /** * Gets the total number of inputs. * *

This can be useful as a guide for the initial allocated size for data structures. */ abstract int getNumberOfInputs(); // // Intermediate state and results produced and needed by particular passes. // TODO(rluble): move these into the general structure for keeping state between pass runs. // /** Adds exported names to keep track. */ public abstract void addExportedNames(Set exportedVariableNames); /** Gets the names that have been exported. */ public abstract Set getExportedNames(); /** Sets the variable renaming map */ public abstract void setVariableMap(VariableMap variableMap); /** Sets the property renaming map */ public abstract void setPropertyMap(VariableMap propertyMap); /** Sets the string replacement map */ public abstract void setStringMap(VariableMap stringMap); /** Sets the css names found during compilation. */ public abstract void setCssNames(LinkedHashMap newCssNames); /** Sets the mapping for instrumentation parameter encoding. */ public abstract void setInstrumentationMapping(VariableMap instrumentationMapping); /** Sets the id generator for cross-module motion. */ public abstract void setIdGeneratorMap(String serializedIdMappings); /** Gets the id generator for cross-module motion. */ public abstract IdGenerator getCrossModuleIdGenerator(); /** Sets the naming map for anonymous functions */ public abstract void setAnonymousFunctionNameMap(VariableMap functionMap); // // End of intermediate state needed by passes. // /** Sets whether the typechecking passes have run. */ abstract void setTypeCheckingHasRun(boolean hasRun); /** Returns whether the typechecking passes have run */ public abstract boolean hasTypeCheckingRun(); /** Whether the AST has been annotated with optimization colors. */ public abstract boolean hasOptimizationColors(); /** * Returns `true` when type checking has run, but the type registry has been cleared. * *

See also `clearJSTypeRegistry()`. */ public abstract boolean isTypeRegistryCleared(); /** Gets a central registry of type information from the compiled JS. */ public abstract JSTypeRegistry getTypeRegistry(); public abstract void clearJSTypeRegistry(); /** Gets a central registry of colors from deserialized JS types. */ public abstract ColorRegistry getColorRegistry(); /** Sets the color registry */ public abstract void setColorRegistry(ColorRegistry registry); abstract void forwardDeclareType(String typeName); /** Gets a memoized scope creator with type information. */ abstract ScopeCreator getTypedScopeCreator(); abstract void clearTypedScopeCreator(); /** Gets the top scope. */ public abstract TypedScope getTopScope(); /** Sets the top scope. */ abstract void setTopScope(TypedScope x); /** * Returns a scope containing only externs and synthetic code or other code in the first script. * *

Intended for transpilation passes to look up types when synthesizing new code. */ abstract StaticScope getTranspilationNamespace(); /** Report an error or warning. */ public abstract void report(JSError error); /** Report an internal error. */ abstract void throwInternalError(String msg, Throwable cause); /** Gets the current coding convention. */ public abstract CodingConvention getCodingConvention(); /** * Passes that make modifications in a scope that is different than the Compiler.currentScope use * this (eg, InlineVariables and many others) */ public abstract void reportChangeToEnclosingScope(Node n); /** * Mark modifications in a scope that is different than the Compiler.currentScope use this (eg, * InlineVariables and many others) */ public abstract void reportChangeToChangeScope(Node changeScopeRoot); /** * Mark a specific function node as known to be deleted. Is part of having accurate change * tracking which is necessary to streamline optimizations. */ abstract void reportFunctionDeleted(Node node); /** Sets the CssRenamingMap. */ abstract void setCssRenamingMap(CssRenamingMap map); /** Gets the CssRenamingMap. */ abstract CssRenamingMap getCssRenamingMap(); /** * Gets a suitable SCRIPT node to serve as a parent for code insertion. If {@code module} contains * any inputs, the returned node will be the SCRIPT node corresponding to its first input. If * {@code module} is empty, on the other hand, then the returned node will be the first SCRIPT * node in a non-empty module that {@code module} depends on (the deepest one possible). * * @param module A module. If null, will return the first SCRIPT node of all modules. * @return A SCRIPT node (never null). */ abstract Node getNodeForCodeInsertion(@Nullable JSChunk module); abstract TypeValidator getTypeValidator(); /** Gets the central registry of type violations. */ public abstract Iterable getTypeMismatches(); abstract void setExternExports(String externExports); /** Parses code for injecting, and associate it with a given source file. */ public abstract Node parseSyntheticCode(String filename, String code); /** Parses code for testing. */ @VisibleForTesting abstract Node parseTestCode(String code); /** Parses code for testing. */ @VisibleForTesting abstract Node parseTestCode(ImmutableList code); /** Prints a node to source code. */ public abstract String toSource(); /** Prints a node to source code. */ public abstract String toSource(Node root); /** Gets a default error reporter for injecting into Rhino. */ abstract ErrorReporter getDefaultErrorReporter(); /** Get an interpreter for type analysis. */ public abstract ReverseAbstractInterpreter getReverseAbstractInterpreter(); /** Returns the current life-cycle stage of the AST we're working on. */ public LifeCycleStage getLifeCycleStage() { return stage; } private static final String FILL_FILE_SUFFIX = "$fillFile"; /** Empty modules get an empty "fill" file, so that we can move code into an empty module. */ static String createFillFileName(String moduleName) { return moduleName + FILL_FILE_SUFFIX; } /** Returns whether a file name was created by {@link createFillFileName}. */ public static boolean isFillFileName(String fileName) { return fileName.endsWith(FILL_FILE_SUFFIX); } /** * Deserialize runtime libraries from a TypedAST packaged as a JAR resource and reconcile their * Colors with the current inputs. * *

This method must be called anywhere that Colors are reconciled for application to the AST. * Otherwise Color information won't be consistent. `colorPoolBuilder` must be the same builder as * used for the other inputs, and the caller retains ownership. * * @param colorPoolBuilder if present, includes inferred optimization colors on the deserialized * ASTs. If absent, does not include colors. */ public void initRuntimeLibraryTypedAsts(Optional colorPoolBuilder) { throw new UnsupportedOperationException( "Implementation in Compiler.java is not J2CL compatible."); } /** * Generates unique String Ids when requested via a compiler instance. * *

This supplier provides Ids that are deterministic and unique across all input files given to * the compiler. The generated ID format is: uniqueId = "fileHashCode$counterForThisFile" */ abstract UniqueIdSupplier getUniqueIdSupplier(); /** * Generates unique ids. * * @deprecated because the generated names during transpilation are not unique across all input * files. Use the new supplier by calling {@code getUniqueIdSupplier()}. */ @Deprecated abstract Supplier getUniqueNameIdSupplier(); /** * @return Whether any errors have been encountered that should stop the compilation process. */ abstract boolean hasHaltingErrors(); /** Register a listener for code change events. */ abstract void addChangeHandler(CodeChangeHandler handler); /** Remove a listener for code change events. */ abstract void removeChangeHandler(CodeChangeHandler handler); /** Register a provider for some type of index. */ abstract void addIndexProvider(IndexProvider indexProvider); /** * Returns, from a provider, the desired index of type T, otherwise null if no provider is * registered for the given type. */ abstract T getIndex(Class type); /** A monotonically increasing value to identify a change */ abstract int getChangeStamp(); /** * An accumulation of changed scope nodes since the last time the given pass was run. A returned * empty list means no scope nodes have changed since the last run and a returned null means this * is the first time the pass has run. */ abstract List getChangedScopeNodesForPass(String passName); /** * An accumulation of deleted scope nodes since the last time the given pass was run. A returned * null or empty list means no scope nodes have been deleted since the last run or this is the * first time the pass has run. */ abstract List getDeletedScopeNodesForPass(String passName); /** Called to indicate that the current change stamp has been used */ abstract void incrementChangeStamp(); /** Returns the root of the source tree, ignoring externs */ abstract Node getJsRoot(); /** True iff a function changed since the last time a pass was run */ abstract boolean hasScopeChanged(Node n); /** * Represents the different contexts for which the compiler could have distinct configurations. */ static enum ConfigContext { /** Normal JavaScript. */ DEFAULT, /** Externs files. */ EXTERNS } /** Returns the parser configuration for the specified context. */ abstract Config getParserConfig(ConfigContext context); /** Gets the error manager. */ public abstract ErrorManager getErrorManager(); /** Set the current life-cycle state. */ void setLifeCycleStage(LifeCycleStage stage) { this.stage = stage; } /** * Are the nodes equal for the purpose of inlining? If type aware optimizations are on, type * equality is checked. */ abstract boolean areNodesEqualForInlining(Node n1, Node n2); /** * Set if RegExp global properties are used. * * @param references Whether there are references to the RegExp global object properties. */ abstract void setHasRegExpGlobalReferences(boolean references); /** * @return Whether the AST contains references to the RegExp global object properties. */ abstract boolean hasRegExpGlobalReferences(); /** * @return The error level the given error object will be reported at. */ abstract CheckLevel getErrorLevel(JSError error); /** What point in optimizations we're in. For use by compiler passes */ public static enum LifeCycleStage implements Serializable { RAW, // See constraints put on the AST by either running the ConvertTypesToColors.java pass /or/ by // having created the AST via serialization/TypedAstDeserializer.java // NORMALIZED implies this constraint COLORS_AND_SIMPLIFIED_JSDOC, // See constraints put on the tree by Normalize.java NORMALIZED, // The normalize pass has put constraints on the tree, // but variables and properties have been renamed so // coding conventions no longer apply. NORMALIZED_OBFUSCATED; public boolean isNormalized() { return this == NORMALIZED || this == NORMALIZED_OBFUSCATED; } public boolean isNormalizedUnobfuscated() { return this == NORMALIZED; } public boolean isNormalizedObfuscated() { return this == NORMALIZED_OBFUSCATED; } public boolean hasColorAndSimplifiedJSDoc() { return this != RAW; } } /** * Runs a given compiler-pass by calling its {@code process()} method. * * @param pass The pass to be run. */ abstract void process(CompilerPass pass); /** Returns the root node of the AST, which includes both externs and source. */ public abstract Node getRoot(); abstract CompilerOptions getOptions(); /** * The set of features defined by the input language mode that have not (yet) been transpiled * away. * *

This is **not** the exact set of all features currently in the AST, but rather an improper * super set. This set starts out as the set of features from the language input mode specified by * the options, which is verified to be a super set of what appears in the input code (if the * input code contains a feature outside the language input mode it is an error). As the compiler * transpiles code any features that are transpiled away from the AST are removed from this set. * *

The feature set is computed this way, rather than using the detected features in the AST, so * that the set of passes that run is determined purely by input flags. Otherwise, introducing a * previously unused feature in any of the transitive deps of compilation target could effect the * build behaviour. */ abstract FeatureSet getFeatureSet(); abstract void setFeatureSet(FeatureSet fs); /** * @return a CompilerInput that can be modified to add additional extern definitions to the * beginning of the externs AST */ abstract CompilerInput getSynthesizedExternsInput(); /** Gets the last pass name set by setProgress. */ abstract String getLastPassName(); static final String RUNTIME_LIB_DIR = "src/com/google/javascript/jscomp/js/"; /** * The subdir js/ contains libraries of code that we inject at compile-time only if requested by * this function. * *

Notice that these libraries will almost always create global symbols. * * @param resourceName The name of the library. For example, if "base" is is specified, then we * load js/base.js * @param force Inject the library even if compiler options say not to. * @return The last node of the most-recently-injected runtime library. If new code was injected, * this will be the last expression node of the library. If the caller needs to add additional * code, they should add it as the next sibling of this node. If no runtime libraries have * been injected, then null is returned. */ abstract Node ensureLibraryInjected(String resourceName, boolean force); /** * Sets the names of the properties defined in externs. * * @param externProperties The set of property names defined in externs. */ abstract void setExternProperties(ImmutableSet externProperties); /** * Gets the names of the properties defined in externs or null if GatherExternProperties pass was * not run yet. */ public abstract ImmutableSet getExternProperties(); /** * Adds a {@link SourceMapInput} for the given {@code sourceFileName}, to be used for error * reporting and source map combining. */ public abstract void addInputSourceMap(String name, SourceMapInput sourceMap); abstract void addComments(String filename, List comments); /** * Returns a summary an entry for every property name found in the AST with a getter and / or * setter defined. * *

Property names for which there are no getters or setters will not be in the map. */ abstract AccessorSummary getAccessorSummary(); /** Sets the summary of properties with getters and setters. */ public abstract void setAccessorSummary(AccessorSummary summary); /** Returns all the comments from the given file. */ abstract List getComments(String filename); /** Gets the module loader. */ abstract ModuleLoader getModuleLoader(); /** Lookup the type of a module from its name. */ abstract CompilerInput.ModuleType getModuleTypeByName(String moduleName); /** Set whether J2CL passes should run */ abstract void setRunJ2clPasses(boolean runJ2clPasses); /** Whether J2CL passes should run */ abstract boolean runJ2clPasses(); /** * Returns a new AstFactory that will add type information to the nodes it creates if and only if * type checking has already happened and types have not been converted into colors. * *

Note that the AstFactory will /not/ add colors to the AST if types have been converted into * colors. The AstFactory does not understand colors, although color support could certainly be * added if it proves useful. */ public final AstFactory createAstFactory() { return hasTypeCheckingRun() ? (hasOptimizationColors() ? AstFactory.createFactoryWithColors(getColorRegistry()) : AstFactory.createFactoryWithTypes(getTypeRegistry())) : AstFactory.createFactoryWithoutTypes(); } /** * Returns a new AstFactory that will not add type information, regardless of whether type * checking has already happened. */ public final AstFactory createAstFactoryWithoutTypes() { return AstFactory.createFactoryWithoutTypes(); } /** * Returns a new AstAnalyzer configured correctly to answer questions about Nodes in the AST * currently being compiled. */ public AstAnalyzer getAstAnalyzer() { return new AstAnalyzer(this, getOptions().getAssumeGettersArePure()); } public ExpressionDecomposer createDefaultExpressionDecomposer() { return createExpressionDecomposer( this.getUniqueNameIdSupplier(), ImmutableSet.of(), Scope.createGlobalScope(new Node(Token.SCRIPT))); } public ExpressionDecomposer createExpressionDecomposer( Supplier uniqueNameIdSupplier, ImmutableSet knownConstantFunctions, Scope scope) { // If the output is ES5, then it may end up running on IE11, so enable a workaround // for one of its bugs. final EnumSet enabledWorkarounds = FeatureSet.ES5.contains(getOptions().getOutputFeatureSet()) ? EnumSet.of(Workaround.BROKEN_IE11_LOCATION_ASSIGN) : EnumSet.noneOf(Workaround.class); return new ExpressionDecomposer( this, uniqueNameIdSupplier, knownConstantFunctions, scope, enabledWorkarounds); } public abstract ModuleMetadataMap getModuleMetadataMap(); public abstract void setModuleMetadataMap(ModuleMetadataMap moduleMetadataMap); public abstract ModuleMap getModuleMap(); public abstract void setModuleMap(ModuleMap moduleMap); public final boolean isDebugLoggingEnabled() { return this.getOptions().getDebugLogDirectory() != null; } public final List getDebugLogFilterList() { if (this.getOptions().getDebugLogFilter() == null) { return new ArrayList<>(); } return Splitter.on(',').omitEmptyStrings().splitToList(this.getOptions().getDebugLogFilter()); } /** Provides logging access to a file with the specified name. */ @MustBeClosed public final LogFile createOrReopenLog( Class owner, String firstNamePart, String... restNameParts) { if (!this.isDebugLoggingEnabled()) { return LogFile.createNoOp(); } Path dir = getOptions().getDebugLogDirectory(); Path relativeParts = Paths.get(firstNamePart, restNameParts); Path file = dir.resolve(owner.getSimpleName()).resolve(relativeParts); // If a filter list for log file names was provided, only create a log file if any // of the filter strings matches. List filters = getDebugLogFilterList(); if (filters.isEmpty()) { return LogFile.createOrReopen(file); } for (String filter : filters) { if (file.toString().contains(filter)) { return LogFile.createOrReopen(file); } } return LogFile.createNoOp(); } /** * Provides logging access to a file with the specified name, differentiated by the index of the * current pass. * *

Indexing helps in separating logs from different pass loops. The filename pattern is * "[debug_log_directory]/[owner_name]/([name_part[i]]/){0,n-1}[pass_index]_[name_part[n]]". */ @MustBeClosed public final LogFile createOrReopenIndexedLog( Class owner, String firstNamePart, String... restNameParts) { checkState(this.currentPassIndex >= 0, this.currentPassIndex); String index = Strings.padStart(Integer.toString(this.currentPassIndex), 3, '0'); int length = restNameParts.length; if (length == 0) { firstNamePart = index + "_" + firstNamePart; } else { restNameParts[length - 1] = index + "_" + restNameParts[length - 1]; } return this.createOrReopenLog(owner, firstNamePart, restNameParts); } /** Returns the InputId of the synthetic code input (even if it is not initialized yet). */ abstract InputId getSyntheticCodeInputId(); /** * Adds a synthetic script to the front of the AST * *

Useful to allow inserting code into the global scope, earlier than any of the user-provided * code, in the case that the first user-provided input is a module. */ abstract void initializeSyntheticCodeInput(); /** Removes the script added by {@link #initializeSyntheticCodeInput} */ abstract void removeSyntheticCodeInput(); /** * Merges all code in the script added by {@link #initializeSyntheticCodeInput} into the first * non-synthetic script. Will crash if the first non-synthetic script is a module and module * rewriting has not occurred. */ abstract void mergeSyntheticCodeInput(); }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy