Add `getPreviouslyRequestedDepValue()` method so that base `SkyFunctionEnvironment` reads from the env-scoped map and subclass `SkipsBatchPrefetch` queries the `evaluatorExecutor` graph on demand

PiperOrigin-RevId: 626375961
Change-Id: I1a88bc4443c5030f81fe3dcb30bf9f3422309ed9
diff --git a/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java b/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
index e799388..414811a 100644
--- a/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
+++ b/src/main/java/com/google/devtools/build/skyframe/SkyFunctionEnvironment.java
@@ -467,7 +467,7 @@
         return bubbleErrorInfoValue;
       }
     }
-    SkyValue directDepsValue = previouslyRequestedDepsValues.get(key);
+    SkyValue directDepsValue = getPreviouslyRequestedDepValue(key);
     if (directDepsValue != null) {
       return directDepsValue;
     }
@@ -475,6 +475,20 @@
     return directDepsValue == MANUALLY_REGISTERED_MARKER ? null : directDepsValue;
   }
 
+  /**
+   * Gets the value of previously requested dep from either the env-scoped map or the {@link
+   * #evaluatorContext}'s graph.
+   *
+   * <p>In {@link SkipsBatchPrefetch}, since previously requested deps values are not available
+   * after environment creation, so it needs to query the {@link #evaluatorContext}'s graph on
+   * demand.
+   */
+  @Nullable
+  @ForOverride
+  SkyValue getPreviouslyRequestedDepValue(SkyKey key) {
+    return previouslyRequestedDepsValues.get(key);
+  }
+
   @ForOverride
   @Nullable
   SkyValue lookupRequestedDep(SkyKey depKey) {
@@ -1152,6 +1166,29 @@
 
     @Nullable
     @Override
+    SkyValue getPreviouslyRequestedDepValue(SkyKey key) {
+      SkyFunctionEnvironment env = this;
+      if (!env.previouslyRequestedDeps.contains(key)) {
+        return null;
+      }
+      SkyValue possibleValueInMap = env.previouslyRequestedDepsValues.get(key);
+      if (possibleValueInMap != null) {
+        return possibleValueInMap;
+      }
+      try {
+        // TODO: b/324948927#comment14 - Figure out the approach to properly handle possible missing
+        // or undone deps before expanding the usage of `SkipsBatchPrefetch` or making
+        // `SkipsBatchPrefetch` as the default environment to create.
+        NodeEntry depEntry =
+            env.evaluatorContext.getGraph().get(env.skyKey, Reason.DEP_REQUESTED, key);
+        return processDepEntry(key, depEntry);
+      } catch (InterruptedException e) {
+        throw new IllegalStateException("No interruption when getting depEntry from depGraph", e);
+      }
+    }
+
+    @Nullable
+    @Override
     SkyValue lookupRequestedDep(SkyKey depKey) {
       SkyFunctionEnvironment env = this;
       checkArgument(
diff --git a/src/test/java/com/google/devtools/build/skyframe/StateMachineTest.java b/src/test/java/com/google/devtools/build/skyframe/StateMachineTest.java
index a4a1e79..0754228 100644
--- a/src/test/java/com/google/devtools/build/skyframe/StateMachineTest.java
+++ b/src/test/java/com/google/devtools/build/skyframe/StateMachineTest.java
@@ -100,7 +100,10 @@
   private static final SkyKey KEY_B3 = GraphTester.skyKey("B3");
   private static final SkyValue VALUE_B3 = new StringValue("B3");
 
-  private static final SkyKey ROOT_KEY = GraphTester.skyKey("root");
+  @TestParameter private boolean rootKeySkipsBatchPrefetch;
+
+  private SkyKey rootKey;
+
   private static final SkyValue DONE_VALUE = new StringValue("DONE");
   private static final StringValue SUCCESS_VALUE = new StringValue("SUCCESS");
 
@@ -112,6 +115,10 @@
     tester.getOrCreate(KEY_B1).setConstantValue(VALUE_B1);
     tester.getOrCreate(KEY_B2).setConstantValue(VALUE_B2);
     tester.getOrCreate(KEY_B3).setConstantValue(VALUE_B3);
+    rootKey =
+        rootKeySkipsBatchPrefetch
+            ? GraphTester.skipBatchPrefetchKey("root")
+            : GraphTester.skyKey("root");
   }
 
   private static class StateMachineWrapper implements SkyKeyComputeState {
@@ -129,7 +136,7 @@
   /**
    * Defines a {@link SkyFunction} that executes the gives state machine.
    *
-   * <p>The function always has key {@link ROOT_KEY} and value {@link DONE_VALUE}. State machine
+   * <p>The function always has key {@link rootKey} and value {@link DONE_VALUE}. State machine
    * internals can be observed with consumers.
    *
    * @return a counter that stores the restart count.
@@ -137,7 +144,7 @@
   private AtomicInteger defineRootMachine(Supplier<StateMachine> rootMachineSupplier) {
     var restartCount = new AtomicInteger();
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               if (!env.getState(() -> new StateMachineWrapper(rootMachineSupplier.get()))
@@ -152,7 +159,7 @@
 
   private int evalMachine(Supplier<StateMachine> rootMachineSupplier) throws InterruptedException {
     var restartCount = defineRootMachine(rootMachineSupplier);
-    assertThat(eval(ROOT_KEY, /* keepGoing= */ false).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+    assertThat(eval(rootKey, /* keepGoing= */ false).get(rootKey)).isEqualTo(DONE_VALUE);
     return restartCount.get();
   }
 
@@ -342,7 +349,7 @@
               instantiationCount.getAndIncrement();
               return new ExampleWithSubmachines(a1Sink, a2Sink, a3Sink, b1Sink, b2Sink, b3Sink);
             });
-    assertThat(eval(ROOT_KEY, keepGoing).getError(ROOT_KEY)).isNotNull();
+    assertThat(eval(rootKey, keepGoing).getError(rootKey)).isNotNull();
 
     assertThat(restartCount.get()).isEqualTo(2);
     assertThat(a1Sink.get()).isNull();
@@ -389,15 +396,15 @@
                       });
                   return StateMachine.DONE;
                 });
-    var result = eval(ROOT_KEY, keepGoing);
+    var result = eval(rootKey, keepGoing);
     if (keepGoing) {
       // In keepGoing mode, the swallowed error vanishes.
-      assertThat(result.get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+      assertThat(result.get(rootKey)).isEqualTo(DONE_VALUE);
       assertThat(result.hasError()).isFalse();
     } else {
       // In nokeepGoing mode, the error is processed in error bubbling, but the function does not
       // complete and the error is still propagated to the top level.
-      assertThat(result.get(ROOT_KEY)).isNull();
+      assertThat(result.get(rootKey)).isNull();
       assertThatEvaluationResult(result).hasSingletonErrorThat(KEY_A1);
     }
     assertThat(restartCount.get()).isEqualTo(1);
@@ -435,7 +442,7 @@
   @Test
   public void valueOrExceptionProducer_propagatesValues() throws InterruptedException {
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrExceptionProducer::new);
@@ -450,7 +457,7 @@
               }
               return DONE_VALUE;
             });
-    assertThat(eval(ROOT_KEY, /* keepGoing= */ false).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+    assertThat(eval(rootKey, /* keepGoing= */ false).get(rootKey)).isEqualTo(DONE_VALUE);
     assertThat(StringOrExceptionProducer.isProcessValueOrExceptionCalled).isTrue();
   }
 
@@ -460,7 +467,7 @@
     var hasRestarted = new AtomicBoolean(false);
     tester.getOrCreate(KEY_A1).unsetConstantValue().setHasError(true);
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrExceptionProducer::new);
@@ -477,12 +484,12 @@
               assertThrows(SomeErrorException.class, () -> producer.tryProduceValue(env));
               return DONE_VALUE;
             });
-    var result = eval(ROOT_KEY, keepGoing);
+    var result = eval(rootKey, keepGoing);
     if (keepGoing) {
-      assertThat(result.get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+      assertThat(result.get(rootKey)).isEqualTo(DONE_VALUE);
       assertThat(result.hasError()).isFalse();
     } else {
-      assertThat(result.get(ROOT_KEY)).isNull();
+      assertThat(result.get(rootKey)).isNull();
       assertThatEvaluationResult(result).hasSingletonErrorThat(KEY_A1);
     }
     assertThat(StringOrExceptionProducer.isProcessValueOrExceptionCalled).isTrue();
@@ -523,7 +530,7 @@
     var gotError = new AtomicBoolean(false);
     tester.getOrCreate(KEY_A2).unsetConstantValue().setHasError(true);
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (unusedKey, env) -> {
               // Primes KEY_A2, making the error available.
@@ -541,9 +548,9 @@
             });
     // keepGoing must be false below, otherwise the state machine will be run a second time when
     // KEY_A1 becomes available.
-    var result = eval(ROOT_KEY, /* keepGoing= */ false);
+    var result = eval(rootKey, /* keepGoing= */ false);
     assertThat(gotError.get()).isTrue();
-    assertThat(result.get(ROOT_KEY)).isNull();
+    assertThat(result.get(rootKey)).isNull();
     assertThatEvaluationResult(result).hasSingletonErrorThat(KEY_A2);
   }
 
@@ -598,7 +605,7 @@
   @Test
   public void valueOrException2Producer_propagatesValues() throws InterruptedException {
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException2Producer::new);
@@ -613,7 +620,7 @@
               }
               return DONE_VALUE;
             });
-    assertThat(eval(ROOT_KEY, /* keepGoing= */ false).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+    assertThat(eval(rootKey, /* keepGoing= */ false).get(rootKey)).isEqualTo(DONE_VALUE);
   }
 
   @Test
@@ -624,7 +631,7 @@
     SkyKey errorKey = trueForException1 ? KEY_A1 : KEY_B1;
     tester.getOrCreate(errorKey).unsetConstantValue().setHasError(true);
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException2Producer::new);
@@ -643,12 +650,12 @@
               }
               return DONE_VALUE;
             });
-    var result = eval(ROOT_KEY, keepGoing);
+    var result = eval(rootKey, keepGoing);
     if (keepGoing) {
-      assertThat(result.get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+      assertThat(result.get(rootKey)).isEqualTo(DONE_VALUE);
       assertThat(result.hasError()).isFalse();
     } else {
-      assertThat(result.get(ROOT_KEY)).isNull();
+      assertThat(result.get(rootKey)).isNull();
       assertThatEvaluationResult(result).hasSingletonErrorThat(errorKey);
     }
   }
@@ -706,7 +713,7 @@
   public void valueOrException2Producer_singleLookup_propagatesValuesAndInvokesRunAfter()
       throws InterruptedException {
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException2ProducerWithSingleLookup::new);
@@ -721,7 +728,7 @@
               }
               return DONE_VALUE;
             });
-    assertThat(eval(ROOT_KEY, /* keepGoing= */ false).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+    assertThat(eval(rootKey, /* keepGoing= */ false).get(rootKey)).isEqualTo(DONE_VALUE);
     assertThat(StringOrException2ProducerWithSingleLookup.isProcessValueOrExceptionCalled).isTrue();
   }
 
@@ -741,7 +748,7 @@
             });
 
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException2ProducerWithSingleLookup::new);
@@ -761,9 +768,9 @@
               return DONE_VALUE;
             });
 
-    var result = eval(ROOT_KEY, /* keepGoing= */ false);
+    var result = eval(rootKey, /* keepGoing= */ false);
 
-    assertThat(result.get(ROOT_KEY)).isNull();
+    assertThat(result.get(rootKey)).isNull();
     assertThatEvaluationResult(result).hasSingletonErrorThat(KEY_A1);
     assertThat(StringOrException2ProducerWithSingleLookup.isProcessValueOrExceptionCalled).isTrue();
   }
@@ -810,7 +817,7 @@
   @Test
   public void valueOrException3Producer_propagatesValues() throws InterruptedException {
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException3Producer::new);
@@ -825,7 +832,7 @@
               }
               return DONE_VALUE;
             });
-    assertThat(eval(ROOT_KEY, /* keepGoing= */ false).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+    assertThat(eval(rootKey, /* keepGoing= */ false).get(rootKey)).isEqualTo(DONE_VALUE);
   }
 
   enum ValueOrException3ExceptionCase {
@@ -859,7 +866,7 @@
     SkyKey errorKey = exceptionCase.errorKey();
     tester.getOrCreate(errorKey).unsetConstantValue().setHasError(true);
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException3Producer::new);
@@ -884,12 +891,12 @@
               }
               return DONE_VALUE;
             });
-    var result = eval(ROOT_KEY, keepGoing);
+    var result = eval(rootKey, keepGoing);
     if (keepGoing) {
-      assertThat(result.get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+      assertThat(result.get(rootKey)).isEqualTo(DONE_VALUE);
       assertThat(result.hasError()).isFalse();
     } else {
-      assertThat(result.get(ROOT_KEY)).isNull();
+      assertThat(result.get(rootKey)).isNull();
       assertThatEvaluationResult(result).hasSingletonErrorThat(errorKey);
     }
   }
@@ -936,7 +943,7 @@
   public void valueOrException3Producer_singleLookup_propagatesValues()
       throws InterruptedException {
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException3ProducerWithSingleLookup::new);
@@ -951,7 +958,7 @@
               }
               return DONE_VALUE;
             });
-    assertThat(eval(ROOT_KEY, /* keepGoing= */ false).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+    assertThat(eval(rootKey, /* keepGoing= */ false).get(rootKey)).isEqualTo(DONE_VALUE);
     assertThat(StringOrException3ProducerWithSingleLookup.isProcessValueOrExceptionCalled).isTrue();
   }
 
@@ -980,7 +987,7 @@
             });
 
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               var producer = env.getState(StringOrException3ProducerWithSingleLookup::new);
@@ -1006,9 +1013,9 @@
               return DONE_VALUE;
             });
 
-    var result = eval(ROOT_KEY, /* keepGoing= */ false);
+    var result = eval(rootKey, /* keepGoing= */ false);
 
-    assertThat(result.get(ROOT_KEY)).isNull();
+    assertThat(result.get(rootKey)).isNull();
     assertThatEvaluationResult(result).hasSingletonErrorThat(KEY_A1);
     assertThat(StringOrException3ProducerWithSingleLookup.isProcessValueOrExceptionCalled).isTrue();
   }
@@ -1033,7 +1040,7 @@
     } else {
       var unused = defineRootMachine(rootSupplier);
       // There are no errors in this test so the keepGoing value is arbitrary.
-      assertThat(eval(ROOT_KEY, /* keepGoing= */ true).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+      assertThat(eval(rootKey, /* keepGoing= */ true).get(rootKey)).isEqualTo(DONE_VALUE);
     }
     assertThat(sink.value).isEqualTo(VALUE_A1);
     assertThat(sink.exception).isNull();
@@ -1090,23 +1097,23 @@
     }
 
     var unused = defineRootMachine(rootSupplier);
-    var result = eval(ROOT_KEY, keepGoing);
+    var result = eval(rootKey, keepGoing);
     assertThat(sink.value).isNull();
     if (exceptionCase.exceptionOrdinal() > lookupType.exceptionCount()) {
       // The exception was not handled.
       assertThat(sink.exception).isNull();
-      assertThat(result.get(ROOT_KEY)).isNull();
+      assertThat(result.get(rootKey)).isNull();
       assertThatEvaluationResult(result).hasSingletonErrorThat(KEY_A1);
       return;
     }
     assertThat(sink.exception).isEqualTo(exception);
     if (keepGoing) {
       // The error is completely handled.
-      assertThat(result.get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+      assertThat(result.get(rootKey)).isEqualTo(DONE_VALUE);
       return;
     }
     assertThatEvaluationResult(result).hasSingletonErrorThat(KEY_A1);
-    assertThat(result.get(ROOT_KEY)).isNull();
+    assertThat(result.get(rootKey)).isNull();
   }
 
   /**
@@ -1448,7 +1455,7 @@
       Supplier<List<StateMachine>> rootMachineSupplier) {
     AtomicInteger restartCount = new AtomicInteger();
     tester
-        .getOrCreate(ROOT_KEY)
+        .getOrCreate(rootKey)
         .setBuilder(
             (k, env) -> {
               if (!env.getState(
@@ -1467,14 +1474,14 @@
   private int evalMachineWithMultipleDrivers(Supplier<List<StateMachine>> rootMachineSupplier)
       throws InterruptedException {
     AtomicInteger restartCount = defineRootMachineWithMultipleDriver(rootMachineSupplier);
-    assertThat(eval(ROOT_KEY, /* keepGoing= */ false).get(ROOT_KEY)).isEqualTo(DONE_VALUE);
+    assertThat(eval(rootKey, /* keepGoing= */ false).get(rootKey)).isEqualTo(DONE_VALUE);
     return restartCount.get();
   }
 
   @Test
   public void test_multipleStateMachinesInParallelDriver() throws InterruptedException {
     for (int i = 0; i < 100; ++i) {
-      graph.remove(ROOT_KEY);
+      graph.remove(rootKey);
       graph.remove(KEY_A1);
       graph.remove(KEY_A2);
       var v1Sink = new SkyValueSink();