blob: 3e636c27704facaeed32432839851d8999a8ff85 [file] [log] [blame]
/*
* 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.javac;
import com.google.gwt.core.ext.TreeLogger;
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.Multimap;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Helper class to invalidate units in a set based on errors or references to
* other invalidate units.
*/
public class CompilationUnitInvalidator {
/**
* Mutates {@code units} by retaining only valid units. A unit is invalid if
* it
* <ul>
* <li>has errors,
* <li>depends on a member of {@code validClasses} that has errors,
* <li>depends on another member of {@code units} that has errors, or
* <li>references a source name that is not provided by a good member of
* {@code units} or {@code validClasses}.
* </ul>
*/
public static void retainValidUnits(TreeLogger logger, Collection<CompilationUnit> units,
Map<String, CompiledClass> validClasses, CompilationErrorsIndex compilationErrorsIndex) {
logger = logger.branch(TreeLogger.TRACE, "Removing invalidated units");
// Build a map of api-refs -> dependent units.
// This map excludes refs provided by good validClasses; it only contains
// dependencies that need to be provided by members of units.
Multimap<String, CompilationUnit> depsNeeded = HashMultimap.create();
// This set contains the source names of types provided by units, and after
// initial population, may shrink as problems are discovered in individual
// units.
Set<String> depsProvided = new HashSet<String>();
// For fast membership checking of the initial set of units
Set<CompilationUnit> initialUnits = (units instanceof Set)
? (Set<CompilationUnit>) units
: ImmutableSet.<CompilationUnit>copyOf(units);
// These are all of the invalid units
Set<CompilationUnit> allBrokenUnits = new HashSet<CompilationUnit>();
// Populate depsNeeded, depsProvided, and allBrokenUnits with their initial values.
// At first, only compilation units that directly contain an error are known to be
// broken, not their dependencies.
for (CompilationUnit unit : units) {
if (unit.isError()) {
// It is bad and can be removed immediately
allBrokenUnits.add(unit);
} else {
// Update set of dependencies the unit provides
for (CompiledClass cc : unit.getCompiledClasses()) {
depsProvided.add(cc.getSourceName());
}
// Update map of dependencies that the unit needs
for (String ref : unit.getDependencies().getApiRefs()) {
// Check validClasses
CompiledClass compiledClass = validClasses.get(ref);
if ((compiledClass == null)
|| compiledClass.getUnit().isError()
|| initialUnits.contains(compiledClass.getUnit())) {
// we'll put this into the double-check pot
depsNeeded.put(ref, unit);
}
}
}
}
// Repeatedly remove CompilationUnits that have a dependency that's known to
// be broken.
Multimap<String, CompilationUnit> missing;
do {
// Find the missing deps for this pass
missing = HashMultimap.create();
missing.putAll(depsNeeded);
missing.keySet().removeAll(depsProvided);
// Process the units with missing deps
for (Map.Entry<String, CompilationUnit> brokenEntry : missing.entries()) {
CompilationUnit brokenUnit = brokenEntry.getValue();
// Modify depsProvided for newly discovered broken units
// (side-effect add in 'if' condition)
if (allBrokenUnits.add(brokenUnit)) {
// Remove the broken unit from the provides set
for (CompiledClass cc : brokenUnit.getCompiledClasses()) {
depsProvided.remove(cc.getSourceName());
}
// Log it to maintain some logging compatibility with prior versions
// of this class.
TreeLogger branch = logger.branch(TreeLogger.DEBUG,
"Compilation unit '" + brokenUnit
+ "' is removed due to invalid reference(s):");
branch.log(TreeLogger.DEBUG, brokenEntry.getKey());
// Record inferred errors resulting from references to broken types so that accurate
// errors chains can be reported.
compilationErrorsIndex.add(brokenUnit.getTypeName(), brokenUnit.getResourceLocation(),
brokenUnit.getDependencies().getApiRefs(),
ImmutableList.of(brokenEntry.getKey() + " cannot be resolved to a type"));
}
}
// Having found and removed some units with missing deps, remove their
// needs.
depsNeeded.keySet().removeAll(missing.keySet());
} while (!missing.isEmpty());
units.removeAll(allBrokenUnits);
}
}