Ejemplo n.º 1
0
  @Override
  void validate(final ValidationEnvironment env) throws EvalException {
    ValidationEnvironment localEnv = new ValidationEnvironment(env);
    FunctionSignature sig = signature.getSignature();
    FunctionSignature.Shape shape = sig.getShape();
    ImmutableList<String> names = sig.getNames();
    List<Expression> defaultExpressions = signature.getDefaultValues();

    int positionals = shape.getPositionals();
    int mandatoryPositionals = shape.getMandatoryPositionals();
    int namedOnly = shape.getNamedOnly();
    int mandatoryNamedOnly = shape.getMandatoryNamedOnly();
    boolean starArg = shape.hasStarArg();
    boolean kwArg = shape.hasKwArg();
    int named = positionals + namedOnly;
    int args = named + (starArg ? 1 : 0) + (kwArg ? 1 : 0);
    int startOptionals = mandatoryPositionals;
    int endOptionals = named - mandatoryNamedOnly;

    int j = 0; // index for the defaultExpressions
    for (int i = 0; i < args; i++) {
      String name = names.get(i);
      if (startOptionals <= i && i < endOptionals) {
        defaultExpressions.get(j++).validate(env);
      }
      localEnv.declare(name, getLocation());
    }
    for (Statement stmts : statements) {
      stmts.validate(localEnv);
    }
  }
Ejemplo n.º 2
0
  protected boolean hasSelfArgument() {
    Class<?> clazz = getObjectType();
    if (clazz == null) {
      return false;
    }
    List<SkylarkType> types = signature.getTypes();
    ImmutableList<String> names = signature.getSignature().getNames();

    return (!types.isEmpty() && types.get(0).canBeCastTo(clazz))
        || (!names.isEmpty() && names.get(0).equals("self"));
  }
Ejemplo n.º 3
0
  /** 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));
      }
    }
  }
Ejemplo n.º 4
0
  /**
   * Returns the signature as "[className.]methodName(name1: paramType1, name2: paramType2, ...)" or
   * "[className.]methodName(paramType1, paramType2, ...)", depending on the value of showNames.
   */
  public String getShortSignature(boolean showNames) {
    StringBuilder builder = new StringBuilder();
    boolean hasSelf = hasSelfArgument();

    builder.append(getFullName()).append("(");
    signature.toStringBuilder(builder, showNames, false, false, hasSelf);
    builder.append(")");

    return builder.toString();
  }
Ejemplo n.º 5
0
 /** Render this object in the form of an equivalent Python function signature. */
 @Override
 public String toString() {
   StringBuilder sb = new StringBuilder();
   sb.append(getName());
   if (signature != null) {
     sb.append('(');
     signature.toStringBuilder(sb);
     sb.append(')');
   } // if unconfigured, don't even output parentheses
   return sb.toString();
 }
Ejemplo n.º 6
0
  @Override
  void exec(Environment env) throws EvalException, InterruptedException {
    List<Expression> defaultExpressions = signature.getDefaultValues();
    ArrayList<Object> defaultValues = null;
    ArrayList<SkylarkType> types = null;

    if (defaultExpressions != null) {
      defaultValues = new ArrayList<>(defaultExpressions.size());
      for (Expression expr : defaultExpressions) {
        defaultValues.add(expr.eval(env));
      }
    }
    env.update(
        ident.getName(),
        new UserDefinedFunction(
            ident,
            FunctionSignature.WithValues.<Object, SkylarkType>create(
                signature.getSignature(), defaultValues, types),
            statements,
            (SkylarkEnvironment) env));
  }
Ejemplo n.º 7
0
  protected MixedModeFunction(
      String name,
      Iterable<String> parameters,
      int numMandatoryParameters,
      boolean onlyNamedArguments,
      Location location) {
    super(name);
    this.parameters = ImmutableList.copyOf(parameters);
    this.numMandatoryParameters = numMandatoryParameters;
    this.onlyNamedArguments = onlyNamedArguments;
    this.location = location;

    // Fake a signature from the above
    this.signature =
        FunctionSignature.WithValues.<Object, SkylarkType>create(
            FunctionSignature.of(numMandatoryParameters, this.parameters.toArray(new String[0])));
  }
Ejemplo n.º 8
0
  /** Create a function using a signature with defaults */
  public MixedModeFunction(
      String name, FunctionSignature.WithValues<Object, SkylarkType> signature, Location location) {
    super(name);

    // TODO(bazel-team): lift the following limitations, by actually implementing
    // the full function call protocol.
    FunctionSignature sig = signature.getSignature();
    FunctionSignature.Shape shape = sig.getShape();
    Preconditions.checkArgument(
        !shape.hasKwArg() && !shape.hasStarArg() && shape.getNamedOnly() == 0,
        "no star, star-star or named-only parameters (for now)");

    this.signature = signature;
    this.parameters = ImmutableList.copyOf(sig.getNames());
    this.numMandatoryParameters = shape.getMandatoryPositionals();
    this.onlyNamedArguments = false;
    this.location = location;
  }
Ejemplo n.º 9
0
  @Override
  public Object call(
      List<Object> args, Map<String, Object> kwargs, FuncallExpression ast, Environment env)
      throws EvalException, InterruptedException {

    // ast is null when called from Java (as there's no Skylark call site).
    Location loc = ast == null ? location : ast.getLocation();
    if (onlyNamedArguments && !args.isEmpty()) {
      throw new EvalException(loc, getSignature() + " does not accept positional arguments");
    }

    if (kwargs == null) {
      kwargs = ImmutableMap.<String, Object>of();
    }

    int numParams = parameters.size();
    int numArgs = args.size();
    Object[] namedArguments = new Object[numParams];

    // first, positional arguments:
    if (numArgs > numParams) {
      throw new EvalException(loc, "too many positional arguments in call to " + getSignature());
    }
    for (int ii = 0; ii < numArgs; ++ii) {
      namedArguments[ii] = args.get(ii);
    }

    // TODO(bazel-team): here, support *varargs splicing

    // second, keyword arguments:
    for (Map.Entry<String, Object> entry : kwargs.entrySet()) {
      String keyword = entry.getKey();
      int pos = parameters.indexOf(keyword);
      if (pos == -1) {
        List<String> unexpected = listDifference(new ArrayList<>(kwargs.keySet()), parameters);
        Collections.sort(unexpected); // issue stable error messages.
        throw new EvalException(
            loc,
            "unexpected keyword"
                + (unexpected.size() > 1 ? "s" : "")
                + " '"
                + Joiner.on("', '").join(unexpected)
                + "' in call to "
                + getSignature());
      } else {
        if (namedArguments[pos] != null) {
          throw new EvalException(
              loc, getSignature() + " got multiple values for keyword argument '" + keyword + "'");
        }
        namedArguments[pos] = kwargs.get(keyword);
      }
    }

    // third, check mandatory parameters:
    for (int ii = 0; ii < numMandatoryParameters; ++ii) {
      if (namedArguments[ii] == null) {
        throw new EvalException(loc, getSignature() + " received insufficient arguments");
      }
    }

    // fourth, fill in defaults from the signature, if any
    List<Object> defaults = signature.getDefaultValues();
    if (defaults != null) {
      int jj = 0;
      for (int ii = numMandatoryParameters; ii < numParams; ++ii) {
        if (namedArguments[ii] == null) {
          namedArguments[ii] = defaults.get(jj);
        }
        jj++;
      }
    }

    try {
      return call(namedArguments, ast, env);
    } catch (ConversionException
        | IllegalArgumentException
        | IllegalStateException
        | ClassCastException e) {
      throw new EvalException(loc, e.getMessage());
    }
  }
Ejemplo n.º 10
0
 /** Create a function using a signature without defaults */
 public MixedModeFunction(String name, FunctionSignature signature) {
   this(name, FunctionSignature.WithValues.<Object, SkylarkType>create(signature), null);
 }
Ejemplo n.º 11
0
 /** 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();
 }
Ejemplo n.º 12
0
  /**
   * Process the caller-provided arguments into an array suitable for the callee (this function).
   */
  public Object[] processArguments(
      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 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],
      // and this is actually the same as in Python.
      int starParamIndex = numNamedParams;
      if (numPositionalArgs > numPositionalParams) {
        arguments[starParamIndex] =
            Tuple.copyOf(args.subList(numPositionalParams, numPositionalArgs));
        numPositionalArgs = numPositionalParams; // clip numPositionalArgs
      } else {
        arguments[starParamIndex] = Tuple.EMPTY;
      }
    } 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,
      // thanks to the absence of mandatory named-only parameters as checked above.
      if (defaultValues != null) {
        int j = numPositionalArgs - numMandatoryPositionalParams;
        int endOptionalParams = numPositionalParams + numOptionalNamedOnlyParams;
        for (int i = numPositionalArgs; i < endOptionalParams; i++) {
          arguments[i] = defaultValues.get(j++);
        }
      }
      // If there's a kwParam, it's empty.
      if (hasKwParam) {
        // TODO(bazel-team): create a fresh mutable dict, like Python does
        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.
      // TODO(bazel-team): create a fresh mutable dict, like Python does
      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) {
        // TODO(bazel-team): create a fresh mutable dict, like Python does
        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), this));
        }
      }

      int endMandatoryNamedOnlyParams = numPositionalParams + numMandatoryNamedOnlyParams;
      for (int i = numPositionalParams; i < endMandatoryNamedOnlyParams; i++) {
        if (arguments[i] == null) {
          throw new EvalException(
              loc,
              String.format(
                  "missing mandatory named-only argument '%s' while calling %s",
                  names.get(i), this));
        }
      }

      // Get defaults for those parameters that weren't passed.
      if (defaultValues != null) {
        for (int i = Math.max(numPositionalArgs, numMandatoryPositionalParams);
            i < numPositionalParams;
            i++) {
          if (arguments[i] == null) {
            arguments[i] = defaultValues.get(i - numMandatoryPositionalParams);
          }
        }
        int numMandatoryParams = numMandatoryPositionalParams + numMandatoryNamedOnlyParams;
        for (int i = numMandatoryParams + numOptionalPositionalParams; i < numNamedParams; i++) {
          if (arguments[i] == null) {
            arguments[i] = defaultValues.get(i - numMandatoryParams);
          }
        }
      }
    } // End of general case 2c for argument passing.

    return arguments;
  }
Ejemplo n.º 13
0
 /** The size of the array required by the callee. */
 protected int getArgArraySize() {
   return signature.getSignature().getShape().getArguments();
 }