Optimize memory usage of `RegularFileValue`... by removing it.

`RegularFileValue` (i.e. the path's real path is itself, and it's an existing file) is the common case situation for `FileValue`. In this situation we don't actually need to spend shallow heap to store a reference to the original `RootedPath` in order to service `FileValue#realRootedPath` and `FileValue#logicalChainDuringResolution`; instead we refactor all callers to pass in the original `RootedPath`. After making this change we notice that `RegularFileValue` is now just a wrapper around `FileStateValue`, so then we remove the need for the wrapper by turning `RegularFileValue` into an abstract class (an interface would also work), and then having `FileStateValue` extend it.

PiperOrigin-RevId: 655774211
Change-Id: Icf1684c85c605a267a85c7917bb53dffaeb85fd7
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingFunction.java
index eb01dcd..b31c0fa 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingFunction.java
@@ -38,7 +38,7 @@
       return null;
     }
 
-    RootedPath realDirRootedPath = dirFileValue.realRootedPath();
+    RootedPath realDirRootedPath = dirFileValue.realRootedPath(dirRootedPath);
     if (!dirFileValue.isDirectory()) {
       // Recall that the directory is assumed to exist (see DirectoryListingValue#key).
       throw new DirectoryListingFunctionException(new InconsistentFilesystemException(
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingValue.java b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingValue.java
index d9e9f91..02da3e5 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingValue.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryListingValue.java
@@ -95,10 +95,11 @@
 
   static DirectoryListingValue value(RootedPath dirRootedPath, FileValue dirFileValue,
       DirectoryListingStateValue realDirectoryListingStateValue) {
-    return dirFileValue.realRootedPath().equals(dirRootedPath)
+    RootedPath realRootedPath = dirFileValue.realRootedPath(dirRootedPath);
+    return realRootedPath.equals(dirRootedPath)
         ? new RegularDirectoryListingValue(realDirectoryListingStateValue)
-        : new DifferentRealPathDirectoryListingValue(dirFileValue.realRootedPath(),
-            realDirectoryListingStateValue);
+        : new DifferentRealPathDirectoryListingValue(
+            realRootedPath, realDirectoryListingStateValue);
   }
 
   /** Normal {@link DirectoryListingValue}. */
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryTreeDigestFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryTreeDigestFunction.java
index ae23c37..80ad4ab 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryTreeDigestFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/DirectoryTreeDigestFunction.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.devtools.build.lib.actions.FileValue;
 import com.google.devtools.build.lib.util.Fingerprint;
+import com.google.devtools.build.lib.util.Pair;
 import com.google.devtools.build.lib.vfs.Dirent;
 import com.google.devtools.build.lib.vfs.RootedPath;
 import com.google.devtools.build.skyframe.SkyFunction;
@@ -53,7 +54,8 @@
             .collect(toImmutableSet());
 
     // Turn each entry into a FileValue.
-    ImmutableList<FileValue> fileValues = getFileValues(env, sortedDirents, rootedPath);
+    ImmutableList<Pair<RootedPath, FileValue>> fileValues =
+        getFileValues(env, sortedDirents, rootedPath);
     if (fileValues == null) {
       return null;
     }
@@ -70,13 +72,15 @@
     fp.addStrings(sortedDirents);
     fp.addStrings(subDirTreeDigests);
     try {
-      for (FileValue fileValue : fileValues) {
+      for (Pair<RootedPath, FileValue> rootedPathAndFileValue : fileValues) {
+        RootedPath direntRootedPath = rootedPathAndFileValue.getFirst();
+        FileValue fileValue = rootedPathAndFileValue.getSecond();
         fp.addInt(fileValue.realFileStateValue().getType().ordinal());
         if (fileValue.isFile()) {
           byte[] digest = fileValue.realFileStateValue().getDigest();
           if (digest == null) {
             // Fast digest not available, or it would have been in the FileValue.
-            digest = fileValue.realRootedPath().asPath().getDigest();
+            digest = fileValue.realRootedPath(direntRootedPath).asPath().getDigest();
           }
           fp.addBytes(digest);
         }
@@ -89,7 +93,7 @@
   }
 
   @Nullable
-  private static ImmutableList<FileValue> getFileValues(
+  private static ImmutableList<Pair<RootedPath, FileValue>> getFileValues(
       Environment env, ImmutableSet<String> sortedDirents, RootedPath rootedPath)
       throws InterruptedException {
     ImmutableSet<FileValue.Key> fileValueKeys =
@@ -105,10 +109,9 @@
     if (env.valuesMissing()) {
       return null;
     }
-    ImmutableList<FileValue> fileValues =
+    ImmutableList<Pair<RootedPath, FileValue>> fileValues =
         fileValueKeys.stream()
-            .map(result::get)
-            .map(FileValue.class::cast)
+            .map(k -> Pair.of((RootedPath) k.argument(), (FileValue) result.get(k)))
             .collect(toImmutableList());
     if (env.valuesMissing()) {
       return null;
@@ -118,11 +121,12 @@
 
   @Nullable
   private static ImmutableList<String> getSubDirTreeDigests(
-      Environment env, ImmutableList<FileValue> fileValues) throws InterruptedException {
+      Environment env, ImmutableList<Pair<RootedPath, FileValue>> fileValues)
+      throws InterruptedException {
     ImmutableSet<SkyKey> dirTreeDigestValueKeys =
         fileValues.stream()
-            .filter(FileValue::isDirectory)
-            .map(fv -> DirectoryTreeDigestValue.key(fv.realRootedPath()))
+            .filter(p -> p.getSecond().isDirectory())
+            .map(p -> DirectoryTreeDigestValue.key(p.getSecond().realRootedPath(p.getFirst())))
             .collect(toImmutableSet());
     SkyframeLookupResult result = env.getValuesAndExceptions(dirTreeDigestValueKeys);
     if (env.valuesMissing()) {
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
index 4d7d97d..1439311 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/FileFunction.java
@@ -196,14 +196,19 @@
     }
 
     RootedPath rootedPathFromAncestors =
-        getChild(parentFileValue.realRootedPath(), baseName, parentRootedPath, rootedPath);
+        getChild(
+            parentFileValue.realRootedPath(parentRootedPath),
+            baseName,
+            parentRootedPath,
+            rootedPath);
 
     if (!parentFileValue.exists() || !parentFileValue.isDirectory()) {
       return new PartialResolutionResult(
           rootedPathFromAncestors, FileStateValue.NONEXISTENT_FILE_STATE_NODE);
     }
 
-    for (RootedPath parentPartialRootedPath : parentFileValue.logicalChainDuringResolution()) {
+    for (RootedPath parentPartialRootedPath :
+        parentFileValue.logicalChainDuringResolution(parentRootedPath)) {
       checkAndNotePathSeenDuringPartialResolution(
           getChild(parentPartialRootedPath, baseName, parentRootedPath, rootedPath),
           symlinkResolutionState,
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
index 0151bc0..6aec2b6 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/RecursiveFilesystemTraversalFunction.java
@@ -386,21 +386,24 @@
       }
     } else {
       // Stat the file.
+      RootedPath rootedPath = traversal.root().asRootedPath();
       FileValue fileValue =
-          (FileValue)
-              env.getValueOrThrow(
-                  FileValue.key(traversal.root().asRootedPath()), IOException.class);
+          (FileValue) env.getValueOrThrow(FileValue.key(rootedPath), IOException.class);
 
       if (env.valuesMissing()) {
         return null;
       }
-      return toFileInfo(fileValue, env, traversal.root().asPath(), syscallCache);
+      return toFileInfo(rootedPath, fileValue, env, traversal.root().asPath(), syscallCache);
     }
   }
 
   @Nullable
   private static FileInfo toFileInfo(
-      FileValue fileValue, Environment env, Path path, SyscallCache syscallCache)
+      RootedPath rootedPath,
+      FileValue fileValue,
+      Environment env,
+      Path path,
+      SyscallCache syscallCache)
       throws IOException, InterruptedException {
     if (fileValue.unboundedAncestorSymlinkExpansionChain() != null) {
       SkyKey uniquenessKey =
@@ -437,7 +440,7 @@
     return new FileInfo(
         type,
         withDigest(fileValue.realFileStateValue(), path, syscallCache),
-        fileValue.realRootedPath(),
+        fileValue.realRootedPath(rootedPath),
         unresolvedLinkTarget);
   }
 
@@ -715,7 +718,12 @@
       }
       if (key instanceof FileValue.Key fileKey) {
         FileInfo fileInfo =
-            toFileInfo((FileValue) value, env, fileKey.argument().asPath(), syscallCache);
+            toFileInfo(
+                fileKey.argument(),
+                (FileValue) value,
+                env,
+                fileKey.argument().asPath(),
+                syscallCache);
         if (fileInfo != null) {
           childValues.add(resultForFileRoot(fileKey.argument(), fileInfo));
         }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/config/PlatformMappingFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/config/PlatformMappingFunction.java
index fd7f68a..f27aa6c 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/config/PlatformMappingFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/config/PlatformMappingFunction.java
@@ -111,7 +111,8 @@
 
       List<String> lines;
       try {
-        lines = FileSystemUtils.readLines(fileValue.realRootedPath().asPath(), UTF_8);
+        lines =
+            FileSystemUtils.readLines(fileValue.realRootedPath(rootedMappingPath).asPath(), UTF_8);
       } catch (IOException e) {
         throw new PlatformMappingFunctionException(e);
       }