bazel syntax: delete BuiltinFunction

PackageFactory's BuiltinRuleFunction (rule) and
WorkspaceFactory's newRuleFunction (repository_rule)
both now extend BaseFunction directly, without BuiltinFunction.
(We can further simplify by eliminating BaseFunction in a follow-up.)
Both functions now do their own argument checking, which is trivial:
they merely reject positional arguments.

The push/pop operations on the AllocationTracker Callstack are now
done by StarlarkThread.push/pop, so the stack contains all callables,
not just those that implement BaseFunction.
(The whole thing is redundant and will be eliminated.)

The BaseFunction constructor that accepts default values was
made package-private; only StarlarkFunction needs it.

Also, simplify the exception handling. Starlark.call is the sole place
that augments an EvalException with a location.

PiperOrigin-RevId: 285080959
diff --git a/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java b/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java
index c909d9e..41a5b8a 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/NativeProvider.java
@@ -69,7 +69,7 @@
   }
 
   protected NativeProvider(Class<V> valueClass, String name) {
-    super(FunctionSignature.KWARGS, /*defaultValues=*/ null);
+    super(FunctionSignature.KWARGS);
     this.name = name;
     this.key = new NativeKey(name, getClass());
     this.valueClass = valueClass;
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 58a1f6e..e5446be 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
@@ -37,7 +37,6 @@
 import com.google.devtools.build.lib.packages.RuleFactory.BuildLangTypedAttributeValuesMap;
 import com.google.devtools.build.lib.syntax.Argument;
 import com.google.devtools.build.lib.syntax.BaseFunction;
-import com.google.devtools.build.lib.syntax.BuiltinFunction;
 import com.google.devtools.build.lib.syntax.ClassObject;
 import com.google.devtools.build.lib.syntax.DefStatement;
 import com.google.devtools.build.lib.syntax.EvalException;
@@ -581,20 +580,31 @@
   }
 
   /**
-   * {@link BuiltinFunction} adapter for creating {@link Rule}s for native {@link
+   * {@link BaseFunction} adapter for creating {@link Rule}s for native {@link
    * com.google.devtools.build.lib.packages.RuleClass}es.
    */
-  private static class BuiltinRuleFunction extends BuiltinFunction implements RuleFunction {
+  private static class BuiltinRuleFunction extends BaseFunction implements RuleFunction {
     private final RuleClass ruleClass;
 
     BuiltinRuleFunction(RuleClass ruleClass) {
+      // TODO(adonovan): the only thing BaseFunction is doing for us is holding
+      // an (uninteresting) FunctionSignature. Can we extend StarlarkCallable directly?
+      // Only docgen appears to depend on BaseFunction.
       super(FunctionSignature.KWARGS);
       this.ruleClass = Preconditions.checkNotNull(ruleClass);
     }
 
-    @SuppressWarnings("unused")
-    public NoneType invoke(Map<String, Object> kwargs, Location loc, StarlarkThread thread)
+    @Override
+    public NoneType callImpl(
+        StarlarkThread thread,
+        @Nullable FuncallExpression call,
+        List<Object> args,
+        Map<String, Object> kwargs)
         throws EvalException, InterruptedException {
+      if (!args.isEmpty()) {
+        throw new EvalException(null, "unexpected positional arguments");
+      }
+      Location loc = call != null ? call.getLocation() : Location.BUILTIN;
       SkylarkUtils.checkLoadingOrWorkspacePhase(thread, ruleClass.getName(), loc);
       try {
         addRule(getContext(thread, loc), kwargs, loc, thread);
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkProvider.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkProvider.java
index f8d6012..baa4cbe 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkProvider.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkProvider.java
@@ -127,7 +127,7 @@
    */
   private SkylarkProvider(
       @Nullable SkylarkKey key, @Nullable ImmutableList<String> fields, Location location) {
-    super(buildSignature(fields), /*defaultValues=*/ null);
+    super(buildSignature(fields));
     this.location = location;
     this.fields = fields;
     this.key = key;  // possibly null
diff --git a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
index 378623ec..af97687 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/WorkspaceFactory.java
@@ -14,8 +14,6 @@
 
 package com.google.devtools.build.lib.packages;
 
-import static com.google.devtools.build.lib.syntax.Starlark.NONE;
-
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
@@ -30,10 +28,10 @@
 import com.google.devtools.build.lib.packages.Package.NameConflictException;
 import com.google.devtools.build.lib.packages.PackageFactory.EnvironmentExtension;
 import com.google.devtools.build.lib.syntax.BaseFunction;
-import com.google.devtools.build.lib.syntax.BuiltinFunction;
 import com.google.devtools.build.lib.syntax.ClassObject;
 import com.google.devtools.build.lib.syntax.EvalException;
 import com.google.devtools.build.lib.syntax.EvalUtils;
+import com.google.devtools.build.lib.syntax.FuncallExpression;
 import com.google.devtools.build.lib.syntax.FunctionSignature;
 import com.google.devtools.build.lib.syntax.Module;
 import com.google.devtools.build.lib.syntax.Mutability;
@@ -51,6 +49,7 @@
 import com.google.devtools.build.lib.vfs.Root;
 import com.google.devtools.build.lib.vfs.RootedPath;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import javax.annotation.Nullable;
 
@@ -259,16 +258,28 @@
    * Returns a function-value implementing the build or workspace rule "ruleClass" (e.g. cc_library)
    * in the specified package context.
    */
-  private static BuiltinFunction newRuleFunction(
+  private static BaseFunction newRuleFunction(
       final RuleFactory ruleFactory, final String ruleClassName, final boolean allowOverride) {
-    return new BuiltinFunction(FunctionSignature.KWARGS) {
+    // TODO(adonovan): the only thing BaseFunction is doing for us is holding
+    // an (uninteresting) FunctionSignature. Can we extend StarlarkCallable directly?
+    // Only docgen appears to depend on BaseFunction.
+    return new BaseFunction(FunctionSignature.KWARGS) {
       @Override
       public String getName() {
         return ruleClassName;
       }
 
-      public Object invoke(Map<String, Object> kwargs, Location loc, StarlarkThread thread)
+      @Override
+      public Object callImpl(
+          StarlarkThread thread,
+          @Nullable FuncallExpression call,
+          List<Object> args,
+          Map<String, Object> kwargs)
           throws EvalException, InterruptedException {
+        if (!args.isEmpty()) {
+          throw new EvalException(null, "unexpected positional arguments");
+        }
+        Location loc = call != null ? call.getLocation() : Location.BUILTIN;
         try {
           Package.Builder builder = PackageFactory.getContext(thread, loc).pkgBuilder;
           String externalRepoName = (String) kwargs.get("name");
@@ -308,7 +319,7 @@
             | LabelSyntaxException e) {
           throw new EvalException(loc, e.getMessage());
         }
-        return NONE;
+        return Starlark.NONE;
       }
     };
   }
diff --git a/src/main/java/com/google/devtools/build/lib/profiler/memory/AllocationTracker.java b/src/main/java/com/google/devtools/build/lib/profiler/memory/AllocationTracker.java
index af51d73..d6bbdbf 100644
--- a/src/main/java/com/google/devtools/build/lib/profiler/memory/AllocationTracker.java
+++ b/src/main/java/com/google/devtools/build/lib/profiler/memory/AllocationTracker.java
@@ -25,9 +25,9 @@
 import com.google.devtools.build.lib.packages.AspectClass;
 import com.google.devtools.build.lib.packages.RuleClass;
 import com.google.devtools.build.lib.packages.RuleFunction;
-import com.google.devtools.build.lib.syntax.BaseFunction;
 import com.google.devtools.build.lib.syntax.Callstack;
 import com.google.devtools.build.lib.syntax.Node;
+import com.google.devtools.build.lib.syntax.StarlarkCallable;
 import com.google.monitoring.runtime.instrumentation.Sampler;
 import com.google.perftools.profiles.ProfileProto.Function;
 import com.google.perftools.profiles.ProfileProto.Line;
@@ -256,8 +256,8 @@
           final Location location;
           if (object instanceof Node) {
             location = ((Node) object).getLocation();
-          } else if (object instanceof BaseFunction) {
-            location = ((BaseFunction) object).getLocation();
+          } else if (object instanceof StarlarkCallable) {
+            location = ((StarlarkCallable) object).getLocation();
           } else {
             throw new IllegalStateException(
                 "Unknown node type: " + object.getClass().getSimpleName());
@@ -269,12 +269,8 @@
             file = "<native>";
           }
         }
-        String function = null;
-        if (object instanceof BaseFunction) {
-          BaseFunction baseFunction = (BaseFunction) object;
-          function = baseFunction.getName();
-        }
-        if (function != null) {
+        if (object instanceof StarlarkCallable) {
+          String function = ((StarlarkCallable) object).getName();
           sample.addLocationId(
               locationTable.get(Strings.nullToEmpty(file), Strings.nullToEmpty(function), line));
           line = -1;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BUILD b/src/main/java/com/google/devtools/build/lib/syntax/BUILD
index 348ab44..770e47a 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BUILD
@@ -91,7 +91,6 @@
     srcs = [
         "BaseFunction.java",
         "BuiltinCallable.java",
-        "BuiltinFunction.java",
         "CallUtils.java",
         "Callstack.java",
         "ClassObject.java",
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
index 307c485..c354797 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
@@ -26,32 +26,19 @@
 import javax.annotation.Nullable;
 
 /**
- * A base class for Skylark functions, whether builtin or user-defined.
- *
- * <p>Nomenclature: We call "Parameters" the formal parameters of a function definition. We call
- * "Arguments" the actual values supplied at the call site.
- *
- * <p>The outer calling convention is like that of python3, with named parameters that can be
- * mandatory or optional, and also be positional or named-only, and rest parameters for extra
- * positional and keyword arguments. Callers supply a {@code List<Object>} args for positional
- * arguments and a {@code Map<String, Object>} for keyword arguments, where positional arguments
- * will be resolved first, then keyword arguments, with errors for a clash between the two, for
- * missing mandatory parameter, or for unexpected extra positional or keyword argument in absence of
- * rest parameter.
- *
- * <p>The inner calling convention is to pass the underlying method an {@code Object[]} of the
- * type-checked argument values, one per expected parameter, parameters being sorted as documented
- * in {@link FunctionSignature}.
- *
- * <p>The function may provide default values for optional parameters not provided by the caller.
- * These default values can be null if there are no optional parameters or for builtin functions,
- * but not for user-defined functions that have optional parameters.
+ * BaseFunction is a base class for functions that have a FunctionSignature and optional default
+ * values. Its {@link #callImpl} method converts the positional and named arguments into an array of
+ * values corresponding to the parameters of the FunctionSignature, then calls the subclass's {@link
+ * #call} method with this array.
  */
+// TODO(adonovan): express the processArguments functionality of this class as a standalone function
+// that takes signature and defaultValues as explicit parameters, and do away with this class. There
+// is no real need for a concept of "StarlarkCallable with Signature", though a few places in Bazel
+// rely on this concept. For example, the Args.add_all(map_all=..., map_each=...) parameters have
+// their signatures checked to discover errors eagerly.
+// Only StarlarkFunction needs getDefaultValues().
 public abstract class BaseFunction implements StarlarkCallable {
 
-  // TODO(adonovan): Turn fields into abstract methods. Make processArguments a static function
-  // with multiple parameters, instead of a "mix-in" that accesses instance fields.
-
   private final FunctionSignature signature;
 
   /**
@@ -77,7 +64,7 @@
   }
 
   /** Constructs a BaseFunction with a given signature and default values. */
-  protected BaseFunction(FunctionSignature signature, @Nullable List<Object> defaultValues) {
+  BaseFunction(FunctionSignature signature, @Nullable List<Object> defaultValues) {
     this.signature = Preconditions.checkNotNull(signature);
     this.defaultValues = defaultValues;
     if (defaultValues != null) {
@@ -91,23 +78,15 @@
   }
 
   /**
-   * The size of the array required by the callee.
-   */
-  protected int getArgArraySize() {
-    return signature.numParameters();
-  }
-
-  /**
    * Process the caller-provided arguments into an array suitable for the callee (this function).
    */
-  public Object[] processArguments(
+  private Object[] processArguments(
       List<Object> args,
       @Nullable Map<String, Object> kwargs,
       @Nullable Location loc,
       @Nullable StarlarkThread thread)
       throws EvalException {
-
-    Object[] arguments = new Object[getArgArraySize()];
+    Object[] arguments = new Object[signature.numParameters()];
 
     ImmutableList<String> names = signature.getParameterNames();
 
@@ -276,20 +255,8 @@
       throws EvalException, InterruptedException {
     // call is null when called from Java (as there's no Skylark call site).
     Location loc = call == null ? Location.BUILTIN : call.getLocation();
-
     Object[] arguments = processArguments(args, kwargs, loc, thread);
-
-    // TODO(adonovan): move Callstack.push/pop into StarlarkThread.push/pop.
-    try {
-      if (Callstack.enabled) {
-        Callstack.push(this);
-      }
-      return call(arguments, call, thread);
-    } finally {
-      if (Callstack.enabled) {
-        Callstack.pop();
-      }
-    }
+    return call(arguments, call, thread);
   }
 
   /**
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java
index 879ed7e..b8ed052 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinCallable.java
@@ -70,7 +70,7 @@
     Object[] javaArguments =
         CallUtils.convertStarlarkArgumentsToJavaMethodArguments(
             thread, call, desc, objValue.getClass(), args, kwargs);
-    return desc.call(objValue, javaArguments, call.getLocation(), thread.mutability());
+    return desc.call(objValue, javaArguments, thread.mutability());
   }
 
   private MethodDescriptor getMethodDescriptor(StarlarkSemantics semantics) {
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
deleted file mode 100644
index 1a5a6f2..0000000
--- a/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
+++ /dev/null
@@ -1,220 +0,0 @@
-// Copyright 2014 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.syntax;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Throwables;
-import com.google.common.collect.Lists;
-import com.google.devtools.build.lib.events.Location;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Arrays;
-import java.util.List;
-import java.util.NoSuchElementException;
-import javax.annotation.Nullable;
-
-/**
- * A class for Skylark functions provided as builtins by the Skylark implementation. Instances of
- * this class do not need to be serializable because they should effectively be treated as
- * constants.
- */
-public abstract class BuiltinFunction extends BaseFunction {
-
-  // The underlying invoke() method.
-  @Nullable private Method invokeMethod;
-
-  // Classes of extra arguments required beside signature,
-  // computed by configure from parameter types of invoke method.
-  // TODO(adonovan): eliminate Location, FuncallExpression when they can be derived from the thread.
-  private Class<?>[] extraParams; // ordered subset of {Location,FuncallExpression,StarlarkThread}
-
-  // The returnType of the method.
-  private Class<?> returnType;
-
-  /** Creates a BuiltinFunction with the given signature. */
-  protected BuiltinFunction(FunctionSignature signature) {
-    super(signature);
-    initialize();
-  }
-
-  @Override
-  protected final int getArgArraySize() {
-    return invokeMethod.getParameterCount();
-  }
-
-  @Override
-  @Nullable
-  public Object call(Object[] args, @Nullable FuncallExpression ast, StarlarkThread thread)
-      throws EvalException, InterruptedException {
-    Preconditions.checkNotNull(thread);
-
-    // ast is null when called from Java (as there's no Skylark call site).
-    Location loc = ast == null ? Location.BUILTIN : ast.getLocation();
-
-    // Add extra arguments as needed.
-    {
-      int i = args.length - extraParams.length;
-      for (Class<?> cls : extraParams) {
-        if (cls == Location.class) {
-          args[i] = loc;
-        } else if (cls == FuncallExpression.class) {
-          args[i] = ast;
-        } else if (cls == StarlarkThread.class) {
-          args[i] = thread;
-        } else {
-          throw new IllegalStateException("invalid extra argument: " + cls);
-        }
-        i++;
-      }
-    }
-
-    // Last but not least, actually make an inner call to the function with the resolved arguments.
-    try {
-      return invokeMethod.invoke(this, args);
-    } catch (InvocationTargetException x) {
-      Throwable e = x.getCause();
-
-      if (e instanceof EvalException) {
-        throw ((EvalException) e).ensureLocation(loc);
-      } else if (e instanceof IllegalArgumentException) {
-        throw new EvalException(loc, "illegal argument in call to " + getName(), e);
-      }
-      Throwables.throwIfInstanceOf(e, InterruptedException.class);
-      Throwables.throwIfUnchecked(e);
-      throw badCallException(loc, e, args);
-    } catch (IllegalArgumentException e) {
-      // Either this was thrown by Java itself, or it's a bug
-      // To cover the first case, let's manually check the arguments.
-      final int len = args.length - extraParams.length;
-      final Class<?>[] types = invokeMethod.getParameterTypes();
-      for (int i = 0; i < args.length; i++) {
-        if (args[i] != null && !types[i].isAssignableFrom(args[i].getClass())) {
-          String paramName =
-              i < len ? getSignature().getParameterNames().get(i) : extraParams[i - len].getName();
-          throw new EvalException(
-              loc,
-              String.format(
-                  "argument '%s' has type '%s', but should be '%s'\nin call to %s",
-                  paramName,
-                  EvalUtils.getDataTypeName(args[i]),
-                  EvalUtils.getDataTypeNameFromClass(types[i]),
-                  getShortSignature()));
-        }
-      }
-      throw badCallException(loc, e, args);
-    } catch (IllegalAccessException e) {
-      throw badCallException(loc, e, args);
-    }
-  }
-
-  private static String stacktraceToString(StackTraceElement[] elts) {
-    StringBuilder b = new StringBuilder();
-    for (StackTraceElement e : elts) {
-      b.append(e);
-      b.append("\n");
-    }
-    return b.toString();
-  }
-
-  private IllegalStateException badCallException(Location loc, Throwable e, Object... args) {
-    // If this happens, it's a bug in our code.
-    return new IllegalStateException(
-        String.format(
-            "%s%s (%s)\n" + "while calling %s with args %s\n" + "Java parameter types: %s",
-            (loc == null) ? "" : loc + ": ",
-            Arrays.asList(args),
-            e.getClass().getName(),
-            stacktraceToString(e.getStackTrace()),
-            this,
-            Arrays.asList(invokeMethod.getParameterTypes())),
-        e);
-  }
-
-  // Configures the reflection mechanism.
-  private final void initialize() {
-    this.invokeMethod = findMethod(this.getClass(), "invoke");
-    Class<?>[] parameterTypes = invokeMethod.getParameterTypes();
-    int numParameters = getSignature().numParameters();
-    this.extraParams = extraParams(numParameters, parameterTypes);
-
-    if (returnType != null) {
-      Class<?> type = returnType;
-      Class<?> methodReturnType = invokeMethod.getReturnType();
-      Preconditions.checkArgument(
-          type == methodReturnType,
-          "signature for function %s says it returns %s but its invoke method returns %s",
-          getName(),
-          returnType,
-          methodReturnType);
-    }
-  }
-
-  /**
-   * Returns the signature as "[className.]methodName(name1: paramType1, name2: paramType2, ...)"
-   */
-  private String getShortSignature() {
-    StringBuilder builder = new StringBuilder();
-    builder.append(getName()).append("(");
-    getSignature().toStringBuilder(builder, /*defaultValuePrinter=*/ null);
-    builder.append(")");
-    return builder.toString();
-  }
-
-  // Returns the list of extra parameters beyond those in the signature.
-  private Class<?>[] extraParams(int i, Class<?>[] parameterTypes) {
-    List<Class<?>> extra = Lists.newArrayList();
-    for (Class<?> cls : EXTRA_PARAM_CLASSES) {
-      if (i < parameterTypes.length && parameterTypes[i] == cls) {
-        extra.add(cls);
-        i++;
-      }
-    }
-    if (i != parameterTypes.length) {
-      throw new IllegalStateException(
-          String.format(
-              "bad argument count for %s: method has %s arguments, type list has %s",
-              getName(), i, parameterTypes.length));
-    }
-    return extra.toArray(new Class<?>[0]);
-  }
-
-  private static final Class<?>[] EXTRA_PARAM_CLASSES = {
-    Location.class, FuncallExpression.class, StarlarkThread.class
-  };
-
-  // finds the method and makes it accessible (which is needed to find it, and later to use it)
-  private static Method findMethod(Class<?> cls, String name) {
-    Method found = null;
-    for (Method method : cls.getDeclaredMethods()) {
-      method.setAccessible(true);
-      if (name.equals(method.getName())) {
-        if (found != null) {
-          throw new IllegalArgumentException(
-              String.format("class %s has more than one method named %s", cls.getName(), name));
-        }
-        found = method;
-      }
-    }
-    if (found == null) {
-      throw new NoSuchElementException(
-          String.format("class %s doesn't have a method named %s", cls.getName(), name));
-    }
-    return found;
-  }
-
-  @Override
-  public void repr(Printer printer) {
-    printer.append("<built-in function " + getName() + ">");
-  }
-}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Callstack.java b/src/main/java/com/google/devtools/build/lib/syntax/Callstack.java
index a314ab7..3c92f8b 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Callstack.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Callstack.java
@@ -20,7 +20,7 @@
 import java.util.List;
 
 /**
- * Holds the Skylark callstack in thread-local storage. Contains all Expressions and BaseFunctions
+ * Holds the Skylark callstack in thread-local storage. Contains all Expressions and functions
  * currently being evaluated.
  *
  * <p>This is needed for memory tracking, since the evaluator is not available in the context of
@@ -41,8 +41,8 @@
     callstack.get().add(node);
   }
 
-  public static void push(BaseFunction function) {
-    callstack.get().add(function);
+  public static void push(StarlarkCallable fn) {
+    callstack.get().add(fn);
   }
 
   public static void pop() {
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 3c020cb8..e17d302 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
@@ -136,7 +136,7 @@
     if (useStarlarkSemantics) {
       args[nargs - 1] = semantics;
     }
-    return call(obj, args, loc, mu);
+    return call(obj, args, mu);
   }
 
   /**
@@ -146,31 +146,31 @@
    *
    * <p>The Mutability is used if it is necessary to allocate a Starlark copy of a Java result.
    */
-  Object call(Object obj, Object[] args, Location loc, @Nullable Mutability mu)
+  Object call(Object obj, Object[] args, @Nullable Mutability mu)
       throws EvalException, InterruptedException {
     Preconditions.checkNotNull(obj);
     Object result;
     try {
       result = method.invoke(obj, args);
-    } catch (IllegalAccessException e) {
-      // TODO(bazel-team): Print a nice error message. Maybe the method exists
-      // and an argument is missing or has the wrong type.
-      throw new EvalException(loc, "Method invocation failed: " + e);
-    } catch (InvocationTargetException x) {
-      Throwable e = x.getCause();
+    } catch (IllegalAccessException ex) {
+      // The annotated processor ensures that annotated methods are accessible.
+      throw new IllegalStateException(ex);
+
+    } catch (InvocationTargetException ex) {
+      Throwable e = ex.getCause();
       if (e == null) {
-        // This is unlikely to happen.
-        throw new IllegalStateException(
-            String.format(
-                "causeless InvocationTargetException when calling %s with arguments %s at %s",
-                obj, Arrays.toString(args), loc),
-            x);
+        throw new IllegalStateException(e);
       }
-      Throwables.propagateIfPossible(e, InterruptedException.class);
+      // Don't intercept unchecked exceptions.
+      Throwables.throwIfUnchecked(e);
       if (e instanceof EvalException) {
-        throw ((EvalException) e).ensureLocation(loc);
+        throw (EvalException) e;
+      } else if (e instanceof InterruptedException) {
+        throw (InterruptedException) e;
+      } else {
+        // All other checked exceptions (e.g. LabelSyntaxException) are reported to Starlark.
+        throw new EvalException(null, null, e);
       }
-      throw new EvalException(loc, null, e);
     }
     if (method.getReturnType().equals(Void.TYPE)) {
       return Starlark.NONE;
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Starlark.java b/src/main/java/com/google/devtools/build/lib/syntax/Starlark.java
index 2f89636..00638b5 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Starlark.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Starlark.java
@@ -225,6 +225,8 @@
       List<Object> args,
       Map<String, Object> kwargs)
       throws EvalException, InterruptedException {
+    Location loc = call != null ? call.getLocation() : null;
+
     StarlarkCallable callable;
     if (fn instanceof StarlarkCallable) {
       callable = (StarlarkCallable) fn;
@@ -234,17 +236,16 @@
           CallUtils.getSelfCallMethodDescriptor(thread.getSemantics(), fn.getClass());
       if (desc == null) {
         throw new EvalException(
-            call != null ? call.getLocation() : null,
-            "'" + EvalUtils.getDataTypeName(fn) + "' object is not callable");
+            loc, "'" + EvalUtils.getDataTypeName(fn) + "' object is not callable");
       }
       callable = new BuiltinCallable(fn, desc.getName(), desc);
     }
 
-    Location loc = call != null ? call.getLocation() : null;
     thread.push(callable, loc, call);
     try {
-      // TODO(adonovan): unify exception handling here.
       return callable.callImpl(thread, call, args, ImmutableMap.copyOf(kwargs));
+    } catch (EvalException ex) {
+      throw ex.ensureLocation(loc);
     } finally {
       thread.pop();
     }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
index 45a3168..04eefa9 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/StarlarkThread.java
@@ -517,6 +517,12 @@
     CallFrame fr = new CallFrame(fn, loc, call, this.lexicalFrame, this.globalFrame);
     callstack.add(fr);
 
+    // Push the function onto the allocation tracker's stack.
+    // TODO(adonovan): optimize it out of existence.
+    if (Callstack.enabled) {
+      Callstack.push(fn);
+    }
+
     ProfilerTask taskKind;
     if (fn instanceof StarlarkFunction) {
       StarlarkFunction sfn = (StarlarkFunction) fn;
@@ -554,6 +560,10 @@
     if (top.profileSpan != null) {
       top.profileSpan.close();
     }
+
+    if (Callstack.enabled) {
+      Callstack.pop();
+    }
   }
 
   // Builtins cannot create or modify variable bindings,
@@ -1009,7 +1019,4 @@
   public String getTransitiveContentHashCode() {
     return transitiveHashCode;
   }
-
-  // legacy for copybara; to be inlined and deleted in Nov 2019.
-  public static final Module SKYLARK = Module.createForBuiltins(Starlark.UNIVERSE);
 }
diff --git a/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java b/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java
index c03a3a4..b698fba 100644
--- a/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java
+++ b/src/test/java/com/google/devtools/build/lib/profiler/memory/AllocationTrackerTest.java
@@ -62,7 +62,7 @@
     private final Location location;
 
     TestFunction(String file, String name, int line) {
-      super(FunctionSignature.ANY, /*defaultValues=*/ null);
+      super(FunctionSignature.ANY);
       this.name = name;
       this.location = location(file, line);
     }
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 a8cdca1..a258ec3 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
@@ -170,7 +170,7 @@
     }
 
     @SkylarkCallable(name = "interrupted_struct_field", documented = false, structField = true)
-    public BuiltinFunction structFieldInterruptedCallable() throws InterruptedException {
+    public Object structFieldInterruptedCallable() throws InterruptedException {
       throw new InterruptedException();
     }