summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-06-21 08:44:50 +0000
committerCoprDistGit <infra@openeuler.org>2023-06-21 08:44:50 +0000
commit2e312f4001c27e25bc432546aeb7a68c0b98cbe0 (patch)
tree988d44f3d3dd0adc383d94825eafb1487f950470
parente6b69cb13b95a6aa4fb51d2a0baa7ad1acc7ff58 (diff)
automatic import of protobufopeneuler23.03openeuler22.09openeuler20.03
-rw-r--r--.gitignore1
-rw-r--r--0001-add-secure-compile-option-in-Makefile.patch12
-rw-r--r--0002-add-secure-compile-fs-check-in-Makefile.patch26
-rw-r--r--0003-fix-CVE-2021-22570.patch73
-rw-r--r--0004-Improve-performance-of-parsing-unknown-fields-in-Jav.patch1272
-rw-r--r--0005-fix-CVE-2022-1941.patch368
-rw-r--r--0006-fix-CVE-2022-3171.patch4924
-rw-r--r--protobuf-init.el6
-rw-r--r--protobuf.spec431
-rw-r--r--sources1
10 files changed, 7114 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..a6d9a92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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
diff --git a/sources b/sources
new file mode 100644
index 0000000..7a7ed2f
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+d0a7dd930210af5285c08c8a2c2304ab protobuf-all-3.14.0.tar.gz