Store the hash code of the Environment in the RuleClass object. When a RuleClass is deserialized as part of a Skylark rule, the Environment is currently not present, but it is needed to detect changes to the rule.

Also precompute and store the Environment's hash code, and do a drive-by clean-up of a bunch of warnings in the Environment code.

--
MOS_MIGRATED_REVID=122838588
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
index 8ef298a..e26b257 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Environment.java
@@ -176,7 +176,7 @@
      * This provides a O(n) way of extracting the list of all variables visible in an Environment.
      * @param vars the set of visible variables in the Environment, being computed.
      */
-    public void addVariableNamesTo(Set<String> vars) {
+    void addVariableNamesTo(Set<String> vars) {
       vars.addAll(bindings.keySet());
       if (parent != null) {
         parent.addVariableNamesTo(vars);
@@ -224,7 +224,6 @@
         FuncallExpression caller,
         Frame lexicalFrame,
         Frame globalFrame,
-        Set<String> knownGlobalVariables,
         boolean isSkylark) {
       this.continuation = continuation;
       this.function = function;
@@ -275,14 +274,7 @@
       this.transitiveContentHashCode = env.getTransitiveContentHashCode();
     }
 
-    // Hack to allow serialization.
-    private Extension() {
-      super();
-      this.transitiveContentHashCode = null;
-    }
-
-    @VisibleForTesting // This is only used in one test.
-    public String getTransitiveContentHashCode() {
+    String getTransitiveContentHashCode() {
       return transitiveContentHashCode;
     }
 
@@ -375,11 +367,11 @@
    * @param globals the global Frame that this function closes over from its definition Environment
    */
   void enterScope(BaseFunction function, FuncallExpression caller, Frame globals) {
-    continuation = new Continuation(
-        continuation, function, caller, lexicalFrame, globalFrame, knownGlobalVariables, isSkylark);
+    continuation =
+        new Continuation(continuation, function, caller, lexicalFrame, globalFrame, isSkylark);
     lexicalFrame = new Frame(mutability(), null);
     globalFrame = globals;
-    knownGlobalVariables = new HashSet<String>();
+    knownGlobalVariables = new HashSet<>();
     isSkylark = true;
   }
 
@@ -395,10 +387,7 @@
     continuation = continuation.continuation;
   }
 
-  /**
-   * When evaluating code from a file, this contains a hash of the file.
-   */
-  @Nullable private String fileContentHashCode;
+  private final String transitiveHashCode;
 
   /**
    * Is this Environment being evaluated during the loading phase?
@@ -434,10 +423,8 @@
     return dynamicFrame.mutability();
   }
 
-  /**
-   * @return the current Frame, in which variable side-effects happen.
-   */
-  public Frame currentFrame() {
+  /** @return the current Frame, in which variable side-effects happen. */
+  private Frame currentFrame() {
     return isGlobal() ? globalFrame : lexicalFrame;
   }
 
@@ -457,10 +444,8 @@
     return eventHandler;
   }
 
-  /**
-   * @return the current stack trace as a list of functions.
-   */
-  public ImmutableList<BaseFunction> getStackTrace() {
+  /** @return the current stack trace as a list of functions. */
+  ImmutableList<BaseFunction> getStackTrace() {
     ImmutableList.Builder<BaseFunction> builder = new ImmutableList.Builder<>();
     for (Continuation k = continuation; k != null; k = k.continuation) {
       builder.add(k.function);
@@ -512,10 +497,11 @@
     this.eventHandler = eventHandler;
     this.importedExtensions = importedExtensions;
     this.isSkylark = isSkylark;
-    this.fileContentHashCode = fileContentHashCode;
     this.phase = phase;
     this.callerLabel = callerLabel;
     this.toolsRepository = toolsRepository;
+    this.transitiveHashCode =
+        computeTransitiveContentHashCode(fileContentHashCode, importedExtensions);
   }
 
   /**
@@ -783,7 +769,7 @@
     return knownGlobalVariables != null && knownGlobalVariables.contains(varname);
   }
 
-  public void handleEvent(Event event) {
+  void handleEvent(Event event) {
     eventHandler.handle(event);
   }
 
@@ -831,7 +817,7 @@
    * An Exception thrown when an attempt is made to import a symbol from a file
    * that was not properly loaded.
    */
-  public static class LoadFailedException extends Exception {
+  static class LoadFailedException extends Exception {
     LoadFailedException(String importString) {
       super(String.format("file '%s' was not correctly loaded. "
               + "Make sure the 'load' statement appears in the global scope in your file",
@@ -839,7 +825,7 @@
     }
   }
 
-  public void importSymbol(String importString, Identifier symbol, String nameInLoadedFile)
+  void importSymbol(String importString, Identifier symbol, String nameInLoadedFile)
       throws NoSuchVariableException, LoadFailedException {
     Preconditions.checkState(isGlobal()); // loading is only allowed at global scope.
 
@@ -864,14 +850,13 @@
     }
   }
 
-  /**
-   * Returns a hash code calculated from the hash code of this Environment and the
-   * transitive closure of other Environments it loads.
-   */
-  public String getTransitiveContentHashCode() {
+  private static String computeTransitiveContentHashCode(
+      @Nullable String baseHashCode, Map<String, Extension> importedExtensions) {
     // Calculate a new hash from the hash of the loaded Extension-s.
     Fingerprint fingerprint = new Fingerprint();
-    fingerprint.addString(Preconditions.checkNotNull(fileContentHashCode));
+    if (baseHashCode != null) {
+      fingerprint.addString(Preconditions.checkNotNull(baseHashCode));
+    }
     TreeSet<String> importStrings = new TreeSet<>(importedExtensions.keySet());
     for (String importString : importStrings) {
       fingerprint.addString(importedExtensions.get(importString).getTransitiveContentHashCode());
@@ -879,9 +864,16 @@
     return fingerprint.hexDigestAndReset();
   }
 
+  /**
+   * Returns a hash code calculated from the hash code of this Environment and the
+   * transitive closure of other Environments it loads.
+   */
+  public String getTransitiveContentHashCode() {
+    return transitiveHashCode;
+  }
 
   /** A read-only Environment.Frame with global constants in it only */
-  public static final Frame CONSTANTS_ONLY = createConstantsGlobals();
+  static final Frame CONSTANTS_ONLY = createConstantsGlobals();
 
   /** A read-only Environment.Frame with initial globals for the BUILD language */
   public static final Frame BUILD = createBuildGlobals();