| // 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.packages; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * Provides attribute setting and retrieval for a Rule. Encapsulating attribute access here means it |
| * can be passed around independently of the Rule itself. In particular, it can be consumed by |
| * independent {@link AttributeMap} instances that can apply varying kinds of logic for determining |
| * the "value" of an attribute. For example, a configurable attribute's "value" may be a { config |
| * --> value } dictionary or a configuration-bound lookup on that dictionary, depending on the |
| * context in which it's requested. |
| * |
| * <p>This class provides the lowest-level access to attribute information. It is *not* intended to |
| * be a robust public interface, but rather just an input to {@link AttributeMap} instances. Use |
| * those instances for all domain-level attribute access. |
| */ |
| public final class AttributeContainer { |
| |
| private final RuleClass ruleClass; |
| |
| // Attribute values, keyed by attribute index: |
| private final Object[] attributeValues; |
| |
| // Holds a list of attribute indices. |
| // The first byte gives the length of the list. |
| // The list records which attributes were set explicitly in the BUILD file. |
| // The list may be padded with zeros at the end. |
| private byte[] state; |
| |
| /** |
| * Create a container for a rule of the given rule class. |
| */ |
| public AttributeContainer(RuleClass ruleClass) { |
| int n = ruleClass.getAttributeCount(); |
| if (n > 254) { |
| // We reserve the zero byte as a hole/sentinel inside state[]. |
| // If you hit this limit, replace byte with char and remove the masking with 0xff. |
| throw new AssertionError("can't pack " + n + " rule indices into bytes"); |
| } |
| this.ruleClass = ruleClass; |
| this.attributeValues = new Object[n]; |
| this.state = EMPTY_STATE; |
| } |
| |
| private static final byte[] EMPTY_STATE = {0}; |
| |
| /** |
| * Returns an attribute value by name, or null on no match. |
| */ |
| @Nullable |
| public Object getAttr(String attrName) { |
| Integer idx = ruleClass.getAttributeIndex(attrName); |
| return idx != null ? attributeValues[idx] : null; |
| } |
| |
| /** |
| * See {@link #isAttributeValueExplicitlySpecified(String)}. |
| */ |
| public boolean isAttributeValueExplicitlySpecified(Attribute attribute) { |
| return isAttributeValueExplicitlySpecified(attribute.getName()); |
| } |
| |
| /** |
| * Returns true iff the value of the specified attribute is explicitly set in the BUILD file. This |
| * returns true also if the value explicitly specified in the BUILD file is the same as the |
| * attribute's default value. In addition, this method return false if the rule has no attribute |
| * with the given name. |
| */ |
| public boolean isAttributeValueExplicitlySpecified(String attributeName) { |
| Integer idx = ruleClass.getAttributeIndex(attributeName); |
| return idx != null && getExplicit(idx); |
| } |
| |
| /** |
| * Returns the number of elements of state[] currently used to store |
| * indices of "explicitly set" attributes. |
| */ |
| private int explicitCount() { |
| return 0xff & state[0]; |
| } |
| |
| private boolean getExplicit(int index) { |
| int n = explicitCount(); |
| for (int i = 1; i <= n; ++i) { |
| if ((0xff & state[i]) == index + 1) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private void setExplicit(int index) { |
| if (getExplicit(index)) { |
| return; |
| } |
| ensureSpace(); |
| int n = explicitCount() + 1; |
| state[0] = (byte) n; |
| state[n] = (byte) (index + 1); |
| } |
| |
| /** |
| * Ensures that the state[n] byte is equal to the sentinel value 0, so there is room for another |
| * attribute's explicit bit to be set. |
| */ |
| private void ensureSpace() { |
| int n = explicitCount() + 1; |
| if (n < state.length && state[n] == 0) { |
| return; |
| } |
| // Grow up to the next multiple of eight bytes, as the object will be |
| // aligned to eight bytes anyway. Pad with zeros at the end. |
| byte[] newState = new byte[(state.length | 7) + 1]; |
| // Copy stored explicit attributes to the beginning of the array. |
| System.arraycopy(state, 0, newState, 0, n); |
| state = newState; |
| } |
| |
| Object getAttributeValue(int index) { |
| return attributeValues[index]; |
| } |
| |
| void setAttributeValue(Attribute attribute, Object value, boolean explicit) { |
| String name = attribute.getName(); |
| Integer index = ruleClass.getAttributeIndex(name); |
| if (!explicit && getExplicit(index)) { |
| throw new IllegalArgumentException("attribute " + name + " already explicitly set"); |
| } |
| attributeValues[index] = value; |
| if (explicit) { |
| setExplicit(index); |
| } |
| } |
| |
| // This sets the attribute "explicitly" as if it came from the BUILD file. |
| // At present, the sole use of this is for the test_suite.$implicit_tests |
| // attribute, which is synthesized during package loading. We do want to |
| // consider that "explicitly set" so that it appears in query output. |
| void setAttributeValueByName(String attrName, Object value) { |
| setAttributeValue(ruleClass.getAttributeByName(attrName), value, true); |
| } |
| } |