Make two Skyframe nodes with the same events and values equal.
We do this by implementing equality for TaggedEvents (and all objects
it transitively includes). Before this change, if a Skyframe node
re-evaluated to the same value as in the previous build, but had
(transitive) events, change pruning would not cut off the evaluation
of its parents. This is not a big issue in practice because most nodes
that would re-evaluate to the same value (like FileValues or
GlobValues) never emit events, and others (like ActionExecutionValues)
have secondary caches that mask this effect.
Also do a drive-by fix where we were using the hash code of a nested
set instead of the shallow hash code (didn't have any bad effects in
practice because we never hash these values).
(Minor formatting clean-ups from https://bazel-review.googlesource.com/1610 )
--
Change-Id: I751a8479627f0456993c5ec8834528aeb593d736
Reviewed-on: https://bazel-review.googlesource.com/1610
MOS_MIGRATED_REVID=98115908
diff --git a/src/main/java/com/google/devtools/build/lib/events/Event.java b/src/main/java/com/google/devtools/build/lib/events/Event.java
index c35e769..27c122e 100644
--- a/src/main/java/com/google/devtools/build/lib/events/Event.java
+++ b/src/main/java/com/google/devtools/build/lib/events/Event.java
@@ -18,6 +18,8 @@
import com.google.common.base.Preconditions;
import java.io.Serializable;
+import java.util.Arrays;
+import java.util.Objects;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@@ -116,6 +118,24 @@
+ getMessage();
}
+ @Override
+ public int hashCode() {
+ return Objects.hash(kind, location, message, tag, Arrays.hashCode(messageBytes));
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ Event that = (Event) other;
+ return Objects.equals(this.kind, that.kind)
+ && Objects.equals(this.location, that.location)
+ && Objects.equals(this.tag, that.tag)
+ && Objects.equals(this.message, that.message)
+ && Arrays.equals(this.messageBytes, that.messageBytes);
+ }
+
/**
* Replay a sequence of events on an {@link EventHandler}.
*/
diff --git a/src/main/java/com/google/devtools/build/lib/events/Location.java b/src/main/java/com/google/devtools/build/lib/events/Location.java
index 160d4a5..58301b7 100644
--- a/src/main/java/com/google/devtools/build/lib/events/Location.java
+++ b/src/main/java/com/google/devtools/build/lib/events/Location.java
@@ -19,6 +19,7 @@
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.Serializable;
+import java.util.Objects;
/**
* A Location is a range of characters within a file.
@@ -52,6 +53,22 @@
public LineAndColumn getStartLineAndColumn() {
return startLineAndColumn;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(path, startLineAndColumn, internalHashCode());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ LocationWithPathAndStartColumn that = (LocationWithPathAndStartColumn) other;
+ return internalEquals(that)
+ && Objects.equals(this.path, that.path)
+ && Objects.equals(this.startLineAndColumn, that.startLineAndColumn);
+ }
}
protected final int startOffset;
@@ -212,6 +229,14 @@
return print();
}
+ protected int internalHashCode() {
+ return Objects.hash(startOffset, endOffset);
+ }
+
+ protected boolean internalEquals(Location that) {
+ return this.startOffset == that.startOffset && this.endOffset == that.endOffset;
+ }
+
/**
* A value class that describes the line and column of an offset in a file.
*/
@@ -247,7 +272,7 @@
@Override
public int hashCode() {
- return line * 81 + column;
+ return line * 41 + column;
}
}
}
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java b/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java
index d5a612e..1f2c609 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/PackageDeserializer.java
@@ -43,6 +43,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -197,6 +198,26 @@
public LineAndColumn getEndLineAndColumn() {
return new LineAndColumn(endLine, endColumn);
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(
+ path.hashCode(), startLine, startColumn, endLine, endColumn, internalHashCode());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ ExplicitLocation that = (ExplicitLocation) other;
+ return this.startLine == that.startLine
+ && this.startColumn == that.startColumn
+ && this.endLine == that.endLine
+ && this.endColumn == that.endColumn
+ && internalEquals(that)
+ && Objects.equals(this.path, that.path);
+ }
}
/**
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java b/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java
index c4a58a9..90b40e3 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java
@@ -26,6 +26,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Stack;
/**
@@ -182,6 +183,21 @@
public LineAndColumn getEndLineAndColumn() {
return lineNumberTable.getLineAndColumn(getEndOffset());
}
+
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(lineNumberTable, internalHashCode());
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ LexerLocation that = (LexerLocation) other;
+ return internalEquals(that) && Objects.equals(this.lineNumberTable, that.lineNumberTable);
+ }
}
/** invariant: symbol positions are half-open intervals. */
diff --git a/src/main/java/com/google/devtools/build/lib/syntax/LineNumberTable.java b/src/main/java/com/google/devtools/build/lib/syntax/LineNumberTable.java
index 3bcf0a0..ac629c7 100644
--- a/src/main/java/com/google/devtools/build/lib/syntax/LineNumberTable.java
+++ b/src/main/java/com/google/devtools/build/lib/syntax/LineNumberTable.java
@@ -25,8 +25,10 @@
import java.io.Serializable;
import java.nio.CharBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -146,6 +148,23 @@
? linestart[line + 1]
: bufferLength);
}
+
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(Arrays.hashCode(linestart), path, bufferLength);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ Regular that = (Regular) other;
+ return this.bufferLength == that.bufferLength
+ && Arrays.equals(this.linestart, that.linestart)
+ && Objects.equals(this.path, that.path);
+ }
}
/**
@@ -197,7 +216,7 @@
}
this.table = hashOrdering.immutableSortedCopy(unorderedTable);
this.bufferLength = buffer.length;
- this.defaultPath = defaultPath;
+ this.defaultPath = Preconditions.checkNotNull(defaultPath);
}
private SingleHashLine getHashLine(int offset) {
@@ -242,5 +261,21 @@
}
return Pair.of(0, 0);
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(table, defaultPath, bufferLength);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ HashLine that = (HashLine) other;
+ return this.bufferLength == that.bufferLength
+ && this.defaultPath.equals(that.defaultPath)
+ && this.table.equals(that.table);
+ }
}
}
diff --git a/src/main/java/com/google/devtools/build/skyframe/TaggedEvents.java b/src/main/java/com/google/devtools/build/skyframe/TaggedEvents.java
index b285b62..394f186 100644
--- a/src/main/java/com/google/devtools/build/skyframe/TaggedEvents.java
+++ b/src/main/java/com/google/devtools/build/skyframe/TaggedEvents.java
@@ -18,6 +18,7 @@
import com.google.devtools.build.lib.events.Event;
import java.io.Serializable;
+import java.util.Objects;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
@@ -60,4 +61,18 @@
public String toString() {
return tag == null ? "<unknown>" : tag + ": " + Iterables.toString(events);
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(tag, events);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other == null || !other.getClass().equals(getClass())) {
+ return false;
+ }
+ TaggedEvents that = (TaggedEvents) other;
+ return Objects.equals(this.tag, that.tag) && Objects.equals(this.events, that.events);
+ }
}
diff --git a/src/main/java/com/google/devtools/build/skyframe/ValueWithMetadata.java b/src/main/java/com/google/devtools/build/skyframe/ValueWithMetadata.java
index e153a68..3795832 100644
--- a/src/main/java/com/google/devtools/build/skyframe/ValueWithMetadata.java
+++ b/src/main/java/com/google/devtools/build/skyframe/ValueWithMetadata.java
@@ -116,7 +116,7 @@
@Override
public int hashCode() {
- return 31 * value.hashCode() + transitiveEvents.hashCode();
+ return 31 * value.hashCode() + transitiveEvents.shallowHashCode();
}
@Override