Create SkylarkCallable.useContext and use it instead of Environment.BazelInfo

This makes the Starlark interpreter pass a StarlarkContext object to methods, which, for Bazel, should be a stand-in replacement for the previous BazelInfo object.

In a future cleanup, we can move Mutability to context, and then turn down useEnvironment. Environment is a bit too powerful an object to be passing around.

RELNOTES: None.
PiperOrigin-RevId: 224216211
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index 2d7a0ab..c6a35ff 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -374,6 +374,18 @@
 )
 
 java_library(
+    name = "starlark_context",
+    srcs = [
+        "analysis/skylark/BazelStarlarkContext.java",
+    ],
+    deps = [
+        ":skylarkinterface",
+        "//third_party:guava",
+        "//third_party:jsr305",
+    ],
+)
+
+java_library(
     name = "skylark_semantics",
     srcs = [
         "syntax/SkylarkSemantics.java",
@@ -493,6 +505,7 @@
         ":events",
         ":skylark_semantics",
         ":skylarkinterface",
+        ":starlark_context",
         ":syntax",
         ":transitive-info-provider",
         ":util",
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
index ffeb3ab..b8a6589 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/ConfiguredRuleClassProvider.java
@@ -34,6 +34,7 @@
 import com.google.devtools.build.lib.analysis.config.DefaultsPackage;
 import com.google.devtools.build.lib.analysis.config.FragmentOptions;
 import com.google.devtools.build.lib.analysis.constraints.ConstraintSemantics;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
 import com.google.devtools.build.lib.analysis.skylark.SkylarkModules;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
@@ -811,16 +812,17 @@
       EventHandler eventHandler,
       String astFileContentHashCode,
       Map<String, Extension> importMap) {
+    BazelStarlarkContext context =
+        new BazelStarlarkContext(toolsRepository, configurationFragmentMap);
     Environment env =
         Environment.builder(mutability)
             .setGlobals(globals)
             .setSemantics(skylarkSemantics)
             .setEventHandler(eventHandler)
             .setFileContentHashCode(astFileContentHashCode)
+            .setStarlarkContext(context)
             .setImportedExtensions(importMap)
             .build();
-    SkylarkUtils.setToolsRepository(env, toolsRepository);
-    SkylarkUtils.setFragmentMap(env, configurationFragmentMap);
     SkylarkUtils.setPhase(env, Phase.LOADING);
     return env;
   }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java
index efd87e7..7fbf62a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelBuildApiGlobals.java
@@ -16,9 +16,8 @@
 
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkBuildApiGlobals;
-import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.EvalException;
-import com.google.devtools.build.lib.syntax.SkylarkUtils;
 
 /**
  * Bazel implementation of {@link SkylarkBuildApiGlobals}: a collection of global skylark build
@@ -28,9 +27,9 @@
 
   @Override
   public SkylarkLateBoundDefault<?> configurationField(
-      String fragment, String name, Location loc, Environment env)
-      throws EvalException {
-    Class<?> fragmentClass = SkylarkUtils.getFragmentMap(env).get(fragment);
+      String fragment, String name, Location loc, StarlarkContext context) throws EvalException {
+    BazelStarlarkContext bazelContext = (BazelStarlarkContext) context;
+    Class<?> fragmentClass = bazelContext.getFragmentNameToClass().get(fragment);
 
     if (fragmentClass == null) {
       throw new EvalException(
@@ -39,7 +38,7 @@
     }
     try {
       return SkylarkLateBoundDefault.forConfigurationField(
-          fragmentClass, name, SkylarkUtils.getToolsRepository(env));
+          fragmentClass, name, bazelContext.getToolsRepository());
     } catch (SkylarkLateBoundDefault.InvalidConfigurationFieldException exception) {
       throw new EvalException(loc, exception);
     }
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelStarlarkContext.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelStarlarkContext.java
new file mode 100644
index 0000000..7ebf761
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/BazelStarlarkContext.java
@@ -0,0 +1,70 @@
+// 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.analysis.skylark;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+/** An implementation of {@link StarlarkContext} containing Bazel-specific context. */
+public class BazelStarlarkContext implements StarlarkContext {
+  private final String toolsRepository;
+  @Nullable private final ImmutableMap<String, Class<?>> fragmentNameToClass;
+
+  /**
+   * @param toolsRepository the name of the tools repository, such as "bazel_tools"
+   * @param fragmentNameToClass a map from configuration fragment name to configuration fragment
+   *     class, such as "apple" to AppleConfiguration.class
+   */
+  public BazelStarlarkContext(
+      String toolsRepository, ImmutableMap<String, Class<?>> fragmentNameToClass) {
+    this.toolsRepository = toolsRepository;
+    this.fragmentNameToClass = fragmentNameToClass;
+  }
+
+  /** @param toolsRepository the name of the tools repository, such as "bazel_tools" */
+  public BazelStarlarkContext(String toolsRepository) {
+    this(toolsRepository, null);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (this == obj) {
+      return true;
+    }
+    if (!(obj instanceof BazelStarlarkContext)) {
+      return false;
+    }
+    BazelStarlarkContext that = (BazelStarlarkContext) obj;
+    return Objects.equals(this.toolsRepository, that.toolsRepository)
+        && Objects.equals(this.fragmentNameToClass, that.fragmentNameToClass);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(toolsRepository, fragmentNameToClass);
+  }
+
+  /** Returns the name of the tools repository, such as "bazel_tools". */
+  public String getToolsRepository() {
+    return toolsRepository;
+  }
+
+  /** Returns a map from configuration fragment name to configuration fragment class. */
+  public ImmutableMap<String, Class<?>> getFragmentNameToClass() {
+    return fragmentNameToClass;
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
index 5382551..bc9385a 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleClassFunctions.java
@@ -72,6 +72,7 @@
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleFunctionsApi;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkPrinter;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -275,17 +276,19 @@
       Object analysisTest,
       Object buildSetting,
       FuncallExpression ast,
-      Environment funcallEnv)
+      Environment funcallEnv,
+      StarlarkContext context)
       throws EvalException, ConversionException {
     SkylarkUtils.checkLoadingOrWorkspacePhase(funcallEnv, "rule", ast.getLocation());
 
+    BazelStarlarkContext bazelContext = (BazelStarlarkContext) context;
     // analysis_test=true implies test=true.
     test |= Boolean.TRUE.equals(analysisTest);
 
     RuleClassType type = test ? RuleClassType.TEST : RuleClassType.NORMAL;
     RuleClass parent =
         test
-            ? getTestBaseRule(SkylarkUtils.getToolsRepository(funcallEnv))
+            ? getTestBaseRule(bazelContext.getToolsRepository())
             : (executable ? binaryBaseRule : baseRule);
 
     // We'll set the name later, pass the empty string for now.
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
index 4f5afc9..189876b 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageFactory.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
 import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
 import com.google.devtools.build.lib.cmdline.Label;
 import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
 import com.google.devtools.build.lib.cmdline.LabelValidator;
@@ -1612,15 +1613,17 @@
     StoredEventHandler eventHandler = new StoredEventHandler();
 
     try (Mutability mutability = Mutability.create("package %s", packageId)) {
+      BazelStarlarkContext starlarkContext =
+          new BazelStarlarkContext(ruleClassProvider.getToolsRepository());
       Environment pkgEnv =
           Environment.builder(mutability)
               .setGlobals(BazelLibrary.GLOBALS)
               .setSemantics(skylarkSemantics)
               .setEventHandler(eventHandler)
               .setImportedExtensions(imports)
+              .setStarlarkContext(starlarkContext)
               .build();
       SkylarkUtils.setPhase(pkgEnv, Phase.LOADING);
-      SkylarkUtils.setToolsRepository(pkgEnv, ruleClassProvider.getToolsRepository());
 
       pkgBuilder.setFilename(buildFilePath)
           .setDefaultVisibility(defaultVisibility)
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java
index ecb02bc..eedb78a 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkBuildApiGlobals.java
@@ -18,7 +18,7 @@
 import com.google.devtools.build.lib.skylarkinterface.Param;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
-import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.EvalException;
 
 /**
@@ -28,36 +28,35 @@
 public interface SkylarkBuildApiGlobals {
 
   @SkylarkCallable(
-    name = "configuration_field",
-    // TODO(cparsons): Provide a link to documentation for available SkylarkConfigurationFields.
-    doc = "References a late-bound default value for an attribute of type "
-      + "<a href=\"attr.html#label\">label</a>. A value is 'late-bound' if it requires "
-      + "the configuration to be built before determining the value. Any attribute using this "
-      + "as a value must <a href=\"../rules.html#private-attributes\">be private</a>. "
-      + "<p>Example usage: "
-      + "<p>Defining a rule attribute: <br><pre class=language-python>"
-      + "'_foo': attr.label(default=configuration_field(fragment='java', name='toolchain'))</pre>"
-      + "<p>Accessing in rule implementation: <br><pre class=language-python>"
-      + "  def _rule_impl(ctx):\n"
-      + "    foo_info = ctx.attr._foo\n"
-      + "    ...</pre>",
-    parameters = {
+      name = "configuration_field",
+      // TODO(cparsons): Provide a link to documentation for available SkylarkConfigurationFields.
+      doc =
+          "References a late-bound default value for an attribute of type "
+              + "<a href=\"attr.html#label\">label</a>. A value is 'late-bound' if it requires "
+              + "the configuration to be built before determining the value. Any attribute using "
+              + "this as a value must <a href=\"../rules.html#private-attributes\">be private</a>. "
+              + "<p>Example usage: "
+              + "<p>Defining a rule attribute: <br><pre class=language-python>"
+              + "'_foo': attr.label(default=configuration_field(fragment='java', "
+              + "name='toolchain'))</pre>"
+              + "<p>Accessing in rule implementation: <br><pre class=language-python>"
+              + "  def _rule_impl(ctx):\n"
+              + "    foo_info = ctx.attr._foo\n"
+              + "    ...</pre>",
+      parameters = {
         @Param(
             name = "fragment",
             type = String.class,
             named = true,
-            doc = "The name of a configuration fragment which contains the late-bound value."
-        ),
+            doc = "The name of a configuration fragment which contains the late-bound value."),
         @Param(
             name = "name",
             type = String.class,
             named = true,
             doc = "The name of the value to obtain from the configuration fragment."),
-    },
-    useLocation = true,
-    useEnvironment = true
-  )
+      },
+      useLocation = true,
+      useContext = true)
   public LateBoundDefaultApi configurationField(
-      String fragment, String name, Location loc, Environment env)
-      throws EvalException;
+      String fragment, String name, Location loc, StarlarkContext context) throws EvalException;
 }
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
index aab2e53..18543da 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkbuildapi/SkylarkRuleFunctionsApi.java
@@ -22,6 +22,7 @@
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkConstructor;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkGlobalLibrary;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -329,7 +330,8 @@
                     + "added to this rule, with a type corresponding to the value passed in here."),
       },
       useAst = true,
-      useEnvironment = true)
+      useEnvironment = true,
+      useContext = true)
   public BaseFunction rule(
       BaseFunction implementation,
       Boolean test,
@@ -348,7 +350,8 @@
       Object analysisTest,
       Object buildSetting,
       FuncallExpression ast,
-      Environment funcallEnv)
+      Environment funcallEnv,
+      StarlarkContext context)
       throws EvalException;
 
   @SkylarkCallable(
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java
index ee60453..546d255 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/SkylarkCallable.java
@@ -31,12 +31,12 @@
  * {@code @SkylarkModule}.
  *
  * <p>If a method is annotated with {@code @SkylarkCallable}, it is not allowed to have any
- * overloads or hide any static or default methods. Overriding is allowed, but the {@code
- * @SkylarkCallable} annotation itself must not be repeated on the override. This ensures that given
- * a method, we can always determine its corresponding {@code @SkylarkCallable} annotation, if it
- * has one, by scanning all methods of the same name in its class hierarchy, without worrying about
- * complications like overloading or generics. The lookup functionality is implemented by {@link
- * SkylarkInterfaceUtils#getSkylarkCallable}.
+ * overloads or hide any static or default methods. Overriding is allowed, but the
+ * {@code @SkylarkCallable} annotation itself must not be repeated on the override. This ensures
+ * that given a method, we can always determine its corresponding {@code @SkylarkCallable}
+ * annotation, if it has one, by scanning all methods of the same name in its class hierarchy,
+ * without worrying about complications like overloading or generics. The lookup functionality is
+ * implemented by {@link SkylarkInterfaceUtils#getSkylarkCallable}.
  *
  * <p>Methods having this annotation are required to satisfy the following (enforced by an
  * annotation processor):
@@ -46,7 +46,7 @@
  *   <li>If structField=true, there must be zero user-supplied parameters.
  *   <li>The underlying java method's parameters must be supplied in the following order:
  *       <pre>method([positionals]*[named args]*(extra positionals list)(extra kwargs)
- *       (Location)(FuncallExpression)(Envrionment)(SkylarkSemantics))</pre>
+ *       (Location)(FuncallExpression)(Envrionment)(SkylarkSemantics)(StarlarkContext))</pre>
  *       where (extra positionals list) is a SkylarkList if extraPositionals is defined, (extra
  *       kwargs) is a SkylarkDict if extraKeywords is defined, and Location, FuncallExpression,
  *       Environment, and SkylarkSemantics are supplied by the interpreter if and only if
@@ -171,6 +171,13 @@
   boolean useSkylarkSemantics() default false;
 
   /**
+   * If true, StarlarkContext will be passed as an argument of the annotated function. (Thus, the
+   * annotated method signature must contain StarlarkContext as a parameter. See the interface-level
+   * javadoc for details.)
+   */
+  boolean useContext() default false;
+
+  /**
    * If not NONE, the annotated method will only be callable if the given semantic flag is true.
    * Note that at most one of {@link #enableOnlyWithFlag} and {@link #disableWithFlag} can be
    * non-NONE.
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/StarlarkContext.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/StarlarkContext.java
new file mode 100644
index 0000000..a220431
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/StarlarkContext.java
@@ -0,0 +1,29 @@
+// 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.skylarkinterface;
+
+/**
+ * A container of application-specific context which may be passed to Starlark callable functions.
+ *
+ * <p>An application using the Starlark interpreter can provide a custom context implementation to a
+ * Starlark environment using {@link Environment#setStarlarkContext}. The context object will then
+ * be passed to any {@link SkylarkCallable} method with {@link SkylarkCallable#useContext} set to
+ * true.
+ *
+ * <p>Note that the interpreter passes a StarlarkContext to a callable method which requests it, and
+ * does no casting to an application-specific context object. The application's implementation of
+ * the callable method must cast a context object to the application-specific context itself.
+ */
+public interface StarlarkContext {}
diff --git a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
index d9e1db1..ac7aab0 100644
--- a/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
+++ b/src/main/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessor.java
@@ -85,6 +85,8 @@
   private static final String ENVIRONMENT = "com.google.devtools.build.lib.syntax.Environment";
   private static final String SKYLARK_SEMANTICS =
       "com.google.devtools.build.lib.syntax.SkylarkSemantics";
+  private static final String STARLARK_CONTEXT =
+      "com.google.devtools.build.lib.skylarkinterface.StarlarkContext";
 
   @Override
   public SourceVersion getSupportedSourceVersion() {
@@ -448,6 +450,17 @@
                 SKYLARK_SEMANTICS,
                 methodSignatureParams.get(currentIndex).asType().toString()));
       }
+      currentIndex++;
+    }
+    if (annotation.useContext()) {
+      if (!STARLARK_CONTEXT.equals(methodSignatureParams.get(currentIndex).asType().toString())) {
+        throw new SkylarkCallableProcessorException(
+            methodElement,
+            String.format(
+                "Expected parameter index %d to be the %s type, matching useContext, "
+                    + "but was %s",
+                currentIndex, STARLARK_CONTEXT, methodSignatureParams.get(currentIndex).asType()));
+      }
     }
   }
 
@@ -459,6 +472,7 @@
     numExtraInterpreterParams += annotation.useAst() ? 1 : 0;
     numExtraInterpreterParams += annotation.useEnvironment() ? 1 : 0;
     numExtraInterpreterParams += annotation.useSkylarkSemantics() ? 1 : 0;
+    numExtraInterpreterParams += annotation.useContext() ? 1 : 0;
     return numExtraInterpreterParams;
   }
 
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 b8d20f5..3b74a32 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
@@ -28,6 +28,7 @@
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.Mutability.Freezable;
 import com.google.devtools.build.lib.syntax.Mutability.MutabilityException;
 import com.google.devtools.build.lib.syntax.Parser.ParsingLevel;
@@ -746,6 +747,8 @@
   /** The semantics options that affect how Skylark code is evaluated. */
   private final SkylarkSemantics semantics;
 
+  private final StarlarkContext starlarkContext;
+
   /**
    * An EventHandler for errors and warnings. This is not used in the BUILD language, however it
    * might be used in Skylark code called from the BUILD language, so shouldn't be null.
@@ -874,6 +877,7 @@
       GlobalFrame globalFrame,
       LexicalFrame dynamicFrame,
       SkylarkSemantics semantics,
+      StarlarkContext starlarkContext,
       EventHandler eventHandler,
       Map<String, Extension> importedExtensions,
       @Nullable String fileContentHashCode,
@@ -884,6 +888,7 @@
     Preconditions.checkArgument(!globalFrame.mutability().isFrozen());
     Preconditions.checkArgument(!dynamicFrame.mutability().isFrozen());
     this.semantics = semantics;
+    this.starlarkContext = starlarkContext;
     this.eventHandler = eventHandler;
     this.importedExtensions = importedExtensions;
     this.callerLabel = callerLabel;
@@ -901,6 +906,7 @@
     private final Mutability mutability;
     @Nullable private GlobalFrame parent;
     @Nullable private SkylarkSemantics semantics;
+    @Nullable private StarlarkContext starlarkContext;
     @Nullable private EventHandler eventHandler;
     @Nullable private Map<String, Extension> importedExtensions;
     @Nullable private String fileContentHashCode;
@@ -908,6 +914,8 @@
 
     Builder(Mutability mutability) {
       this.mutability = mutability;
+      // TODO(cparsons): Require specifying a starlarkContext (or declaring use of an empty stub).
+      this.starlarkContext = new StarlarkContext() {};
     }
 
     /**
@@ -931,6 +939,16 @@
       return this;
     }
 
+    public Builder setStarlarkContext(StarlarkContext starlarkContext) {
+      this.starlarkContext = starlarkContext;
+      return this;
+    }
+
+    public Builder useEmptyStarlarkContext() {
+      this.starlarkContext = new StarlarkContext() {};
+      return this;
+    }
+
     /** Sets an EventHandler for errors and warnings. */
     public Builder setEventHandler(EventHandler eventHandler) {
       Preconditions.checkState(this.eventHandler == null);
@@ -988,6 +1006,7 @@
           globalFrame,
           dynamicFrame,
           semantics,
+          starlarkContext,
           eventHandler,
           importedExtensions,
           fileContentHashCode,
@@ -1169,6 +1188,10 @@
     return semantics;
   }
 
+  public StarlarkContext getStarlarkContext() {
+    return starlarkContext;
+  }
+
   public void handleEvent(Event event) {
     eventHandler.handle(event);
   }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
index b44411a..bca295a 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FuncallExpression.java
@@ -466,7 +466,8 @@
     Preconditions.checkArgument(
         !methodDescriptor.isUseEnvironment()
             || !methodDescriptor.isUseSkylarkSemantics()
-            || !methodDescriptor.isUseLocation(),
+            || !methodDescriptor.isUseLocation()
+            || !methodDescriptor.isUseContext(),
         "Cannot be invoked on structField callables with extra interpreter params");
     return methodDescriptor.call(obj, new Object[0], Location.BUILTIN, null);
   }
@@ -725,6 +726,9 @@
     if (method.isUseSkylarkSemantics()) {
       builder.add(env.getSemantics());
     }
+    if (method.isUseContext()) {
+      builder.add(env.getStarlarkContext());
+    }
   }
 
   private static String formatMethod(
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java b/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
index 6335f15..1ca3516 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/MethodDescriptor.java
@@ -47,6 +47,7 @@
   private final boolean useAst;
   private final boolean useEnvironment;
   private final boolean useSkylarkSemantics;
+  private final boolean useContext;
 
   private MethodDescriptor(
       Method method,
@@ -63,7 +64,8 @@
       boolean useLocation,
       boolean useAst,
       boolean useEnvironment,
-      boolean useSkylarkSemantics) {
+      boolean useSkylarkSemantics,
+      boolean useContext) {
     this.method = method;
     this.annotation = annotation;
     this.name = name;
@@ -79,6 +81,7 @@
     this.useAst = useAst;
     this.useEnvironment = useEnvironment;
     this.useSkylarkSemantics = useSkylarkSemantics;
+    this.useContext = useContext;
   }
 
   /** Returns the SkylarkCallable annotation corresponding to this method. */
@@ -109,7 +112,8 @@
         annotation.useLocation(),
         annotation.useAst(),
         annotation.useEnvironment(),
-        annotation.useSkylarkSemantics());
+        annotation.useSkylarkSemantics(),
+        annotation.useContext());
   }
 
   /** @return The result of this method invocation on the {@code obj} as a target. */
@@ -188,6 +192,11 @@
     return useSkylarkSemantics;
   }
 
+  /** See {@link SkylarkCallable#useContext()}. */
+  boolean isUseContext() {
+    return useContext;
+  }
+
   /** @see SkylarkCallable#useLocation() */
   public boolean isUseLocation() {
     return useLocation;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
index db46c1d..8b5b409 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkUtils.java
@@ -14,80 +14,13 @@
 
 package com.google.devtools.build.lib.syntax;
 
-import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.lib.events.Location;
-import java.util.Objects;
 
 /** This class contains Bazel-specific functions to extend or interoperate with Skylark. */
 public final class SkylarkUtils {
 
-  /** Bazel-specific information that we store in the Environment. */
-  private static class BazelInfo {
-    String toolsRepository;
-    ImmutableMap<String, Class<?>> fragmentNameToClass;
-
-    @Override
-    public boolean equals(Object obj) {
-      if (this == obj) {
-        return true;
-      }
-      if (!(obj instanceof BazelInfo)) {
-        return false;
-      }
-      BazelInfo that = (BazelInfo) obj;
-      return Objects.equals(this.toolsRepository, that.toolsRepository)
-          && Objects.equals(this.fragmentNameToClass, that.fragmentNameToClass);
-    }
-
-    @Override
-    public int hashCode() {
-      return Objects.hash(toolsRepository, fragmentNameToClass);
-    }
-  }
-
-  private static final String BAZEL_INFO_KEY = "$bazel";
   private static final String PHASE_KEY = "$phase";
 
-  private static BazelInfo getInfo(Environment env) {
-    Object info = env.moduleLookup(BAZEL_INFO_KEY);
-    if (info != null) {
-      return (BazelInfo) info;
-    }
-
-    BazelInfo result = new BazelInfo();
-    try {
-      env.update(BAZEL_INFO_KEY, result);
-      return result;
-    } catch (EvalException e) {
-      throw new AssertionError(e);
-    }
-  }
-
-  public static void setToolsRepository(Environment env, String toolsRepository) {
-    getInfo(env).toolsRepository = toolsRepository;
-  }
-
-  public static String getToolsRepository(Environment env) {
-    return getInfo(env).toolsRepository;
-  }
-
-  /**
-   * Sets, on an {@link Environment}, a {@link Map} from configuration fragment name to
-   * configuration fragment class.
-   */
-  public static void setFragmentMap(Environment env,
-      ImmutableMap<String, Class<?>> fragmentNameToClass) {
-    getInfo(env).fragmentNameToClass = fragmentNameToClass;
-  }
-
-  /**
-   * Returns the {@link Map} from configuration fragment name to configuration fragment class, as
-   * set by {@link #setFragmentMap}.
-   */
-  public static ImmutableMap<String, Class<?>> getFragmentMap(Environment env) {
-    return getInfo(env).fragmentNameToClass;
-  }
-
   /** A phase for enabling or disabling certain builtin functions */
   public enum Phase {
     WORKSPACE,
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java
index d23bae5..65beb6f 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeBuildApiGlobals.java
@@ -17,7 +17,7 @@
 import com.google.devtools.build.lib.events.Location;
 import com.google.devtools.build.lib.skylarkbuildapi.LateBoundDefaultApi;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkBuildApiGlobals;
-import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.EvalException;
 
 /**
@@ -26,8 +26,8 @@
 public class FakeBuildApiGlobals implements SkylarkBuildApiGlobals {
 
   @Override
-  public LateBoundDefaultApi configurationField(String fragment, String name, Location loc,
-      Environment env) throws EvalException {
+  public LateBoundDefaultApi configurationField(
+      String fragment, String name, Location loc, StarlarkContext context) throws EvalException {
     return new FakeLateBoundDefaultApi();
   }
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
index af8110a..7e76957 100644
--- a/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
+++ b/src/main/java/com/google/devtools/build/skydoc/fakebuildapi/FakeSkylarkRuleFunctionsApi.java
@@ -24,6 +24,7 @@
 import com.google.devtools.build.lib.skylarkbuildapi.ProviderApi;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkAspectApi;
 import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleFunctionsApi;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -119,7 +120,8 @@
       Object analysisTest,
       Object buildSetting,
       FuncallExpression ast,
-      Environment funcallEnv)
+      Environment funcallEnv,
+      StarlarkContext context)
       throws EvalException {
     List<AttributeInfo> attrInfos;
     ImmutableMap.Builder<String, FakeDescriptor> attrsMapBuilder = ImmutableMap.builder();
diff --git a/src/test/java/com/google/devtools/build/lib/BUILD b/src/test/java/com/google/devtools/build/lib/BUILD
index 01ee15d..89da219 100644
--- a/src/test/java/com/google/devtools/build/lib/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/BUILD
@@ -1119,6 +1119,7 @@
         ":testutil",
         "//src/main/java/com/google/devtools/build/lib:bazel-main",
         "//src/main/java/com/google/devtools/build/lib:bazel-rules",
+        "//src/main/java/com/google/devtools/build/lib:build-base",
         "//src/main/java/com/google/devtools/build/lib:events",
         "//src/main/java/com/google/devtools/build/lib:packages",
         "//src/main/java/com/google/devtools/build/lib:util",
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java b/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java
index 8abf90b..94043f5 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/util/SkylarkTestCase.java
@@ -22,6 +22,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Lists;
 import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
 import com.google.devtools.build.lib.analysis.skylark.SkylarkModules;
 import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext;
 import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
@@ -67,6 +68,7 @@
     return new EvaluationTestCase() {
       @Override
       public Environment newEnvironment() throws Exception {
+        BazelStarlarkContext context = new BazelStarlarkContext(TestConstants.TOOLS_REPOSITORY);
         Environment env =
             Environment.builder(mutability)
                 .setSemantics(semantics)
@@ -75,13 +77,13 @@
                     getSkylarkGlobals()
                         .withLabel(
                             Label.parseAbsoluteUnchecked("//test:label", /*defaultToMain=*/ false)))
+                .setStarlarkContext(context)
                 .build()
                 .setupDynamic(
                     PackageFactory.PKG_CONTEXT,
                     // This dummy pkgContext works because no Skylark unit test attempts to actually
                     // create rules. Creating actual rules is tested in SkylarkIntegrationTest.
                     new PackageContext(null, null, getEventHandler(), null));
-        SkylarkUtils.setToolsRepository(env, TestConstants.TOOLS_REPOSITORY);
         SkylarkUtils.setPhase(env, Phase.LOADING);
         return env;
       }
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD
index 43a24e2..7b380c1 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/BUILD
@@ -16,6 +16,7 @@
     resources = [":ProcessorTestFiles"],
     deps = [
         "//src/main/java/com/google/devtools/build/lib:events",
+        "//src/main/java/com/google/devtools/build/lib:skylarkinterface",
         "//src/main/java/com/google/devtools/build/lib:syntax",
         "//src/main/java/com/google/devtools/build/lib/skylarkinterface/processor:annotation_preprocessor",
         "//third_party:compile_testing",
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
index aa78222..22c3e28 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/SkylarkCallableProcessorTest.java
@@ -19,6 +19,7 @@
 
 import com.google.common.io.Resources;
 import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.testing.compile.JavaFileObjects;
 import javax.tools.JavaFileObject;
@@ -143,6 +144,18 @@
   }
 
   @Test
+  public void testContextMissing() throws Exception {
+    assertAbout(javaSource())
+        .that(getFile("ContextMissing.java"))
+        .processedWith(new SkylarkCallableProcessor())
+        .failsToCompile()
+        .withErrorContaining(
+            "Expected parameter index 2 to be the "
+                + StarlarkContext.class.getCanonicalName()
+                + " type, matching useContext, but was java.lang.String");
+  }
+
+  @Test
   public void testLocationMissing() throws Exception {
     assertAbout(javaSource())
         .that(getFile("LocationMissing.java"))
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ContextMissing.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ContextMissing.java
new file mode 100644
index 0000000..1af6d4d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/ContextMissing.java
@@ -0,0 +1,37 @@
+// 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.skylarkinterface.processor.testsources;
+
+import com.google.devtools.build.lib.skylarkinterface.Param;
+import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+
+/**
+ * Test case for a SkylarkCallable method which does not have an appropriate StarlarkContext
+ * parameter despite having useContext set.
+ */
+public class ContextMissing {
+
+  @SkylarkCallable(
+      name = "three_arg_method_missing_context",
+      documented = false,
+      parameters = {
+        @Param(name = "one", type = String.class, named = true),
+        @Param(name = "two", type = Integer.class, named = true),
+      },
+      useContext = true)
+  public String threeArgMethod(String one, Integer two, String shouldBeContext) {
+    return "bar";
+  }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
index 579c7e2..05ce965 100644
--- a/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
+++ b/src/test/java/com/google/devtools/build/lib/skylarkinterface/processor/testsources/GoldenCase.java
@@ -18,6 +18,7 @@
 import com.google.devtools.build.lib.skylarkinterface.Param;
 import com.google.devtools.build.lib.skylarkinterface.ParamType;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.Environment;
 import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.SkylarkDict;
@@ -157,18 +158,18 @@
   }
 
   @SkylarkCallable(
-    name = "two_arg_method_with_params_and_info_and_kwargs",
-    documented = false,
-    parameters = {
-      @Param(name = "one", type = String.class, named = true),
-      @Param(name = "two", type = Integer.class, named = true),
-    },
-    extraKeywords = @Param(name = "kwargs"),
-    useAst = true,
-    useLocation = true,
-    useEnvironment = true,
-    useSkylarkSemantics = true
-  )
+      name = "two_arg_method_with_params_and_info_and_kwargs",
+      documented = false,
+      parameters = {
+        @Param(name = "one", type = String.class, named = true),
+        @Param(name = "two", type = Integer.class, named = true),
+      },
+      extraKeywords = @Param(name = "kwargs"),
+      useAst = true,
+      useLocation = true,
+      useEnvironment = true,
+      useSkylarkSemantics = true,
+      useContext = true)
   public String twoArgMethodWithParamsAndInfoAndKwargs(
       String one,
       Integer two,
@@ -176,7 +177,8 @@
       Location location,
       FuncallExpression ast,
       Environment environment,
-      SkylarkSemantics skylarkSemantics) {
+      SkylarkSemantics skylarkSemantics,
+      StarlarkContext context) {
     return "blep";
   }
 
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index e7f3861..04f20d8 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -35,6 +35,7 @@
 import com.google.devtools.build.lib.skylarkinterface.SkylarkModule;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkSignature;
 import com.google.devtools.build.lib.skylarkinterface.SkylarkValue;
+import com.google.devtools.build.lib.skylarkinterface.StarlarkContext;
 import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
 import com.google.devtools.build.lib.syntax.SkylarkSemantics.FlagIdentifier;
 import com.google.devtools.build.lib.testutil.TestMode;
@@ -308,15 +309,19 @@
     }
 
     @SkylarkCallable(
-      name = "with_extra",
-      documented = false,
-      useLocation = true,
-      useAst = true,
-      useEnvironment = true,
-      useSkylarkSemantics = true
-    )
+        name = "with_extra",
+        documented = false,
+        useLocation = true,
+        useAst = true,
+        useEnvironment = true,
+        useSkylarkSemantics = true,
+        useContext = true)
     public String withExtraInterpreterParams(
-        Location location, FuncallExpression func, Environment env, SkylarkSemantics sem) {
+        Location location,
+        FuncallExpression func,
+        Environment env,
+        SkylarkSemantics sem,
+        StarlarkContext context) {
       return "with_extra("
           + location.getStartLine()
           + ", "
@@ -325,6 +330,8 @@
           + env.isGlobal()
           + ", "
           + (sem != null)
+          + ", "
+          + (context != null)
           + ")";
     }
 
@@ -1304,7 +1311,7 @@
     new SkylarkTest()
         .update("mock", new Mock())
         .setUp("v = mock.with_extra()")
-        .testLookup("v", "with_extra(1, 0, true, true)");
+        .testLookup("v", "with_extra(1, 0, true, true, true)");
   }
 
   @Test
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java b/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
index ac07655..f7f8ee8 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/util/EvaluationTestCase.java
@@ -19,6 +19,7 @@
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.truth.Ordered;
+import com.google.devtools.build.lib.analysis.skylark.BazelStarlarkContext;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventCollector;
 import com.google.devtools.build.lib.events.EventKind;
@@ -63,13 +64,14 @@
    * No PythonPreprocessing, mostly empty mutable Environment.
    */
   public Environment newBuildEnvironment() {
+    BazelStarlarkContext context = new BazelStarlarkContext(TestConstants.TOOLS_REPOSITORY);
     Environment env =
         Environment.builder(mutability)
             .useDefaultSemantics()
             .setGlobals(BazelLibrary.GLOBALS)
             .setEventHandler(getEventHandler())
+            .setStarlarkContext(context)
             .build();
-    SkylarkUtils.setToolsRepository(env, TestConstants.TOOLS_REPOSITORY);
     SkylarkUtils.setPhase(env, Phase.LOADING);
     return env;
   }