| // Copyright 2019 The Bazel Authors. All rights reserved. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //    http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | package com.google.devtools.build.lib.skyframe; | 
 |  | 
 | import com.google.common.base.MoreObjects; | 
 | import com.google.common.base.Preconditions; | 
 | import com.google.common.collect.ImmutableList; | 
 | import com.google.common.collect.Interner; | 
 | import com.google.devtools.build.skyframe.SkyKey; | 
 | import com.google.errorprone.annotations.CanIgnoreReturnValue; | 
 | import java.util.ArrayList; | 
 | import java.util.List; | 
 | import java.util.Map; | 
 | import java.util.concurrent.atomic.AtomicReference; | 
 |  | 
 | /** | 
 |  * A saved {@link BzlLoadFunction} computation, used when inlining that Skyfunction. | 
 |  * | 
 |  * <p>This holds a requested key, its computed value, and the direct and transitive Skyframe | 
 |  * dependencies that are needed to compute it from scratch (i.e. if it weren't cached). Here | 
 |  * "transitive" means "underneath another {@code BzlLoadFunction} computation"; we split them into | 
 |  * other {@code CachedBzlLoadData} objects so they can be shared by other requesting bzls. | 
 |  */ | 
 | class CachedBzlLoadData { | 
 |   private final BzlLoadValue.Key key; | 
 |   private final BzlLoadValue value; | 
 |   private final ImmutableList<Iterable<SkyKey>> directDeps; | 
 |   private final ImmutableList<CachedBzlLoadData> transitiveDeps; | 
 |  | 
 |   private CachedBzlLoadData( | 
 |       BzlLoadValue.Key key, | 
 |       BzlLoadValue value, | 
 |       ImmutableList<Iterable<SkyKey>> directDeps, | 
 |       ImmutableList<CachedBzlLoadData> transitiveDeps) { | 
 |     this.key = key; | 
 |     this.value = value; | 
 |     this.directDeps = directDeps; | 
 |     this.transitiveDeps = transitiveDeps; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Adds all deps (direct and transitive) of this value to the {@code visitedDeps} set and passes | 
 |    * them to the consumer (with unspecified order and grouping). The traversal does not include | 
 |    * nodes already contained in {@code visitedDeps}. | 
 |    */ | 
 |   void traverse( | 
 |       DepGroupConsumer depGroupConsumer, Map<BzlLoadValue.Key, CachedBzlLoadData> visitedDeps) | 
 |       throws InterruptedException { | 
 |     if (visitedDeps.putIfAbsent(key, this) != null) { | 
 |       return; | 
 |     } | 
 |  | 
 |     for (Iterable<SkyKey> directDepGroup : directDeps) { | 
 |       depGroupConsumer.accept(directDepGroup); | 
 |     } | 
 |     for (CachedBzlLoadData indirectDeps : transitiveDeps) { | 
 |       indirectDeps.traverse(depGroupConsumer, visitedDeps); | 
 |     } | 
 |   } | 
 |  | 
 |   BzlLoadValue getValue() { | 
 |     return value; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public boolean equals(Object obj) { | 
 |     if (obj instanceof CachedBzlLoadData) { | 
 |       // With the interner, force there to be exactly one cached value per key at any given point | 
 |       // in time. | 
 |       return this.key.equals(((CachedBzlLoadData) obj).key); | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public int hashCode() { | 
 |     return key.hashCode(); | 
 |   } | 
 |  | 
 |   /** A consumer of dependency groups that can be interrupted. */ | 
 |   interface DepGroupConsumer { | 
 |     void accept(Iterable<SkyKey> keys) throws InterruptedException; | 
 |   } | 
 |  | 
 |   static class Builder { | 
 |     Builder(Interner<CachedBzlLoadData> interner) { | 
 |       this.interner = interner; | 
 |     } | 
 |  | 
 |     private final Interner<CachedBzlLoadData> interner; | 
 |     private final List<Iterable<SkyKey>> directDeps = new ArrayList<>(); | 
 |     private final List<CachedBzlLoadData> transitiveDeps = new ArrayList<>(); | 
 |     private final AtomicReference<Exception> exceptionSeen = new AtomicReference<>(null); | 
 |     private BzlLoadValue value; | 
 |     private BzlLoadValue.Key key; | 
 |  | 
 |     @CanIgnoreReturnValue | 
 |     Builder addDep(SkyKey key) { | 
 |       directDeps.add(ImmutableList.of(key)); | 
 |       return this; | 
 |     } | 
 |  | 
 |     @CanIgnoreReturnValue | 
 |     Builder addDeps(Iterable<SkyKey> keys) { | 
 |       directDeps.add(keys); | 
 |       return this; | 
 |     } | 
 |  | 
 |     @CanIgnoreReturnValue | 
 |     Builder noteException(Exception e) { | 
 |       exceptionSeen.set(e); | 
 |       return this; | 
 |     } | 
 |  | 
 |     @CanIgnoreReturnValue | 
 |     Builder addTransitiveDeps(CachedBzlLoadData transitiveDeps) { | 
 |       this.transitiveDeps.add(transitiveDeps); | 
 |       return this; | 
 |     } | 
 |  | 
 |     @CanIgnoreReturnValue | 
 |     Builder setValue(BzlLoadValue value) { | 
 |       this.value = value; | 
 |       return this; | 
 |     } | 
 |  | 
 |     @CanIgnoreReturnValue | 
 |     Builder setKey(BzlLoadValue.Key key) { | 
 |       this.key = key; | 
 |       return this; | 
 |     } | 
 |  | 
 |     CachedBzlLoadData build() { | 
 |       // We expect that we don't handle any exceptions in BzlLoadFunction directly. | 
 |       Preconditions.checkState(exceptionSeen.get() == null, "Caching a value in error?: %s", this); | 
 |       Preconditions.checkNotNull(value, "Expected value to be set: %s", this); | 
 |       Preconditions.checkNotNull(key, "Expected key to be set: %s", this); | 
 |       return interner.intern( | 
 |           new CachedBzlLoadData( | 
 |               key, value, ImmutableList.copyOf(directDeps), ImmutableList.copyOf(transitiveDeps))); | 
 |     } | 
 |  | 
 |     @Override | 
 |     public String toString() { | 
 |       return MoreObjects.toStringHelper(CachedBzlLoadData.Builder.class) | 
 |           .add("key", key) | 
 |           .add("value", value) | 
 |           .add("directDeps", directDeps) | 
 |           .add("transitiveDeps", transitiveDeps) | 
 |           .add("exceptionSeen", exceptionSeen) | 
 |           .toString(); | 
 |     } | 
 |  | 
 |   } | 
 | } |