// 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.base.Preconditions;
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.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.syntax.Printer.BasePrinter;
import com.google.devtools.build.lib.util.StringCanonicalizer;
import java.io.Serializable;
import java.util.ArrayList;
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).
*/
@AutoCodec
@AutoValue
public abstract class FunctionSignature implements Serializable {
/** The shape of a FunctionSignature, without names */
@AutoValue
@AutoCodec
public abstract static class Shape implements Serializable {
private static final Interner interner = BlazeInterners.newWeakInterner();
/** Create a function signature */
@AutoCodec.Instantiator
public static Shape create(
int mandatoryPositionals,
int optionalPositionals,
int mandatoryNamedOnly,
int optionalNamedOnly,
boolean hasStarArg,
boolean hasKwArg) {
Preconditions.checkArgument(
0 <= mandatoryPositionals && 0 <= optionalPositionals
&& 0 <= mandatoryNamedOnly && 0 <= optionalNamedOnly);
return interner.intern(
new AutoValue_FunctionSignature_Shape(
mandatoryPositionals,
optionalPositionals,
mandatoryNamedOnly,
optionalNamedOnly,
hasStarArg,
hasKwArg));
}
// 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);
}
}
/** 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()));
}
// 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.
*/
@AutoCodec.Instantiator
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.