Define a reusable comparator for Skylark objects.
--
MOS_MIGRATED_REVID=91304912
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
index 6eaf45e..b5bf4f5 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/BinaryOperatorExpression.java
@@ -62,37 +62,11 @@
return lhs + " " + operator + " " + rhs;
}
- /**
- * Compares two lists, following on Python semantics.
- *
- * <p>Elements are compared until two elements are not equal or we reach the end of a list.
- */
- private int compareLists(SkylarkList lval, SkylarkList rval) throws EvalException {
- for (int i = 0; i < Math.min(lval.size(), rval.size()); i++) {
- int cmp = compare(lval.get(i), rval.get(i));
- if (cmp != 0) {
- return cmp;
- }
- }
- return Integer.compare(lval.size(), rval.size());
- }
-
- @SuppressWarnings("unchecked")
private int compare(Object lval, Object rval) throws EvalException {
- lval = SkylarkType.convertToSkylark(lval, getLocation());
- rval = SkylarkType.convertToSkylark(rval, getLocation());
-
- if (lval instanceof SkylarkList && rval instanceof SkylarkList) {
- return compareLists((SkylarkList) lval, (SkylarkList) rval);
- }
- if (!(lval instanceof Comparable)) {
- throw new EvalException(getLocation(), lval + " is not comparable");
- }
try {
- return ((Comparable<Object>) lval).compareTo(rval);
- } catch (ClassCastException e) {
- throw new EvalException(getLocation(), "Cannot compare " + EvalUtils.getDataTypeName(lval)
- + " with " + EvalUtils.getDataTypeName(rval));
+ return EvalUtils.SKYLARK_COMPARATOR.compare(lval, rval);
+ } catch (EvalUtils.ComparisonException e) {
+ throw new EvalException(getLocation(), e);
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
index 9f4cd83..bef2b09 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/EvalUtils.java
@@ -32,6 +32,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
+import java.util.Comparator;
import java.util.Formattable;
import java.util.Formatter;
import java.util.IllegalFormatException;
@@ -45,6 +46,57 @@
*/
public abstract class EvalUtils {
+ /**
+ * The exception that SKYLARK_COMPARATOR might throw. This is an unchecked exception
+ * because Comparator doesn't let us declare exceptions. It should normally be caught
+ * and wrapped in an EvalException.
+ */
+ public static class ComparisonException extends RuntimeException {
+ public ComparisonException(String msg) {
+ super(msg);
+ }
+ }
+
+ /**
+ * Compare two Skylark objects.
+ *
+ * <p> It may throw an unchecked exception ComparisonException that should be wrapped in
+ * an EvalException.
+ */
+ public static final Comparator<Object> SKYLARK_COMPARATOR = new Comparator<Object>() {
+ private int compareLists(SkylarkList o1, SkylarkList o2) {
+ for (int i = 0; i < Math.min(o1.size(), o2.size()); i++) {
+ int cmp = compare(o1.get(i), o2.get(i));
+ if (cmp != 0) {
+ return cmp;
+ }
+ }
+ return Integer.compare(o1.size(), o2.size());
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public int compare(Object o1, Object o2) {
+ Location loc = null;
+ try {
+ o1 = SkylarkType.convertToSkylark(o1, loc);
+ o2 = SkylarkType.convertToSkylark(o2, loc);
+ } catch (EvalException e) {
+ throw new ComparisonException(e.getMessage());
+ }
+
+ if (o1 instanceof SkylarkList && o2 instanceof SkylarkList) {
+ return compareLists((SkylarkList) o1, (SkylarkList) o2);
+ }
+ try {
+ return ((Comparable<Object>) o1).compareTo(o2);
+ } catch (ClassCastException e) {
+ throw new ComparisonException(
+ "Cannot compare " + getDataTypeName(o1) + " with " + EvalUtils.getDataTypeName(o2));
+ }
+ }
+ };
+
// TODO(bazel-team): Yet an other hack committed in the name of Skylark. One problem is that the
// syntax package cannot depend on actions so we have to have this until Actions are immutable.
// The other is that BuildConfigurations are technically not immutable but they cannot be modified
@@ -609,7 +661,11 @@
Map<Comparable<?>, Object> dict = (Map<Comparable<?>, Object>) o;
// For dictionaries we iterate through the keys only
// For determinism, we sort the keys.
- return Ordering.natural().sortedCopy(dict.keySet());
+ try {
+ return Ordering.from(SKYLARK_COMPARATOR).sortedCopy(dict.keySet());
+ } catch (ComparisonException e) {
+ throw new EvalException(loc, e);
+ }
} else if (o instanceof SkylarkNestedSet) {
return ((SkylarkNestedSet) o).toCollection();
} else {