Cache SkylarkLookupImportValues in memory so that we don't recompute them multiple times.

PiperOrigin-RevId: 187941859
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/CachedSkylarkImportLookupValueAndDeps.java b/src/main/java/com/google/devtools/build/lib/skyframe/CachedSkylarkImportLookupValueAndDeps.java
new file mode 100644
index 0000000..4cee4ba
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/CachedSkylarkImportLookupValueAndDeps.java
@@ -0,0 +1,121 @@
+// Copyright 2018 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.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.skyframe.SkyKey;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+class CachedSkylarkImportLookupValueAndDeps {
+  private final SkylarkImportLookupValue value;
+  // We store the deps separately so that we can let go of the value when the cache drops it.
+  final CachedSkylarkImportLookupFunctionDeps deps;
+
+  private CachedSkylarkImportLookupValueAndDeps(
+      SkylarkImportLookupValue value, CachedSkylarkImportLookupFunctionDeps deps) {
+    this.value = value;
+    this.deps = deps;
+  }
+
+  SkylarkImportLookupValue getValue() {
+    return value;
+  }
+
+  static CachedSkylarkImportLookupValueAndDeps.Builder newBuilder() {
+    return new CachedSkylarkImportLookupValueAndDeps.Builder();
+  }
+
+  static class Builder {
+    private final CachedSkylarkImportLookupFunctionDeps.Builder depsBuilder =
+        CachedSkylarkImportLookupFunctionDeps.newBuilder();
+    private SkylarkImportLookupValue value;
+
+    @CanIgnoreReturnValue
+    Builder addDep(SkyKey key) {
+      depsBuilder.addDep(key);
+      return this;
+    }
+
+    @CanIgnoreReturnValue
+    Builder addDeps(Iterable<SkyKey> keys) {
+      depsBuilder.addDeps(keys);
+      return this;
+    }
+
+    @CanIgnoreReturnValue
+    Builder addTransitiveDeps(CachedSkylarkImportLookupValueAndDeps transitiveDeps) {
+      depsBuilder.addTransitiveDeps(transitiveDeps.deps);
+      return this;
+    }
+
+    @CanIgnoreReturnValue
+    Builder setValue(SkylarkImportLookupValue value) {
+      this.value = value;
+      return this;
+    }
+
+    CachedSkylarkImportLookupValueAndDeps build() {
+      return new CachedSkylarkImportLookupValueAndDeps(value, depsBuilder.build());
+    }
+  }
+
+  static class CachedSkylarkImportLookupFunctionDeps implements Iterable<Iterable<SkyKey>> {
+    private final ImmutableList<Iterable<SkyKey>> deps;
+
+    private CachedSkylarkImportLookupFunctionDeps(ImmutableList<Iterable<SkyKey>> deps) {
+      this.deps = deps;
+    }
+
+    static CachedSkylarkImportLookupFunctionDeps.Builder newBuilder() {
+      return new CachedSkylarkImportLookupFunctionDeps.Builder();
+    }
+
+    @Override
+    public Iterator<Iterable<SkyKey>> iterator() {
+      return deps.iterator();
+    }
+
+    static class Builder {
+      private final List<Iterable<SkyKey>> deps = new ArrayList<>();
+
+      // We only add the ASTFileLookupFunction through this so we don't need to worry about memory
+      // optimizations by adding it raw.
+      @CanIgnoreReturnValue
+      Builder addDep(SkyKey key) {
+        deps.add(ImmutableList.of(key));
+        return this;
+      }
+
+      @CanIgnoreReturnValue
+      Builder addDeps(Iterable<SkyKey> keys) {
+        deps.add(keys);
+        return this;
+      }
+
+      @CanIgnoreReturnValue
+      Builder addTransitiveDeps(CachedSkylarkImportLookupFunctionDeps transitiveDeps) {
+        Iterables.addAll(deps, transitiveDeps);
+        return this;
+      }
+
+      CachedSkylarkImportLookupFunctionDeps build() {
+        return new CachedSkylarkImportLookupFunctionDeps(ImmutableList.copyOf(deps));
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
index ad4c5a9..cddbf48 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkylarkImportLookupFunction.java
@@ -15,6 +15,8 @@
 
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicates;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -27,6 +29,7 @@
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.cmdline.PackageIdentifier;
+import com.google.devtools.build.lib.concurrent.BlazeInterners;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.events.ExtendedEventHandler.Postable;
@@ -47,6 +50,7 @@
 import com.google.devtools.build.lib.syntax.SkylarkSemantics;
 import com.google.devtools.build.lib.syntax.Statement;
 import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.skyframe.RecordingSkyFunctionEnvironment;
 import com.google.devtools.build.skyframe.SkyFunction;
 import com.google.devtools.build.skyframe.SkyFunctionException;
 import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
@@ -57,22 +61,26 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.logging.Logger;
 import javax.annotation.Nullable;
 
 /**
  * A Skyframe function to look up and import a single Skylark extension.
  *
- * <p> Given a {@link Label} referencing a Skylark file, attempts to locate the file and load it.
- * The Label must be absolute, and must not reference the special {@code external} package. If
- * loading is successful, returns a {@link SkylarkImportLookupValue} that encapsulates
- * the loaded {@link Extension} and {@link SkylarkFileDependency} information. If loading is
- * unsuccessful, throws a {@link SkylarkImportLookupFunctionException} that encapsulates the
- * cause of the failure.
+ * <p>Given a {@link Label} referencing a Skylark file, attempts to locate the file and load it. The
+ * Label must be absolute, and must not reference the special {@code external} package. If loading
+ * is successful, returns a {@link SkylarkImportLookupValue} that encapsulates the loaded {@link
+ * Extension} and {@link SkylarkFileDependency} information. If loading is unsuccessful, throws a
+ * {@link SkylarkImportLookupFunctionException} that encapsulates the cause of the failure.
  */
 public class SkylarkImportLookupFunction implements SkyFunction {
 
   private final RuleClassProvider ruleClassProvider;
   private final PackageFactory packageFactory;
+  private Cache<SkyKey, CachedSkylarkImportLookupValueAndDeps> skylarkImportLookupValueCache;
+
+  private static final Logger logger =
+      Logger.getLogger(SkylarkImportLookupFunction.class.getName());
 
   public SkylarkImportLookupFunction(
     RuleClassProvider ruleClassProvider, PackageFactory packageFactory) {
@@ -81,11 +89,17 @@
   }
 
   @Override
-  public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException,
-      InterruptedException {
+  @Nullable
+  public SkyValue compute(SkyKey skyKey, Environment env)
+      throws SkyFunctionException, InterruptedException {
     SkylarkImportLookupKey key = (SkylarkImportLookupKey) skyKey.argument();
     try {
-      return computeInternal(key.importLabel, key.inWorkspace, env, null);
+      return computeInternal(
+          key.importLabel,
+          key.inWorkspace,
+          env,
+          /*alreadyVisited=*/ null,
+          /*inlineCachedValueBuilder=*/ null);
     } catch (InconsistentFilesystemException e) {
       throw new SkylarkImportLookupFunctionException(e, Transience.PERSISTENT);
     } catch (SkylarkImportFailedException e) {
@@ -93,32 +107,88 @@
     }
   }
 
-  SkyValue computeWithInlineCalls(
+  @Nullable
+  SkylarkImportLookupValue computeWithInlineCalls(
       SkyKey skyKey, Environment env, LinkedHashMap<Label, SkylarkImportLookupValue> visited)
       throws InconsistentFilesystemException, SkylarkImportFailedException, InterruptedException {
-    return computeWithInlineCallsInternal(skyKey, env, visited);
+    CachedSkylarkImportLookupValueAndDeps cachedSkylarkImportLookupValueAndDeps =
+        computeWithInlineCallsInternal(skyKey, env, visited);
+    if (cachedSkylarkImportLookupValueAndDeps == null) {
+      return null;
+    }
+    for (Iterable<SkyKey> depGroup : cachedSkylarkImportLookupValueAndDeps.deps) {
+      env.getValues(depGroup);
+    }
+    return cachedSkylarkImportLookupValueAndDeps.getValue();
   }
 
-  private SkyValue computeWithInlineCallsInternal(
+  @Nullable
+  private CachedSkylarkImportLookupValueAndDeps computeWithInlineCallsInternal(
       SkyKey skyKey, Environment env, LinkedHashMap<Label, SkylarkImportLookupValue> visited)
       throws InconsistentFilesystemException, SkylarkImportFailedException, InterruptedException {
     SkylarkImportLookupKey key = (SkylarkImportLookupKey) skyKey.argument();
     SkylarkImportLookupValue precomputedResult = visited.get(key.importLabel);
     if (precomputedResult != null) {
-      return precomputedResult;
+      // We have already registered all the deps for this value.
+      return CachedSkylarkImportLookupValueAndDeps.newBuilder().setValue(precomputedResult).build();
     }
-    return computeInternal(
-        key.importLabel,
-        key.inWorkspace,
-        env,
-        Preconditions.checkNotNull(visited, key.importLabel));
+    // Note that we can't block other threads on the computation of this value due to a potential
+    // deadlock on a cycle. Although we are repeating some work, it is possible we have an import
+    // cycle where one thread starts at one side of the cycle and the other thread starts at the
+    // other side, and they then wait forever on the results of each others computations.
+    CachedSkylarkImportLookupValueAndDeps cachedSkylarkImportLookupValueAndDeps =
+        skylarkImportLookupValueCache.getIfPresent(skyKey);
+    if (cachedSkylarkImportLookupValueAndDeps != null) {
+      return cachedSkylarkImportLookupValueAndDeps;
+    }
+
+    CachedSkylarkImportLookupValueAndDeps.Builder inlineCachedValueBuilder =
+        CachedSkylarkImportLookupValueAndDeps.newBuilder();
+    Preconditions.checkState(
+        !(env instanceof RecordingSkyFunctionEnvironment),
+        "Found nested RecordingSkyFunctionEnvironment but it should have been stripped: %s",
+        env);
+    RecordingSkyFunctionEnvironment recordingEnv =
+        new RecordingSkyFunctionEnvironment(
+            env,
+            addedKey -> inlineCachedValueBuilder.addDep(addedKey),
+            addedSkyKeys -> inlineCachedValueBuilder.addDeps(addedSkyKeys));
+    SkylarkImportLookupValue value =
+        computeInternal(
+            key.importLabel,
+            key.inWorkspace,
+            recordingEnv,
+            Preconditions.checkNotNull(visited, key.importLabel),
+            inlineCachedValueBuilder);
+    if (value != null) {
+      inlineCachedValueBuilder.setValue(value);
+      cachedSkylarkImportLookupValueAndDeps = inlineCachedValueBuilder.build();
+      skylarkImportLookupValueCache.put(skyKey, cachedSkylarkImportLookupValueAndDeps);
+    }
+    return cachedSkylarkImportLookupValueAndDeps;
   }
 
-  private SkyValue computeInternal(
+  public void resetCache() {
+    if (skylarkImportLookupValueCache != null) {
+      logger.info(
+          "Skylark inlining cache stats from earlier build: "
+              + skylarkImportLookupValueCache.stats());
+    }
+    skylarkImportLookupValueCache =
+        CacheBuilder.newBuilder()
+            .concurrencyLevel(BlazeInterners.concurrencyLevel())
+            .maximumSize(10000)
+            .recordStats()
+            .build();
+  }
+
+  @Nullable
+  private SkylarkImportLookupValue computeInternal(
       Label fileLabel,
       boolean inWorkspace,
       Environment env,
-      @Nullable LinkedHashMap<Label, SkylarkImportLookupValue> alreadyVisited)
+      @Nullable LinkedHashMap<Label, SkylarkImportLookupValue> alreadyVisited,
+      @Nullable CachedSkylarkImportLookupValueAndDeps.Builder inlineCachedValueBuilder)
       throws InconsistentFilesystemException, SkylarkImportFailedException, InterruptedException {
     PathFragment filePath = fileLabel.toPathFragment();
 
@@ -150,8 +220,6 @@
 
     // Process the load statements in the file.
     ImmutableList<SkylarkImport> imports = ast.getImports();
-    Map<String, Extension> extensionsForImports = Maps.newHashMapWithExpectedSize(imports.size());
-    ImmutableList.Builder<SkylarkFileDependency> fileDependencies = ImmutableList.builder();
     ImmutableMap<String, Label> labelsForImports;
 
     // Find the labels corresponding to the load statements.
@@ -174,6 +242,9 @@
       skylarkImportMap = env.getValues(importLookupKeys);
       valuesMissing = env.valuesMissing();
     } else {
+      Preconditions.checkNotNull(
+          inlineCachedValueBuilder,
+          "Expected inline cached value builder to be not-null when inlining.");
       // Inlining calls to SkylarkImportLookupFunction.
       if (alreadyVisited.containsKey(fileLabel)) {
         ImmutableList<Label> cycle =
@@ -183,10 +254,16 @@
       }
       alreadyVisited.put(fileLabel, null);
       skylarkImportMap = Maps.newHashMapWithExpectedSize(imports.size());
+
+      Preconditions.checkState(
+          env instanceof RecordingSkyFunctionEnvironment,
+          "Expected to be recording dep requests when inlining SkylarkImportLookupFunction: %s",
+          fileLabel);
+      Environment strippedEnv = ((RecordingSkyFunctionEnvironment) env).getDelegate();
       for (SkyKey importLookupKey : importLookupKeys) {
-        SkyValue skyValue =
-            this.computeWithInlineCallsInternal(importLookupKey, env, alreadyVisited);
-        if (skyValue == null) {
+        CachedSkylarkImportLookupValueAndDeps cachedValue =
+            this.computeWithInlineCallsInternal(importLookupKey, strippedEnv, alreadyVisited);
+        if (cachedValue == null) {
           Preconditions.checkState(
               env.valuesMissing(), "no skylark import value for %s", importLookupKey);
           // We continue making inline calls even if some requested values are missing, to maximize
@@ -194,7 +271,9 @@
           // quadratic number of restarts.
           valuesMissing = true;
         } else {
+          SkyValue skyValue = cachedValue.getValue();
           skylarkImportMap.put(importLookupKey, skyValue);
+          inlineCachedValueBuilder.addTransitiveDeps(cachedValue);
         }
       }
       // All imports traversed, this key can no longer be part of a cycle.
@@ -206,6 +285,9 @@
     }
 
     // Process the loaded imports.
+    Map<String, Extension> extensionsForImports = Maps.newHashMapWithExpectedSize(imports.size());
+    ImmutableList.Builder<SkylarkFileDependency> fileDependencies =
+        ImmutableList.builderWithExpectedSize(labelsForImports.size());
     for (Entry<String, Label> importEntry : labelsForImports.entrySet()) {
       String importString = importEntry.getKey();
       Label importLabel = importEntry.getValue();
@@ -216,15 +298,10 @@
       fileDependencies.add(importLookupValue.getDependency());
     }
 
-    // Skylark UserDefinedFunction-s in that file will share this function definition Environment,
-    // which will be frozen by the time it is returned by createExtension.
-    Extension extension = createExtension(
-        ast,
-        fileLabel,
-        extensionsForImports,
-        skylarkSemantics,
-        env,
-        inWorkspace);
+    // #createExtension does not request values from the Environment. It may post events to the
+    // Environment, but events do not matter when caching SkylarkImportLookupValues.
+    Extension extension =
+        createExtension(ast, fileLabel, extensionsForImports, skylarkSemantics, env, inWorkspace);
     SkylarkImportLookupValue result =
         new SkylarkImportLookupValue(
             extension, new SkylarkFileDependency(fileLabel, fileDependencies.build()));
@@ -243,12 +320,13 @@
    * @throws SkylarkImportFailedException
    */
   @Nullable
-  static ImmutableMap<PathFragment, Label> labelsForAbsoluteImports(
+  private static ImmutableMap<PathFragment, Label> labelsForAbsoluteImports(
       ImmutableSet<PathFragment> pathsToLookup, Environment env)
       throws SkylarkImportFailedException, InterruptedException {
 
     // Import PathFragments are absolute, so there is a 1-1 mapping from corresponding Labels.
-    ImmutableMap.Builder<PathFragment, Label> outputMap = new ImmutableMap.Builder<>();
+    ImmutableMap.Builder<PathFragment, Label> outputMap =
+        ImmutableMap.builderWithExpectedSize(pathsToLookup.size());
 
     // The SkyKey here represents the directory containing an import PathFragment, hence there
     // can in general be multiple imports per lookup.
@@ -309,7 +387,7 @@
       throw new SkylarkImportFailedException(e);
     }
 
-  return outputMap.build();
+    return outputMap.build();
   }
 
   /**
@@ -508,10 +586,5 @@
         Transience transience) {
       super(e, transience);
     }
-
-    private SkylarkImportLookupFunctionException(BuildFileNotFoundException e,
-        Transience transience) {
-      super(e, transience);
-    }
   }
 }
diff --git a/src/main/java/com/google/devtools/build/skyframe/RecordingSkyFunctionEnvironment.java b/src/main/java/com/google/devtools/build/skyframe/RecordingSkyFunctionEnvironment.java
new file mode 100644
index 0000000..16b6c3f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/skyframe/RecordingSkyFunctionEnvironment.java
@@ -0,0 +1,223 @@
+// Copyright 2018 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.skyframe;
+
+import com.google.devtools.build.lib.events.ExtendedEventHandler;
+import com.google.devtools.build.lib.util.GroupedList;
+import com.google.devtools.build.skyframe.SkyFunction.Environment;
+import java.util.Map;
+import java.util.function.Consumer;
+import javax.annotation.Nullable;
+
+/** An environment that can observe the deps requested through getValue(s) calls. */
+public class RecordingSkyFunctionEnvironment implements Environment {
+
+  private final Environment delegate;
+  private final Consumer<SkyKey> skyKeyReceiver;
+  private final Consumer<Iterable<SkyKey>> skyKeysReceiver;
+
+  public RecordingSkyFunctionEnvironment(
+      Environment delegate,
+      Consumer<SkyKey> skyKeyReceiver,
+      Consumer<Iterable<SkyKey>> skyKeysReceiver) {
+    this.delegate = delegate;
+    this.skyKeyReceiver = skyKeyReceiver;
+    this.skyKeysReceiver = skyKeysReceiver;
+  }
+
+  private void recordDep(SkyKey key) {
+    skyKeyReceiver.accept(key);
+  }
+
+  @SuppressWarnings("unchecked") // Cast Iterable<? extends SkyKey> to Iterable<SkyKey>.
+  private void recordDeps(Iterable<? extends SkyKey> keys) {
+    skyKeysReceiver.accept((Iterable<SkyKey>) keys);
+  }
+
+  public Environment getDelegate() {
+    return delegate;
+  }
+
+  @Nullable
+  @Override
+  public SkyValue getValue(SkyKey valueName) throws InterruptedException {
+    recordDep(valueName);
+    return delegate.getValue(valueName);
+  }
+
+  @Nullable
+  @Override
+  public <E extends Exception> SkyValue getValueOrThrow(SkyKey depKey, Class<E> exceptionClass)
+      throws E, InterruptedException {
+    recordDep(depKey);
+    return delegate.getValueOrThrow(depKey, exceptionClass);
+  }
+
+  @Nullable
+  @Override
+  public <E1 extends Exception, E2 extends Exception> SkyValue getValueOrThrow(
+      SkyKey depKey, Class<E1> exceptionClass1, Class<E2> exceptionClass2)
+      throws E1, E2, InterruptedException {
+    recordDep(depKey);
+    return delegate.getValueOrThrow(depKey, exceptionClass1, exceptionClass2);
+  }
+
+  @Nullable
+  @Override
+  public <E1 extends Exception, E2 extends Exception, E3 extends Exception>
+      SkyValue getValueOrThrow(
+          SkyKey depKey,
+          Class<E1> exceptionClass1,
+          Class<E2> exceptionClass2,
+          Class<E3> exceptionClass3)
+          throws E1, E2, E3, InterruptedException {
+    recordDep(depKey);
+    return delegate.getValueOrThrow(depKey, exceptionClass1, exceptionClass2, exceptionClass3);
+  }
+
+  @Nullable
+  @Override
+  public <E1 extends Exception, E2 extends Exception, E3 extends Exception, E4 extends Exception>
+      SkyValue getValueOrThrow(
+          SkyKey depKey,
+          Class<E1> exceptionClass1,
+          Class<E2> exceptionClass2,
+          Class<E3> exceptionClass3,
+          Class<E4> exceptionClass4)
+          throws E1, E2, E3, E4, InterruptedException {
+    recordDep(depKey);
+    return delegate.getValueOrThrow(
+        depKey, exceptionClass1, exceptionClass2, exceptionClass3, exceptionClass4);
+  }
+
+  @Nullable
+  @Override
+  public <
+          E1 extends Exception,
+          E2 extends Exception,
+          E3 extends Exception,
+          E4 extends Exception,
+          E5 extends Exception>
+      SkyValue getValueOrThrow(
+          SkyKey depKey,
+          Class<E1> exceptionClass1,
+          Class<E2> exceptionClass2,
+          Class<E3> exceptionClass3,
+          Class<E4> exceptionClass4,
+          Class<E5> exceptionClass5)
+          throws E1, E2, E3, E4, E5, InterruptedException {
+    recordDep(depKey);
+    return delegate.getValueOrThrow(
+        depKey,
+        exceptionClass1,
+        exceptionClass2,
+        exceptionClass3,
+        exceptionClass4,
+        exceptionClass5);
+  }
+
+  @Override
+  public Map<SkyKey, SkyValue> getValues(Iterable<SkyKey> depKeys) throws InterruptedException {
+    recordDeps(depKeys);
+    return delegate.getValues(depKeys);
+  }
+
+  @Override
+  public <E extends Exception> Map<SkyKey, ValueOrException<E>> getValuesOrThrow(
+      Iterable<? extends SkyKey> depKeys, Class<E> exceptionClass) throws InterruptedException {
+    recordDeps(depKeys);
+    return delegate.getValuesOrThrow(depKeys, exceptionClass);
+  }
+
+  @Override
+  public <E1 extends Exception, E2 extends Exception>
+      Map<SkyKey, ValueOrException2<E1, E2>> getValuesOrThrow(
+          Iterable<? extends SkyKey> depKeys, Class<E1> exceptionClass1, Class<E2> exceptionClass2)
+          throws InterruptedException {
+    recordDeps(depKeys);
+    return delegate.getValuesOrThrow(depKeys, exceptionClass1, exceptionClass2);
+  }
+
+  @Override
+  public <E1 extends Exception, E2 extends Exception, E3 extends Exception>
+      Map<SkyKey, ValueOrException3<E1, E2, E3>> getValuesOrThrow(
+          Iterable<? extends SkyKey> depKeys,
+          Class<E1> exceptionClass1,
+          Class<E2> exceptionClass2,
+          Class<E3> exceptionClass3)
+          throws InterruptedException {
+    recordDeps(depKeys);
+    return delegate.getValuesOrThrow(depKeys, exceptionClass1, exceptionClass2, exceptionClass3);
+  }
+
+  @Override
+  public <E1 extends Exception, E2 extends Exception, E3 extends Exception, E4 extends Exception>
+      Map<SkyKey, ValueOrException4<E1, E2, E3, E4>> getValuesOrThrow(
+          Iterable<? extends SkyKey> depKeys,
+          Class<E1> exceptionClass1,
+          Class<E2> exceptionClass2,
+          Class<E3> exceptionClass3,
+          Class<E4> exceptionClass4)
+          throws InterruptedException {
+    recordDeps(depKeys);
+    return delegate.getValuesOrThrow(
+        depKeys, exceptionClass1, exceptionClass2, exceptionClass3, exceptionClass4);
+  }
+
+  @Override
+  public <
+          E1 extends Exception,
+          E2 extends Exception,
+          E3 extends Exception,
+          E4 extends Exception,
+          E5 extends Exception>
+      Map<SkyKey, ValueOrException5<E1, E2, E3, E4, E5>> getValuesOrThrow(
+          Iterable<? extends SkyKey> depKeys,
+          Class<E1> exceptionClass1,
+          Class<E2> exceptionClass2,
+          Class<E3> exceptionClass3,
+          Class<E4> exceptionClass4,
+          Class<E5> exceptionClass5)
+          throws InterruptedException {
+    recordDeps(depKeys);
+    return delegate.getValuesOrThrow(
+        depKeys,
+        exceptionClass1,
+        exceptionClass2,
+        exceptionClass3,
+        exceptionClass4,
+        exceptionClass5);
+  }
+
+  @Override
+  public boolean valuesMissing() {
+    return delegate.valuesMissing();
+  }
+
+  @Override
+  public ExtendedEventHandler getListener() {
+    return delegate.getListener();
+  }
+
+  @Override
+  public boolean inErrorBubblingForTesting() {
+    return delegate.inErrorBubblingForTesting();
+  }
+
+  @Nullable
+  @Override
+  public GroupedList<SkyKey> getTemporaryDirectDeps() {
+    return delegate.getTemporaryDirectDeps();
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
index 8764ac4..d500815 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkIntegrationTest.java
@@ -1652,6 +1652,7 @@
               .getSkyFunctionsForTesting();
       SkylarkImportLookupFunction skylarkImportLookupFunction =
           new SkylarkImportLookupFunction(this.getRuleClassProvider(), this.getPackageFactory());
+      skylarkImportLookupFunction.resetCache();
       ((PackageFunction) skyFunctions.get(SkyFunctions.PACKAGE))
           .setSkylarkImportLookupFunctionForInliningForTesting(skylarkImportLookupFunction);
     }