summaryrefslogtreecommitdiff
path: root/0006-fix-CVE-2022-3171.patch
diff options
context:
space:
mode:
Diffstat (limited to '0006-fix-CVE-2022-3171.patch')
-rw-r--r--0006-fix-CVE-2022-3171.patch4924
1 files changed, 4924 insertions, 0 deletions
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
+