Create `RegularFileStateValueWithDigest` and `RegularFileStateValueWithContentsProxy` to replace `RegularFileStateValue` to reduce memory overhead.

The `digest` and `contentsProxy` fields are mutually exclusive in `RegularFileStateValue` class so we can refactor `RegularFileStateValue` into two classes, one of which stores `digest` and the other stores `contentsProxy`.

PiperOrigin-RevId: 516938364
Change-Id: I56d473fbbe5f117c47a8953310b0edc2267571df
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 ad69283..3dc808c 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
@@ -13,10 +13,12 @@
 // limitations under the License.
 package com.google.devtools.build.lib.skyframe;
 
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
 import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Preconditions;
 import com.google.common.base.Verify;
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.actions.Artifact;
@@ -24,7 +26,8 @@
 import com.google.devtools.build.lib.actions.FileArtifactValue;
 import com.google.devtools.build.lib.actions.FileStateType;
 import com.google.devtools.build.lib.actions.FileStateValue;
-import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValue;
+import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValueWithContentsProxy;
+import com.google.devtools.build.lib.actions.FileStateValue.RegularFileStateValueWithDigest;
 import com.google.devtools.build.lib.actions.FileValue;
 import com.google.devtools.build.lib.actions.HasDigest;
 import com.google.devtools.build.lib.collect.nestedset.NestedSet;
@@ -105,14 +108,15 @@
       SYMLINK_CYCLE_OR_INFINITE_EXPANSION,
     }
 
-    private final Type type;
+    private final RecursiveFilesystemTraversalException.Type type;
 
-    RecursiveFilesystemTraversalException(String message, Type type) {
+    RecursiveFilesystemTraversalException(
+        String message, RecursiveFilesystemTraversalException.Type type) {
       super(message);
       this.type = type;
     }
 
-    public Type getType() {
+    public RecursiveFilesystemTraversalException.Type getType() {
       return type;
     }
   }
@@ -129,9 +133,9 @@
       super(
           String.format(
               "Found dangling symlink: %s, unresolved path: \"%s\"", path, unresolvedLink),
-          Type.DANGLING_SYMLINK);
-      Preconditions.checkArgument(path != null && !path.isEmpty());
-      Preconditions.checkArgument(unresolvedLink != null && !unresolvedLink.isEmpty());
+          RecursiveFilesystemTraversalException.Type.DANGLING_SYMLINK);
+      checkArgument(path != null && !path.isEmpty());
+      checkArgument(unresolvedLink != null && !unresolvedLink.isEmpty());
     }
   }
 
@@ -288,8 +292,8 @@
         HasDigest metadata,
         @Nullable RootedPath realPath,
         @Nullable PathFragment unresolvedSymlinkTarget) {
-      Preconditions.checkNotNull(metadata.getDigest(), metadata);
-      this.type = Preconditions.checkNotNull(type);
+      checkNotNull(metadata.getDigest(), metadata);
+      this.type = checkNotNull(type);
       this.metadata = metadata;
       this.realPath = realPath;
       this.unresolvedSymlinkTarget = unresolvedSymlinkTarget;
@@ -330,7 +334,7 @@
       }
       RootedPath realPath = traversal.root().asRootedPath();
       if (traversal.strictOutputFiles()) {
-        Preconditions.checkNotNull(fsVal, "Strict Fileset output tree has null FileArtifactValue");
+        checkNotNull(fsVal, "Strict Fileset output tree has null FileArtifactValue");
         return new FileInfo(
             fsVal instanceof TreeArtifactValue ? FileType.DIRECTORY : FileType.FILE,
             fsVal,
@@ -451,15 +455,14 @@
       throws IOException {
     if (fsVal instanceof FileStateValue) {
       FileStateValue fsv = (FileStateValue) fsVal;
-      if (fsv instanceof RegularFileStateValue) {
-        RegularFileStateValue rfsv = (RegularFileStateValue) fsv;
-        return rfsv.getDigest() != null
-            // If we have the digest, then simply convert it with the digest value.
-            ? FileArtifactValue.createForVirtualActionInput(rfsv.getDigest(), rfsv.getSize())
-            // Otherwise, create a file FileArtifactValue (RegularFileArtifactValue) based on the
-            // path and size.
-            : FileArtifactValue.createForNormalFileUsingPath(path, rfsv.getSize(), syscallCache);
+      if (fsv instanceof RegularFileStateValueWithDigest) {
+        RegularFileStateValueWithDigest rfsv = (RegularFileStateValueWithDigest) fsv;
+        return FileArtifactValue.createForVirtualActionInput(rfsv.getDigest(), rfsv.getSize());
+      } else if (fsv instanceof RegularFileStateValueWithContentsProxy) {
+        RegularFileStateValueWithContentsProxy rfsv = (RegularFileStateValueWithContentsProxy) fsv;
+        return FileArtifactValue.createForNormalFileUsingPath(path, rfsv.getSize(), syscallCache);
       }
+
       return new HasDigest.ByteStringDigest(fsv.getValueFingerprint());
     } else if (fsVal instanceof FileArtifactValue) {
       FileArtifactValue fav = ((FileArtifactValue) fsVal);
@@ -481,37 +484,38 @@
       CONFLICT, DIRECTORY, PKG
     }
 
-    private final Type type;
+    private final PkgLookupResult.Type type;
     final TraversalRequest traversal;
     final FileInfo rootInfo;
 
     /** Result for a generated directory that conflicts with a source package. */
     static PkgLookupResult conflict(TraversalRequest traversal, FileInfo rootInfo) {
-      return new PkgLookupResult(Type.CONFLICT, traversal, rootInfo);
+      return new PkgLookupResult(PkgLookupResult.Type.CONFLICT, traversal, rootInfo);
     }
 
     /** Result for a source or generated directory (not a package). */
     static PkgLookupResult directory(TraversalRequest traversal, FileInfo rootInfo) {
-      return new PkgLookupResult(Type.DIRECTORY, traversal, rootInfo);
+      return new PkgLookupResult(PkgLookupResult.Type.DIRECTORY, traversal, rootInfo);
     }
 
     /** Result for a package, i.e. a directory  with a BUILD file. */
     static PkgLookupResult pkg(TraversalRequest traversal, FileInfo rootInfo) {
-      return new PkgLookupResult(Type.PKG, traversal, rootInfo);
+      return new PkgLookupResult(PkgLookupResult.Type.PKG, traversal, rootInfo);
     }
 
-    private PkgLookupResult(Type type, TraversalRequest traversal, FileInfo rootInfo) {
-      this.type = Preconditions.checkNotNull(type);
-      this.traversal = Preconditions.checkNotNull(traversal);
-      this.rootInfo = Preconditions.checkNotNull(rootInfo);
+    private PkgLookupResult(
+        PkgLookupResult.Type type, TraversalRequest traversal, FileInfo rootInfo) {
+      this.type = checkNotNull(type);
+      this.traversal = checkNotNull(traversal);
+      this.rootInfo = checkNotNull(rootInfo);
     }
 
     boolean isPackage() {
-      return type == Type.PKG;
+      return type == PkgLookupResult.Type.PKG;
     }
 
     boolean isConflicting() {
-      return type == Type.CONFLICT;
+      return type == PkgLookupResult.Type.CONFLICT;
     }
 
     @Override
@@ -531,8 +535,8 @@
   private static PkgLookupResult checkIfPackage(
       Environment env, TraversalRequest traversal, FileInfo rootInfo, SyscallCache syscallCache)
       throws IOException, InterruptedException, BuildFileNotFoundException {
-    Preconditions.checkArgument(rootInfo.type.exists() && !rootInfo.type.isFile(),
-        "{%s} {%s}", traversal, rootInfo);
+    checkArgument(
+        rootInfo.type.exists() && !rootInfo.type.isFile(), "{%s} {%s}", traversal, rootInfo);
     // PackageLookupFunction/dependencies can only throw IOException, BuildFileNotFoundException,
     // and RepositoryFetchException, and RepositoryFetchException is not in play here. Note that
     // run-of-the-mill circular symlinks will *not* throw here, and will trigger later errors during
@@ -579,8 +583,7 @@
    */
   private static RecursiveFilesystemTraversalValue resultForDanglingSymlink(
       RootedPath linkName, FileInfo info) {
-    Preconditions.checkState(info.type.isSymlink() && !info.type.exists(), "{%s} {%s}", linkName,
-        info.type);
+    checkState(info.type.isSymlink() && !info.type.exists(), "{%s} {%s}", linkName, info.type);
     return RecursiveFilesystemTraversalValue.of(
         ResolvedFileFactory.danglingSymlink(linkName, info.unresolvedSymlinkTarget, info.metadata));
   }
@@ -593,8 +596,7 @@
    */
   private static RecursiveFilesystemTraversalValue resultForFileRoot(
       RootedPath path, FileInfo info) {
-    Preconditions.checkState(
-        info.type.isFile() && info.type.exists(), "{%s} {%s}", path, info.type);
+    checkState(info.type.isFile() && info.type.exists(), "{%s} {%s}", path, info.type);
     if (info.type.isSymlink()) {
       return RecursiveFilesystemTraversalValue.of(
           ResolvedFileFactory.symlinkToFile(