SourceArtifacts are interned on deserialization using an ArtifactFactory.  This should reduce memory consumption in NestedSet deserialization, which currently does not recycle Artifact instances.

PiperOrigin-RevId: 194083901
diff --git a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
index ccec350..abbd347 100644
--- a/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
+++ b/src/test/java/com/google/devtools/build/lib/actions/ArtifactTest.java
@@ -17,14 +17,19 @@
 import static org.junit.Assert.fail;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.devtools.build.lib.actions.ActionAnalysisMetadata.MiddlemanType;
+import com.google.devtools.build.lib.actions.Artifact.SourceArtifact;
+import com.google.devtools.build.lib.actions.ArtifactResolver.ArtifactResolverSupplier;
 import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
 import com.google.devtools.build.lib.actions.util.LabelArtifactOwner;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.rules.cpp.CppFileTypes;
 import com.google.devtools.build.lib.rules.java.JavaSemantics;
+import com.google.devtools.build.lib.skyframe.serialization.AutoRegistry;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodecs;
 import com.google.devtools.build.lib.skyframe.serialization.testutils.SerializationTester;
 import com.google.devtools.build.lib.testutil.MoreAsserts;
 import com.google.devtools.build.lib.testutil.Scratch;
@@ -320,6 +325,43 @@
   }
 
   @Test
+  public void testCodecRecyclesSourceArtifactInstances() throws Exception {
+    Root root = Root.fromPath(scratch.dir("/"));
+    ArtifactRoot artifactRoot = ArtifactRoot.asSourceRoot(root);
+    ArtifactFactory artifactFactory = new ArtifactFactory(execDir, "blaze-out");
+    artifactFactory.setSourceArtifactRoots(ImmutableMap.of(root, artifactRoot));
+    ArtifactResolverSupplier artifactResolverSupplierForTest = () -> artifactFactory;
+
+    OutputBaseSupplier outputBaseSupplier = () -> scratch.getFileSystem().getPath("/");
+    ObjectCodecs objectCodecs =
+        new ObjectCodecs(
+            AutoRegistry.get()
+                .getBuilder()
+                .addReferenceConstant(scratch.getFileSystem())
+                .setAllowDefaultCodec(true)
+                .build(),
+            ImmutableMap.of(
+                FileSystem.class, scratch.getFileSystem(),
+                OutputBaseSupplier.class, outputBaseSupplier,
+                ArtifactResolverSupplier.class, artifactResolverSupplierForTest));
+
+    PathFragment pathFragment = PathFragment.create("src/foo.cc");
+    ArtifactOwner owner = new LabelArtifactOwner(Label.parseAbsoluteUnchecked("//foo:bar"));
+    SourceArtifact sourceArtifact = new SourceArtifact(artifactRoot, pathFragment, owner);
+    SourceArtifact deserialized1 =
+        (SourceArtifact) objectCodecs.deserialize(objectCodecs.serialize(sourceArtifact));
+    SourceArtifact deserialized2 =
+        (SourceArtifact) objectCodecs.deserialize(objectCodecs.serialize(sourceArtifact));
+    assertThat(deserialized1).isSameAs(deserialized2);
+
+    Artifact sourceArtifactFromFactory =
+        artifactFactory.getSourceArtifact(pathFragment, root, owner);
+    Artifact deserialized =
+        (Artifact) objectCodecs.deserialize(objectCodecs.serialize(sourceArtifactFromFactory));
+    assertThat(sourceArtifactFromFactory).isSameAs(deserialized);
+  }
+
+  @Test
   public void testLongDirname() throws Exception {
     String dirName = createDirNameArtifact().getDirname();