diff --git a/0001-add-secure-compile-option-in-Makefile.patch b/0001-add-secure-compile-fs-check-in-Makefile.patch similarity index 42% rename from 0001-add-secure-compile-option-in-Makefile.patch rename to 0001-add-secure-compile-fs-check-in-Makefile.patch index 988ba4334d481993d49a6dbce0eb2beabea2317d..17a11a27159709142faacbd738ec8b0b8adf4493 100644 --- a/0001-add-secure-compile-option-in-Makefile.patch +++ b/0001-add-secure-compile-fs-check-in-Makefile.patch @@ -1,12 +1,19 @@ +#From: haozi007 +#Date: Sat, 20 Feb 2021 16:52:15 +0800 +#Subject: [PATCH] add secure compile fs check in Makefile +# +#Signed-off-by: haozi007 +#--- +# diff --git a/src/Makefile.am b/src/Makefile.am -index f1099d9..9b7053b 100644 +index 662710a..2b87f3e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,6 +19,7 @@ PTHREAD_DEF = endif - PROTOBUF_VERSION = 25:0:0 -+PROTOBUF_OPT_FLAG += -Wl,-z,now + PROTOBUF_VERSION = 26:6:0 ++PROTOBUF_OPT_FLAG += -Wl,-z,now -fstack-check if GCC # Turn on all warnings except for sign comparison (we ignore sign comparison diff --git a/0002-add-secure-compile-fs-check-in-Makefile.patch b/0002-add-secure-compile-fs-check-in-Makefile.patch deleted file mode 100644 index 9a5ff529883142f31d3f38c4e7c3c5214b7866eb..0000000000000000000000000000000000000000 --- a/0002-add-secure-compile-fs-check-in-Makefile.patch +++ /dev/null @@ -1,26 +0,0 @@ -From dddceb14106499f9fca17e75cdce458a205b102c Mon Sep 17 00:00:00 2001 -From: haozi007 -Date: Sat, 20 Feb 2021 16:52:15 +0800 -Subject: [PATCH] add secure compile fs check in Makefile - -Signed-off-by: haozi007 ---- - src/Makefile.am | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/Makefile.am b/src/Makefile.am -index 9b7053b..e447b05 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -19,7 +19,7 @@ PTHREAD_DEF = - endif - - PROTOBUF_VERSION = 25:0:0 --PROTOBUF_OPT_FLAG += -Wl,-z,now -+PROTOBUF_OPT_FLAG += -Wl,-z,now -fstack-check - - if GCC - # Turn on all warnings except for sign comparison (we ignore sign comparison --- -2.25.1 - diff --git a/0003-fix-CVE-2021-22570.patch b/0003-fix-CVE-2021-22570.patch deleted file mode 100644 index fa6fb403ccfbb0154bb52f73de3df61526ad0d6c..0000000000000000000000000000000000000000 --- a/0003-fix-CVE-2021-22570.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 5afdc4d13ac997204873e734b20c30b6efc253d1 Mon Sep 17 00:00:00 2001 -From: wangxiaochao -Date: Fri, 18 Mar 2022 14:46:35 +0800 -Subject: [PATCH] fix CVE-2021-22570 - -Signed-off-by: wangxiaochao - ---- - src/google/protobuf/descriptor.cc | 20 ++++++++++++++++++++ - 1 file changed, 20 insertions(+) - -diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc -index 8998e1b..e6f7ec2 100644 ---- a/src/google/protobuf/descriptor.cc -+++ b/src/google/protobuf/descriptor.cc -@@ -2626,6 +2626,8 @@ void Descriptor::DebugString(int depth, std::string* contents, - const Descriptor::ReservedRange* range = reserved_range(i); - if (range->end == range->start + 1) { - strings::SubstituteAndAppend(contents, "$0, ", range->start); -+ } else if (range->end > FieldDescriptor::kMaxNumber) { -+ strings::SubstituteAndAppend(contents, "$0 to max, ", range->start); - } else { - strings::SubstituteAndAppend(contents, "$0 to $1, ", range->start, - range->end - 1); -@@ -2829,6 +2831,8 @@ void EnumDescriptor::DebugString( - const EnumDescriptor::ReservedRange* range = reserved_range(i); - if (range->end == range->start) { - strings::SubstituteAndAppend(contents, "$0, ", range->start); -+ } else if (range->end == INT_MAX) { -+ strings::SubstituteAndAppend(contents, "$0 to max, ", range->start); - } else { - strings::SubstituteAndAppend(contents, "$0 to $1, ", range->start, - range->end); -@@ -4019,6 +4023,11 @@ bool DescriptorBuilder::AddSymbol(const std::string& full_name, - // Use its file as the parent instead. - if (parent == nullptr) parent = file_; - -+ if (full_name.find('\0') != std::string::npos) { -+ AddError(full_name, proto, DescriptorPool::ErrorCollector::NAME, -+ "\"" + full_name + "\" contains null character."); -+ return false; -+ } - if (tables_->AddSymbol(full_name, symbol)) { - if (!file_tables_->AddAliasUnderParent(parent, name, symbol)) { - // This is only possible if there was already an error adding something of -@@ -4059,6 +4068,11 @@ bool DescriptorBuilder::AddSymbol(const std::string& full_name, - void DescriptorBuilder::AddPackage(const std::string& name, - const Message& proto, - const FileDescriptor* file) { -+ if (name.find('\0') != std::string::npos) { -+ AddError(name, proto, DescriptorPool::ErrorCollector::NAME, -+ "\"" + name + "\" contains null character."); -+ return; -+ } - if (tables_->AddSymbol(name, Symbol(file))) { - // Success. Also add parent package, if any. - std::string::size_type dot_pos = name.find_last_of('.'); -@@ -4372,6 +4386,12 @@ FileDescriptor* DescriptorBuilder::BuildFileImpl( - } - result->pool_ = pool_; - -+ if (result->name().find('\0') != std::string::npos) { -+ AddError(result->name(), proto, DescriptorPool::ErrorCollector::NAME, -+ "\"" + result->name() + "\" contains null character."); -+ return nullptr; -+ } -+ - // Add to tables. - if (!tables_->AddFile(result)) { - AddError(proto.name(), proto, DescriptorPool::ErrorCollector::OTHER, --- -2.25.1 - diff --git a/0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch b/0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch deleted file mode 100644 index a56f88d9a402fa1d9ee2bd5e7c370aaa9d05b2ad..0000000000000000000000000000000000000000 --- a/0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch +++ /dev/null @@ -1,1272 +0,0 @@ -From 8890b0a81e2f4b1de4a33cc6d81aba07655bde1a Mon Sep 17 00:00:00 2001 -From: wangxiaochao -Date: Thu, 24 Mar 2022 20:54:36 +0800 -Subject: [PATCH] Improve performance of parsing unknown fields in Java - -Signed-off-by: wangxiaochao - ---- - Makefile.am | 1 + - .../com/google/protobuf/UnknownFieldSet.java | 427 +++++++++--------- - .../UnknownFieldSetPerformanceTest.java | 78 ++++ - .../google/protobuf/UnknownFieldSetTest.java | 171 ++++++- - java/lite/pom.xml | 3 +- - 5 files changed, 466 insertions(+), 214 deletions(-) - create mode 100644 java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java - -diff --git a/Makefile.am b/Makefile.am -index 4fc706b..908c2d2 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -487,6 +487,7 @@ java_EXTRA_DIST= - java/core/src/test/java/com/google/protobuf/TypeRegistryTest.java \ - java/core/src/test/java/com/google/protobuf/UnknownEnumValueTest.java \ - java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java \ -+ java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java \ - java/core/src/test/java/com/google/protobuf/UnmodifiableLazyStringListTest.java \ - java/core/src/test/java/com/google/protobuf/Utf8Test.java \ - java/core/src/test/java/com/google/protobuf/Utf8Utils.java \ -diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java -index ba2f9df..5c482d6 100644 ---- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java -+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSet.java -@@ -43,13 +43,13 @@ import java.util.Map; - import java.util.TreeMap; - - /** -- * {@code UnknownFieldSet} is used to keep track of fields which were seen when parsing a protocol -+ * {@code UnknownFieldSet} keeps track of fields which were seen when parsing a protocol - * message but whose field numbers or types are unrecognized. This most frequently occurs when new - * fields are added to a message type and then messages containing those fields are read by old - * software that was compiled before the new types were added. - * - *

Every {@link Message} contains an {@code UnknownFieldSet} (and every {@link Message.Builder} -- * contains an {@link Builder}). -+ * contains a {@link Builder}). - * - *

Most users will never need to use this class. - * -@@ -57,9 +57,13 @@ import java.util.TreeMap; - */ - public final class UnknownFieldSet implements MessageLite { - -- private UnknownFieldSet() { -- fields = null; -- fieldsDescending = null; -+ private final TreeMap fields; -+ -+ /** -+ * Construct an {@code UnknownFieldSet} around the given map. -+ */ -+ UnknownFieldSet(TreeMap fields) { -+ this.fields = fields; - } - - /** Create a new {@link Builder}. */ -@@ -68,7 +72,7 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** Create a new {@link Builder} and initialize it to be a copy of {@code copyFrom}. */ -- public static Builder newBuilder(final UnknownFieldSet copyFrom) { -+ public static Builder newBuilder(UnknownFieldSet copyFrom) { - return newBuilder().mergeFrom(copyFrom); - } - -@@ -83,25 +87,11 @@ public final class UnknownFieldSet implements MessageLite { - } - - private static final UnknownFieldSet defaultInstance = -- new UnknownFieldSet( -- Collections.emptyMap(), Collections.emptyMap()); -- -- /** -- * Construct an {@code UnknownFieldSet} around the given map. The map is expected to be immutable. -- */ -- UnknownFieldSet(final Map fields, final Map fieldsDescending) { -- this.fields = fields; -- this.fieldsDescending = fieldsDescending; -- } -- -- private final Map fields; -- -- /** A copy of {@link #fields} who's iterator order is reversed. */ -- private final Map fieldsDescending; -+ new UnknownFieldSet(new TreeMap()); - - - @Override -- public boolean equals(final Object other) { -+ public boolean equals(Object other) { - if (this == other) { - return true; - } -@@ -110,29 +100,33 @@ public final class UnknownFieldSet implements MessageLite { - - @Override - public int hashCode() { -+ if (fields.isEmpty()) { // avoid allocation of iterator. -+ // This optimization may not be helpful but it is needed for the allocation tests to pass. -+ return 0; -+ } - return fields.hashCode(); - } - - /** Get a map of fields in the set by number. */ - public Map asMap() { -- return fields; -+ return (Map) fields.clone(); - } - - /** Check if the given field number is present in the set. */ -- public boolean hasField(final int number) { -+ public boolean hasField(int number) { - return fields.containsKey(number); - } - - /** Get a field by number. Returns an empty field if not present. Never returns {@code null}. */ -- public Field getField(final int number) { -- final Field result = fields.get(number); -+ public Field getField(int number) { -+ Field result = fields.get(number); - return (result == null) ? Field.getDefaultInstance() : result; - } - - /** Serializes the set and writes it to {@code output}. */ - @Override -- public void writeTo(final CodedOutputStream output) throws IOException { -- for (final Map.Entry entry : fields.entrySet()) { -+ public void writeTo(CodedOutputStream output) throws IOException { -+ for (Map.Entry entry : fields.entrySet()) { - Field field = entry.getValue(); - field.writeTo(entry.getKey(), output); - } -@@ -154,10 +148,10 @@ public final class UnknownFieldSet implements MessageLite { - @Override - public ByteString toByteString() { - try { -- final ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize()); -+ ByteString.CodedBuilder out = ByteString.newCodedBuilder(getSerializedSize()); - writeTo(out.getCodedOutput()); - return out.build(); -- } catch (final IOException e) { -+ } catch (IOException e) { - throw new RuntimeException( - "Serializing to a ByteString threw an IOException (should never happen).", e); - } -@@ -170,12 +164,12 @@ public final class UnknownFieldSet implements MessageLite { - @Override - public byte[] toByteArray() { - try { -- final byte[] result = new byte[getSerializedSize()]; -- final CodedOutputStream output = CodedOutputStream.newInstance(result); -+ byte[] result = new byte[getSerializedSize()]; -+ CodedOutputStream output = CodedOutputStream.newInstance(result); - writeTo(output); - output.checkNoSpaceLeft(); - return result; -- } catch (final IOException e) { -+ } catch (IOException e) { - throw new RuntimeException( - "Serializing to a byte array threw an IOException (should never happen).", e); - } -@@ -186,16 +180,16 @@ public final class UnknownFieldSet implements MessageLite { - * {@link #writeTo(CodedOutputStream)}. - */ - @Override -- public void writeTo(final OutputStream output) throws IOException { -- final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); -+ public void writeTo(OutputStream output) throws IOException { -+ CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); - writeTo(codedOutput); - codedOutput.flush(); - } - - @Override - public void writeDelimitedTo(OutputStream output) throws IOException { -- final CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); -- codedOutput.writeRawVarint32(getSerializedSize()); -+ CodedOutputStream codedOutput = CodedOutputStream.newInstance(output); -+ codedOutput.writeUInt32NoTag(getSerializedSize()); - writeTo(codedOutput); - codedOutput.flush(); - } -@@ -204,15 +198,17 @@ public final class UnknownFieldSet implements MessageLite { - @Override - public int getSerializedSize() { - int result = 0; -- for (final Map.Entry entry : fields.entrySet()) { -- result += entry.getValue().getSerializedSize(entry.getKey()); -+ if (!fields.isEmpty()) { -+ for (Map.Entry entry : fields.entrySet()) { -+ result += entry.getValue().getSerializedSize(entry.getKey()); -+ } - } - return result; - } - - /** Serializes the set and writes it to {@code output} using {@code MessageSet} wire format. */ -- public void writeAsMessageSetTo(final CodedOutputStream output) throws IOException { -- for (final Map.Entry entry : fields.entrySet()) { -+ public void writeAsMessageSetTo(CodedOutputStream output) throws IOException { -+ for (Map.Entry entry : fields.entrySet()) { - entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), output); - } - } -@@ -221,7 +217,7 @@ public final class UnknownFieldSet implements MessageLite { - void writeTo(Writer writer) throws IOException { - if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { - // Write fields in descending order. -- for (Map.Entry entry : fieldsDescending.entrySet()) { -+ for (Map.Entry entry : fields.descendingMap().entrySet()) { - entry.getValue().writeTo(entry.getKey(), writer); - } - } else { -@@ -233,15 +229,15 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** Serializes the set and writes it to {@code writer} using {@code MessageSet} wire format. */ -- void writeAsMessageSetTo(final Writer writer) throws IOException { -+ void writeAsMessageSetTo(Writer writer) throws IOException { - if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { - // Write fields in descending order. -- for (final Map.Entry entry : fieldsDescending.entrySet()) { -+ for (Map.Entry entry : fields.descendingMap().entrySet()) { - entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), writer); - } - } else { - // Write fields in ascending order. -- for (final Map.Entry entry : fields.entrySet()) { -+ for (Map.Entry entry : fields.entrySet()) { - entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), writer); - } - } -@@ -250,7 +246,7 @@ public final class UnknownFieldSet implements MessageLite { - /** Get the number of bytes required to encode this set using {@code MessageSet} wire format. */ - public int getSerializedSizeAsMessageSet() { - int result = 0; -- for (final Map.Entry entry : fields.entrySet()) { -+ for (Map.Entry entry : fields.entrySet()) { - result += entry.getValue().getSerializedSizeAsMessageSetExtension(entry.getKey()); - } - return result; -@@ -264,23 +260,23 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** Parse an {@code UnknownFieldSet} from the given input stream. */ -- public static UnknownFieldSet parseFrom(final CodedInputStream input) throws IOException { -+ public static UnknownFieldSet parseFrom(CodedInputStream input) throws IOException { - return newBuilder().mergeFrom(input).build(); - } - - /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ -- public static UnknownFieldSet parseFrom(final ByteString data) -+ public static UnknownFieldSet parseFrom(ByteString data) - throws InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).build(); - } - - /** Parse {@code data} as an {@code UnknownFieldSet} and return it. */ -- public static UnknownFieldSet parseFrom(final byte[] data) throws InvalidProtocolBufferException { -+ public static UnknownFieldSet parseFrom(byte[] data) throws InvalidProtocolBufferException { - return newBuilder().mergeFrom(data).build(); - } - - /** Parse an {@code UnknownFieldSet} from {@code input} and return it. */ -- public static UnknownFieldSet parseFrom(final InputStream input) throws IOException { -+ public static UnknownFieldSet parseFrom(InputStream input) throws IOException { - return newBuilder().mergeFrom(input).build(); - } - -@@ -309,64 +305,43 @@ public final class UnknownFieldSet implements MessageLite { - // This constructor should never be called directly (except from 'create'). - private Builder() {} - -- private Map fields; -- -- // Optimization: We keep around a builder for the last field that was -- // modified so that we can efficiently add to it multiple times in a -- // row (important when parsing an unknown repeated field). -- private int lastFieldNumber; -- private Field.Builder lastField; -+ private TreeMap fieldBuilders = new TreeMap<>(); - - private static Builder create() { -- Builder builder = new Builder(); -- builder.reinitialize(); -- return builder; -+ return new Builder(); - } - - /** - * Get a field builder for the given field number which includes any values that already exist. - */ -- private Field.Builder getFieldBuilder(final int number) { -- if (lastField != null) { -- if (number == lastFieldNumber) { -- return lastField; -- } -- // Note: addField() will reset lastField and lastFieldNumber. -- addField(lastFieldNumber, lastField.build()); -- } -+ private Field.Builder getFieldBuilder(int number) { - if (number == 0) { - return null; - } else { -- final Field existing = fields.get(number); -- lastFieldNumber = number; -- lastField = Field.newBuilder(); -- if (existing != null) { -- lastField.mergeFrom(existing); -+ Field.Builder builder = fieldBuilders.get(number); -+ if (builder == null) { -+ builder = Field.newBuilder(); -+ fieldBuilders.put(number, builder); - } -- return lastField; -+ return builder; - } - } - - /** - * Build the {@link UnknownFieldSet} and return it. -- * -- *

Once {@code build()} has been called, the {@code Builder} will no longer be usable. -- * Calling any method after {@code build()} will result in undefined behavior and can cause a -- * {@code NullPointerException} to be thrown. - */ - @Override - public UnknownFieldSet build() { -- getFieldBuilder(0); // Force lastField to be built. -- final UnknownFieldSet result; -- if (fields.isEmpty()) { -+ UnknownFieldSet result; -+ if (fieldBuilders.isEmpty()) { - result = getDefaultInstance(); - } else { -- Map descendingFields = null; -- descendingFields = -- Collections.unmodifiableMap(((TreeMap) fields).descendingMap()); -- result = new UnknownFieldSet(Collections.unmodifiableMap(fields), descendingFields); -+ TreeMap fields = new TreeMap<>(); -+ for (Map.Entry entry : fieldBuilders.entrySet()) { -+ fields.put(entry.getKey(), entry.getValue().build()); -+ } -+ result = new UnknownFieldSet(fields); - } -- fields = null; - return result; - } - -@@ -378,11 +353,13 @@ public final class UnknownFieldSet implements MessageLite { - - @Override - public Builder clone() { -- getFieldBuilder(0); // Force lastField to be built. -- Map descendingFields = null; -- descendingFields = -- Collections.unmodifiableMap(((TreeMap) fields).descendingMap()); -- return UnknownFieldSet.newBuilder().mergeFrom(new UnknownFieldSet(fields, descendingFields)); -+ Builder clone = UnknownFieldSet.newBuilder(); -+ for (Map.Entry entry : fieldBuilders.entrySet()) { -+ Integer key = entry.getKey(); -+ Field.Builder value = entry.getValue(); -+ clone.fieldBuilders.put(key, value.clone()); -+ } -+ return clone; - } - - @Override -@@ -390,31 +367,24 @@ public final class UnknownFieldSet implements MessageLite { - return UnknownFieldSet.getDefaultInstance(); - } - -- private void reinitialize() { -- fields = Collections.emptyMap(); -- lastFieldNumber = 0; -- lastField = null; -- } -- - /** Reset the builder to an empty set. */ - @Override - public Builder clear() { -- reinitialize(); -+ fieldBuilders = new TreeMap<>(); - return this; - } - -- /** Clear fields from the set with a given field number. */ -- public Builder clearField(final int number) { -- if (number == 0) { -- throw new IllegalArgumentException("Zero is not a valid field number."); -- } -- if (lastField != null && lastFieldNumber == number) { -- // Discard this. -- lastField = null; -- lastFieldNumber = 0; -+ /** -+ * Clear fields from the set with a given field number. -+ * -+ * @throws IllegalArgumentException if number is not positive -+ */ -+ public Builder clearField(int number) { -+ if (number <= 0) { -+ throw new IllegalArgumentException(number + " is not a valid field number."); - } -- if (fields.containsKey(number)) { -- fields.remove(number); -+ if (fieldBuilders.containsKey(number)) { -+ fieldBuilders.remove(number); - } - return this; - } -@@ -423,9 +393,9 @@ public final class UnknownFieldSet implements MessageLite { - * Merge the fields from {@code other} into this set. If a field number exists in both sets, - * {@code other}'s values for that field will be appended to the values in this set. - */ -- public Builder mergeFrom(final UnknownFieldSet other) { -+ public Builder mergeFrom(UnknownFieldSet other) { - if (other != getDefaultInstance()) { -- for (final Map.Entry entry : other.fields.entrySet()) { -+ for (Map.Entry entry : other.fields.entrySet()) { - mergeField(entry.getKey(), entry.getValue()); - } - } -@@ -435,10 +405,12 @@ public final class UnknownFieldSet implements MessageLite { - /** - * Add a field to the {@code UnknownFieldSet}. If a field with the same number already exists, - * the two are merged. -+ * -+ * @throws IllegalArgumentException if number is not positive - */ -- public Builder mergeField(final int number, final Field field) { -- if (number == 0) { -- throw new IllegalArgumentException("Zero is not a valid field number."); -+ public Builder mergeField(int number, final Field field) { -+ if (number <= 0) { -+ throw new IllegalArgumentException(number + " is not a valid field number."); - } - if (hasField(number)) { - getFieldBuilder(number).mergeFrom(field); -@@ -454,10 +426,12 @@ public final class UnknownFieldSet implements MessageLite { - /** - * Convenience method for merging a new field containing a single varint value. This is used in - * particular when an unknown enum value is encountered. -+ * -+ * @throws IllegalArgumentException if number is not positive - */ -- public Builder mergeVarintField(final int number, final int value) { -- if (number == 0) { -- throw new IllegalArgumentException("Zero is not a valid field number."); -+ public Builder mergeVarintField(int number, int value) { -+ if (number <= 0) { -+ throw new IllegalArgumentException(number + " is not a valid field number."); - } - getFieldBuilder(number).addVarint(value); - return this; -@@ -467,40 +441,33 @@ public final class UnknownFieldSet implements MessageLite { - * Convenience method for merging a length-delimited field. - * - *

For use by generated code only. -+ * -+ * @throws IllegalArgumentException if number is not positive - */ -- public Builder mergeLengthDelimitedField(final int number, final ByteString value) { -- if (number == 0) { -- throw new IllegalArgumentException("Zero is not a valid field number."); -+ public Builder mergeLengthDelimitedField(int number, ByteString value) { -+ if (number <= 0) { -+ throw new IllegalArgumentException(number + " is not a valid field number."); - } - getFieldBuilder(number).addLengthDelimited(value); - return this; - } - - /** Check if the given field number is present in the set. */ -- public boolean hasField(final int number) { -- if (number == 0) { -- throw new IllegalArgumentException("Zero is not a valid field number."); -- } -- return number == lastFieldNumber || fields.containsKey(number); -+ public boolean hasField(int number) { -+ return fieldBuilders.containsKey(number); - } - - /** - * Add a field to the {@code UnknownFieldSet}. If a field with the same number already exists, - * it is removed. -+ * -+ * @throws IllegalArgumentException if number is not positive - */ -- public Builder addField(final int number, final Field field) { -- if (number == 0) { -- throw new IllegalArgumentException("Zero is not a valid field number."); -- } -- if (lastField != null && lastFieldNumber == number) { -- // Discard this. -- lastField = null; -- lastFieldNumber = 0; -+ public Builder addField(int number, Field field) { -+ if (number <= 0) { -+ throw new IllegalArgumentException(number + " is not a valid field number."); - } -- if (fields.isEmpty()) { -- fields = new TreeMap(); -- } -- fields.put(number, field); -+ fieldBuilders.put(number, Field.newBuilder(field)); - return this; - } - -@@ -509,15 +476,18 @@ public final class UnknownFieldSet implements MessageLite { - * changes may or may not be reflected in this map. - */ - public Map asMap() { -- getFieldBuilder(0); // Force lastField to be built. -+ TreeMap fields = new TreeMap<>(); -+ for (Map.Entry entry : fieldBuilders.entrySet()) { -+ fields.put(entry.getKey(), entry.getValue().build()); -+ } - return Collections.unmodifiableMap(fields); - } - - /** Parse an entire message from {@code input} and merge its fields into this set. */ - @Override -- public Builder mergeFrom(final CodedInputStream input) throws IOException { -+ public Builder mergeFrom(CodedInputStream input) throws IOException { - while (true) { -- final int tag = input.readTag(); -+ int tag = input.readTag(); - if (tag == 0 || !mergeFieldFrom(tag, input)) { - break; - } -@@ -531,8 +501,8 @@ public final class UnknownFieldSet implements MessageLite { - * @param tag The field's tag number, which was already parsed. - * @return {@code false} if the tag is an end group tag. - */ -- public boolean mergeFieldFrom(final int tag, final CodedInputStream input) throws IOException { -- final int number = WireFormat.getTagFieldNumber(tag); -+ public boolean mergeFieldFrom(int tag, CodedInputStream input) throws IOException { -+ int number = WireFormat.getTagFieldNumber(tag); - switch (WireFormat.getTagWireType(tag)) { - case WireFormat.WIRETYPE_VARINT: - getFieldBuilder(number).addVarint(input.readInt64()); -@@ -544,7 +514,7 @@ public final class UnknownFieldSet implements MessageLite { - getFieldBuilder(number).addLengthDelimited(input.readBytes()); - return true; - case WireFormat.WIRETYPE_START_GROUP: -- final Builder subBuilder = newBuilder(); -+ Builder subBuilder = newBuilder(); - input.readGroup(number, subBuilder, ExtensionRegistry.getEmptyRegistry()); - getFieldBuilder(number).addGroup(subBuilder.build()); - return true; -@@ -563,15 +533,15 @@ public final class UnknownFieldSet implements MessageLite { - * is just a small wrapper around {@link #mergeFrom(CodedInputStream)}. - */ - @Override -- public Builder mergeFrom(final ByteString data) throws InvalidProtocolBufferException { -+ public Builder mergeFrom(ByteString data) throws InvalidProtocolBufferException { - try { -- final CodedInputStream input = data.newCodedInput(); -+ CodedInputStream input = data.newCodedInput(); - mergeFrom(input); - input.checkLastTagWas(0); - return this; -- } catch (final InvalidProtocolBufferException e) { -+ } catch (InvalidProtocolBufferException e) { - throw e; -- } catch (final IOException e) { -+ } catch (IOException e) { - throw new RuntimeException( - "Reading from a ByteString threw an IOException (should never happen).", e); - } -@@ -582,15 +552,15 @@ public final class UnknownFieldSet implements MessageLite { - * is just a small wrapper around {@link #mergeFrom(CodedInputStream)}. - */ - @Override -- public Builder mergeFrom(final byte[] data) throws InvalidProtocolBufferException { -+ public Builder mergeFrom(byte[] data) throws InvalidProtocolBufferException { - try { -- final CodedInputStream input = CodedInputStream.newInstance(data); -+ CodedInputStream input = CodedInputStream.newInstance(data); - mergeFrom(input); - input.checkLastTagWas(0); - return this; -- } catch (final InvalidProtocolBufferException e) { -+ } catch (InvalidProtocolBufferException e) { - throw e; -- } catch (final IOException e) { -+ } catch (IOException e) { - throw new RuntimeException( - "Reading from a byte array threw an IOException (should never happen).", e); - } -@@ -601,8 +571,8 @@ public final class UnknownFieldSet implements MessageLite { - * This is just a small wrapper around {@link #mergeFrom(CodedInputStream)}. - */ - @Override -- public Builder mergeFrom(final InputStream input) throws IOException { -- final CodedInputStream codedInput = CodedInputStream.newInstance(input); -+ public Builder mergeFrom(InputStream input) throws IOException { -+ CodedInputStream codedInput = CodedInputStream.newInstance(input); - mergeFrom(codedInput); - codedInput.checkLastTagWas(0); - return this; -@@ -610,12 +580,12 @@ public final class UnknownFieldSet implements MessageLite { - - @Override - public boolean mergeDelimitedFrom(InputStream input) throws IOException { -- final int firstByte = input.read(); -+ int firstByte = input.read(); - if (firstByte == -1) { - return false; - } -- final int size = CodedInputStream.readRawVarint32(firstByte, input); -- final InputStream limitedInput = new LimitedInputStream(input, size); -+ int size = CodedInputStream.readRawVarint32(firstByte, input); -+ InputStream limitedInput = new LimitedInputStream(input, size); - mergeFrom(limitedInput); - return true; - } -@@ -644,7 +614,7 @@ public final class UnknownFieldSet implements MessageLite { - @Override - public Builder mergeFrom(byte[] data, int off, int len) throws InvalidProtocolBufferException { - try { -- final CodedInputStream input = CodedInputStream.newInstance(data, off, len); -+ CodedInputStream input = CodedInputStream.newInstance(data, off, len); - mergeFrom(input); - input.checkLastTagWas(0); - return this; -@@ -718,7 +688,7 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** Construct a new {@link Builder} and initialize it to a copy of {@code copyFrom}. */ -- public static Builder newBuilder(final Field copyFrom) { -+ public static Builder newBuilder(Field copyFrom) { - return newBuilder().mergeFrom(copyFrom); - } - -@@ -758,7 +728,7 @@ public final class UnknownFieldSet implements MessageLite { - } - - @Override -- public boolean equals(final Object other) { -+ public boolean equals(Object other) { - if (this == other) { - return true; - } -@@ -785,7 +755,7 @@ public final class UnknownFieldSet implements MessageLite { - public ByteString toByteString(int fieldNumber) { - try { - // TODO(lukes): consider caching serialized size in a volatile long -- final ByteString.CodedBuilder out = -+ ByteString.CodedBuilder out = - ByteString.newCodedBuilder(getSerializedSize(fieldNumber)); - writeTo(fieldNumber, out.getCodedOutput()); - return out.build(); -@@ -796,40 +766,40 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** Serializes the field, including field number, and writes it to {@code output}. */ -- public void writeTo(final int fieldNumber, final CodedOutputStream output) throws IOException { -- for (final long value : varint) { -+ public void writeTo(int fieldNumber, CodedOutputStream output) throws IOException { -+ for (long value : varint) { - output.writeUInt64(fieldNumber, value); - } -- for (final int value : fixed32) { -+ for (int value : fixed32) { - output.writeFixed32(fieldNumber, value); - } -- for (final long value : fixed64) { -+ for (long value : fixed64) { - output.writeFixed64(fieldNumber, value); - } -- for (final ByteString value : lengthDelimited) { -+ for (ByteString value : lengthDelimited) { - output.writeBytes(fieldNumber, value); - } -- for (final UnknownFieldSet value : group) { -+ for (UnknownFieldSet value : group) { - output.writeGroup(fieldNumber, value); - } - } - - /** Get the number of bytes required to encode this field, including field number. */ -- public int getSerializedSize(final int fieldNumber) { -+ public int getSerializedSize(int fieldNumber) { - int result = 0; -- for (final long value : varint) { -+ for (long value : varint) { - result += CodedOutputStream.computeUInt64Size(fieldNumber, value); - } -- for (final int value : fixed32) { -+ for (int value : fixed32) { - result += CodedOutputStream.computeFixed32Size(fieldNumber, value); - } -- for (final long value : fixed64) { -+ for (long value : fixed64) { - result += CodedOutputStream.computeFixed64Size(fieldNumber, value); - } -- for (final ByteString value : lengthDelimited) { -+ for (ByteString value : lengthDelimited) { - result += CodedOutputStream.computeBytesSize(fieldNumber, value); - } -- for (final UnknownFieldSet value : group) { -+ for (UnknownFieldSet value : group) { - result += CodedOutputStream.computeGroupSize(fieldNumber, value); - } - return result; -@@ -839,15 +809,15 @@ public final class UnknownFieldSet implements MessageLite { - * Serializes the field, including field number, and writes it to {@code output}, using {@code - * MessageSet} wire format. - */ -- public void writeAsMessageSetExtensionTo(final int fieldNumber, final CodedOutputStream output) -+ public void writeAsMessageSetExtensionTo(int fieldNumber, CodedOutputStream output) - throws IOException { -- for (final ByteString value : lengthDelimited) { -+ for (ByteString value : lengthDelimited) { - output.writeRawMessageSetExtension(fieldNumber, value); - } - } - - /** Serializes the field, including field number, and writes it to {@code writer}. */ -- void writeTo(final int fieldNumber, final Writer writer) throws IOException { -+ void writeTo(int fieldNumber, Writer writer) throws IOException { - writer.writeInt64List(fieldNumber, varint, false); - writer.writeFixed32List(fieldNumber, fixed32, false); - writer.writeFixed64List(fieldNumber, fixed64, false); -@@ -872,7 +842,7 @@ public final class UnknownFieldSet implements MessageLite { - * Serializes the field, including field number, and writes it to {@code writer}, using {@code - * MessageSet} wire format. - */ -- private void writeAsMessageSetExtensionTo(final int fieldNumber, final Writer writer) -+ private void writeAsMessageSetExtensionTo(int fieldNumber, Writer writer) - throws IOException { - if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) { - // Write in descending field order. -@@ -882,7 +852,7 @@ public final class UnknownFieldSet implements MessageLite { - } - } else { - // Write in ascending field order. -- for (final ByteString value : lengthDelimited) { -+ for (ByteString value : lengthDelimited) { - writer.writeMessageSetItem(fieldNumber, value); - } - } -@@ -892,9 +862,9 @@ public final class UnknownFieldSet implements MessageLite { - * Get the number of bytes required to encode this field, including field number, using {@code - * MessageSet} wire format. - */ -- public int getSerializedSizeAsMessageSetExtension(final int fieldNumber) { -+ public int getSerializedSizeAsMessageSetExtension(int fieldNumber) { - int result = 0; -- for (final ByteString value : lengthDelimited) { -+ for (ByteString value : lengthDelimited) { - result += CodedOutputStream.computeRawMessageSetExtensionSize(fieldNumber, value); - } - return result; -@@ -912,52 +882,85 @@ public final class UnknownFieldSet implements MessageLite { - *

Use {@link Field#newBuilder()} to construct a {@code Builder}. - */ - public static final class Builder { -- // This constructor should never be called directly (except from 'create'). -- private Builder() {} -+ // This constructor should only be called directly from 'create' and 'clone'. -+ private Builder() { -+ result = new Field(); -+ } - - private static Builder create() { - Builder builder = new Builder(); -- builder.result = new Field(); - return builder; - } - - private Field result; - -+ @Override -+ public Builder clone() { -+ Field copy = new Field(); -+ if (result.varint == null) { -+ copy.varint = null; -+ } else { -+ copy.varint = new ArrayList<>(result.varint); -+ } -+ if (result.fixed32 == null) { -+ copy.fixed32 = null; -+ } else { -+ copy.fixed32 = new ArrayList<>(result.fixed32); -+ } -+ if (result.fixed64 == null) { -+ copy.fixed64 = null; -+ } else { -+ copy.fixed64 = new ArrayList<>(result.fixed64); -+ } -+ if (result.lengthDelimited == null) { -+ copy.lengthDelimited = null; -+ } else { -+ copy.lengthDelimited = new ArrayList<>(result.lengthDelimited); -+ } -+ if (result.group == null) { -+ copy.group = null; -+ } else { -+ copy.group = new ArrayList<>(result.group); -+ } -+ -+ Builder clone = new Builder(); -+ clone.result = copy; -+ return clone; -+ } -+ - /** -- * Build the field. After {@code build()} has been called, the {@code Builder} is no longer -- * usable. Calling any other method will result in undefined behavior and can cause a {@code -- * NullPointerException} to be thrown. -+ * Build the field. - */ - public Field build() { -+ Field built = new Field(); - if (result.varint == null) { -- result.varint = Collections.emptyList(); -+ built.varint = Collections.emptyList(); - } else { -- result.varint = Collections.unmodifiableList(result.varint); -+ built.varint = Collections.unmodifiableList(new ArrayList<>(result.varint)); - } - if (result.fixed32 == null) { -- result.fixed32 = Collections.emptyList(); -+ built.fixed32 = Collections.emptyList(); - } else { -- result.fixed32 = Collections.unmodifiableList(result.fixed32); -+ built.fixed32 = Collections.unmodifiableList(new ArrayList<>(result.fixed32)); - } - if (result.fixed64 == null) { -- result.fixed64 = Collections.emptyList(); -+ built.fixed64 = Collections.emptyList(); - } else { -- result.fixed64 = Collections.unmodifiableList(result.fixed64); -+ built.fixed64 = Collections.unmodifiableList(new ArrayList<>(result.fixed64)); - } - if (result.lengthDelimited == null) { -- result.lengthDelimited = Collections.emptyList(); -+ built.lengthDelimited = Collections.emptyList(); - } else { -- result.lengthDelimited = Collections.unmodifiableList(result.lengthDelimited); -+ built.lengthDelimited = Collections.unmodifiableList( -+ new ArrayList<>(result.lengthDelimited)); - } - if (result.group == null) { -- result.group = Collections.emptyList(); -+ built.group = Collections.emptyList(); - } else { -- result.group = Collections.unmodifiableList(result.group); -+ built.group = Collections.unmodifiableList(new ArrayList<>(result.group)); - } - -- final Field returnMe = result; -- result = null; -- return returnMe; -+ return built; - } - - /** Discard the field's contents. */ -@@ -970,7 +973,7 @@ public final class UnknownFieldSet implements MessageLite { - * Merge the values in {@code other} into this field. For each list of values, {@code other}'s - * values are append to the ones in this field. - */ -- public Builder mergeFrom(final Field other) { -+ public Builder mergeFrom(Field other) { - if (!other.varint.isEmpty()) { - if (result.varint == null) { - result.varint = new ArrayList(); -@@ -985,19 +988,19 @@ public final class UnknownFieldSet implements MessageLite { - } - if (!other.fixed64.isEmpty()) { - if (result.fixed64 == null) { -- result.fixed64 = new ArrayList(); -+ result.fixed64 = new ArrayList<>(); - } - result.fixed64.addAll(other.fixed64); - } - if (!other.lengthDelimited.isEmpty()) { - if (result.lengthDelimited == null) { -- result.lengthDelimited = new ArrayList(); -+ result.lengthDelimited = new ArrayList<>(); - } - result.lengthDelimited.addAll(other.lengthDelimited); - } - if (!other.group.isEmpty()) { - if (result.group == null) { -- result.group = new ArrayList(); -+ result.group = new ArrayList<>(); - } - result.group.addAll(other.group); - } -@@ -1005,45 +1008,45 @@ public final class UnknownFieldSet implements MessageLite { - } - - /** Add a varint value. */ -- public Builder addVarint(final long value) { -+ public Builder addVarint(long value) { - if (result.varint == null) { -- result.varint = new ArrayList(); -+ result.varint = new ArrayList<>(); - } - result.varint.add(value); - return this; - } - - /** Add a fixed32 value. */ -- public Builder addFixed32(final int value) { -+ public Builder addFixed32(int value) { - if (result.fixed32 == null) { -- result.fixed32 = new ArrayList(); -+ result.fixed32 = new ArrayList<>(); - } - result.fixed32.add(value); - return this; - } - - /** Add a fixed64 value. */ -- public Builder addFixed64(final long value) { -+ public Builder addFixed64(long value) { - if (result.fixed64 == null) { -- result.fixed64 = new ArrayList(); -+ result.fixed64 = new ArrayList<>(); - } - result.fixed64.add(value); - return this; - } - - /** Add a length-delimited value. */ -- public Builder addLengthDelimited(final ByteString value) { -+ public Builder addLengthDelimited(ByteString value) { - if (result.lengthDelimited == null) { -- result.lengthDelimited = new ArrayList(); -+ result.lengthDelimited = new ArrayList<>(); - } - result.lengthDelimited.add(value); - return this; - } - - /** Add an embedded group. */ -- public Builder addGroup(final UnknownFieldSet value) { -+ public Builder addGroup(UnknownFieldSet value) { - if (result.group == null) { -- result.group = new ArrayList(); -+ result.group = new ArrayList<>(); - } - result.group.add(value); - return this; -diff --git a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java -new file mode 100644 -index 0000000..6ce0fc7 ---- /dev/null -+++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java -@@ -0,0 +1,78 @@ -+// Protocol Buffers - Google's data interchange format -+// Copyright 2008 Google Inc. All rights reserved. -+// https://developers.google.com/protocol-buffers/ -+// -+// Redistribution and use in source and binary forms, with or without -+// modification, are permitted provided that the following conditions are -+// met: -+// -+// * Redistributions of source code must retain the above copyright -+// notice, this list of conditions and the following disclaimer. -+// * Redistributions in binary form must reproduce the above -+// copyright notice, this list of conditions and the following disclaimer -+// in the documentation and/or other materials provided with the -+// distribution. -+// * Neither the name of Google Inc. nor the names of its -+// contributors may be used to endorse or promote products derived from -+// this software without specific prior written permission. -+// -+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ -+package com.google.protobuf; -+ -+import static com.google.common.truth.Truth.assertThat; -+ -+import java.io.ByteArrayInputStream; -+import java.io.IOException; -+import java.io.InputStream; -+import org.junit.Test; -+import org.junit.runner.RunWith; -+import org.junit.runners.JUnit4; -+ -+@RunWith(JUnit4.class) -+public final class UnknownFieldSetPerformanceTest { -+ -+ private static byte[] generateBytes(int length) { -+ assertThat(length % 4).isEqualTo(0); -+ byte[] input = new byte[length]; -+ for (int i = 0; i < length; i += 4) { -+ input[i] = (byte) 0x08; // field 1, wiretype 0 -+ input[i + 1] = (byte) 0x08; // field 1, payload 8 -+ input[i + 2] = (byte) 0x20; // field 4, wiretype 0 -+ input[i + 3] = (byte) 0x20; // field 4, payload 32 -+ } -+ return input; -+ } -+ -+ @Test -+ // This is a performance test. Failure here is a timeout. -+ public void testAlternatingFieldNumbers() throws IOException { -+ byte[] input = generateBytes(800000); -+ InputStream in = new ByteArrayInputStream(input); -+ UnknownFieldSet.Builder builder = UnknownFieldSet.newBuilder(); -+ CodedInputStream codedInput = CodedInputStream.newInstance(in); -+ builder.mergeFrom(codedInput); -+ } -+ -+ @Test -+ // This is a performance test. Failure here is a timeout. -+ public void testAddField() { -+ UnknownFieldSet.Builder builder = UnknownFieldSet.newBuilder(); -+ for (int i = 1; i <= 100000; i++) { -+ UnknownFieldSet.Field field = UnknownFieldSet.Field.newBuilder().addFixed32(i).build(); -+ builder.addField(i, field); -+ } -+ UnknownFieldSet fieldSet = builder.build(); -+ assertThat(fieldSet.getField(100000).getFixed32List().get(0)).isEqualTo(100000); -+ } -+} -diff --git a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java -index c7eb57c..3e1e928 100644 ---- a/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java -+++ b/java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java -@@ -30,6 +30,9 @@ - - package com.google.protobuf; - -+import static com.google.common.truth.Truth.assertThat; -+import static com.google.common.truth.Truth.assertWithMessage; -+ - import protobuf_unittest.UnittestProto; - import protobuf_unittest.UnittestProto.ForeignEnum; - import protobuf_unittest.UnittestProto.TestAllExtensions; -@@ -39,8 +42,10 @@ import protobuf_unittest.UnittestProto.TestEmptyMessageWithExtensions; - import protobuf_unittest.UnittestProto.TestPackedExtensions; - import protobuf_unittest.UnittestProto.TestPackedTypes; - import proto3_unittest.UnittestProto3; -+import java.util.List; - import java.util.Arrays; - import java.util.Map; -+import org.junit.Assert; - import junit.framework.TestCase; - - /** -@@ -58,7 +63,7 @@ public class UnknownFieldSetTest extends TestCase { - unknownFields = emptyMessage.getUnknownFields(); - } - -- UnknownFieldSet.Field getField(String name) { -+ private UnknownFieldSet.Field getField(String name) { - Descriptors.FieldDescriptor field = descriptor.findFieldByName(name); - assertNotNull(field); - return unknownFields.getField(field.getNumber()); -@@ -97,6 +102,161 @@ public class UnknownFieldSetTest extends TestCase { - - // ================================================================= - -+ public void testFieldBuildersAreReusable() { -+ UnknownFieldSet.Field.Builder fieldBuilder = UnknownFieldSet.Field.newBuilder(); -+ fieldBuilder.addFixed32(10); -+ UnknownFieldSet.Field first = fieldBuilder.build(); -+ UnknownFieldSet.Field second = fieldBuilder.build(); -+ fieldBuilder.addFixed32(11); -+ UnknownFieldSet.Field third = fieldBuilder.build(); -+ -+ assertThat(first).isEqualTo(second); -+ assertThat(first).isNotEqualTo(third); -+ } -+ -+ public void testClone() { -+ UnknownFieldSet.Builder unknownSetBuilder = UnknownFieldSet.newBuilder(); -+ UnknownFieldSet.Field.Builder fieldBuilder = UnknownFieldSet.Field.newBuilder(); -+ fieldBuilder.addFixed32(10); -+ unknownSetBuilder.addField(8, fieldBuilder.build()); -+ // necessary to call clone twice to expose the bug -+ UnknownFieldSet.Builder clone1 = unknownSetBuilder.clone(); -+ UnknownFieldSet.Builder clone2 = unknownSetBuilder.clone(); // failure is a NullPointerException -+ assertThat(clone1).isNotSameInstanceAs(clone2); -+ } -+ -+ public void testClone_lengthDelimited() { -+ UnknownFieldSet.Builder destUnknownFieldSet = -+ UnknownFieldSet.newBuilder() -+ .addField(997, UnknownFieldSet.Field.newBuilder().addVarint(99).build()) -+ .addField( -+ 999, -+ UnknownFieldSet.Field.newBuilder() -+ .addLengthDelimited(ByteString.copyFromUtf8("some data")) -+ .addLengthDelimited(ByteString.copyFromUtf8("some more data")) -+ .build()); -+ UnknownFieldSet clone = destUnknownFieldSet.clone().build(); -+ assertThat(clone.getField(997)).isNotNull(); -+ UnknownFieldSet.Field field999 = clone.getField(999); -+ List lengthDelimited = field999.getLengthDelimitedList(); -+ assertThat(lengthDelimited.get(0).toStringUtf8()).isEqualTo("some data"); -+ assertThat(lengthDelimited.get(1).toStringUtf8()).isEqualTo("some more data"); -+ -+ UnknownFieldSet clone2 = destUnknownFieldSet.clone().build(); -+ assertThat(clone2.getField(997)).isNotNull(); -+ UnknownFieldSet.Field secondField = clone2.getField(999); -+ List lengthDelimited2 = secondField.getLengthDelimitedList(); -+ assertThat(lengthDelimited2.get(0).toStringUtf8()).isEqualTo("some data"); -+ assertThat(lengthDelimited2.get(1).toStringUtf8()).isEqualTo("some more data"); -+ } -+ -+ public void testReuse() { -+ UnknownFieldSet.Builder builder = -+ UnknownFieldSet.newBuilder() -+ .addField(997, UnknownFieldSet.Field.newBuilder().addVarint(99).build()) -+ .addField( -+ 999, -+ UnknownFieldSet.Field.newBuilder() -+ .addLengthDelimited(ByteString.copyFromUtf8("some data")) -+ .addLengthDelimited(ByteString.copyFromUtf8("some more data")) -+ .build()); -+ -+ UnknownFieldSet fieldSet1 = builder.build(); -+ UnknownFieldSet fieldSet2 = builder.build(); -+ builder.addField(1000, UnknownFieldSet.Field.newBuilder().addVarint(-90).build()); -+ UnknownFieldSet fieldSet3 = builder.build(); -+ -+ assertThat(fieldSet1).isEqualTo(fieldSet2); -+ assertThat(fieldSet1).isNotEqualTo(fieldSet3); -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testAddField_zero() { -+ UnknownFieldSet.Field field = getField("optional_int32"); -+ try { -+ UnknownFieldSet.newBuilder().addField(0, field); -+ Assert.fail(); -+ } catch (IllegalArgumentException expected) { -+ assertThat(expected).hasMessageThat().isEqualTo("0 is not a valid field number."); -+ } -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testAddField_negative() { -+ UnknownFieldSet.Field field = getField("optional_int32"); -+ try { -+ UnknownFieldSet.newBuilder().addField(-2, field); -+ Assert.fail(); -+ } catch (IllegalArgumentException expected) { -+ assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); -+ } -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testClearField_negative() { -+ try { -+ UnknownFieldSet.newBuilder().clearField(-28); -+ Assert.fail(); -+ } catch (IllegalArgumentException expected) { -+ assertThat(expected).hasMessageThat().isEqualTo("-28 is not a valid field number."); -+ } -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testMergeField_negative() { -+ UnknownFieldSet.Field field = getField("optional_int32"); -+ try { -+ UnknownFieldSet.newBuilder().mergeField(-2, field); -+ Assert.fail(); -+ } catch (IllegalArgumentException expected) { -+ assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); -+ } -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testMergeVarintField_negative() { -+ try { -+ UnknownFieldSet.newBuilder().mergeVarintField(-2, 78); -+ Assert.fail(); -+ } catch (IllegalArgumentException expected) { -+ assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); -+ } -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testHasField_negative() { -+ assertThat(UnknownFieldSet.newBuilder().hasField(-2)).isFalse(); -+ } -+ -+ @SuppressWarnings("ModifiedButNotUsed") -+ public void testMergeLengthDelimitedField_negative() { -+ ByteString byteString = ByteString.copyFromUtf8("some data"); -+ try { -+ UnknownFieldSet.newBuilder().mergeLengthDelimitedField(-2, byteString); -+ Assert.fail(); -+ } catch (IllegalArgumentException expected) { -+ assertThat(expected).hasMessageThat().isEqualTo("-2 is not a valid field number."); -+ } -+ } -+ -+ public void testAddField() { -+ UnknownFieldSet.Field field = getField("optional_int32"); -+ UnknownFieldSet fieldSet = UnknownFieldSet.newBuilder().addField(1, field).build(); -+ assertThat(fieldSet.getField(1)).isEqualTo(field); -+ } -+ -+ public void testAddField_withReplacement() { -+ UnknownFieldSet.Field first = UnknownFieldSet.Field.newBuilder().addFixed32(56).build(); -+ UnknownFieldSet.Field second = UnknownFieldSet.Field.newBuilder().addFixed32(25).build(); -+ UnknownFieldSet fieldSet = UnknownFieldSet.newBuilder() -+ .addField(1, first) -+ .addField(1, second) -+ .build(); -+ List list = fieldSet.getField(1).getFixed32List(); -+ assertThat(list).hasSize(1); -+ assertThat(list.get(0)).isEqualTo(25); -+ } -+ - public void testVarint() throws Exception { - UnknownFieldSet.Field field = getField("optional_int32"); - assertEquals(1, field.getVarintList().size()); -@@ -173,6 +333,15 @@ public class UnknownFieldSetTest extends TestCase { - assertEquals("1: 1\n2: 2\n3: 3\n3: 4\n", destination.toString()); - } - -+ public void testAsMap() throws Exception { -+ UnknownFieldSet.Builder builder = UnknownFieldSet.newBuilder().mergeFrom(unknownFields); -+ Map mapFromBuilder = builder.asMap(); -+ assertThat(mapFromBuilder).isNotEmpty(); -+ UnknownFieldSet fields = builder.build(); -+ Map mapFromFieldSet = fields.asMap(); -+ assertThat(mapFromFieldSet).containsExactlyEntriesIn(mapFromBuilder); -+ } -+ - public void testClear() throws Exception { - UnknownFieldSet fields = UnknownFieldSet.newBuilder().mergeFrom(unknownFields).clear().build(); - assertTrue(fields.asMap().isEmpty()); -diff --git a/java/lite/pom.xml b/java/lite/pom.xml -index 104c5c1..d095483 100644 ---- a/java/lite/pom.xml -+++ b/java/lite/pom.xml -@@ -232,7 +232,8 @@ - TestUtil.java - TypeRegistryTest.java - UnknownEnumValueTest.java -- UnknownFieldSetLiteTest.java -+ UnknownFieldSetLiteTest.java -+ UnknownFieldSetPerformanceTest.java - UnknownFieldSetTest.java - WellKnownTypesTest.java - WireFormatTest.java --- -2.30.0 - diff --git a/0005-fix-CVE-2022-1941.patch b/0005-fix-CVE-2022-1941.patch deleted file mode 100644 index 59745937aab7ca5b49f30607abafac745de3c26a..0000000000000000000000000000000000000000 --- a/0005-fix-CVE-2022-1941.patch +++ /dev/null @@ -1,368 +0,0 @@ -From 55815e423bb82cc828836bbd60c79c1f9a195763 Mon Sep 17 00:00:00 2001 -From: Deanna Garcia -Date: Tue, 13 Sep 2022 17:20:00 +0000 -Subject: [PATCH] Apply patch - ---- - src/google/protobuf/extension_set_inl.h | 27 +++-- - src/google/protobuf/wire_format.cc | 26 +++-- - src/google/protobuf/wire_format_lite.h | 27 +++-- - src/google/protobuf/wire_format_unittest.cc | 109 ++++++++++++++++++-- - 4 files changed, 152 insertions(+), 37 deletions(-) - -diff --git a/src/google/protobuf/extension_set_inl.h b/src/google/protobuf/extension_set_inl.h -index 074784b96..77f95f62f 100644 ---- a/src/google/protobuf/extension_set_inl.h -+++ b/src/google/protobuf/extension_set_inl.h -@@ -206,16 +206,21 @@ const char* ExtensionSet::ParseMessageSetItemTmpl( - const char* ptr, const Msg* containing_type, - internal::InternalMetadata* metadata, internal::ParseContext* ctx) { - std::string payload; -- uint32 type_id = 0; -- bool payload_read = false; -+ uint32 type_id; -+ enum class State { kNoTag, kHasType, kHasPayload, kDone }; -+ State state = State::kNoTag; -+ - while (!ctx->Done(&ptr)) { - uint32 tag = static_cast(*ptr++); - if (tag == WireFormatLite::kMessageSetTypeIdTag) { - uint64 tmp; - ptr = ParseBigVarint(ptr, &tmp); - GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); -- type_id = tmp; -- if (payload_read) { -+ if (state == State::kNoTag) { -+ type_id = tmp; -+ state = State::kHasType; -+ } else if (state == State::kHasPayload) { -+ type_id = tmp; - ExtensionInfo extension; - bool was_packed_on_wire; - if (!FindExtension(2, type_id, containing_type, ctx, &extension, -@@ -241,20 +246,24 @@ const char* ExtensionSet::ParseMessageSetItemTmpl( - GOOGLE_PROTOBUF_PARSER_ASSERT(value->_InternalParse(p, &tmp_ctx) && - tmp_ctx.EndedAtLimit()); - } -- type_id = 0; -+ state = State::kDone; - } - } else if (tag == WireFormatLite::kMessageSetMessageTag) { -- if (type_id != 0) { -+ if (state == State::kHasType) { - ptr = ParseFieldMaybeLazily(static_cast(type_id) * 8 + 2, ptr, - containing_type, metadata, ctx); - GOOGLE_PROTOBUF_PARSER_ASSERT(ptr != nullptr); -- type_id = 0; -+ state = State::kDone; - } else { -+ std::string tmp; - int32 size = ReadSize(&ptr); - GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); -- ptr = ctx->ReadString(ptr, size, &payload); -+ ptr = ctx->ReadString(ptr, size, &tmp); - GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); -- payload_read = true; -+ if (state == State::kNoTag) { -+ payload = std::move(tmp); -+ state = State::kHasPayload; -+ } - } - } else { - ptr = ReadTag(ptr - 1, &tag); -diff --git a/src/google/protobuf/wire_format.cc b/src/google/protobuf/wire_format.cc -index c30b7abff..382d01ea0 100644 ---- a/src/google/protobuf/wire_format.cc -+++ b/src/google/protobuf/wire_format.cc -@@ -657,9 +657,11 @@ struct WireFormat::MessageSetParser { - const char* _InternalParse(const char* ptr, internal::ParseContext* ctx) { - // Parse a MessageSetItem - auto metadata = reflection->MutableInternalMetadata(msg); -+ enum class State { kNoTag, kHasType, kHasPayload, kDone }; -+ State state = State::kNoTag; -+ - std::string payload; - uint32 type_id = 0; -- bool payload_read = false; - while (!ctx->Done(&ptr)) { - // We use 64 bit tags in order to allow typeid's that span the whole - // range of 32 bit numbers. -@@ -668,8 +670,11 @@ struct WireFormat::MessageSetParser { - uint64 tmp; - ptr = ParseBigVarint(ptr, &tmp); - GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); -- type_id = tmp; -- if (payload_read) { -+ if (state == State::kNoTag) { -+ type_id = tmp; -+ state = State::kHasType; -+ } else if (state == State::kHasPayload) { -+ type_id = tmp; - const FieldDescriptor* field; - if (ctx->data().pool == nullptr) { - field = reflection->FindKnownExtensionByNumber(type_id); -@@ -696,17 +701,17 @@ struct WireFormat::MessageSetParser { - GOOGLE_PROTOBUF_PARSER_ASSERT(value->_InternalParse(p, &tmp_ctx) && - tmp_ctx.EndedAtLimit()); - } -- type_id = 0; -+ state = State::kDone; - } - continue; - } else if (tag == WireFormatLite::kMessageSetMessageTag) { -- if (type_id == 0) { -+ if (state == State::kNoTag) { - int32 size = ReadSize(&ptr); - GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); - ptr = ctx->ReadString(ptr, size, &payload); - GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); -- payload_read = true; -- } else { -+ state = State::kHasPayload; -+ } else if (state == State::kHasType) { - // We're now parsing the payload - const FieldDescriptor* field = nullptr; - if (descriptor->IsExtensionNumber(type_id)) { -@@ -720,7 +725,12 @@ struct WireFormat::MessageSetParser { - ptr = WireFormat::_InternalParseAndMergeField( - msg, ptr, ctx, static_cast(type_id) * 8 + 2, reflection, - field); -- type_id = 0; -+ state = State::kDone; -+ } else { -+ int32 size = ReadSize(&ptr); -+ GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); -+ ptr = ctx->Skip(ptr, size); -+ GOOGLE_PROTOBUF_PARSER_ASSERT(ptr); - } - } else { - // An unknown field in MessageSetItem. -diff --git a/src/google/protobuf/wire_format_lite.h b/src/google/protobuf/wire_format_lite.h -index f2a3cad82..0b13096cc 100644 ---- a/src/google/protobuf/wire_format_lite.h -+++ b/src/google/protobuf/wire_format_lite.h -@@ -1798,6 +1798,9 @@ bool ParseMessageSetItemImpl(io::CodedInputStream* input, MS ms) { - // we can parse it later. - std::string message_data; - -+ enum class State { kNoTag, kHasType, kHasPayload, kDone }; -+ State state = State::kNoTag; -+ - while (true) { - const uint32 tag = input->ReadTagNoLastTag(); - if (tag == 0) return false; -@@ -1806,26 +1809,34 @@ bool ParseMessageSetItemImpl(io::CodedInputStream* input, MS ms) { - case WireFormatLite::kMessageSetTypeIdTag: { - uint32 type_id; - if (!input->ReadVarint32(&type_id)) return false; -- last_type_id = type_id; -- -- if (!message_data.empty()) { -+ if (state == State::kNoTag) { -+ last_type_id = type_id; -+ state = State::kHasType; -+ } else if (state == State::kHasPayload) { - // We saw some message data before the type_id. Have to parse it - // now. - io::CodedInputStream sub_input( - reinterpret_cast(message_data.data()), - static_cast(message_data.size())); - sub_input.SetRecursionLimit(input->RecursionBudget()); -- if (!ms.ParseField(last_type_id, &sub_input)) { -+ if (!ms.ParseField(type_id, &sub_input)) { - return false; - } - message_data.clear(); -+ state = State::kDone; - } - - break; - } - - case WireFormatLite::kMessageSetMessageTag: { -- if (last_type_id == 0) { -+ if (state == State::kHasType) { -+ // Already saw type_id, so we can parse this directly. -+ if (!ms.ParseField(last_type_id, input)) { -+ return false; -+ } -+ state = State::kDone; -+ } else if (state == State::kNoTag) { - // We haven't seen a type_id yet. Append this data to message_data. - uint32 length; - if (!input->ReadVarint32(&length)) return false; -@@ -1836,11 +1847,9 @@ bool ParseMessageSetItemImpl(io::CodedInputStream* input, MS ms) { - auto ptr = reinterpret_cast(&message_data[0]); - ptr = io::CodedOutputStream::WriteVarint32ToArray(length, ptr); - if (!input->ReadRaw(ptr, length)) return false; -+ state = State::kHasPayload; - } else { -- // Already saw type_id, so we can parse this directly. -- if (!ms.ParseField(last_type_id, input)) { -- return false; -- } -+ if (!ms.SkipField(tag, input)) return false; - } - - break; -diff --git a/src/google/protobuf/wire_format_unittest.cc b/src/google/protobuf/wire_format_unittest.cc -index e75fc316f..8d767b283 100644 ---- a/src/google/protobuf/wire_format_unittest.cc -+++ b/src/google/protobuf/wire_format_unittest.cc -@@ -46,6 +46,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -585,30 +586,56 @@ TEST(WireFormatTest, ParseMessageSet) { - EXPECT_EQ(message_set.DebugString(), dynamic_message_set.DebugString()); - } - --TEST(WireFormatTest, ParseMessageSetWithReverseTagOrder) { -+namespace { -+std::string BuildMessageSetItemStart() { - std::string data; - { -- unittest::TestMessageSetExtension1 message; -- message.set_i(123); -- // Build a MessageSet manually with its message content put before its -- // type_id. - io::StringOutputStream output_stream(&data); - io::CodedOutputStream coded_output(&output_stream); - coded_output.WriteTag(WireFormatLite::kMessageSetItemStartTag); -+ } -+ return data; -+} -+std::string BuildMessageSetItemEnd() { -+ std::string data; -+ { -+ io::StringOutputStream output_stream(&data); -+ io::CodedOutputStream coded_output(&output_stream); -+ coded_output.WriteTag(WireFormatLite::kMessageSetItemEndTag); -+ } -+ return data; -+} -+std::string BuildMessageSetTestExtension1(int value = 123) { -+ std::string data; -+ { -+ unittest::TestMessageSetExtension1 message; -+ message.set_i(value); -+ io::StringOutputStream output_stream(&data); -+ io::CodedOutputStream coded_output(&output_stream); - // Write the message content first. - WireFormatLite::WriteTag(WireFormatLite::kMessageSetMessageNumber, - WireFormatLite::WIRETYPE_LENGTH_DELIMITED, - &coded_output); - coded_output.WriteVarint32(message.ByteSizeLong()); - message.SerializeWithCachedSizes(&coded_output); -- // Write the type id. -- uint32 type_id = message.GetDescriptor()->extension(0)->number(); -+ } -+ return data; -+} -+std::string BuildMessageSetItemTypeId(int extension_number) { -+ std::string data; -+ { -+ io::StringOutputStream output_stream(&data); -+ io::CodedOutputStream coded_output(&output_stream); - WireFormatLite::WriteUInt32(WireFormatLite::kMessageSetTypeIdNumber, -- type_id, &coded_output); -- coded_output.WriteTag(WireFormatLite::kMessageSetItemEndTag); -+ extension_number, &coded_output); - } -+ return data; -+} -+void ValidateTestMessageSet(const std::string& test_case, -+ const std::string& data) { -+ SCOPED_TRACE(test_case); - { -- proto2_wireformat_unittest::TestMessageSet message_set; -+ ::proto2_wireformat_unittest::TestMessageSet message_set; - ASSERT_TRUE(message_set.ParseFromString(data)); - - EXPECT_EQ(123, -@@ -616,10 +643,15 @@ TEST(WireFormatTest, ParseMessageSetWithReverseTagOrder) { - .GetExtension( - unittest::TestMessageSetExtension1::message_set_extension) - .i()); -+ -+ // Make sure it does not contain anything else. -+ message_set.ClearExtension( -+ unittest::TestMessageSetExtension1::message_set_extension); -+ EXPECT_EQ(message_set.SerializeAsString(), ""); - } - { - // Test parse the message via Reflection. -- proto2_wireformat_unittest::TestMessageSet message_set; -+ ::proto2_wireformat_unittest::TestMessageSet message_set; - io::CodedInputStream input(reinterpret_cast(data.data()), - data.size()); - EXPECT_TRUE(WireFormat::ParseAndMergePartial(&input, &message_set)); -@@ -631,6 +663,61 @@ TEST(WireFormatTest, ParseMessageSetWithReverseTagOrder) { - unittest::TestMessageSetExtension1::message_set_extension) - .i()); - } -+ { -+ // Test parse the message via DynamicMessage. -+ DynamicMessageFactory factory; -+ std::unique_ptr msg( -+ factory -+ .GetPrototype( -+ ::proto2_wireformat_unittest::TestMessageSet::descriptor()) -+ ->New()); -+ msg->ParseFromString(data); -+ auto* reflection = msg->GetReflection(); -+ std::vector fields; -+ reflection->ListFields(*msg, &fields); -+ ASSERT_EQ(fields.size(), 1); -+ const auto& sub = reflection->GetMessage(*msg, fields[0]); -+ reflection = sub.GetReflection(); -+ EXPECT_EQ(123, reflection->GetInt32( -+ sub, sub.GetDescriptor()->FindFieldByName("i"))); -+ } -+} -+} // namespace -+ -+TEST(WireFormatTest, ParseMessageSetWithAnyTagOrder) { -+ std::string start = BuildMessageSetItemStart(); -+ std::string end = BuildMessageSetItemEnd(); -+ std::string id = BuildMessageSetItemTypeId( -+ unittest::TestMessageSetExtension1::descriptor()->extension(0)->number()); -+ std::string message = BuildMessageSetTestExtension1(); -+ -+ ValidateTestMessageSet("id + message", start + id + message + end); -+ ValidateTestMessageSet("message + id", start + message + id + end); -+} -+ -+TEST(WireFormatTest, ParseMessageSetWithDuplicateTags) { -+ std::string start = BuildMessageSetItemStart(); -+ std::string end = BuildMessageSetItemEnd(); -+ std::string id = BuildMessageSetItemTypeId( -+ unittest::TestMessageSetExtension1::descriptor()->extension(0)->number()); -+ std::string other_id = BuildMessageSetItemTypeId(123456); -+ std::string message = BuildMessageSetTestExtension1(); -+ std::string other_message = BuildMessageSetTestExtension1(321); -+ -+ // Double id -+ ValidateTestMessageSet("id + other_id + message", -+ start + id + other_id + message + end); -+ ValidateTestMessageSet("id + message + other_id", -+ start + id + message + other_id + end); -+ ValidateTestMessageSet("message + id + other_id", -+ start + message + id + other_id + end); -+ // Double message -+ ValidateTestMessageSet("id + message + other_message", -+ start + id + message + other_message + end); -+ ValidateTestMessageSet("message + id + other_message", -+ start + message + id + other_message + end); -+ ValidateTestMessageSet("message + other_message + id", -+ start + message + other_message + id + end); - } - - void SerializeReverseOrder( --- -2.25.1 - diff --git a/0006-fix-CVE-2022-3171.patch b/0006-fix-CVE-2022-3171.patch deleted file mode 100644 index 9bd8d1a39a0ed7264f2c420a2adc4fdbbe764f33..0000000000000000000000000000000000000000 --- a/0006-fix-CVE-2022-3171.patch +++ /dev/null @@ -1,4924 +0,0 @@ -From 11ceafebd2234b5c3e559f6ae3d433bf0ed7f667 Mon Sep 17 00:00:00 2001 -From: chengzrz -Date: Mon, 17 Oct 2022 21:43:05 +0800 -Subject: [PATCH] fix cve-2022-3171 - -Signed-off-by: chengzrz ---- - .../com/google/protobuf/AbstractMessage.java | 27 +- - .../com/google/protobuf/ArrayDecoders.java | 146 +++--- - .../com/google/protobuf/BinaryReader.java | 32 +- - .../protobuf/CodedInputStreamReader.java | 51 +- - .../DescriptorMessageInfoFactory.java | 4 +- - .../com/google/protobuf/DynamicMessage.java | 5 +- - .../com/google/protobuf/ExtensionSchema.java | 1 + - .../google/protobuf/ExtensionSchemaFull.java | 5 +- - .../google/protobuf/ExtensionSchemaLite.java | 52 +- - .../java/com/google/protobuf/FieldSet.java | 63 ++- - .../google/protobuf/GeneratedMessageLite.java | 169 ++++-- - .../google/protobuf/GeneratedMessageV3.java | 110 +++- - .../google/protobuf/MessageLiteToString.java | 117 +++-- - .../google/protobuf/MessageReflection.java | 439 +++++++++++++++- - .../com/google/protobuf/MessageSchema.java | 485 +++++++++++------- - .../com/google/protobuf/MessageSetSchema.java | 17 +- - .../protobuf/NewInstanceSchemaLite.java | 5 +- - .../main/java/com/google/protobuf/Reader.java | 8 + - .../java/com/google/protobuf/SchemaUtil.java | 30 +- - .../java/com/google/protobuf/TextFormat.java | 2 +- - .../google/protobuf/UnknownFieldSetLite.java | 38 +- - .../protobuf/UnknownFieldSetLiteSchema.java | 12 +- - .../java/com/google/protobuf/LiteTest.java | 36 +- - .../google/protobuf/util/FieldMaskTree.java | 10 +- - .../protobuf/compiler/java/java_enum_field.cc | 94 ++-- - .../protobuf/compiler/java/java_enum_field.h | 94 ++-- - .../protobuf/compiler/java/java_field.cc | 4 +- - .../protobuf/compiler/java/java_field.h | 5 +- - .../protobuf/compiler/java/java_map_field.cc | 30 +- - .../protobuf/compiler/java/java_map_field.h | 34 +- - .../protobuf/compiler/java/java_message.cc | 220 ++------ - .../compiler/java/java_message_builder.cc | 163 ++++-- - .../compiler/java/java_message_builder.h | 5 + - .../compiler/java/java_message_field.cc | 126 ++--- - .../compiler/java/java_message_field.h | 87 ++-- - .../compiler/java/java_primitive_field.cc | 66 +-- - .../compiler/java/java_primitive_field.h | 90 ++-- - .../compiler/java/java_string_field.cc | 59 +-- - .../compiler/java/java_string_field.h | 87 ++-- - 39 files changed, 1907 insertions(+), 1121 deletions(-) - -diff --git a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java -index 1364fce..ebf4318 100644 ---- a/java/core/src/main/java/com/google/protobuf/AbstractMessage.java -+++ b/java/core/src/main/java/com/google/protobuf/AbstractMessage.java -@@ -426,27 +426,22 @@ public abstract class AbstractMessage - throws IOException { - boolean discardUnknown = input.shouldDiscardUnknownFields(); - final UnknownFieldSet.Builder unknownFields = -- discardUnknown ? null : UnknownFieldSet.newBuilder(getUnknownFields()); -- while (true) { -- final int tag = input.readTag(); -- if (tag == 0) { -- break; -- } -- -- MessageReflection.BuilderAdapter builderAdapter = -- new MessageReflection.BuilderAdapter(this); -- if (!MessageReflection.mergeFieldFrom( -- input, unknownFields, extensionRegistry, getDescriptorForType(), builderAdapter, tag)) { -- // end group tag -- break; -- } -- } -+ discardUnknown ? null : getUnknownFieldSetBuilder(); -+ MessageReflection.mergeMessageFrom(this, unknownFields, input, extensionRegistry); - if (unknownFields != null) { -- setUnknownFields(unknownFields.build()); -+ setUnknownFieldSetBuilder(unknownFields); - } - return (BuilderType) this; - } - -+ protected UnknownFieldSet.Builder getUnknownFieldSetBuilder() { -+ return UnknownFieldSet.newBuilder(getUnknownFields()); -+ } -+ -+ protected void setUnknownFieldSetBuilder(final UnknownFieldSet.Builder builder) { -+ setUnknownFields(builder.build()); -+ } -+ - @Override - public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) { - setUnknownFields( -diff --git a/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java b/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java -index 1217e11..39b7927 100644 ---- a/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java -+++ b/java/core/src/main/java/com/google/protobuf/ArrayDecoders.java -@@ -234,6 +234,29 @@ final class ArrayDecoders { - @SuppressWarnings({"unchecked", "rawtypes"}) - static int decodeMessageField( - Schema schema, byte[] data, int position, int limit, Registers registers) throws IOException { -+ Object msg = schema.newInstance(); -+ int offset = mergeMessageField(msg, schema, data, position, limit, registers); -+ schema.makeImmutable(msg); -+ registers.object1 = msg; -+ return offset; -+ } -+ -+ /** Decodes a group value. */ -+ @SuppressWarnings({"unchecked", "rawtypes"}) -+ static int decodeGroupField( -+ Schema schema, byte[] data, int position, int limit, int endGroup, Registers registers) -+ throws IOException { -+ Object msg = schema.newInstance(); -+ int offset = mergeGroupField(msg, schema, data, position, limit, endGroup, registers); -+ schema.makeImmutable(msg); -+ registers.object1 = msg; -+ return offset; -+ } -+ -+ @SuppressWarnings({"unchecked", "rawtypes"}) -+ static int mergeMessageField( -+ Object msg, Schema schema, byte[] data, int position, int limit, Registers registers) -+ throws IOException { - int length = data[position++]; - if (length < 0) { - position = decodeVarint32(length, data, position, registers); -@@ -242,27 +265,28 @@ final class ArrayDecoders { - if (length < 0 || length > limit - position) { - throw InvalidProtocolBufferException.truncatedMessage(); - } -- Object result = schema.newInstance(); -- schema.mergeFrom(result, data, position, position + length, registers); -- schema.makeImmutable(result); -- registers.object1 = result; -+ schema.mergeFrom(msg, data, position, position + length, registers); -+ registers.object1 = msg; - return position + length; - } - -- /** Decodes a group value. */ - @SuppressWarnings({"unchecked", "rawtypes"}) -- static int decodeGroupField( -- Schema schema, byte[] data, int position, int limit, int endGroup, Registers registers) -+ static int mergeGroupField( -+ Object msg, -+ Schema schema, -+ byte[] data, -+ int position, -+ int limit, -+ int endGroup, -+ Registers registers) - throws IOException { - // A group field must has a MessageSchema (the only other subclass of Schema is MessageSetSchema - // and it can't be used in group fields). - final MessageSchema messageSchema = (MessageSchema) schema; -- Object result = messageSchema.newInstance(); - // It's OK to directly use parseProto2Message since proto3 doesn't have group. - final int endPosition = -- messageSchema.parseProto2Message(result, data, position, limit, endGroup, registers); -- messageSchema.makeImmutable(result); -- registers.object1 = result; -+ messageSchema.parseProto2Message(msg, data, position, limit, endGroup, registers); -+ registers.object1 = msg; - return endPosition; - } - -@@ -847,26 +871,19 @@ final class ArrayDecoders { - break; - } - case ENUM: -- { -- IntArrayList list = new IntArrayList(); -- position = decodePackedVarint32List(data, position, list, registers); -- UnknownFieldSetLite unknownFields = message.unknownFields; -- if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { -- unknownFields = null; -- } -- unknownFields = -- SchemaUtil.filterUnknownEnumList( -- fieldNumber, -- list, -- extension.descriptor.getEnumType(), -- unknownFields, -- unknownFieldSchema); -- if (unknownFields != null) { -- message.unknownFields = unknownFields; -+ { -+ IntArrayList list = new IntArrayList(); -+ position = decodePackedVarint32List(data, position, list, registers); -+ SchemaUtil.filterUnknownEnumList( -+ message, -+ fieldNumber, -+ list, -+ extension.descriptor.getEnumType(), -+ null, -+ unknownFieldSchema); -+ extensions.setField(extension.descriptor, list); -+ break; - } -- extensions.setField(extension.descriptor, list); -- break; -- } - default: - throw new IllegalStateException( - "Type cannot be packed: " + extension.descriptor.getLiteType()); -@@ -878,13 +895,8 @@ final class ArrayDecoders { - position = decodeVarint32(data, position, registers); - Object enumValue = extension.descriptor.getEnumType().findValueByNumber(registers.int1); - if (enumValue == null) { -- UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields; -- if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { -- unknownFields = UnknownFieldSetLite.newInstance(); -- ((GeneratedMessageLite) message).unknownFields = unknownFields; -- } - SchemaUtil.storeUnknownEnum( -- fieldNumber, registers.int1, unknownFields, unknownFieldSchema); -+ message, fieldNumber, registers.int1, null, unknownFieldSchema); - return position; - } - // Note, we store the integer value instead of the actual enum object in FieldSet. -@@ -941,20 +953,45 @@ final class ArrayDecoders { - value = registers.object1; - break; - case GROUP: -- final int endTag = (fieldNumber << 3) | WireFormat.WIRETYPE_END_GROUP; -- position = decodeGroupField( -- Protobuf.getInstance().schemaFor(extension.getMessageDefaultInstance().getClass()), -- data, position, limit, endTag, registers); -- value = registers.object1; -- break; -- -+ { -+ final int endTag = (fieldNumber << 3) | WireFormat.WIRETYPE_END_GROUP; -+ final Schema fieldSchema = -+ Protobuf.getInstance() -+ .schemaFor(extension.getMessageDefaultInstance().getClass()); -+ if (extension.isRepeated()) { -+ position = decodeGroupField(fieldSchema, data, position, limit, endTag, registers); -+ extensions.addRepeatedField(extension.descriptor, registers.object1); -+ } else { -+ Object oldValue = extensions.getField(extension.descriptor); -+ if (oldValue == null) { -+ oldValue = fieldSchema.newInstance(); -+ extensions.setField(extension.descriptor, oldValue); -+ } -+ position = -+ mergeGroupField( -+ oldValue, fieldSchema, data, position, limit, endTag, registers); -+ } -+ return position; -+ } - case MESSAGE: -- position = decodeMessageField( -- Protobuf.getInstance().schemaFor(extension.getMessageDefaultInstance().getClass()), -- data, position, limit, registers); -- value = registers.object1; -- break; -- -+ { -+ final Schema fieldSchema = -+ Protobuf.getInstance() -+ .schemaFor(extension.getMessageDefaultInstance().getClass()); -+ if (extension.isRepeated()) { -+ position = decodeMessageField(fieldSchema, data, position, limit, registers); -+ extensions.addRepeatedField(extension.descriptor, registers.object1); -+ } else { -+ Object oldValue = extensions.getField(extension.descriptor); -+ if (oldValue == null) { -+ oldValue = fieldSchema.newInstance(); -+ extensions.setField(extension.descriptor, oldValue); -+ } -+ position = -+ mergeMessageField(oldValue, fieldSchema, data, position, limit, registers); -+ } -+ return position; -+ } - case ENUM: - throw new IllegalStateException("Shouldn't reach here."); - } -@@ -962,17 +999,6 @@ final class ArrayDecoders { - if (extension.isRepeated()) { - extensions.addRepeatedField(extension.descriptor, value); - } else { -- switch (extension.getLiteType()) { -- case MESSAGE: -- case GROUP: -- Object oldValue = extensions.getField(extension.descriptor); -- if (oldValue != null) { -- value = Internal.mergeMessage(oldValue, value); -- } -- break; -- default: -- break; -- } - extensions.setField(extension.descriptor, value); - } - } -diff --git a/java/core/src/main/java/com/google/protobuf/BinaryReader.java b/java/core/src/main/java/com/google/protobuf/BinaryReader.java -index d64574c..3a0e04d 100644 ---- a/java/core/src/main/java/com/google/protobuf/BinaryReader.java -+++ b/java/core/src/main/java/com/google/protobuf/BinaryReader.java -@@ -247,6 +247,15 @@ abstract class BinaryReader implements Reader { - - private T readMessage(Schema schema, ExtensionRegistryLite extensionRegistry) - throws IOException { -+ T newInstance = schema.newInstance(); -+ mergeMessageField(newInstance, schema, extensionRegistry); -+ schema.makeImmutable(newInstance); -+ return newInstance; -+ } -+ -+ @Override -+ public void mergeMessageField( -+ T target, Schema schema, ExtensionRegistryLite extensionRegistry) throws IOException { - int size = readVarint32(); - requireBytes(size); - -@@ -256,15 +265,10 @@ abstract class BinaryReader implements Reader { - limit = newLimit; - - try { -- // Allocate and read the message. -- T message = schema.newInstance(); -- schema.mergeFrom(message, this, extensionRegistry); -- schema.makeImmutable(message); -- -+ schema.mergeFrom(target, this, extensionRegistry); - if (pos != newLimit) { - throw InvalidProtocolBufferException.parseFailure(); - } -- return message; - } finally { - // Restore the limit. - limit = prevLimit; -@@ -287,19 +291,23 @@ abstract class BinaryReader implements Reader { - - private T readGroup(Schema schema, ExtensionRegistryLite extensionRegistry) - throws IOException { -+ T newInstance = schema.newInstance(); -+ mergeGroupField(newInstance, schema, extensionRegistry); -+ schema.makeImmutable(newInstance); -+ return newInstance; -+ } -+ -+ @Override -+ public void mergeGroupField( -+ T target, Schema schema, ExtensionRegistryLite extensionRegistry) throws IOException { - int prevEndGroupTag = endGroupTag; - endGroupTag = WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WIRETYPE_END_GROUP); - - try { -- // Allocate and read the message. -- T message = schema.newInstance(); -- schema.mergeFrom(message, this, extensionRegistry); -- schema.makeImmutable(message); -- -+ schema.mergeFrom(target, this, extensionRegistry); - if (tag != endGroupTag) { - throw InvalidProtocolBufferException.parseFailure(); - } -- return message; - } finally { - // Restore the old end group tag. - endGroupTag = prevEndGroupTag; -diff --git a/java/core/src/main/java/com/google/protobuf/CodedInputStreamReader.java b/java/core/src/main/java/com/google/protobuf/CodedInputStreamReader.java -index 7658f62..1d992d7 100644 ---- a/java/core/src/main/java/com/google/protobuf/CodedInputStreamReader.java -+++ b/java/core/src/main/java/com/google/protobuf/CodedInputStreamReader.java -@@ -197,9 +197,15 @@ final class CodedInputStreamReader implements Reader { - return readGroup(schema, extensionRegistry); - } - -- // Should have the same semantics of CodedInputStream#readMessage() -- private T readMessage(Schema schema, ExtensionRegistryLite extensionRegistry) -- throws IOException { -+ @Override -+ public void mergeMessageField( -+ T target, Schema schema, ExtensionRegistryLite extensionRegistry) throws IOException { -+ requireWireType(WIRETYPE_LENGTH_DELIMITED); -+ mergeMessageFieldInternal(target, schema, extensionRegistry); -+ } -+ -+ private void mergeMessageFieldInternal( -+ T target, Schema schema, ExtensionRegistryLite extensionRegistry) throws IOException { - int size = input.readUInt32(); - if (input.recursionDepth >= input.recursionLimit) { - throw InvalidProtocolBufferException.recursionLimitExceeded(); -@@ -207,39 +213,54 @@ final class CodedInputStreamReader implements Reader { - - // Push the new limit. - final int prevLimit = input.pushLimit(size); -- // Allocate and read the message. -- T message = schema.newInstance(); - ++input.recursionDepth; -- schema.mergeFrom(message, this, extensionRegistry); -- schema.makeImmutable(message); -+ schema.mergeFrom(target, this, extensionRegistry); - input.checkLastTagWas(0); - --input.recursionDepth; - // Restore the previous limit. - input.popLimit(prevLimit); -- return message; - } - -- private T readGroup(Schema schema, ExtensionRegistryLite extensionRegistry) -+ // Should have the same semantics of CodedInputStream#readMessage() -+ private T readMessage(Schema schema, ExtensionRegistryLite extensionRegistry) - throws IOException { -+ T newInstance = schema.newInstance(); -+ mergeMessageFieldInternal(newInstance, schema, extensionRegistry); -+ schema.makeImmutable(newInstance); -+ return newInstance; -+ } -+ -+ @Override -+ public void mergeGroupField( -+ T target, Schema schema, ExtensionRegistryLite extensionRegistry) throws IOException { -+ requireWireType(WIRETYPE_START_GROUP); -+ mergeGroupFieldInternal(target, schema, extensionRegistry); -+ } -+ -+ private void mergeGroupFieldInternal( -+ T target, Schema schema, ExtensionRegistryLite extensionRegistry) throws IOException { - int prevEndGroupTag = endGroupTag; - endGroupTag = WireFormat.makeTag(WireFormat.getTagFieldNumber(tag), WIRETYPE_END_GROUP); - - try { -- // Allocate and read the message. -- T message = schema.newInstance(); -- schema.mergeFrom(message, this, extensionRegistry); -- schema.makeImmutable(message); -- -+ schema.mergeFrom(target, this, extensionRegistry); - if (tag != endGroupTag) { - throw InvalidProtocolBufferException.parseFailure(); - } -- return message; - } finally { - // Restore the old end group tag. - endGroupTag = prevEndGroupTag; - } - } - -+ private T readGroup(Schema schema, ExtensionRegistryLite extensionRegistry) -+ throws IOException { -+ T newInstance = schema.newInstance(); -+ mergeGroupFieldInternal(newInstance, schema, extensionRegistry); -+ schema.makeImmutable(newInstance); -+ return newInstance; -+ } -+ - @Override - public ByteString readBytes() throws IOException { - requireWireType(WIRETYPE_LENGTH_DELIMITED); -diff --git a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java -index 7975136..21ded52 100644 ---- a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java -+++ b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java -@@ -402,8 +402,8 @@ final class DescriptorMessageInfoFactory implements MessageInfoFactory { - boolean enforceUtf8 = true; - for (int i = 0; i < fieldDescriptors.size(); ++i) { - FieldDescriptor fd = fieldDescriptors.get(i); -- if (fd.getContainingOneof() != null) { -- // Build a oneof member field. -+ if (fd.getContainingOneof() != null && !fd.getContainingOneof().isSynthetic()) { -+ // Build a oneof member field. But only if it is a real oneof, not a proto3 optional - builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8, null)); - continue; - } -diff --git a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java -index 8beebba..51e6b0c 100644 ---- a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java -+++ b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java -@@ -421,7 +421,10 @@ public final class DynamicMessage extends AbstractMessage { - fields.makeImmutable(); - DynamicMessage result = - new DynamicMessage( -- type, fields, java.util.Arrays.copyOf(oneofCases, oneofCases.length), unknownFields); -+ type, -+ fields, -+ java.util.Arrays.copyOf(oneofCases, oneofCases.length), -+ unknownFields); - return result; - } - -diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionSchema.java b/java/core/src/main/java/com/google/protobuf/ExtensionSchema.java -index 2eae22d..bd391a2 100644 ---- a/java/core/src/main/java/com/google/protobuf/ExtensionSchema.java -+++ b/java/core/src/main/java/com/google/protobuf/ExtensionSchema.java -@@ -59,6 +59,7 @@ abstract class ExtensionSchema> { - * or UnknownFieldSetLite in lite runtime. - */ - abstract UB parseExtension( -+ Object containerMessage, - Reader reader, - Object extension, - ExtensionRegistryLite extensionRegistry, -diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java b/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java -index 9055851..9376e87 100644 ---- a/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java -+++ b/java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java -@@ -85,6 +85,7 @@ final class ExtensionSchemaFull extends ExtensionSchema { - - @Override - UB parseExtension( -+ Object containerMessage, - Reader reader, - Object extensionObject, - ExtensionRegistryLite extensionRegistry, -@@ -202,7 +203,7 @@ final class ExtensionSchemaFull extends ExtensionSchema { - } else { - unknownFields = - SchemaUtil.storeUnknownEnum( -- fieldNumber, number, unknownFields, unknownFieldSchema); -+ containerMessage, fieldNumber, number, unknownFields, unknownFieldSchema); - } - } - value = enumList; -@@ -221,7 +222,7 @@ final class ExtensionSchemaFull extends ExtensionSchema { - Object enumValue = extension.descriptor.getEnumType().findValueByNumber(number); - if (enumValue == null) { - return SchemaUtil.storeUnknownEnum( -- fieldNumber, number, unknownFields, unknownFieldSchema); -+ containerMessage, fieldNumber, number, unknownFields, unknownFieldSchema); - } - value = enumValue; - } else { -diff --git a/java/core/src/main/java/com/google/protobuf/ExtensionSchemaLite.java b/java/core/src/main/java/com/google/protobuf/ExtensionSchemaLite.java -index 437cca2..7e20ed2 100644 ---- a/java/core/src/main/java/com/google/protobuf/ExtensionSchemaLite.java -+++ b/java/core/src/main/java/com/google/protobuf/ExtensionSchemaLite.java -@@ -32,7 +32,6 @@ package com.google.protobuf; - - import com.google.protobuf.GeneratedMessageLite.ExtensionDescriptor; - import java.io.IOException; --import java.nio.ByteBuffer; - import java.util.ArrayList; - import java.util.List; - import java.util.Map; -@@ -67,6 +66,7 @@ final class ExtensionSchemaLite extends ExtensionSchema { - - @Override - UB parseExtension( -+ Object containerMessage, - Reader reader, - Object extensionObject, - ExtensionRegistryLite extensionRegistry, -@@ -178,6 +178,7 @@ final class ExtensionSchemaLite extends ExtensionSchema { - reader.readEnumList(list); - unknownFields = - SchemaUtil.filterUnknownEnumList( -+ containerMessage, - fieldNumber, - list, - extension.descriptor.getEnumType(), -@@ -199,7 +200,7 @@ final class ExtensionSchemaLite extends ExtensionSchema { - Object enumValue = extension.descriptor.getEnumType().findValueByNumber(number); - if (enumValue == null) { - return SchemaUtil.storeUnknownEnum( -- fieldNumber, number, unknownFields, unknownFieldSchema); -+ containerMessage, fieldNumber, number, unknownFields, unknownFieldSchema); - } - // Note, we store the integer value instead of the actual enum object in FieldSet. - // This is also different from full-runtime where we store EnumValueDescriptor. -@@ -253,12 +254,46 @@ final class ExtensionSchemaLite extends ExtensionSchema { - value = reader.readString(); - break; - case GROUP: -+ // Special case handling for non-repeated sub-messages: merge in-place rather than -+ // building up new sub-messages and merging those, which is too slow. -+ // TODO(b/249368670): clean this up -+ if (!extension.isRepeated()) { -+ Object oldValue = extensions.getField(extension.descriptor); -+ if (oldValue instanceof GeneratedMessageLite) { -+ Schema extSchema = Protobuf.getInstance().schemaFor(oldValue); -+ if (!((GeneratedMessageLite) oldValue).isMutable()) { -+ Object newValue = extSchema.newInstance(); -+ extSchema.mergeFrom(newValue, oldValue); -+ extensions.setField(extension.descriptor, newValue); -+ oldValue = newValue; -+ } -+ reader.mergeGroupField(oldValue, extSchema, extensionRegistry); -+ return unknownFields; -+ } -+ } - value = - reader.readGroup( - extension.getMessageDefaultInstance().getClass(), extensionRegistry); - break; - - case MESSAGE: -+ // Special case handling for non-repeated sub-messages: merge in-place rather than -+ // building up new sub-messages and merging those, which is too slow. -+ // TODO(b/249368670): clean this up -+ if (!extension.isRepeated()) { -+ Object oldValue = extensions.getField(extension.descriptor); -+ if (oldValue instanceof GeneratedMessageLite) { -+ Schema extSchema = Protobuf.getInstance().schemaFor(oldValue); -+ if (!((GeneratedMessageLite) oldValue).isMutable()) { -+ Object newValue = extSchema.newInstance(); -+ extSchema.mergeFrom(newValue, oldValue); -+ extensions.setField(extension.descriptor, newValue); -+ oldValue = newValue; -+ } -+ reader.mergeMessageField(oldValue, extSchema, extensionRegistry); -+ return unknownFields; -+ } -+ } - value = - reader.readMessage( - extension.getMessageDefaultInstance().getClass(), extensionRegistry); -@@ -274,6 +309,7 @@ final class ExtensionSchemaLite extends ExtensionSchema { - switch (extension.getLiteType()) { - case MESSAGE: - case GROUP: -+ // TODO(b/249368670): this shouldn't be reachable, clean this up - Object oldValue = extensions.getField(extension.descriptor); - if (oldValue != null) { - value = Internal.mergeMessage(oldValue, value); -@@ -527,15 +563,13 @@ final class ExtensionSchemaLite extends ExtensionSchema { - throws IOException { - GeneratedMessageLite.GeneratedExtension extension = - (GeneratedMessageLite.GeneratedExtension) extensionObject; -- Object value = extension.getMessageDefaultInstance().newBuilderForType().buildPartial(); - -- Reader reader = BinaryReader.newInstance(ByteBuffer.wrap(data.toByteArray()), true); -+ MessageLite.Builder builder = extension.getMessageDefaultInstance().newBuilderForType(); - -- Protobuf.getInstance().mergeFrom(value, reader, extensionRegistry); -- extensions.setField(extension.descriptor, value); -+ final CodedInputStream input = data.newCodedInput(); - -- if (reader.getFieldNumber() != Reader.READ_DONE) { -- throw InvalidProtocolBufferException.invalidEndTag(); -- } -+ builder.mergeFrom(input, extensionRegistry); -+ extensions.setField(extension.descriptor, builder.buildPartial()); -+ input.checkLastTagWas(0); - } - } -diff --git a/java/core/src/main/java/com/google/protobuf/FieldSet.java b/java/core/src/main/java/com/google/protobuf/FieldSet.java -index d52aede..e30cf90 100644 ---- a/java/core/src/main/java/com/google/protobuf/FieldSet.java -+++ b/java/core/src/main/java/com/google/protobuf/FieldSet.java -@@ -39,6 +39,7 @@ import java.util.Collections; - import java.util.Iterator; - import java.util.List; - import java.util.Map; -+import java.util.Map.Entry; - - /** - * A class which represents an arbitrary set of fields of some message type. This is used to -@@ -124,6 +125,12 @@ final class FieldSet> { - if (isImmutable) { - return; - } -+ for (int i = 0; i < fields.getNumArrayEntries(); ++i) { -+ Entry entry = fields.getArrayEntryAt(i); -+ if (entry.getValue() instanceof GeneratedMessageLite) { -+ ((GeneratedMessageLite) entry.getValue()).makeImmutable(); -+ } -+ } - fields.makeImmutable(); - isImmutable = true; - } -@@ -933,8 +940,27 @@ final class FieldSet> { - this.isMutable = true; - } - -- /** Creates the FieldSet */ -+ /** -+ * Creates the FieldSet -+ * -+ * @throws UninitializedMessageException if a message field is missing required fields. -+ */ - public FieldSet build() { -+ return buildImpl(false); -+ } -+ -+ /** Creates the FieldSet but does not validate that all required fields are present. */ -+ public FieldSet buildPartial() { -+ return buildImpl(true); -+ } -+ -+ /** -+ * Creates the FieldSet. -+ * -+ * @param partial controls whether to do a build() or buildPartial() when converting submessage -+ * builders to messages. -+ */ -+ private FieldSet buildImpl(boolean partial) { - if (fields.isEmpty()) { - return FieldSet.emptySet(); - } -@@ -943,7 +969,7 @@ final class FieldSet> { - if (hasNestedBuilders) { - // Make a copy of the fields map with all Builders replaced by Message. - fieldsForBuild = cloneAllFieldsMap(fields, /* copyList */ false); -- replaceBuilders(fieldsForBuild); -+ replaceBuilders(fieldsForBuild, partial); - } - FieldSet fieldSet = new FieldSet<>(fieldsForBuild); - fieldSet.hasLazyField = hasLazyField; -@@ -951,22 +977,22 @@ final class FieldSet> { - } - - private static > void replaceBuilders( -- SmallSortedMap fieldMap) { -+ SmallSortedMap fieldMap, boolean partial) { - for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { -- replaceBuilders(fieldMap.getArrayEntryAt(i)); -+ replaceBuilders(fieldMap.getArrayEntryAt(i), partial); - } - for (Map.Entry entry : fieldMap.getOverflowEntries()) { -- replaceBuilders(entry); -+ replaceBuilders(entry, partial); - } - } - - private static > void replaceBuilders( -- Map.Entry entry) { -- entry.setValue(replaceBuilders(entry.getKey(), entry.getValue())); -+ Map.Entry entry, boolean partial) { -+ entry.setValue(replaceBuilders(entry.getKey(), entry.getValue(), partial)); - } - - private static > Object replaceBuilders( -- T descriptor, Object value) { -+ T descriptor, Object value, boolean partial) { - if (value == null) { - return value; - } -@@ -981,7 +1007,7 @@ final class FieldSet> { - List list = (List) value; - for (int i = 0; i < list.size(); i++) { - Object oldElement = list.get(i); -- Object newElement = replaceBuilder(oldElement); -+ Object newElement = replaceBuilder(oldElement, partial); - if (newElement != oldElement) { - // If the list contains a Message.Builder, then make a copy of that list and then - // modify the Message.Builder into a Message and return the new list. This way, the -@@ -995,14 +1021,21 @@ final class FieldSet> { - } - return list; - } else { -- return replaceBuilder(value); -+ return replaceBuilder(value, partial); - } - } - return value; - } - -- private static Object replaceBuilder(Object value) { -- return (value instanceof MessageLite.Builder) ? ((MessageLite.Builder) value).build() : value; -+ private static Object replaceBuilder(Object value, boolean partial) { -+ if (!(value instanceof MessageLite.Builder)) { -+ return value; -+ } -+ MessageLite.Builder builder = (MessageLite.Builder) value; -+ if (partial) { -+ return builder.buildPartial(); -+ } -+ return builder.build(); - } - - /** Returns a new Builder using the fields from {@code fieldSet}. */ -@@ -1021,7 +1054,7 @@ final class FieldSet> { - if (fields.isImmutable()) { - result.makeImmutable(); - } else { -- replaceBuilders(result); -+ replaceBuilders(result, true); - } - return result; - } -@@ -1044,7 +1077,7 @@ final class FieldSet> { - */ - public Object getField(final T descriptor) { - Object value = getFieldAllowBuilders(descriptor); -- return replaceBuilders(descriptor, value); -+ return replaceBuilders(descriptor, value, true); - } - - /** Same as {@link #getField(F)}, but allow a {@link MessageLite.Builder} to be returned. */ -@@ -1131,7 +1164,7 @@ final class FieldSet> { - ensureIsMutable(); - } - Object value = getRepeatedFieldAllowBuilders(descriptor, index); -- return replaceBuilder(value); -+ return replaceBuilder(value, true); - } - - /** -diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java -index 27f5210..943f6ee 100644 ---- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java -+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java -@@ -62,11 +62,50 @@ public abstract class GeneratedMessageLite< - BuilderType extends GeneratedMessageLite.Builder> - extends AbstractMessageLite { - -+ /* For use by lite runtime only */ -+ static final int UNINITIALIZED_SERIALIZED_SIZE = 0x7FFFFFFF; -+ private static final int MUTABLE_FLAG_MASK = 0x80000000; -+ private static final int MEMOIZED_SERIALIZED_SIZE_MASK = 0x7FFFFFFF; -+ -+ /** -+ * We use the high bit of memoizedSerializedSize as the explicit mutability flag. It didn't make -+ * sense to have negative sizes anyway. Messages start as mutable. -+ * -+ *

Adding a standalone boolean would have added 8 bytes to every message instance. -+ * -+ *

We also reserve 0x7FFFFFFF as the "uninitialized" value. -+ */ -+ private int memoizedSerializedSize = MUTABLE_FLAG_MASK | UNINITIALIZED_SERIALIZED_SIZE; -+ -+ /* For use by the runtime only */ -+ static final int UNINITIALIZED_HASH_CODE = 0; -+ - /** For use by generated code only. Lazily initialized to reduce allocations. */ - protected UnknownFieldSetLite unknownFields = UnknownFieldSetLite.getDefaultInstance(); - -- /** For use by generated code only. */ -- protected int memoizedSerializedSize = -1; -+ boolean isMutable() { -+ return (memoizedSerializedSize & MUTABLE_FLAG_MASK) != 0; -+ } -+ -+ void markImmutable() { -+ memoizedSerializedSize &= ~MUTABLE_FLAG_MASK; -+ } -+ -+ int getMemoizedHashCode() { -+ return memoizedHashCode; -+ } -+ -+ void setMemoizedHashCode(int value) { -+ memoizedHashCode = value; -+ } -+ -+ void clearMemoizedHashCode() { -+ memoizedHashCode = UNINITIALIZED_HASH_CODE; -+ } -+ -+ boolean hashCodeIsNotMemoized() { -+ return UNINITIALIZED_HASH_CODE == getMemoizedHashCode(); -+ } - - @Override - @SuppressWarnings("unchecked") // Guaranteed by runtime. -@@ -86,6 +125,10 @@ public abstract class GeneratedMessageLite< - return (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); - } - -+ MessageType newMutableInstance() { -+ return (MessageType) dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); -+ } -+ - /** - * A reflective toString function. This is primarily intended as a developer aid, while keeping - * binary size down. The first line of the {@code toString()} representation includes a commented -@@ -106,11 +149,19 @@ public abstract class GeneratedMessageLite< - @SuppressWarnings("unchecked") // Guaranteed by runtime - @Override - public int hashCode() { -- if (memoizedHashCode != 0) { -- return memoizedHashCode; -+ if (isMutable()) { -+ return computeHashCode(); - } -- memoizedHashCode = Protobuf.getInstance().schemaFor(this).hashCode(this); -- return memoizedHashCode; -+ -+ if (hashCodeIsNotMemoized()) { -+ setMemoizedHashCode(computeHashCode()); -+ } -+ -+ return getMemoizedHashCode(); -+ } -+ -+ int computeHashCode() { -+ return Protobuf.getInstance().schemaFor(this).hashCode(this); - } - - @SuppressWarnings("unchecked") // Guaranteed by isInstance + runtime -@@ -173,6 +224,7 @@ public abstract class GeneratedMessageLite< - /** Called by subclasses to complete parsing. For use by generated code only. */ - protected void makeImmutable() { - Protobuf.getInstance().schemaFor(this).makeImmutable(this); -+ markImmutable(); - } - - protected final < -@@ -198,8 +250,7 @@ public abstract class GeneratedMessageLite< - @SuppressWarnings("unchecked") - public final BuilderType toBuilder() { - BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); -- builder.mergeFrom((MessageType) this); -- return builder; -+ return builder.mergeFrom((MessageType) this); - } - - /** -@@ -256,27 +307,67 @@ public abstract class GeneratedMessageLite< - return dynamicMethod(method, null, null); - } - -+ void clearMemoizedSerializedSize() { -+ setMemoizedSerializedSize(UNINITIALIZED_SERIALIZED_SIZE); -+ } -+ - @Override - int getMemoizedSerializedSize() { -- return memoizedSerializedSize; -+ return memoizedSerializedSize & MEMOIZED_SERIALIZED_SIZE_MASK; - } - - @Override - void setMemoizedSerializedSize(int size) { -- memoizedSerializedSize = size; -+ if (size < 0) { -+ throw new IllegalStateException("serialized size must be non-negative, was " + size); -+ } -+ memoizedSerializedSize = -+ (memoizedSerializedSize & MUTABLE_FLAG_MASK) | (size & MEMOIZED_SERIALIZED_SIZE_MASK); - } - -+ @Override - public void writeTo(CodedOutputStream output) throws IOException { - Protobuf.getInstance() - .schemaFor(this) - .writeTo(this, CodedOutputStreamWriter.forCodedOutput(output)); - } - -+ @Override -+ int getSerializedSize(Schema schema) { -+ if (isMutable()) { -+ // The serialized size should never be memoized for mutable instances. -+ int size = computeSerializedSize(schema); -+ if (size < 0) { -+ throw new IllegalStateException("serialized size must be non-negative, was " + size); -+ } -+ return size; -+ } -+ -+ // If memoizedSerializedSize has already been set, return it. -+ if (getMemoizedSerializedSize() != UNINITIALIZED_SERIALIZED_SIZE) { -+ return getMemoizedSerializedSize(); -+ } -+ -+ // Need to compute and memoize the serialized size. -+ int size = computeSerializedSize(schema); -+ setMemoizedSerializedSize(size); -+ return size; -+ } -+ -+ @Override - public int getSerializedSize() { -- if (memoizedSerializedSize == -1) { -- memoizedSerializedSize = Protobuf.getInstance().schemaFor(this).getSerializedSize(this); -+ // Calling this with 'null' to delay schema lookup in case the serializedSize is already -+ // memoized. -+ return getSerializedSize(null); - } -- return memoizedSerializedSize; -+ -+ private int computeSerializedSize(Schema nullableSchema) { -+ if (nullableSchema == null) { -+ return Protobuf.getInstance().schemaFor(this).getSerializedSize(this); -+ } else { -+ return ((Schema>) nullableSchema) -+ .getSerializedSize(this); -+ } - } - - /** Constructs a {@link MessageInfo} for this message type. */ -@@ -316,6 +407,7 @@ public abstract class GeneratedMessageLite< - protected static > void registerDefaultInstance( - Class clazz, T defaultInstance) { - defaultInstanceMap.put(clazz, defaultInstance); -+ defaultInstance.makeImmutable(); - } - - protected static Object newMessageInfo( -@@ -340,13 +432,19 @@ public abstract class GeneratedMessageLite< - - private final MessageType defaultInstance; - protected MessageType instance; -- protected boolean isBuilt; - - protected Builder(MessageType defaultInstance) { - this.defaultInstance = defaultInstance; -- this.instance = -- (MessageType) defaultInstance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); -- isBuilt = false; -+ if (defaultInstance.isMutable()) { -+ throw new IllegalArgumentException("Default instance must be immutable."); -+ } -+ // this.instance should be set to defaultInstance but some tests rely on newBuilder().build() -+ // creating unique instances. -+ this.instance = newMutableInstance(); -+ } -+ -+ private MessageType newMutableInstance() { -+ return defaultInstance.newMutableInstance(); - } - - /** -@@ -354,15 +452,13 @@ public abstract class GeneratedMessageLite< - * state before the write happens to preserve immutability guarantees. - */ - protected final void copyOnWrite() { -- if (isBuilt) { -+ if (!instance.isMutable()) { - copyOnWriteInternal(); -- isBuilt = false; - } - } - - protected void copyOnWriteInternal() { -- MessageType newInstance = -- (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); -+ MessageType newInstance = newMutableInstance(); - mergeFromInstance(newInstance, instance); - instance = newInstance; - } -@@ -374,27 +470,28 @@ public abstract class GeneratedMessageLite< - - @Override - public final BuilderType clear() { -- // No need to copy on write since we're dropping the instance anyways. -- instance = (MessageType) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); -+ // No need to copy on write since we're dropping the instance anyway. -+ if (defaultInstance.isMutable()) { -+ throw new IllegalArgumentException("Default instance must be immutable."); -+ } -+ instance = newMutableInstance(); // should be defaultInstance; - return (BuilderType) this; - } - - @Override - public BuilderType clone() { - BuilderType builder = (BuilderType) getDefaultInstanceForType().newBuilderForType(); -- builder.mergeFrom(buildPartial()); -+ builder.instance = buildPartial(); - return builder; - } - - @Override - public MessageType buildPartial() { -- if (isBuilt) { -+ if (!instance.isMutable()) { - return instance; - } - - instance.makeImmutable(); -- -- isBuilt = true; - return instance; - } - -@@ -414,12 +511,15 @@ public abstract class GeneratedMessageLite< - - /** All subclasses implement this. */ - public BuilderType mergeFrom(MessageType message) { -+ if (getDefaultInstanceForType().equals(message)) { -+ return (BuilderType) this; -+ } - copyOnWrite(); - mergeFromInstance(instance, message); - return (BuilderType) this; - } - -- private void mergeFromInstance(MessageType dest, MessageType src) { -+ private static void mergeFromInstance(MessageType dest, MessageType src) { - Protobuf.getInstance().schemaFor(dest).mergeFrom(dest, src); - } - -@@ -930,7 +1030,9 @@ public abstract class GeneratedMessageLite< - @Override - protected void copyOnWriteInternal() { - super.copyOnWriteInternal(); -- instance.extensions = instance.extensions.clone(); -+ if (instance.extensions != FieldSet.emptySet()) { -+ instance.extensions = instance.extensions.clone(); -+ } - } - - private FieldSet ensureExtensionsAreMutable() { -@@ -944,7 +1046,7 @@ public abstract class GeneratedMessageLite< - - @Override - public final MessageType buildPartial() { -- if (isBuilt) { -+ if (!instance.isMutable()) { - return instance; - } - -@@ -1528,7 +1630,7 @@ public abstract class GeneratedMessageLite< - T instance, CodedInputStream input, ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - @SuppressWarnings("unchecked") // Guaranteed by protoc -- T result = (T) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); -+ T result = instance.newMutableInstance(); - try { - // TODO(yilunchong): Try to make input with type CodedInpuStream.ArrayDecoder use - // fast path. -@@ -1554,15 +1656,12 @@ public abstract class GeneratedMessageLite< - T instance, byte[] input, int offset, int length, ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { - @SuppressWarnings("unchecked") // Guaranteed by protoc -- T result = (T) instance.dynamicMethod(MethodToInvoke.NEW_MUTABLE_INSTANCE); -+ T result = instance.newMutableInstance(); - try { - Schema schema = Protobuf.getInstance().schemaFor(result); - schema.mergeFrom( - result, input, offset, offset + length, new ArrayDecoders.Registers(extensionRegistry)); - schema.makeImmutable(result); -- if (result.memoizedHashCode != 0) { -- throw new RuntimeException(); -- } - } catch (IOException e) { - if (e.getCause() instanceof InvalidProtocolBufferException) { - throw (InvalidProtocolBufferException) e.getCause(); -diff --git a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java -index 86f88a0..e322604 100644 ---- a/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java -+++ b/java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java -@@ -133,6 +133,10 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - return internalGetFieldAccessorTable().descriptor; - } - -+ // TODO(b/248143958): This method should be removed. It enables parsing directly into an -+ // "immutable" message. Have to leave it for now to support old gencode. -+ // @deprecated use newBuilder().mergeFrom() instead -+ @Deprecated - protected void mergeFromAndMakeImmutableInternal( - CodedInputStream input, ExtensionRegistryLite extensionRegistry) - throws InvalidProtocolBufferException { -@@ -299,13 +303,14 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - - @Override - public UnknownFieldSet getUnknownFields() { -- throw new UnsupportedOperationException( -- "This is supposed to be overridden by subclasses."); -+ return unknownFields; - } - - /** - * Called by subclasses to parse an unknown field. - * -+ *

TODO(b/248153893) remove this method -+ * - * @return {@code true} unless the tag is an end-group tag. - */ - protected boolean parseUnknownField( -@@ -323,6 +328,8 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - /** - * Delegates to parseUnknownField. This method is obsolete, but we must retain it for - * compatibility with older generated code. -+ * -+ *

TODO(b/248153893) remove this method - */ - protected boolean parseUnknownFieldProto3( - CodedInputStream input, -@@ -547,8 +554,18 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - // to dispatch dirty invalidations. See GeneratedMessageV3.BuilderListener. - private boolean isClean; - -- private UnknownFieldSet unknownFields = -- UnknownFieldSet.getDefaultInstance(); -+ /** -+ * This field holds either an {@link UnknownFieldSet} or {@link UnknownFieldSet.Builder}. -+ * -+ *

We use an object because it should only be one or the other of those things at a time and -+ * Object is the only common base. This also saves space. -+ * -+ *

Conversions are lazy: if {@link #setUnknownFields} is called, this will contain {@link -+ * UnknownFieldSet}. If unknown fields are merged into this builder, the current {@link -+ * UnknownFieldSet} will be converted to a {@link UnknownFieldSet.Builder} and left that way -+ * until either {@link #setUnknownFields} or {@link #buildPartial} or {@link #build} is called. -+ */ -+ private Object unknownFieldsOrBuilder = UnknownFieldSet.getDefaultInstance(); - - protected Builder() { - this(null); -@@ -604,7 +621,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - */ - @Override - public BuilderType clear() { -- unknownFields = UnknownFieldSet.getDefaultInstance(); -+ unknownFieldsOrBuilder = UnknownFieldSet.getDefaultInstance(); - onChanged(); - return (BuilderType) this; - } -@@ -757,7 +774,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - } - - private BuilderType setUnknownFieldsInternal(final UnknownFieldSet unknownFields) { -- this.unknownFields = unknownFields; -+ unknownFieldsOrBuilder = unknownFields; - onChanged(); - return (BuilderType) this; - } -@@ -776,12 +793,20 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - } - - @Override -- public BuilderType mergeUnknownFields( -- final UnknownFieldSet unknownFields) { -- return setUnknownFields( -- UnknownFieldSet.newBuilder(this.unknownFields) -- .mergeFrom(unknownFields) -- .build()); -+ public BuilderType mergeUnknownFields(final UnknownFieldSet unknownFields) { -+ if (UnknownFieldSet.getDefaultInstance().equals(unknownFields)) { -+ return (BuilderType) this; -+ } -+ -+ if (UnknownFieldSet.getDefaultInstance().equals(unknownFieldsOrBuilder)) { -+ unknownFieldsOrBuilder = unknownFields; -+ onChanged(); -+ return (BuilderType) this; -+ } -+ -+ getUnknownFieldSetBuilder().mergeFrom(unknownFields); -+ onChanged(); -+ return (BuilderType) this; - } - - -@@ -817,7 +842,50 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - - @Override - public final UnknownFieldSet getUnknownFields() { -- return unknownFields; -+ if (unknownFieldsOrBuilder instanceof UnknownFieldSet) { -+ return (UnknownFieldSet) unknownFieldsOrBuilder; -+ } else { -+ return ((UnknownFieldSet.Builder) unknownFieldsOrBuilder).buildPartial(); -+ } -+ } -+ -+ /** -+ * Called by generated subclasses to parse an unknown field. -+ * -+ * @return {@code true} unless the tag is an end-group tag. -+ */ -+ protected boolean parseUnknownField( -+ CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag) -+ throws IOException { -+ if (input.shouldDiscardUnknownFields()) { -+ return input.skipField(tag); -+ } -+ return getUnknownFieldSetBuilder().mergeFieldFrom(tag, input); -+ } -+ -+ /** Called by generated subclasses to add to the unknown field set. */ -+ protected final void mergeUnknownLengthDelimitedField(int number, ByteString bytes) { -+ getUnknownFieldSetBuilder().mergeLengthDelimitedField(number, bytes); -+ } -+ -+ /** Called by generated subclasses to add to the unknown field set. */ -+ protected final void mergeUnknownVarintField(int number, int value) { -+ getUnknownFieldSetBuilder().mergeVarintField(number, value); -+ } -+ -+ @Override -+ protected UnknownFieldSet.Builder getUnknownFieldSetBuilder() { -+ if (unknownFieldsOrBuilder instanceof UnknownFieldSet) { -+ unknownFieldsOrBuilder = ((UnknownFieldSet) unknownFieldsOrBuilder).toBuilder(); -+ } -+ onChanged(); -+ return (UnknownFieldSet.Builder) unknownFieldsOrBuilder; -+ } -+ -+ @Override -+ protected void setUnknownFieldSetBuilder(UnknownFieldSet.Builder builder) { -+ unknownFieldsOrBuilder = builder; -+ onChanged(); - } - - /** -@@ -1609,7 +1677,7 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - private FieldSet buildExtensions() { - return extensions == null - ? (FieldSet) FieldSet.emptySet() -- : extensions.build(); -+ : extensions.buildPartial(); - } - - @Override -@@ -1815,6 +1883,20 @@ public abstract class GeneratedMessageV3 extends AbstractMessage - } - } - -+ @Override -+ protected boolean parseUnknownField( -+ CodedInputStream input, ExtensionRegistryLite extensionRegistry, int tag) -+ throws IOException { -+ ensureExtensionsIsMutable(); -+ return MessageReflection.mergeFieldFrom( -+ input, -+ input.shouldDiscardUnknownFields() ? null : getUnknownFieldSetBuilder(), -+ extensionRegistry, -+ getDescriptorForType(), -+ new MessageReflection.ExtensionBuilderAdapter(extensions), -+ tag); -+ } -+ - private void verifyContainingType(final FieldDescriptor field) { - if (field.getContainingType() != getDescriptorForType()) { - throw new IllegalArgumentException( -diff --git a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java -index 4aea952..3a3a70f 100644 ---- a/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java -+++ b/java/core/src/main/java/com/google/protobuf/MessageLiteToString.java -@@ -32,12 +32,15 @@ package com.google.protobuf; - - import java.lang.reflect.Method; - import java.lang.reflect.Modifier; -+import java.util.Arrays; - import java.util.HashMap; -+import java.util.HashSet; - import java.util.Iterator; - import java.util.List; - import java.util.Map; -+import java.util.Map.Entry; - import java.util.Set; --import java.util.TreeSet; -+import java.util.TreeMap; - - /** Helps generate {@link String} representations of {@link MessageLite} protos. */ - final class MessageLiteToString { -@@ -46,6 +49,11 @@ final class MessageLiteToString { - private static final String BUILDER_LIST_SUFFIX = "OrBuilderList"; - private static final String MAP_SUFFIX = "Map"; - private static final String BYTES_SUFFIX = "Bytes"; -+ private static final char[] INDENT_BUFFER = new char[80]; -+ -+ static { -+ Arrays.fill(INDENT_BUFFER, ' '); -+ } - - /** - * Returns a {@link String} representation of the {@link MessageLite} object. The first line of -@@ -73,37 +81,51 @@ final class MessageLiteToString { - // Build a map of method name to method. We're looking for methods like getFoo(), hasFoo(), - // getFooList() and getFooMap() which might be useful for building an object's string - // representation. -- Map nameToNoArgMethod = new HashMap(); -- Map nameToMethod = new HashMap(); -- Set getters = new TreeSet(); -+ Set setters = new HashSet<>(); -+ Map hazzers = new HashMap<>(); -+ Map getters = new TreeMap<>(); - for (Method method : messageLite.getClass().getDeclaredMethods()) { -- nameToMethod.put(method.getName(), method); -- if (method.getParameterTypes().length == 0) { -- nameToNoArgMethod.put(method.getName(), method); -+ if (Modifier.isStatic(method.getModifiers())) { -+ continue; -+ } -+ if (method.getName().length() < 3) { -+ continue; -+ } - -- if (method.getName().startsWith("get")) { -- getters.add(method.getName()); -- } -+ if (method.getName().startsWith("set")) { -+ setters.add(method.getName()); -+ continue; -+ } -+ -+ if (!Modifier.isPublic(method.getModifiers())) { -+ continue; -+ } -+ -+ if (method.getParameterTypes().length != 0) { -+ continue; -+ } -+ -+ if (method.getName().startsWith("has")) { -+ hazzers.put(method.getName(), method); -+ } else if (method.getName().startsWith("get")) { -+ getters.put(method.getName(), method); - } - } - -- for (String getter : getters) { -- String suffix = getter.startsWith("get") ? getter.substring(3) : getter; -+ for (Entry getter : getters.entrySet()) { -+ String suffix = getter.getKey().substring(3); - if (suffix.endsWith(LIST_SUFFIX) - && !suffix.endsWith(BUILDER_LIST_SUFFIX) - // Sometimes people have fields named 'list' that aren't repeated. - && !suffix.equals(LIST_SUFFIX)) { -- String camelCase = -- suffix.substring(0, 1).toLowerCase() -- + suffix.substring(1, suffix.length() - LIST_SUFFIX.length()); - // Try to reflectively get the value and toString() the field as if it were repeated. This - // only works if the method names have not been proguarded out or renamed. -- Method listMethod = nameToNoArgMethod.get(getter); -+ Method listMethod = getter.getValue(); - if (listMethod != null && listMethod.getReturnType().equals(List.class)) { - printField( - buffer, - indent, -- camelCaseToSnakeCase(camelCase), -+ suffix.substring(0, suffix.length() - LIST_SUFFIX.length()), - GeneratedMessageLite.invokeOrDie(listMethod, messageLite)); - continue; - } -@@ -111,12 +133,9 @@ final class MessageLiteToString { - if (suffix.endsWith(MAP_SUFFIX) - // Sometimes people have fields named 'map' that aren't maps. - && !suffix.equals(MAP_SUFFIX)) { -- String camelCase = -- suffix.substring(0, 1).toLowerCase() -- + suffix.substring(1, suffix.length() - MAP_SUFFIX.length()); - // Try to reflectively get the value and toString() the field as if it were a map. This only - // works if the method names have not been proguarded out or renamed. -- Method mapMethod = nameToNoArgMethod.get(getter); -+ Method mapMethod = getter.getValue(); - if (mapMethod != null - && mapMethod.getReturnType().equals(Map.class) - // Skip the deprecated getter method with no prefix "Map" when the field name ends with -@@ -127,29 +146,25 @@ final class MessageLiteToString { - printField( - buffer, - indent, -- camelCaseToSnakeCase(camelCase), -+ suffix.substring(0, suffix.length() - MAP_SUFFIX.length()), - GeneratedMessageLite.invokeOrDie(mapMethod, messageLite)); - continue; - } - } - -- Method setter = nameToMethod.get("set" + suffix); -- if (setter == null) { -+ if (!setters.contains("set" + suffix)) { - continue; - } - if (suffix.endsWith(BYTES_SUFFIX) -- && nameToNoArgMethod.containsKey( -- "get" + suffix.substring(0, suffix.length() - "Bytes".length()))) { -+ && getters.containsKey("get" + suffix.substring(0, suffix.length() - "Bytes".length()))) { - // Heuristic to skip bytes based accessors for string fields. - continue; - } - -- String camelCase = suffix.substring(0, 1).toLowerCase() + suffix.substring(1); -- - // Try to reflectively get the value and toString() the field as if it were optional. This - // only works if the method names have not been proguarded out or renamed. -- Method getMethod = nameToNoArgMethod.get("get" + suffix); -- Method hasMethod = nameToNoArgMethod.get("has" + suffix); -+ Method getMethod = getter.getValue(); -+ Method hasMethod = hazzers.get("has" + suffix); - // TODO(dweis): Fix proto3 semantics. - if (getMethod != null) { - Object value = GeneratedMessageLite.invokeOrDie(getMethod, messageLite); -@@ -159,7 +174,7 @@ final class MessageLiteToString { - : (Boolean) GeneratedMessageLite.invokeOrDie(hasMethod, messageLite); - // TODO(dweis): This doesn't stop printing oneof case twice: value and enum style. - if (hasValue) { -- printField(buffer, indent, camelCaseToSnakeCase(camelCase), value); -+ printField(buffer, indent, suffix, value); - } - continue; - } -@@ -215,10 +230,10 @@ final class MessageLiteToString { - * - * @param buffer the buffer to write to - * @param indent the number of spaces the proto should be indented by -- * @param name the field name (in lower underscore case) -+ * @param name the field name (in PascalCase) - * @param object the object value of the field - */ -- static final void printField(StringBuilder buffer, int indent, String name, Object object) { -+ static void printField(StringBuilder buffer, int indent, String name, Object object) { - if (object instanceof List) { - List list = (List) object; - for (Object entry : list) { -@@ -235,10 +250,8 @@ final class MessageLiteToString { - } - - buffer.append('\n'); -- for (int i = 0; i < indent; i++) { -- buffer.append(' '); -- } -- buffer.append(name); -+ indent(indent, buffer); -+ buffer.append(pascalCaseToSnakeCase(name)); - - if (object instanceof String) { - buffer.append(": \"").append(TextFormatEscaper.escapeText((String) object)).append('"'); -@@ -248,9 +261,7 @@ final class MessageLiteToString { - buffer.append(" {"); - reflectivePrintWithIndent((GeneratedMessageLite) object, buffer, indent + 2); - buffer.append("\n"); -- for (int i = 0; i < indent; i++) { -- buffer.append(' '); -- } -+ indent(indent, buffer); - buffer.append("}"); - } else if (object instanceof Map.Entry) { - buffer.append(" {"); -@@ -258,19 +269,33 @@ final class MessageLiteToString { - printField(buffer, indent + 2, "key", entry.getKey()); - printField(buffer, indent + 2, "value", entry.getValue()); - buffer.append("\n"); -- for (int i = 0; i < indent; i++) { -- buffer.append(' '); -- } -+ indent(indent, buffer); - buffer.append("}"); - } else { - buffer.append(": ").append(object.toString()); - } - } - -- private static final String camelCaseToSnakeCase(String camelCase) { -+ private static void indent(int indent, StringBuilder buffer) { -+ while (indent > 0) { -+ int partialIndent = indent; -+ if (partialIndent > INDENT_BUFFER.length) { -+ partialIndent = INDENT_BUFFER.length; -+ } -+ buffer.append(INDENT_BUFFER, 0, partialIndent); -+ indent -= partialIndent; -+ } -+ } -+ -+ private static String pascalCaseToSnakeCase(String pascalCase) { -+ if (pascalCase.isEmpty()) { -+ return pascalCase; -+ } -+ - StringBuilder builder = new StringBuilder(); -- for (int i = 0; i < camelCase.length(); i++) { -- char ch = camelCase.charAt(i); -+ builder.append(Character.toLowerCase(pascalCase.charAt(0))); -+ for (int i = 1; i < pascalCase.length(); i++) { -+ char ch = pascalCase.charAt(i); - if (Character.isUpperCase(ch)) { - builder.append("_"); - } -diff --git a/java/core/src/main/java/com/google/protobuf/MessageReflection.java b/java/core/src/main/java/com/google/protobuf/MessageReflection.java -index 6741e1c..13a6b8d 100644 ---- a/java/core/src/main/java/com/google/protobuf/MessageReflection.java -+++ b/java/core/src/main/java/com/google/protobuf/MessageReflection.java -@@ -30,6 +30,7 @@ - - package com.google.protobuf; - -+import com.google.protobuf.Descriptors.Descriptor; - import com.google.protobuf.Descriptors.FieldDescriptor; - import java.io.IOException; - import java.util.ArrayList; -@@ -323,6 +324,34 @@ class MessageReflection { - Message defaultInstance) - throws IOException; - -+ /** -+ * Read the given group field from the wire, merging with the existing field if it is already -+ * present. -+ * -+ *

For extensions, defaultInstance must be specified. For regular fields, defaultInstance can -+ * be null. -+ */ -+ void mergeGroup( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException; -+ -+ /** -+ * Read the given message field from the wire, merging with the existing field if it is already -+ * present. -+ * -+ *

For extensions, defaultInstance must be specified. For regular fields, defaultInstance can -+ * be null. -+ */ -+ void mergeMessage( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException; -+ - /** Returns the UTF8 validation level for the field. */ - WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor descriptor); - -@@ -349,6 +378,7 @@ class MessageReflection { - static class BuilderAdapter implements MergeTarget { - - private final Message.Builder builder; -+ private boolean hasNestedBuilders = true; - - @Override - public Descriptors.Descriptor getDescriptorForType() { -@@ -364,6 +394,17 @@ class MessageReflection { - return builder.getField(field); - } - -+ private Message.Builder getFieldBuilder(Descriptors.FieldDescriptor field) { -+ if (hasNestedBuilders) { -+ try { -+ return builder.getFieldBuilder(field); -+ } catch (UnsupportedOperationException e) { -+ hasNestedBuilders = false; -+ } -+ } -+ return null; -+ } -+ - @Override - public boolean hasField(Descriptors.FieldDescriptor field) { - return builder.hasField(field); -@@ -371,6 +412,12 @@ class MessageReflection { - - @Override - public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) { -+ if (!field.isRepeated() && value instanceof MessageLite.Builder) { -+ if (value != getFieldBuilder(field)) { -+ builder.setField(field, ((MessageLite.Builder) value).buildPartial()); -+ } -+ return this; -+ } - builder.setField(field, value); - return this; - } -@@ -384,12 +431,18 @@ class MessageReflection { - @Override - public MergeTarget setRepeatedField( - Descriptors.FieldDescriptor field, int index, Object value) { -+ if (value instanceof MessageLite.Builder) { -+ value = ((MessageLite.Builder) value).buildPartial(); -+ } - builder.setRepeatedField(field, index, value); - return this; - } - - @Override - public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) { -+ if (value instanceof MessageLite.Builder) { -+ value = ((MessageLite.Builder) value).buildPartial(); -+ } - builder.addRepeatedField(field, value); - return this; - } -@@ -499,15 +552,88 @@ class MessageReflection { - return subBuilder.buildPartial(); - } - -+ @Override -+ public void mergeGroup( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ if (!field.isRepeated()) { -+ Message.Builder subBuilder; -+ if (hasField(field)) { -+ subBuilder = getFieldBuilder(field); -+ if (subBuilder != null) { -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ return; -+ } else { -+ subBuilder = newMessageFieldInstance(field, defaultInstance); -+ subBuilder.mergeFrom((Message) getField(field)); -+ } -+ } else { -+ subBuilder = newMessageFieldInstance(field, defaultInstance); -+ } -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ Object unused = setField(field, subBuilder.buildPartial()); -+ } else { -+ Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance); -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ Object unused = addRepeatedField(field, subBuilder.buildPartial()); -+ } -+ } -+ -+ @Override -+ public void mergeMessage( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ Descriptors.FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ if (!field.isRepeated()) { -+ Message.Builder subBuilder; -+ if (hasField(field)) { -+ subBuilder = getFieldBuilder(field); -+ if (subBuilder != null) { -+ input.readMessage(subBuilder, extensionRegistry); -+ return; -+ } else { -+ subBuilder = newMessageFieldInstance(field, defaultInstance); -+ subBuilder.mergeFrom((Message) getField(field)); -+ } -+ } else { -+ subBuilder = newMessageFieldInstance(field, defaultInstance); -+ } -+ input.readMessage(subBuilder, extensionRegistry); -+ Object unused = setField(field, subBuilder.buildPartial()); -+ } else { -+ Message.Builder subBuilder = newMessageFieldInstance(field, defaultInstance); -+ input.readMessage(subBuilder, extensionRegistry); -+ Object unused = addRepeatedField(field, subBuilder.buildPartial()); -+ } -+ } -+ -+ private Message.Builder newMessageFieldInstance( -+ FieldDescriptor field, Message defaultInstance) { -+ // When default instance is not null. The field is an extension field. -+ if (defaultInstance != null) { -+ return defaultInstance.newBuilderForType(); -+ } else { -+ return builder.newBuilderForField(field); -+ } -+ } -+ - @Override - public MergeTarget newMergeTargetForField( - Descriptors.FieldDescriptor field, Message defaultInstance) { - Message.Builder subBuilder; -- if (defaultInstance != null) { -- subBuilder = defaultInstance.newBuilderForType(); -- } else { -- subBuilder = builder.newBuilderForField(field); -+ if (!field.isRepeated() && hasField(field)) { -+ subBuilder = getFieldBuilder(field); -+ if (subBuilder != null) { -+ return new BuilderAdapter(subBuilder); -+ } - } -+ -+ subBuilder = newMessageFieldInstance(field, defaultInstance); - if (!field.isRepeated()) { - Message originalMessage = (Message) getField(field); - if (originalMessage != null) { -@@ -543,7 +669,7 @@ class MessageReflection { - - @Override - public Object finish() { -- return builder.buildPartial(); -+ return builder; - } - } - -@@ -665,6 +791,276 @@ class MessageReflection { - return subBuilder.buildPartial(); - } - -+ @Override -+ public void mergeGroup( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ if (!field.isRepeated()) { -+ if (hasField(field)) { -+ MessageLite.Builder current = ((MessageLite) getField(field)).toBuilder(); -+ input.readGroup(field.getNumber(), current, extensionRegistry); -+ Object unused = setField(field, current.buildPartial()); -+ return; -+ } -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ Object unused = setField(field, subBuilder.buildPartial()); -+ } else { -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ Object unused = addRepeatedField(field, subBuilder.buildPartial()); -+ } -+ } -+ -+ @Override -+ public void mergeMessage( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ if (!field.isRepeated()) { -+ if (hasField(field)) { -+ MessageLite.Builder current = ((MessageLite) getField(field)).toBuilder(); -+ input.readMessage(current, extensionRegistry); -+ Object unused = setField(field, current.buildPartial()); -+ return; -+ } -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readMessage(subBuilder, extensionRegistry); -+ Object unused = setField(field, subBuilder.buildPartial()); -+ } else { -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readMessage(subBuilder, extensionRegistry); -+ Object unused = addRepeatedField(field, subBuilder.buildPartial()); -+ } -+ } -+ -+ @Override -+ public Object parseMessageFromBytes( -+ ByteString bytes, -+ ExtensionRegistryLite registry, -+ Descriptors.FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ if (!field.isRepeated()) { -+ Message originalMessage = (Message) getField(field); -+ if (originalMessage != null) { -+ subBuilder.mergeFrom(originalMessage); -+ } -+ } -+ subBuilder.mergeFrom(bytes, registry); -+ return subBuilder.buildPartial(); -+ } -+ -+ @Override -+ public MergeTarget newMergeTargetForField( -+ Descriptors.FieldDescriptor descriptor, Message defaultInstance) { -+ throw new UnsupportedOperationException("newMergeTargetForField() called on FieldSet object"); -+ } -+ -+ @Override -+ public MergeTarget newEmptyTargetForField( -+ Descriptors.FieldDescriptor descriptor, Message defaultInstance) { -+ throw new UnsupportedOperationException("newEmptyTargetForField() called on FieldSet object"); -+ } -+ -+ @Override -+ public WireFormat.Utf8Validation getUtf8Validation(Descriptors.FieldDescriptor descriptor) { -+ if (descriptor.needsUtf8Check()) { -+ return WireFormat.Utf8Validation.STRICT; -+ } -+ // TODO(b/248145492): support lazy strings for ExtesnsionSet. -+ return WireFormat.Utf8Validation.LOOSE; -+ } -+ -+ @Override -+ public Object finish() { -+ throw new UnsupportedOperationException("finish() called on FieldSet object"); -+ } -+ } -+ -+ static class ExtensionBuilderAdapter implements MergeTarget { -+ -+ private final FieldSet.Builder extensions; -+ -+ ExtensionBuilderAdapter(FieldSet.Builder extensions) { -+ this.extensions = extensions; -+ } -+ -+ @Override -+ public Descriptors.Descriptor getDescriptorForType() { -+ throw new UnsupportedOperationException("getDescriptorForType() called on FieldSet object"); -+ } -+ -+ @Override -+ public Object getField(Descriptors.FieldDescriptor field) { -+ return extensions.getField(field); -+ } -+ -+ @Override -+ public boolean hasField(Descriptors.FieldDescriptor field) { -+ return extensions.hasField(field); -+ } -+ -+ @Override -+ public MergeTarget setField(Descriptors.FieldDescriptor field, Object value) { -+ extensions.setField(field, value); -+ return this; -+ } -+ -+ @Override -+ public MergeTarget clearField(Descriptors.FieldDescriptor field) { -+ extensions.clearField(field); -+ return this; -+ } -+ -+ @Override -+ public MergeTarget setRepeatedField( -+ Descriptors.FieldDescriptor field, int index, Object value) { -+ extensions.setRepeatedField(field, index, value); -+ return this; -+ } -+ -+ @Override -+ public MergeTarget addRepeatedField(Descriptors.FieldDescriptor field, Object value) { -+ extensions.addRepeatedField(field, value); -+ return this; -+ } -+ -+ @Override -+ public boolean hasOneof(Descriptors.OneofDescriptor oneof) { -+ return false; -+ } -+ -+ @Override -+ public MergeTarget clearOneof(Descriptors.OneofDescriptor oneof) { -+ // Nothing to clear. -+ return this; -+ } -+ -+ @Override -+ public Descriptors.FieldDescriptor getOneofFieldDescriptor(Descriptors.OneofDescriptor oneof) { -+ return null; -+ } -+ -+ @Override -+ public ContainerType getContainerType() { -+ return ContainerType.EXTENSION_SET; -+ } -+ -+ @Override -+ public ExtensionRegistry.ExtensionInfo findExtensionByName( -+ ExtensionRegistry registry, String name) { -+ return registry.findImmutableExtensionByName(name); -+ } -+ -+ @Override -+ public ExtensionRegistry.ExtensionInfo findExtensionByNumber( -+ ExtensionRegistry registry, Descriptors.Descriptor containingType, int fieldNumber) { -+ return registry.findImmutableExtensionByNumber(containingType, fieldNumber); -+ } -+ -+ @Override -+ public Object parseGroup( -+ CodedInputStream input, -+ ExtensionRegistryLite registry, -+ Descriptors.FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ if (!field.isRepeated()) { -+ Message originalMessage = (Message) getField(field); -+ if (originalMessage != null) { -+ subBuilder.mergeFrom(originalMessage); -+ } -+ } -+ input.readGroup(field.getNumber(), subBuilder, registry); -+ return subBuilder.buildPartial(); -+ } -+ -+ @Override -+ public Object parseMessage( -+ CodedInputStream input, -+ ExtensionRegistryLite registry, -+ Descriptors.FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ if (!field.isRepeated()) { -+ Message originalMessage = (Message) getField(field); -+ if (originalMessage != null) { -+ subBuilder.mergeFrom(originalMessage); -+ } -+ } -+ input.readMessage(subBuilder, registry); -+ return subBuilder.buildPartial(); -+ } -+ -+ @Override -+ public void mergeGroup( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ if (!field.isRepeated()) { -+ if (hasField(field)) { -+ Object fieldOrBuilder = extensions.getFieldAllowBuilders(field); -+ MessageLite.Builder subBuilder; -+ if (fieldOrBuilder instanceof MessageLite.Builder) { -+ subBuilder = (MessageLite.Builder) fieldOrBuilder; -+ } else { -+ subBuilder = ((MessageLite) fieldOrBuilder).toBuilder(); -+ extensions.setField(field, subBuilder); -+ } -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ return; -+ } -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ Object unused = setField(field, subBuilder); -+ } else { -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readGroup(field.getNumber(), subBuilder, extensionRegistry); -+ Object unused = addRepeatedField(field, subBuilder.buildPartial()); -+ } -+ } -+ -+ @Override -+ public void mergeMessage( -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry, -+ FieldDescriptor field, -+ Message defaultInstance) -+ throws IOException { -+ if (!field.isRepeated()) { -+ if (hasField(field)) { -+ Object fieldOrBuilder = extensions.getFieldAllowBuilders(field); -+ MessageLite.Builder subBuilder; -+ if (fieldOrBuilder instanceof MessageLite.Builder) { -+ subBuilder = (MessageLite.Builder) fieldOrBuilder; -+ } else { -+ subBuilder = ((MessageLite) fieldOrBuilder).toBuilder(); -+ extensions.setField(field, subBuilder); -+ } -+ input.readMessage(subBuilder, extensionRegistry); -+ return; -+ } -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readMessage(subBuilder, extensionRegistry); -+ Object unused = setField(field, subBuilder); -+ } else { -+ Message.Builder subBuilder = defaultInstance.newBuilderForType(); -+ input.readMessage(subBuilder, extensionRegistry); -+ Object unused = addRepeatedField(field, subBuilder.buildPartial()); -+ } -+ } -+ - @Override - public Object parseMessageFromBytes( - ByteString bytes, -@@ -700,7 +1096,7 @@ class MessageReflection { - if (descriptor.needsUtf8Check()) { - return WireFormat.Utf8Validation.STRICT; - } -- // TODO(liujisi): support lazy strings for ExtesnsionSet. -+ // TODO(b/248145492): support lazy strings for ExtesnsionSet. - return WireFormat.Utf8Validation.LOOSE; - } - -@@ -829,13 +1225,13 @@ class MessageReflection { - switch (field.getType()) { - case GROUP: - { -- value = target.parseGroup(input, extensionRegistry, field, defaultInstance); -- break; -+ target.mergeGroup(input, extensionRegistry, field, defaultInstance); -+ return true; - } - case MESSAGE: - { -- value = target.parseMessage(input, extensionRegistry, field, defaultInstance); -- break; -+ target.mergeMessage(input, extensionRegistry, field, defaultInstance); -+ return true; - } - case ENUM: - final int rawValue = input.readEnum(); -@@ -870,6 +1266,29 @@ class MessageReflection { - return true; - } - -+ /** Read a message from the given input stream into the provided target and UnknownFieldSet. */ -+ static void mergeMessageFrom( -+ Message.Builder target, -+ UnknownFieldSet.Builder unknownFields, -+ CodedInputStream input, -+ ExtensionRegistryLite extensionRegistry) -+ throws IOException { -+ BuilderAdapter builderAdapter = new BuilderAdapter(target); -+ Descriptor descriptorForType = target.getDescriptorForType(); -+ while (true) { -+ final int tag = input.readTag(); -+ if (tag == 0) { -+ break; -+ } -+ -+ if (!mergeFieldFrom( -+ input, unknownFields, extensionRegistry, descriptorForType, builderAdapter, tag)) { -+ // end group tag -+ break; -+ } -+ } -+ } -+ - /** Called by {@code #mergeFieldFrom()} to parse a MessageSet extension into MergeTarget. */ - private static void mergeMessageSetExtensionFromCodedStream( - CodedInputStream input, -diff --git a/java/core/src/main/java/com/google/protobuf/MessageSchema.java b/java/core/src/main/java/com/google/protobuf/MessageSchema.java -index 33c8e91..8f873c1 100644 ---- a/java/core/src/main/java/com/google/protobuf/MessageSchema.java -+++ b/java/core/src/main/java/com/google/protobuf/MessageSchema.java -@@ -42,7 +42,6 @@ import static com.google.protobuf.ArrayDecoders.decodeFixed64; - import static com.google.protobuf.ArrayDecoders.decodeFixed64List; - import static com.google.protobuf.ArrayDecoders.decodeFloat; - import static com.google.protobuf.ArrayDecoders.decodeFloatList; --import static com.google.protobuf.ArrayDecoders.decodeGroupField; - import static com.google.protobuf.ArrayDecoders.decodeGroupList; - import static com.google.protobuf.ArrayDecoders.decodeMessageField; - import static com.google.protobuf.ArrayDecoders.decodeMessageList; -@@ -66,6 +65,8 @@ import static com.google.protobuf.ArrayDecoders.decodeVarint32; - import static com.google.protobuf.ArrayDecoders.decodeVarint32List; - import static com.google.protobuf.ArrayDecoders.decodeVarint64; - import static com.google.protobuf.ArrayDecoders.decodeVarint64List; -+import static com.google.protobuf.ArrayDecoders.mergeGroupField; -+import static com.google.protobuf.ArrayDecoders.mergeMessageField; - import static com.google.protobuf.ArrayDecoders.skipField; - - import com.google.protobuf.ArrayDecoders.Registers; -@@ -1176,6 +1177,7 @@ final class MessageSchema implements Schema { - - @Override - public void mergeFrom(T message, T other) { -+ checkMutable(message); - if (other == null) { - throw new NullPointerException(); - } -@@ -1374,47 +1376,83 @@ final class MessageSchema implements Schema { - } - } - -- private void mergeMessage(T message, T other, int pos) { -+ private void mergeMessage(T targetParent, T sourceParent, int pos) { -+ if (!isFieldPresent(sourceParent, pos)) { -+ return; -+ } -+ - final int typeAndOffset = typeAndOffsetAt(pos); - final long offset = offset(typeAndOffset); - -- if (!isFieldPresent(other, pos)) { -+ final Object source = UNSAFE.getObject(sourceParent, offset); -+ if (source == null) { -+ throw new IllegalStateException( -+ "Source subfield " + numberAt(pos) + " is present but null: " + sourceParent); -+ } -+ -+ final Schema fieldSchema = getMessageFieldSchema(pos); -+ if (!isFieldPresent(targetParent, pos)) { -+ if (!isMutable(source)) { -+ // Can safely share source if it is immutable -+ UNSAFE.putObject(targetParent, offset, source); -+ } else { -+ // Make a safetey copy of source -+ final Object copyOfSource = fieldSchema.newInstance(); -+ fieldSchema.mergeFrom(copyOfSource, source); -+ UNSAFE.putObject(targetParent, offset, copyOfSource); -+ } -+ setFieldPresent(targetParent, pos); - return; - } - -- Object mine = UnsafeUtil.getObject(message, offset); -- Object theirs = UnsafeUtil.getObject(other, offset); -- if (mine != null && theirs != null) { -- Object merged = Internal.mergeMessage(mine, theirs); -- UnsafeUtil.putObject(message, offset, merged); -- setFieldPresent(message, pos); -- } else if (theirs != null) { -- UnsafeUtil.putObject(message, offset, theirs); -- setFieldPresent(message, pos); -+ // Sub-message is present, merge from source -+ Object target = UNSAFE.getObject(targetParent, offset); -+ if (!isMutable(target)) { -+ Object newInstance = fieldSchema.newInstance(); -+ fieldSchema.mergeFrom(newInstance, target); -+ UNSAFE.putObject(targetParent, offset, newInstance); -+ target = newInstance; - } -+ fieldSchema.mergeFrom(target, source); - } - -- private void mergeOneofMessage(T message, T other, int pos) { -- int typeAndOffset = typeAndOffsetAt(pos); -+ private void mergeOneofMessage(T targetParent, T sourceParent, int pos) { - int number = numberAt(pos); -- long offset = offset(typeAndOffset); -- -- if (!isOneofPresent(other, number, pos)) { -+ if (!isOneofPresent(sourceParent, number, pos)) { - return; - } -- Object mine = null; -- if (isOneofPresent(message, number, pos)) { -- mine = UnsafeUtil.getObject(message, offset); -+ -+ long offset = offset(typeAndOffsetAt(pos)); -+ final Object source = UNSAFE.getObject(sourceParent, offset); -+ if (source == null) { -+ throw new IllegalStateException( -+ "Source subfield " + numberAt(pos) + " is present but null: " + sourceParent); - } -- Object theirs = UnsafeUtil.getObject(other, offset); -- if (mine != null && theirs != null) { -- Object merged = Internal.mergeMessage(mine, theirs); -- UnsafeUtil.putObject(message, offset, merged); -- setOneofPresent(message, number, pos); -- } else if (theirs != null) { -- UnsafeUtil.putObject(message, offset, theirs); -- setOneofPresent(message, number, pos); -+ -+ final Schema fieldSchema = getMessageFieldSchema(pos); -+ if (!isOneofPresent(targetParent, number, pos)) { -+ if (!isMutable(source)) { -+ // Can safely share source if it is immutable -+ UNSAFE.putObject(targetParent, offset, source); -+ } else { -+ // Make a safety copy of theirs -+ final Object copyOfSource = fieldSchema.newInstance(); -+ fieldSchema.mergeFrom(copyOfSource, source); -+ UNSAFE.putObject(targetParent, offset, copyOfSource); -+ } -+ setOneofPresent(targetParent, number, pos); -+ return; - } -+ -+ // Sub-message is present, merge from source -+ Object target = UNSAFE.getObject(targetParent, offset); -+ if (!isMutable(target)) { -+ Object newInstance = fieldSchema.newInstance(); -+ fieldSchema.mergeFrom(newInstance, target); -+ UNSAFE.putObject(targetParent, offset, newInstance); -+ target = newInstance; -+ } -+ fieldSchema.mergeFrom(target, source); - } - - @Override -@@ -3853,6 +3891,7 @@ final class MessageSchema implements Schema { - if (extensionRegistry == null) { - throw new NullPointerException(); - } -+ checkMutable(message); - mergeFromHelper(unknownFieldSchema, extensionSchema, message, reader, extensionRegistry); - } - -@@ -3889,6 +3928,7 @@ final class MessageSchema implements Schema { - } - unknownFields = - extensionSchema.parseExtension( -+ message, - reader, - extension, - extensionRegistry, -@@ -3955,21 +3995,10 @@ final class MessageSchema implements Schema { - break; - case 9: - { // MESSAGE: -- if (isFieldPresent(message, pos)) { -- Object mergedResult = -- Internal.mergeMessage( -- UnsafeUtil.getObject(message, offset(typeAndOffset)), -- reader.readMessageBySchemaWithCheck( -- (Schema) getMessageFieldSchema(pos), extensionRegistry)); -- UnsafeUtil.putObject(message, offset(typeAndOffset), mergedResult); -- } else { -- UnsafeUtil.putObject( -- message, -- offset(typeAndOffset), -- reader.readMessageBySchemaWithCheck( -- (Schema) getMessageFieldSchema(pos), extensionRegistry)); -- setFieldPresent(message, pos); -- } -+ final MessageLite current = (MessageLite) mutableMessageFieldForMerge(message, pos); -+ reader.mergeMessageField( -+ current, (Schema) getMessageFieldSchema(pos), extensionRegistry); -+ storeMessageField(message, pos, current); - break; - } - case 10: // BYTES: -@@ -3990,7 +4019,7 @@ final class MessageSchema implements Schema { - } else { - unknownFields = - SchemaUtil.storeUnknownEnum( -- number, enumValue, unknownFields, unknownFieldSchema); -+ message, number, enumValue, unknownFields, unknownFieldSchema); - } - break; - } -@@ -4012,21 +4041,10 @@ final class MessageSchema implements Schema { - break; - case 17: - { // GROUP: -- if (isFieldPresent(message, pos)) { -- Object mergedResult = -- Internal.mergeMessage( -- UnsafeUtil.getObject(message, offset(typeAndOffset)), -- reader.readGroupBySchemaWithCheck( -- (Schema) getMessageFieldSchema(pos), extensionRegistry)); -- UnsafeUtil.putObject(message, offset(typeAndOffset), mergedResult); -- } else { -- UnsafeUtil.putObject( -- message, -- offset(typeAndOffset), -- reader.readGroupBySchemaWithCheck( -- (Schema) getMessageFieldSchema(pos), extensionRegistry)); -- setFieldPresent(message, pos); -- } -+ final MessageLite current = (MessageLite) mutableMessageFieldForMerge(message, pos); -+ reader.mergeGroupField( -+ current, (Schema) getMessageFieldSchema(pos), extensionRegistry); -+ storeMessageField(message, pos, current); - break; - } - case 18: // DOUBLE_LIST: -@@ -4089,6 +4107,7 @@ final class MessageSchema implements Schema { - reader.readEnumList(enumList); - unknownFields = - SchemaUtil.filterUnknownEnumList( -+ message, - number, - enumList, - getEnumFieldVerifier(pos), -@@ -4155,6 +4174,7 @@ final class MessageSchema implements Schema { - reader.readEnumList(enumList); - unknownFields = - SchemaUtil.filterUnknownEnumList( -+ message, - number, - enumList, - getEnumFieldVerifier(pos), -@@ -4235,24 +4255,15 @@ final class MessageSchema implements Schema { - readString(message, typeAndOffset, reader); - setOneofPresent(message, number, pos); - break; -- case 60: // ONEOF_MESSAGE: -- if (isOneofPresent(message, number, pos)) { -- Object mergedResult = -- Internal.mergeMessage( -- UnsafeUtil.getObject(message, offset(typeAndOffset)), -- reader.readMessageBySchemaWithCheck( -- getMessageFieldSchema(pos), extensionRegistry)); -- UnsafeUtil.putObject(message, offset(typeAndOffset), mergedResult); -- } else { -- UnsafeUtil.putObject( -- message, -- offset(typeAndOffset), -- reader.readMessageBySchemaWithCheck( -- getMessageFieldSchema(pos), extensionRegistry)); -- setFieldPresent(message, pos); -+ case 60: -+ { // ONEOF_MESSAGE: -+ final MessageLite current = -+ (MessageLite) mutableOneofMessageFieldForMerge(message, number, pos); -+ reader.mergeMessageField( -+ current, (Schema) getMessageFieldSchema(pos), extensionRegistry); -+ storeOneofMessageField(message, number, pos, current); -+ break; - } -- setOneofPresent(message, number, pos); -- break; - case 61: // ONEOF_BYTES: - UnsafeUtil.putObject(message, offset(typeAndOffset), reader.readBytes()); - setOneofPresent(message, number, pos); -@@ -4272,7 +4283,7 @@ final class MessageSchema implements Schema { - } else { - unknownFields = - SchemaUtil.storeUnknownEnum( -- number, enumValue, unknownFields, unknownFieldSchema); -+ message, number, enumValue, unknownFields, unknownFieldSchema); - } - break; - } -@@ -4296,17 +4307,19 @@ final class MessageSchema implements Schema { - message, offset(typeAndOffset), Long.valueOf(reader.readSInt64())); - setOneofPresent(message, number, pos); - break; -- case 68: // ONEOF_GROUP: -- UnsafeUtil.putObject( -- message, -- offset(typeAndOffset), -- reader.readGroupBySchemaWithCheck(getMessageFieldSchema(pos), extensionRegistry)); -- setOneofPresent(message, number, pos); -- break; -+ case 68: -+ { // ONEOF_GROUP: -+ final MessageLite current = -+ (MessageLite) mutableOneofMessageFieldForMerge(message, number, pos); -+ reader.mergeGroupField( -+ current, (Schema) getMessageFieldSchema(pos), extensionRegistry); -+ storeOneofMessageField(message, number, pos, current); -+ break; -+ } - default: - // Assume we've landed on an empty entry. Treat it as an unknown field. - if (unknownFields == null) { -- unknownFields = unknownFieldSchema.newBuilder(); -+ unknownFields = unknownFieldSchema.getBuilderFromMessage(message); - } - if (!unknownFieldSchema.mergeOneFieldFrom(unknownFields, reader)) { - return; -@@ -4333,7 +4346,8 @@ final class MessageSchema implements Schema { - } finally { - for (int i = checkInitializedCount; i < repeatedFieldOffsetStart; i++) { - unknownFields = -- filterMapUnknownEnumValues(message, intArray[i], unknownFields, unknownFieldSchema); -+ filterMapUnknownEnumValues( -+ message, intArray[i], unknownFields, unknownFieldSchema, message); - } - if (unknownFields != null) { - unknownFieldSchema.setBuilderToMessage(message, unknownFields); -@@ -4343,6 +4357,8 @@ final class MessageSchema implements Schema { - - @SuppressWarnings("ReferenceEquality") - static UnknownFieldSetLite getMutableUnknownFields(Object message) { -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this -+ // better. - UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields; - if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { - unknownFields = UnknownFieldSetLite.newInstance(); -@@ -4603,24 +4619,13 @@ final class MessageSchema implements Schema { - } else { - break; - } -- UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields; -- if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { -- // filterUnknownEnumList() expects the unknownFields parameter to be mutable or null. -- // Since we don't know yet whether there exist unknown enum values, we'd better pass -- // null to it instead of allocating a mutable instance. This is also needed to be -- // consistent with the behavior of generated parser/builder. -- unknownFields = null; -- } -- unknownFields = -- SchemaUtil.filterUnknownEnumList( -- number, -- (ProtobufList) list, -- getEnumFieldVerifier(bufferPosition), -- unknownFields, -- (UnknownFieldSchema) unknownFieldSchema); -- if (unknownFields != null) { -- ((GeneratedMessageLite) message).unknownFields = unknownFields; -- } -+ SchemaUtil.filterUnknownEnumList( -+ message, -+ number, -+ (ProtobufList) list, -+ getEnumFieldVerifier(bufferPosition), -+ null, -+ (UnknownFieldSchema) unknownFieldSchema); - break; - case 33: // SINT32_LIST: - case 47: // SINT32_LIST_PACKED: -@@ -4774,20 +4779,11 @@ final class MessageSchema implements Schema { - break; - case 60: // ONEOF_MESSAGE: - if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) { -+ final Object current = mutableOneofMessageFieldForMerge(message, number, bufferPosition); - position = -- decodeMessageField( -- getMessageFieldSchema(bufferPosition), data, position, limit, registers); -- final Object oldValue = -- unsafe.getInt(message, oneofCaseOffset) == number -- ? unsafe.getObject(message, fieldOffset) -- : null; -- if (oldValue == null) { -- unsafe.putObject(message, fieldOffset, registers.object1); -- } else { -- unsafe.putObject( -- message, fieldOffset, Internal.mergeMessage(oldValue, registers.object1)); -- } -- unsafe.putInt(message, oneofCaseOffset, number); -+ mergeMessageField( -+ current, getMessageFieldSchema(bufferPosition), data, position, limit, registers); -+ storeOneofMessageField(message, number, bufferPosition, current); - } - break; - case 61: // ONEOF_BYTES: -@@ -4827,21 +4823,18 @@ final class MessageSchema implements Schema { - break; - case 68: // ONEOF_GROUP: - if (wireType == WireFormat.WIRETYPE_START_GROUP) { -+ final Object current = mutableOneofMessageFieldForMerge(message, number, bufferPosition); - final int endTag = (tag & ~0x7) | WireFormat.WIRETYPE_END_GROUP; - position = -- decodeGroupField( -- getMessageFieldSchema(bufferPosition), data, position, limit, endTag, registers); -- final Object oldValue = -- unsafe.getInt(message, oneofCaseOffset) == number -- ? unsafe.getObject(message, fieldOffset) -- : null; -- if (oldValue == null) { -- unsafe.putObject(message, fieldOffset, registers.object1); -- } else { -- unsafe.putObject( -- message, fieldOffset, Internal.mergeMessage(oldValue, registers.object1)); -- } -- unsafe.putInt(message, oneofCaseOffset, number); -+ mergeGroupField( -+ current, -+ getMessageFieldSchema(bufferPosition), -+ data, -+ position, -+ limit, -+ endTag, -+ registers); -+ storeOneofMessageField(message, number, bufferPosition, current); - } - break; - default: -@@ -4878,6 +4871,7 @@ final class MessageSchema implements Schema { - int parseProto2Message( - T message, byte[] data, int position, int limit, int endGroup, Registers registers) - throws IOException { -+ checkMutable(message); - final sun.misc.Unsafe unsafe = UNSAFE; - int currentPresenceFieldOffset = NO_PRESENCE_SENTINEL; - int currentPresenceField = 0; -@@ -4994,18 +4988,11 @@ final class MessageSchema implements Schema { - break; - case 9: // MESSAGE - if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) { -+ final Object current = mutableMessageFieldForMerge(message, pos); - position = -- decodeMessageField( -- getMessageFieldSchema(pos), data, position, limit, registers); -- if ((currentPresenceField & presenceMask) == 0) { -- unsafe.putObject(message, fieldOffset, registers.object1); -- } else { -- unsafe.putObject( -- message, -- fieldOffset, -- Internal.mergeMessage( -- unsafe.getObject(message, fieldOffset), registers.object1)); -- } -+ mergeMessageField( -+ current, getMessageFieldSchema(pos), data, position, limit, registers); -+ storeMessageField(message, pos, current); - currentPresenceField |= presenceMask; - continue; - } -@@ -5054,20 +5041,18 @@ final class MessageSchema implements Schema { - break; - case 17: // GROUP - if (wireType == WireFormat.WIRETYPE_START_GROUP) { -+ final Object current = mutableMessageFieldForMerge(message, pos); - final int endTag = (number << 3) | WireFormat.WIRETYPE_END_GROUP; - position = -- decodeGroupField( -- getMessageFieldSchema(pos), data, position, limit, endTag, registers); -- if ((currentPresenceField & presenceMask) == 0) { -- unsafe.putObject(message, fieldOffset, registers.object1); -- } else { -- unsafe.putObject( -- message, -- fieldOffset, -- Internal.mergeMessage( -- unsafe.getObject(message, fieldOffset), registers.object1)); -- } -- -+ mergeGroupField( -+ current, -+ getMessageFieldSchema(pos), -+ data, -+ position, -+ limit, -+ endTag, -+ registers); -+ storeMessageField(message, pos, current); - currentPresenceField |= presenceMask; - continue; - } -@@ -5165,7 +5150,8 @@ final class MessageSchema implements Schema { - message, - intArray[i], - unknownFields, -- (UnknownFieldSchema) unknownFieldSchema); -+ (UnknownFieldSchema) unknownFieldSchema, -+ message); - } - if (unknownFields != null) { - ((UnknownFieldSchema) unknownFieldSchema) -@@ -5183,9 +5169,65 @@ final class MessageSchema implements Schema { - return position; - } - -+ private Object mutableMessageFieldForMerge(T message, int pos) { -+ final Schema fieldSchema = getMessageFieldSchema(pos); -+ final long offset = offset(typeAndOffsetAt(pos)); -+ -+ // Field not present, create a new one -+ if (!isFieldPresent(message, pos)) { -+ return fieldSchema.newInstance(); -+ } -+ -+ // Field present, if mutable, ready to merge -+ final Object current = UNSAFE.getObject(message, offset); -+ if (isMutable(current)) { -+ return current; -+ } -+ -+ // Field present but immutable, make a new mutable copy -+ final Object newMessage = fieldSchema.newInstance(); -+ if (current != null) { -+ fieldSchema.mergeFrom(newMessage, current); -+ } -+ return newMessage; -+ } -+ -+ private void storeMessageField(T message, int pos, Object field) { -+ UNSAFE.putObject(message, offset(typeAndOffsetAt(pos)), field); -+ setFieldPresent(message, pos); -+ } -+ -+ private Object mutableOneofMessageFieldForMerge(T message, int fieldNumber, int pos) { -+ final Schema fieldSchema = getMessageFieldSchema(pos); -+ -+ // Field not present, create it and mark it present -+ if (!isOneofPresent(message, fieldNumber, pos)) { -+ return fieldSchema.newInstance(); -+ } -+ -+ // Field present, if mutable, ready to merge -+ final Object current = UNSAFE.getObject(message, offset(typeAndOffsetAt(pos))); -+ if (isMutable(current)) { -+ return current; -+ } -+ -+ // Field present but immutable, make a new mutable copy -+ final Object newMessage = fieldSchema.newInstance(); -+ if (current != null) { -+ fieldSchema.mergeFrom(newMessage, current); -+ } -+ return newMessage; -+ } -+ -+ private void storeOneofMessageField(T message, int fieldNumber, int pos, Object field) { -+ UNSAFE.putObject(message, offset(typeAndOffsetAt(pos)), field); -+ setOneofPresent(message, fieldNumber, pos); -+ } -+ - /** Parses a proto3 message and returns the limit if parsing is successful. */ - private int parseProto3Message( - T message, byte[] data, int position, int limit, Registers registers) throws IOException { -+ checkMutable(message); - final sun.misc.Unsafe unsafe = UNSAFE; - int currentPresenceFieldOffset = NO_PRESENCE_SENTINEL; - int currentPresenceField = 0; -@@ -5307,16 +5349,11 @@ final class MessageSchema implements Schema { - break; - case 9: // MESSAGE: - if (wireType == WireFormat.WIRETYPE_LENGTH_DELIMITED) { -+ final Object current = mutableMessageFieldForMerge(message, pos); - position = -- decodeMessageField( -- getMessageFieldSchema(pos), data, position, limit, registers); -- final Object oldValue = unsafe.getObject(message, fieldOffset); -- if (oldValue == null) { -- unsafe.putObject(message, fieldOffset, registers.object1); -- } else { -- unsafe.putObject( -- message, fieldOffset, Internal.mergeMessage(oldValue, registers.object1)); -- } -+ mergeMessageField( -+ current, getMessageFieldSchema(pos), data, position, limit, registers); -+ storeMessageField(message, pos, current); - currentPresenceField |= presenceMask; - continue; - } -@@ -5447,18 +5484,73 @@ final class MessageSchema implements Schema { - - @Override - public void makeImmutable(T message) { -- // Make all repeated/map fields immutable. -- for (int i = checkInitializedCount; i < repeatedFieldOffsetStart; i++) { -- long offset = offset(typeAndOffsetAt(intArray[i])); -- Object mapField = UnsafeUtil.getObject(message, offset); -- if (mapField == null) { -- continue; -- } -- UnsafeUtil.putObject(message, offset, mapFieldSchema.toImmutable(mapField)); -+ if (!isMutable(message)) { -+ return; -+ } -+ -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this -+ // better. -+ if (message instanceof GeneratedMessageLite) { -+ GeneratedMessageLite generatedMessage = ((GeneratedMessageLite) message); -+ generatedMessage.clearMemoizedSerializedSize(); -+ generatedMessage.clearMemoizedHashCode(); -+ generatedMessage.markImmutable(); - } -- final int length = intArray.length; -- for (int i = repeatedFieldOffsetStart; i < length; i++) { -- listFieldSchema.makeImmutableListAt(message, intArray[i]); -+ -+ final int bufferLength = buffer.length; -+ for (int pos = 0; pos < bufferLength; pos += INTS_PER_FIELD) { -+ final int typeAndOffset = typeAndOffsetAt(pos); -+ final long offset = offset(typeAndOffset); -+ switch (type(typeAndOffset)) { -+ case 17: // GROUP -+ case 9: // MESSAGE -+ if (isFieldPresent(message, pos)) { -+ getMessageFieldSchema(pos).makeImmutable(UNSAFE.getObject(message, offset)); -+ } -+ break; -+ case 18: // DOUBLE_LIST: -+ case 19: // FLOAT_LIST: -+ case 20: // INT64_LIST: -+ case 21: // UINT64_LIST: -+ case 22: // INT32_LIST: -+ case 23: // FIXED64_LIST: -+ case 24: // FIXED32_LIST: -+ case 25: // BOOL_LIST: -+ case 26: // STRING_LIST: -+ case 27: // MESSAGE_LIST: -+ case 28: // BYTES_LIST: -+ case 29: // UINT32_LIST: -+ case 30: // ENUM_LIST: -+ case 31: // SFIXED32_LIST: -+ case 32: // SFIXED64_LIST: -+ case 33: // SINT32_LIST: -+ case 34: // SINT64_LIST: -+ case 35: // DOUBLE_LIST_PACKED: -+ case 36: // FLOAT_LIST_PACKED: -+ case 37: // INT64_LIST_PACKED: -+ case 38: // UINT64_LIST_PACKED: -+ case 39: // INT32_LIST_PACKED: -+ case 40: // FIXED64_LIST_PACKED: -+ case 41: // FIXED32_LIST_PACKED: -+ case 42: // BOOL_LIST_PACKED: -+ case 43: // UINT32_LIST_PACKED: -+ case 44: // ENUM_LIST_PACKED: -+ case 45: // SFIXED32_LIST_PACKED: -+ case 46: // SFIXED64_LIST_PACKED: -+ case 47: // SINT32_LIST_PACKED: -+ case 48: // SINT64_LIST_PACKED: -+ case 49: // GROUP_LIST: -+ listFieldSchema.makeImmutableListAt(message, offset); -+ break; -+ case 50: // MAP: -+ { -+ Object mapField = UNSAFE.getObject(message, offset); -+ if (mapField != null) { -+ UNSAFE.putObject(message, offset, mapFieldSchema.toImmutable(mapField)); -+ } -+ } -+ break; -+ } - } - unknownFieldSchema.makeImmutable(message); - if (hasExtensions) { -@@ -5495,8 +5587,12 @@ final class MessageSchema implements Schema { - extensionRegistry); - } - -- private final UB filterMapUnknownEnumValues( -- Object message, int pos, UB unknownFields, UnknownFieldSchema unknownFieldSchema) { -+ private UB filterMapUnknownEnumValues( -+ Object message, -+ int pos, -+ UB unknownFields, -+ UnknownFieldSchema unknownFieldSchema, -+ Object containerMessage) { - int fieldNumber = numberAt(pos); - long offset = offset(typeAndOffsetAt(pos)); - Object mapField = UnsafeUtil.getObject(message, offset); -@@ -5511,25 +5607,32 @@ final class MessageSchema implements Schema { - // Filter unknown enum values. - unknownFields = - filterUnknownEnumMap( -- pos, fieldNumber, mapData, enumVerifier, unknownFields, unknownFieldSchema); -+ pos, -+ fieldNumber, -+ mapData, -+ enumVerifier, -+ unknownFields, -+ unknownFieldSchema, -+ containerMessage); - return unknownFields; - } - - @SuppressWarnings("unchecked") -- private final UB filterUnknownEnumMap( -+ private UB filterUnknownEnumMap( - int pos, - int number, - Map mapData, - EnumVerifier enumVerifier, - UB unknownFields, -- UnknownFieldSchema unknownFieldSchema) { -+ UnknownFieldSchema unknownFieldSchema, -+ Object containerMessage) { - Metadata metadata = - (Metadata) mapFieldSchema.forMapMetadata(getMapFieldDefaultEntry(pos)); - for (Iterator> it = mapData.entrySet().iterator(); it.hasNext(); ) { - Map.Entry entry = it.next(); - if (!enumVerifier.isInRange((Integer) entry.getValue())) { - if (unknownFields == null) { -- unknownFields = unknownFieldSchema.newBuilder(); -+ unknownFields = unknownFieldSchema.getBuilderFromMessage(containerMessage); - } - int entrySize = - MapEntryLite.computeSerializedSize(metadata, entry.getKey(), entry.getValue()); -@@ -5746,6 +5849,28 @@ final class MessageSchema implements Schema { - return value & OFFSET_MASK; - } - -+ private static boolean isMutable(Object message) { -+ if (message == null) { -+ return false; -+ } -+ -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this -+ // better. -+ if (message instanceof GeneratedMessageLite) { -+ return ((GeneratedMessageLite) message).isMutable(); -+ } -+ -+ // For other types, we'll assume this is true because that's what was -+ // happening before we started checking. -+ return true; -+ } -+ -+ private static void checkMutable(Object message) { -+ if (!isMutable(message)) { -+ throw new IllegalArgumentException("Mutating immutable message: " + message); -+ } -+ } -+ - private static double doubleAt(T message, long offset) { - return UnsafeUtil.getDouble(message, offset); - } -diff --git a/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java b/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java -index 187dc8b..eae93b9 100644 ---- a/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java -+++ b/java/core/src/main/java/com/google/protobuf/MessageSetSchema.java -@@ -61,7 +61,13 @@ final class MessageSetSchema implements Schema { - @SuppressWarnings("unchecked") - @Override - public T newInstance() { -- return (T) defaultInstance.newBuilderForType().buildPartial(); -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this -+ // better. -+ if (defaultInstance instanceof GeneratedMessageLite) { -+ return (T) ((GeneratedMessageLite) defaultInstance).newMutableInstance(); -+ } else { -+ return (T) defaultInstance.newBuilderForType().buildPartial(); -+ } - } - - @Override -@@ -132,6 +138,8 @@ final class MessageSetSchema implements Schema { - public void mergeFrom( - T message, byte[] data, int position, int limit, ArrayDecoders.Registers registers) - throws IOException { -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this -+ // better. - UnknownFieldSetLite unknownFields = ((GeneratedMessageLite) message).unknownFields; - if (unknownFields == UnknownFieldSetLite.getDefaultInstance()) { - unknownFields = UnknownFieldSetLite.newInstance(); -@@ -180,9 +188,12 @@ final class MessageSetSchema implements Schema { - if (wireType == WireFormat.WIRETYPE_VARINT) { - position = ArrayDecoders.decodeVarint32(data, position, registers); - typeId = registers.int1; -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and -+ // handle this better. - extension = -- (GeneratedMessageLite.GeneratedExtension) extensionSchema -- .findExtensionByNumber(registers.extensionRegistry, defaultInstance, typeId); -+ (GeneratedMessageLite.GeneratedExtension) -+ extensionSchema.findExtensionByNumber( -+ registers.extensionRegistry, defaultInstance, typeId); - continue; - } - break; -diff --git a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaLite.java b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaLite.java -index 9b92266..00cfe3b 100644 ---- a/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaLite.java -+++ b/java/core/src/main/java/com/google/protobuf/NewInstanceSchemaLite.java -@@ -33,7 +33,8 @@ package com.google.protobuf; - final class NewInstanceSchemaLite implements NewInstanceSchema { - @Override - public Object newInstance(Object defaultInstance) { -- return ((GeneratedMessageLite) defaultInstance) -- .dynamicMethod(GeneratedMessageLite.MethodToInvoke.NEW_MUTABLE_INSTANCE); -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this -+ // better. -+ return ((GeneratedMessageLite) defaultInstance).newMutableInstance(); - } - } -diff --git a/java/core/src/main/java/com/google/protobuf/Reader.java b/java/core/src/main/java/com/google/protobuf/Reader.java -index 705096f..b99ee43 100644 ---- a/java/core/src/main/java/com/google/protobuf/Reader.java -+++ b/java/core/src/main/java/com/google/protobuf/Reader.java -@@ -158,6 +158,14 @@ interface Reader { - T readGroupBySchemaWithCheck(Schema schema, ExtensionRegistryLite extensionRegistry) - throws IOException; - -+ /** Read a message field from the wire format and merge the results into the given target. */ -+ void mergeMessageField(T target, Schema schema, ExtensionRegistryLite extensionRegistry) -+ throws IOException; -+ -+ /** Read a group field from the wire format and merge the results into the given target. */ -+ void mergeGroupField(T target, Schema schema, ExtensionRegistryLite extensionRegistry) -+ throws IOException; -+ - /** - * Reads and returns the next field of type {@code BYTES} and advances the reader to the next - * field. -diff --git a/java/core/src/main/java/com/google/protobuf/SchemaUtil.java b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java -index 4c8bb06..0e4c42c 100644 ---- a/java/core/src/main/java/com/google/protobuf/SchemaUtil.java -+++ b/java/core/src/main/java/com/google/protobuf/SchemaUtil.java -@@ -59,6 +59,8 @@ final class SchemaUtil { - * GeneratedMessageLite}. - */ - public static void requireGeneratedMessage(Class messageType) { -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle this -+ // better. - if (!GeneratedMessageLite.class.isAssignableFrom(messageType) - && GENERATED_MESSAGE_CLASS != null - && !GENERATED_MESSAGE_CLASS.isAssignableFrom(messageType)) { -@@ -808,6 +810,8 @@ final class SchemaUtil { - - private static Class getGeneratedMessageClass() { - try { -+ // TODO(b/248560713) decide if we're keeping support for Full in schema classes and handle -+ // this better. - return Class.forName("com.google.protobuf.GeneratedMessageV3"); - } catch (Throwable e) { - return null; -@@ -901,6 +905,7 @@ final class SchemaUtil { - - /** Filters unrecognized enum values in a list. */ - static UB filterUnknownEnumList( -+ Object containerMessage, - int number, - List enumList, - EnumLiteMap enumMap, -@@ -921,7 +926,9 @@ final class SchemaUtil { - } - ++writePos; - } else { -- unknownFields = storeUnknownEnum(number, enumValue, unknownFields, unknownFieldSchema); -+ unknownFields = -+ storeUnknownEnum( -+ containerMessage, number, enumValue, unknownFields, unknownFieldSchema); - } - } - if (writePos != size) { -@@ -931,7 +938,9 @@ final class SchemaUtil { - for (Iterator it = enumList.iterator(); it.hasNext(); ) { - int enumValue = it.next(); - if (enumMap.findValueByNumber(enumValue) == null) { -- unknownFields = storeUnknownEnum(number, enumValue, unknownFields, unknownFieldSchema); -+ unknownFields = -+ storeUnknownEnum( -+ containerMessage, number, enumValue, unknownFields, unknownFieldSchema); - it.remove(); - } - } -@@ -941,6 +950,7 @@ final class SchemaUtil { - - /** Filters unrecognized enum values in a list. */ - static UB filterUnknownEnumList( -+ Object containerMessage, - int number, - List enumList, - EnumVerifier enumVerifier, -@@ -961,7 +971,9 @@ final class SchemaUtil { - } - ++writePos; - } else { -- unknownFields = storeUnknownEnum(number, enumValue, unknownFields, unknownFieldSchema); -+ unknownFields = -+ storeUnknownEnum( -+ containerMessage, number, enumValue, unknownFields, unknownFieldSchema); - } - } - if (writePos != size) { -@@ -971,7 +983,9 @@ final class SchemaUtil { - for (Iterator it = enumList.iterator(); it.hasNext(); ) { - int enumValue = it.next(); - if (!enumVerifier.isInRange(enumValue)) { -- unknownFields = storeUnknownEnum(number, enumValue, unknownFields, unknownFieldSchema); -+ unknownFields = -+ storeUnknownEnum( -+ containerMessage, number, enumValue, unknownFields, unknownFieldSchema); - it.remove(); - } - } -@@ -981,9 +995,13 @@ final class SchemaUtil { - - /** Stores an unrecognized enum value as an unknown value. */ - static UB storeUnknownEnum( -- int number, int enumValue, UB unknownFields, UnknownFieldSchema unknownFieldSchema) { -+ Object containerMessage, -+ int number, -+ int enumValue, -+ UB unknownFields, -+ UnknownFieldSchema unknownFieldSchema) { - if (unknownFields == null) { -- unknownFields = unknownFieldSchema.newBuilder(); -+ unknownFields = unknownFieldSchema.getBuilderFromMessage(containerMessage); - } - unknownFieldSchema.addVarint(unknownFields, number, enumValue); - return unknownFields; -diff --git a/java/core/src/main/java/com/google/protobuf/TextFormat.java b/java/core/src/main/java/com/google/protobuf/TextFormat.java -index bbc0f8d..a5709cc 100644 ---- a/java/core/src/main/java/com/google/protobuf/TextFormat.java -+++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java -@@ -593,7 +593,7 @@ public final class TextFormat { - - case MESSAGE: - case GROUP: -- print((Message) value, generator); -+ print((MessageOrBuilder) value, generator); - break; - } - } -diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java -index b2cb7be..37a14e2 100644 ---- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java -+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java -@@ -388,7 +388,7 @@ public final class UnknownFieldSetLite { - // Package private for unsafe experimental runtime. - void storeField(int tag, Object value) { - checkMutable(); -- ensureCapacity(); -+ ensureCapacity(count + 1); - - tags[count] = tag; - objects[count] = value; -@@ -396,13 +396,23 @@ public final class UnknownFieldSetLite { - } - - /** Ensures that our arrays are long enough to store more metadata. */ -- private void ensureCapacity() { -- if (count == tags.length) { -- int increment = count < (MIN_CAPACITY / 2) ? MIN_CAPACITY : count >> 1; -- int newLength = count + increment; -+ private void ensureCapacity(int minCapacity) { -+ if (minCapacity > this.tags.length) { -+ // Increase by at least 50% -+ int newCapacity = count + count / 2; -+ -+ // Or new capacity if higher -+ if (newCapacity < minCapacity) { -+ newCapacity = minCapacity; -+ } -+ -+ // And never less than MIN_CAPACITY -+ if (newCapacity < MIN_CAPACITY) { -+ newCapacity = MIN_CAPACITY; -+ } - -- tags = Arrays.copyOf(tags, newLength); -- objects = Arrays.copyOf(objects, newLength); -+ this.tags = Arrays.copyOf(this.tags, newCapacity); -+ this.objects = Arrays.copyOf(this.objects, newCapacity); - } - } - -@@ -487,4 +497,18 @@ public final class UnknownFieldSetLite { - } - return this; - } -+ -+ UnknownFieldSetLite mergeFrom(UnknownFieldSetLite other) { -+ if (other.equals(getDefaultInstance())) { -+ return this; -+ } -+ -+ checkMutable(); -+ int newCount = this.count + other.count; -+ ensureCapacity(newCount); -+ System.arraycopy(other.tags, 0, tags, this.count, other.count); -+ System.arraycopy(other.objects, 0, objects, this.count, other.count); -+ this.count = newCount; -+ return this; -+ } - } -diff --git a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLiteSchema.java b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLiteSchema.java -index ffd7232..2cfdeca 100644 ---- a/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLiteSchema.java -+++ b/java/core/src/main/java/com/google/protobuf/UnknownFieldSetLiteSchema.java -@@ -122,10 +122,14 @@ class UnknownFieldSetLiteSchema - } - - @Override -- UnknownFieldSetLite merge(UnknownFieldSetLite message, UnknownFieldSetLite other) { -- return other.equals(UnknownFieldSetLite.getDefaultInstance()) -- ? message -- : UnknownFieldSetLite.mutableCopyOf(message, other); -+ UnknownFieldSetLite merge(UnknownFieldSetLite target, UnknownFieldSetLite source) { -+ if (UnknownFieldSetLite.getDefaultInstance().equals(source)) { -+ return target; -+ } -+ if (UnknownFieldSetLite.getDefaultInstance().equals(target)) { -+ return UnknownFieldSetLite.mutableCopyOf(target, source); -+ } -+ return target.mergeFrom(source); - } - - @Override -diff --git a/java/lite/src/test/java/com/google/protobuf/LiteTest.java b/java/lite/src/test/java/com/google/protobuf/LiteTest.java -index 140df72..7481b69 100644 ---- a/java/lite/src/test/java/com/google/protobuf/LiteTest.java -+++ b/java/lite/src/test/java/com/google/protobuf/LiteTest.java -@@ -49,15 +49,6 @@ import com.google.protobuf.UnittestLite.TestAllTypesLite.RepeatedGroup; - import com.google.protobuf.UnittestLite.TestAllTypesLiteOrBuilder; - import com.google.protobuf.UnittestLite.TestHugeFieldNumbersLite; - import com.google.protobuf.UnittestLite.TestNestedExtensionLite; --import map_lite_test.MapTestProto.TestMap; --import map_lite_test.MapTestProto.TestMap.MessageValue; --import protobuf_unittest.NestedExtensionLite; --import protobuf_unittest.NonNestedExtensionLite; --import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar; --import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime; --import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo; --import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals; --import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof; - import java.io.ByteArrayInputStream; - import java.io.ByteArrayOutputStream; - import java.io.IOException; -@@ -69,6 +60,15 @@ import java.util.Arrays; - import java.util.Iterator; - import java.util.List; - import junit.framework.TestCase; -+import map_lite_test.MapTestProto.TestMap; -+import map_lite_test.MapTestProto.TestMap.MessageValue; -+import protobuf_unittest.NestedExtensionLite; -+import protobuf_unittest.NonNestedExtensionLite; -+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Bar; -+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.BarPrime; -+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.Foo; -+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestOneofEquals; -+import protobuf_unittest.lite_equals_and_hash.LiteEqualsAndHash.TestRecursiveOneof; - - /** - * Test lite runtime. -@@ -181,16 +181,24 @@ public class LiteTest extends TestCase { - TestAllExtensionsLite message = TestUtilLite.getAllLiteExtensionsSet(); - - // Test serialized size is memoized -- message.memoizedSerializedSize = -1; -+ assertEquals( -+ GeneratedMessageLite.UNINITIALIZED_SERIALIZED_SIZE, -+ message.getMemoizedSerializedSize()); - int size = message.getSerializedSize(); - assertTrue(size > 0); -- assertEquals(size, message.memoizedSerializedSize); -+ assertEquals(size, message.getMemoizedSerializedSize()); -+ message.clearMemoizedSerializedSize(); -+ assertEquals( -+ GeneratedMessageLite.UNINITIALIZED_SERIALIZED_SIZE, -+ message.getMemoizedSerializedSize()); - - // Test hashCode is memoized -- assertEquals(0, message.memoizedHashCode); -+ assertTrue(message.hashCodeIsNotMemoized()); - int hashCode = message.hashCode(); -- assertTrue(hashCode != 0); -- assertEquals(hashCode, message.memoizedHashCode); -+ assertFalse(message.hashCodeIsNotMemoized()); -+ assertEquals(hashCode, message.getMemoizedHashCode()); -+ message.clearMemoizedHashCode(); -+ assertTrue(message.hashCodeIsNotMemoized()); - - // Test isInitialized is memoized - Field memo = message.getClass().getDeclaredField("memoizedIsInitialized"); -diff --git a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java -index 352376e..2aa0916 100644 ---- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java -+++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java -@@ -35,6 +35,7 @@ import com.google.errorprone.annotations.CanIgnoreReturnValue; - import com.google.protobuf.Descriptors.Descriptor; - import com.google.protobuf.Descriptors.FieldDescriptor; - import com.google.protobuf.FieldMask; -+import com.google.protobuf.GeneratedMessage; - import com.google.protobuf.Message; - import java.util.ArrayList; - import java.util.List; -@@ -304,9 +305,12 @@ final class FieldMaskTree { - // so we don't create unnecessary empty messages. - continue; - } -- String childPath = path.isEmpty() ? entry.getKey() : path + "." + entry.getKey(); -- Message.Builder childBuilder = ((Message) destination.getField(field)).toBuilder(); -- merge(entry.getValue(), childPath, (Message) source.getField(field), childBuilder, options); -+ // This is a mess because of java proto API 1 still hanging around. -+ Message.Builder childBuilder = -+ destination instanceof GeneratedMessage.Builder -+ ? destination.getFieldBuilder(field) -+ : ((Message) destination.getField(field)).toBuilder(); -+ merge(entry.getValue(), path, (Message) source.getField(field), childBuilder, options); - destination.setField(field, childBuilder.buildPartial()); - continue; - } -diff --git a/src/google/protobuf/compiler/java/java_enum_field.cc b/src/google/protobuf/compiler/java/java_enum_field.cc -index d96ac7d..15626f9 100644 ---- a/src/google/protobuf/compiler/java/java_enum_field.cc -+++ b/src/google/protobuf/compiler/java/java_enum_field.cc -@@ -110,13 +110,6 @@ void SetEnumVariables(const FieldDescriptor* descriptor, int messageBitIndex, - (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); - -- // For repeated fields, one bit is used for whether the array is immutable -- // in the parsing constructor. -- (*variables)["get_mutable_bit_parser"] = -- GenerateGetBitMutableLocal(builderBitIndex); -- (*variables)["set_mutable_bit_parser"] = -- GenerateSetBitMutableLocal(builderBitIndex); -- - (*variables)["get_has_field_bit_from_local"] = - GenerateGetBitFromLocal(builderBitIndex); - (*variables)["set_has_field_bit_to_local"] = -@@ -314,32 +307,26 @@ void ImmutableEnumFieldGenerator::GenerateBuildingCode( - printer->Print(variables_, "result.$name$_ = $name$_;\n"); - } - --void ImmutableEnumFieldGenerator::GenerateParsingCode( -+void ImmutableEnumFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - if (SupportUnknownEnumValue(descriptor_->file())) { - printer->Print(variables_, -- "int rawValue = input.readEnum();\n" -- "$set_has_field_bit_message$\n" -- "$name$_ = rawValue;\n"); -+ "$name$_ = input.readEnum();\n" -+ "$set_has_field_bit_builder$\n"); - } else { - printer->Print(variables_, -- "int rawValue = input.readEnum();\n" -- " @SuppressWarnings(\"deprecation\")\n" -- "$type$ value = $type$.$for_number$(rawValue);\n" -- "if (value == null) {\n" -- " unknownFields.mergeVarintField($number$, rawValue);\n" -+ "int tmpRaw = input.readEnum();\n" -+ "$type$ tmpValue =\n" -+ " $type$.forNumber(tmpRaw);\n" -+ "if (tmpValue == null) {\n" -+ " mergeUnknownVarintField($number$, tmpRaw);\n" - "} else {\n" -- " $set_has_field_bit_message$\n" -- " $name$_ = rawValue;\n" -+ " $name$_ = tmpRaw;\n" -+ " $set_has_field_bit_builder$\n" - "}\n"); - } - } - --void ImmutableEnumFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- // noop for enums --} -- - void ImmutableEnumFieldGenerator::GenerateSerializationCode( - io::Printer* printer) const { - printer->Print(variables_, -@@ -502,6 +489,11 @@ void ImmutableEnumOneofFieldGenerator::GenerateBuilderMembers( - printer->Annotate("{", "}", descriptor_); - } - -+void ImmutableEnumOneofFieldGenerator::GenerateBuilderClearCode( -+ io::Printer* printer) const { -+ // No-op: Enum fields in oneofs are correctly cleared by clearing the oneof -+} -+ - void ImmutableEnumOneofFieldGenerator::GenerateBuildingCode( - io::Printer* printer) const { - printer->Print(variables_, -@@ -522,7 +514,7 @@ void ImmutableEnumOneofFieldGenerator::GenerateMergingCode( - } - } - --void ImmutableEnumOneofFieldGenerator::GenerateParsingCode( -+void ImmutableEnumOneofFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - if (SupportUnknownEnumValue(descriptor_->file())) { - printer->Print(variables_, -@@ -532,10 +524,10 @@ void ImmutableEnumOneofFieldGenerator::GenerateParsingCode( - } else { - printer->Print(variables_, - "int rawValue = input.readEnum();\n" -- "@SuppressWarnings(\"deprecation\")\n" -- "$type$ value = $type$.$for_number$(rawValue);\n" -+ "$type$ value =\n" -+ " $type$.forNumber(rawValue);\n" - "if (value == null) {\n" -- " unknownFields.mergeVarintField($number$, rawValue);\n" -+ " mergeUnknownVarintField($number$, rawValue);\n" - "} else {\n" - " $set_oneof_case_message$;\n" - " $oneof_name$_ = rawValue;\n" -@@ -914,36 +906,29 @@ void RepeatedImmutableEnumFieldGenerator::GenerateBuildingCode( - "result.$name$_ = $name$_;\n"); - } - --void RepeatedImmutableEnumFieldGenerator::GenerateParsingCode( -+void RepeatedImmutableEnumFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - // Read and store the enum - if (SupportUnknownEnumValue(descriptor_->file())) { - printer->Print(variables_, -- "int rawValue = input.readEnum();\n" -- "if (!$get_mutable_bit_parser$) {\n" -- " $name$_ = new java.util.ArrayList();\n" -- " $set_mutable_bit_parser$;\n" -- "}\n" -- "$name$_.add(rawValue);\n"); -+ "int tmpRaw = input.readEnum();\n" -+ "ensure$capitalized_name$IsMutable();\n" -+ "$name$_.add(tmpRaw);\n"); - } else { -- printer->Print( -- variables_, -- "int rawValue = input.readEnum();\n" -- "@SuppressWarnings(\"deprecation\")\n" -- "$type$ value = $type$.$for_number$(rawValue);\n" -- "if (value == null) {\n" -- " unknownFields.mergeVarintField($number$, rawValue);\n" -- "} else {\n" -- " if (!$get_mutable_bit_parser$) {\n" -- " $name$_ = new java.util.ArrayList();\n" -- " $set_mutable_bit_parser$;\n" -- " }\n" -- " $name$_.add(rawValue);\n" -- "}\n"); -+ printer->Print(variables_, -+ "int tmpRaw = input.readEnum();\n" -+ "$type$ tmpValue =\n" -+ " $type$.forNumber(tmpRaw);\n" -+ "if (tmpValue == null) {\n" -+ " mergeUnknownVarintField($number$, tmpRaw);\n" -+ "} else {\n" -+ " ensure$capitalized_name$IsMutable();\n" -+ " $name$_.add(tmpRaw);\n" -+ "}\n"); - } - } - --void RepeatedImmutableEnumFieldGenerator::GenerateParsingCodeFromPacked( -+void RepeatedImmutableEnumFieldGenerator::GenerateBuilderParsingCodeFromPacked( - io::Printer* printer) const { - // Wrap GenerateParsingCode's contents with a while loop. - -@@ -953,7 +938,7 @@ void RepeatedImmutableEnumFieldGenerator::GenerateParsingCodeFromPacked( - "while(input.getBytesUntilLimit() > 0) {\n"); - printer->Indent(); - -- GenerateParsingCode(printer); -+ GenerateBuilderParsingCode(printer); - - printer->Outdent(); - printer->Print(variables_, -@@ -961,15 +946,6 @@ void RepeatedImmutableEnumFieldGenerator::GenerateParsingCodeFromPacked( - "input.popLimit(oldLimit);\n"); - } - --void RepeatedImmutableEnumFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- printer->Print( -- variables_, -- "if ($get_mutable_bit_parser$) {\n" -- " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" -- "}\n"); --} -- - void RepeatedImmutableEnumFieldGenerator::GenerateSerializationCode( - io::Printer* printer) const { - if (descriptor_->is_packed()) { -diff --git a/src/google/protobuf/compiler/java/java_enum_field.h b/src/google/protobuf/compiler/java/java_enum_field.h -index 95c7db5..318e013 100644 ---- a/src/google/protobuf/compiler/java/java_enum_field.h -+++ b/src/google/protobuf/compiler/java/java_enum_field.h -@@ -64,24 +64,24 @@ class ImmutableEnumFieldGenerator : public ImmutableFieldGenerator { - - // implements ImmutableFieldGenerator - // --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - protected: - const FieldDescriptor* descriptor_; -@@ -99,15 +99,16 @@ class ImmutableEnumOneofFieldGenerator : public ImmutableEnumFieldGenerator { - Context* context); - ~ImmutableEnumOneofFieldGenerator(); - -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; - - private: - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableEnumOneofFieldGenerator); -@@ -121,25 +122,26 @@ class RepeatedImmutableEnumFieldGenerator : public ImmutableFieldGenerator { - ~RepeatedImmutableEnumFieldGenerator(); - - // implements ImmutableFieldGenerator --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingCodeFromPacked(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCodeFromPacked( -+ io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - private: - const FieldDescriptor* descriptor_; -diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc -index 2f775a6..229b3b3 100644 ---- a/src/google/protobuf/compiler/java/java_field.cc -+++ b/src/google/protobuf/compiler/java/java_field.cc -@@ -185,7 +185,7 @@ static inline void ReportUnexpectedPackedFieldsCall(io::Printer* printer) { - // but this method should be overridden. - // - This FieldGenerator doesn't support packing, and this method - // should never have been called. -- GOOGLE_LOG(FATAL) << "GenerateParsingCodeFromPacked() " -+ GOOGLE_LOG(FATAL) << "GenerateBuilderParsingCodeFromPacked() " - << "called on field generator that does not support packing."; - } - -@@ -193,7 +193,7 @@ static inline void ReportUnexpectedPackedFieldsCall(io::Printer* printer) { - - ImmutableFieldGenerator::~ImmutableFieldGenerator() {} - --void ImmutableFieldGenerator::GenerateParsingCodeFromPacked( -+void ImmutableFieldGenerator::GenerateBuilderParsingCodeFromPacked( - io::Printer* printer) const { - ReportUnexpectedPackedFieldsCall(printer); - } -diff --git a/src/google/protobuf/compiler/java/java_field.h b/src/google/protobuf/compiler/java/java_field.h -index 9d04dc8..7e0cd77 100644 ---- a/src/google/protobuf/compiler/java/java_field.h -+++ b/src/google/protobuf/compiler/java/java_field.h -@@ -76,9 +76,8 @@ class ImmutableFieldGenerator { - virtual void GenerateBuilderClearCode(io::Printer* printer) const = 0; - virtual void GenerateMergingCode(io::Printer* printer) const = 0; - virtual void GenerateBuildingCode(io::Printer* printer) const = 0; -- virtual void GenerateParsingCode(io::Printer* printer) const = 0; -- virtual void GenerateParsingCodeFromPacked(io::Printer* printer) const; -- virtual void GenerateParsingDoneCode(io::Printer* printer) const = 0; -+ virtual void GenerateBuilderParsingCode(io::Printer* printer) const = 0; -+ virtual void GenerateBuilderParsingCodeFromPacked(io::Printer* printer) const; - virtual void GenerateSerializationCode(io::Printer* printer) const = 0; - virtual void GenerateSerializedSizeCode(io::Printer* printer) const = 0; - virtual void GenerateFieldBuilderInitializationCode( -diff --git a/src/google/protobuf/compiler/java/java_map_field.cc b/src/google/protobuf/compiler/java/java_map_field.cc -index 5db199d..1fb8f1e 100644 ---- a/src/google/protobuf/compiler/java/java_map_field.cc -+++ b/src/google/protobuf/compiler/java/java_map_field.cc -@@ -138,13 +138,6 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, - descriptor->options().deprecated() ? "@java.lang.Deprecated " : ""; - (*variables)["on_changed"] = "onChanged();"; - -- // For repeated fields, one bit is used for whether the array is immutable -- // in the parsing constructor. -- (*variables)["get_mutable_bit_parser"] = -- GenerateGetBitMutableLocal(builderBitIndex); -- (*variables)["set_mutable_bit_parser"] = -- GenerateSetBitMutableLocal(builderBitIndex); -- - (*variables)["default_entry"] = - (*variables)["capitalized_name"] + "DefaultEntryHolder.defaultEntry"; - (*variables)["map_field_parameter"] = (*variables)["default_entry"]; -@@ -681,27 +674,19 @@ void ImmutableMapFieldGenerator::GenerateBuildingCode( - "result.$name$_.makeImmutable();\n"); - } - --void ImmutableMapFieldGenerator::GenerateParsingCode( -+void ImmutableMapFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { -- printer->Print(variables_, -- "if (!$get_mutable_bit_parser$) {\n" -- " $name$_ = com.google.protobuf.MapField.newMapField(\n" -- " $map_field_parameter$);\n" -- " $set_mutable_bit_parser$;\n" -- "}\n"); - if (!SupportUnknownEnumValue(descriptor_->file()) && - GetJavaType(ValueField(descriptor_)) == JAVATYPE_ENUM) { - printer->Print( - variables_, - "com.google.protobuf.ByteString bytes = input.readBytes();\n" - "com.google.protobuf.MapEntry<$type_parameters$>\n" -- "$name$__ = $default_entry$.getParserForType().parseFrom(bytes);\n"); -- printer->Print( -- variables_, -+ "$name$__ = $default_entry$.getParserForType().parseFrom(bytes);\n" - "if ($value_enum_type$.forNumber($name$__.getValue()) == null) {\n" -- " unknownFields.mergeLengthDelimitedField($number$, bytes);\n" -+ " mergeUnknownLengthDelimitedField($number$, bytes);\n" - "} else {\n" -- " $name$_.getMutableMap().put(\n" -+ " internalGetMutable$capitalized_name$().getMutableMap().put(\n" - " $name$__.getKey(), $name$__.getValue());\n" - "}\n"); - } else { -@@ -710,16 +695,11 @@ void ImmutableMapFieldGenerator::GenerateParsingCode( - "com.google.protobuf.MapEntry<$type_parameters$>\n" - "$name$__ = input.readMessage(\n" - " $default_entry$.getParserForType(), extensionRegistry);\n" -- "$name$_.getMutableMap().put(\n" -+ "internalGetMutable$capitalized_name$().getMutableMap().put(\n" - " $name$__.getKey(), $name$__.getValue());\n"); - } - } - --void ImmutableMapFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- // Nothing to do here. --} -- - void ImmutableMapFieldGenerator::GenerateSerializationCode( - io::Printer* printer) const { - printer->Print(variables_, -diff --git a/src/google/protobuf/compiler/java/java_map_field.h b/src/google/protobuf/compiler/java/java_map_field.h -index 2ff1f76..4e46222 100644 ---- a/src/google/protobuf/compiler/java/java_map_field.h -+++ b/src/google/protobuf/compiler/java/java_map_field.h -@@ -46,23 +46,23 @@ class ImmutableMapFieldGenerator : public ImmutableFieldGenerator { - ~ImmutableMapFieldGenerator(); - - // implements ImmutableFieldGenerator --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ - std::string GetBoxedType() const; - - private: -diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc -index 6623595..43c325a 100644 ---- a/src/google/protobuf/compiler/java/java_message.cc -+++ b/src/google/protobuf/compiler/java/java_message.cc -@@ -51,6 +51,7 @@ - #include - #include - #include -+#include - #include - #include - #include -@@ -367,6 +368,7 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { - "}\n" - "\n"); - -+ // TODO(b/248149118): Remove this superfluous override. - printer->Print( - "@java.lang.Override\n" - "public final com.google.protobuf.UnknownFieldSet\n" -@@ -374,10 +376,6 @@ void ImmutableMessageGenerator::Generate(io::Printer* printer) { - " return this.unknownFields;\n" - "}\n"); - -- if (context_->HasGeneratedMethods(descriptor_)) { -- GenerateParsingConstructor(printer); -- } -- - GenerateDescriptorMethods(printer); - - // Nested types -@@ -624,9 +622,9 @@ void ImmutableMessageGenerator::GenerateMessageSerializationMethods( - } - - if (descriptor_->options().message_set_wire_format()) { -- printer->Print("unknownFields.writeAsMessageSetTo(output);\n"); -+ printer->Print("getUnknownFields().writeAsMessageSetTo(output);\n"); - } else { -- printer->Print("unknownFields.writeTo(output);\n"); -+ printer->Print("getUnknownFields().writeTo(output);\n"); - } - - printer->Outdent(); -@@ -655,9 +653,10 @@ void ImmutableMessageGenerator::GenerateMessageSerializationMethods( - } - - if (descriptor_->options().message_set_wire_format()) { -- printer->Print("size += unknownFields.getSerializedSizeAsMessageSet();\n"); -+ printer->Print( -+ "size += getUnknownFields().getSerializedSizeAsMessageSet();\n"); - } else { -- printer->Print("size += unknownFields.getSerializedSize();\n"); -+ printer->Print("size += getUnknownFields().getSerializedSize();\n"); - } - - printer->Print( -@@ -1054,7 +1053,8 @@ void ImmutableMessageGenerator::GenerateEqualsAndHashCode( - // false for non-canonical ordering when running in LITE_RUNTIME but it's - // the best we can do. - printer->Print( -- "if (!unknownFields.equals(other.unknownFields)) return false;\n"); -+ "if (!getUnknownFields().equals(other.getUnknownFields())) return " -+ "false;\n"); - if (descriptor_->extension_range_count() > 0) { - printer->Print( - "if (!getExtensionFields().equals(other.getExtensionFields()))\n" -@@ -1128,7 +1128,7 @@ void ImmutableMessageGenerator::GenerateEqualsAndHashCode( - printer->Print("hash = hashFields(hash, getExtensionFields());\n"); - } - -- printer->Print("hash = (29 * hash) + unknownFields.hashCode();\n"); -+ printer->Print("hash = (29 * hash) + getUnknownFields().hashCode();\n"); - printer->Print( - "memoizedHashCode = hash;\n" - "return hash;\n"); -@@ -1153,186 +1153,33 @@ void ImmutableMessageGenerator::GenerateExtensionRegistrationCode( - } - } - --// =================================================================== --void ImmutableMessageGenerator::GenerateParsingConstructor( -- io::Printer* printer) { -- std::unique_ptr sorted_fields( -- SortFieldsByNumber(descriptor_)); -- -- printer->Print( -- "private $classname$(\n" -- " com.google.protobuf.CodedInputStream input,\n" -- " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" -- " throws com.google.protobuf.InvalidProtocolBufferException {\n", -- "classname", descriptor_->name()); -- printer->Indent(); -- -- // Initialize all fields to default. -- printer->Print( -- "this();\n" -- "if (extensionRegistry == null) {\n" -- " throw new java.lang.NullPointerException();\n" -- "}\n"); -- -- // Use builder bits to track mutable repeated fields. -- int totalBuilderBits = 0; -- for (int i = 0; i < descriptor_->field_count(); i++) { -- const ImmutableFieldGenerator& field = -- field_generators_.get(descriptor_->field(i)); -- totalBuilderBits += field.GetNumBitsForBuilder(); -- } -- int totalBuilderInts = (totalBuilderBits + 31) / 32; -- for (int i = 0; i < totalBuilderInts; i++) { -- printer->Print("int mutable_$bit_field_name$ = 0;\n", "bit_field_name", -- GetBitFieldName(i)); -- } -- -- printer->Print( -- "com.google.protobuf.UnknownFieldSet.Builder unknownFields =\n" -- " com.google.protobuf.UnknownFieldSet.newBuilder();\n"); -- -- printer->Print("try {\n"); -- printer->Indent(); -- -- printer->Print( -- "boolean done = false;\n" -- "while (!done) {\n"); -- printer->Indent(); -- -- printer->Print( -- "int tag = input.readTag();\n" -- "switch (tag) {\n"); -- printer->Indent(); -- -- printer->Print( -- "case 0:\n" // zero signals EOF / limit reached -- " done = true;\n" -- " break;\n"); -- -- for (int i = 0; i < descriptor_->field_count(); i++) { -- const FieldDescriptor* field = sorted_fields[i]; -- uint32 tag = WireFormatLite::MakeTag( -- field->number(), WireFormat::WireTypeForFieldType(field->type())); -- -- printer->Print("case $tag$: {\n", "tag", -- StrCat(static_cast(tag))); -- printer->Indent(); -- -- field_generators_.get(field).GenerateParsingCode(printer); -- -- printer->Outdent(); -- printer->Print( -- " break;\n" -- "}\n"); -- -- if (field->is_packable()) { -- // To make packed = true wire compatible, we generate parsing code from a -- // packed version of this field regardless of field->options().packed(). -- uint32 packed_tag = WireFormatLite::MakeTag( -- field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED); -- printer->Print("case $tag$: {\n", "tag", -- StrCat(static_cast(packed_tag))); -- printer->Indent(); -- -- field_generators_.get(field).GenerateParsingCodeFromPacked(printer); -- -- printer->Outdent(); -- printer->Print( -- " break;\n" -- "}\n"); -- } -- } -- -- printer->Print( -- "default: {\n" -- " if (!parseUnknownField(\n" -- " input, unknownFields, extensionRegistry, tag)) {\n" -- " done = true;\n" // it's an endgroup tag -- " }\n" -- " break;\n" -- "}\n"); -- -- printer->Outdent(); -- printer->Outdent(); -- printer->Print( -- " }\n" // switch (tag) -- "}\n"); // while (!done) -- -- printer->Outdent(); -- printer->Print( -- "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" -- " throw e.setUnfinishedMessage(this);\n" -- "} catch (java.io.IOException e) {\n" -- " throw new com.google.protobuf.InvalidProtocolBufferException(\n" -- " e).setUnfinishedMessage(this);\n" -- "} finally {\n"); -- printer->Indent(); -- -- // Make repeated field list immutable. -- for (int i = 0; i < descriptor_->field_count(); i++) { -- const FieldDescriptor* field = sorted_fields[i]; -- field_generators_.get(field).GenerateParsingDoneCode(printer); -- } -- -- // Make unknown fields immutable. -- printer->Print("this.unknownFields = unknownFields.build();\n"); -- -- // Make extensions immutable. -- printer->Print("makeExtensionsImmutable();\n"); -- -- printer->Outdent(); -- printer->Outdent(); -- printer->Print( -- " }\n" // finally -- "}\n"); --} -- - // =================================================================== - void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { - printer->Print( - "$visibility$ static final com.google.protobuf.Parser<$classname$>\n" -- " PARSER = new com.google.protobuf.AbstractParser<$classname$>() {\n", -- "visibility", -- ExposePublicParser(descriptor_->file()) ? "@java.lang.Deprecated public" -- : "private", -- "classname", descriptor_->name()); -- printer->Indent(); -- printer->Print( -- "@java.lang.Override\n" -- "public $classname$ parsePartialFrom(\n" -- " com.google.protobuf.CodedInputStream input,\n" -- " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" -- " throws com.google.protobuf.InvalidProtocolBufferException {\n", -- "classname", descriptor_->name()); -- if (context_->HasGeneratedMethods(descriptor_)) { -- printer->Print(" return new $classname$(input, extensionRegistry);\n", -- "classname", descriptor_->name()); -- } else { -- // When parsing constructor isn't generated, use builder to parse -- // messages. Note, will fallback to use reflection based mergeFieldFrom() -- // in AbstractMessage.Builder. -- printer->Indent(); -- printer->Print( -- "Builder builder = newBuilder();\n" -- "try {\n" -- " builder.mergeFrom(input, extensionRegistry);\n" -- "} catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" -- " throw e.setUnfinishedMessage(builder.buildPartial());\n" -- "} catch (java.io.IOException e) {\n" -- " throw new com.google.protobuf.InvalidProtocolBufferException(\n" -- " e.getMessage()).setUnfinishedMessage(\n" -- " builder.buildPartial());\n" -- "}\n" -- "return builder.buildPartial();\n"); -- printer->Outdent(); -- } -- printer->Print("}\n"); -- printer->Outdent(); -- printer->Print( -+ " PARSER = new com.google.protobuf.AbstractParser<$classname$>() {\n" -+ " @java.lang.Override\n" -+ " public $classname$ parsePartialFrom(\n" -+ " com.google.protobuf.CodedInputStream input,\n" -+ " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" -+ " throws com.google.protobuf.InvalidProtocolBufferException {\n" -+ " Builder builder = newBuilder();\n" -+ " try {\n" -+ " builder.mergeFrom(input, extensionRegistry);\n" -+ " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" -+ " throw e.setUnfinishedMessage(builder.buildPartial());\n" -+ " } catch (com.google.protobuf.UninitializedMessageException e) {\n" -+ " throw " -+ "e.asInvalidProtocolBufferException().setUnfinishedMessage(builder." -+ "buildPartial());\n" -+ " } catch (java.io.IOException e) {\n" -+ " throw new com.google.protobuf.InvalidProtocolBufferException(e)\n" -+ " .setUnfinishedMessage(builder.buildPartial());\n" -+ " }\n" -+ " return builder.buildPartial();\n" -+ " }\n" - "};\n" -- "\n"); -- -- printer->Print( -+ "\n" - "public static com.google.protobuf.Parser<$classname$> parser() {\n" - " return PARSER;\n" - "}\n" -@@ -1342,6 +1189,9 @@ void ImmutableMessageGenerator::GenerateParser(io::Printer* printer) { - " return PARSER;\n" - "}\n" - "\n", -+ "visibility", -+ ExposePublicParser(descriptor_->file()) ? "@java.lang.Deprecated public" -+ : "private", - "classname", descriptor_->name()); - } - -diff --git a/src/google/protobuf/compiler/java/java_message_builder.cc b/src/google/protobuf/compiler/java/java_message_builder.cc -index 320852b..24ea648 100644 ---- a/src/google/protobuf/compiler/java/java_message_builder.cc -+++ b/src/google/protobuf/compiler/java/java_message_builder.cc -@@ -58,6 +58,9 @@ namespace protobuf { - namespace compiler { - namespace java { - -+using internal::WireFormat; -+using internal::WireFormatLite; -+ - namespace { - std::string MapValueImmutableClassdName(const Descriptor* descriptor, - ClassNameResolver* name_resolver) { -@@ -285,43 +288,63 @@ void MessageBuilderGenerator::GenerateDescriptorMethods(io::Printer* printer) { - - void MessageBuilderGenerator::GenerateCommonBuilderMethods( - io::Printer* printer) { -+ // Decide if we really need to have the "maybeForceBuilderInitialization()" -+ // method. -+ // TODO(b/249158148): Remove the need for this entirely -+ bool need_maybe_force_builder_init = false; -+ for (int i = 0; i < descriptor_->field_count(); i++) { -+ if (descriptor_->field(i)->message_type() != nullptr && -+ !IsRealOneof(descriptor_->field(i)) && -+ HasHasbit(descriptor_->field(i))) { -+ need_maybe_force_builder_init = true; -+ break; -+ } -+ } -+ -+ const char* force_builder_init = need_maybe_force_builder_init -+ ? " maybeForceBuilderInitialization();" -+ : ""; -+ - printer->Print( - "// Construct using $classname$.newBuilder()\n" - "private Builder() {\n" -- " maybeForceBuilderInitialization();\n" -+ "$force_builder_init$\n" - "}\n" - "\n", -- "classname", name_resolver_->GetImmutableClassName(descriptor_)); -+ "classname", name_resolver_->GetImmutableClassName(descriptor_), -+ "force_builder_init", force_builder_init); - - printer->Print( - "private Builder(\n" - " com.google.protobuf.GeneratedMessage$ver$.BuilderParent parent) {\n" - " super(parent);\n" -- " maybeForceBuilderInitialization();\n" -+ "$force_builder_init$\n" - "}\n", - "classname", name_resolver_->GetImmutableClassName(descriptor_), "ver", -- GeneratedCodeVersionSuffix()); -+ GeneratedCodeVersionSuffix(), "force_builder_init", force_builder_init); - -- printer->Print( -- "private void maybeForceBuilderInitialization() {\n" -- " if (com.google.protobuf.GeneratedMessage$ver$\n" -- " .alwaysUseFieldBuilders) {\n", -- "ver", GeneratedCodeVersionSuffix()); -+ if (need_maybe_force_builder_init) { -+ printer->Print( -+ "private void maybeForceBuilderInitialization() {\n" -+ " if (com.google.protobuf.GeneratedMessage$ver$\n" -+ " .alwaysUseFieldBuilders) {\n", -+ "ver", GeneratedCodeVersionSuffix()); - -- printer->Indent(); -- printer->Indent(); -- for (int i = 0; i < descriptor_->field_count(); i++) { -- if (!IsRealOneof(descriptor_->field(i))) { -- field_generators_.get(descriptor_->field(i)) -- .GenerateFieldBuilderInitializationCode(printer); -+ printer->Indent(); -+ printer->Indent(); -+ for (int i = 0; i < descriptor_->field_count(); i++) { -+ if (!IsRealOneof(descriptor_->field(i))) { -+ field_generators_.get(descriptor_->field(i)) -+ .GenerateFieldBuilderInitializationCode(printer); -+ } - } -- } -- printer->Outdent(); -- printer->Outdent(); -+ printer->Outdent(); -+ printer->Outdent(); - -- printer->Print( -- " }\n" -- "}\n"); -+ printer->Print( -+ " }\n" -+ "}\n"); -+ } - - printer->Print( - "@java.lang.Override\n" -@@ -331,10 +354,8 @@ void MessageBuilderGenerator::GenerateCommonBuilderMethods( - printer->Indent(); - - for (int i = 0; i < descriptor_->field_count(); i++) { -- if (!IsRealOneof(descriptor_->field(i))) { -- field_generators_.get(descriptor_->field(i)) -- .GenerateBuilderClearCode(printer); -- } -+ field_generators_.get(descriptor_->field(i)) -+ .GenerateBuilderClearCode(printer); - } - - for (auto oneof : oneofs_) { -@@ -575,7 +596,7 @@ void MessageBuilderGenerator::GenerateCommonBuilderMethods( - printer->Print(" this.mergeExtensionFields(other);\n"); - } - -- printer->Print(" this.mergeUnknownFields(other.unknownFields);\n"); -+ printer->Print(" this.mergeUnknownFields(other.getUnknownFields());\n"); - - printer->Print(" onChanged();\n"); - -@@ -596,20 +617,92 @@ void MessageBuilderGenerator::GenerateBuilderParsingMethods( - " com.google.protobuf.CodedInputStream input,\n" - " com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n" - " throws java.io.IOException {\n" -- " $classname$ parsedMessage = null;\n" -+ " if (extensionRegistry == null) {\n" -+ " throw new java.lang.NullPointerException();\n" -+ " }\n" - " try {\n" -- " parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);\n" -+ " boolean done = false;\n" -+ " while (!done) {\n" -+ " int tag = input.readTag();\n" -+ " switch (tag) {\n" -+ " case 0:\n" // zero signals EOF / limit reached -+ " done = true;\n" -+ " break;\n"); -+ printer->Indent(); // method -+ printer->Indent(); // try -+ printer->Indent(); // while -+ printer->Indent(); // switch -+ GenerateBuilderFieldParsingCases(printer); -+ printer->Outdent(); // switch -+ printer->Outdent(); // while -+ printer->Outdent(); // try -+ printer->Outdent(); // method -+ printer->Print( -+ " default: {\n" -+ " if (!super.parseUnknownField(input, extensionRegistry, tag)) " -+ "{\n" -+ " done = true; // was an endgroup tag\n" -+ " }\n" -+ " break;\n" -+ " } // default:\n" -+ " } // switch (tag)\n" -+ " } // while (!done)\n" - " } catch (com.google.protobuf.InvalidProtocolBufferException e) {\n" -- " parsedMessage = ($classname$) e.getUnfinishedMessage();\n" - " throw e.unwrapIOException();\n" - " } finally {\n" -- " if (parsedMessage != null) {\n" -- " mergeFrom(parsedMessage);\n" -- " }\n" -- " }\n" -+ " onChanged();\n" -+ " } // finally\n" - " return this;\n" -- "}\n", -- "classname", name_resolver_->GetImmutableClassName(descriptor_)); -+ "}\n"); -+} -+ -+void MessageBuilderGenerator::GenerateBuilderFieldParsingCases( -+ io::Printer* printer) { -+ std::unique_ptr sorted_fields( -+ SortFieldsByNumber(descriptor_)); -+ for (int i = 0; i < descriptor_->field_count(); i++) { -+ const FieldDescriptor* field = sorted_fields[i]; -+ GenerateBuilderFieldParsingCase(printer, field); -+ if (field->is_packable()) { -+ GenerateBuilderPackedFieldParsingCase(printer, field); -+ } -+ } -+} -+ -+void MessageBuilderGenerator::GenerateBuilderFieldParsingCase( -+ io::Printer* printer, const FieldDescriptor* field) { -+ uint32_t tag = WireFormatLite::MakeTag( -+ field->number(), WireFormat::WireTypeForFieldType(field->type())); -+ std::string tagString = StrCat(static_cast(tag)); -+ printer->Print("case $tag$: {\n", "tag", tagString); -+ printer->Indent(); -+ -+ field_generators_.get(field).GenerateBuilderParsingCode(printer); -+ -+ printer->Outdent(); -+ printer->Print( -+ " break;\n" -+ "} // case $tag$\n", -+ "tag", tagString); -+} -+ -+void MessageBuilderGenerator::GenerateBuilderPackedFieldParsingCase( -+ io::Printer* printer, const FieldDescriptor* field) { -+ // To make packed = true wire compatible, we generate parsing code from a -+ // packed version of this field regardless of field->options().packed(). -+ uint32_t tag = WireFormatLite::MakeTag( -+ field->number(), WireFormatLite::WIRETYPE_LENGTH_DELIMITED); -+ std::string tagString = StrCat(static_cast(tag)); -+ printer->Print("case $tag$: {\n", "tag", tagString); -+ printer->Indent(); -+ -+ field_generators_.get(field).GenerateBuilderParsingCodeFromPacked(printer); -+ -+ printer->Outdent(); -+ printer->Print( -+ " break;\n" -+ "} // case $tag$\n", -+ "tag", tagString); - } - - // =================================================================== -diff --git a/src/google/protobuf/compiler/java/java_message_builder.h b/src/google/protobuf/compiler/java/java_message_builder.h -index fcd73b3..96f289a 100644 ---- a/src/google/protobuf/compiler/java/java_message_builder.h -+++ b/src/google/protobuf/compiler/java/java_message_builder.h -@@ -70,6 +70,11 @@ class MessageBuilderGenerator { - void GenerateCommonBuilderMethods(io::Printer* printer); - void GenerateDescriptorMethods(io::Printer* printer); - void GenerateBuilderParsingMethods(io::Printer* printer); -+ void GenerateBuilderFieldParsingCases(io::Printer* printer); -+ void GenerateBuilderFieldParsingCase(io::Printer* printer, -+ const FieldDescriptor* field); -+ void GenerateBuilderPackedFieldParsingCase(io::Printer* printer, -+ const FieldDescriptor* field); - void GenerateIsInitialized(io::Printer* printer); - - const Descriptor* descriptor_; -diff --git a/src/google/protobuf/compiler/java/java_message_field.cc b/src/google/protobuf/compiler/java/java_message_field.cc -index 96c0c11..a1db832 100644 ---- a/src/google/protobuf/compiler/java/java_message_field.cc -+++ b/src/google/protobuf/compiler/java/java_message_field.cc -@@ -102,13 +102,6 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, - (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); - -- // For repeated fields, one bit is used for whether the array is immutable -- // in the parsing constructor. -- (*variables)["get_mutable_bit_parser"] = -- GenerateGetBitMutableLocal(builderBitIndex); -- (*variables)["set_mutable_bit_parser"] = -- GenerateSetBitMutableLocal(builderBitIndex); -- - (*variables)["get_has_field_bit_from_local"] = - GenerateGetBitFromLocal(builderBitIndex); - (*variables)["set_has_field_bit_to_local"] = -@@ -456,35 +449,21 @@ void ImmutableMessageFieldGenerator::GenerateBuildingCode( - } - } - --void ImmutableMessageFieldGenerator::GenerateParsingCode( -+void ImmutableMessageFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { -- printer->Print(variables_, -- "$type$.Builder subBuilder = null;\n" -- "if ($is_field_present_message$) {\n" -- " subBuilder = $name$_.toBuilder();\n" -- "}\n"); -- - if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { - printer->Print(variables_, -- "$name$_ = input.readGroup($number$, $type$.$get_parser$,\n" -- " extensionRegistry);\n"); -+ "input.readGroup($number$,\n" -+ " get$capitalized_name$FieldBuilder().getBuilder(),\n" -+ " extensionRegistry);\n" -+ "$set_has_field_bit_builder$\n"); - } else { - printer->Print(variables_, -- "$name$_ = input.readMessage($type$.$get_parser$, " -- "extensionRegistry);\n"); -+ "input.readMessage(\n" -+ " get$capitalized_name$FieldBuilder().getBuilder(),\n" -+ " extensionRegistry);\n" -+ "$set_has_field_bit_builder$\n"); - } -- -- printer->Print(variables_, -- "if (subBuilder != null) {\n" -- " subBuilder.mergeFrom($name$_);\n" -- " $name$_ = subBuilder.buildPartial();\n" -- "}\n" -- "$set_has_field_bit_message$\n"); --} -- --void ImmutableMessageFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- // noop for messages. - } - - void ImmutableMessageFieldGenerator::GenerateSerializationCode( -@@ -736,6 +715,15 @@ void ImmutableMessageOneofFieldGenerator::GenerateBuilderMembers( - printer->Annotate("{", "}", descriptor_); - } - -+void ImmutableMessageOneofFieldGenerator::GenerateBuilderClearCode( -+ io::Printer* printer) const { -+ // Make sure the builder gets cleared. -+ printer->Print(variables_, -+ "if ($name$Builder_ != null) {\n" -+ " $name$Builder_.clear();\n" -+ "}\n"); -+} -+ - void ImmutableMessageOneofFieldGenerator::GenerateBuildingCode( - io::Printer* printer) const { - printer->Print(variables_, "if ($has_oneof_case_message$) {\n"); -@@ -756,32 +744,21 @@ void ImmutableMessageOneofFieldGenerator::GenerateMergingCode( - "merge$capitalized_name$(other.get$capitalized_name$());\n"); - } - --void ImmutableMessageOneofFieldGenerator::GenerateParsingCode( -+void ImmutableMessageOneofFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { -- printer->Print(variables_, -- "$type$.Builder subBuilder = null;\n" -- "if ($has_oneof_case_message$) {\n" -- " subBuilder = (($type$) $oneof_name$_).toBuilder();\n" -- "}\n"); -- - if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { -- printer->Print( -- variables_, -- "$oneof_name$_ = input.readGroup($number$, $type$.$get_parser$,\n" -- " extensionRegistry);\n"); -+ printer->Print(variables_, -+ "input.readGroup($number$,\n" -+ " get$capitalized_name$FieldBuilder().getBuilder(),\n" -+ " extensionRegistry);\n" -+ "$set_oneof_case_message$;\n"); - } else { -- printer->Print( -- variables_, -- "$oneof_name$_ =\n" -- " input.readMessage($type$.$get_parser$, extensionRegistry);\n"); -+ printer->Print(variables_, -+ "input.readMessage(\n" -+ " get$capitalized_name$FieldBuilder().getBuilder(),\n" -+ " extensionRegistry);\n" -+ "$set_oneof_case_message$;\n"); - } -- -- printer->Print(variables_, -- "if (subBuilder != null) {\n" -- " subBuilder.mergeFrom(($type$) $oneof_name$_);\n" -- " $oneof_name$_ = subBuilder.buildPartial();\n" -- "}\n"); -- printer->Print(variables_, "$set_oneof_case_message$;\n"); - } - - void ImmutableMessageOneofFieldGenerator::GenerateSerializationCode( -@@ -1232,10 +1209,12 @@ void RepeatedImmutableMessageFieldGenerator::GenerateInitializationCode( - void RepeatedImmutableMessageFieldGenerator::GenerateBuilderClearCode( - io::Printer* printer) const { - PrintNestedBuilderCondition(printer, -- "$name$_ = java.util.Collections.emptyList();\n" -- "$clear_mutable_bit_builder$;\n", -+ "$name$_ = java.util.Collections.emptyList();\n", - -+ "$name$_ = null;\n" - "$name$Builder_.clear();\n"); -+ -+ printer->Print(variables_, "$clear_mutable_bit_builder$;\n"); - } - - void RepeatedImmutableMessageFieldGenerator::GenerateMergingCode( -@@ -1290,34 +1269,25 @@ void RepeatedImmutableMessageFieldGenerator::GenerateBuildingCode( - "result.$name$_ = $name$Builder_.build();\n"); - } - --void RepeatedImmutableMessageFieldGenerator::GenerateParsingCode( -+void RepeatedImmutableMessageFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { -- printer->Print(variables_, -- "if (!$get_mutable_bit_parser$) {\n" -- " $name$_ = new java.util.ArrayList<$type$>();\n" -- " $set_mutable_bit_parser$;\n" -- "}\n"); -- - if (GetType(descriptor_) == FieldDescriptor::TYPE_GROUP) { -- printer->Print( -- variables_, -- "$name$_.add(input.readGroup($number$, $type$.$get_parser$,\n" -- " extensionRegistry));\n"); -+ printer->Print(variables_, -+ "$type$ m =\n" -+ " input.readGroup($number$,\n" -+ " $type$.$get_parser$,\n" -+ " extensionRegistry);\n"); - } else { -- printer->Print( -- variables_, -- "$name$_.add(\n" -- " input.readMessage($type$.$get_parser$, extensionRegistry));\n"); -+ printer->Print(variables_, -+ "$type$ m =\n" -+ " input.readMessage(\n" -+ " $type$.$get_parser$,\n" -+ " extensionRegistry);\n"); - } --} -- --void RepeatedImmutableMessageFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- printer->Print( -- variables_, -- "if ($get_mutable_bit_parser$) {\n" -- " $name$_ = java.util.Collections.unmodifiableList($name$_);\n" -- "}\n"); -+ PrintNestedBuilderCondition(printer, -+ "ensure$capitalized_name$IsMutable();\n" -+ "$name$_.add(m);\n", -+ "$name$Builder_.addMessage(m);\n"); - } - - void RepeatedImmutableMessageFieldGenerator::GenerateSerializationCode( -diff --git a/src/google/protobuf/compiler/java/java_message_field.h b/src/google/protobuf/compiler/java/java_message_field.h -index 36fa492..07e0256 100644 ---- a/src/google/protobuf/compiler/java/java_message_field.h -+++ b/src/google/protobuf/compiler/java/java_message_field.h -@@ -65,24 +65,24 @@ class ImmutableMessageFieldGenerator : public ImmutableFieldGenerator { - - // implements ImmutableFieldGenerator - // --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - protected: - const FieldDescriptor* descriptor_; -@@ -110,13 +110,14 @@ class ImmutableMessageOneofFieldGenerator - Context* context); - ~ImmutableMessageOneofFieldGenerator(); - -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; - - private: - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableMessageOneofFieldGenerator); -@@ -130,24 +131,24 @@ class RepeatedImmutableMessageFieldGenerator : public ImmutableFieldGenerator { - ~RepeatedImmutableMessageFieldGenerator(); - - // implements ImmutableFieldGenerator --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - protected: - const FieldDescriptor* descriptor_; -diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc -index f06e8fb..b562a2a 100644 ---- a/src/google/protobuf/compiler/java/java_primitive_field.cc -+++ b/src/google/protobuf/compiler/java/java_primitive_field.cc -@@ -166,13 +166,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, - (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); - -- // For repeated fields, one bit is used for whether the array is immutable -- // in the parsing constructor. -- (*variables)["get_mutable_bit_parser"] = -- GenerateGetBitMutableLocal(builderBitIndex); -- (*variables)["set_mutable_bit_parser"] = -- GenerateSetBitMutableLocal(builderBitIndex); -- - (*variables)["get_has_field_bit_from_local"] = - GenerateGetBitFromLocal(builderBitIndex); - (*variables)["set_has_field_bit_to_local"] = -@@ -352,16 +345,11 @@ void ImmutablePrimitiveFieldGenerator::GenerateBuildingCode( - } - } - --void ImmutablePrimitiveFieldGenerator::GenerateParsingCode( -+void ImmutablePrimitiveFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - printer->Print(variables_, -- "$set_has_field_bit_message$\n" -- "$name$_ = input.read$capitalized_type$();\n"); --} -- --void ImmutablePrimitiveFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- // noop for primitives. -+ "$name$_ = input.read$capitalized_type$();\n" -+ "$set_has_field_bit_builder$\n"); - } - - void ImmutablePrimitiveFieldGenerator::GenerateSerializationCode( -@@ -570,6 +558,12 @@ void ImmutablePrimitiveOneofFieldGenerator::GenerateBuilderMembers( - printer->Annotate("{", "}", descriptor_); - } - -+void ImmutablePrimitiveOneofFieldGenerator::GenerateBuilderClearCode( -+ io::Printer* printer) const { -+ // No-Op: When a primitive field is in a oneof, clearing the oneof clears that -+ // field. -+} -+ - void ImmutablePrimitiveOneofFieldGenerator::GenerateBuildingCode( - io::Printer* printer) const { - printer->Print(variables_, -@@ -584,7 +578,7 @@ void ImmutablePrimitiveOneofFieldGenerator::GenerateMergingCode( - "set$capitalized_name$(other.get$capitalized_name$());\n"); - } - --void ImmutablePrimitiveOneofFieldGenerator::GenerateParsingCode( -+void ImmutablePrimitiveOneofFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - printer->Print(variables_, - "$set_oneof_case_message$;\n" -@@ -844,38 +838,24 @@ void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuildingCode( - "result.$name$_ = $name$_;\n"); - } - --void RepeatedImmutablePrimitiveFieldGenerator::GenerateParsingCode( -+void RepeatedImmutablePrimitiveFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - printer->Print(variables_, -- "if (!$get_mutable_bit_parser$) {\n" -- " $name$_ = $create_list$;\n" -- " $set_mutable_bit_parser$;\n" -- "}\n" -- "$repeated_add$(input.read$capitalized_type$());\n"); -+ "$type$ v = input.read$capitalized_type$();\n" -+ "ensure$capitalized_name$IsMutable();\n" -+ "$repeated_add$(v);\n"); - } - --void RepeatedImmutablePrimitiveFieldGenerator::GenerateParsingCodeFromPacked( -- io::Printer* printer) const { -- printer->Print( -- variables_, -- "int length = input.readRawVarint32();\n" -- "int limit = input.pushLimit(length);\n" -- "if (!$get_mutable_bit_parser$ && input.getBytesUntilLimit() > 0) {\n" -- " $name$_ = $create_list$;\n" -- " $set_mutable_bit_parser$;\n" -- "}\n" -- "while (input.getBytesUntilLimit() > 0) {\n" -- " $repeated_add$(input.read$capitalized_type$());\n" -- "}\n" -- "input.popLimit(limit);\n"); --} -- --void RepeatedImmutablePrimitiveFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -+void RepeatedImmutablePrimitiveFieldGenerator:: -+ GenerateBuilderParsingCodeFromPacked(io::Printer* printer) const { - printer->Print(variables_, -- "if ($get_mutable_bit_parser$) {\n" -- " $name_make_immutable$; // C\n" -- "}\n"); -+ "int length = input.readRawVarint32();\n" -+ "int limit = input.pushLimit(length);\n" -+ "ensure$capitalized_name$IsMutable();\n" -+ "while (input.getBytesUntilLimit() > 0) {\n" -+ " $repeated_add$(input.read$capitalized_type$());\n" -+ "}\n" -+ "input.popLimit(limit);\n"); - } - - void RepeatedImmutablePrimitiveFieldGenerator::GenerateSerializationCode( -diff --git a/src/google/protobuf/compiler/java/java_primitive_field.h b/src/google/protobuf/compiler/java/java_primitive_field.h -index db20750..e74044a 100644 ---- a/src/google/protobuf/compiler/java/java_primitive_field.h -+++ b/src/google/protobuf/compiler/java/java_primitive_field.h -@@ -65,24 +65,24 @@ class ImmutablePrimitiveFieldGenerator : public ImmutableFieldGenerator { - - // implements ImmutableFieldGenerator - // --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - protected: - const FieldDescriptor* descriptor_; -@@ -101,13 +101,14 @@ class ImmutablePrimitiveOneofFieldGenerator - int builderBitIndex, Context* context); - ~ImmutablePrimitiveOneofFieldGenerator(); - -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; - - private: - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutablePrimitiveOneofFieldGenerator); -@@ -122,25 +123,26 @@ class RepeatedImmutablePrimitiveFieldGenerator - virtual ~RepeatedImmutablePrimitiveFieldGenerator(); - - // implements ImmutableFieldGenerator --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingCodeFromPacked(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCodeFromPacked( -+ io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - private: - const FieldDescriptor* descriptor_; -diff --git a/src/google/protobuf/compiler/java/java_string_field.cc b/src/google/protobuf/compiler/java/java_string_field.cc -index 548f898..6edac5e 100644 ---- a/src/google/protobuf/compiler/java/java_string_field.cc -+++ b/src/google/protobuf/compiler/java/java_string_field.cc -@@ -118,13 +118,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, - (*variables)["set_mutable_bit_builder"] = GenerateSetBit(builderBitIndex); - (*variables)["clear_mutable_bit_builder"] = GenerateClearBit(builderBitIndex); - -- // For repeated fields, one bit is used for whether the array is immutable -- // in the parsing constructor. -- (*variables)["get_mutable_bit_parser"] = -- GenerateGetBitMutableLocal(builderBitIndex); -- (*variables)["set_mutable_bit_parser"] = -- GenerateSetBitMutableLocal(builderBitIndex); -- - (*variables)["get_has_field_bit_from_local"] = - GenerateGetBitFromLocal(builderBitIndex); - (*variables)["set_has_field_bit_to_local"] = -@@ -413,26 +406,19 @@ void ImmutableStringFieldGenerator::GenerateBuildingCode( - printer->Print(variables_, "result.$name$_ = $name$_;\n"); - } - --void ImmutableStringFieldGenerator::GenerateParsingCode( -+void ImmutableStringFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - if (CheckUtf8(descriptor_)) { - printer->Print(variables_, -- "java.lang.String s = input.readStringRequireUtf8();\n" -- "$set_has_field_bit_message$\n" -- "$name$_ = s;\n"); -+ "$name$_ = input.readStringRequireUtf8();\n" -+ "$set_has_field_bit_builder$\n"); - } else { - printer->Print(variables_, -- "com.google.protobuf.ByteString bs = input.readBytes();\n" -- "$set_has_field_bit_message$\n" -- "$name$_ = bs;\n"); -+ "$name$_ = input.readBytes();\n" -+ "$set_has_field_bit_builder$\n"); - } - } - --void ImmutableStringFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- // noop for strings. --} -- - void ImmutableStringFieldGenerator::GenerateSerializationCode( - io::Printer* printer) const { - printer->Print(variables_, -@@ -661,6 +647,11 @@ void ImmutableStringOneofFieldGenerator::GenerateBuilderMembers( - "}\n"); - } - -+void ImmutableStringOneofFieldGenerator::GenerateBuilderClearCode( -+ io::Printer* printer) const { -+ // No-Op: String fields in oneofs are correctly cleared by clearing the oneof -+} -+ - void ImmutableStringOneofFieldGenerator::GenerateMergingCode( - io::Printer* printer) const { - // Allow a slight breach of abstraction here in order to avoid forcing -@@ -679,7 +670,7 @@ void ImmutableStringOneofFieldGenerator::GenerateBuildingCode( - "}\n"); - } - --void ImmutableStringOneofFieldGenerator::GenerateParsingCode( -+void ImmutableStringOneofFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - if (CheckUtf8(descriptor_)) { - printer->Print(variables_, -@@ -969,35 +960,21 @@ void RepeatedImmutableStringFieldGenerator::GenerateBuildingCode( - "result.$name$_ = $name$_;\n"); - } - --void RepeatedImmutableStringFieldGenerator::GenerateParsingCode( -+void RepeatedImmutableStringFieldGenerator::GenerateBuilderParsingCode( - io::Printer* printer) const { - if (CheckUtf8(descriptor_)) { - printer->Print(variables_, -- "java.lang.String s = input.readStringRequireUtf8();\n"); -+ "java.lang.String s = input.readStringRequireUtf8();\n" -+ "ensure$capitalized_name$IsMutable();\n" -+ "$name$_.add(s);\n"); - } else { - printer->Print(variables_, -- "com.google.protobuf.ByteString bs = input.readBytes();\n"); -- } -- printer->Print(variables_, -- "if (!$get_mutable_bit_parser$) {\n" -- " $name$_ = new com.google.protobuf.LazyStringArrayList();\n" -- " $set_mutable_bit_parser$;\n" -- "}\n"); -- if (CheckUtf8(descriptor_)) { -- printer->Print(variables_, "$name$_.add(s);\n"); -- } else { -- printer->Print(variables_, "$name$_.add(bs);\n"); -+ "com.google.protobuf.ByteString bs = input.readBytes();\n" -+ "ensure$capitalized_name$IsMutable();\n" -+ "$name$_.add(bs);\n"); - } - } - --void RepeatedImmutableStringFieldGenerator::GenerateParsingDoneCode( -- io::Printer* printer) const { -- printer->Print(variables_, -- "if ($get_mutable_bit_parser$) {\n" -- " $name$_ = $name$_.getUnmodifiableView();\n" -- "}\n"); --} -- - void RepeatedImmutableStringFieldGenerator::GenerateSerializationCode( - io::Printer* printer) const { - printer->Print(variables_, -diff --git a/src/google/protobuf/compiler/java/java_string_field.h b/src/google/protobuf/compiler/java/java_string_field.h -index 1c00ae8..4aaabfb 100644 ---- a/src/google/protobuf/compiler/java/java_string_field.h -+++ b/src/google/protobuf/compiler/java/java_string_field.h -@@ -65,24 +65,24 @@ class ImmutableStringFieldGenerator : public ImmutableFieldGenerator { - - // implements ImmutableFieldGenerator - // --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - protected: - const FieldDescriptor* descriptor_; -@@ -102,13 +102,14 @@ class ImmutableStringOneofFieldGenerator - ~ImmutableStringOneofFieldGenerator(); - - private: -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; - - GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ImmutableStringOneofFieldGenerator); - }; -@@ -121,24 +122,24 @@ class RepeatedImmutableStringFieldGenerator : public ImmutableFieldGenerator { - ~RepeatedImmutableStringFieldGenerator(); - - // implements ImmutableFieldGenerator --------------------------------------- -- int GetNumBitsForMessage() const; -- int GetNumBitsForBuilder() const; -- void GenerateInterfaceMembers(io::Printer* printer) const; -- void GenerateMembers(io::Printer* printer) const; -- void GenerateBuilderMembers(io::Printer* printer) const; -- void GenerateInitializationCode(io::Printer* printer) const; -- void GenerateBuilderClearCode(io::Printer* printer) const; -- void GenerateMergingCode(io::Printer* printer) const; -- void GenerateBuildingCode(io::Printer* printer) const; -- void GenerateParsingCode(io::Printer* printer) const; -- void GenerateParsingDoneCode(io::Printer* printer) const; -- void GenerateSerializationCode(io::Printer* printer) const; -- void GenerateSerializedSizeCode(io::Printer* printer) const; -- void GenerateFieldBuilderInitializationCode(io::Printer* printer) const; -- void GenerateEqualsCode(io::Printer* printer) const; -- void GenerateHashCode(io::Printer* printer) const; -- -- std::string GetBoxedType() const; -+ int GetNumBitsForMessage() const override; -+ int GetNumBitsForBuilder() const override; -+ void GenerateInterfaceMembers(io::Printer* printer) const override; -+ void GenerateMembers(io::Printer* printer) const override; -+ void GenerateBuilderMembers(io::Printer* printer) const override; -+ void GenerateInitializationCode(io::Printer* printer) const override; -+ void GenerateBuilderClearCode(io::Printer* printer) const override; -+ void GenerateMergingCode(io::Printer* printer) const override; -+ void GenerateBuildingCode(io::Printer* printer) const override; -+ void GenerateBuilderParsingCode(io::Printer* printer) const override; -+ void GenerateSerializationCode(io::Printer* printer) const override; -+ void GenerateSerializedSizeCode(io::Printer* printer) const override; -+ void GenerateFieldBuilderInitializationCode( -+ io::Printer* printer) const override; -+ void GenerateEqualsCode(io::Printer* printer) const override; -+ void GenerateHashCode(io::Printer* printer) const override; -+ -+ std::string GetBoxedType() const override; - - private: - const FieldDescriptor* descriptor_; --- -2.25.1 - diff --git a/0007-add-coverage-compile-option.patch b/0007-add-coverage-compile-option.patch deleted file mode 100644 index 3e423f92540f22cd079bb6ca277b0ed9f632eb78..0000000000000000000000000000000000000000 --- a/0007-add-coverage-compile-option.patch +++ /dev/null @@ -1,49 +0,0 @@ -From c87fadf8be81e48697eca3308981ec3a684ecfcc Mon Sep 17 00:00:00 2001 -From: chengzrz -Date: Wed, 16 Nov 2022 17:47:10 +0800 -Subject: [PATCH] protobuf: add coverage compile option - -Type:testcode -reason:add coverage compile option - -Signed-off-by: chengzrz ---- - configure.ac | 7 +++++++ - src/Makefile.am | 4 ++++ - 2 files changed, 11 insertions(+) - -diff --git a/configure.ac b/configure.ac -index eb70a76..2ef1668 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -72,6 +72,13 @@ AC_ARG_WITH([protoc], - [use the given protoc command instead of building a new one when building tests (useful for cross-compiling)])], - [],[with_protoc=no]) - -+AC_ARG_ENABLE([coverage], -+ [AS_HELP_STRING([--enable-coverage], -+ [generate coverage report])], -+ [coverage=yes],[coverage=no]) -+ -+AM_CONDITIONAL([HAVE_COVERAGE], [test "x$coverage" == "xyes"]) -+ - # Checks for programs. - AC_PROG_CC - AC_PROG_CXX -diff --git a/src/Makefile.am b/src/Makefile.am -index 9af3db1..81d1418 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -21,6 +21,10 @@ endif - PROTOBUF_VERSION = 25:0:0 - PROTOBUF_OPT_FLAG += -Wl,-z,now -fstack-check - -+if HAVE_COVERAGE -+PROTOBUF_OPT_FLAG += -fprofile-arcs -ftest-coverage -+endif -+ - if GCC - # Turn on all warnings except for sign comparison (we ignore sign comparison - # in Google so our code base have tons of such warnings). --- -2.26.3 diff --git a/protobuf-all-3.14.0.tar.gz b/protobuf-all-3.15.6.tar.gz similarity index 49% rename from protobuf-all-3.14.0.tar.gz rename to protobuf-all-3.15.6.tar.gz index 3c6f0b8506f4b14e9c377a181d8572551c297e35..b85c7c5a6266e2242a031dfc9109d7fe7ae6e938 100644 Binary files a/protobuf-all-3.14.0.tar.gz and b/protobuf-all-3.15.6.tar.gz differ diff --git a/protobuf.spec b/protobuf.spec index c6d43369a2675cb497911029e2d8f13fcced0aaf..65e629f345088c24d15e23ba218465d820b54f84 100644 --- a/protobuf.spec +++ b/protobuf.spec @@ -7,21 +7,14 @@ Summary: Protocol Buffers - Google's data interchange format Name: protobuf -Version: 3.14.0 -Release: 8 +Version: 3.15.6 +Release: 1 License: BSD URL: https://github.com/protocolbuffers/protobuf Source: https://github.com/protocolbuffers/protobuf/releases/download/v%{version}%{?rcver}/%{name}-all-%{version}%{?rcver}.tar.gz Source1: protobuf-init.el -Patch9000: 0001-add-secure-compile-option-in-Makefile.patch -Patch9001: 0002-add-secure-compile-fs-check-in-Makefile.patch -Patch9002: 0003-fix-CVE-2021-22570.patch -Patch9003: 0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch -Patch9004: 0005-fix-CVE-2022-1941.patch -Patch9005: 0006-fix-CVE-2022-3171.patch -Patch9006: 0007-add-coverage-compile-option.patch - +Patch9001: 0001-add-secure-compile-fs-check-in-Makefile.patch BuildRequires: make autoconf automake emacs gcc-c++ libtool pkgconfig zlib-devel %description @@ -35,7 +28,6 @@ Summary: Protocol Buffers compiler Requires: %{name} = %{version}-%{release} Obsoletes: protobuf-emacs < %{version} Obsoletes: protobuf-emacs-el < %{version} -Obsoletes: protobuf2-compiler Requires: emacs-filesystem >= %{_emacs_version} %description compiler @@ -51,9 +43,6 @@ Provides: %{name}-static Provides: %{name}-vim Obsoletes: %{name}-static < %{version} Obsoletes: %{name}-vim < %{version} -Obsoletes: protobuf2-devel -Obsoletes: protobuf2-vim -Obsoletes: protobuf2-static %description devel @@ -76,8 +65,6 @@ Requires: %{name}-devel = %{version}-%{release} Requires: %{name}-lite = %{version}-%{release} Provides: %{name}-lite-static Obsoletes: %{name}-lite-static < %{version} -Obsoletes: protobuf2-lite-devel -Obsoletes: protobuf2-lite-static %description lite-devel This package contains development libraries built with @@ -185,8 +172,6 @@ rm java/core/src/test/java/com/google/protobuf/BoundedByteStringTest.java rm java/core/src/test/java/com/google/protobuf/RopeByteStringTest.java rm java/core/src/test/java/com/google/protobuf/RopeByteStringSubstringTest.java rm java/core/src/test/java/com/google/protobuf/TextFormatTest.java -rm java/core/src/test/java/com/google/protobuf/UnknownFieldSetTest.java -rm java/core/src/test/java/com/google/protobuf/UnknownFieldSetPerformanceTest.java rm -r java/util/src/test/java/com/google/protobuf/util rm -r java/util/src/main/java/com/google/protobuf/util @@ -270,11 +255,11 @@ install -p -m 0644 %{SOURCE1} %{buildroot}%{_emacs_sitestartdir} %files %doc CHANGES.txt CONTRIBUTORS.txt README.md %license LICENSE -%{_libdir}/libprotobuf.so.25* +%{_libdir}/libprotobuf.so.26* %files compiler %{_bindir}/protoc -%{_libdir}/libprotoc.so.25* +%{_libdir}/libprotoc.so.26* %{_emacs_sitelispdir}/%{name}/ %{_emacs_sitestartdir}/protobuf-init.el %license LICENSE @@ -293,7 +278,7 @@ install -p -m 0644 %{SOURCE1} %{buildroot}%{_emacs_sitestartdir} %{_datadir}/vim/vimfiles/syntax/proto.vim %files lite -%{_libdir}/libprotobuf-lite.so.25* +%{_libdir}/libprotobuf-lite.so.26* %files lite-devel %{_libdir}/libprotobuf-lite.so @@ -304,8 +289,8 @@ install -p -m 0644 %{SOURCE1} %{buildroot}%{_emacs_sitestartdir} %files -n python%{python3_pkgversion}-protobuf %dir %{python3_sitelib}/google %{python3_sitelib}/google/protobuf/ -%{python3_sitelib}/protobuf-%{version}%{?rcver}-py3.*.egg-info/ -%{python3_sitelib}/protobuf-%{version}%{?rcver}-py3.*-nspkg.pth +%{python3_sitelib}/protobuf-%{version}%{?rcver}-py3.?.egg-info/ +%{python3_sitelib}/protobuf-%{version}%{?rcver}-py3.?-nspkg.pth %doc python/README.md %doc examples/add_person.py examples/list_people.py examples/addressbook.proto %endif @@ -332,6 +317,9 @@ install -p -m 0644 %{SOURCE1} %{buildroot}%{_emacs_sitestartdir} %endif %changelog +* Thu May 04 2023 liksh - 3.15.6-1 +- Upgrade for openstack wallaby + * Wed Dec 06 2023 konglidong - 3.14.0-8 - obsolets protobuf2 for fix install conflict