bazel packages: delete StructImpl.hasField
Essentially every use is followed by a call to getField,
so it is unnecessary and inefficient to make two calls,
and risks potential inconsistency.
Also delete getValueOrNull.
@VisibleForTesting isn't worth the paper it is printed on.
PiperOrigin-RevId: 284208235
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
index c61fd98..8247ec4 100644
--- a/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
+++ b/src/main/java/com/google/devtools/build/lib/analysis/skylark/SkylarkRuleConfiguredTargetUtil.java
@@ -347,7 +347,7 @@
StructImpl struct = (StructImpl) target;
oldStyleProviders = struct;
- if (struct.hasField("providers")) {
+ if (struct.getValue("providers") != null) {
Iterable<?> iterable = cast("providers", struct, Iterable.class, loc);
for (Object o : iterable) {
Info declaredProvider =
diff --git a/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java b/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java
index a05d9b9..867e18a 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/NativeInfo.java
@@ -41,10 +41,14 @@
@Override
public Object getValue(String name) throws EvalException {
+ // Starlark field
Object x = values.get(name);
if (x != null) {
return x;
- } else if (hasField(name)) {
+ }
+
+ // @SkylarkCallable(structField=true) -- Java field
+ if (getFieldNames().contains(name)) {
try {
return CallUtils.getField(SEMANTICS, this, name);
} catch (InterruptedException exception) {
@@ -52,28 +56,32 @@
// exceptions, as they should be logicless field accessors. If this occurs, it's
// indicative of a bad NativeInfo implementation.
throw new IllegalStateException(
- String.format("Access of field %s was unexpectedly interrupted, but should be "
- + "uninterruptible. This is indicative of a bad provider implementation.", name));
+ String.format(
+ "Access of field %s was unexpectedly interrupted, but should be "
+ + "uninterruptible. This is indicative of a bad provider implementation.",
+ name));
}
- } else if (name.equals("to_json") || name.equals("to_proto")) {
- // to_json and to_proto should not be methods of struct or provider instances.
- // However, they are, for now, and it is important that they be consistently
- // returned by attribute lookup operations regardless of whether a field or method
- // is desired. TODO(adonovan): eliminate this hack.
- return new BuiltinCallable(this, name);
- } else {
- return null;
}
- }
- @Override
- public boolean hasField(String name) {
- return getFieldNames().contains(name);
+ // to_json and to_proto should not be methods of struct or provider instances.
+ // However, they are, for now, and it is important that they be consistently
+ // returned by attribute lookup operations regardless of whether a field or method
+ // is desired. TODO(adonovan): eliminate this hack.
+ if (name.equals("to_json") || name.equals("to_proto")) {
+ return new BuiltinCallable(this, name);
+ }
+
+ return null;
}
@Override
public ImmutableCollection<String> getFieldNames() {
if (fieldNames == null) {
+ // TODO(adonovan): the assignment to this.fieldNames is not thread safe!
+ // We cannot assume that build() is a constructor of an object all of
+ // whose fields are final (and thus subject to a write barrier).
+ //
+ // Also, consider using a lazy union of the two underlying sets.
fieldNames =
ImmutableSet.<String>builder()
.addAll(values.keySet())
diff --git a/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java b/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java
index 53f4796..df63ea5 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/SkylarkInfo.java
@@ -243,11 +243,6 @@
return map.size();
}
- /** Returns whether or not a field is mentioned in the layout. */
- public boolean hasField(String field) {
- return map.containsKey(field);
- }
-
/**
* Returns the index position associated with the given field, or null if the field is not
* mentioned by the layout.
@@ -292,11 +287,6 @@
}
@Override
- public boolean hasField(String name) {
- return values.containsKey(name);
- }
-
- @Override
public Object getValue(String name) {
return values.get(name);
}
@@ -365,12 +355,6 @@
}
@Override
- public boolean hasField(String name) {
- Integer index = layout.getFieldIndex(name);
- return index != null && values[index] != null;
- }
-
- @Override
public ImmutableCollection<String> getFieldNames() {
ImmutableSet.Builder<String> result = ImmutableSet.builder();
for (Map.Entry<String, Integer> entry : layout.entrySet()) {
diff --git a/src/main/java/com/google/devtools/build/lib/packages/StructImpl.java b/src/main/java/com/google/devtools/build/lib/packages/StructImpl.java
index 87a1a10..b03a5f0 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/StructImpl.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/StructImpl.java
@@ -13,7 +13,6 @@
// limitations under the License.
package com.google.devtools.build.lib.packages;
-import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
@@ -82,26 +81,6 @@
}
/**
- * Returns whether the given field name exists.
- *
- * <p>This conceptually extends the API for {@link ClassObject}.
- */
- public abstract boolean hasField(String name);
-
- /**
- * <p>Wraps {@link ClassObject#getValue(String)}, returning null in cases where
- * {@link EvalException} would have been thrown.
- */
- @VisibleForTesting
- public Object getValueOrNull(String name) {
- try {
- return getValue(name);
- } catch (EvalException e) {
- return null;
- }
- }
-
- /**
* Returns the result of {@link #getValue(String)}, cast as the given type, throwing {@link
* EvalException} if the cast fails.
*/
@@ -197,6 +176,14 @@
printer.append(")");
}
+ private Object getValueOrNull(String name) {
+ try {
+ return getValue(name);
+ } catch (EvalException e) {
+ return null;
+ }
+ }
+
@Override
public String toProto(Location loc) throws EvalException {
StringBuilder sb = new StringBuilder();
diff --git a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
index ea1bf79..6f7b470 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/cpp/CcModule.java
@@ -57,6 +57,7 @@
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.Link.LinkingMode;
import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcModuleApi;
+import com.google.devtools.build.lib.syntax.ClassObject;
import com.google.devtools.build.lib.syntax.Depset;
import com.google.devtools.build.lib.syntax.Dict;
import com.google.devtools.build.lib.syntax.EvalException;
@@ -882,17 +883,25 @@
/** Checks whether the {@link SkylarkInfo} is of the required type. */
private static void checkRightProviderType(SkylarkInfo provider, String type)
throws EvalException {
- String providerType = (String) provider.getValueOrNull("type_name");
+ String providerType = (String) getValueOrNull(provider, "type_name");
if (providerType == null) {
providerType = provider.getProvider().getPrintableName();
}
- if (!provider.hasField("type_name") || !provider.getValue("type_name").equals(type)) {
+ if (!type.equals(provider.getValue("type_name"))) {
throw new EvalException(
provider.getCreationLoc(),
String.format("Expected object of type '%s', received '%s'.", type, providerType));
}
}
+ private static Object getValueOrNull(ClassObject x, String name) {
+ try {
+ return x.getValue(name);
+ } catch (EvalException e) {
+ return null;
+ }
+ }
+
/** Creates a {@link Feature} from a {@link SkylarkInfo}. */
@VisibleForTesting
static Feature featureFromSkylark(SkylarkInfo featureStruct) throws EvalException {
@@ -939,8 +948,7 @@
ImmutableList<SkylarkInfo> requires =
getSkylarkProviderListFromSkylarkField(featureStruct, "requires");
for (SkylarkInfo featureSetStruct : requires) {
- if (!featureSetStruct.hasField("type_name")
- || !featureSetStruct.getValue("type_name").equals("feature_set")) {
+ if (!"feature_set".equals(featureSetStruct.getValue("type_name"))) { // getValue() may be null
throw new EvalException(
featureStruct.getCreationLoc(), "expected object of type 'feature_set'.");
}
@@ -1290,7 +1298,7 @@
private static <T> T getFieldFromSkylarkProvider(
SkylarkInfo provider, String fieldName, Class<T> clazz) throws EvalException {
- Object obj = provider.getValueOrNull(fieldName);
+ Object obj = provider.getValue(fieldName);
if (obj == null) {
throw new EvalException(
provider.getCreationLoc(), String.format("Missing mandatory field '%s'", fieldName));
@@ -1309,28 +1317,25 @@
/** Returns a list of strings from a field of a {@link SkylarkInfo}. */
private static ImmutableList<String> getStringListFromSkylarkProviderField(
SkylarkInfo provider, String fieldName) throws EvalException {
- return Sequence.castSkylarkListOrNoneToList(
- provider.getValueOrNull(fieldName), String.class, fieldName)
- .stream()
- .collect(ImmutableList.toImmutableList());
+ return ImmutableList.copyOf(
+ Sequence.castSkylarkListOrNoneToList(
+ getValueOrNull(provider, fieldName), String.class, fieldName));
}
/** Returns a set of strings from a field of a {@link SkylarkInfo}. */
private static ImmutableSet<String> getStringSetFromSkylarkProviderField(
SkylarkInfo provider, String fieldName) throws EvalException {
- return Sequence.castSkylarkListOrNoneToList(
- provider.getValueOrNull(fieldName), String.class, fieldName)
- .stream()
- .collect(ImmutableSet.toImmutableSet());
+ return ImmutableSet.copyOf(
+ Sequence.castSkylarkListOrNoneToList(
+ getValueOrNull(provider, fieldName), String.class, fieldName));
}
/** Returns a list of SkylarkInfo providers from a field of a {@link SkylarkInfo}. */
private static ImmutableList<SkylarkInfo> getSkylarkProviderListFromSkylarkField(
SkylarkInfo provider, String fieldName) throws EvalException {
- return Sequence.castSkylarkListOrNoneToList(
- provider.getValueOrNull(fieldName), SkylarkInfo.class, fieldName)
- .stream()
- .collect(ImmutableList.toImmutableList());
+ return ImmutableList.copyOf(
+ Sequence.castSkylarkListOrNoneToList(
+ getValueOrNull(provider, fieldName), SkylarkInfo.class, fieldName));
}
private static void getLegacyArtifactNamePatterns(
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java b/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java
index 522ecaa..96a2a07 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/ClassObject.java
@@ -18,6 +18,7 @@
import javax.annotation.Nullable;
/** An interface for Skylark objects (such as structs) that have fields. */
+// TODO(adonovan): rename "HasFields".
public interface ClassObject {
/**
@@ -25,6 +26,7 @@
*
* @throws EvalException if a user-visible error occurs (other than non-existent field).
*/
+ // TODO(adonovan): rename "getField".
@Nullable
Object getValue(String name) throws EvalException;
@@ -33,6 +35,7 @@
*
* @throws EvalException if a user-visible error occurs
*/
+ // TODO(adonovan): change type to ImmutableSet. Users rely on O(1) lookup.
ImmutableCollection<String> getFieldNames() throws EvalException;
/**