diff options
author | 2016-09-02 13:56:25 +0000 | |
---|---|---|
committer | 2016-09-06 15:37:42 +0000 | |
commit | 0cfa5d6d89f66cc6be9668dedb61042acd841268 (patch) | |
tree | 5e7354f49df2cfb36cd88a3d6bfcd82bc36db596 /src/main/java | |
parent | d9d524ab77ebff673009e7405678c237bdb510fc (diff) |
Rollback of commit 19db71413329da3f5d22b5fc7681471f3d971d88.
--
MOS_MIGRATED_REVID=132058819
Diffstat (limited to 'src/main/java')
7 files changed, 147 insertions, 526 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java b/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java index abaccccdff..4fab639d96 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java +++ b/src/main/java/com/google/devtools/build/lib/packages/AggregatingAttributeMapper.java @@ -42,7 +42,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.CollectionUtils; -import com.google.devtools.build.lib.packages.Attribute.ComputationLimiter; import com.google.devtools.build.lib.packages.BuildType.Selector; import com.google.devtools.build.lib.packages.BuildType.SelectorList; import com.google.devtools.build.lib.syntax.Type; @@ -56,7 +55,6 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; import javax.annotation.Nullable; /** @@ -69,12 +67,18 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { private static final ImmutableSet<Type<?>> scalarTypes = ImmutableSet.of(INTEGER, STRING, LABEL, NODEP_LABEL, OUTPUT, BOOLEAN, TRISTATE, LICENSE); - private final Rule rule; + /** + * Store for all of this rule's attributes that are non-configurable. These are + * unconditionally available to computed defaults no matter what dependencies + * they've declared. + */ + private final List<String> nonConfigurableAttributes; private AggregatingAttributeMapper(Rule rule) { super(rule.getPackage(), rule.getRuleClassObject(), rule.getLabel(), rule.getAttributeContainer()); - this.rule = rule; + + nonConfigurableAttributes = rule.getRuleClassObject().getNonConfigurableAttributes(); } public static AggregatingAttributeMapper of(Rule rule) { @@ -82,14 +86,6 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { } /** - * Returns all of this rule's attributes that are non-configurable. These are unconditionally - * available to computed defaults no matter what dependencies they've declared. - */ - private List<String> getNonConfigurableAttributes() { - return rule.getRuleClassObject().getNonConfigurableAttributes(); - } - - /** * Override that also visits the rule's configurable attribute keys (which are themselves labels). * * <p>Note that we directly parse the selectors rather than just calling {@link #visitAttribute} @@ -329,7 +325,16 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { // or y1, then compute default values for the (x1,y1), (x1,y2), (x2,y1), and (x2,y2) cases. Attribute.ComputedDefault computedDefault = getComputedDefault(attributeName, type); if (computedDefault != null) { - return computedDefault.getPossibleValues(type, rule); + // The depMaps list holds every assignment of possible values to the computed default's + // declared possibly-configurable dependencies. + List<Map<String, Object>> depMaps = visitAttributes(computedDefault.dependencies()); + + List<T> possibleValues = new ArrayList<>(); // Not ImmutableList.Builder: values may be null. + // For each combination, call getDefault on a specialized AttributeMap providing those values. + for (Map<String, Object> depMap : depMaps) { + possibleValues.add(type.cast(computedDefault.getDefault(mapBackedAttributeMap(depMap)))); + } + return possibleValues; } // For any other attribute, just return its direct value. @@ -456,20 +461,18 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { * ] * </pre> * - * <p>The work done by this method may be limited by providing a {@link ComputationLimiter} that - * throws if too much work is attempted. + * <p>This uses time and space exponential on the number of inputs. To guard against misuse, + * {@code attributes.size()} must be two or less. */ - <TException extends Exception> List<Map<String, Object>> visitAttributes( - List<String> attributes, ComputationLimiter<TException> limiter) throws TException { + private List<Map<String, Object>> visitAttributes(List<String> attributes) { + Preconditions.checkState(attributes.size() <= 2); List<Map<String, Object>> depMaps = new LinkedList<>(); - AtomicInteger combinationsSoFar = new AtomicInteger(0); - visitAttributesInner( - attributes, depMaps, ImmutableMap.<String, Object>of(), combinationsSoFar, limiter); + visitAttributesInner(attributes, depMaps, ImmutableMap.<String, Object>of()); return depMaps; } /** - * A recursive function used in the implementation of {@link #visitAttributes}. + * A recursive function used in the implementation of {@link #visitAttributes(List)}. * * @param attributes a list of attributes that are not yet assigned values in the {@code * currentMap} parameter. @@ -477,21 +480,10 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { * will add newly discovered maps to the list. * @param currentMap a (possibly non-empty) map holding {attrName --> attrValue} assignments for * attributes not in the {@code attributes} list. - * @param combinationsSoFar a counter for all previously processed combinations of possible - * values. - * @param limiter a strategy to limit the work done by invocations of this method. */ - private <TException extends Exception> void visitAttributesInner( - List<String> attributes, - List<Map<String, Object>> mappings, - Map<String, Object> currentMap, - AtomicInteger combinationsSoFar, - ComputationLimiter<TException> limiter) - throws TException { + private void visitAttributesInner( + List<String> attributes, List<Map<String, Object>> mappings, Map<String, Object> currentMap) { if (attributes.isEmpty()) { - // Because this method uses exponential time/space on the number of inputs, we may limit - // the total number of method calls. - limiter.onComputationCount(combinationsSoFar.incrementAndGet()); // Recursive base case: store whatever's already been populated in currentMap. mappings.add(currentMap); return; @@ -508,8 +500,7 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { Map<String, Object> newMap = new HashMap<>(); newMap.putAll(currentMap); newMap.put(firstAttribute, value); - visitAttributesInner( - attributes.subList(1, attributes.size()), mappings, newMap, combinationsSoFar, limiter); + visitAttributesInner(attributes.subList(1, attributes.size()), mappings, newMap); } } @@ -520,14 +511,14 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { * configurable attribute that's not in {@code directMap} causes an {@link * IllegalArgumentException} to be thrown. */ - AttributeMap createMapBackedAttributeMap(final Map<String, Object> directMap) { + private AttributeMap mapBackedAttributeMap(final Map<String, Object> directMap) { final AggregatingAttributeMapper owner = AggregatingAttributeMapper.this; return new AttributeMap() { @Override public <T> T get(String attributeName, Type<T> type) { owner.checkType(attributeName, type); - if (getNonConfigurableAttributes().contains(attributeName)) { + if (nonConfigurableAttributes.contains(attributeName)) { return owner.get(attributeName, type); } if (!directMap.containsKey(attributeName)) { @@ -558,7 +549,7 @@ public class AggregatingAttributeMapper extends AbstractAttributeMapper { public Iterable<String> getAttributeNames() { return ImmutableList.<String>builder() .addAll(directMap.keySet()) - .addAll(getNonConfigurableAttributes()) + .addAll(nonConfigurableAttributes) .build(); } diff --git a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java index 68540c8b87..996371494d 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/Attribute.java +++ b/src/main/java/com/google/devtools/build/lib/packages/Attribute.java @@ -22,18 +22,14 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; -import com.google.common.collect.Ordering; import com.google.common.collect.Sets; import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.cmdline.Label; -import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; import com.google.devtools.build.lib.syntax.ClassObject; import com.google.devtools.build.lib.syntax.EvalException; -import com.google.devtools.build.lib.syntax.EvalUtils; -import com.google.devtools.build.lib.syntax.Runtime; import com.google.devtools.build.lib.syntax.SkylarkCallbackFunction; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.syntax.Type.ConversionException; @@ -41,7 +37,6 @@ import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.FileTypeSet; import com.google.devtools.build.lib.util.Preconditions; import com.google.devtools.build.lib.util.StringUtil; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; @@ -50,7 +45,6 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.atomic.AtomicReference; import javax.annotation.concurrent.Immutable; /** @@ -633,14 +627,16 @@ public final class Attribute implements Comparable<Attribute> { } /** - * Sets the attribute default value to a computed default value - use this when the default - * value is a function of other attributes of the Rule. The type of the computed default value - * for a mandatory attribute must match the type parameter: (e.g. list=[], integer=0, string="", + * Sets the attribute default value to a computed default value - use + * this when the default value is a function of other attributes of the + * Rule. The type of the computed default value for a mandatory attribute + * must match the type parameter: (e.g. list=[], integer=0, string="", * label=null). The {@code defaultValue} implementation must be immutable. * - * <p>If the computed default returns a Label that is a target, that target will become an - * implicit dependency of this Rule; we will load the target (and its dependencies) if it - * encounters the Rule and build the target if needs to apply the Rule. + * <p>If computedDefault returns a Label that is a target, that target will + * become an implicit dependency of this Rule; we will load the target + * (and its dependencies) if it encounters the Rule and build the target if + * needs to apply the Rule. */ public Builder<TYPE> value(ComputedDefault defaultValue) { Preconditions.checkState(!valueSet, "the default value is already set"); @@ -651,28 +647,6 @@ public final class Attribute implements Comparable<Attribute> { } /** - * Sets the attribute default value to a Skylark computed default template. Like a native - * Computed Default, this allows a Skylark-defined Rule Class to specify that the default value - * of an attribute is a function of other attributes of the Rule. - * - * <p>During the loading phase, the computed default template will be specialized for each rule - * it applies to. Those rules' attribute values will not be references to {@link - * SkylarkComputedDefaultTemplate}s, but instead will be references to {@link - * SkylarkComputedDefault}s. - * - * <p>If the computed default returns a Label that is a target, that target will become an - * implicit dependency of this Rule; we will load the target (and its dependencies) if it - * encounters the Rule and build the target if needs to apply the Rule. - */ - public Builder<TYPE> value(SkylarkComputedDefaultTemplate skylarkComputedDefaultTemplate) { - Preconditions.checkState(!valueSet, "the default value is already set"); - value = skylarkComputedDefaultTemplate; - valueSource = AttributeValueSource.COMPUTED_DEFAULT; - valueSet = true; - return this; - } - - /** * Sets the attribute default value to be late-bound, i.e., it is derived from the build * configuration. */ @@ -1047,154 +1021,28 @@ public final class Attribute implements Comparable<Attribute> { } /** - * A strategy for dealing with too many computations, used when creating lookup tables for {@link - * ComputedDefault}s. + * A computed default is a default value for a Rule attribute that is a + * function of other attributes of the rule. * - * @param <TException> The type of exception this strategy throws if too many computations are - * attempted. - */ - interface ComputationLimiter<TException extends Exception> { - void onComputationCount(int count) throws TException; - } - - /** - * An implementation of {@link ComputationLimiter} that never throws. For use with - * natively-defined {@link ComputedDefault}s, which are limited in the number of configurable - * attributes they depend on, not on the number of different combinations of possible inputs. - */ - private static final ComputationLimiter<RuntimeException> NULL_COMPUTATION_LIMITER = - new ComputationLimiter<RuntimeException>() { - @Override - public void onComputationCount(int count) throws RuntimeException {} - }; - - /** Exception for computed default attributes that depend on too many configurable attributes. */ - private static class TooManyConfigurableAttributesException extends Exception { - TooManyConfigurableAttributesException(int max) { - super( - String.format( - "Too many configurable attributes to compute all possible values: " - + "Found more than %d possible values.", - max)); - } - } - - private static class FixedComputationLimiter - implements ComputationLimiter<TooManyConfigurableAttributesException> { - - /** Upper bound of the number of combinations of values for a computed default attribute. */ - private static final int COMPUTED_DEFAULT_MAX_COMBINATIONS = 64; - - private static final FixedComputationLimiter INSTANCE = new FixedComputationLimiter(); - - @Override - public void onComputationCount(int count) throws TooManyConfigurableAttributesException { - if (count > COMPUTED_DEFAULT_MAX_COMBINATIONS) { - throw new TooManyConfigurableAttributesException(COMPUTED_DEFAULT_MAX_COMBINATIONS); - } - } - } - - /** - * Specifies how values of {@link ComputedDefault} attributes are computed based on the values of - * other attributes. + * <p>Attributes whose defaults are computed are first initialized to the default + * for their type, and then the computed defaults are evaluated after all + * non-computed defaults have been initialized. There is no defined order + * among computed defaults, so they must not depend on each other. * - * <p>The {@code TComputeException} type parameter allows the two specializations of this class to - * describe whether and how their computations throw. For natively defined computed defaults, - * computation does not throw, but for Skylark-defined computed defaults, computation may throw - * {@link InterruptedException}. - */ - private abstract static class ComputationStrategy<TComputeException extends Exception> { - abstract Object compute(AttributeMap map) throws TComputeException; - - /** - * Returns a lookup table mapping from: - * - * <ul> - * <li>tuples of values that may be assigned by {@code rule} to attributes with names in {@code - * dependencies} (note that there may be more than one such tuple for any given rule, if any - * of the dependencies are configurable) - * </ul> - * - * <p>to: - * - * <ul> - * <li>the value {@link #compute(AttributeMap)} evaluates to when the provided {@link - * AttributeMap} contains the values specified by that assignment, or {@code null} if the - * {@link ComputationStrategy} failed to evaluate. - * </ul> - * - * <p>The lookup table contains a tuple for each possible assignment to the {@code dependencies} - * attributes. The meaning of each tuple is well-defined because {@code dependencies} is - * ordered. - * - * <p>This is useful because configurable attributes may have many possible values. During the - * loading phase a configurable attribute can't be resolved to a single value. Configuration - * information, needed to resolve such an attribute, is only available during analysis. However, - * any labels that a ComputedDefault attribute may evaluate to must be loaded during the loading - * phase. - */ - <T, TLimitException extends Exception> Map<List<Object>, T> computeValuesForAllCombinations( - List<String> dependencies, - Type<T> type, - Rule rule, - ComputationLimiter<TLimitException> limiter) - throws TComputeException, TLimitException { - // This will hold every (value1, value2, ..) combination of the declared dependencies. - // Collect those combinations. - AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule); - List<Map<String, Object>> depMaps = mapper.visitAttributes(dependencies, limiter); - // For each combination, call compute() on a specialized AttributeMap providing those - // values. - Map<List<Object>, T> valueMap = new HashMap<>(); - for (Map<String, Object> depMap : depMaps) { - AttributeMap attrMap = mapper.createMapBackedAttributeMap(depMap); - Object value = compute(attrMap); - List<Object> key = createDependencyAssignmentTuple(dependencies, attrMap); - valueMap.put(key, type.cast(value)); - } - return valueMap; - } - - /** - * Given an {@link AttributeMap}, containing an assignment to each attribute in {@code - * dependencies}, this returns a list of the assigned values, ordered as {@code dependencies} is - * ordered. - */ - static List<Object> createDependencyAssignmentTuple( - List<String> dependencies, AttributeMap attrMap) { - ArrayList<Object> tuple = new ArrayList<>(dependencies.size()); - for (String attrName : dependencies) { - Type<?> attrType = attrMap.getAttributeType(attrName); - tuple.add(attrMap.get(attrName, attrType)); - } - return tuple; - } - } - - /** - * A computed default is a default value for a Rule attribute that is a function of other - * attributes of the rule. - * - * <p>Attributes whose defaults are computed are first initialized to the default for their type, - * and then the computed defaults are evaluated after all non-computed defaults have been - * initialized. There is no defined order among computed defaults, so they must not depend on each - * other. - * - * <p>If a computed default reads the value of another attribute, at least one of the following - * must be true: + * <p>If a computed default reads the value of another attribute, at least one of + * the following must be true: * * <ol> - * <li>The other attribute must be declared in the computed default's constructor - * <li>The other attribute must be non-configurable ({@link Builder#nonconfigurable} + * <li>The other attribute must be declared in the computed default's constructor</li> + * <li>The other attribute must be non-configurable ({@link Builder#nonconfigurable}</li> * </ol> * - * <p>The reason for enforced declarations is that, since attribute values might be configurable, - * a computed default that depends on them may itself take multiple values. Since we have no - * access to a target's configuration at the time these values are computed, we need the ability - * to probe the default's *complete* dependency space. Declared dependencies allow us to do so - * sanely. Non-configurable attributes don't have this problem because their value is fixed and - * known even without configuration information. + * <p>The reason for enforced declarations is that, since attribute values might be + * configurable, a computed default that depends on them may itself take multiple + * values. Since we have no access to a target's configuration at the time these values + * are computed, we need the ability to probe the default's *complete* dependency space. + * Declared dependencies allow us to do so sanely. Non-configurable attributes don't have + * this problem because their value is fixed and known even without configuration information. * * <p>Implementations of this interface must be immutable. */ @@ -1206,7 +1054,7 @@ public final class Attribute implements Comparable<Attribute> { * configurable attribute values. */ public ComputedDefault() { - this(ImmutableList.<String>of()); + dependencies = ImmutableList.of(); } /** @@ -1214,7 +1062,7 @@ public final class Attribute implements Comparable<Attribute> { * explicitly specified configurable attribute value */ public ComputedDefault(String depAttribute) { - this(ImmutableList.of(depAttribute)); + dependencies = ImmutableList.of(depAttribute); } /** @@ -1222,38 +1070,7 @@ public final class Attribute implements Comparable<Attribute> { * explicitly specified configurable attribute values. */ public ComputedDefault(String depAttribute1, String depAttribute2) { - this(ImmutableList.of(depAttribute1, depAttribute2)); - } - - /** - * Creates a computed default that can read all non-configurable attributes and some explicitly - * specified configurable attribute values. - * - * <p>This constructor should not be used by native {@link ComputedDefault} functions. The limit - * of at-most-two depended-on configurable attributes is intended, to limit the exponential - * growth of possible values. {@link SkylarkComputedDefault} uses this, but is limited by {@link - * FixedComputationLimiter#COMPUTED_DEFAULT_MAX_COMBINATIONS}. - */ - protected ComputedDefault(ImmutableList<String> dependencies) { - // Order is important for #createDependencyAssignmentTuple. - this.dependencies = Ordering.natural().immutableSortedCopy(dependencies); - } - - <T> Iterable<T> getPossibleValues(Type<T> type, Rule rule) { - final ComputedDefault owner = ComputedDefault.this; - ComputationStrategy<RuntimeException> strategy = - new ComputationStrategy<RuntimeException>() { - @Override - public Object compute(AttributeMap map) { - return owner.getDefault(map); - } - }; - // Note that this uses ArrayList instead of something like ImmutableList because some - // values may be null. - return new ArrayList<>( - strategy - .computeValuesForAllCombinations(dependencies, type, rule, NULL_COMPUTATION_LIMITER) - .values()); + dependencies = ImmutableList.of(depAttribute1, depAttribute2); } /** The list of configurable attributes this ComputedDefault declares it may read. */ @@ -1261,211 +1078,10 @@ public final class Attribute implements Comparable<Attribute> { return dependencies; } - /** - * Returns the value this {@link ComputedDefault} evaluates to, given the inputs contained in - * {@code rule}. - */ public abstract Object getDefault(AttributeMap rule); } /** - * A Skylark-defined computed default, which can be precomputed for a specific {@link Rule} by - * calling {@link #computePossibleValues}, which returns a {@link SkylarkComputedDefault} that - * contains a lookup table. - */ - public static final class SkylarkComputedDefaultTemplate { - private final Type<?> type; - private final SkylarkCallbackFunction callback; - private final Location location; - private final ImmutableList<String> dependencies; - - /** - * Creates a new SkylarkComputedDefaultTemplate that allows the computation of attribute values - * via a callback function during loading phase. - * - * @param type The type of the value of this attribute. - * @param dependencies A list of all names of other attributes that are accessed by this - * attribute. - * @param callback A function to compute the actual attribute value. - * @param location The location of the Skylark function. - */ - public SkylarkComputedDefaultTemplate( - Type<?> type, - ImmutableList<String> dependencies, - SkylarkCallbackFunction callback, - Location location) { - this.type = Preconditions.checkNotNull(type); - this.dependencies = Preconditions.checkNotNull(dependencies); - this.callback = Preconditions.checkNotNull(callback); - this.location = Preconditions.checkNotNull(location); - } - - /** - * Returns a {@link SkylarkComputedDefault} containing a lookup table specifying the output of - * this {@link SkylarkComputedDefaultTemplate}'s callback given each possible assignment {@code - * rule} might make to the attributes specified by {@link #dependencies}. - * - * <p>If the rule is missing an attribute specified by {@link #dependencies}, or if there are - * too many possible assignments, or if any evaluation fails, this throws {@link - * CannotPrecomputeDefaultsException}. - * - * <p>May only be called after all non-{@link ComputedDefault} attributes have been set on the - * {@code rule}. - */ - SkylarkComputedDefault computePossibleValues( - Attribute attr, final Rule rule, final EventHandler eventHandler) - throws InterruptedException, CannotPrecomputeDefaultsException { - - final SkylarkComputedDefaultTemplate owner = SkylarkComputedDefaultTemplate.this; - final String msg = - String.format( - "Cannot compute default value of attribute '%s' in rule '%s': ", - attr.getName().replace('$', '_'), rule.getLabel()); - final AtomicReference<EvalException> caughtEvalExceptionIfAny = new AtomicReference<>(); - ComputationStrategy<InterruptedException> strategy = - new ComputationStrategy<InterruptedException>() { - @Override - public Object compute(AttributeMap map) throws InterruptedException { - try { - return owner.computeValue(map); - } catch (EvalException ex) { - caughtEvalExceptionIfAny.compareAndSet(null, ex); - return null; - } - } - }; - - ImmutableList.Builder<Type<?>> dependencyTypesBuilder = ImmutableList.builder(); - Map<List<Object>, Object> lookupTable = new HashMap<>(); - try { - for (String dependency : dependencies) { - Attribute attribute = rule.getRuleClassObject().getAttributeByNameMaybe(dependency); - if (attribute == null) { - throw new AttributeNotFoundException( - String.format("No such attribute %s in rule %s", dependency, rule.getLabel())); - } - dependencyTypesBuilder.add(attribute.getType()); - } - lookupTable.putAll( - strategy.computeValuesForAllCombinations( - dependencies, attr.getType(), rule, FixedComputationLimiter.INSTANCE)); - if (caughtEvalExceptionIfAny.get() != null) { - throw caughtEvalExceptionIfAny.get(); - } - } catch (AttributeNotFoundException - | TooManyConfigurableAttributesException - | EvalException ex) { - String error = msg + ex.getMessage(); - rule.reportError(error, eventHandler); - throw new CannotPrecomputeDefaultsException(error); - } - return new SkylarkComputedDefault(dependencies, dependencyTypesBuilder.build(), lookupTable); - } - - private Object computeValue(AttributeMap rule) throws EvalException, InterruptedException { - Map<String, Object> attrValues = new HashMap<>(); - for (String attrName : rule.getAttributeNames()) { - Attribute attr = rule.getAttributeDefinition(attrName); - if (!attr.hasComputedDefault()) { - Object value = rule.get(attrName, attr.getType()); - if (!EvalUtils.isNullOrNone(value)) { - attrValues.put(attr.getName(), value); - } - } - } - return invokeCallback(attrValues); - } - - private Object invokeCallback(Map<String, Object> attrValues) - throws EvalException, InterruptedException { - ClassObject attrs = - SkylarkClassObjectConstructor.STRUCT.create( - attrValues, "No such regular (non computed) attribute '%s'."); - Object result = callback.call(attrs); - try { - return type.cast((result == Runtime.NONE) ? type.getDefaultValue() : result); - } catch (ClassCastException ex) { - throw new EvalException( - location, - String.format( - "Expected '%s', but got '%s'", type, EvalUtils.getDataTypeName(result, true))); - } - } - - private static class AttributeNotFoundException extends Exception { - private AttributeNotFoundException(String message) { - super(message); - } - } - - static class CannotPrecomputeDefaultsException extends Exception { - private CannotPrecomputeDefaultsException(String message) { - super(message); - } - } - } - - /** - * A class for computed attributes defined in Skylark. - * - * <p>Unlike {@link ComputedDefault}, instances of this class contain a pre-computed table of all - * possible assignments of depended-on attributes and what the Skylark function evaluates to, and - * {@link #getPossibleValues(Type, Rule)} and {@link #getDefault(AttributeMap)} do lookups in that - * table. - */ - static final class SkylarkComputedDefault extends ComputedDefault { - - private final List<Type<?>> dependencyTypes; - private final Map<List<Object>, Object> lookupTable; - - /** - * Creates a new SkylarkComputedDefault containing a lookup table. - * - * @param requiredAttributes A list of all names of other attributes that are accessed by this - * attribute. - * @param dependencyTypes A list of requiredAttributes' types. - * @param lookupTable An exhaustive mapping from requiredAttributes assignments to values this - * computed default evaluates to. - */ - SkylarkComputedDefault( - ImmutableList<String> requiredAttributes, - ImmutableList<Type<?>> dependencyTypes, - Map<List<Object>, Object> lookupTable) { - super(Preconditions.checkNotNull(requiredAttributes)); - this.dependencyTypes = Preconditions.checkNotNull(dependencyTypes); - this.lookupTable = Preconditions.checkNotNull(lookupTable); - } - - List<Type<?>> getDependencyTypes() { - return dependencyTypes; - } - - Map<List<Object>, Object> getLookupTable() { - return lookupTable; - } - - @Override - public Object getDefault(AttributeMap rule) { - List<Object> key = ComputationStrategy.createDependencyAssignmentTuple(dependencies(), rule); - Preconditions.checkState( - lookupTable.containsKey(key), - "Error in rule '%s': precomputed value missing for dependencies: %s", - rule.getLabel(), - Iterables.toString(key)); - return lookupTable.get(key); - } - - @Override - <T> Iterable<T> getPossibleValues(Type<T> type, Rule rule) { - List<T> result = new ArrayList<>(lookupTable.size()); - for (Object obj : lookupTable.values()) { - result.add(type.cast(obj)); - } - return result; - } - } - - /** * Marker interface for late-bound values. Unfortunately, we can't refer to BuildConfiguration * right now, since that is in a separate compilation unit. * @@ -1596,6 +1212,51 @@ public final class Attribute implements Comparable<Attribute> { public abstract List<Label> resolve(Rule rule, AttributeMap attributes, T configuration); } + /** + * A class for late bound attributes defined in Skylark. + */ + public static final class SkylarkLateBound implements LateBoundDefault<Object> { + + private final SkylarkCallbackFunction callback; + + public SkylarkLateBound(SkylarkCallbackFunction callback) { + this.callback = callback; + } + + @Override + public boolean useHostConfiguration() { + return false; + } + + @Override + public ImmutableSet<Class<?>> getRequiredConfigurationFragments() { + return ImmutableSet.of(); + } + + @Override + public Object getDefault() { + return null; + } + + @Override + public Object resolve(Rule rule, AttributeMap attributes, Object o) + throws EvalException, InterruptedException { + Map<String, Object> attrValues = new HashMap<>(); + for (Attribute attr : rule.getAttributes()) { + if (!attr.isLateBound()) { + Object value = attributes.get(attr.getName(), attr.getType()); + if (value != null) { + attrValues.put(attr.getName(), value); + } + } + } + ClassObject attrs = SkylarkClassObjectConstructor.STRUCT.create( + attrValues, + "No such regular (non late-bound) attribute '%s'."); + return callback.call(attrs, o); + } + } + private final String name; private final Type<?> type; @@ -1606,10 +1267,8 @@ public final class Attribute implements Comparable<Attribute> { // 1. defaultValue == null. // 2. defaultValue instanceof ComputedDefault && // type.isValid(defaultValue.getDefault()) - // 3. defaultValue instanceof SkylarkComputedDefaultTemplate && - // type.isValid(defaultValue.computePossibleValues().getDefault()) - // 4. type.isValid(defaultValue). - // 5. defaultValue instanceof LateBoundDefault && + // 3. type.isValid(defaultValue). + // 4. defaultValue instanceof LateBoundDefault && // type.isValid(defaultValue.getDefault(configuration)) // (We assume a hypothetical Type.isValid(Object) predicate.) private final Object defaultValue; @@ -1997,9 +1656,7 @@ public final class Attribute implements Comparable<Attribute> { * @see #getDefaultValue(Rule) */ boolean hasComputedDefault() { - return (defaultValue instanceof ComputedDefault) - || (defaultValue instanceof SkylarkComputedDefaultTemplate) - || (condition != null); + return (defaultValue instanceof ComputedDefault) || (condition != null); } /** diff --git a/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java b/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java index 7acaa58444..30999c283f 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java +++ b/src/main/java/com/google/devtools/build/lib/packages/AttributeValueSource.java @@ -34,8 +34,7 @@ public enum AttributeValueSource { * Creates a new instance and defines the prefixes for both Skylark and native. * * @param nativePrefix The prefix when converted to a native attribute name. - * @param mustHaveSkylarkPrefix Whether the Skylark name must start with {@link - * AttributeValueSource#SKYLARK_PREFIX}. + * @param mustHaveSkylarkPrefix Whether the Skylark name must start with {@link SKYLARK_PREFIX}. */ AttributeValueSource(String nativePrefix, boolean mustHaveSkylarkPrefix) { this.nativePrefix = nativePrefix; diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java index cba10a705f..751e6cf7f6 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java +++ b/src/main/java/com/google/devtools/build/lib/packages/RuleClass.java @@ -14,8 +14,8 @@ package com.google.devtools.build.lib.packages; -import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; @@ -34,8 +34,6 @@ import com.google.devtools.build.lib.cmdline.PackageIdentifier; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.events.NullEventHandler; -import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate; -import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate.CannotPrecomputeDefaultsException; import com.google.devtools.build.lib.packages.BuildType.SelectorList; import com.google.devtools.build.lib.packages.ConfigurationFragmentPolicy.MissingFragmentPolicy; import com.google.devtools.build.lib.packages.RuleFactory.AttributeValuesMap; @@ -1312,15 +1310,13 @@ public final class RuleClass { * attributeValues} or the default attribute values associated with this {@link RuleClass} and * {@code pkgBuilder}. * - * <p>The created {@link Rule} will also be populated with output files. These output files will - * have been collected from the explicitly provided values of type {@link BuildType#OUTPUT} and - * {@link BuildType#OUTPUT_LIST} as well as from the implicit outputs determined by this {@link - * RuleClass} and the values in {@code attributeValues}. + * <p>The created {@link Rule} will also be populated with output files. These output files + * will have been collected from the explicitly provided values of type {@link BuildType#OUTPUT} + * and {@link BuildType#OUTPUT_LIST} as well as from the implicit outputs determined by this + * {@link RuleClass} and the values in {@code attributeValues}. * * <p>This performs several validity checks. Invalid output file labels result in a thrown {@link - * LabelSyntaxException}. Computed default attributes that fail during precomputation result in a - * {@link CannotPrecomputeDefaultsException}. All other errors are reported on {@code - * eventHandler}. + * LabelSyntaxException}. All other errors are reported on {@code eventHandler}. */ Rule createRule( Package.Builder pkgBuilder, @@ -1330,7 +1326,7 @@ public final class RuleClass { @Nullable FuncallExpression ast, Location location, AttributeContainer attributeContainer) - throws LabelSyntaxException, InterruptedException, CannotPrecomputeDefaultsException { + throws LabelSyntaxException, InterruptedException { Rule rule = pkgBuilder.createRule(ruleLabel, this, location, attributeContainer); populateRuleAttributeValues(rule, pkgBuilder, attributeValues, eventHandler); checkAspectAllowedValues(rule, eventHandler); @@ -1358,7 +1354,7 @@ public final class RuleClass { Location location, AttributeContainer attributeContainer, ImplicitOutputsFunction implicitOutputsFunction) - throws LabelSyntaxException, InterruptedException, CannotPrecomputeDefaultsException { + throws LabelSyntaxException, InterruptedException { Rule rule = pkgBuilder.createRule( ruleLabel, this, @@ -1381,8 +1377,7 @@ public final class RuleClass { Rule rule, Package.Builder pkgBuilder, AttributeValuesMap attributeValues, - EventHandler eventHandler) - throws InterruptedException, CannotPrecomputeDefaultsException { + EventHandler eventHandler) { BitSet definedAttrIndices = populateDefinedRuleAttributeValues(rule, attributeValues, eventHandler); populateDefaultRuleAttributeValues(rule, pkgBuilder, definedAttrIndices, eventHandler); @@ -1462,15 +1457,14 @@ public final class RuleClass { /** * Populates the attributes table of the new {@link Rule} with default values provided by this - * {@link RuleClass} and the {@code pkgBuilder}. This will only provide values for attributes that - * haven't already been populated, using {@code definedAttrIndices} to determine whether an + * {@link RuleClass} and the {@code pkgBuilder}. This will only provide values for attributes + * that haven't already been populated, using {@code definedAttrIndices} to determine whether an * attribute was populated. * * <p>Errors are reported on {@code eventHandler}. */ private void populateDefaultRuleAttributeValues( - Rule rule, Package.Builder pkgBuilder, BitSet definedAttrIndices, EventHandler eventHandler) - throws InterruptedException, CannotPrecomputeDefaultsException { + Rule rule, Package.Builder pkgBuilder, BitSet definedAttrIndices, EventHandler eventHandler) { // Set defaults; ensure that every mandatory attribute has a value. Use the default if none // is specified. List<Attribute> attrsWithComputedDefaults = new ArrayList<>(); @@ -1507,29 +1501,13 @@ public final class RuleClass { // Set computed default attribute values now that all other (i.e. non-computed) default values // have been set. for (Attribute attr : attrsWithComputedDefaults) { - // If Attribute#hasComputedDefault was true above, Attribute#getDefaultValue returns the - // computed default function object or a Skylark computed default template. Note that we - // cannot determine the exact value of the computed default function here because it may - // depend on other attribute values that are configurable (i.e. they came from select({..}) - // expressions in the build language, and they require configuration data from the analysis - // phase to be resolved). Instead, we're setting the attribute value to a reference to the - // computed default function, or if #getDefaultValue is a Skylark computed default - // template, setting the attribute value to a reference to the SkylarkComputedDefault - // returned from SkylarkComputedDefaultTemplate#computePossibleValues. - // - // SkylarkComputedDefaultTemplate#computePossibleValues pre-computes all possible values the - // function may evaluate to, and records them in a lookup table. By calling it here, with an - // EventHandler, any errors that might occur during the function's evaluation can - // be discovered and propagated here. - Object valueToSet; - Object defaultValue = attr.getDefaultValue(rule); - if (defaultValue instanceof SkylarkComputedDefaultTemplate) { - SkylarkComputedDefaultTemplate template = (SkylarkComputedDefaultTemplate) defaultValue; - valueToSet = template.computePossibleValues(attr, rule, eventHandler); - } else { - valueToSet = defaultValue; - } - rule.setAttributeValue(attr, valueToSet, /*explicit=*/ false); + // If Attribute#hasComputedDefault is true, Attribute#getDefaultValue returns the computed + // default function object. Note that we don't evaluate the computed default function here + // because it may depend on other attribute values that are configurable (i.e. they came + // from select({..}) expressions in the build language, and they require configuration data + // from the analysis phase to be resolved). We're setting the attribute value to a + // reference to the computed default function. + rule.setAttributeValue(attr, attr.getDefaultValue(rule), /*explicit=*/ false); } } diff --git a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java index 4795c7b1ac..3b6cf4c904 100644 --- a/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java +++ b/src/main/java/com/google/devtools/build/lib/packages/RuleFactory.java @@ -20,7 +20,6 @@ import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.cmdline.LabelSyntaxException; import com.google.devtools.build.lib.events.EventHandler; import com.google.devtools.build.lib.events.Location; -import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate.CannotPrecomputeDefaultsException; import com.google.devtools.build.lib.packages.Package.NameConflictException; import com.google.devtools.build.lib.packages.PackageFactory.PackageContext; import com.google.devtools.build.lib.syntax.BaseFunction; @@ -29,8 +28,10 @@ import com.google.devtools.build.lib.syntax.FuncallExpression; import com.google.devtools.build.lib.syntax.UserDefinedFunction; import com.google.devtools.build.lib.util.Pair; import com.google.devtools.build.lib.util.Preconditions; + import java.util.Map; import java.util.Set; + import javax.annotation.Nullable; /** @@ -134,7 +135,7 @@ public class RuleFactory { ast, generator.location, attributeContainer); - } catch (LabelSyntaxException | CannotPrecomputeDefaultsException e) { + } catch (LabelSyntaxException e) { throw new RuleFactory.InvalidRuleException(ruleClass + " " + e.getMessage()); } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java index 49740a5fe4..3e2665dc97 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkAttr.java @@ -22,7 +22,7 @@ import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Attribute.AllowedValueSet; import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition; -import com.google.devtools.build.lib.packages.Attribute.SkylarkComputedDefaultTemplate; +import com.google.devtools.build.lib.packages.Attribute.SkylarkLateBound; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.SkylarkAspect; import com.google.devtools.build.lib.skylarkinterface.Param; @@ -46,9 +46,11 @@ import com.google.devtools.build.lib.syntax.UserDefinedFunction; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.FileTypeSet; import com.google.devtools.build.lib.util.Preconditions; + import java.util.ArrayList; import java.util.List; import java.util.Map; + import javax.annotation.Nullable; /** @@ -161,20 +163,10 @@ public final class SkylarkAttr { Object defaultValue = arguments.get(DEFAULT_ARG); if (!EvalUtils.isNullOrNone(defaultValue)) { if (defaultValue instanceof UserDefinedFunction) { - // Computed attribute. Non label type attributes already caused a type check error. - UserDefinedFunction callback = (UserDefinedFunction) defaultValue; - - // SkylarkComputedDefaultTemplate needs to know the names of all attributes that it depends - // on. However, this method does not know anything about other attributes. - // We solve this problem by asking the UserDefinedFunction for the parameter names used in - // the function definition, which must be the names of attributes used by the callback. - ImmutableList<String> dependencies = callback.getSignature().getSignature().getNames(); + // Late bound attribute. Non label type attributes already caused a type check error. builder.value( - new SkylarkComputedDefaultTemplate( - type, - dependencies, - new SkylarkCallbackFunction(callback, ast, env), - ast.getLocation())); + new SkylarkLateBound( + new SkylarkCallbackFunction((UserDefinedFunction) defaultValue, ast, env))); } else { builder.defaultValue(defaultValue); } 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 60b293bb1a..04ff0ac2e7 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 @@ -26,16 +26,7 @@ import com.google.devtools.build.lib.syntax.compiler.LoopLabels; import com.google.devtools.build.lib.syntax.compiler.ReflectionUtils; import com.google.devtools.build.lib.syntax.compiler.VariableScope; import com.google.devtools.build.lib.vfs.PathFragment; -import java.io.File; -import java.io.IOException; -import java.io.PrintWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.nio.file.Files; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; + import net.bytebuddy.ByteBuddy; import net.bytebuddy.asm.ClassVisitorWrapper; import net.bytebuddy.description.method.MethodDescription; @@ -51,6 +42,7 @@ import net.bytebuddy.implementation.MethodDelegation; import net.bytebuddy.implementation.bytecode.ByteCodeAppender; import net.bytebuddy.implementation.bytecode.member.MethodReturn; import net.bytebuddy.matcher.ElementMatchers; + import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; @@ -58,6 +50,17 @@ import org.objectweb.asm.Opcodes; import org.objectweb.asm.util.Textifier; import org.objectweb.asm.util.TraceClassVisitor; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + /** * The actual function registered in the environment. This function is defined in the * parsed code using {@link FunctionDefStatement}. |