| // Copyright 2018 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.collect.ImmutableList; |
| import com.google.devtools.build.lib.skylarkinterface.Param; |
| import com.google.devtools.build.lib.syntax.StarlarkSemantics.FlagIdentifier; |
| import java.util.Arrays; |
| import javax.annotation.Nullable; |
| |
| /** A value class for storing {@link Param} metadata to avoid using Java proxies. */ |
| public final class ParamDescriptor { |
| |
| private final String name; |
| private final String defaultValue; |
| private final Class<?> type; |
| private final ImmutableList<ParamTypeDescriptor> allowedTypes; |
| private final Class<?> generic1; |
| private final boolean noneable; |
| private final boolean named; |
| private final boolean positional; |
| // While the type can be inferred completely by the Param annotation, this tuple allows for the |
| // type of a given parameter to be determined only once, as it is an expensive operation. |
| private final SkylarkType skylarkType; |
| |
| // The next two fields relate to toggling this parameter via semantic flag -- they will |
| // be null if and only if this parameter is enabled, and will otherwise contain information |
| // about what to do with the disabled parameter. (If the parameter is 'disabled', it will be |
| // treated as unusable from Starlark.) |
| |
| // The value of this disabled parameter (as interpreted in Starlark) will be passed to the Java |
| // method. |
| @Nullable private final String valueOverride; |
| // The flag responsible for disabling this parameter. If a user attempts to use this disabled |
| // parameter from Starlark, this identifier can be used to create the appropriate error message. |
| @Nullable private final FlagIdentifier flagResponsibleForDisable; |
| |
| private ParamDescriptor( |
| String name, |
| String defaultValue, |
| Class<?> type, |
| ImmutableList<ParamTypeDescriptor> allowedTypes, |
| Class<?> generic1, |
| boolean noneable, |
| boolean named, |
| boolean positional, |
| SkylarkType skylarkType, |
| @Nullable String valueOverride, |
| @Nullable FlagIdentifier flagResponsibleForDisable) { |
| this.name = name; |
| this.defaultValue = defaultValue; |
| this.type = type; |
| this.allowedTypes = allowedTypes; |
| this.generic1 = generic1; |
| this.noneable = noneable; |
| this.named = named; |
| this.positional = positional; |
| this.skylarkType = skylarkType; |
| this.valueOverride = valueOverride; |
| this.flagResponsibleForDisable = flagResponsibleForDisable; |
| } |
| |
| /** |
| * Returns a {@link ParamDescriptor} representing the given raw {@link Param} annotation and the |
| * given semantics. |
| */ |
| public static ParamDescriptor of(Param param, StarlarkSemantics starlarkSemantics) { |
| ImmutableList<ParamTypeDescriptor> allowedTypes = |
| Arrays.stream(param.allowedTypes()) |
| .map(ParamTypeDescriptor::of) |
| .collect(ImmutableList.toImmutableList()); |
| Class<?> type = param.type(); |
| Class<?> generic = param.generic1(); |
| boolean noneable = param.noneable(); |
| |
| boolean isParamEnabledWithCurrentSemantics = |
| starlarkSemantics.isFeatureEnabledBasedOnTogglingFlags( |
| param.enableOnlyWithFlag(), param.disableWithFlag()); |
| |
| String valueOverride = null; |
| FlagIdentifier flagResponsibleForDisable = FlagIdentifier.NONE; |
| if (!isParamEnabledWithCurrentSemantics) { |
| valueOverride = param.valueWhenDisabled(); |
| flagResponsibleForDisable = |
| param.enableOnlyWithFlag() != FlagIdentifier.NONE |
| ? param.enableOnlyWithFlag() |
| : param.disableWithFlag(); |
| } |
| return new ParamDescriptor( |
| param.name(), |
| param.defaultValue(), |
| type, |
| allowedTypes, |
| generic, |
| noneable, |
| isNamed(param, starlarkSemantics), |
| param.positional(), |
| getType(type, generic, allowedTypes, noneable), |
| valueOverride, |
| flagResponsibleForDisable); |
| } |
| |
| private static boolean isNamed(Param param, StarlarkSemantics starlarkSemantics) { |
| if (param.named()) { |
| return true; |
| } |
| return param.legacyNamed() && !starlarkSemantics.incompatibleRestrictNamedParams(); |
| } |
| |
| /** @see Param#name() */ |
| public String getName() { |
| return name; |
| } |
| |
| /** @see Param#allowedTypes() */ |
| public ImmutableList<ParamTypeDescriptor> getAllowedTypes() { |
| return allowedTypes; |
| } |
| |
| /** @see Param#type() */ |
| public Class<?> getType() { |
| return type; |
| } |
| |
| private static SkylarkType getType( |
| Class<?> type, |
| Class<?> generic, |
| ImmutableList<ParamTypeDescriptor> allowedTypes, |
| boolean noneable) { |
| SkylarkType result = SkylarkType.BOTTOM; |
| if (!allowedTypes.isEmpty()) { |
| Preconditions.checkState(Object.class.equals(type)); |
| for (ParamTypeDescriptor paramType : allowedTypes) { |
| SkylarkType t = |
| paramType.getGeneric1() != Object.class |
| ? SkylarkType.of(paramType.getType(), paramType.getGeneric1()) |
| : SkylarkType.of(paramType.getType()); |
| result = SkylarkType.Union.of(result, t); |
| } |
| } else { |
| result = generic != Object.class ? SkylarkType.of(type, generic) : SkylarkType.of(type); |
| } |
| |
| if (noneable) { |
| result = SkylarkType.Union.of(result, SkylarkType.NONE); |
| } |
| return result; |
| } |
| |
| /** @see Param#generic1() */ |
| public Class<?> getGeneric1() { |
| return generic1; |
| } |
| |
| /** @see Param#noneable() */ |
| public boolean isNoneable() { |
| return noneable; |
| } |
| |
| /** @see Param#positional() */ |
| public boolean isPositional() { |
| return positional; |
| } |
| |
| /** @see Param#named() */ |
| public boolean isNamed() { |
| return named; |
| } |
| |
| /** @see Param#defaultValue() */ |
| public String getDefaultValue() { |
| return defaultValue; |
| } |
| |
| SkylarkType getSkylarkType() { |
| return skylarkType; |
| } |
| |
| /** Returns true if this parameter is disabled under the current skylark semantic flags. */ |
| public boolean isDisabledInCurrentSemantics() { |
| return valueOverride != null; |
| } |
| |
| /** |
| * Returns the value the parameter should take, given that the parameter is disabled under the |
| * current skylark semantics. |
| * |
| * @throws IllegalStateException if invoked when {@link #isDisabledInCurrentSemantics()} is false |
| */ |
| public String getValueOverride() { |
| Preconditions.checkState( |
| isDisabledInCurrentSemantics(), |
| "parameter is not disabled under the current semantic flags. getValueOverride should be " |
| + "called only if isParameterDisabled is true"); |
| return valueOverride; |
| } |
| |
| /** |
| * Returns the flag responsible for disabling this parameter, given that the parameter is disabled |
| * under the current skylark semantics. |
| * |
| * @throws IllegalStateException if invoked when {@link #isDisabledInCurrentSemantics()} is false |
| */ |
| public FlagIdentifier getFlagResponsibleForDisable() { |
| Preconditions.checkState( |
| isDisabledInCurrentSemantics(), |
| "parameter is not disabled under the current semantic flags. getFlagResponsibleForDisable " |
| + " should be called only if isParameterDisabled is true"); |
| return flagResponsibleForDisable; |
| } |
| } |