aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java
diff options
context:
space:
mode:
authorGravatar shahan <shahan@google.com>2018-01-16 18:21:16 -0800
committerGravatar Copybara-Service <copybara-piper@google.com>2018-01-16 18:22:48 -0800
commit7ac7b63c658509fd335db6f0149da8e2786c488a (patch)
tree04bf7c852fc0137ace91ce568c7a4a268396ed79 /src/main/java
parent6f95124c3453006149eac955d7620540c7d6bda4 (diff)
Codec for Location.
* Moves SingletonCodec to third_party. PiperOrigin-RevId: 182143153
Diffstat (limited to 'src/main/java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/BUILD2
-rw-r--r--src/main/java/com/google/devtools/build/lib/events/Location.java70
-rw-r--r--src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java88
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/Lexer.java15
-rw-r--r--src/main/java/com/google/devtools/build/lib/syntax/LineNumberTable.java123
5 files changed, 232 insertions, 66 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD
index a9c52a334a..826ce84a35 100644
--- a/src/main/java/com/google/devtools/build/lib/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/BUILD
@@ -240,6 +240,7 @@ java_library(
deps = [
"//src/main/java/com/google/devtools/build/lib:io",
"//src/main/java/com/google/devtools/build/lib/concurrent",
+ "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/vfs",
"//third_party:guava",
"//third_party:jsr305",
@@ -327,6 +328,7 @@ java_library(
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
"//src/main/java/com/google/devtools/build/lib/concurrent",
"//src/main/java/com/google/devtools/build/lib/profiler",
+ "//src/main/java/com/google/devtools/build/lib/skyframe/serialization/autocodec",
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
"//third_party:asm",
"//third_party:asm-commons",
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 24d2c2dcb8..412d601a2d 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
@@ -15,6 +15,9 @@
package com.google.devtools.build.lib.events;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.SingletonCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.io.Serializable;
@@ -23,24 +26,29 @@ import java.util.Objects;
/**
* A Location is a range of characters within a file.
*
- * <p>The start and end locations may be the same, in which case the Location
- * denotes a point in the file, not a range. The path may be null, indicating
- * an unknown file.
+ * <p>The start and end locations may be the same, in which case the Location denotes a point in the
+ * file, not a range. The path may be null, indicating an unknown file.
*
- * <p>Implementations of Location should be optimised for speed of construction,
- * not speed of attribute access, as far more Locations are created during
- * parsing than are ever used to display error messages.
+ * <p>Implementations of Location should be optimised for speed of construction, not speed of
+ * attribute access, as far more Locations are created during parsing than are ever used to display
+ * error messages.
*/
+@AutoCodec(strategy = AutoCodec.Strategy.POLYMORPHIC)
public abstract class Location implements Serializable {
+ public static final ObjectCodec<Location> CODEC = new Location_AutoCodec();
+ @AutoCodec
@Immutable
- private static final class LocationWithPathAndStartColumn extends Location {
+ static final class LocationWithPathAndStartColumn extends Location {
+ public static final ObjectCodec<LocationWithPathAndStartColumn> CODEC =
+ new Location_LocationWithPathAndStartColumn_AutoCodec();
+
private final PathFragment path;
private final LineAndColumn startLineAndColumn;
- private LocationWithPathAndStartColumn(PathFragment path, int startOffSet, int endOffSet,
- LineAndColumn startLineAndColumn) {
- super(startOffSet, endOffSet);
+ LocationWithPathAndStartColumn(
+ PathFragment path, int startOffset, int endOffset, LineAndColumn startLineAndColumn) {
+ super(startOffset, endOffset);
this.path = path;
this.startLineAndColumn = startLineAndColumn;
}
@@ -254,11 +262,12 @@ public abstract class Location implements Serializable {
return this.startOffset == that.startOffset && this.endOffset == that.endOffset;
}
- /**
- * A value class that describes the line and column of an offset in a file.
- */
+ /** A value class that describes the line and column of an offset in a file. */
+ @AutoCodec
@Immutable
- public static final class LineAndColumn implements Serializable {
+ public static final class LineAndColumn {
+ public static final ObjectCodec<LineAndColumn> CODEC = new Location_LineAndColumn_AutoCodec();
+
private final int line;
private final int column;
@@ -293,11 +302,16 @@ public abstract class Location implements Serializable {
}
}
- /**
- * Dummy location for built-in functions which ensures that stack traces contain "nice" location
- * strings.
- */
- public static final Location BUILTIN = new Location(0, 0) {
+ private static final class BuiltinLocation extends Location {
+ public static final BuiltinLocation INSTANCE = new BuiltinLocation();
+
+ public static final ObjectCodec<BuiltinLocation> CODEC =
+ SingletonCodec.of(INSTANCE, "BuiltinLocation");
+
+ private BuiltinLocation() {
+ super(0, 0);
+ }
+
@Override
public String toString() {
return "Built-In";
@@ -307,7 +321,23 @@ public abstract class Location implements Serializable {
public PathFragment getPath() {
return null;
}
- };
+
+ @Override
+ public int hashCode() {
+ return internalHashCode();
+ }
+
+ @Override
+ public boolean equals(Object object) {
+ return object instanceof BuiltinLocation;
+ }
+ }
+
+ /**
+ * Dummy location for built-in functions which ensures that stack traces contain "nice" location
+ * strings.
+ */
+ public static final Location BUILTIN = BuiltinLocation.INSTANCE;
/**
* Returns the location in the format "filename:line".
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java
new file mode 100644
index 0000000000..3dbf328f37
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/serialization/SingletonCodec.java
@@ -0,0 +1,88 @@
+// Copyright 2018 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.skyframe.serialization;
+
+import com.google.common.base.Preconditions;
+import com.google.protobuf.CodedInputStream;
+import com.google.protobuf.CodedOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Specialized {@link ObjectCodec} for storing singleton values. Values serialize to a supplied
+ * representation, which is useful for debugging and is used to verify the serialized representation
+ * during deserialization.
+ */
+public class SingletonCodec<T> implements ObjectCodec<T> {
+
+ /**
+ * Create instance wrapping the singleton {@code value}. Will serialize to the byte array
+ * representation of {@code mnemonic}. On deserialization if {@code mnemonic} matches the
+ * serialized data then {@code value} is returned.
+ */
+ public static <T> SingletonCodec<T> of(T value, String mnemonic) {
+ return new SingletonCodec<T>(value, mnemonic);
+ }
+
+ private final T value;
+ private final byte[] mnemonic;
+
+ private SingletonCodec(T value, String mnemonic) {
+ this.value = Preconditions.checkNotNull(value, "SingletonCodec cannot represent null");
+ this.mnemonic = mnemonic.getBytes(StandardCharsets.UTF_8);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Class<T> getEncodedClass() {
+ return (Class<T>) value.getClass();
+ }
+
+ @Override
+ public void serialize(T t, CodedOutputStream codedOut) throws IOException {
+ // TODO(michajlo): See how usefuly mnemonic actually winds up being for debugging, we may
+ // want to just toss it and trust that the classifier for this value is good enough.
+ codedOut.writeByteArrayNoTag(mnemonic);
+ }
+
+ @Override
+ public T deserialize(CodedInputStream codedIn) throws SerializationException, IOException {
+ // Get ByteBuffer instead of raw bytes, as it may be a direct view of the data and not a copy,
+ // which is much more efficient.
+ ByteBuffer readMnemonic = codedIn.readByteBuffer();
+ if (!bytesEqual(mnemonic, readMnemonic)) {
+ throw new SerializationException(
+ "Failed to decode singleton " + value + " expected " + Arrays.toString(mnemonic));
+ }
+ return value;
+ }
+
+ private static boolean bytesEqual(byte[] expected, ByteBuffer buffer) {
+ if (buffer.remaining() != expected.length) {
+ return false;
+ }
+
+ for (int i = 0; i < expected.length; i++) {
+ if (expected[i] != buffer.get(i)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
+
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 10ff03e100..7e3b731d6f 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
@@ -23,6 +23,8 @@ import com.google.devtools.build.lib.events.EventHandler;
import com.google.devtools.build.lib.events.Location;
import com.google.devtools.build.lib.profiler.Profiler;
import com.google.devtools.build.lib.profiler.ProfilerTask;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.HashMap;
@@ -157,19 +159,21 @@ public final class Lexer {
}
Location createLocation(int start, int end) {
- return new LexerLocation(locationInfo, start, end);
+ return new LexerLocation(locationInfo.lineNumberTable, start, end);
}
// Don't use an inner class as we don't want to close over the Lexer, only
// the LocationInfo.
+ @AutoCodec
@Immutable
- private static final class LexerLocation extends Location {
+ static final class LexerLocation extends Location {
+ public static final ObjectCodec<LexerLocation> CODEC = new Lexer_LexerLocation_AutoCodec();
private final LineNumberTable lineNumberTable;
- LexerLocation(LocationInfo locationInfo, int start, int end) {
- super(start, end);
- this.lineNumberTable = locationInfo.lineNumberTable;
+ LexerLocation(LineNumberTable lineNumberTable, int startOffset, int endOffset) {
+ super(startOffset, endOffset);
+ this.lineNumberTable = lineNumberTable;
}
@Override
@@ -192,7 +196,6 @@ public final class Lexer {
return lineNumberTable.getLineAndColumn(endOffset);
}
-
@Override
public int hashCode() {
return Objects.hash(lineNumberTable, internalHashCode());
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 1e20e4577a..4706fa2f3b 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
@@ -15,9 +15,12 @@
package com.google.devtools.build.lib.syntax;
import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable;
import com.google.devtools.build.lib.events.Location.LineAndColumn;
+import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
+import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.StringUtilities;
import com.google.devtools.build.lib.vfs.PathFragment;
@@ -38,7 +41,9 @@ import java.util.regex.Pattern;
* their buffer using {@link #create}. The client can then ask for the line and column given a
* position using ({@link #getLineAndColumn(int)}).
*/
+@AutoCodec(strategy = AutoCodec.Strategy.POLYMORPHIC)
public abstract class LineNumberTable implements Serializable {
+ public static final ObjectCodec<LineNumberTable> CODEC = new LineNumberTable_AutoCodec();
/**
* Returns the (line, column) pair for the specified offset.
@@ -61,16 +66,15 @@ public abstract class LineNumberTable implements Serializable {
// by gconfig2blaze. We ignore all actual newlines and compute the logical
// LNT based only on the presence of #line markers.
return StringUtilities.containsSubarray(buffer, "\n#line ".toCharArray())
- ? new HashLine(buffer, path)
+ ? HashLine.create(buffer, path)
: new Regular(buffer, path);
}
- /**
- * Line number table implementation for regular source files. Records
- * offsets of newlines.
- */
+ /** Line number table implementation for regular source files. Records offsets of newlines. */
+ @AutoCodec
@Immutable
public static class Regular extends LineNumberTable {
+ public static final ObjectCodec<Regular> CODEC = new LineNumberTable_Regular_AutoCodec();
/**
* A mapping from line number (line >= 1) to character offset into the file.
@@ -80,32 +84,14 @@ public abstract class LineNumberTable implements Serializable {
private final int bufferLength;
public Regular(char[] buffer, PathFragment path) {
- // Compute the size.
- int size = 2;
- for (int i = 0; i < buffer.length; i++) {
- if (buffer[i] == '\n') {
- size++;
- }
- }
- linestart = new int[size];
-
- int index = 0;
- linestart[index++] = 0; // The 0th line does not exist - so we fill something in
- // to make sure the start pos for the 1st line ends up at
- // linestart[1]. Using 0 is useful for tables that are
- // completely empty.
- linestart[index++] = 0; // The first line ("line 1") starts at offset 0.
+ this(computeLinestart(buffer), path, buffer.length);
+ }
- // Scan the buffer and record the offset of each line start. Doing this
- // once upfront is faster than checking each char as it is pulled from
- // the buffer.
- for (int i = 0; i < buffer.length; i++) {
- if (buffer[i] == '\n') {
- linestart[index++] = i + 1;
- }
- }
- this.bufferLength = buffer.length;
+ @AutoCodec.Constructor
+ Regular(int[] linestart, PathFragment path, int bufferLength) {
+ this.linestart = linestart;
this.path = path;
+ this.bufferLength = bufferLength;
}
private int getLineAt(int offset) {
@@ -169,19 +155,51 @@ public abstract class LineNumberTable implements Serializable {
&& Arrays.equals(this.linestart, that.linestart)
&& Objects.equals(this.path, that.path);
}
+
+ private static int[] computeLinestart(char[] buffer) {
+ // Compute the size.
+ int size = 2;
+ for (int i = 0; i < buffer.length; i++) {
+ if (buffer[i] == '\n') {
+ size++;
+ }
+ }
+ int[] linestart = new int[size];
+
+ int index = 0;
+ linestart[index++] = 0; // The 0th line does not exist - so we fill something in
+ // to make sure the start pos for the 1st line ends up at
+ // linestart[1]. Using 0 is useful for tables that are
+ // completely empty.
+ linestart[index++] = 0; // The first line ("line 1") starts at offset 0.
+
+ // Scan the buffer and record the offset of each line start. Doing this
+ // once upfront is faster than checking each char as it is pulled from
+ // the buffer.
+ for (int i = 0; i < buffer.length; i++) {
+ if (buffer[i] == '\n') {
+ linestart[index++] = i + 1;
+ }
+ }
+ return linestart;
+ }
}
/**
- * Line number table implementation for source files that have been
- * preprocessed. Ignores newlines and uses only #line directives.
+ * Line number table implementation for source files that have been preprocessed. Ignores newlines
+ * and uses only #line directives.
*/
+ @AutoCodec
@Immutable
public static class HashLine extends LineNumberTable {
+ public static final ObjectCodec<HashLine> CODEC = new LineNumberTable_HashLine_AutoCodec();
+
+ /** Represents a "#line" directive */
+ @AutoCodec
+ static class SingleHashLine implements Serializable {
+ public static final ObjectCodec<SingleHashLine> CODEC =
+ new LineNumberTable_HashLine_SingleHashLine_AutoCodec();
- /**
- * Represents a "#line" directive
- */
- private static class SingleHashLine implements Serializable {
private final int offset;
private final int line;
private final PathFragment path;
@@ -191,6 +209,23 @@ public abstract class LineNumberTable implements Serializable {
this.line = line;
this.path = path;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(offset, line, path);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof SingleHashLine)) {
+ return false;
+ }
+ SingleHashLine that = (SingleHashLine) obj;
+ return offset == that.offset && line == that.line && Objects.equals(path, that.path);
+ }
}
private static final Ordering<SingleHashLine> hashOrdering =
@@ -204,11 +239,11 @@ public abstract class LineNumberTable implements Serializable {
private static final Pattern pattern = Pattern.compile("\n#line ([0-9]+) \"([^\"\\n]+)\"");
- private final List<SingleHashLine> table;
+ private final ImmutableList<SingleHashLine> table;
private final PathFragment defaultPath;
private final int bufferLength;
- public HashLine(char[] buffer, PathFragment defaultPath) {
+ public static HashLine create(char[] buffer, PathFragment defaultPath) {
CharSequence bufString = CharBuffer.wrap(buffer);
Matcher m = pattern.matcher(bufString);
List<SingleHashLine> unorderedTable = new ArrayList<>();
@@ -221,9 +256,17 @@ public abstract class LineNumberTable implements Serializable {
Integer.parseInt(m.group(1)), // line number
pathFragment)); // filename is an absolute path
}
- this.table = hashOrdering.immutableSortedCopy(unorderedTable);
- this.bufferLength = buffer.length;
- this.defaultPath = Preconditions.checkNotNull(defaultPath);
+ return new HashLine(
+ hashOrdering.immutableSortedCopy(unorderedTable),
+ Preconditions.checkNotNull(defaultPath),
+ buffer.length);
+ }
+
+ @AutoCodec.Constructor
+ HashLine(ImmutableList<SingleHashLine> table, PathFragment defaultPath, int bufferLength) {
+ this.table = table;
+ this.defaultPath = defaultPath;
+ this.bufferLength = bufferLength;
}
private SingleHashLine getHashLine(int offset) {