Add a method in MetadataHandler to retrieve contained artifacts from TreeArtifacts.

--
MOS_MIGRATED_REVID=135485914
diff --git a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
index 5e74180..f7a2706 100644
--- a/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/actions/cache/MetadataHandler.java
@@ -26,21 +26,19 @@
    * Returns metadata for the given artifact or null if it does not exist or is intentionally
    * omitted.
    *
-   * <p>This should always be used for the inputs to {@link MiddlemanAction}s instead of
-   * {@link #getMetadata(Artifact)} since we may allow non-existent inputs to middlemen.</p>
+   * <p>This should always be used for the inputs to {@link MiddlemanAction}s instead of {@link
+   * #getMetadata(Artifact)} since we may allow non-existent inputs to middlemen.
    *
    * @param artifact artifact
-   *
    * @return metadata instance or null if metadata cannot be obtained.
    */
   Metadata getMetadataMaybe(Artifact artifact);
 
   /**
-   * Returns metadata for the given artifact or throws an exception if the
-   * metadata could not be obtained.
+   * Returns metadata for the given artifact or throws an exception if the metadata could not be
+   * obtained.
    *
    * @return metadata instance
-   *
    * @throws IOException if metadata could not be obtained.
    */
   Metadata getMetadata(Artifact artifact) throws IOException;
@@ -49,28 +47,31 @@
   void setDigestForVirtualArtifact(Artifact artifact, Md5Digest md5Digest);
 
   /**
-   * Registers the given output as contents of a TreeArtifact, without injecting its digest.
-   * Prefer {@link #injectDigest} when the digest is available.
+   * Registers the given output as contents of a TreeArtifact, without injecting its digest. Prefer
+   * {@link #injectDigest} when the digest is available.
    */
   void addExpandedTreeOutput(TreeFileArtifact output);
 
+  /** Retrieves the artifacts inside the TreeArtifact, without injecting its digest. */
+  Iterable<TreeFileArtifact> getExpandedOutputs(Artifact artifact);
+
   /**
    * Injects provided digest into the metadata handler, simultaneously caching lstat() data as well.
    */
   void injectDigest(ActionInput output, FileStatus statNoFollow, byte[] digest);
 
   /**
-   * Marks an artifact as intentionally omitted. Acknowledges that this Artifact could have
-   * existed, but was intentionally not saved, most likely as an optimization.
+   * Marks an artifact as intentionally omitted. Acknowledges that this Artifact could have existed,
+   * but was intentionally not saved, most likely as an optimization.
    */
   void markOmitted(ActionInput output);
 
   /**
    * Returns true iff artifact exists.
    *
-   * <p>It is important to note that implementations may cache non-existence as a side effect
-   * of this method. If there is a possibility an artifact was intentionally omitted then
-   * {@link #artifactOmitted(Artifact)} should be checked first to avoid the side effect.</p>
+   * <p>It is important to note that implementations may cache non-existence as a side effect of
+   * this method. If there is a possibility an artifact was intentionally omitted then {@link
+   * #artifactOmitted(Artifact)} should be checked first to avoid the side effect.
    */
   boolean artifactExists(Artifact artifact);
 
@@ -83,18 +84,17 @@
   /**
    * @return Whether the artifact's data was injected.
    * @throws IOException if implementation tried to stat the Artifact which threw an exception.
-   *         Technically, this means that the artifact could not have been injected, but by throwing
-   *         here we save the caller trying to stat this file on their own and throwing the same
-   *         exception. Implementations are not guaranteed to throw in this case if they are able to
-   *         determine that the artifact is not injected without statting it.
+   *     Technically, this means that the artifact could not have been injected, but by throwing
+   *     here we save the caller trying to stat this file on their own and throwing the same
+   *     exception. Implementations are not guaranteed to throw in this case if they are able to
+   *     determine that the artifact is not injected without statting it.
    */
   boolean isInjected(Artifact file) throws IOException;
 
   /**
-   * Discards all known output artifact metadata, presumably because outputs will be modified.
-   * May only be called before any metadata is injected using {@link #injectDigest} or
-   * {@link #markOmitted};
+   * Discards all known output artifact metadata, presumably because outputs will be modified. May
+   * only be called before any metadata is injected using {@link #injectDigest} or {@link
+   * #markOmitted};
    */
   void discardOutputMetadata();
-
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
index 087e97a..a7230a6 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/ActionMetadataHandler.java
@@ -147,10 +147,15 @@
   }
 
   @Nullable
-  private FileArtifactValue getInputFileArtifactValue(ActionInput input) {
-    if (outputs.contains(input) || !(input instanceof Artifact)) {
+  private FileArtifactValue getInputFileArtifactValue(Artifact input) {
+    if (outputs.contains(input)) {
       return null;
     }
+
+    if (input.hasParent() && outputs.contains(input.getParent())) {
+      return null;
+    }
+
     return Preconditions.checkNotNull(inputArtifactData.get(input), input);
   }
 
@@ -399,6 +404,11 @@
   }
 
   @Override
+  public Iterable<TreeFileArtifact> getExpandedOutputs(Artifact artifact) {
+    return ImmutableSet.copyOf(getTreeArtifactContents(artifact));
+  }
+
+  @Override
   public void injectDigest(ActionInput output, FileStatus statNoFollow, byte[] digest) {
     // Assumption: any non-Artifact output is 'virtual' and should be ignored here.
     if (output instanceof Artifact) {
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactActionTest.java b/src/test/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactActionTest.java
index 7edf4b9..87d26bb 100644
--- a/src/test/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactActionTest.java
+++ b/src/test/java/com/google/devtools/build/lib/analysis/actions/PopulateTreeArtifactActionTest.java
@@ -68,6 +68,11 @@
     }
 
     @Override
+    public Iterable<TreeFileArtifact> getExpandedOutputs(Artifact artifact) {
+      throw new UnsupportedOperationException(artifact.prettyPrint());
+    }
+
+    @Override
     public Metadata getMetadataMaybe(Artifact artifact) {
       throw new UnsupportedOperationException(artifact.prettyPrint());
     }