Use Object for what instead of String
The string is only used for error messaging on failure. Instead of constructing
lots of strings allow passing in a generic Object whos toString is used for the
error message. This allows us to bypass a lot of garbage string generation, at
th expense of a little indirection.
Chose Object over some specialized type, since we use inline strings frequently
as well.
--
MOS_MIGRATED_REVID=133741724
diff --git a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
index 3a8c69c..825efb5 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/BuildType.java
@@ -28,7 +28,6 @@
import com.google.devtools.build.lib.syntax.Type.ConversionException;
import com.google.devtools.build.lib.syntax.Type.DictType;
import com.google.devtools.build.lib.syntax.Type.ListType;
-
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -36,7 +35,6 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-
import javax.annotation.Nullable;
/**
@@ -87,7 +85,7 @@
}
@Override
- public DistributionType convert(Object x, String what, Object context) {
+ public DistributionType convert(Object x, Object what, Object context) {
throw new UnsupportedOperationException();
}
@@ -158,7 +156,7 @@
* <p>The caller is responsible for casting the returned value appropriately.
*/
public static <T> Object selectableConvert(
- Type type, Object x, String what, @Nullable Label context)
+ Type type, Object x, Object what, @Nullable Label context)
throws ConversionException {
if (x instanceof com.google.devtools.build.lib.syntax.SelectorList) {
return new SelectorList<T>(
@@ -177,7 +175,7 @@
}
@Override
- public FilesetEntry convert(Object x, String what, Object context)
+ public FilesetEntry convert(Object x, Object what, Object context)
throws ConversionException {
if (!(x instanceof FilesetEntry)) {
throw new ConversionException(this, x, what);
@@ -233,7 +231,7 @@
}
@Override
- public Label convert(Object x, String what, Object context)
+ public Label convert(Object x, Object what, Object context)
throws ConversionException {
if (x instanceof Label) {
return (Label) x;
@@ -259,7 +257,7 @@
}
@Override
- public License convert(Object x, String what, Object context) throws ConversionException {
+ public License convert(Object x, Object what, Object context) throws ConversionException {
try {
List<String> licenseStrings = STRING_LIST.convert(x, what);
return License.parseLicense(licenseStrings);
@@ -303,7 +301,7 @@
}
@Override
- public Set<DistributionType> convert(Object x, String what, Object context)
+ public Set<DistributionType> convert(Object x, Object what, Object context)
throws ConversionException {
try {
List<String> distribStrings = STRING_LIST.convert(x, what);
@@ -366,7 +364,7 @@
}
@Override
- public Label convert(Object x, String what, Object context)
+ public Label convert(Object x, Object what, Object context)
throws ConversionException {
String value;
@@ -401,7 +399,7 @@
private final List<Selector<T>> elements;
@VisibleForTesting
- SelectorList(List<Object> x, String what, @Nullable Label context,
+ SelectorList(List<Object> x, Object what, @Nullable Label context,
Type<T> originalType) throws ConversionException {
if (x.size() > 1 && originalType.concat(ImmutableList.<T>of()) == null) {
throw new ConversionException(
@@ -411,7 +409,7 @@
ImmutableList.Builder<Selector<T>> builder = ImmutableList.builder();
for (Object elem : x) {
if (elem instanceof SelectorValue) {
- builder.add(new Selector<T>(((SelectorValue) elem).getDictionary(), what,
+ builder.add(new Selector<>(((SelectorValue) elem).getDictionary(), what,
context, originalType, ((SelectorValue) elem).getNoMatchError()));
} else {
T directValue = originalType.convert(elem, what, context);
@@ -482,7 +480,7 @@
/**
* Creates a new Selector using the default error message when no conditions match.
*/
- Selector(ImmutableMap<?, ?> x, String what, @Nullable Label context, Type<T> originalType)
+ Selector(ImmutableMap<?, ?> x, Object what, @Nullable Label context, Type<T> originalType)
throws ConversionException {
this(x, what, context, originalType, "");
}
@@ -490,7 +488,7 @@
/**
* Creates a new Selector with a custom error message for when no conditions match.
*/
- Selector(ImmutableMap<?, ?> x, String what, @Nullable Label context, Type<T> originalType,
+ Selector(ImmutableMap<?, ?> x, Object what, @Nullable Label context, Type<T> originalType,
String noMatchError) throws ConversionException {
this.originalType = originalType;
LinkedHashMap<Label, T> result = new LinkedHashMap<>();
@@ -633,7 +631,7 @@
// Like BooleanType, this must handle integers as well.
@Override
- public TriState convert(Object x, String what, Object context)
+ public TriState convert(Object x, Object what, Object context)
throws ConversionException {
if (x instanceof TriState) {
return (TriState) x;
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 cba10a7..53aabfa 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
@@ -1727,9 +1727,11 @@
*/
private static Object convertFromBuildLangType(Rule rule, Attribute attr, Object buildLangValue)
throws ConversionException {
- String what = "attribute '" + attr.getName() + "' in '" + rule.getRuleClass() + "' rule";
- Object converted =
- BuildType.selectableConvert(attr.getType(), buildLangValue, what, rule.getLabel());
+ Object converted = BuildType.selectableConvert(
+ attr.getType(),
+ buildLangValue,
+ new AttributeConversionContext(attr.getName(), rule.getRuleClass()),
+ rule.getLabel());
if ((converted instanceof SelectorList<?>) && !attr.isConfigurable()) {
throw new ConversionException(
@@ -1749,6 +1751,28 @@
}
/**
+ * Provides a {@link #toString()} description of the attribute being converted for
+ * {@link BuildType#selectableConvert}. This is preferred over a raw string to avoid uselessly
+ * constructing strings which are never used. A separate class instead of inline to avoid
+ * accidental memory leaks.
+ */
+ private static class AttributeConversionContext {
+ private final String attrName;
+ private final String ruleClass;
+
+ AttributeConversionContext(String attrName, String ruleClass) {
+ this.attrName = attrName;
+ this.ruleClass = ruleClass;
+ }
+
+ @Override
+ public String toString() {
+ return "attribute '" + attrName + "' in '" + ruleClass + "' rule";
+ }
+ }
+
+
+ /**
* Verifies that the rule has a valid value for the attribute according to its allowed values.
*
* <p>If the value for the given attribute on the given rule is invalid, an error will be recorded
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Type.java b/src/main/java/com/google/devtools/build/lib/syntax/Type.java
index a8987e8..37c280c 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Type.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Type.java
@@ -23,7 +23,6 @@
import com.google.devtools.build.lib.syntax.SkylarkList.MutableList;
import com.google.devtools.build.lib.util.LoggingUtil;
import com.google.devtools.build.lib.util.StringCanonicalizer;
-
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
@@ -33,7 +32,6 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
-
import javax.annotation.Nullable;
/**
@@ -73,14 +71,14 @@
* {@link com.google.devtools.build.lib.packages.BuildType#selectableConvert}.
*
* @param x the build-interpreter value to convert.
- * @param what a string description of what x is for; should be included in
- * any exception thrown. Grammatically, must describe a syntactic
+ * @param what an object having a toString describing what x is for; should be included in
+ * any exception thrown. Grammatically, must produce a string describe a syntactic
* construct, e.g. "attribute 'srcs' of rule foo".
* @param context the label of the current BUILD rule; must be non-null if resolution of
* package-relative label strings is required
* @throws ConversionException if there was a problem performing the type conversion
*/
- public abstract T convert(Object x, String what, @Nullable Object context)
+ public abstract T convert(Object x, Object what, @Nullable Object context)
throws ConversionException;
// TODO(bazel-team): Check external calls (e.g. in PackageFactory), verify they always want
// this over selectableConvert.
@@ -90,7 +88,7 @@
* Useful for converting values to types that do not involve the type {@code LABEL}
* and hence do not require the label of the current package.
*/
- public final T convert(Object x, String what) throws ConversionException {
+ public final T convert(Object x, Object what) throws ConversionException {
return convert(x, what, null);
}
@@ -240,7 +238,7 @@
* an explanatory error message.
*/
public static class ConversionException extends EvalException {
- private static String message(Type<?> type, Object value, String what) {
+ private static String message(Type<?> type, Object value, @Nullable Object what) {
StringBuilder builder = new StringBuilder();
builder.append("expected value of type '").append(type).append("'");
if (what != null) {
@@ -252,7 +250,7 @@
return builder.toString();
}
- public ConversionException(Type<?> type, Object value, String what) {
+ public ConversionException(Type<?> type, Object value, @Nullable Object what) {
super(null, message(type, value, what));
}
@@ -295,7 +293,7 @@
}
@Override
- public Object convert(Object x, String what, Object context) {
+ public Object convert(Object x, Object what, Object context) {
return x;
}
}
@@ -327,7 +325,7 @@
}
@Override
- public Integer convert(Object x, String what, Object context)
+ public Integer convert(Object x, Object what, Object context)
throws ConversionException {
if (!(x instanceof Integer)) {
throw new ConversionException(this, x, what);
@@ -373,7 +371,7 @@
// Conversion to boolean must also tolerate integers of 0 and 1 only.
@Override
- public Boolean convert(Object x, String what, Object context)
+ public Boolean convert(Object x, Object what, Object context)
throws ConversionException {
if (x instanceof Boolean) {
return (Boolean) x;
@@ -428,7 +426,7 @@
}
@Override
- public String convert(Object x, String what, Object context)
+ public String convert(Object x, Object what, Object context)
throws ConversionException {
if (!(x instanceof String)) {
throw new ConversionException(this, x, what);
@@ -512,11 +510,11 @@
}
@Override
- public Map<KeyT, ValueT> convert(Object x, String what, Object context)
+ public Map<KeyT, ValueT> convert(Object x, Object what, Object context)
throws ConversionException {
if (!(x instanceof Map<?, ?>)) {
throw new ConversionException(String.format(
- "Expected a map for dictionary but got a %s", x.getClass().getName()));
+ "Expected a map for dictionary but got a %s", x.getClass().getName()));
}
// Order the keys so the return value will be independent of insertion order.
Map<KeyT, ValueT> result = new TreeMap<>();
@@ -596,7 +594,7 @@
}
@Override
- public List<ElemT> convert(Object x, String what, Object context)
+ public List<ElemT> convert(Object x, Object what, Object context)
throws ConversionException {
if (!(x instanceof Iterable<?>)) {
throw new ConversionException(this, x, what);
@@ -604,8 +602,10 @@
int index = 0;
Iterable<?> iterable = (Iterable<?>) x;
List<ElemT> result = new ArrayList<>(Iterables.size(iterable));
+ ListConversionContext conversionContext = new ListConversionContext(what);
for (Object elem : iterable) {
- ElemT converted = elemType.convert(elem, "element " + index + " of " + what, context);
+ conversionContext.update(index);
+ ElemT converted = elemType.convert(elem, conversionContext, context);
if (converted != null) {
result.add(converted);
} else {
@@ -660,6 +660,30 @@
}
return tags;
}
+
+ /**
+ * Provides a {@link #toString()} description of the context of the value in a list being
+ * converted. This is preferred over a raw string to avoid uselessly constructing strings which
+ * are never used. This class is mutable (the index is updated).
+ */
+ private static class ListConversionContext {
+ private final Object what;
+ private int index = 0;
+
+ ListConversionContext(Object what) {
+ this.what = what;
+ }
+
+ void update(int index) {
+ this.index = index;
+ }
+
+ @Override
+ public String toString() {
+ return "element " + index + " of " + what;
+ }
+
+ }
}
/** Type for lists of arbitrary objects */
@@ -673,7 +697,7 @@
@Override
@SuppressWarnings("unchecked")
- public List<Object> convert(Object x, String what, Object context)
+ public List<Object> convert(Object x, Object what, Object context)
throws ConversionException {
if (x instanceof SkylarkList) {
return ((SkylarkList) x).getImmutableList();