Allow FilesystemValueChecker to operate on a WalkableGraph and add TODOs for replacing MemoizingEvaluator#getValues et al with WalkableGraph usage. I initially attempted to do this but punted once I realized how much work it would be.

Also make DirectoryListingStateValue and FileStateValue public for use in outside callers of FilesystemValueChecker.

--
MOS_MIGRATED_REVID=107447425
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateValue.java
index 15abaf4..7a422e5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingStateValue.java
@@ -39,7 +39,7 @@
  *
  * <p>This class is an implementation detail of {@link DirectoryListingValue}.
  */
-final class DirectoryListingStateValue implements SkyValue {
+public final class DirectoryListingStateValue implements SkyValue {
 
   private final CompactSortedDirents compactSortedDirents;
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
index b774c58..28d4808 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileStateValue.java
@@ -58,7 +58,8 @@
   public static final NonexistentFileStateValue NONEXISTENT_FILE_STATE_NODE =
       new NonexistentFileStateValue();
 
-  enum Type {
+  /** Type of a path. */
+  public enum Type {
     REGULAR_FILE,
     SPECIAL_FILE,
     DIRECTORY,
@@ -69,7 +70,7 @@
   protected FileStateValue() {
   }
 
-  static FileStateValue create(RootedPath rootedPath,
+  public static FileStateValue create(RootedPath rootedPath,
       @Nullable TimestampGranularityMonitor tsgm) throws InconsistentFilesystemException,
       IOException {
     Path path = rootedPath.asPath();
@@ -105,7 +106,7 @@
     return new SkyKey(SkyFunctions.FILE_STATE, rootedPath);
   }
 
-  abstract Type getType();
+  public abstract Type getType();
 
   PathFragment getSymlinkTarget() {
     throw new IllegalStateException();
@@ -193,7 +194,7 @@
     }
 
     @Override
-    Type getType() {
+    public Type getType() {
       return Type.REGULAR_FILE;
     }
 
@@ -261,7 +262,7 @@
     }
 
     @Override
-    Type getType() {
+    public Type getType() {
       return Type.SPECIAL_FILE;
     }
 
@@ -307,7 +308,7 @@
     }
 
     @Override
-    Type getType() {
+    public Type getType() {
       return Type.DIRECTORY;
     }
 
@@ -338,7 +339,7 @@
     }
 
     @Override
-    Type getType() {
+    public Type getType() {
       return Type.SYMLINK;
     }
 
@@ -374,7 +375,7 @@
     }
 
     @Override
-    Type getType() {
+    public Type getType() {
       return Type.NONEXISTENT;
     }
 
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FilesystemValueChecker.java b/src/main/java/com/google/devtools/build/lib/skyframe/FilesystemValueChecker.java
index 4c7efc3..0e8a5cf 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FilesystemValueChecker.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FilesystemValueChecker.java
@@ -15,8 +15,6 @@
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
-import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Range;
@@ -35,15 +33,16 @@
 import com.google.devtools.build.lib.vfs.BatchStat;
 import com.google.devtools.build.lib.vfs.FileStatusWithDigest;
 import com.google.devtools.build.skyframe.Differencer;
-import com.google.devtools.build.skyframe.MemoizingEvaluator;
 import com.google.devtools.build.skyframe.SkyFunctionName;
 import com.google.devtools.build.skyframe.SkyKey;
 import com.google.devtools.build.skyframe.SkyValue;
+import com.google.devtools.build.skyframe.WalkableGraph;
 
 import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -61,7 +60,7 @@
  * A helper class to find dirty values by accessing the filesystem directly (contrast with
  * {@link DiffAwareness}).
  */
-class FilesystemValueChecker {
+public class FilesystemValueChecker {
 
   private static final int DIRTINESS_CHECK_THREADS = 200;
   private static final Logger LOG = Logger.getLogger(FilesystemValueChecker.class.getName());
@@ -72,70 +71,102 @@
   private final TimestampGranularityMonitor tsgm;
   @Nullable
   private final Range<Long> lastExecutionTimeRange;
-  private final Supplier<Map<SkyKey, SkyValue>> valuesSupplier;
   private AtomicInteger modifiedOutputFilesCounter = new AtomicInteger(0);
   private AtomicInteger modifiedOutputFilesIntraBuildCounter = new AtomicInteger(0);
 
-  FilesystemValueChecker(Supplier<Map<SkyKey, SkyValue>> valuesSupplier,
-      @Nullable TimestampGranularityMonitor tsgm, @Nullable Range<Long> lastExecutionTimeRange) {
-    this.valuesSupplier = valuesSupplier;
+  public FilesystemValueChecker(@Nullable TimestampGranularityMonitor tsgm,
+      @Nullable Range<Long> lastExecutionTimeRange) {
     this.tsgm = tsgm;
     this.lastExecutionTimeRange = lastExecutionTimeRange;
   }
 
-  FilesystemValueChecker(final MemoizingEvaluator evaluator,
-      @Nullable TimestampGranularityMonitor tsgm, @Nullable Range<Long> lastExecutionTimeRange) {
-    this.tsgm = tsgm;
-    this.lastExecutionTimeRange = lastExecutionTimeRange;
-
-    // Construct the full map view of the entire graph at most once ("memoized"), lazily. If
-    // getDirtyFilesystemValues(Iterable<SkyKey>) is called on an empty Iterable, we avoid having
-    // to create the Map of value keys to values. This is useful in the case where the graph
-    // getValues() method could be slow.
-    this.valuesSupplier = Suppliers.memoize(new Supplier<Map<SkyKey, SkyValue>>() {
-      @Override
-      public Map<SkyKey, SkyValue> get() {
-        return evaluator.getValues();
-      }
-    });
-  }
-
   /**
-   * Returns a {@link Differencer.DiffWithDelta} containing keys from the backing graph (of the
-   * {@link MemoizingEvaluator} given at construction time) that are dirty according to the
-   * passed-in {@code dirtinessChecker}.
+   * Returns a {@link Differencer.DiffWithDelta} containing keys from the give map that are dirty
+   * according to the passed-in {@code dirtinessChecker}.
    */
-  Differencer.DiffWithDelta getDirtyKeys(SkyValueDirtinessChecker dirtinessChecker)
-      throws InterruptedException {
-    return getDirtyValues(valuesSupplier.get().keySet(), dirtinessChecker,
-        /*checkMissingValues=*/false);
+  // TODO(bazel-team): Refactor these methods so that FilesystemValueChecker only operates on a
+  // WalkableGraph.
+  Differencer.DiffWithDelta getDirtyKeys(Map<SkyKey, SkyValue> valuesMap,
+      SkyValueDirtinessChecker dirtinessChecker) throws InterruptedException {
+    return getDirtyValues(new MapBackedValueFetcher(valuesMap), valuesMap.keySet(),
+        dirtinessChecker, /*checkMissingValues=*/false);
   }
 
   /**
    * Returns a {@link Differencer.DiffWithDelta} containing keys that are dirty according to the
    * passed-in {@code dirtinessChecker}.
    */
-  Differencer.DiffWithDelta getNewAndOldValues(Iterable<SkyKey> keys,
-      SkyValueDirtinessChecker dirtinessChecker) throws InterruptedException {
-    return getDirtyValues(keys, dirtinessChecker, /*checkMissingValues=*/true);
+  public Differencer.DiffWithDelta getNewAndOldValues(Map<SkyKey, SkyValue> valuesMap,
+      Iterable<SkyKey> keys, SkyValueDirtinessChecker dirtinessChecker)
+          throws InterruptedException {
+    return getDirtyValues(new MapBackedValueFetcher(valuesMap), keys,
+        dirtinessChecker, /*checkMissingValues=*/true);
+  }
+
+  /**
+   * Returns a {@link Differencer.DiffWithDelta} containing keys that are dirty according to the
+   * passed-in {@code dirtinessChecker}.
+   */
+  public Differencer.DiffWithDelta getNewAndOldValues(WalkableGraph walkableGraph,
+      Iterable<SkyKey> keys, SkyValueDirtinessChecker dirtinessChecker)
+          throws InterruptedException {
+    return getDirtyValues(new WalkableGraphBackedValueFetcher(walkableGraph), keys,
+        dirtinessChecker, /*checkMissingValues=*/true);
+  }
+
+  private static interface ValueFetcher {
+    @Nullable
+    SkyValue get(SkyKey key);
+  }
+
+  private static class WalkableGraphBackedValueFetcher implements ValueFetcher {
+    private final WalkableGraph walkableGraph;
+
+    private WalkableGraphBackedValueFetcher(WalkableGraph walkableGraph) {
+      this.walkableGraph = walkableGraph;
+    }
+
+    @Override
+    @Nullable
+    public SkyValue get(SkyKey key) {
+      return walkableGraph.exists(key) ? walkableGraph.getValue(key) : null;
+    }
+  }
+
+  private static class MapBackedValueFetcher implements ValueFetcher {
+    private final Map<SkyKey, SkyValue> valuesMap;
+
+    private MapBackedValueFetcher(Map<SkyKey, SkyValue> valuesMap) {
+      this.valuesMap = valuesMap;
+    }
+
+    @Override
+    @Nullable
+    public SkyValue get(SkyKey key) {
+      return valuesMap.get(key);
+    }
   }
 
   /**
    * Return a collection of action values which have output files that are not in-sync with
    * the on-disk file value (were modified externally).
    */
-  Collection<SkyKey> getDirtyActionValues(@Nullable final BatchStat batchStatter)
-      throws InterruptedException {
+  Collection<SkyKey> getDirtyActionValues(Map<SkyKey, SkyValue> valuesMap,
+      @Nullable final BatchStat batchStatter) throws InterruptedException {
     // CPU-bound (usually) stat() calls, plus a fudge factor.
     LOG.info("Accumulating dirty actions");
     final int numOutputJobs = Runtime.getRuntime().availableProcessors() * 4;
-    final Set<SkyKey> actionSkyKeys =
-        Sets.filter(valuesSupplier.get().keySet(), ACTION_FILTER);
+    final Set<SkyKey> actionSkyKeys = new HashSet<>();
+    for (SkyKey key : valuesMap.keySet()) {
+      if (ACTION_FILTER.apply(key)) {
+        actionSkyKeys.add(key);
+      }
+    }
     final Sharder<Pair<SkyKey, ActionExecutionValue>> outputShards =
         new Sharder<>(numOutputJobs, actionSkyKeys.size());
 
     for (SkyKey key : actionSkyKeys) {
-      outputShards.add(Pair.of(key, (ActionExecutionValue) valuesSupplier.get().get(key)));
+      outputShards.add(Pair.of(key, (ActionExecutionValue) valuesMap.get(key)));
     }
     LOG.info("Sharded action values for batching");
 
@@ -285,8 +316,8 @@
     return isDirty;
   }
 
-  private BatchDirtyResult getDirtyValues(
-      Iterable<SkyKey> values, final SkyValueDirtinessChecker checker,
+  private BatchDirtyResult getDirtyValues(ValueFetcher fetcher,
+      Iterable<SkyKey> keys, final SkyValueDirtinessChecker checker,
       final boolean checkMissingValues) throws InterruptedException {
     ExecutorService executor =
         Executors.newFixedThreadPool(
@@ -310,23 +341,24 @@
         }
     };
     try (AutoProfiler prof = AutoProfiler.create(elapsedTimeReceiver)) {
-      for (final SkyKey key : values) {
+      for (final SkyKey key : keys) {
         numKeysScanned.incrementAndGet();
         if (!checker.applies(key)) {
           continue;
         }
-        final SkyValue value = valuesSupplier.get().get(key);
+        final SkyValue value = fetcher.get(key);
+        if (!checkMissingValues && value == null) {
+          continue;
+        }
         executor.execute(
             wrapper.wrap(
                 new Runnable() {
                   @Override
                   public void run() {
-                    if (value != null || checkMissingValues) {
-                      numKeysChecked.incrementAndGet();
-                      DirtyResult result = checker.check(key, value, tsgm);
-                      if (result.isDirty()) {
-                        batchResult.add(key, value, result.getNewValue());
-                      }
+                    numKeysChecked.incrementAndGet();
+                    DirtyResult result = checker.check(key, value, tsgm);
+                    if (result.isDirty()) {
+                      batchResult.add(key, value, result.getNewValue());
                     }
                   }
                 }));
@@ -342,7 +374,7 @@
   }
 
   /**
-   * Result of a batch call to {@link SkyValueDirtinessChecker#maybeCheck}. Partitions the dirty
+   * Result of a batch call to {@link SkyValueDirtinessChecker#check}. Partitions the dirty
    * values based on whether we have a new value available for them or not.
    */
   private static class BatchDirtyResult implements Differencer.DiffWithDelta {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
index 7a29573..c4b11d2 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SequencedSkyframeExecutor.java
@@ -341,7 +341,7 @@
     buildDriver.evaluate(ImmutableList.<SkyKey>of(), false,
         DEFAULT_THREAD_COUNT, eventHandler);
 
-    FilesystemValueChecker fsvc = new FilesystemValueChecker(memoizingEvaluator, tsgm, null);
+    FilesystemValueChecker fsvc = new FilesystemValueChecker(tsgm, null);
     // We need to manually check for changes to known files. This entails finding all dirty file
     // system values under package roots for which we don't have diff information. If at least
     // one path entry doesn't have diff information, then we're going to have to iterate over
@@ -353,6 +353,7 @@
     }
     Differencer.Diff diff =
         fsvc.getDirtyKeys(
+            memoizingEvaluator.getValues(),
             new UnionDirtinessChecker(
                 Iterables.concat(
                     customDirtinessCheckers,
@@ -444,7 +445,7 @@
     }
     Differencer.Diff diff;
     if (modifiedFileSet.treatEverythingAsModified()) {
-      diff = new FilesystemValueChecker(memoizingEvaluator, tsgm, null).getDirtyKeys(
+      diff = new FilesystemValueChecker(tsgm, null).getDirtyKeys(memoizingEvaluator.getValues(),
           new BasicFilesystemDirtinessChecker());
     } else {
       diff = getDiff(modifiedFileSet.modifiedSourceFiles(), pathEntry);
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
index 28ac669..76ad0e7 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -22,7 +22,6 @@
 import com.google.common.base.Predicates;
 import com.google.common.base.Stopwatch;
 import com.google.common.base.Supplier;
-import com.google.common.base.Suppliers;
 import com.google.common.base.Throwables;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
@@ -779,6 +778,9 @@
 
   protected Differencer.Diff getDiff(Iterable<PathFragment> modifiedSourceFiles,
       final Path pathEntry) throws InterruptedException {
+    if (Iterables.isEmpty(modifiedSourceFiles)) {
+      return new ImmutableDiff(ImmutableList.<SkyKey>of(), ImmutableMap.<SkyKey, SkyValue>of());
+    }
     // TODO(bazel-team): change ModifiedFileSet to work with RootedPaths instead of PathFragments.
     Iterable<SkyKey> dirtyFileStateSkyKeys = Iterables.transform(modifiedSourceFiles,
         new Function<PathFragment, SkyKey>() {
@@ -794,16 +796,10 @@
     // information here, so we compute it ourselves.
     // TODO(bazel-team): Fancy filesystems could provide it with a hypothetically modified
     // DiffAwareness interface.
-    Supplier<Map<SkyKey, SkyValue>> valuesSupplier = Suppliers.memoize(
-        new Supplier<Map<SkyKey, SkyValue>>() {
-      @Override
-      public Map<SkyKey, SkyValue> get() {
-        return memoizingEvaluator.getValues();
-      }
-    });
-    FilesystemValueChecker fsvc = new FilesystemValueChecker(valuesSupplier, tsgm, null);
+    FilesystemValueChecker fsvc = new FilesystemValueChecker(tsgm, null);
+    Map<SkyKey, SkyValue> valuesMap = memoizingEvaluator.getValues();
     Differencer.DiffWithDelta diff =
-        fsvc.getNewAndOldValues(dirtyFileStateSkyKeys, new FileDirtinessChecker());
+        fsvc.getNewAndOldValues(valuesMap, dirtyFileStateSkyKeys, new FileDirtinessChecker());
 
     Set<SkyKey> valuesToInvalidate = new HashSet<>();
     Map<SkyKey, SkyValue> valuesToInject = new HashMap<>();
@@ -830,7 +826,7 @@
         changedType = !oldValue.getType().equals(newValue.getType());
       } else {
         DirectoryListingStateValue oldDirListingStateValue =
-            (DirectoryListingStateValue) valuesSupplier.get().get(dirListingStateKey);
+            (DirectoryListingStateValue) valuesMap.get(dirListingStateKey);
         if (oldDirListingStateValue != null) {
           String baseName = rootedPath.getRelativePath().getBaseName();
           Dirent oldDirent = oldDirListingStateValue.getDirents().maybeGetDirent(baseName);
@@ -858,14 +854,6 @@
     return DirectoryListingStateValue.key(parentDirRootedPath);
   }
 
-  protected static SkyKey createFileStateKey(RootedPath rootedPath) {
-    return FileStateValue.key(rootedPath);
-  }
-
-  protected static SkyKey createDirectoryListingStateKey(RootedPath rootedPath) {
-    return DirectoryListingStateValue.key(rootedPath);
-  }
-
   /**
    * Sets the packages that should be treated as deleted and ignored.
    */
@@ -1653,9 +1641,9 @@
 
     if (checkOutputFiles) {
       // Detect external modifications in the output tree.
-      FilesystemValueChecker fsvc = new FilesystemValueChecker(memoizingEvaluator, tsgm,
-          lastExecutionTimeRange);
-      invalidateDirtyActions(fsvc.getDirtyActionValues(batchStatter));
+      FilesystemValueChecker fsvc = new FilesystemValueChecker(tsgm, lastExecutionTimeRange);
+      invalidateDirtyActions(fsvc.getDirtyActionValues(memoizingEvaluator.getValues(),
+          batchStatter));
       modifiedFiles += fsvc.getNumberOfModifiedOutputFiles();
       outputDirtyFiles += fsvc.getNumberOfModifiedOutputFiles();
       modifiedFilesDuringPreviousBuild += fsvc.getNumberOfModifiedOutputFilesDuringPreviousBuild();
diff --git a/src/main/java/com/google/devtools/build/skyframe/Differencer.java b/src/main/java/com/google/devtools/build/skyframe/Differencer.java
index f30558c..ddcb34f 100644
--- a/src/main/java/com/google/devtools/build/skyframe/Differencer.java
+++ b/src/main/java/com/google/devtools/build/skyframe/Differencer.java
@@ -92,5 +92,6 @@
   /**
    * Returns the value keys that have changed between the two Versions.
    */
-  Diff getDiff(Version fromVersion, Version toVersion) throws InterruptedException;
+  Diff getDiff(WalkableGraph fromGraph, Version fromVersion, Version toVersion)
+      throws InterruptedException;
 }
diff --git a/src/main/java/com/google/devtools/build/skyframe/InMemoryMemoizingEvaluator.java b/src/main/java/com/google/devtools/build/skyframe/InMemoryMemoizingEvaluator.java
index e35cc54..a16ff87 100644
--- a/src/main/java/com/google/devtools/build/skyframe/InMemoryMemoizingEvaluator.java
+++ b/src/main/java/com/google/devtools/build/skyframe/InMemoryMemoizingEvaluator.java
@@ -153,7 +153,8 @@
       // It clears the internal data structures after getDiff is called and will not return
       // diffs for historical versions. This makes the following code sensitive to interrupts.
       // Ideally we would simply not update lastGraphVersion if an interrupt occurs.
-      Diff diff = differencer.getDiff(lastGraphVersion, version);
+      Diff diff = differencer.getDiff(new DelegatingWalkableGraph(graph), lastGraphVersion,
+          version);
       valuesToInject.putAll(diff.changedKeysWithNewValues());
       invalidate(diff.changedKeysWithoutNewValues());
       pruneInjectedValues(valuesToInject);
diff --git a/src/main/java/com/google/devtools/build/skyframe/MemoizingEvaluator.java b/src/main/java/com/google/devtools/build/skyframe/MemoizingEvaluator.java
index e54dc8d..f00d88f 100644
--- a/src/main/java/com/google/devtools/build/skyframe/MemoizingEvaluator.java
+++ b/src/main/java/com/google/devtools/build/skyframe/MemoizingEvaluator.java
@@ -86,9 +86,11 @@
    *
    * <p>The returned map may be a live view of the graph.
    */
+  // TODO(bazel-team): Replace all usages of getValues, getDoneValues, getExistingValueForTesting,
+  // and getExistingErrorForTesting with usages of WalkableGraph. Changing the getValues usages
+  // require some care because getValues gives access to the previous value for changed/dirty nodes.
   Map<SkyKey, SkyValue> getValues();
 
-
   /**
    * Returns the done (without error) values in the graph.
    *
diff --git a/src/main/java/com/google/devtools/build/skyframe/RecordingDifferencer.java b/src/main/java/com/google/devtools/build/skyframe/RecordingDifferencer.java
index 1582e52..4d25e0e6 100644
--- a/src/main/java/com/google/devtools/build/skyframe/RecordingDifferencer.java
+++ b/src/main/java/com/google/devtools/build/skyframe/RecordingDifferencer.java
@@ -41,7 +41,7 @@
   }
 
   @Override
-  public Diff getDiff(Version fromVersion, Version toVersion) {
+  public Diff getDiff(WalkableGraph fromGraph, Version fromVersion, Version toVersion) {
     Diff diff = new ImmutableDiff(valuesToInvalidate, valuesToInject);
     clear();
     return diff;
diff --git a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
index 430c5bf..2aed79a 100644
--- a/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skyframe/FilesystemValueCheckerTest.java
@@ -110,17 +110,17 @@
 
   @Test
   public void testEmpty() throws Exception {
-    FilesystemValueChecker checker = new FilesystemValueChecker(evaluator, tsgm, null);
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    FilesystemValueChecker checker = new FilesystemValueChecker(tsgm, null);
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
   }
 
   @Test
   public void testSimple() throws Exception {
-    FilesystemValueChecker checker = new FilesystemValueChecker(evaluator, tsgm, null);
+    FilesystemValueChecker checker = new FilesystemValueChecker(tsgm, null);
 
     Path path = fs.getPath("/foo");
     FileSystemUtils.createEmptyFile(path);
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
 
     SkyKey skyKey =
         FileStateValue.key(RootedPath.toRootedPath(fs.getRootDirectory(), new PathFragment("foo")));
@@ -132,13 +132,13 @@
             NullEventHandler.INSTANCE);
     assertFalse(result.hasError());
 
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
 
     FileSystemUtils.writeContentAsLatin1(path, "hello");
-    assertDiffWithNewValues(getDirtyFilesystemKeys(checker), skyKey);
+    assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
 
     // The dirty bits are not reset until the FileValues are actually revalidated.
-    assertDiffWithNewValues(getDirtyFilesystemKeys(checker), skyKey);
+    assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), skyKey);
 
     differencer.invalidate(ImmutableList.of(skyKey));
     result =
@@ -148,7 +148,7 @@
             SkyframeExecutor.DEFAULT_THREAD_COUNT,
             NullEventHandler.INSTANCE);
     assertFalse(result.hasError());
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
   }
 
   /**
@@ -160,7 +160,7 @@
    */
   @Test
   public void testDirtySymlink() throws Exception {
-    FilesystemValueChecker checker = new FilesystemValueChecker(evaluator, tsgm, null);
+    FilesystemValueChecker checker = new FilesystemValueChecker(tsgm, null);
 
     Path path = fs.getPath("/foo");
     FileSystemUtils.writeContentAsLatin1(path, "foo contents");
@@ -194,12 +194,12 @@
     assertTrue(symlinkValue.toString(), symlinkValue.isSymlink());
     // Digest is not always available, so use size as a proxy for contents.
     assertEquals(fooValue.getSize(), symlinkValue.getSize());
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
 
     // Before second build, move sym1 to point to sym2.
     assertTrue(sym1.delete());
     FileSystemUtils.ensureSymbolicLink(sym1, sym2);
-    assertDiffWithNewValues(getDirtyFilesystemKeys(checker), sym1FileStateKey);
+    assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
 
     differencer.invalidate(ImmutableList.of(sym1FileStateKey));
     result =
@@ -209,7 +209,7 @@
             SkyframeExecutor.DEFAULT_THREAD_COUNT,
             NullEventHandler.INSTANCE);
     assertFalse(result.hasError());
-    assertDiffWithNewValues(getDirtyFilesystemKeys(checker), sym1FileStateKey);
+    assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), sym1FileStateKey);
 
     // Before third build, move sym1 back to original (so change pruning will prevent signaling of
     // its parents, but change symlink for real.
@@ -217,7 +217,7 @@
     FileSystemUtils.ensureSymbolicLink(sym1, path);
     assertTrue(symlink.delete());
     FileSystemUtils.writeContentAsLatin1(symlink, "new symlink contents");
-    assertDiffWithNewValues(getDirtyFilesystemKeys(checker), symlinkFileStateKey);
+    assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), symlinkFileStateKey);
     differencer.invalidate(ImmutableList.of(symlinkFileStateKey));
     result =
         driver.evaluate(
@@ -227,18 +227,18 @@
     assertFalse(symlinkValue.toString(), symlinkValue.isSymlink());
     assertEquals(fooValue, result.get(fooKey));
     assertThat(symlinkValue.getSize()).isNotEqualTo(fooValue.getSize());
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
   }
 
   @Test
   public void testExplicitFiles() throws Exception {
-    FilesystemValueChecker checker = new FilesystemValueChecker(evaluator, tsgm, null);
+    FilesystemValueChecker checker = new FilesystemValueChecker(tsgm, null);
 
     Path path1 = fs.getPath("/foo1");
     Path path2 = fs.getPath("/foo2");
     FileSystemUtils.createEmptyFile(path1);
     FileSystemUtils.createEmptyFile(path2);
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
 
     SkyKey key1 =
         FileStateValue.key(
@@ -252,20 +252,20 @@
             skyKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
     assertFalse(result.hasError());
 
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
 
     FileSystemUtils.writeContentAsLatin1(path1, "hello1");
     FileSystemUtils.writeContentAsLatin1(path1, "hello2");
     path1.setLastModifiedTime(27);
     path2.setLastModifiedTime(42);
-    assertDiffWithNewValues(getDirtyFilesystemKeys(checker), key1, key2);
+    assertDiffWithNewValues(getDirtyFilesystemKeys(evaluator, checker), key1, key2);
 
     differencer.invalidate(skyKeys);
     result =
         driver.evaluate(
             skyKeys, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
     assertFalse(result.hasError());
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
   }
 
   @Test
@@ -285,8 +285,8 @@
     assertTrue(result.hasError());
 
     fs.readlinkThrowsIoException = false;
-    FilesystemValueChecker checker = new FilesystemValueChecker(evaluator, tsgm, null);
-    Diff diff = getDirtyFilesystemKeys(checker);
+    FilesystemValueChecker checker = new FilesystemValueChecker(tsgm, null);
+    Diff diff = getDirtyFilesystemKeys(evaluator, checker);
     assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
     assertThat(diff.changedKeysWithNewValues()).isEmpty();
   }
@@ -309,8 +309,8 @@
             NullEventHandler.INSTANCE);
     assertTrue(result.hasError());
 
-    FilesystemValueChecker checker = new FilesystemValueChecker(evaluator, tsgm, null);
-    Diff diff = getDirtyFilesystemKeys(checker);
+    FilesystemValueChecker checker = new FilesystemValueChecker(tsgm, null);
+    Diff diff = getDirtyFilesystemKeys(evaluator, checker);
     assertThat(diff.changedKeysWithoutNewValues()).isEmpty();
     assertThat(diff.changedKeysWithNewValues()).isEmpty();
   }
@@ -336,14 +336,15 @@
         driver
             .evaluate(ImmutableList.<SkyKey>of(), false, 1, NullEventHandler.INSTANCE)
             .hasError());
-    assertThat(new FilesystemValueChecker(evaluator, tsgm, null).getDirtyActionValues(batchStatter))
-        .isEmpty();
+    assertThat(new FilesystemValueChecker(tsgm, null).getDirtyActionValues(evaluator.getValues(),
+        batchStatter)).isEmpty();
 
     FileSystemUtils.writeContentAsLatin1(out1.getPath(), "goodbye");
     assertEquals(
         ActionExecutionValue.key(action1),
         Iterables.getOnlyElement(
-            new FilesystemValueChecker(evaluator, tsgm, null).getDirtyActionValues(batchStatter)));
+            new FilesystemValueChecker(tsgm, null).getDirtyActionValues(evaluator.getValues(),
+                batchStatter)));
   }
 
   private Artifact createDerivedArtifact(String relPath) throws IOException {
@@ -432,13 +433,13 @@
         ImmutableList.of(FileValue.key(RootedPath.toRootedPath(pkgRoot, new PathFragment("foo"))));
     driver.evaluate(
         values, false, SkyframeExecutor.DEFAULT_THREAD_COUNT, NullEventHandler.INSTANCE);
-    FilesystemValueChecker checker = new FilesystemValueChecker(evaluator, tsgm, null);
+    FilesystemValueChecker checker = new FilesystemValueChecker(tsgm, null);
 
-    assertEmptyDiff(getDirtyFilesystemKeys(checker));
+    assertEmptyDiff(getDirtyFilesystemKeys(evaluator, checker));
 
     fs.statThrowsRuntimeException = true;
     try {
-      getDirtyFilesystemKeys(checker);
+      getDirtyFilesystemKeys(evaluator, checker);
       fail();
     } catch (RuntimeException e) {
       assertThat(e).hasMessage("bork");
@@ -531,8 +532,8 @@
     };
   }
 
-  private static Diff getDirtyFilesystemKeys(FilesystemValueChecker checker)
-      throws InterruptedException {
-    return checker.getDirtyKeys(new BasicFilesystemDirtinessChecker());
+  private static Diff getDirtyFilesystemKeys(MemoizingEvaluator evaluator,
+      FilesystemValueChecker checker) throws InterruptedException {
+    return checker.getDirtyKeys(evaluator.getValues(), new BasicFilesystemDirtinessChecker());
   }
 }