// 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 static com.google.common.collect.ImmutableList.toImmutableList;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Interner;
import com.google.devtools.build.lib.concurrent.BlazeInterners;
import com.google.devtools.build.lib.syntax.Printer.BasePrinter;
import com.google.devtools.build.lib.syntax.SkylarkList.Tuple;
import com.google.devtools.build.lib.util.Preconditions;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
/**
* Function Signatures for BUILD language (same as Python)
*
*
Skylark's function signatures are just like Python3's.
* A function may have 6 kinds of arguments:
* positional mandatory, positional optional, positional rest (aka *star argument),
* key-only mandatory, key-only optional, key rest (aka **star_star argument).
* A caller may specify all arguments but the *star and **star_star arguments by name,
* and thus all mandatory and optional arguments are named arguments.
*
*
To enable various optimizations in the argument processing routine,
* we sort arguments according the following constraints, enabling corresponding optimizations:
*
*
The positional mandatories come just before the positional optionals,
* so they can be filled in one go.
*
Positionals come first, so it's easy to prepend extra positional arguments such as "self"
* to an argument list, and we optimize for the common case of no key-only mandatory parameters.
* key-only parameters are thus grouped together.
* positional mandatory and key-only mandatory parameters are separate,
* but there is no loop over a contiguous chunk of them, anyway.
*
The named are all grouped together, with star and star_star rest arguments coming last.
*
Mandatory arguments in each category (positional and named-only) come before the optional
* arguments, for the sake of slightly better clarity to human implementers. This eschews an
* optimization whereby grouping optionals together allows to iterate over them in one go instead
* of two; however, this relatively minor optimization only matters when keyword arguments are
* passed, at which point it is dwarfed by the slowness of keyword processing.
*
*
*
Parameters are thus sorted in the following order:
* positional mandatory arguments (if any), positional optional arguments (if any),
* key-only mandatory arguments (if any), key-only optional arguments (if any),
* then star argument (if any), then star_star argument (if any).
*/
@AutoValue
public abstract class FunctionSignature implements Serializable {
/**
* The shape of a FunctionSignature, without names
*/
@AutoValue
public abstract static class Shape implements Serializable {
private static final Interner interner = BlazeInterners.newWeakInterner();
/** Create a function signature */
public static Shape create(
int mandatoryPositionals,
int optionalPositionals,
int mandatoryNamedOnly,
int optionalNamedOnly,
boolean starArg,
boolean kwArg) {
Preconditions.checkArgument(
0 <= mandatoryPositionals && 0 <= optionalPositionals
&& 0 <= mandatoryNamedOnly && 0 <= optionalNamedOnly);
return interner.intern(new AutoValue_FunctionSignature_Shape(
mandatoryPositionals, optionalPositionals,
mandatoryNamedOnly, optionalNamedOnly, starArg, kwArg));
}
// These abstract getters specify the actual argument count fields to be defined by AutoValue.
/** number of mandatory positional arguments */
public abstract int getMandatoryPositionals();
/** number of optional positional arguments */
public abstract int getOptionalPositionals();
/** number of mandatory named-only arguments. */
public abstract int getMandatoryNamedOnly();
/** number of optional named-only arguments */
public abstract int getOptionalNamedOnly();
/** indicator for presence of a star argument for extra positional arguments */
public abstract boolean hasStarArg();
/** indicator for presence of a star-star argument for extra keyword arguments */
public abstract boolean hasKwArg();
// These are computed argument counts
/** number of optional and mandatory positional arguments. */
public int getPositionals() {
return getMandatoryPositionals() + getOptionalPositionals();
}
/** number of optional and mandatory named-only arguments. */
public int getNamedOnly() {
return getMandatoryNamedOnly() + getOptionalNamedOnly();
}
/** number of optional arguments. */
public int getOptionals() {
return getOptionalPositionals() + getOptionalNamedOnly();
}
/** number of all named parameters: mandatory and optional of positionals and named-only */
public int getAllNamed() {
return getPositionals() + getNamedOnly();
}
/** total number of arguments */
public int getArguments() {
return getAllNamed() + (hasStarArg() ? 1 : 0) + (hasKwArg() ? 1 : 0);
}
/**
* @return this signature shape converted to a list of classes
*/
public List> toClasses() {
List> parameters = new ArrayList<>();
parameters.addAll(Collections.nCopies(getAllNamed(), Object.class));
if (hasStarArg()) {
parameters.add(Tuple.class);
}
if (hasKwArg()) {
parameters.add(SkylarkDict.class);
}
return parameters;
}
}
/** Names of a FunctionSignature */
private static final Interner> namesInterner =
BlazeInterners.newWeakInterner();
/** Intern a list of names */
public static ImmutableList names(List names) {
return namesInterner.intern(
names.stream().map(StringCanonicalizer::intern).collect(toImmutableList()));
}
/** Intern a list of names */
public static ImmutableList names(String... names) {
return names(ImmutableList.copyOf(names));
}
// Interner
private static final Interner signatureInterner =
BlazeInterners.newWeakInterner();
/**
* Signatures proper.
*
*
A signature is a Shape and an ImmutableList of argument variable names
* NB: we assume these lists are short, so we may do linear scans.
*/
public static FunctionSignature create(Shape shape, ImmutableList names) {
Preconditions.checkArgument(names.size() == shape.getArguments());
return signatureInterner.intern(new AutoValue_FunctionSignature(shape, names(names)));
}
// Field definition (details filled in by AutoValue)
/** The shape */
public abstract Shape getShape();
/** The names */
public abstract ImmutableList getNames();
/** append a representation of this signature to a string buffer. */
public StringBuilder toStringBuilder(StringBuilder sb) {
return WithValues.