blob: eea03414547a3707b7f71bfb03afaf6dbbbd3d2f [file] [log] [blame]
/*
* Copyright 2014 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;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.linker.ArtifactSet;
import com.google.gwt.core.ext.linker.StatementRanges;
import com.google.gwt.dev.cfg.ModuleDef;
import com.google.gwt.dev.javac.CompilationUnit;
import com.google.gwt.dev.javac.CompiledClass;
import com.google.gwt.dev.javac.GeneratedUnit;
import com.google.gwt.dev.javac.Shared;
import com.google.gwt.dev.jjs.JsSourceMap;
import com.google.gwt.dev.jjs.ast.JProgram;
import com.google.gwt.dev.jjs.ast.JTypeOracle;
import com.google.gwt.dev.jjs.ast.JTypeOracle.ImmediateTypeRelations;
import com.google.gwt.dev.jjs.impl.RapidTypeAnalyzer;
import com.google.gwt.dev.jjs.impl.ResolveRuntimeTypeReferences.IntTypeMapper;
import com.google.gwt.dev.js.JsIncrementalNamer.JsIncrementalNamerState;
import com.google.gwt.dev.resource.Resource;
import com.google.gwt.dev.util.Name.InternalName;
import com.google.gwt.thirdparty.guava.common.annotations.VisibleForTesting;
import com.google.gwt.thirdparty.guava.common.base.Objects;
import com.google.gwt.thirdparty.guava.common.base.Predicates;
import com.google.gwt.thirdparty.guava.common.collect.HashMultimap;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableList;
import com.google.gwt.thirdparty.guava.common.collect.ImmutableSet;
import com.google.gwt.thirdparty.guava.common.collect.Iterables;
import com.google.gwt.thirdparty.guava.common.collect.Maps;
import com.google.gwt.thirdparty.guava.common.collect.Multimap;
import com.google.gwt.thirdparty.guava.common.collect.Multimaps;
import com.google.gwt.thirdparty.guava.common.collect.Sets;
import cern.colt.list.IntArrayList;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* MinimalRebuildCache contains compiler information that can be persisted between compiles to
* decrease compilation time.
* <p>
* Cached information is specific to a single permutation and it is the responsibility of the
* framework driving the Compiler to supply the right Cache instance for the right Module
* configuration and to make sure that the Compiler is only processing one permutation at a time.
* <p>
* All type names referenced here are assumed to be binary names.
* <p>
* A "typeName" here might be a root type or nested type but a "compilationUnitName" here will
* always be the name of just the root type in a compilation unit.
*/
public class MinimalRebuildCache implements Serializable {
private static void appendSubTypes(Set<String> accumulatedTypeNames, Set<String> parentTypeNames,
JTypeOracle typeOracle) {
for (String parentTypeName : parentTypeNames) {
Iterables.addAll(accumulatedTypeNames, typeOracle.getSubTypeNames(parentTypeName));
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static void copyCollection(Collection fromCollection, Collection toCollection) {
toCollection.clear();
toCollection.addAll(fromCollection);
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static void copyMap(Map fromMap, Map toMap) {
toMap.clear();
toMap.putAll(fromMap);
}
@SuppressWarnings({"rawtypes", "unchecked"})
private static void copyMultimap(Multimap fromMap, Multimap toMap) {
toMap.clear();
toMap.putAll(fromMap);
}
/**
* Diffs lastModifiedByResourcePath from the previous compile against currentResources from the
* current compile. modifiedResourcePaths is wiped and recreated to be a list of just the modified
* or deleted resources, deletedResourcePaths is wiped and recreated to be a list of just the
* deleted resources and modifiedResourcePaths is updated in place with new lastModified dates.
*/
private static void recordModifiedResources(Map<String, Long> currentModifiedByResourcePath,
Map<String, Long> lastModifiedByResourcePath, Set<String> modifiedResourcePaths,
Set<String> deletedResourcePaths) {
deletedResourcePaths.clear();
modifiedResourcePaths.clear();
Set<String> currentResourcePaths = Sets.newHashSet();
for (Entry<String, Long> entry : currentModifiedByResourcePath.entrySet()) {
String currentResourcePath = entry.getKey();
Long currentResourceModified = entry.getValue();
currentResourcePaths.add(currentResourcePath);
Long lastKnownModified =
lastModifiedByResourcePath.put(currentResourcePath, currentResourceModified);
if (!Objects.equal(lastKnownModified, currentResourceModified)) {
// Added or Modified resource.
modifiedResourcePaths.add(currentResourcePath);
}
}
// Removed resources.
{
// Figure out which resources were removed.
Set<String> removedResourcePaths = Sets.newHashSet(
Sets.difference(lastModifiedByResourcePath.keySet(), currentResourcePaths));
// Log them as "modified".
deletedResourcePaths.addAll(removedResourcePaths);
modifiedResourcePaths.addAll(removedResourcePaths);
// Remove any path to modified date entries for them.
for (String removedResourcePath : removedResourcePaths) {
lastModifiedByResourcePath.remove(removedResourcePath);
}
}
}
private static Set<String> resourcePathsToCompilationUnitNames(Set<String> resourcePaths) {
Set<String> compilationUnitNames = Sets.newHashSet();
for (String resourcePath : resourcePaths) {
compilationUnitNames.add(Shared.toTypeName(resourcePath));
}
return compilationUnitNames;
}
private static Map<String, Long> resourcesToModifiedByPath(Collection<Resource> resources) {
Map<String, Long> modifiedByPath = Maps.newHashMap();
for (Resource resource : resources) {
modifiedByPath.put(resource.getPath(), resource.getLastModified());
}
return modifiedByPath;
}
/*
* Update copyFrom() whenever adding more fields.
*/
protected final ImmediateTypeRelations immediateTypeRelations = new ImmediateTypeRelations();
private final Map<String, String> compilationUnitTypeNameByNestedTypeName = Maps.newHashMap();
private final Map<String, String> contentHashByGeneratedTypeName = Maps.newHashMap();
private final Set<String> deletedCompilationUnitNames = Sets.newHashSet();
private final Set<String> deletedDiskSourcePaths = Sets.newHashSet();
private final Set<String> deletedResourcePaths = Sets.newHashSet();
private final Set<String> dualJsoImplInterfaceNames = Sets.newHashSet();
private final Map<String, String> descriptionByExportedGlobalNames = Maps.newHashMap();
private final Multimap<String, String> exportedGlobalNamesByTypeName = HashMultimap.create();
private final ArtifactSet generatedArtifacts = new ArtifactSet();
private final Multimap<String, String> generatedCompilationUnitNamesByReboundTypeNames =
HashMultimap.create();
private final IntTypeMapper intTypeMapper = new IntTypeMapper();
private final Map<String, String> jsByTypeName = Maps.newHashMap();
private final JsIncrementalNamerState jsIncrementalNamerState = new JsIncrementalNamerState();
private final Set<String> jsoStatusChangedTypeNames = Sets.newHashSet();
private final Set<String> jsoTypeNames = Sets.newHashSet();
private Integer lastLinkedJsBytes;
private final Map<String, Long> lastModifiedByDiskSourcePath = Maps.newHashMap();
private final Map<String, Long> lastModifiedByResourcePath = Maps.newHashMap();
private final Set<String> lastReachableTypeNames = Sets.newHashSet();
private final Set<String> modifiedCompilationUnitNames = Sets.newHashSet();
private final Set<String> modifiedDiskSourcePaths = Sets.newHashSet();
private final Set<String> modifiedResourcePaths = Sets.newHashSet();
private final Multimap<String, String> nestedTypeNamesByUnitTypeName = HashMultimap.create();
private final Set<String> preambleTypeNames = Sets.newHashSet();
private transient ImmutableSet<String> processedStaleTypeNames = ImmutableSet.<String> of();
private final Multimap<String, String> rebinderTypeNamesByReboundTypeName = HashMultimap.create();
private final Multimap<String, String> reboundTypeNamesByGeneratedCompilationUnitNames =
HashMultimap.create();
private final Multimap<String, String> reboundTypeNamesByInputResource = HashMultimap.create();
private final Multimap<String, String> referencedTypeNamesByTypeName = HashMultimap.create();
private final Set<String> rootTypeNames = Sets.newHashSet();
private final Set<String> singleJsoImplInterfaceNames = Sets.newHashSet();
private final Set<String> sourceCompilationUnitNames = Sets.newHashSet();
private final Map<String, JsSourceMap> sourceMapsByTypeName = Maps.newHashMap();
private final Set<String> staleTypeNames = Sets.newHashSet();
private final Map<String, StatementRanges> statementRangesByTypeName = Maps.newHashMap();
private StringAnalyzableTypeEnvironment typeEnvironment = new StringAnalyzableTypeEnvironment();
private final Multimap<String, String> typeNamesByReferencingTypeName = HashMultimap.create();
public String addExportedGlobalName(
String exportedGlobalName, String description, String inTypeName) {
exportedGlobalNamesByTypeName.put(inTypeName, exportedGlobalName);
return descriptionByExportedGlobalNames.put(exportedGlobalName, description);
}
/**
* Accumulates generated artifacts so that they can be output on recompiles even if no generators
* are run.
*/
public void addGeneratedArtifacts(ArtifactSet generatedArtifacts) {
this.generatedArtifacts.addAll(generatedArtifacts);
}
public void addModifiedCompilationUnitNames(TreeLogger logger,
Set<String> modifiedCompilationUnitNames) {
logger.log(TreeLogger.DEBUG, "adding to cached list of known modified compilation units "
+ modifiedCompilationUnitNames);
this.modifiedCompilationUnitNames.addAll(modifiedCompilationUnitNames);
}
public void addSourceCompilationUnitName(String sourceCompilationUnitName) {
// sourceCompilationUnitNames contains all compilation unit type names seen so far even
// across recompiles. There is a need to know whether a unit is available in source form (
// either compiled from source in this iteration or its AST available from cache) so that
// we insert the correct type of reference when we see a BinaryTypeReference in the JDT
// AST. {@see GwtAstBuilder}.
// NOTE: DO NOT RESET OR CLEAR THIS SET.
this.sourceCompilationUnitNames.add(sourceCompilationUnitName);
}
public void addTypeReference(String fromTypeName, String toTypeName) {
referencedTypeNamesByTypeName.put(fromTypeName, toTypeName);
typeNamesByReferencingTypeName.put(toTypeName, fromTypeName);
}
public void associateReboundTypeWithGeneratedCompilationUnitName(String reboundTypeName,
String generatedCompilationUnitName) {
reboundTypeNamesByGeneratedCompilationUnitNames.put(generatedCompilationUnitName,
reboundTypeName);
}
/**
* Record that a Generator that was ran as a result of a GWT.create(ReboundType.class) call read a
* particular resource.
*/
public void associateReboundTypeWithInputResource(String reboundTypeName,
String inputResourcePath) {
reboundTypeNamesByInputResource.put(inputResourcePath, reboundTypeName);
}
public void clearPerTypeJsCache() {
rootTypeNames.clear();
preambleTypeNames.clear();
deletedResourcePaths.clear();
modifiedResourcePaths.clear();
deletedDiskSourcePaths.clear();
modifiedDiskSourcePaths.clear();
contentHashByGeneratedTypeName.clear();
jsByTypeName.clear();
referencedTypeNamesByTypeName.clear();
sourceMapsByTypeName.clear();
statementRangesByTypeName.clear();
typeNamesByReferencingTypeName.clear();
}
public void clearRebinderTypeAssociations(String rebinderTypeName) {
rebinderTypeNamesByReboundTypeName.values().remove(rebinderTypeName);
}
public void clearReboundTypeAssociations(String reboundTypeName) {
reboundTypeNamesByInputResource.values().remove(reboundTypeName);
reboundTypeNamesByGeneratedCompilationUnitNames.values().remove(reboundTypeName);
}
/**
* Calculates the set of stale types and clears their cached Js, StatementRanges and SourceMaps.
* <p>
* The calculation of stale types starts with the list of known modified types and expands that
* set using various patterns intended to find any types whose output JS would be affected by the
* changes in the modified types. For example if the parent of class Foo was changed then the
* castmaps in all children of Foo need to be recreated.
* <p>
* In some ways this process is similar to that performed by the CompilationUnitInvalidator but it
* differs both in what type of cached objects are being cleared (JS versus CompilationUnits) and
* in what invalidation rules must be applied. CompilationUnitInvalidator is concerned only with
* changes in interface while the invalidation here must also look at JSO promotion/demotion and
* cast references because of the peculiarities of output JS format.
*/
public Set<String> computeAndClearStaleTypesCache(TreeLogger logger, JTypeOracle typeOracle) {
if (!isPopulated()) {
return Sets.newHashSet();
}
// Cache the ReboundTypeNames -> GeneratedCompilationUnitNames reverse map as it will be needed
// several times.
generatedCompilationUnitNamesByReboundTypeNames.clear();
Multimaps.invertFrom(reboundTypeNamesByGeneratedCompilationUnitNames,
generatedCompilationUnitNamesByReboundTypeNames);
// Store stale type names in a persisted field so that tests can inspect behavior.
staleTypeNames.clear();
Set<String> modifiedTypeNames = computeModifiedTypeNames();
// Accumulate the names of stale types resulting from some known type or resource modifications,
// using various patterns (sub types, referencing types, etc).
{
staleTypeNames.addAll(modifiedTypeNames);
appendSubTypes(staleTypeNames, modifiedTypeNames, typeOracle);
// Marks stale any type that references either a modified type or any subtype of a modified
// type. The references of subtypes can be done away with once we revamp Array castmap
// generation.
ImmutableList<String> modifiedTypeAndSubTypeNames = ImmutableList.copyOf(staleTypeNames);
appendReferencingTypes(staleTypeNames, modifiedTypeAndSubTypeNames);
appendReferencingTypes(staleTypeNames, jsoStatusChangedTypeNames);
staleTypeNames.addAll(
computeTypesThatRebindTypes(computeReboundTypesAffectedByModifiedResources()));
appendTypesToRegenerateStaleGeneratedTypes(staleTypeNames);
// Generator output is affected by types queried from the TypeOracle but changes in these
// types are not being directly supported at this time since some of them are already handled
// because they are referenced by the Generator output and since changes in subtype queries
// probably make GWTRPC output incompatible with a server anyway (and thus already forces a
// restart).
staleTypeNames.removeAll(JProgram.SYNTHETIC_TYPE_NAMES);
}
/*
* Filter for just those stale types that are actually reachable. Since if they're not reachable
* we don't want to artificially traverse them and unnecessarily reveal dependency problems. And
* if they have become reachable, since they're missing JS, they will already be fully traversed
* when seen in Unify.
*/
copyCollection(filterUnreachableTypeNames(staleTypeNames), staleTypeNames);
// These log lines can be expensive.
if (logger.isLoggable(TreeLogger.DEBUG)) {
logger.log(TreeLogger.DEBUG, "known modified types = " + modifiedTypeNames);
logger.log(TreeLogger.DEBUG, "known modified resources = " + modifiedResourcePaths);
logger.log(TreeLogger.DEBUG,
"clearing cached output for resulting stale types = " + staleTypeNames);
}
for (String staleTypeName : staleTypeNames) {
clearCachedTypeOutput(staleTypeName);
}
return Sets.newHashSet(staleTypeNames);
}
/**
* Computes and returns the set of names of deleted types.
* <p>
* Deleted types are all those types that are nested within compilation units that are known to
* have been deleted.
*/
public Set<String> computeDeletedTypeNames() {
return computeNestedTypeNames(deletedCompilationUnitNames);
}
/**
* Computes and returns the set of names of modified types.
* <p>
* Modified types are all those types that are nested within compilation units that are known to
* have been modified.
*/
public Set<String> computeModifiedTypeNames() {
// Accumulate the names of types that are nested within known modified compilation units.
return computeNestedTypeNames(modifiedCompilationUnitNames);
}
/**
* Computes and returns the names of the set of types that are referenceable within the method
* level control flow starting from the entry methods, immortal codegen types and exported
* JsInterop methods.
* <p>
* Should only be called once per compile, so that the "lastReachableTypeNames" list accurately
* reflects the reachable types of the immediately previous compile.
*/
public Set<String> computeReachableTypeNames() {
RapidTypeAnalyzer rapidTypeAnalyzer = new RapidTypeAnalyzer(typeEnvironment);
// Artificially reach and traverse immortal codegen types since references to these may have
// been synthesized in JS generation. These will not be pruned.
for (String immortalCodegenTypeName : JProgram.IMMORTAL_CODEGEN_TYPES_SET) {
int immortalCodegenTypeId = typeEnvironment.getTypeIdByName(immortalCodegenTypeName);
rapidTypeAnalyzer.markTypeIdReachable(immortalCodegenTypeId);
// Includes the clinit method.
rapidTypeAnalyzer.markMemberMethodIdsReachable(immortalCodegenTypeId);
}
// Artificially reach @JsExport and @JsType methods. Since the enclosing types are
// not being marked instantiated or reachable there's still a chance that they will be pruned
// if no instantiations or static references are found.
IntArrayList enclosingTypeIds = typeEnvironment.getEnclosingTypeIdsOfExportedMethods();
for (int i = 0; i < enclosingTypeIds.size(); i++) {
int enclosingTypeId = enclosingTypeIds.get(i);
IntArrayList exportedMethodIds =
typeEnvironment.getExportedMemberMethodIdsIn(enclosingTypeId);
if (exportedMethodIds == null) {
continue;
}
for (int j = 0; j < exportedMethodIds.size(); j++) {
int exportedMethodId = exportedMethodIds.get(j);
rapidTypeAnalyzer.markMethodIdReachable(exportedMethodId, false);
}
}
// Artificially reach types with @JsExport'ed or @JsType'ed static fields or methods (including
// Constructors). These types will not be pruned.
IntArrayList typeIdsWithExportedStaticReferences =
typeEnvironment.getTypeIdsWithExportedStaticReferences();
for (int i = 0; i < typeIdsWithExportedStaticReferences.size(); i++) {
int typeId = typeIdsWithExportedStaticReferences.get(i);
rapidTypeAnalyzer.markTypeIdReachable(typeId);
String typeName = typeEnvironment.getTypeNameById(typeId);
int typeClinitMethodId = typeEnvironment.getMethodIdByName(typeName + "::$clinit()V");
rapidTypeAnalyzer.markMethodIdReachable(typeClinitMethodId, false);
}
// Reach and traverse entry method types. This is just the EntryMethodHolder
// class and its init() function. It will not be pruned. This is the conceptual "root" of the
// application execution.
IntArrayList entryMethodIds = typeEnvironment.getEntryMethodIds();
for (int i = 0; i < entryMethodIds.size(); i++) {
int entryMethodId = entryMethodIds.get(i);
int typeId = typeEnvironment.getEnclosingTypeId(entryMethodId);
rapidTypeAnalyzer.markTypeIdReachable(typeId);
// Includes the clinit method.
rapidTypeAnalyzer.markMemberMethodIdsReachable(typeId);
}
// Perform rapid type analysis.
IntArrayList reachableTypeIds = rapidTypeAnalyzer.computeReachableTypeIds();
// Translate ids back to strings and keep the results around for the next compile.
Set<String> reachableTypeNames = Sets.newHashSet();
for (int i = 0; i < reachableTypeIds.size(); i++) {
int reachableTypeId = reachableTypeIds.get(i);
reachableTypeNames.add(typeEnvironment.getTypeNameById(reachableTypeId));
}
copyCollection(reachableTypeNames, lastReachableTypeNames);
return reachableTypeNames;
}
/**
* Replaces the contents of this cache with the contents of the given cache.
* <p>
* This operation should be kept fast as it will be called once per compile. At the moment it
* takes about 2.5% of the time in an incremental recompile. If wanting to recover this time in
* the future consider parallelizing the copy or grouping the values from hash tables with similar
* keys into a single data object and hashtable so that fewer references need to be replicated.
*/
public void copyFrom(MinimalRebuildCache that) {
this.lastLinkedJsBytes = that.lastLinkedJsBytes;
this.intTypeMapper.copyFrom(that.intTypeMapper);
this.jsIncrementalNamerState.copyFrom(that.jsIncrementalNamerState);
this.immediateTypeRelations.copyFrom(that.immediateTypeRelations);
this.typeEnvironment.copyFrom(that.typeEnvironment);
copyMap(that.compilationUnitTypeNameByNestedTypeName,
this.compilationUnitTypeNameByNestedTypeName);
copyMap(that.contentHashByGeneratedTypeName, this.contentHashByGeneratedTypeName);
copyMap(that.descriptionByExportedGlobalNames, this.descriptionByExportedGlobalNames);
copyMap(that.jsByTypeName, this.jsByTypeName);
copyMap(that.lastModifiedByDiskSourcePath, this.lastModifiedByDiskSourcePath);
copyMap(that.lastModifiedByResourcePath, this.lastModifiedByResourcePath);
copyMap(that.sourceMapsByTypeName, this.sourceMapsByTypeName);
copyMap(that.statementRangesByTypeName, this.statementRangesByTypeName);
copyMultimap(that.exportedGlobalNamesByTypeName, this.exportedGlobalNamesByTypeName);
copyMultimap(that.generatedCompilationUnitNamesByReboundTypeNames,
this.generatedCompilationUnitNamesByReboundTypeNames);
copyMultimap(that.nestedTypeNamesByUnitTypeName, this.nestedTypeNamesByUnitTypeName);
copyMultimap(that.rebinderTypeNamesByReboundTypeName, this.rebinderTypeNamesByReboundTypeName);
copyMultimap(that.reboundTypeNamesByGeneratedCompilationUnitNames,
this.reboundTypeNamesByGeneratedCompilationUnitNames);
copyMultimap(that.reboundTypeNamesByInputResource, this.reboundTypeNamesByInputResource);
copyMultimap(that.referencedTypeNamesByTypeName, this.referencedTypeNamesByTypeName);
copyMultimap(that.typeNamesByReferencingTypeName, this.typeNamesByReferencingTypeName);
copyCollection(that.deletedCompilationUnitNames, this.deletedCompilationUnitNames);
copyCollection(that.deletedDiskSourcePaths, this.deletedDiskSourcePaths);
copyCollection(that.deletedResourcePaths, this.deletedResourcePaths);
copyCollection(that.dualJsoImplInterfaceNames, this.dualJsoImplInterfaceNames);
copyCollection(that.generatedArtifacts, this.generatedArtifacts);
copyCollection(that.jsoStatusChangedTypeNames, this.jsoStatusChangedTypeNames);
copyCollection(that.jsoTypeNames, this.jsoTypeNames);
copyCollection(that.lastReachableTypeNames, this.lastReachableTypeNames);
copyCollection(that.modifiedCompilationUnitNames, this.modifiedCompilationUnitNames);
copyCollection(that.modifiedDiskSourcePaths, this.modifiedDiskSourcePaths);
copyCollection(that.modifiedResourcePaths, this.modifiedResourcePaths);
copyCollection(that.preambleTypeNames, this.preambleTypeNames);
copyCollection(that.rootTypeNames, this.rootTypeNames);
copyCollection(that.singleJsoImplInterfaceNames, this.singleJsoImplInterfaceNames);
copyCollection(that.sourceCompilationUnitNames, this.sourceCompilationUnitNames);
copyCollection(that.staleTypeNames, this.staleTypeNames);
}
/**
* Return the set of provided typeNames with unreachable types filtered out.
*/
public Set<String> filterUnreachableTypeNames(Set<String> typeNames) {
return Sets.newHashSet(Sets.filter(typeNames, Predicates.in(lastReachableTypeNames)));
}
public ArtifactSet getGeneratedArtifacts() {
return generatedArtifacts;
}
public ImmediateTypeRelations getImmediateTypeRelations() {
return immediateTypeRelations;
}
public String getJs(String typeName) {
return jsByTypeName.get(typeName);
}
public int getLastLinkedJsBytes() {
return lastLinkedJsBytes;
}
@VisibleForTesting
public Set<String> getModifiedCompilationUnitNames() {
return modifiedCompilationUnitNames;
}
public JsIncrementalNamerState getPersistentPrettyNamerState() {
return jsIncrementalNamerState;
}
public Set<String> getPreambleTypeNames() {
return preambleTypeNames;
}
/**
* Returns the set of the names of types that were processed as stale. This list can be larger
* than the calculated list of stale types for example when a new reference is created to a type
* that had not been processed in a previous compile.
*/
public Set<String> getProcessedStaleTypeNames() {
return processedStaleTypeNames;
}
public JsSourceMap getSourceMap(String typeName) {
return sourceMapsByTypeName.get(typeName);
}
@VisibleForTesting
public Set<String> getStaleTypeNames() {
return staleTypeNames;
}
public StatementRanges getStatementRanges(String typeName) {
return statementRangesByTypeName.get(typeName);
}
public StringAnalyzableTypeEnvironment getTypeEnvironment() {
return typeEnvironment;
}
public IntTypeMapper getTypeMapper() {
return intTypeMapper;
}
public boolean hasJs(String typeName) {
return jsByTypeName.containsKey(typeName);
}
public boolean hasPreambleTypeNames() {
return !preambleTypeNames.isEmpty();
}
/**
* Returns true if there is cached data to reuse in the next recompile.
*/
public boolean isPopulated() {
return !immediateTypeRelations.isEmpty();
}
public boolean isSourceCompilationUnit(String compilationUnitName) {
return sourceCompilationUnitNames.contains(compilationUnitName);
}
public boolean knowsLastLinkedJsBytes() {
return lastLinkedJsBytes != null;
}
@VisibleForTesting
public void markSourceFileStale(String typeName) {
lastModifiedByDiskSourcePath.remove(typeName);
}
/**
* Records the resource paths and modification dates of build resources in the current compile and
* builds a list of known modified resource paths by comparing the resource paths and modification
* dates of build resources in the previous compile with those of the current compile.
*/
public void recordBuildResources(ModuleDef module) {
Map<String, Long> currentModifiedByResourcePath =
resourcesToModifiedByPath(module.getBuildResourceOracle().getResources());
recordModifiedResources(currentModifiedByResourcePath, lastModifiedByResourcePath,
modifiedResourcePaths, deletedResourcePaths);
}
/**
* Records the paths and modification dates of source resources in the current compile and builds
* a list of known modified paths by comparing the paths and modification dates of source
* resources in the previous compile with those of the current compile.
*/
@VisibleForTesting
public void recordDiskSourceResources(Map<String, Long> currentModifiedByDiskSourcePath) {
recordModifiedResources(currentModifiedByDiskSourcePath, lastModifiedByDiskSourcePath,
modifiedDiskSourcePaths, deletedDiskSourcePaths);
deletedCompilationUnitNames.clear();
deletedCompilationUnitNames.addAll(resourcePathsToCompilationUnitNames(deletedDiskSourcePaths));
modifiedCompilationUnitNames.clear();
modifiedCompilationUnitNames.addAll(
resourcePathsToCompilationUnitNames(modifiedDiskSourcePaths));
}
/**
* Records the paths and modification dates of source resources in the current compile and builds
* a list of known modified paths by comparing the paths and modification dates of source
* resources in the previous compile with those of the current compile.
*/
public void recordDiskSourceResources(ModuleDef module) {
Map<String, Long> currentModifiedByDiskSourcePath =
resourcesToModifiedByPath(module.getSourceResourceOracle().getResources());
recordDiskSourceResources(currentModifiedByDiskSourcePath);
}
/**
* Records the paths and content ids of generated source resources in the current compile and
* updates a list of known modified paths by comparing the paths and content ids of generated
* source resources in the previous compile with those of the current compile.
*/
public void recordGeneratedUnits(Collection<GeneratedUnit> generatedUnits) {
// Not all Generators are run on each compile so it is not possible to compare previous and
// current generated units to detect deletions. As a result only modifications are tracked.
for (GeneratedUnit generatedUnit : generatedUnits) {
String currentStrongHash = generatedUnit.getStrongHash();
String lastKnownStrongHash =
contentHashByGeneratedTypeName.put(generatedUnit.getTypeName(), currentStrongHash);
if (!Objects.equal(lastKnownStrongHash, currentStrongHash)) {
// Added or Modified resource.
modifiedCompilationUnitNames.add(generatedUnit.getTypeName());
}
}
}
@VisibleForTesting
public void recordNestedTypeName(String compilationUnitTypeName, String nestedTypeName) {
nestedTypeNamesByUnitTypeName.put(compilationUnitTypeName, nestedTypeName);
compilationUnitTypeNameByNestedTypeName.put(nestedTypeName, compilationUnitTypeName);
}
public void recordNestedTypeNamesPerType(CompilationUnit compilationUnit) {
// For the root type in the compilation unit the source name and binary name are the same.
String compilationUnitTypeName = compilationUnit.getTypeName();
nestedTypeNamesByUnitTypeName.removeAll(compilationUnitTypeName);
for (CompiledClass compiledClass : compilationUnit.getCompiledClasses()) {
String nestedTypeName = InternalName.toBinaryName(compiledClass.getInternalName());
recordNestedTypeName(compilationUnitTypeName, nestedTypeName);
}
}
/**
* Records that rebinder type Foo contains a GWT.create(ReboundTypeBar.class) call.
*/
public void recordRebinderTypeForReboundType(String reboundTypeName, String rebinderType) {
rebinderTypeNamesByReboundTypeName.put(reboundTypeName, rebinderType);
}
public void removeExportedNames(String inTypeName) {
Collection<String> exportedGlobalNamesForType =
exportedGlobalNamesByTypeName.removeAll(inTypeName);
descriptionByExportedGlobalNames.keySet().removeAll(exportedGlobalNamesForType);
}
public void removeReferencesFrom(String fromTypeName) {
Collection<String> toTypeNames = referencedTypeNamesByTypeName.get(fromTypeName);
for (String toTypeName : toTypeNames) {
typeNamesByReferencingTypeName.remove(toTypeName, fromTypeName);
}
referencedTypeNamesByTypeName.removeAll(fromTypeName);
}
public void setEntryMethodNames(List<String> entryMethodNames) {
typeEnvironment.setEntryMethodNames(entryMethodNames);
}
public void setJsForType(TreeLogger logger, String typeName, String typeJs) {
logger.log(TreeLogger.SPAM, "caching JS for type " + typeName);
jsByTypeName.put(typeName, typeJs);
}
/**
* Records the names of JSO subtypes, single impl interfaces and dual impl interfaces as well as
* keeps track of types that entered or left one of the lists in the most recent iteration.
*/
public void setJsoTypeNames(Set<String> jsoTypeNames, Set<String> singleJsoImplInterfaceNames,
Set<String> dualJsoImplInterfaceNames) {
this.jsoStatusChangedTypeNames.clear();
this.jsoStatusChangedTypeNames.addAll(
Sets.symmetricDifference(this.jsoTypeNames, jsoTypeNames));
this.jsoStatusChangedTypeNames.addAll(
Sets.symmetricDifference(this.singleJsoImplInterfaceNames, singleJsoImplInterfaceNames));
this.jsoStatusChangedTypeNames.addAll(
Sets.symmetricDifference(this.dualJsoImplInterfaceNames, dualJsoImplInterfaceNames));
this.jsoTypeNames.clear();
this.jsoTypeNames.addAll(jsoTypeNames);
this.singleJsoImplInterfaceNames.clear();
this.singleJsoImplInterfaceNames.addAll(singleJsoImplInterfaceNames);
this.dualJsoImplInterfaceNames.clear();
this.dualJsoImplInterfaceNames.addAll(dualJsoImplInterfaceNames);
}
public void setLastLinkedJsBytes(int lastLinkedJsBytes) {
this.lastLinkedJsBytes = lastLinkedJsBytes;
}
public void setPreambleTypeNames(TreeLogger logger, Set<String> preambleTypeNames) {
logger.log(TreeLogger.DEBUG, "caching list of known preamble types " + preambleTypeNames);
this.preambleTypeNames.addAll(preambleTypeNames);
}
public void setProcessedStaleTypeNames(Set<String> processedStaleTypeNames) {
// If this is the first compile.
if (!isPopulated()) {
// Don't record processed stale types, since the list is huge and not useful for test
// assertions.
return;
}
this.processedStaleTypeNames = ImmutableSet.copyOf(processedStaleTypeNames);
}
public void setRootTypeNames(Collection<String> rootTypeNames) {
this.rootTypeNames.clear();
this.rootTypeNames.addAll(rootTypeNames);
}
public void setSourceMapForType(String typeName, JsSourceMap sourceMap) {
sourceMapsByTypeName.put(typeName, sourceMap);
}
public void setStatementRangesForType(String typeName, StatementRanges statementRanges) {
statementRangesByTypeName.put(typeName, statementRanges);
}
@VisibleForTesting
boolean hasSameContent(MinimalRebuildCache that) {
// Ignoring processedStaleTypeNames since it is transient.
return this.immediateTypeRelations.hasSameContent(that.immediateTypeRelations) && Objects.equal(
this.compilationUnitTypeNameByNestedTypeName, that.compilationUnitTypeNameByNestedTypeName)
&& Objects.equal(this.contentHashByGeneratedTypeName, that.contentHashByGeneratedTypeName)
&& Objects.equal(this.deletedCompilationUnitNames, that.deletedCompilationUnitNames)
&& Objects.equal(this.deletedDiskSourcePaths, that.deletedDiskSourcePaths)
&& Objects.equal(this.deletedResourcePaths, that.deletedResourcePaths)
&& Objects.equal(this.dualJsoImplInterfaceNames, that.dualJsoImplInterfaceNames)
&& Objects.equal(this.generatedArtifacts, that.generatedArtifacts)
&& Objects.equal(this.descriptionByExportedGlobalNames,
that.descriptionByExportedGlobalNames)
&& Objects.equal(this.exportedGlobalNamesByTypeName, that.exportedGlobalNamesByTypeName)
&& Objects.equal(this.generatedCompilationUnitNamesByReboundTypeNames,
that.generatedCompilationUnitNamesByReboundTypeNames)
&& this.intTypeMapper.hasSameContent(that.intTypeMapper)
&& Objects.equal(this.jsByTypeName, that.jsByTypeName)
&& Objects.equal(this.jsoStatusChangedTypeNames, that.jsoStatusChangedTypeNames)
&& Objects.equal(this.jsoTypeNames, that.jsoTypeNames)
&& Objects.equal(this.lastLinkedJsBytes, that.lastLinkedJsBytes)
&& Objects.equal(this.lastModifiedByDiskSourcePath, that.lastModifiedByDiskSourcePath)
&& Objects.equal(this.lastModifiedByResourcePath, that.lastModifiedByResourcePath)
&& Objects.equal(this.lastReachableTypeNames, that.lastReachableTypeNames)
&& Objects.equal(this.modifiedCompilationUnitNames, that.modifiedCompilationUnitNames)
&& Objects.equal(this.modifiedDiskSourcePaths, that.modifiedDiskSourcePaths)
&& Objects.equal(this.modifiedResourcePaths, that.modifiedResourcePaths)
&& Objects.equal(this.nestedTypeNamesByUnitTypeName, that.nestedTypeNamesByUnitTypeName)
&& this.jsIncrementalNamerState.hasSameContent(that.jsIncrementalNamerState)
&& Objects.equal(this.preambleTypeNames, that.preambleTypeNames) && Objects.equal(
this.rebinderTypeNamesByReboundTypeName, that.rebinderTypeNamesByReboundTypeName)
&& Objects.equal(this.reboundTypeNamesByGeneratedCompilationUnitNames,
that.reboundTypeNamesByGeneratedCompilationUnitNames)
&& Objects.equal(this.reboundTypeNamesByInputResource, that.reboundTypeNamesByInputResource)
&& Objects.equal(this.referencedTypeNamesByTypeName, that.referencedTypeNamesByTypeName)
&& Objects.equal(this.rootTypeNames, that.rootTypeNames)
&& Objects.equal(this.singleJsoImplInterfaceNames, that.singleJsoImplInterfaceNames)
&& Objects.equal(this.sourceCompilationUnitNames, that.sourceCompilationUnitNames)
&& Objects.equal(this.sourceMapsByTypeName, that.sourceMapsByTypeName)
&& Objects.equal(this.staleTypeNames, that.staleTypeNames)
&& Objects.equal(this.statementRangesByTypeName, that.statementRangesByTypeName)
&& this.typeEnvironment.hasSameContent(that.typeEnvironment)
&& Objects.equal(this.typeNamesByReferencingTypeName, that.typeNamesByReferencingTypeName);
}
private void appendReferencingTypes(Set<String> accumulatedTypeNames,
Collection<String> referencedTypeNames) {
for (String referencedTypeName : referencedTypeNames) {
Collection<String> referencingTypeNames =
typeNamesByReferencingTypeName.get(referencedTypeName);
accumulatedTypeNames.addAll(referencingTypeNames);
}
}
/**
* If type Foo is a generated type and is stale this pass will append type Bar that triggers
* Generator Baz that regenerates type Foo.
* <p>
* This is necessary since just clearing the cache for type Foo would not be adequate to cause the
* recreation of its cached JS without also rerunning the Generator that creates type Foo.
*/
private void appendTypesToRegenerateStaleGeneratedTypes(Set<String> staleTypeNames) {
Set<String> generatedCompilationUnitNames =
reboundTypeNamesByGeneratedCompilationUnitNames.keySet();
// Filter the current stale types list for any compilation units that are known to be generated.
Set<String> staleGeneratedCompilationUnitNames = Sets.intersection(
computeCompilationUnitNames(staleTypeNames), generatedCompilationUnitNames);
boolean discoveredMoreStaleTypes;
do {
// Accumulate staleGeneratedCompilationUnits -> generators ->
// generatorTriggeringCompilationUnits.
Set<String> reboundTypesThatGenerateTheStaleCompilationUnits =
computeReboundTypesThatGenerateTypes(staleGeneratedCompilationUnitNames);
Set<String> generatorTriggeringTypes =
computeTypesThatRebindTypes(reboundTypesThatGenerateTheStaleCompilationUnits);
// Mark these generator triggering types stale and keep track of whether any of them were not
// previously known to be stale.
discoveredMoreStaleTypes = staleTypeNames.addAll(generatorTriggeringTypes);
// It's possible that a generator triggering type was itself also created by a Generator.
// Repeat the backwards trace process till none of the newly stale types are generated types.
staleGeneratedCompilationUnitNames = Sets.intersection(
computeCompilationUnitNames(generatorTriggeringTypes), generatedCompilationUnitNames);
} while (discoveredMoreStaleTypes);
}
private void clearCachedTypeOutput(String staleTypeName) {
jsByTypeName.remove(staleTypeName);
statementRangesByTypeName.remove(staleTypeName);
sourceMapsByTypeName.remove(staleTypeName);
}
/**
* Returns the set of names of CompilationUnits that contain all of the given type names.
*/
private Set<String> computeCompilationUnitNames(Set<String> typeNames) {
Set<String> compilationUnitNames = Sets.newHashSet();
for (String typeName : typeNames) {
compilationUnitNames.add(compilationUnitTypeNameByNestedTypeName.get(typeName));
}
return compilationUnitNames;
}
/**
* Returns the set of type names contained within the given compilation units.
*/
private Set<String> computeNestedTypeNames(Set<String> compilationUnitNames) {
Set<String> nestedTypeNames = Sets.newHashSet();
nestedTypeNames.addAll(compilationUnitNames);
for (String compilationUnitName : compilationUnitNames) {
nestedTypeNames.addAll(nestedTypeNamesByUnitTypeName.get(compilationUnitName));
}
return nestedTypeNames;
}
/**
* Returns the set of names of types that when rebound trigger Generators that access resources
* which are known to have been modified.
*/
private Set<String> computeReboundTypesAffectedByModifiedResources() {
Set<String> affectedRebindTypeNames = Sets.newHashSet();
for (String modifiedResourcePath : modifiedResourcePaths) {
affectedRebindTypeNames.addAll(reboundTypeNamesByInputResource.get(modifiedResourcePath));
}
return affectedRebindTypeNames;
}
private Set<String> computeReboundTypesThatGenerateTypes(
Set<String> staleGeneratedCompilationUnitNames) {
Set<String> reboundTypesThatGenerateTypes = Sets.newHashSet();
for (String staleGeneratedCompilationUnitName : staleGeneratedCompilationUnitNames) {
reboundTypesThatGenerateTypes.addAll(
reboundTypeNamesByGeneratedCompilationUnitNames.get(staleGeneratedCompilationUnitName));
}
return reboundTypesThatGenerateTypes;
}
/**
* Returns the set of names of types that contain GWT.create(ReboundType.class) calls that rebind
* the given set of type names.
*/
private Set<String> computeTypesThatRebindTypes(Set<String> reboundTypeNames) {
Set<String> typesThatRebindTypes = Sets.newHashSet();
for (String reboundTypeName : reboundTypeNames) {
typesThatRebindTypes.addAll(rebinderTypeNamesByReboundTypeName.get(reboundTypeName));
}
return typesThatRebindTypes;
}
}