New class hierarchy for Skylark functions

* New hierarchy BaseFunction > UserModeFunction, BuiltinFunction.
  The old hierarchy still exists for now, to be deleted after migration:
  Function > AbstractFunction > MixedModeFunction >
    (UserModeFunction, SkylarkFunction > SimpleSkylarkFunction)
  (UserModeFunction is already migrated, and
  BaseFunction implements Function, for now.)

* Function supports *args and **kwargs when calling functions, and
  mandatory named-only parameters in the style of Python 3.
  Notable difference with Python: *args binds the variable to a tuple,
  because a Skylark list would have to be monomorphic.

* A better, simpler, safer FFI using reflection with BuiltinFunction.
  Handles typechecking, passes parameters in a more Java style.
  (Not used for now, will be used later.)

* A new annotation @SkylarkSignature, intended to replace @SkylarkBuiltin,
  supports the full function call protocol, including default arguments.

* Support for annotating function Factory-s rather than functions.

--
MOS_MIGRATED_REVID=88958581
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
new file mode 100644
index 0000000..f1a6bd7
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BaseFunction.java
@@ -0,0 +1,474 @@
+// Copyright 2014 Google Inc. 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.Joiner;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.Type.ConversionException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+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.
+ */
+// TODO(bazel-team):
+// Provide optimized argument frobbing depending of FunctionSignature and CallerSignature
+// (that FuncallExpression must supply), optimizing for the all-positional and all-keyword cases.
+// Also, use better pure maps to minimize map O(n) re-creation events when processing keyword maps.
+public abstract class BaseFunction implements Function {
+
+  // The name of the function
+  private final String name;
+
+  // A function signature, including defaults and types
+  // never null after it is configured
+  @Nullable protected FunctionSignature.WithValues<Object, SkylarkType> signature;
+
+  // Location of the function definition, or null for builtin functions
+  @Nullable protected Location location;
+
+  // Some functions are also Namespaces or other Skylark entities.
+  @Nullable protected Class<?> objectType;
+
+  // Documentation for variables, if any
+  @Nullable protected List<String> paramDoc;
+
+  // True if this function is only allowed during the Loading Phase
+  protected boolean onlyLoadingPhase;
+
+  // The types actually enforced by the Skylark runtime, as opposed to those enforced by the JVM,
+  // or those displayed to the user in the documentation.
+  @Nullable protected List<SkylarkType> enforcedArgumentTypes;
+
+  // Defaults to be used when configure(annotation) is called (after the function is constructed).
+  @Nullable private Iterable<Object> unconfiguredDefaultValues;
+  // The configure(annotation) function will include these defaults in the function signature.
+  // We need to supply these defaultValues to the constructor, that will store them here, because
+  // they can't be supplied via Java annotations, due to the limitations in the annotation facility.
+  // (For extra brownies, we could supply them as Skylark expression strings, to be evaluated by our
+  // evaluator without the help of any unconfigured functions, or to be processed at compile-time;
+  // but we resolve annotations at runtime for now.)
+  // Limitations in Java annotations mean we can't express them in the SkylarkSignature annotation.
+  // (In the future, we could parse and evaluate simple Skylark expression strings, but then
+  // we'd have to be very careful of circularities during initialization).
+  // Note that though we want this list to be immutable, we don't use ImmutableList,
+  // because that can't store nulls and nulls are essential for some BuiltinFunction-s.
+  // We trust the user not to modify the list behind our back.
+
+
+  /** Returns the name of this function. */
+  public String getName() {
+    return name;
+  }
+
+  /** Returns the signature of this function. */
+  @Nullable public FunctionSignature.WithValues<Object, SkylarkType> getSignature() {
+    return signature;
+  }
+
+  /** This function may also be viewed by Skylark as being of a special ObjectType */
+  @Nullable public Class<?> getObjectType() {
+    return objectType;
+  }
+
+  /** Returns true if the BaseFunction is configured. */
+  public boolean isConfigured() {
+    return signature != null;
+  }
+
+  /** Returns true if the function is only available during loading phase */
+  public boolean isOnlyLoadingPhase() {
+    return onlyLoadingPhase;
+  }
+
+  /**
+   * Creates an unconfigured BaseFunction with the given name.
+   *
+   * @param name the function name
+   */
+  public BaseFunction(String name) {
+    this.name = name;
+  }
+
+  /**
+   * Constructs a BaseFunction with a given name, signature and location.
+   *
+   * @param name the function name
+   * @param signature the signature with default values and types
+   * @param location the location of function definition
+   */
+  public BaseFunction(String name,
+      @Nullable FunctionSignature.WithValues<Object, SkylarkType> signature,
+      @Nullable Location location) {
+    this(name);
+    this.signature = signature;
+    this.location = location;
+  }
+
+  /**
+   * Constructs a BaseFunction with a given name, signature.
+   *
+   * @param name the function name
+   * @param signature the signature, with default values and types
+   */
+  public BaseFunction(String name,
+      @Nullable FunctionSignature.WithValues<Object, SkylarkType> signature) {
+    this(name, signature, null);
+  }
+
+  /**
+   * Constructs a BaseFunction with a given name and signature without default values or types.
+   *
+   * @param name the function name
+   * @param signature the signature, without default values or types
+   */
+  public BaseFunction(String name, FunctionSignature signature) {
+    this(name, FunctionSignature.WithValues.<Object, SkylarkType>create(signature), null);
+  }
+
+  /**
+   * Constructs a BaseFunction with a given name and list of unconfigured defaults.
+   *
+   * @param name the function name
+   * @param defaultValues a list of default values for the optional arguments to be configured.
+   */
+  public BaseFunction(String name, @Nullable Iterable<Object> defaultValues) {
+    this(name);
+    this.unconfiguredDefaultValues = defaultValues;
+  }
+
+  /** Get parameter documentation as a list corresponding to each parameter */
+  public List<String> getParamDoc() {
+    return paramDoc;
+  }
+
+  /**
+   * The size of the array required by the callee.
+   */
+  protected int getArgArraySize() {
+    return signature.getSignature().getShape().getArguments();
+  }
+
+  /**
+   * The types that will be actually enforced by Skylark itself, so we may skip those already
+   * enforced by the JVM during calls to BuiltinFunction, but also so we may lie to the user
+   * in the automatically-generated documentation
+   */
+  public List<SkylarkType> getEnforcedArgumentTypes() {
+    return enforcedArgumentTypes;
+  }
+
+  /**
+   * Process the caller-provided arguments into an array suitable for the callee (this function).
+   */
+  public Object[] processArguments(@Nullable List<Object> args,
+      @Nullable Map<String, Object> kwargs,
+      @Nullable Location loc)
+      throws EvalException {
+
+    Object[] arguments = new Object[getArgArraySize()];
+
+    // extract function signature
+    FunctionSignature sig = signature.getSignature();
+    FunctionSignature.Shape shape = sig.getShape();
+    ImmutableList<String> names = sig.getNames();
+    List<Object> defaultValues = signature.getDefaultValues();
+
+    // Note that this variable will be adjusted down if there are extra positionals,
+    // after these extra positionals are dumped into starParam.
+    int numPositionalArgs = args.size();
+
+    int numMandatoryPositionalParams = shape.getMandatoryPositionals();
+    int numOptionalPositionalParams = shape.getOptionalPositionals();
+    int numMandatoryNamedOnlyParams = shape.getMandatoryNamedOnly();
+    int numOptionalNamedOnlyParams = shape.getOptionalNamedOnly();
+    boolean hasStarParam = shape.hasStarArg();
+    boolean hasKwParam = shape.hasKwArg();
+    int numPositionalParams = numMandatoryPositionalParams + numOptionalPositionalParams;
+    int numNamedOnlyParams = numMandatoryNamedOnlyParams + numOptionalNamedOnlyParams;
+    int numNamedParams = numPositionalParams + numNamedOnlyParams;
+    int numOptionalParams = numOptionalPositionalParams + numOptionalNamedOnlyParams;
+    int endOptionalParams = numMandatoryPositionalParams + numOptionalParams;
+    int kwParamIndex = names.size() - 1; // only valid if hasKwParam
+
+    // (1) handle positional arguments
+    if (hasStarParam) {
+      // Nota Bene: we collect extra positional arguments in a (tuple,) rather than a [list],
+      // so the collection can be heterogeneous; that's a notable difference with Python.
+      int starParamIndex = numNamedParams;
+      if (numPositionalArgs > numPositionalParams) {
+        arguments[starParamIndex] = SkylarkList.tuple(
+            args.subList(numPositionalParams, numPositionalArgs));
+        numPositionalArgs = numPositionalParams; // clip numPositionalArgs
+      } else {
+        arguments[starParamIndex] = SkylarkList.EMPTY_TUPLE;
+      }
+    } else if (numPositionalArgs > numPositionalParams) {
+      throw new EvalException(loc,
+          numPositionalParams > 0
+          ? "too many (" + numPositionalArgs + ") positional arguments in call to " + this
+          : this + " does not accept positional arguments, but got " + numPositionalArgs);
+    }
+
+    for (int i = 0; i < numPositionalArgs; i++) {
+      arguments[i] = args.get(i);
+    }
+
+    // (2) handle keyword arguments
+    if (kwargs == null || kwargs.isEmpty()) {
+      // Easy case (2a): there are no keyword arguments.
+      // All arguments were positional, so check we had enough to fill all mandatory positionals.
+      if (numPositionalArgs < numMandatoryPositionalParams) {
+        throw new EvalException(loc, String.format(
+            "insufficient arguments received by %s (got %s, expected at least %s)",
+            this, numPositionalArgs, numMandatoryPositionalParams));
+      }
+      // We had no named argument, so fail if there were mandatory named-only parameters
+      if (numMandatoryNamedOnlyParams > 0) {
+        throw new EvalException(loc, String.format(
+            "missing mandatory keyword arguments in call to %s", this));
+      }
+      // Fill in defaults for missing optional parameters, that were conveniently grouped together.
+      if (defaultValues != null) {
+        int j = numPositionalArgs - numMandatoryPositionalParams;
+        for (int i = numPositionalArgs; i < endOptionalParams; i++) {
+          arguments[i] = defaultValues.get(j++);
+        }
+      }
+      // If there's a kwParam, it's empty.
+      if (hasKwParam) {
+        arguments[kwParamIndex] = ImmutableMap.<String, Object>of();
+      }
+    } else if (hasKwParam && numNamedParams == 0) {
+      // Easy case (2b): there are no named parameters, but there is a **kwParam.
+      // Therefore all keyword arguments go directly to the kwParam.
+      // Note that *starParam and **kwParam themselves don't count as named.
+      // Also note that no named parameters means no mandatory parameters that weren't passed,
+      // and no missing optional parameters for which to use a default. Thus, no loops.
+      arguments[kwParamIndex] = kwargs; // NB: not 2a means kwarg isn't null
+    } else {
+      // Hard general case (2c): some keyword arguments may correspond to named parameters
+      HashMap<String, Object> kwArg = hasKwParam ? new HashMap<String, Object>() : null;
+
+      // For nicer stabler error messages, start by checking against
+      // an argument being provided both as positional argument and as keyword argument.
+      ArrayList<String> bothPosKey = new ArrayList<>();
+      for (int i = 0; i < numPositionalArgs; i++) {
+        String name = names.get(i);
+        if (kwargs.containsKey(name)) { bothPosKey.add(name); }
+      }
+      if (!bothPosKey.isEmpty()) {
+        throw new EvalException(loc,
+            String.format("argument%s '%s' passed both by position and by name in call to %s",
+                (bothPosKey.size() > 1 ? "s" : ""), Joiner.on("', '").join(bothPosKey), this));
+      }
+
+      // Accept the arguments that were passed.
+      for (Map.Entry<String, Object> entry : kwargs.entrySet()) {
+        String keyword = entry.getKey();
+        Object value = entry.getValue();
+        int pos = names.indexOf(keyword); // the list should be short, so linear scan is OK.
+        if (0 <= pos && pos < numNamedParams) {
+          arguments[pos] = value;
+        } else {
+          if (!hasKwParam) {
+            List<String> unexpected = Ordering.natural().sortedCopy(Sets.difference(
+                kwargs.keySet(), ImmutableSet.copyOf(names.subList(0, numNamedParams))));
+            throw new EvalException(loc, String.format("unexpected keyword%s '%s' in call to %s",
+                    unexpected.size() > 1 ? "s" : "", Joiner.on("', '").join(unexpected), this));
+          }
+          if (kwArg.containsKey(keyword)) {
+            throw new EvalException(loc, String.format(
+                "%s got multiple values for keyword argument '%s'", this, keyword));
+          }
+          kwArg.put(keyword, value);
+        }
+      }
+      if (hasKwParam) {
+        arguments[kwParamIndex] = ImmutableMap.copyOf(kwArg);
+      }
+
+      // Check that all mandatory parameters were filled in general case 2c.
+      // Note: it's possible that numPositionalArgs > numMandatoryPositionalParams but that's OK.
+      for (int i = numPositionalArgs; i < numMandatoryPositionalParams; i++) {
+        if (arguments[i] == null) {
+          throw new EvalException(loc, String.format(
+              "missing mandatory positional argument '%s' while calling %s",
+              names.get(i), toString()));
+        }
+      }
+
+      for (int i = numNamedParams - numMandatoryNamedOnlyParams; i < numNamedParams; i++) {
+        if (arguments[i] == null) {
+          throw new EvalException(loc, String.format(
+              "missing mandatory named-only argument '%s' while calling %s",
+              names.get(i), toString()));
+        }
+      }
+
+      // Get defaults for those parameters that weren't passed.
+      if (defaultValues != null) {
+        for (int j = numPositionalArgs > numMandatoryPositionalParams
+                 ? numPositionalArgs - numMandatoryPositionalParams : 0;
+             j < numOptionalParams; j++) {
+          int i = j + numMandatoryPositionalParams;
+          if (arguments[i] == null) {
+            arguments[i] = defaultValues.get(j);
+          }
+        }
+      }
+    } // End of general case 2c for argument passing.
+
+    return arguments;
+  }
+
+  /** check types and convert as required */
+  protected void canonicalizeArguments(Object[] arguments, Location loc) throws EvalException {
+    // TODO(bazel-team): maybe link syntax.SkylarkType and package.Type,
+    // so we can simultaneously typecheck and convert?
+    // Note that a BuiltinFunction already does typechecking of simple types.
+
+    List<SkylarkType> types = getEnforcedArgumentTypes();
+
+    // Check types, if supplied
+    if (types == null) {
+      return;
+    }
+    List<String> names = signature.getSignature().getNames();
+    int length = types.size();
+    for (int i = 0; i < length; i++) {
+      Object value = arguments[i];
+      SkylarkType type = types.get(i);
+      if (value != null && type != null && !type.contains(value)) {
+        throw new EvalException(loc,
+            String.format("expected %s for '%s' while calling %s but got %s instead: %s",
+                type, names.get(i), getName(), EvalUtils.getDataTypeName(value, true), value));
+      }
+    }
+  }
+
+  /**
+   * The outer calling convention to a BaseFunction.
+   *
+   * @param args a list of all positional arguments (as in *starArg)
+   * @param kwargs a map for key arguments (as in **kwArgs)
+   * @param ast the expression for this function's definition
+   * @param env the lexical Environment for the function call
+   * @return the value resulting from evaluating the function with the given arguments
+   * @throws construction of EvalException-s containing source information.
+   */
+  @Override
+  public Object call(@Nullable List<Object> args,
+      @Nullable Map<String, Object> kwargs,
+      @Nullable FuncallExpression ast,
+      @Nullable Environment env)
+      throws EvalException, InterruptedException {
+
+    Preconditions.checkState(isConfigured(), "Function %s was not configured", getName());
+
+    // ast is null when called from Java (as there's no Skylark call site).
+    Location loc = ast == null ? location : ast.getLocation();
+
+    Object[] arguments = processArguments(args, kwargs, loc);
+    canonicalizeArguments(arguments, loc);
+
+    try {
+      return call(arguments, ast, env);
+    } catch (ConversionException e) {
+      throw new EvalException(loc, e.getMessage());
+    }
+  }
+
+  /**
+   * Inner call to a BaseFunction
+   * subclasses need to @Override this method.
+   *
+   * @param args an array of argument values sorted as per the signature.
+   * @param ast the source code for the function if user-defined
+   * @param ast the lexical environment of the function call
+   */
+  // Don't make it abstract, so that subclasses may be defined that @Override the outer call() only.
+  public Object call(Object[] args,
+      @Nullable FuncallExpression ast, @Nullable Environment env)
+      throws EvalException, ConversionException, InterruptedException {
+    throw new EvalException(ast.getLocation(),
+        String.format("function %s not implemented", getName()));
+  }
+
+  /**
+   * Render this object in the form of an equivalent Python function signature.
+   */
+  public String toString() {
+    StringBuffer sb = new StringBuffer();
+    sb.append(getName());
+    if (signature != null) {
+      sb.append('(');
+      signature.toStringBuffer(sb);
+      sb.append(')');
+    } // if unconfigured, don't even output parentheses
+    return sb.toString();
+  }
+
+  /** Configure a BaseFunction from a @SkylarkSignature annotation */
+  public void configure(SkylarkSignature annotation) {
+    Preconditions.checkState(!isConfigured()); // must not be configured yet
+
+    this.paramDoc = new ArrayList<>();
+    this.signature = SkylarkSignatureProcessor.getSignature(
+        getName(), annotation, unconfiguredDefaultValues, paramDoc, getEnforcedArgumentTypes());
+    this.objectType = annotation.objectType().equals(Object.class)
+        ? null : annotation.objectType();
+    this.onlyLoadingPhase = annotation.onlyLoadingPhase();
+    configure();
+  }
+
+  /** Configure a function based on its signature */
+  protected void configure() {
+    // this function is called after the signature was initialized
+    Preconditions.checkState(signature != null);
+    enforcedArgumentTypes = signature.getTypes();
+  }
+}
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
new file mode 100644
index 0000000..23b5c7d
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BuiltinFunction.java
@@ -0,0 +1,330 @@
+// Copyright 2014 Google Inc. 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.devtools.build.lib.events.Location;
+import com.google.devtools.build.lib.packages.Type.ConversionException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.NoSuchElementException;
+import java.util.concurrent.ExecutionException;
+
+import javax.annotation.Nullable;
+
+/**
+ * A class for Skylark functions provided as builtins by the Skylark implementation
+ */
+public class BuiltinFunction extends BaseFunction {
+
+  /** ExtraArgKind so you can tweek your function's own calling convention */
+  public static enum ExtraArgKind {
+    LOCATION,
+    SYNTAX_TREE,
+    ENVIRONMENT;
+  }
+  // Predefined system add-ons to function signatures
+  public static final ExtraArgKind[] USE_LOC =
+      new ExtraArgKind[] {ExtraArgKind.LOCATION};
+  public static final ExtraArgKind[] USE_LOC_ENV =
+      new ExtraArgKind[] {ExtraArgKind.LOCATION, ExtraArgKind.ENVIRONMENT};
+  public static final ExtraArgKind[] USE_AST =
+      new ExtraArgKind[] {ExtraArgKind.SYNTAX_TREE};
+  public static final ExtraArgKind[] USE_AST_ENV =
+      new ExtraArgKind[] {ExtraArgKind.SYNTAX_TREE, ExtraArgKind.ENVIRONMENT};
+
+
+  // The underlying invoke() method.
+  @Nullable private Method invokeMethod;
+
+  // extra arguments required beside signature.
+  @Nullable private ExtraArgKind[] extraArgs;
+
+  // The count of arguments in the inner invoke method,
+  // to be used as size of argument array by the outer call method.
+  private int innerArgumentCount;
+
+
+  /** Create unconfigured function from its name */
+  public BuiltinFunction(String name) {
+    super(name);
+  }
+
+  /** Creates an unconfigured BuiltinFunction with the given name and defaultValues */
+  public BuiltinFunction(String name, Iterable<Object> defaultValues) {
+    super(name, defaultValues);
+  }
+
+  /** Creates a BuiltinFunction with the given name and signature */
+  public BuiltinFunction(String name, FunctionSignature signature) {
+    super(name, signature);
+    configure();
+  }
+
+  /** Creates a BuiltinFunction with the given name and signature with values */
+  public BuiltinFunction(String name,
+      FunctionSignature.WithValues<Object, SkylarkType> signature) {
+    super(name, signature);
+    configure();
+  }
+
+  /** Creates a BuiltinFunction with the given name and signature and extra arguments */
+  public BuiltinFunction(String name, FunctionSignature signature, ExtraArgKind[] extraArgs) {
+    super(name, signature);
+    this.extraArgs = extraArgs;
+    configure();
+  }
+
+  /** Creates a BuiltinFunction with the given name, signature with values, and extra arguments */
+  public BuiltinFunction(String name,
+      FunctionSignature.WithValues<Object, SkylarkType> signature, ExtraArgKind[] extraArgs) {
+    super(name, signature);
+    this.extraArgs = extraArgs;
+    configure();
+  }
+
+
+  /** Creates a BuiltinFunction from the given name and a Factory */
+  public BuiltinFunction(String name, Factory factory) {
+    super(name);
+    configure(factory);
+  }
+
+  @Override
+  protected int getArgArraySize () {
+    return innerArgumentCount;
+  }
+
+  protected ExtraArgKind[] getExtraArgs () {
+    return extraArgs;
+  }
+
+  @Override
+  @Nullable
+  public Object call(Object[] args,
+      @Nullable FuncallExpression ast, @Nullable Environment env)
+      throws EvalException, InterruptedException {
+    final Location loc = (ast == null) ? location : ast.getLocation();
+
+    // Add extra arguments, if needed
+    if (extraArgs != null) {
+      int i = args.length - extraArgs.length;
+      for (BuiltinFunction.ExtraArgKind extraArg : extraArgs) {
+        switch(extraArg) {
+          case LOCATION:
+            args[i] = loc;
+            break;
+
+          case SYNTAX_TREE:
+            args[i] = ast;
+            break;
+
+          case ENVIRONMENT:
+            args[i] = env;
+            break;
+        }
+        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;
+      } else if (e instanceof InterruptedException) {
+        throw (InterruptedException) e;
+      } else if (e instanceof ConversionException
+          || e instanceof ClassCastException
+          || e instanceof ExecutionException
+          || e instanceof IllegalStateException) {
+        throw new EvalException(loc, e.getMessage(), e);
+      } else if (e instanceof IllegalArgumentException) {
+        // Assume it was thrown by SkylarkType.cast and has a good message.
+        throw new EvalException(loc, String.format("%s\nin call to %s", e.getMessage(), this), e);
+      } else {
+        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 - ((extraArgs == null) ? 0 : extraArgs.length);
+        final Class<?>[] types = invokeMethod.getParameterTypes();
+        for (int i = 0; i < args.length; i++) {
+          if (args[i] != null && !types[i].isAssignableFrom(args[i].getClass())) {
+            final String paramName = i < len
+                ? signature.getSignature().getNames().get(i) : extraArgs[i - len].name();
+            throw new EvalException(loc, String.format(
+                "expected %s for '%s' while calling %s but got %s instead",
+                EvalUtils.getDataTypeNameFromClass(types[i]), paramName, getName(),
+                EvalUtils.getDataTypeName(args[i])), e);
+          }
+        }
+        throw badCallException(loc, e, args);
+    } catch (IllegalAccessException e) {
+      throw badCallException(loc, e, args);
+    }
+  }
+
+  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\nJava parameter types: %s\nSkylark type checks: %s",
+            (loc == null) ? "" : loc + ": ",
+            e.getClass().getName(), e.getMessage(), this,
+            Arrays.asList(args),
+            Arrays.asList(invokeMethod.getParameterTypes()),
+            signature.getTypes()), e);
+  }
+
+
+  /** Configure the reflection mechanism */
+  @Override
+  public void configure(SkylarkSignature annotation) {
+    Preconditions.checkState(!isConfigured()); // must not be configured yet
+    enforcedArgumentTypes = new ArrayList<>();
+    this.extraArgs = SkylarkSignatureProcessor.getExtraArgs(annotation);
+    super.configure(annotation);
+  }
+
+  // finds the method and makes it accessible (which is needed to find it, and later to use it)
+  protected Method findMethod(final String name) {
+    Method found = null;
+    for (Method method : this.getClass().getDeclaredMethods()) {
+      method.setAccessible(true);
+      if (name.equals(method.getName())) {
+        if (method != null) {
+          throw new IllegalArgumentException(String.format(
+              "function %s has more than one method named %s", getName(), name));
+        }
+        found = method;
+      }
+    }
+    if (found == null) {
+      throw new NoSuchElementException(String.format(
+          "function %s doesn't have a method named %s", getName(), name));
+    }
+    return found;
+  }
+
+  /** Configure the reflection mechanism */
+  @Override
+  protected void configure() {
+    invokeMethod = findMethod("invoke");
+
+    int arguments = signature.getSignature().getShape().getArguments();
+    innerArgumentCount = arguments + (extraArgs == null ? 0 : extraArgs.length);
+    Class<?>[] parameterTypes = invokeMethod.getParameterTypes();
+    Preconditions.checkArgument(innerArgumentCount == parameterTypes.length, getName());
+
+    // TODO(bazel-team): also grab the returnType from the annotations,
+    // and check it against method return type
+    if (enforcedArgumentTypes != null) {
+      for (int i = 0; i < arguments; i++) {
+        SkylarkType enforcedType = enforcedArgumentTypes.get(i);
+        if (enforcedType != null) {
+          Class<?> parameterType = parameterTypes[i];
+          String msg = String.format("fun %s, param %s, enforcedType: %s (%s); parameterType: %s",
+              getName(), signature.getSignature().getNames().get(i),
+              enforcedType, enforcedType.getClass(), parameterType);
+          if (enforcedType instanceof SkylarkType.Simple) {
+            Preconditions.checkArgument(
+                enforcedType == SkylarkType.of(parameterType), msg);
+            // No need to enforce Simple types on the Skylark side, the JVM will do it for us.
+            enforcedArgumentTypes.set(i, null);
+          } else if (enforcedType instanceof SkylarkType.Combination) {
+            Preconditions.checkArgument(
+                enforcedType.getType() == parameterType, msg);
+          } else {
+            Preconditions.checkArgument(
+                parameterType == Object.class || parameterType == null, msg);
+          }
+        }
+      }
+    }
+    // No need for the enforcedArgumentTypes List if all the types were Simple
+    enforcedArgumentTypes = FunctionSignature.<SkylarkType>valueListOrNull(enforcedArgumentTypes);
+  }
+
+  /** Configure by copying another function's configuration */
+  // Alternatively, we could have an extension BuiltinFunctionSignature of FunctionSignature,
+  // and use *that* instead of a Factory.
+  public void configure(BuiltinFunction.Factory factory) {
+    // this function must not be configured yet, but the factory must be
+    Preconditions.checkState(!isConfigured());
+    Preconditions.checkState(factory.isConfigured(),
+        "function factory is not configured for %s", getName());
+
+    this.paramDoc = factory.getParamDoc();
+    this.signature = factory.getSignature();
+    this.extraArgs = factory.getExtraArgs();
+    this.objectType = factory.getObjectType();
+    this.onlyLoadingPhase = factory.isOnlyLoadingPhase();
+    configure();
+  }
+
+  /**
+   * A Factory allows for a @SkylarkSignature annotation to be provided and processed in advance
+   * for a function that will be defined later as a closure (see e.g. in PackageFactory).
+   *
+   * <p>Each instance of this class must define a method create that closes over some (final)
+   * variables and returns a BuiltinFunction.
+   */
+  public abstract static class Factory extends BuiltinFunction {
+    @Nullable private Method createMethod;
+
+    /** Create unconfigured function Factory from its name */
+    public Factory(String name) {
+      super(name);
+    }
+
+    /** Creates an unconfigured function Factory with the given name and defaultValues */
+    public Factory(String name, Iterable<Object> defaultValues) {
+      super(name, defaultValues);
+    }
+
+    @Override
+    public void configure() {
+      if (createMethod != null) {
+        return;
+      }
+      createMethod = findMethod("create");
+    }
+
+    @Override
+    public Object call(Object[] args, @Nullable FuncallExpression ast, @Nullable Environment env)
+      throws EvalException {
+      throw new EvalException(null, "Tried to invoke a Factory for function " + this);
+    }
+
+    /** Instantiate the Factory
+     * @param args arguments to pass to the create method
+     * @return a new BuiltinFunction that closes over the arguments
+     */
+    public BuiltinFunction apply(Object... args) {
+      try {
+        return (BuiltinFunction) createMethod.invoke(this, args);
+      } catch (InvocationTargetException | IllegalArgumentException | IllegalAccessException e) {
+        throw new RuntimeException(String.format(
+            "Exception while applying BuiltinFunction.Factory %s: %s",
+            this, e.getMessage()), e);
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java b/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java
index 28639bc..5b0119e 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/FunctionDefStatement.java
@@ -13,7 +13,6 @@
 // limitations under the License.
 package com.google.devtools.build.lib.syntax;
 
-import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableList;
 import com.google.devtools.build.lib.syntax.SkylarkType.SkylarkFunctionType;
 
@@ -34,12 +33,6 @@
   public FunctionDefStatement(Ident ident,
       FunctionSignature.WithValues<Expression, Expression> args,
       Collection<Statement> statements) {
-
-    // TODO(bazel-team): lift the following limitation from {@link MixedModeFunction}
-    FunctionSignature.Shape shape = args.getSignature().getShape();
-    Preconditions.checkArgument(!shape.hasKwArg() && !shape.hasStarArg()
-        && shape.getNamedOnly() == 0, "no star, star-star or named-only parameters (for now)");
-
     this.ident = ident;
     this.args = args;
     this.statements = ImmutableList.copyOf(statements);
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
index 9805e45..5816007 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Parser.java
@@ -1151,13 +1151,6 @@
     expect(TokenKind.RPAREN);
     expect(TokenKind.COLON);
     List<Statement> block = parseSuite();
-    // TODO(bazel-team): lift this limitation
-    FunctionSignature.Shape shape = args.getSignature().getShape();
-    if (shape.hasKwArg() || shape.hasStarArg() || shape.getNamedOnly() > 0) {
-      reportError(lexer.createLocation(start, token.right),
-          "no star, star-star or named-only parameters (for now)");
-      return;
-    }
     FunctionDefStatement stmt = new FunctionDefStatement(ident, args, block);
     list.add(setLocation(stmt, start, token.right));
   }
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignature.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignature.java
new file mode 100644
index 0000000..1bd34a9
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignature.java
@@ -0,0 +1,90 @@
+// Copyright 2014 Google Inc. 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * An annotation to mark built-in keyword argument methods accessible from Skylark.
+ */
+@Target({ElementType.FIELD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface SkylarkSignature {
+
+  // TODO(bazel-team): parse most everything from single string specifying the signature
+  // in Skylark syntax, e.g.: signature = "foo(a: string, b: ListOf(int)) -> NoneType"
+  // String signature() default "";
+
+  String name();
+
+  String doc();
+
+  Param[] mandatoryPositionals() default {};
+
+  Param[] optionalPositionals() default {};
+
+  Param[] optionalNamedOnly() default {};
+
+  Param[] mandatoryNamedOnly() default {};
+
+  Param[] extraPositionals() default {};
+
+  Param[] extraKeywords() default {};
+
+  boolean undocumented() default false;
+
+  Class<?> objectType() default Object.class;
+
+  Class<?> returnType() default Object.class;
+
+  boolean onlyLoadingPhase() default false;
+
+  // TODO(bazel-team): determine this way whether to accept mutable Lists
+  // boolean mutableLists() default false;
+
+  boolean useLocation() default false;
+
+  boolean useAst() default false;
+
+  boolean useEnvironment() default false;
+
+  /**
+   * An annotation for parameters of Skylark built-in functions.
+   */
+  @Retention(RetentionPolicy.RUNTIME)
+  public @interface Param {
+
+    String name();
+
+    String doc();
+
+    String defaultValue() default "";
+
+    Class<?> type() default Object.class;
+
+    Class<?> generic1() default Object.class;
+
+    boolean callbackEnabled() default false;
+
+    boolean noneable() default false;
+
+    // TODO(bazel-team): parse the type from a single field in Skylark syntax,
+    // and allow a Union as "ThisType or ThatType or NoneType":
+    // String type() default "Object";
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
new file mode 100644
index 0000000..6140188
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/syntax/SkylarkSignatureProcessor.java
@@ -0,0 +1,247 @@
+// Copyright 2014 Google Inc. 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.devtools.build.lib.syntax.BuiltinFunction.ExtraArgKind;
+import com.google.devtools.build.lib.syntax.SkylarkSignature.Param;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nullable;
+
+/**
+ * This class defines utilities to process @SkylarkSignature annotations
+ * to configure a given field.
+ */
+public class SkylarkSignatureProcessor {
+  /**
+   * Extracts a {@code FunctionSignature.WithValues<Object, SkylarkType>} from an annotation
+   * @param name the name of the function
+   * @param annotation the annotation
+   * @param defaultValues an optional list of default values
+   * @param paramDoc an optional list into which to store documentation strings
+   * @param enforcedTypesList an optional list into which to store effective types to enforce
+   */
+  // NB: the two arguments paramDoc and enforcedTypesList are used to "return" extra values via
+  // side-effects, and that's ugly
+  // TODO(bazel-team): use AutoValue to declare a value type to use as return value?
+  public static FunctionSignature.WithValues<Object, SkylarkType> getSignature(
+      String name, SkylarkSignature annotation,
+      @Nullable Iterable<Object> defaultValues,
+      @Nullable List<String> paramDoc, @Nullable List<SkylarkType> enforcedTypesList) {
+
+    Preconditions.checkArgument(name.equals(annotation.name()),
+        "%s != %s", name, annotation.name());
+    ArrayList<Parameter<Object, SkylarkType>> paramList = new ArrayList<>();
+    HashMap<String, SkylarkType> enforcedTypes = enforcedTypesList == null
+        ? null : new HashMap<String, SkylarkType>();
+    HashMap<String, String> doc = new HashMap<>();
+
+    Iterator<Object> defaultValuesIterator = defaultValues == null
+        ? null : defaultValues.iterator();
+    try {
+      for (Param param : annotation.mandatoryPositionals()) {
+        paramList.add(getParameter(name, param, doc, enforcedTypes,
+                /*mandatory=*/true, /*star=*/false, /*starStar=*/false, /*defaultValue=*/null));
+      }
+      for (Param param : annotation.optionalPositionals()) {
+        paramList.add(getParameter(name, param, doc, enforcedTypes,
+                /*mandatory=*/false, /*star=*/false, /*starStar=*/false,
+                /*defaultValue=*/getDefaultValue(param, defaultValuesIterator)));
+      }
+      if (annotation.extraPositionals().length > 0
+          || annotation.optionalNamedOnly().length > 0
+          || annotation.mandatoryNamedOnly().length > 0) {
+        @Nullable Param starParam = null;
+        if (annotation.extraPositionals().length > 0) {
+          Preconditions.checkArgument(annotation.extraPositionals().length == 1);
+          starParam = annotation.extraPositionals()[0];
+        }
+        paramList.add(getParameter(name, starParam, doc, enforcedTypes,
+                /*mandatory=*/false, /*star=*/true, /*starStar=*/false, /*defaultValue=*/null));
+      }
+      for (Param param : annotation.optionalNamedOnly()) {
+        paramList.add(getParameter(name, param, doc, enforcedTypes,
+                /*mandatory=*/false, /*star=*/false, /*starStar=*/false,
+                /*defaultValue=*/getDefaultValue(param, defaultValuesIterator)));
+      }
+      for (Param param : annotation.mandatoryNamedOnly()) {
+        paramList.add(getParameter(name, param, doc, enforcedTypes,
+                /*mandatory=*/true, /*star=*/false, /*starStar=*/false, /*defaultValue=*/null));
+      }
+      if (annotation.extraKeywords().length > 0) {
+        Preconditions.checkArgument(annotation.extraKeywords().length == 1);
+        paramList.add(
+            getParameter(name, annotation.extraKeywords()[0], doc, enforcedTypes,
+                /*mandatory=*/false, /*star=*/false, /*starStar=*/true, /*defaultValue=*/null));
+      }
+      FunctionSignature.WithValues<Object, SkylarkType> signature =
+          FunctionSignature.WithValues.<Object, SkylarkType>of(paramList);
+      for (String paramName : signature.getSignature().getNames()) {
+        if (enforcedTypesList != null) {
+          enforcedTypesList.add(enforcedTypes.get(paramName));
+        }
+        if (paramDoc != null) {
+          paramDoc.add(doc.get(paramName));
+        }
+      }
+      return signature;
+    } catch (FunctionSignature.SignatureException e) {
+      throw new RuntimeException(String.format(
+          "Invalid signature while configuring BuiltinFunction %s", name), e);
+    }
+  }
+
+  /**
+   * Configures the parameter of this Skylark function using the annotation.
+   */
+  // TODO(bazel-team): Maybe have the annotation be a string representing the
+  // python-style calling convention including default values, and have the regular Parser
+  // process it? (builtin function call not allowed when evaluating values, but more complex
+  // values are possible by referencing variables in some definition environment).
+  // Then the only per-parameter information needed is a documentation string.
+  private static Parameter<Object, SkylarkType> getParameter(String name,
+      Param param, Map<String, String> paramDoc, Map<String, SkylarkType> enforcedTypes,
+      boolean mandatory, boolean star, boolean starStar, @Nullable Object defaultValue)
+      throws FunctionSignature.SignatureException {
+
+    @Nullable SkylarkType officialType = null;
+    @Nullable SkylarkType enforcedType = null;
+    if (star && param == null) { // pseudo-parameter to separate positional from named-only
+      return new Parameter.Star<>(null);
+    }
+    if (param.type() != Object.class) {
+      if (param.type() == List.class) {
+        // NB: a List in the annotation actually indicates either a List or a SkylarkList
+        // and we trust the BuiltinFunction to do the enforcement.
+        // For automatic document generation purpose, we lie and just say it's a list;
+        // hopefully user code should never be exposed to the java List case
+        officialType = SkylarkType.of(SkylarkList.class, param.generic1());
+        enforcedType = SkylarkType.Union.of(
+            SkylarkType.of(List.class),
+            SkylarkType.of(SkylarkList.class, param.generic1()));
+        Preconditions.checkArgument(enforcedType instanceof SkylarkType.Union);
+      } else if (param.generic1() != Object.class) {
+        // Otherwise, we will enforce the proper parametric type for Skylark list and set objects
+        officialType = SkylarkType.of(param.type(), param.generic1());
+        enforcedType = officialType;
+      } else {
+        officialType = SkylarkType.of(param.type());
+        enforcedType = officialType;
+      }
+      if (param.callbackEnabled()) {
+        officialType = SkylarkType.Union.of(
+            officialType, SkylarkType.SkylarkFunctionType.of(name, officialType));
+        enforcedType = SkylarkType.Union.of(
+            enforcedType, SkylarkType.SkylarkFunctionType.of(name, enforcedType));
+      }
+      if (param.noneable()) {
+        officialType = SkylarkType.Union.of(officialType, SkylarkType.NONE);
+        enforcedType = SkylarkType.Union.of(enforcedType, SkylarkType.NONE);
+      }
+    }
+    if (enforcedTypes != null) {
+      enforcedTypes.put(param.name(), enforcedType);
+    }
+    if (paramDoc != null) {
+      paramDoc.put(param.name(), param.doc());
+    }
+    if (starStar) {
+      return new Parameter.StarStar<>(param.name(), officialType);
+    } else if (star) {
+      return new Parameter.Star<>(param.name(), officialType);
+    } else if (mandatory) {
+      return new Parameter.Mandatory<>(param.name(), officialType);
+    } else {
+      return new Parameter.Optional<>(param.name(), officialType, defaultValue);
+    }
+  }
+
+  private static Object getDefaultValue(Param param, Iterator<Object> iterator) {
+    if (iterator != null) {
+      return iterator.next();
+    } else if (param.defaultValue().isEmpty()) {
+      return Environment.NONE;
+    } else {
+      try {
+        // TODO(bazel-team): when we have Evaluation, remove the throw and uncomment the return.
+        throw new RuntimeException("Not Implemented Yet!");
+        // return new Evaluation ().eval(param.defaultValue());
+      } catch (Exception e) {
+        throw new RuntimeException(String.format(
+            "Exception while processing @SkylarkSignature.Param %s, default value %s: %s",
+            param.name(), param.defaultValue(), e.getMessage()), e);
+      }
+    }
+  }
+
+  /** Extract additional signature information for BuiltinFunction-s */
+  public static ExtraArgKind[] getExtraArgs(SkylarkSignature annotation) {
+    final int numExtraArgs = (annotation.useLocation() ? 1 : 0)
+        + (annotation.useAst() ? 1 : 0) + (annotation.useEnvironment() ? 1 : 0);
+    if (numExtraArgs == 0) {
+      return null;
+    }
+    final ExtraArgKind[] extraArgs = new ExtraArgKind[numExtraArgs];
+    int i = 0;
+    if (annotation.useLocation()) {
+      extraArgs[i++] = ExtraArgKind.LOCATION;
+    }
+    if (annotation.useAst()) {
+      extraArgs[i++] = ExtraArgKind.SYNTAX_TREE;
+    }
+    if (annotation.useEnvironment()) {
+      extraArgs[i++] = ExtraArgKind.ENVIRONMENT;
+    }
+    return extraArgs;
+  }
+
+  /**
+   * Configure all BaseFunction-s in a class from their @SkylarkSignature annotations
+   * @param type a class containing BuiltinFunction fields that need be configured.
+   * This function is typically called in a static block to initialize a class,
+   * i.e. a class {@code Foo} containing @SkylarkSignature annotations would end with
+   * {@code static { SkylarkSignatureProcessor.configureSkylarkFunctions(Foo.class); }}
+   */
+  public static void configureSkylarkFunctions(Class<?> type) {
+    for (Field field : type.getDeclaredFields()) {
+      if (field.isAnnotationPresent(SkylarkSignature.class)) {
+        // The annotated fields are often private, but we need access them.
+        field.setAccessible(true);
+        SkylarkSignature annotation = field.getAnnotation(SkylarkSignature.class);
+        Object value = null;
+        try {
+          value = Preconditions.checkNotNull(field.get(null),
+              String.format(
+                  "Error while trying to configure %s.%s: its value is null", type, field));
+          if (BaseFunction.class.isAssignableFrom(field.getType())) {
+            BaseFunction function = (BaseFunction) value;
+            if (!function.isConfigured()) {
+              function.configure(annotation);
+            }
+          }
+        } catch (IllegalAccessException e) {
+          throw new RuntimeException(String.format(
+              "Error while trying to configure %s.%s (value %s)", type, field, value), e);
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java b/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
index 58a5b4c..5bb9019 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/UserDefinedFunction.java
@@ -20,7 +20,7 @@
  * The actual function registered in the environment. This function is defined in the
  * parsed code using {@link FunctionDefStatement}.
  */
-public class UserDefinedFunction extends MixedModeFunction {
+public class UserDefinedFunction extends BaseFunction {
 
   private final ImmutableList<Statement> statements;
   private final SkylarkEnvironment definitionEnv;