Make labels in .bzl files in remote repos resolve relative to their repo

For example, if you have a BUILD file that does:

load('@foo//bar:baz.bzl', 'my_rule')
my_rule(...)

If baz.bzl uses Label('//whatever'), this change makes //whatever resolve to
@foo//whatever. Previous to this change, it would be resolved to the repository
the BUILD file using my_rule was in.

RELNOTES[INC]: Labels in .bzl files in remote repositories will be resolved
relative to their repository (instead of the repository the Skylark rule is
used in).

--
MOS_MIGRATED_REVID=117720181
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 f25aed6..2338b31 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
@@ -18,6 +18,7 @@
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.events.EventKind;
@@ -98,10 +99,14 @@
     private final Mutability mutability;
     final Frame parent;
     final Map<String, Object> bindings = new HashMap<>();
+    // The label for the target this frame is defined in (e.g., //foo:bar.bzl).
+    @Nullable
+    private Label label;
 
-    Frame(Mutability mutability, Frame parent) {
+    private Frame(Mutability mutability, Frame parent) {
       this.mutability = mutability;
       this.parent = parent;
+      this.label = parent == null ? null : parent.label;
     }
 
     @Override
@@ -110,6 +115,25 @@
     }
 
     /**
+     * Attaches a label to an existing frame. This is used to get the repository a Skylark
+     * extension is actually defined in.
+     * @param label the label to attach.
+     * @return a new Frame with the existing frame's properties plus the label.
+     */
+    public Frame setLabel(Label label) {
+      this.label = label;
+      return this;
+    }
+
+    /**
+     * Returns the label for this frame.
+     */
+    @Nullable
+    public final Label label() {
+      return label;
+    }
+
+    /**
      * Gets a binding from the current frame or if not found its parent.
      * @param varname the name of the variable to be bound
      * @return the value bound to variable
@@ -325,6 +349,13 @@
   @Nullable private Continuation continuation;
 
   /**
+   * Gets the label of the BUILD file that is using this environment. For example, if a target
+   * //foo has a dependency on //bar which is a Skylark rule defined in //rules:my_rule.bzl being
+   * evaluated in this environment, then this would return //foo.
+   */
+  @Nullable private final Label callerLabel;
+
+  /**
    * Enters a scope by saving state to a new Continuation
    * @param function the function whose scope to enter
    * @param caller the source AST node for the caller
@@ -394,7 +425,7 @@
   /**
    * @return the current Frame, in which variable side-effects happen.
    */
-  private Frame currentFrame() {
+  public Frame currentFrame() {
     return isGlobal() ? globalFrame : lexicalFrame;
   }
 
@@ -450,6 +481,7 @@
    * @param isSkylark true if in Skylark context
    * @param fileContentHashCode a hash for the source file being evaluated, if any
    * @param isLoadingPhase true if in loading phase
+   * @param callerLabel the label this environment came from
    */
   private Environment(
       Frame globalFrame,
@@ -458,7 +490,8 @@
       Map<String, Extension> importedExtensions,
       boolean isSkylark,
       @Nullable String fileContentHashCode,
-      boolean isLoadingPhase) {
+      boolean isLoadingPhase,
+      @Nullable Label callerLabel) {
     this.globalFrame = Preconditions.checkNotNull(globalFrame);
     this.dynamicFrame = Preconditions.checkNotNull(dynamicFrame);
     Preconditions.checkArgument(globalFrame.mutability().isMutable());
@@ -468,6 +501,7 @@
     this.isSkylark = isSkylark;
     this.fileContentHashCode = fileContentHashCode;
     this.isLoadingPhase = isLoadingPhase;
+    this.callerLabel = callerLabel;
   }
 
   /**
@@ -481,6 +515,7 @@
     @Nullable private EventHandler eventHandler;
     @Nullable private Map<String, Extension> importedExtensions;
     @Nullable private String fileContentHashCode;
+    private Label label;
 
     Builder(Mutability mutability) {
       this.mutability = mutability;
@@ -545,7 +580,13 @@
           importedExtensions,
           isSkylark,
           fileContentHashCode,
-          isLoadingPhase);
+          isLoadingPhase,
+          label);
+    }
+
+    public Builder setCallerLabel(Label label) {
+      this.label = label;
+      return this;
     }
   }
 
@@ -554,6 +595,13 @@
   }
 
   /**
+   * Returns the caller's label.
+   */
+  public Label getCallerLabel() {
+    return callerLabel;
+  }
+
+  /**
    * Sets a binding for a special dynamic variable in this Environment.
    * This is not for end-users, and will throw an AssertionError in case of conflict.
    * @param varname the name of the dynamic variable to be bound
@@ -856,7 +904,7 @@
 
   /**
    * Parses some String input without a supporting file, returning statements and comments.
-   * @param input a list of lines of code
+   * @param inputLines a list of lines of code
    */
   @VisibleForTesting
   Parser.ParseResult parseFileWithComments(String... inputLines) {