diff options
author | CoprDistGit <infra@openeuler.org> | 2023-06-21 08:44:50 +0000 |
---|---|---|
committer | CoprDistGit <infra@openeuler.org> | 2023-06-21 08:44:50 +0000 |
commit | 2e312f4001c27e25bc432546aeb7a68c0b98cbe0 (patch) | |
tree | 988d44f3d3dd0adc383d94825eafb1487f950470 | |
parent | e6b69cb13b95a6aa4fb51d2a0baa7ad1acc7ff58 (diff) |
automatic import of protobufopeneuler23.03openeuler22.09openeuler20.03
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | 0001-add-secure-compile-option-in-Makefile.patch | 12 | ||||
-rw-r--r-- | 0002-add-secure-compile-fs-check-in-Makefile.patch | 26 | ||||
-rw-r--r-- | 0003-fix-CVE-2021-22570.patch | 73 | ||||
-rw-r--r-- | 0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch | 1272 | ||||
-rw-r--r-- | 0005-fix-CVE-2022-1941.patch | 368 | ||||
-rw-r--r-- | 0006-fix-CVE-2022-3171.patch | 4924 | ||||
-rw-r--r-- | protobuf-init.el | 6 | ||||
-rw-r--r-- | protobuf.spec | 431 | ||||
-rw-r--r-- | sources | 1 |
10 files changed, 7114 insertions, 0 deletions
@@ -0,0 +1 @@ +/protobuf-all-3.14.0.tar.gz diff --git a/0001-add-secure-compile-option-in-Makefile.patch b/0001-add-secure-compile-option-in-Makefile.patch new file mode 100644 index 0000000..988ba43 --- /dev/null +++ b/0001-add-secure-compile-option-in-Makefile.patch @@ -0,0 +1,12 @@ +diff --git a/src/Makefile.am b/src/Makefile.am +index f1099d9..9b7053b 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 + + 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 new file mode 100644 index 0000000..9a5ff52 --- /dev/null +++ b/0002-add-secure-compile-fs-check-in-Makefile.patch @@ -0,0 +1,26 @@ +From dddceb14106499f9fca17e75cdce458a205b102c Mon Sep 17 00:00:00 2001 +From: haozi007 <liuhao27@huawei.com> +Date: Sat, 20 Feb 2021 16:52:15 +0800 +Subject: [PATCH] add secure compile fs check in Makefile + +Signed-off-by: haozi007 <liuhao27@huawei.com> +--- + 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 new file mode 100644 index 0000000..fa6fb40 --- /dev/null +++ b/0003-fix-CVE-2021-22570.patch @@ -0,0 +1,73 @@ +From 5afdc4d13ac997204873e734b20c30b6efc253d1 Mon Sep 17 00:00:00 2001 +From: wangxiaochao <wangxiaochao2@huawei.com> +Date: Fri, 18 Mar 2022 14:46:35 +0800 +Subject: [PATCH] fix CVE-2021-22570 + +Signed-off-by: wangxiaochao <wangxiaochao2@huawei.com> + +--- + 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 new file mode 100644 index 0000000..a56f88d --- /dev/null +++ b/0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch @@ -0,0 +1,1272 @@ +From 8890b0a81e2f4b1de4a33cc6d81aba07655bde1a Mon Sep 17 00:00:00 2001 +From: wangxiaochao <wangxiaochao2@huawei.com> +Date: Thu, 24 Mar 2022 20:54:36 +0800 +Subject: [PATCH] Improve performance of parsing unknown fields in Java + +Signed-off-by: wangxiaochao <wangxiaochao2@huawei.com> + +--- + 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. + * + * <p>Every {@link Message} contains an {@code UnknownFieldSet} (and every {@link Message.Builder} +- * contains an {@link Builder}). ++ * contains a {@link Builder}). + * + * <p>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<Integer, Field> fields; ++ ++ /** ++ * Construct an {@code UnknownFieldSet} around the given map. ++ */ ++ UnknownFieldSet(TreeMap<Integer, Field> 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.<Integer, Field>emptyMap(), Collections.<Integer, Field>emptyMap()); +- +- /** +- * Construct an {@code UnknownFieldSet} around the given map. The map is expected to be immutable. +- */ +- UnknownFieldSet(final Map<Integer, Field> fields, final Map<Integer, Field> fieldsDescending) { +- this.fields = fields; +- this.fieldsDescending = fieldsDescending; +- } +- +- private final Map<Integer, Field> fields; +- +- /** A copy of {@link #fields} who's iterator order is reversed. */ +- private final Map<Integer, Field> fieldsDescending; ++ new UnknownFieldSet(new TreeMap<Integer, Field>()); + + + @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<Integer, Field> asMap() { +- return fields; ++ return (Map<Integer, Field>) 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<Integer, Field> entry : fields.entrySet()) { ++ public void writeTo(CodedOutputStream output) throws IOException { ++ for (Map.Entry<Integer, Field> 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<Integer, Field> entry : fields.entrySet()) { +- result += entry.getValue().getSerializedSize(entry.getKey()); ++ if (!fields.isEmpty()) { ++ for (Map.Entry<Integer, Field> 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<Integer, Field> entry : fields.entrySet()) { ++ public void writeAsMessageSetTo(CodedOutputStream output) throws IOException { ++ for (Map.Entry<Integer, Field> 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<Integer, Field> entry : fieldsDescending.entrySet()) { ++ for (Map.Entry<Integer, Field> 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<Integer, Field> entry : fieldsDescending.entrySet()) { ++ for (Map.Entry<Integer, Field> entry : fields.descendingMap().entrySet()) { + entry.getValue().writeAsMessageSetExtensionTo(entry.getKey(), writer); + } + } else { + // Write fields in ascending order. +- for (final Map.Entry<Integer, Field> entry : fields.entrySet()) { ++ for (Map.Entry<Integer, Field> 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<Integer, Field> entry : fields.entrySet()) { ++ for (Map.Entry<Integer, Field> 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<Integer, Field> 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<Integer, Field.Builder> 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. +- * +- * <p>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<Integer, Field> descendingFields = null; +- descendingFields = +- Collections.unmodifiableMap(((TreeMap<Integer, Field>) fields).descendingMap()); +- result = new UnknownFieldSet(Collections.unmodifiableMap(fields), descendingFields); ++ TreeMap<Integer, Field> fields = new TreeMap<>(); ++ for (Map.Entry<Integer, Field.Builder> 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<Integer, Field> descendingFields = null; +- descendingFields = +- Collections.unmodifiableMap(((TreeMap<Integer, Field>) fields).descendingMap()); +- return UnknownFieldSet.newBuilder().mergeFrom(new UnknownFieldSet(fields, descendingFields)); ++ Builder clone = UnknownFieldSet.newBuilder(); ++ for (Map.Entry<Integer, Field.Builder> 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<Integer, Field> entry : other.fields.entrySet()) { ++ for (Map.Entry<Integer, Field> 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. + * + * <p>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<Integer, Field>(); +- } +- 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<Integer, Field> asMap() { +- getFieldBuilder(0); // Force lastField to be built. ++ TreeMap<Integer, Field> fields = new TreeMap<>(); ++ for (Map.Entry<Integer, Field.Builder> 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 { + * <p>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<Long>(); +@@ -985,19 +988,19 @@ public final class UnknownFieldSet implements MessageLite { + } + if (!other.fixed64.isEmpty()) { + if (result.fixed64 == null) { +- result.fixed64 = new ArrayList<Long>(); ++ result.fixed64 = new ArrayList<>(); + } + result.fixed64.addAll(other.fixed64); + } + if (!other.lengthDelimited.isEmpty()) { + if (result.lengthDelimited == null) { +- result.lengthDelimited = new ArrayList<ByteString>(); ++ result.lengthDelimited = new ArrayList<>(); + } + result.lengthDelimited.addAll(other.lengthDelimited); + } + if (!other.group.isEmpty()) { + if (result.group == null) { +- result.group = new ArrayList<UnknownFieldSet>(); ++ 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<Long>(); ++ 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<Integer>(); ++ 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<Long>(); ++ 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<ByteString>(); ++ 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<UnknownFieldSet>(); ++ 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<ByteString> 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<ByteString> 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<Integer> 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<Integer, UnknownFieldSet.Field> mapFromBuilder = builder.asMap(); ++ assertThat(mapFromBuilder).isNotEmpty(); ++ UnknownFieldSet fields = builder.build(); ++ Map<Integer, UnknownFieldSet.Field> 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 @@ + <exclude>TestUtil.java</exclude> + <exclude>TypeRegistryTest.java</exclude> + <exclude>UnknownEnumValueTest.java</exclude> +- <exclude>UnknownFieldSetLiteTest.java</exclude> ++ <exclude>UnknownFieldSetLiteTest.java</exclude> ++ <exclude>UnknownFieldSetPerformanceTest.java</exclude> + <exclude>UnknownFieldSetTest.java</exclude> + <exclude>WellKnownTypesTest.java</exclude> + <exclude>WireFormatTest.java</exclude> +-- +2.30.0 + diff --git a/0005-fix-CVE-2022-1941.patch b/0005-fix-CVE-2022-1941.patch new file mode 100644 index 0000000..5974593 --- /dev/null +++ b/0005-fix-CVE-2022-1941.patch @@ -0,0 +1,368 @@ +From 55815e423bb82cc828836bbd60c79c1f9a195763 Mon Sep 17 00:00:00 2001 +From: Deanna Garcia <deannagarcia@google.com> +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<uint8>(*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<uint64>(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<uint64>(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<const uint8*>(message_data.data()), + static_cast<int>(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<uint8*>(&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 <google/protobuf/io/zero_copy_stream_impl.h> + #include <google/protobuf/io/zero_copy_stream_impl_lite.h> + #include <google/protobuf/descriptor.h> ++#include <google/protobuf/dynamic_message.h> + #include <google/protobuf/wire_format_lite.h> + #include <google/protobuf/testing/googletest.h> + #include <google/protobuf/stubs/logging.h> +@@ -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<const uint8*>(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<Message> msg( ++ factory ++ .GetPrototype( ++ ::proto2_wireformat_unittest::TestMessageSet::descriptor()) ++ ->New()); ++ msg->ParseFromString(data); ++ auto* reflection = msg->GetReflection(); ++ std::vector<const FieldDescriptor*> 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 new file mode 100644 index 0000000..9bd8d1a --- /dev/null +++ b/0006-fix-CVE-2022-3171.patch @@ -0,0 +1,4924 @@ +From 11ceafebd2234b5c3e559f6ae3d433bf0ed7f667 Mon Sep 17 00:00:00 2001 +From: chengzrz <czrzrichard@gmail.com> +Date: Mon, 17 Oct 2022 21:43:05 +0800 +Subject: [PATCH] fix cve-2022-3171 + +Signed-off-by: chengzrz <czrzrichard@gmail.com> +--- + .../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> T readMessage(Schema<T> schema, ExtensionRegistryLite extensionRegistry) + throws IOException { ++ T newInstance = schema.newInstance(); ++ mergeMessageField(newInstance, schema, extensionRegistry); ++ schema.makeImmutable(newInstance); ++ return newInstance; ++ } ++ ++ @Override ++ public <T> void mergeMessageField( ++ T target, Schema<T> 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> T readGroup(Schema<T> schema, ExtensionRegistryLite extensionRegistry) + throws IOException { ++ T newInstance = schema.newInstance(); ++ mergeGroupField(newInstance, schema, extensionRegistry); ++ schema.makeImmutable(newInstance); ++ return newInstance; ++ } ++ ++ @Override ++ public <T> void mergeGroupField( ++ T target, Schema<T> 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> T readMessage(Schema<T> schema, ExtensionRegistryLite extensionRegistry) +- throws IOException { ++ @Override ++ public <T> void mergeMessageField( ++ T target, Schema<T> schema, ExtensionRegistryLite extensionRegistry) throws IOException { ++ requireWireType(WIRETYPE_LENGTH_DELIMITED); ++ mergeMessageFieldInternal(target, schema, extensionRegistry); ++ } ++ ++ private <T> void mergeMessageFieldInternal( ++ T target, Schema<T> 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> T readGroup(Schema<T> schema, ExtensionRegistryLite extensionRegistry) ++ // Should have the same semantics of CodedInputStream#readMessage() ++ private <T> T readMessage(Schema<T> schema, ExtensionRegistryLite extensionRegistry) + throws IOException { ++ T newInstance = schema.newInstance(); ++ mergeMessageFieldInternal(newInstance, schema, extensionRegistry); ++ schema.makeImmutable(newInstance); ++ return newInstance; ++ } ++ ++ @Override ++ public <T> void mergeGroupField( ++ T target, Schema<T> schema, ExtensionRegistryLite extensionRegistry) throws IOException { ++ requireWireType(WIRETYPE_START_GROUP); ++ mergeGroupFieldInternal(target, schema, extensionRegistry); ++ } ++ ++ private <T> void mergeGroupFieldInternal( ++ T target, Schema<T> 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> T readGroup(Schema<T> 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<T extends FieldSet.FieldDescriptorLite<T>> { + * or UnknownFieldSetLite in lite runtime. + */ + abstract <UT, UB> 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<FieldDescriptor> { + + @Override + <UT, UB> UB parseExtension( ++ Object containerMessage, + Reader reader, + Object extensionObject, + ExtensionRegistryLite extensionRegistry, +@@ -202,7 +203,7 @@ final class ExtensionSchemaFull extends ExtensionSchema<FieldDescriptor> { + } else { + unknownFields = + SchemaUtil.storeUnknownEnum( +- fieldNumber, number, unknownFields, unknownFieldSchema); ++ containerMessage, fieldNumber, number, unknownFields, unknownFieldSchema); + } + } + value = enumList; +@@ -221,7 +222,7 @@ final class ExtensionSchemaFull extends ExtensionSchema<FieldDescriptor> { + 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<ExtensionDescriptor> { + + @Override + <UT, UB> UB parseExtension( ++ Object containerMessage, + Reader reader, + Object extensionObject, + ExtensionRegistryLite extensionRegistry, +@@ -178,6 +178,7 @@ final class ExtensionSchemaLite extends ExtensionSchema<ExtensionDescriptor> { + reader.readEnumList(list); + unknownFields = + SchemaUtil.filterUnknownEnumList( ++ containerMessage, + fieldNumber, + list, + extension.descriptor.getEnumType(), +@@ -199,7 +200,7 @@ final class ExtensionSchemaLite extends ExtensionSchema<ExtensionDescriptor> { + 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<ExtensionDescriptor> { + 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<ExtensionDescriptor> { + 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<ExtensionDescriptor> { + 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<T extends FieldSet.FieldDescriptorLite<T>> { + if (isImmutable) { + return; + } ++ for (int i = 0; i < fields.getNumArrayEntries(); ++i) { ++ Entry<T, Object> entry = fields.getArrayEntryAt(i); ++ if (entry.getValue() instanceof GeneratedMessageLite) { ++ ((GeneratedMessageLite<?, ?>) entry.getValue()).makeImmutable(); ++ } ++ } + fields.makeImmutable(); + isImmutable = true; + } +@@ -933,8 +940,27 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> { + this.isMutable = true; + } + +- /** Creates the FieldSet */ ++ /** ++ * Creates the FieldSet ++ * ++ * @throws UninitializedMessageException if a message field is missing required fields. ++ */ + public FieldSet<T> build() { ++ return buildImpl(false); ++ } ++ ++ /** Creates the FieldSet but does not validate that all required fields are present. */ ++ public FieldSet<T> 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<T> buildImpl(boolean partial) { + if (fields.isEmpty()) { + return FieldSet.emptySet(); + } +@@ -943,7 +969,7 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> { + 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<T> fieldSet = new FieldSet<>(fieldsForBuild); + fieldSet.hasLazyField = hasLazyField; +@@ -951,22 +977,22 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> { + } + + private static <T extends FieldDescriptorLite<T>> void replaceBuilders( +- SmallSortedMap<T, Object> fieldMap) { ++ SmallSortedMap<T, Object> fieldMap, boolean partial) { + for (int i = 0; i < fieldMap.getNumArrayEntries(); i++) { +- replaceBuilders(fieldMap.getArrayEntryAt(i)); ++ replaceBuilders(fieldMap.getArrayEntryAt(i), partial); + } + for (Map.Entry<T, Object> entry : fieldMap.getOverflowEntries()) { +- replaceBuilders(entry); ++ replaceBuilders(entry, partial); + } + } + + private static <T extends FieldDescriptorLite<T>> void replaceBuilders( +- Map.Entry<T, Object> entry) { +- entry.setValue(replaceBuilders(entry.getKey(), entry.getValue())); ++ Map.Entry<T, Object> entry, boolean partial) { ++ entry.setValue(replaceBuilders(entry.getKey(), entry.getValue(), partial)); + } + + private static <T extends FieldDescriptorLite<T>> Object replaceBuilders( +- T descriptor, Object value) { ++ T descriptor, Object value, boolean partial) { + if (value == null) { + return value; + } +@@ -981,7 +1007,7 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> { + List<Object> list = (List<Object>) 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<T extends FieldSet.FieldDescriptorLite<T>> { + } + 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<T extends FieldSet.FieldDescriptorLite<T>> { + if (fields.isImmutable()) { + result.makeImmutable(); + } else { +- replaceBuilders(result); ++ replaceBuilders(result, true); + } + return result; + } +@@ -1044,7 +1077,7 @@ final class FieldSet<T extends FieldSet.FieldDescriptorLite<T>> { + */ + 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<T extends FieldSet.FieldDescriptorLite<T>> { + 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<MessageType, BuilderType>> + extends AbstractMessageLite<MessageType, BuilderType> { + ++ /* 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. ++ * ++ * <p>Adding a standalone boolean would have added 8 bytes to every message instance. ++ * ++ * <p>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<GeneratedMessageLite<MessageType, BuilderType>>) nullableSchema) ++ .getSerializedSize(this); ++ } + } + + /** Constructs a {@link MessageInfo} for this message type. */ +@@ -316,6 +407,7 @@ public abstract class GeneratedMessageLite< + protected static <T extends GeneratedMessageLite<?, ?>> void registerDefaultInstance( + Class<T> 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 <MessageType> 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<ExtensionDescriptor> 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<T> 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. + * ++ * <p>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. ++ * ++ * <p>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}. ++ * ++ * <p>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. ++ * ++ * <p>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<FieldDescriptor> buildExtensions() { + return extensions == null + ? (FieldSet<FieldDescriptor>) 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<String, Method> nameToNoArgMethod = new HashMap<String, Method>(); +- Map<String, Method> nameToMethod = new HashMap<String, Method>(); +- Set<String> getters = new TreeSet<String>(); ++ Set<String> setters = new HashSet<>(); ++ Map<String, Method> hazzers = new HashMap<>(); ++ Map<String, Method> 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<String, Method> 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. ++ * ++ * <p>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. ++ * ++ * <p>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<Descriptors.FieldDescriptor> extensions; ++ ++ ExtensionBuilderAdapter(FieldSet.Builder<Descriptors.FieldDescriptor> 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<T> implements Schema<T> { + + @Override + public void mergeFrom(T message, T other) { ++ checkMutable(message); + if (other == null) { + throw new NullPointerException(); + } +@@ -1374,47 +1376,83 @@ final class MessageSchema<T> implements Schema<T> { + } + } + +- 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<T> implements Schema<T> { + if (extensionRegistry == null) { + throw new NullPointerException(); + } ++ checkMutable(message); + mergeFromHelper(unknownFieldSchema, extensionSchema, message, reader, extensionRegistry); + } + +@@ -3889,6 +3928,7 @@ final class MessageSchema<T> implements Schema<T> { + } + unknownFields = + extensionSchema.parseExtension( ++ message, + reader, + extension, + extensionRegistry, +@@ -3955,21 +3995,10 @@ final class MessageSchema<T> implements Schema<T> { + break; + case 9: + { // MESSAGE: +- if (isFieldPresent(message, pos)) { +- Object mergedResult = +- Internal.mergeMessage( +- UnsafeUtil.getObject(message, offset(typeAndOffset)), +- reader.readMessageBySchemaWithCheck( +- (Schema<T>) getMessageFieldSchema(pos), extensionRegistry)); +- UnsafeUtil.putObject(message, offset(typeAndOffset), mergedResult); +- } else { +- UnsafeUtil.putObject( +- message, +- offset(typeAndOffset), +- reader.readMessageBySchemaWithCheck( +- (Schema<T>) getMessageFieldSchema(pos), extensionRegistry)); +- setFieldPresent(message, pos); +- } ++ final MessageLite current = (MessageLite) mutableMessageFieldForMerge(message, pos); ++ reader.mergeMessageField( ++ current, (Schema<MessageLite>) getMessageFieldSchema(pos), extensionRegistry); ++ storeMessageField(message, pos, current); + break; + } + case 10: // BYTES: +@@ -3990,7 +4019,7 @@ final class MessageSchema<T> implements Schema<T> { + } else { + unknownFields = + SchemaUtil.storeUnknownEnum( +- number, enumValue, unknownFields, unknownFieldSchema); ++ message, number, enumValue, unknownFields, unknownFieldSchema); + } + break; + } +@@ -4012,21 +4041,10 @@ final class MessageSchema<T> implements Schema<T> { + break; + case 17: + { // GROUP: +- if (isFieldPresent(message, pos)) { +- Object mergedResult = +- Internal.mergeMessage( +- UnsafeUtil.getObject(message, offset(typeAndOffset)), +- reader.readGroupBySchemaWithCheck( +- (Schema<T>) getMessageFieldSchema(pos), extensionRegistry)); +- UnsafeUtil.putObject(message, offset(typeAndOffset), mergedResult); +- } else { +- UnsafeUtil.putObject( +- message, +- offset(typeAndOffset), +- reader.readGroupBySchemaWithCheck( +- (Schema<T>) getMessageFieldSchema(pos), extensionRegistry)); +- setFieldPresent(message, pos); +- } ++ final MessageLite current = (MessageLite) mutableMessageFieldForMerge(message, pos); ++ reader.mergeGroupField( ++ current, (Schema<MessageLite>) getMessageFieldSchema(pos), extensionRegistry); ++ storeMessageField(message, pos, current); + break; + } + case 18: // DOUBLE_LIST: +@@ -4089,6 +4107,7 @@ final class MessageSchema<T> implements Schema<T> { + reader.readEnumList(enumList); + unknownFields = + SchemaUtil.filterUnknownEnumList( ++ message, + number, + enumList, + getEnumFieldVerifier(pos), +@@ -4155,6 +4174,7 @@ final class MessageSchema<T> implements Schema<T> { + reader.readEnumList(enumList); + unknownFields = + SchemaUtil.filterUnknownEnumList( ++ message, + number, + enumList, + getEnumFieldVerifier(pos), +@@ -4235,24 +4255,15 @@ final class MessageSchema<T> implements Schema<T> { + 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<MessageLite>) 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<T> implements Schema<T> { + } else { + unknownFields = + SchemaUtil.storeUnknownEnum( +- number, enumValue, unknownFields, unknownFieldSchema); ++ message, number, enumValue, unknownFields, unknownFieldSchema); + } + break; + } +@@ -4296,17 +4307,19 @@ final class MessageSchema<T> implements Schema<T> { + 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<MessageLite>) 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<T> implements Schema<T> { + } 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<T> implements Schema<T> { + + @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<T> implements Schema<T> { + } 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<Integer>) list, +- getEnumFieldVerifier(bufferPosition), +- unknownFields, +- (UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema); +- if (unknownFields != null) { +- ((GeneratedMessageLite) message).unknownFields = unknownFields; +- } ++ SchemaUtil.filterUnknownEnumList( ++ message, ++ number, ++ (ProtobufList<Integer>) list, ++ getEnumFieldVerifier(bufferPosition), ++ null, ++ (UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema); + break; + case 33: // SINT32_LIST: + case 47: // SINT32_LIST_PACKED: +@@ -4774,20 +4779,11 @@ final class MessageSchema<T> implements Schema<T> { + 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<T> implements Schema<T> { + 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<T> implements Schema<T> { + 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<T> implements Schema<T> { + 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<T> implements Schema<T> { + 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<T> implements Schema<T> { + message, + intArray[i], + unknownFields, +- (UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema); ++ (UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema, ++ message); + } + if (unknownFields != null) { + ((UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema) +@@ -5183,9 +5169,65 @@ final class MessageSchema<T> implements Schema<T> { + 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<T> implements Schema<T> { + 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<T> implements Schema<T> { + + @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<T> implements Schema<T> { + extensionRegistry); + } + +- private final <UT, UB> UB filterMapUnknownEnumValues( +- Object message, int pos, UB unknownFields, UnknownFieldSchema<UT, UB> unknownFieldSchema) { ++ private <UT, UB> UB filterMapUnknownEnumValues( ++ Object message, ++ int pos, ++ UB unknownFields, ++ UnknownFieldSchema<UT, UB> 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<T> implements Schema<T> { + // 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 <K, V, UT, UB> UB filterUnknownEnumMap( ++ private <K, V, UT, UB> UB filterUnknownEnumMap( + int pos, + int number, + Map<K, V> mapData, + EnumVerifier enumVerifier, + UB unknownFields, +- UnknownFieldSchema<UT, UB> unknownFieldSchema) { ++ UnknownFieldSchema<UT, UB> unknownFieldSchema, ++ Object containerMessage) { + Metadata<K, V> metadata = + (Metadata<K, V>) mapFieldSchema.forMapMetadata(getMapFieldDefaultEntry(pos)); + for (Iterator<Map.Entry<K, V>> it = mapData.entrySet().iterator(); it.hasNext(); ) { + Map.Entry<K, V> 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<T> implements Schema<T> { + 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 <T> 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<T> implements Schema<T> { + @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<T> implements Schema<T> { + 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<T> implements Schema<T> { + 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> T readGroupBySchemaWithCheck(Schema<T> schema, ExtensionRegistryLite extensionRegistry) + throws IOException; + ++ /** Read a message field from the wire format and merge the results into the given target. */ ++ <T> void mergeMessageField(T target, Schema<T> schema, ExtensionRegistryLite extensionRegistry) ++ throws IOException; ++ ++ /** Read a group field from the wire format and merge the results into the given target. */ ++ <T> void mergeGroupField(T target, Schema<T> 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 <UT, UB> UB filterUnknownEnumList( ++ Object containerMessage, + int number, + List<Integer> 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<Integer> 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 <UT, UB> UB filterUnknownEnumList( ++ Object containerMessage, + int number, + List<Integer> 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<Integer> 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 <UT, UB> UB storeUnknownEnum( +- int number, int enumValue, UB unknownFields, UnknownFieldSchema<UT, UB> unknownFieldSchema) { ++ Object containerMessage, ++ int number, ++ int enumValue, ++ UB unknownFields, ++ UnknownFieldSchema<UT, UB> 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<java.lang.Integer>();\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<java.lang.Integer>();\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 <google/protobuf/descriptor.pb.h> + #include <google/protobuf/io/coded_stream.h> + #include <google/protobuf/io/printer.h> ++#include <google/protobuf/descriptor.h> + #include <google/protobuf/wire_format.h> + #include <google/protobuf/stubs/strutil.h> + #include <google/protobuf/stubs/substitute.h> +@@ -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<const FieldDescriptor*[]> 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<int32>(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<int32>(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<const FieldDescriptor*[]> 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<int32_t>(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<int32_t>(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/protobuf-init.el b/protobuf-init.el new file mode 100644 index 0000000..008bdef --- /dev/null +++ b/protobuf-init.el @@ -0,0 +1,6 @@ +; Protobuf major mode, init file by Tim Niemueller [www.niemueller.de], BSD +; Add mode to automatically recognized modes +(setq auto-mode-alist (cons '("\\.proto$" . protobuf-mode) auto-mode-alist)) +(autoload 'protobuf-mode "protobuf-mode" "Google protobuf editing mode." t) +; Turn on colorization by default +(add-hook 'protobuf-mode-hook 'turn-on-font-lock) diff --git a/protobuf.spec b/protobuf.spec new file mode 100644 index 0000000..fa25507 --- /dev/null +++ b/protobuf.spec @@ -0,0 +1,431 @@ +# Build -python subpackage +%bcond_without python +# Build -java subpackage +%bcond_without java + +#global rcver rc2 + +Summary: Protocol Buffers - Google's data interchange format +Name: protobuf +Version: 3.14.0 +Release: 6 +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 + +BuildRequires: make autoconf automake emacs gcc-c++ libtool pkgconfig zlib-devel + +%description + +Protocol Buffers (a.k.a., protobuf) are Google's language-neutral, +platform-neutral, extensible mechanism for serializing structured data. +You can find protobuf's documentation on the Google Developers site. + +%package compiler +Summary: Protocol Buffers compiler +Requires: %{name} = %{version}-%{release} +Obsoletes: protobuf-emacs < %{version} +Obsoletes: protobuf-emacs-el < %{version} +Requires: emacs-filesystem >= %{_emacs_version} + +%description compiler +This package containers Protocol Buffers compiler for all programming languages. + + +%package devel +Summary: Protocol Buffers C++ headers and libraries +Requires: %{name} = %{version}-%{release} +Requires: %{name}-compiler = %{version}-%{release} +Requires: zlib-devel pkgconfig vim-enhanced +Provides: %{name}-static +Provides: %{name}-vim +Obsoletes: %{name}-static < %{version} +Obsoletes: %{name}-vim < %{version} + + +%description devel +This package contains Protocol Buffers compiler for all languages and +C++ headers and libraries + +%package lite +Summary: Protocol Buffers LITE_RUNTIME libraries + +%description lite +Protocol Buffers built with optimize_for = LITE_RUNTIME. + +The "optimize_for = LITE_RUNTIME" option causes the compiler to generate code +which only depends libprotobuf-lite, which is much smaller than libprotobuf but +lacks descriptors, reflection, and some other features. + +%package lite-devel +Summary: Protocol Buffers LITE_RUNTIME development libraries +Requires: %{name}-devel = %{version}-%{release} +Requires: %{name}-lite = %{version}-%{release} +Provides: %{name}-lite-static +Obsoletes: %{name}-lite-static < %{version} + +%description lite-devel +This package contains development libraries built with +optimize_for = LITE_RUNTIME. + +The "optimize_for = LITE_RUNTIME" option causes the compiler to generate code +which only depends libprotobuf-lite, which is much smaller than libprotobuf but +lacks descriptors, reflection, and some other features. + +%if %{with python} +%package -n python%{python3_pkgversion}-%{name} +Summary: Python 3 bindings for Google Protocol Buffers +BuildArch: noarch +BuildRequires: python%{python3_pkgversion}-devel +BuildRequires: python%{python3_pkgversion}-setuptools +BuildRequires: python%{python3_pkgversion}-wheel +Requires: python%{python3_pkgversion}-six >= 1.9 +Conflicts: %{name}-compiler > %{version}-%{release} +Conflicts: %{name}-compiler < %{version}-%{release} +Provides: %{name}-python3 = %{version}-%{release} +%{?python_provide:%python_provide python%{python3_pkgversion}-%{name}} + +%description -n python%{python3_pkgversion}-%{name} +This package contains Python 3 libraries for Google Protocol Buffers +%endif + + +%if %{with java} +%package java +Summary: Java Protocol Buffers runtime library +BuildArch: noarch +BuildRequires: maven-local +BuildRequires: mvn(com.google.code.gson:gson) +BuildRequires: mvn(com.google.guava:guava) +BuildRequires: mvn(junit:junit) +BuildRequires: mvn(org.apache.felix:maven-bundle-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-antrun-plugin) +BuildRequires: mvn(org.apache.maven.plugins:maven-source-plugin) +BuildRequires: mvn(org.codehaus.mojo:build-helper-maven-plugin) +BuildRequires: mvn(org.easymock:easymock) +Obsoletes: %{name}-javanano < 3.6.0 + +%description java +This package contains Java Protocol Buffers runtime library. + +%package javalite +Summary: Java Protocol Buffers lite runtime library +BuildArch: noarch + +%description javalite +This package contains Java Protocol Buffers lite runtime library. + +%package java-util +Summary: Utilities for Protocol Buffers +BuildArch: noarch + +%description java-util +Utilities to work with protos. It contains JSON support +as well as utilities to work with proto3 well-known types. + +%package javadoc +Summary: Javadoc for %{name}-java +BuildArch: noarch + +%description javadoc +This package contains the API documentation for %{name}-java. + +%package parent +Summary: Protocol Buffer Parent POM +BuildArch: noarch + +%description parent +Protocol Buffer Parent POM. + +%package bom +Summary: Protocol Buffer BOM POM +BuildArch: noarch + +%description bom +Protocol Buffer BOM POM. + +%endif + +%prep +%setup -q -n %{name}-%{version}%{?rcver} +%autopatch -p1 +find -name \*.cc -o -name \*.h | xargs chmod -x +chmod 644 examples/* +%if %{with java} +#%pom_remove_dep com.google.truth:truth java/pom.xml +#%pom_remove_dep org.easymock:easymockclassextension java/pom.xml java/*/pom.xml +%pom_remove_dep org.easymock:easymockclassextension java/pom.xml java/core/pom.xml java/lite/pom.xml java/util/pom.xml +%pom_remove_dep com.google.truth:truth java/pom.xml java/util/pom.xml java/lite/pom.xml java/core/pom.xml +%pom_remove_dep com.google.errorprone:error_prone_annotations java/util/pom.xml +%pom_remove_dep com.google.guava:guava-testlib java/pom.xml java/util/pom.xml + +# These use easymockclassextension +rm java/core/src/test/java/com/google/protobuf/ServiceTest.java + +#rm -r java/core/src/test + +# These use truth or error_prone_annotations or guava-testlib +rm java/core/src/test/java/com/google/protobuf/LiteralByteStringTest.java +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 + +# Make OSGi dependency on sun.misc package optional +%pom_xpath_inject "pom:configuration/pom:instructions" "<Import-Package>sun.misc;resolution:=optional,*</Import-Package>" java/core + +# Backward compatibility symlink +%mvn_file :protobuf-java:jar: %{name}/%{name}-java %{name} + +# This test is incredibly slow on arm +# https://github.com/protocolbuffers/protobuf/issues/2389 +%ifarch %{arm} +mv java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java \ + java/core/src/test/java/com/google/protobuf/IsValidUtf8Test.java.slow +mv java/core/src/test/java/com/google/protobuf/DecodeUtf8Test.java \ + java/core/src/test/java/com/google/protobuf/DecodeUtf8Test.java.slow +%endif +%endif + +rm -f src/solaris/libstdc++.la + +%build +iconv -f iso8859-1 -t utf-8 CONTRIBUTORS.txt > CONTRIBUTORS.txt.utf8 +mv CONTRIBUTORS.txt.utf8 CONTRIBUTORS.txt +export PTHREAD_LIBS="-lpthread" +./autogen.sh +%configure +%make_build CXXFLAGS="%{build_cxxflags} -Wno-error=type-limits" + +%if %{with python} +pushd python +%py3_build +popd +%endif + +%if %{with java} +%mvn_build -s -- -f java/pom.xml +%endif + +%{_emacs_bytecompile} editors/protobuf-mode.el + + +%check +# Java tests fail on s390x +%ifarch s390x +fail=0 +%else +fail=1 +%endif +%make_build check CXXFLAGS="%{build_cxxflags} -Wno-error=type-limits" || exit $fail + + +%install +%make_install %{?_smp_mflags} STRIPBINARIES=no INSTALL="%{__install} -p" CPPROG="cp -p" +find %{buildroot} -type f -name "*.la" -exec rm -f {} \; + +%if %{with python} +pushd python +#python ./setup.py install --root=%{buildroot} --single-version-externally-managed --record=INSTALLED_FILES --optimize=1 +%py3_install +find %{buildroot}%{python3_sitelib} -name \*.py | + xargs sed -i -e '1{\@^#!@d}' +popd +%endif +install -p -m 644 -D editors/proto.vim %{buildroot}%{_datadir}/vim/vimfiles/syntax/proto.vim + +%if %{with java} +%mvn_install +%endif + +mkdir -p %{buildroot}%{_emacs_sitelispdir}/%{name} +install -p -m 0644 editors/protobuf-mode.el %{buildroot}%{_emacs_sitelispdir}/%{name} +install -p -m 0644 editors/protobuf-mode.elc %{buildroot}%{_emacs_sitelispdir}/%{name} +mkdir -p %{buildroot}%{_emacs_sitestartdir} +install -p -m 0644 %{SOURCE1} %{buildroot}%{_emacs_sitestartdir} + +%ldconfig_scriptlets +%ldconfig_scriptlets lite +%ldconfig_scriptlets compiler + +%files +%doc CHANGES.txt CONTRIBUTORS.txt README.md +%license LICENSE +%{_libdir}/libprotobuf.so.25* + +%files compiler +%{_bindir}/protoc +%{_libdir}/libprotoc.so.25* +%{_emacs_sitelispdir}/%{name}/ +%{_emacs_sitestartdir}/protobuf-init.el +%license LICENSE +%doc README.md + + +%files devel +%dir %{_includedir}/google +%{_includedir}/google/protobuf/ +%{_libdir}/libprotobuf.so +%{_libdir}/libprotoc.so +%{_libdir}/pkgconfig/protobuf.pc +%doc examples/add_person.cc examples/addressbook.proto examples/list_people.cc examples/Makefile examples/README.md +%{_libdir}/libprotobuf.a +%{_libdir}/libprotoc.a +%{_datadir}/vim/vimfiles/syntax/proto.vim + +%files lite +%{_libdir}/libprotobuf-lite.so.25* + +%files lite-devel +%{_libdir}/libprotobuf-lite.so +%{_libdir}/pkgconfig/protobuf-lite.pc +%{_libdir}/libprotobuf-lite.a + +%if %{with python} +%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 +%doc python/README.md +%doc examples/add_person.py examples/list_people.py examples/addressbook.proto +%endif + +%if %{with java} +%files java -f .mfiles-protobuf-java +%doc examples/AddPerson.java examples/ListPeople.java +%doc java/README.md +%license LICENSE + +%files java-util -f .mfiles-protobuf-java-util + +%files javadoc -f .mfiles-javadoc +%license LICENSE + +%files parent -f .mfiles-protobuf-parent +%license LICENSE + +%files bom -f .mfiles-protobuf-bom +%license LICENSE + +%files javalite -f .mfiles-protobuf-javalite +%license LICENSE +%endif + +%changelog +* Tue Oct 18 2022 chengzeruizhi <chengzeruizhi@huawei.com> - 3.14.0-6 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: fix CVE-2022-3171 + +* Tue Oct 11 2022 chengzeruizhi <chengzeruizhi@huawei.com> - 3.14.0-5 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: fix CVE-2022-1941 + +* Wed Apr 27 2022 wangxiaochao <wangxiaochao2@huawei.com> - 3.14.0-4 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: Improve performance of parsing unknown fields in Java + +* Fri Mar 18 2022 wangxiaochao <wangxiaochao2@huawei.com> - 3.14.0-3 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: fix CVE-2021-22570 + +* Thu Mar 10 2022 wangxiaochao <wangxiaochao2@huawei.com> - 3.14.0-2 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: fix mainline compile failed + +* Fri Jul 30 2021 liyanan <liyanan32@huawei.com> - 3.14.0-1 +- update to 3.14.0 + +* Mon Apr 26 2021 haozi007 <liuhao27@huawei.com> - 3.12.3-16 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC: split compiler from devel + +* Sat Feb 20 2021 haozi007 <liuhao27@huawei.com> - 3.12.3-15 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC: add fstack check + +* Tue Sep 1 2020 wutao <wutao61@huawei.com> - 3.12.3-14 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC: enhance java function and fix build errors + +* Sat Aug 29 2020 openEuler Buildteam <buildteam@openeuler.org> - 3.12.3-13 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: remove Conflicts + +* Fri Aug 28 2020 openEuler Buildteam <buildteam@openeuler.org> - 3.12.3-12 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC: invalid version of Conflicts + +* Thu Jul 23 2020 openEuler Buildteam <buildteam@openeuler.org> - 3.12.3-11 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC: drop python2-protobuf and refactor .spec file + +* Thu Jul 16 2020 openEuler Buildteam <buildteam@openeuler.org> - 3.12.3-10 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:upgrade from 3.9.0 to 3.12.3 + +* Wed Apr 08 2020 openEuler Buildteam <buildteam@openeuler.org> - 3.9.0-9 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC: remove unnecessary files + +* Thu Dec 12 2019 openEuler Buildteam <buildteam@openeuler.org> - 3.9.0-8.h3 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:add bind now secure compile option + +* Wed Nov 27 2019 openEuler Buildteam <buildteam@openeuler.org> - 3.9.0-8.h2 +- Type:enhancement +- ID:NA +- SUG:NA +- DESC:compatible to centos 7.5 + +* Tue Nov 26 2019 openEuler Buildteam <buildteam@openeuler.org> - 3.9.0-8.h1 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:upgrade from 3.5.0 to 3.9.0 + +* Fri Nov 01 2019 openEuler Buildteam <buildteam@openeuler.org> - 3.5.0-8.h1 +- Type:bugfix +- ID:NA +- SUG:NA +- DESC:change patch's names according to new rules @@ -0,0 +1 @@ +d0a7dd930210af5285c08c8a2c2304ab protobuf-all-3.14.0.tar.gz |