Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:Step:15-SP1
protobuf.28337
protobuf-CVE-2022-3171.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File protobuf-CVE-2022-3171.patch of Package protobuf.28337
From 42e47e5a3fa7219b136f6a5de7c74a89a79245c6 Mon Sep 17 00:00:00 2001 From: Mike Kruskal <62662355+mkruskal-google@users.noreply.github.com> Date: Thu, 29 Sep 2022 10:17:52 -0700 Subject: [PATCH] Refactoring Java parsing (3.16.x) (#10668) * Update changelog * Porting java cleanup * Extension patch * Remove extra allocations * Fixing merge issues * More merge fixes * Fix TextFormat parser --- java/core/src/main/java/com/google/protobuf/AbstractMessage.java | 27 java/core/src/main/java/com/google/protobuf/ArrayDecoders.java | 146 +-- java/core/src/main/java/com/google/protobuf/BinaryReader.java | 32 java/core/src/main/java/com/google/protobuf/CodedInputStreamReader.java | 51 - java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java | 1 java/core/src/main/java/com/google/protobuf/DynamicMessage.java | 5 java/core/src/main/java/com/google/protobuf/ExtensionSchema.java | 1 java/core/src/main/java/com/google/protobuf/ExtensionSchemaFull.java | 5 java/core/src/main/java/com/google/protobuf/ExtensionSchemaLite.java | 52 - java/core/src/main/java/com/google/protobuf/FieldSet.java | 63 - java/core/src/main/java/com/google/protobuf/GeneratedMessageLite.java | 169 ++- java/core/src/main/java/com/google/protobuf/GeneratedMessageV3.java | 110 +- java/core/src/main/java/com/google/protobuf/MessageLiteToString.java | 117 +- java/core/src/main/java/com/google/protobuf/MessageReflection.java | 439 ++++++++- java/core/src/main/java/com/google/protobuf/MessageSchema.java | 483 ++++++---- java/core/src/main/java/com/google/protobuf/MessageSetSchema.java | 17 java/core/src/main/java/com/google/protobuf/NewInstanceSchemaLite.java | 5 java/core/src/main/java/com/google/protobuf/Reader.java | 8 java/core/src/main/java/com/google/protobuf/SchemaUtil.java | 30 java/core/src/main/java/com/google/protobuf/TextFormat.java | 2 java/core/src/main/java/com/google/protobuf/UnknownFieldSetLite.java | 38 java/core/src/main/java/com/google/protobuf/UnknownFieldSetLiteSchema.java | 12 java/lite/src/test/java/com/google/protobuf/LiteTest.java | 36 java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java | 10 src/google/protobuf/compiler/java/java_enum_field.cc | 94 - src/google/protobuf/compiler/java/java_enum_field.h | 90 - src/google/protobuf/compiler/java/java_field.cc | 4 src/google/protobuf/compiler/java/java_field.h | 5 src/google/protobuf/compiler/java/java_map_field.cc | 30 src/google/protobuf/compiler/java/java_map_field.h | 32 src/google/protobuf/compiler/java/java_message.cc | 220 ---- src/google/protobuf/compiler/java/java_message_builder.cc | 163 ++- src/google/protobuf/compiler/java/java_message_builder.h | 5 src/google/protobuf/compiler/java/java_message_field.cc | 126 -- src/google/protobuf/compiler/java/java_message_field.h | 83 - src/google/protobuf/compiler/java/java_primitive_field.cc | 66 - src/google/protobuf/compiler/java/java_primitive_field.h | 86 - src/google/protobuf/compiler/java/java_string_field.cc | 59 - src/google/protobuf/compiler/java/java_string_field.h | 83 - 39 files changed, 1896 insertions(+), 1109 deletions(-) --- 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( --- 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); } } --- 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 R 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 R 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 R 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; --- 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 imple 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 imple // 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); --- a/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java +++ b/java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java @@ -403,7 +403,6 @@ final class DescriptorMessageInfoFactory for (int i = 0; i < fieldDescriptors.size(); ++i) { FieldDescriptor fd = fieldDescriptors.get(i); if (fd.getContainingOneof() != null) { - // Build a oneof member field. builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8, null)); continue; } --- a/java/core/src/main/java/com/google/protobuf/DynamicMessage.java +++ b/java/core/src/main/java/com/google/protobuf/DynamicMessage.java @@ -426,7 +426,10 @@ public final class DynamicMessage extend 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; } --- 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 * or UnknownFieldSetLite in lite runtime. */ abstract <UT, UB> UB parseExtension( + Object containerMessage, Reader reader, Object extension, ExtensionRegistryLite extensionRegistry, --- 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 @Override <UT, UB> UB parseExtension( + Object containerMessage, Reader reader, Object extensionObject, ExtensionRegistryLite extensionRegistry, @@ -202,7 +203,7 @@ final class ExtensionSchemaFull extends } else { unknownFields = SchemaUtil.storeUnknownEnum( - fieldNumber, number, unknownFields, unknownFieldSchema); + containerMessage, fieldNumber, number, unknownFields, unknownFieldSchema); } } value = enumList; @@ -221,7 +222,7 @@ final class ExtensionSchemaFull extends 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 { --- 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 @Override <UT, UB> UB parseExtension( + Object containerMessage, Reader reader, Object extensionObject, ExtensionRegistryLite extensionRegistry, @@ -178,6 +178,7 @@ final class ExtensionSchemaLite extends reader.readEnumList(list); unknownFields = SchemaUtil.filterUnknownEnumList( + containerMessage, fieldNumber, list, extension.descriptor.getEnumType(), @@ -199,7 +200,7 @@ final class ExtensionSchemaLite extends 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 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 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 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); } } --- 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. 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. 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. 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. } 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. 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. } 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. if (fields.isImmutable()) { result.makeImmutable(); } else { - replaceBuilders(result); + replaceBuilders(result, true); } return result; } @@ -1044,7 +1077,7 @@ final class FieldSet<T extends FieldSet. */ public Object getField(final T descriptor) { Object value = getFieldAllowBuilders(descriptor); - return replaceBuilders(descriptor, value); + return replaceBuilders(descriptor, value, true); } /** Same as {@link #getField(F)}, but allow a {@link MessageLite.Builder} to be returned. */ @@ -1131,7 +1164,7 @@ final class FieldSet<T extends FieldSet. ensureIsMutable(); } Object value = getRepeatedFieldAllowBuilders(descriptor, index); - return replaceBuilder(value); + return replaceBuilder(value, true); } /** --- 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 GeneratedMessageLi 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 GeneratedMessageLi 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 GeneratedMessageLi @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 @@ -169,6 +220,7 @@ public abstract class GeneratedMessageLi /** Called by subclasses to complete parsing. For use by generated code only. */ protected void makeImmutable() { Protobuf.getInstance().schemaFor(this).makeImmutable(this); + markImmutable(); } protected final < @@ -194,8 +246,7 @@ public abstract class GeneratedMessageLi @SuppressWarnings("unchecked") public final BuilderType toBuilder() { BuilderType builder = (BuilderType) dynamicMethod(MethodToInvoke.NEW_BUILDER); - builder.mergeFrom((MessageType) this); - return builder; + return builder.mergeFrom((MessageType) this); } /** @@ -252,27 +303,67 @@ public abstract class GeneratedMessageLi 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); + } + + private int computeSerializedSize(Schema<?> nullableSchema) { + if (nullableSchema == null) { + return Protobuf.getInstance().schemaFor(this).getSerializedSize(this); + } else { + return ((Schema<GeneratedMessageLite<MessageType, BuilderType>>) nullableSchema) + .getSerializedSize(this); } - return memoizedSerializedSize; } /** Constructs a {@link MessageInfo} for this message type. */ @@ -312,6 +403,7 @@ public abstract class GeneratedMessageLi protected static <T extends GeneratedMessageLite<?, ?>> void registerDefaultInstance( Class<T> clazz, T defaultInstance) { defaultInstanceMap.put(clazz, defaultInstance); + defaultInstance.makeImmutable(); } protected static Object newMessageInfo( @@ -336,13 +428,19 @@ public abstract class GeneratedMessageLi 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(); } /** @@ -350,15 +448,13 @@ public abstract class GeneratedMessageLi * 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; } @@ -370,27 +466,28 @@ public abstract class GeneratedMessageLi @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; } @@ -410,12 +507,15 @@ public abstract class GeneratedMessageLi /** 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); } @@ -926,7 +1026,9 @@ public abstract class GeneratedMessageLi @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() { @@ -940,7 +1042,7 @@ public abstract class GeneratedMessageLi @Override public final MessageType buildPartial() { - if (isBuilt) { + if (!instance.isMutable()) { return instance; } @@ -1524,7 +1626,7 @@ public abstract class GeneratedMessageLi 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. @@ -1550,15 +1652,12 @@ public abstract class GeneratedMessageLi 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(); --- 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 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 @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 /** * 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 // 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 */ @Override public BuilderType clear() { - unknownFields = UnknownFieldSet.getDefaultInstance(); + unknownFieldsOrBuilder = UnknownFieldSet.getDefaultInstance(); onChanged(); return (BuilderType) this; } @@ -757,7 +774,7 @@ public abstract class GeneratedMessageV3 } private BuilderType setUnknownFieldsInternal(final UnknownFieldSet unknownFields) { - this.unknownFields = unknownFields; + unknownFieldsOrBuilder = unknownFields; onChanged(); return (BuilderType) this; } @@ -776,12 +793,20 @@ public abstract class GeneratedMessageV3 } @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 @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 private FieldSet<FieldDescriptor> buildExtensions() { return extensions == null ? (FieldSet<FieldDescriptor>) FieldSet.emptySet() - : extensions.build(); + : extensions.buildPartial(); } @Override @@ -1815,6 +1883,20 @@ public abstract class GeneratedMessageV3 } } + @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( --- 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("_"); } --- 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; } @@ -500,14 +553,87 @@ class MessageReflection { } @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; } } @@ -666,6 +792,54 @@ class MessageReflection { } @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, @@ -700,7 +874,229 @@ 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; + } + + @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, + 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; } @@ -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, --- 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.ArrayD 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.ArrayD 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; @@ -1165,6 +1166,7 @@ final class MessageSchema<T> implements @Override public void mergeFrom(T message, T other) { + checkMutable(message); if (other == null) { throw new NullPointerException(); } @@ -1363,45 +1365,83 @@ final class MessageSchema<T> implements } } - 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(sourceParent, number, pos)) { + return; + } - if (!isOneofPresent(other, number, pos)) { + 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); + } + + 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; } - 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); - setOneofPresent(message, number, pos); - } else if (theirs != null) { - UnsafeUtil.putObject(message, offset, theirs); - setOneofPresent(message, number, 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); } @Override @@ -3841,6 +3881,7 @@ final class MessageSchema<T> implements if (extensionRegistry == null) { throw new NullPointerException(); } + checkMutable(message); mergeFromHelper(unknownFieldSchema, extensionSchema, message, reader, extensionRegistry); } @@ -3877,6 +3918,7 @@ final class MessageSchema<T> implements } unknownFields = extensionSchema.parseExtension( + message, reader, extension, extensionRegistry, @@ -3943,21 +3985,10 @@ final class MessageSchema<T> implements 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: @@ -3978,7 +4009,7 @@ final class MessageSchema<T> implements } else { unknownFields = SchemaUtil.storeUnknownEnum( - number, enumValue, unknownFields, unknownFieldSchema); + message, number, enumValue, unknownFields, unknownFieldSchema); } break; } @@ -4000,21 +4031,10 @@ final class MessageSchema<T> implements 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: @@ -4077,6 +4097,7 @@ final class MessageSchema<T> implements reader.readEnumList(enumList); unknownFields = SchemaUtil.filterUnknownEnumList( + message, number, enumList, getEnumFieldVerifier(pos), @@ -4143,6 +4164,7 @@ final class MessageSchema<T> implements reader.readEnumList(enumList); unknownFields = SchemaUtil.filterUnknownEnumList( + message, number, enumList, getEnumFieldVerifier(pos), @@ -4223,24 +4245,15 @@ final class MessageSchema<T> implements 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); @@ -4260,7 +4273,7 @@ final class MessageSchema<T> implements } else { unknownFields = SchemaUtil.storeUnknownEnum( - number, enumValue, unknownFields, unknownFieldSchema); + message, number, enumValue, unknownFields, unknownFieldSchema); } break; } @@ -4284,17 +4297,19 @@ final class MessageSchema<T> implements 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; @@ -4321,7 +4336,8 @@ final class MessageSchema<T> implements } 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); @@ -4331,6 +4347,8 @@ final class MessageSchema<T> implements @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(); @@ -4591,24 +4609,13 @@ final class MessageSchema<T> implements } 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: @@ -4762,20 +4769,11 @@ final class MessageSchema<T> implements 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: @@ -4815,21 +4813,18 @@ final class MessageSchema<T> implements 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: @@ -4866,6 +4861,7 @@ final class MessageSchema<T> implements 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 = -1; int currentPresenceField = 0; @@ -4982,18 +4978,11 @@ final class MessageSchema<T> implements 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; } @@ -5042,20 +5031,18 @@ final class MessageSchema<T> implements 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; } @@ -5153,7 +5140,8 @@ final class MessageSchema<T> implements message, intArray[i], unknownFields, - (UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema); + (UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema, + message); } if (unknownFields != null) { ((UnknownFieldSchema<UnknownFieldSetLite, UnknownFieldSetLite>) unknownFieldSchema) @@ -5171,9 +5159,65 @@ final class MessageSchema<T> implements 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 tag = 0; int oldNumber = -1; @@ -5267,16 +5311,11 @@ final class MessageSchema<T> implements 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); continue; } break; @@ -5399,18 +5438,73 @@ final class MessageSchema<T> implements @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)); - } - final int length = intArray.length; - for (int i = repeatedFieldOffsetStart; i < length; i++) { - listFieldSchema.makeImmutableListAt(message, intArray[i]); + 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 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) { @@ -5447,8 +5541,12 @@ final class MessageSchema<T> implements 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); @@ -5463,25 +5561,32 @@ final class MessageSchema<T> implements // 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()); @@ -5699,6 +5804,28 @@ final class MessageSchema<T> implements 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); } --- 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> implemen @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> implemen 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> implemen 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; --- 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(); } } --- 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. --- 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; --- a/java/core/src/main/java/com/google/protobuf/TextFormat.java +++ b/java/core/src/main/java/com/google/protobuf/TextFormat.java @@ -507,7 +507,7 @@ public final class TextFormat { case MESSAGE: case GROUP: - print((Message) value, generator); + print((MessageOrBuilder) value, generator); break; } } --- 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; + } } --- 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 --- a/java/lite/src/test/java/com/google/protobuf/LiteTest.java +++ b/java/lite/src/test/java/com/google/protobuf/LiteTest.java @@ -48,15 +48,6 @@ import com.google.protobuf.UnittestLite. 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; @@ -68,6 +59,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. @@ -180,16 +180,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"); --- a/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java +++ b/java/util/src/main/java/com/google/protobuf/util/FieldMaskTree.java @@ -34,6 +34,7 @@ import com.google.errorprone.annotations 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; @@ -247,9 +248,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; } --- 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 FieldDescrip (*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"] = @@ -309,32 +302,26 @@ void ImmutableEnumFieldGenerator::Genera 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_, @@ -491,6 +478,11 @@ void ImmutableEnumOneofFieldGenerator::G 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_, @@ -511,7 +503,7 @@ void ImmutableEnumOneofFieldGenerator::G } } -void ImmutableEnumOneofFieldGenerator::GenerateParsingCode( +void ImmutableEnumOneofFieldGenerator::GenerateBuilderParsingCode( io::Printer* printer) const { if (SupportUnknownEnumValue(descriptor_->file())) { printer->Print(variables_, @@ -521,10 +513,10 @@ void ImmutableEnumOneofFieldGenerator::G } 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" @@ -893,36 +885,29 @@ void RepeatedImmutableEnumFieldGenerator "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. @@ -932,7 +917,7 @@ void RepeatedImmutableEnumFieldGenerator "while(input.getBytesUntilLimit() > 0) {\n"); printer->Indent(); - GenerateParsingCode(printer); + GenerateBuilderParsingCode(printer); printer->Outdent(); printer->Print(variables_, @@ -940,15 +925,6 @@ void RepeatedImmutableEnumFieldGenerator "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()) { --- 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 : publ // 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 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; + std::string GetBoxedType() const override; protected: const FieldDescriptor* descriptor_; @@ -99,15 +99,16 @@ class ImmutableEnumOneofFieldGenerator : 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 RepeatedImmutableEnumFieldGenerato ~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; + 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; + std::string GetBoxedType() const override; private: const FieldDescriptor* descriptor_; --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -186,7 +186,7 @@ static inline void ReportUnexpectedPacke // 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."; } @@ -194,7 +194,7 @@ static inline void ReportUnexpectedPacke ImmutableFieldGenerator::~ImmutableFieldGenerator() {} -void ImmutableFieldGenerator::GenerateParsingCodeFromPacked( +void ImmutableFieldGenerator::GenerateBuilderParsingCodeFromPacked( io::Printer* printer) const { ReportUnexpectedPackedFieldsCall(printer); } --- 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( --- 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 FieldDesc 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"]; @@ -668,27 +661,19 @@ void ImmutableMapFieldGenerator::Generat "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 { @@ -697,16 +682,11 @@ void ImmutableMapFieldGenerator::Generat "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_, --- a/src/google/protobuf/compiler/java/java_map_field.h +++ b/src/google/protobuf/compiler/java/java_map_field.h @@ -46,22 +46,22 @@ class ImmutableMapFieldGenerator : publi ~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; --- 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> @@ -364,6 +365,7 @@ void ImmutableMessageGenerator::Generate "}\n" "\n"); + // TODO(b/248149118): Remove this superfluous override. printer->Print( "@java.lang.Override\n" "public final com.google.protobuf.UnknownFieldSet\n" @@ -371,10 +373,6 @@ void ImmutableMessageGenerator::Generate " return this.unknownFields;\n" "}\n"); - if (context_->HasGeneratedMethods(descriptor_)) { - GenerateParsingConstructor(printer); - } - GenerateDescriptorMethods(printer); // Nested types @@ -623,9 +621,9 @@ void ImmutableMessageGenerator::Generate } 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(); @@ -654,9 +652,10 @@ void ImmutableMessageGenerator::Generate } 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( @@ -1067,7 +1066,8 @@ void ImmutableMessageGenerator::Generate // 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" @@ -1142,7 +1142,7 @@ void ImmutableMessageGenerator::Generate 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"); @@ -1168,185 +1168,32 @@ void ImmutableMessageGenerator::Generate } // =================================================================== -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" @@ -1356,6 +1203,9 @@ void ImmutableMessageGenerator::Generate " return PARSER;\n" "}\n" "\n", + "visibility", + ExposePublicParser(descriptor_->file()) ? "@java.lang.Deprecated public" + : "private", "classname", descriptor_->name()); } --- a/src/google/protobuf/compiler/java/java_message_builder.cc +++ b/src/google/protobuf/compiler/java/java_message_builder.cc @@ -60,6 +60,9 @@ namespace protobuf { namespace compiler { namespace java { +using internal::WireFormat; +using internal::WireFormatLite; + namespace { std::string MapValueImmutableClassdName(const Descriptor* descriptor, ClassNameResolver* name_resolver) { @@ -284,43 +287,63 @@ void MessageBuilderGenerator::GenerateDe 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 && + !descriptor_->field(i)->containing_oneof() && + SupportFieldPresence(descriptor_->file())) { + 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 (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateFieldBuilderInitializationCode(printer); + printer->Indent(); + printer->Indent(); + for (int i = 0; i < descriptor_->field_count(); i++) { + if (!descriptor_->field(i)->containing_oneof()) { + 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" @@ -330,10 +353,8 @@ void MessageBuilderGenerator::GenerateCo printer->Indent(); for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - field_generators_.get(descriptor_->field(i)) - .GenerateBuilderClearCode(printer); - } + field_generators_.get(descriptor_->field(i)) + .GenerateBuilderClearCode(printer); } for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { @@ -579,7 +600,7 @@ void MessageBuilderGenerator::GenerateCo printer->Print(" this.mergeExtensionFields(other);\n"); } - printer->Print(" this.mergeUnknownFields(other.unknownFields);\n"); + printer->Print(" this.mergeUnknownFields(other.getUnknownFields());\n"); printer->Print(" onChanged();\n"); @@ -600,20 +621,92 @@ void MessageBuilderGenerator::GenerateBu " 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); } // =================================================================== --- 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_; --- 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 FieldDesc (*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"] = @@ -450,35 +443,21 @@ void ImmutableMessageFieldGenerator::Gen } } -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( @@ -723,6 +702,15 @@ void ImmutableMessageOneofFieldGenerator 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"); @@ -743,32 +731,21 @@ void ImmutableMessageOneofFieldGenerator "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( @@ -1214,10 +1191,12 @@ void RepeatedImmutableMessageFieldGenera 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( @@ -1272,34 +1251,25 @@ void RepeatedImmutableMessageFieldGenera "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( --- 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 : p // 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; + std::string GetBoxedType() const override; protected: const FieldDescriptor* descriptor_; @@ -110,13 +110,14 @@ class ImmutableMessageOneofFieldGenerato 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 RepeatedImmutableMessageFieldGener ~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; + 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; + std::string GetBoxedType() const override; protected: const FieldDescriptor* descriptor_; --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -167,13 +167,6 @@ void SetPrimitiveVariables(const FieldDe (*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"] = @@ -346,16 +339,11 @@ void ImmutablePrimitiveFieldGenerator::G } } -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( @@ -560,6 +548,12 @@ void ImmutablePrimitiveOneofFieldGenerat 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_, @@ -574,7 +568,7 @@ void ImmutablePrimitiveOneofFieldGenerat "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" @@ -829,38 +823,24 @@ void RepeatedImmutablePrimitiveFieldGene "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( --- 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 : // 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; + std::string GetBoxedType() const override; protected: const FieldDescriptor* descriptor_; @@ -101,13 +101,14 @@ class ImmutablePrimitiveOneofFieldGenera 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 RepeatedImmutablePrimitiveFieldGen 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; + 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; + std::string GetBoxedType() const override; private: const FieldDescriptor* descriptor_; --- a/src/google/protobuf/compiler/java/java_string_field.cc +++ b/src/google/protobuf/compiler/java/java_string_field.cc @@ -119,13 +119,6 @@ void SetPrimitiveVariables(const FieldDe (*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"] = @@ -408,26 +401,19 @@ void ImmutableStringFieldGenerator::Gene 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_, @@ -650,6 +636,11 @@ void ImmutableStringOneofFieldGenerator: "}\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 @@ -668,7 +659,7 @@ void ImmutableStringOneofFieldGenerator: "}\n"); } -void ImmutableStringOneofFieldGenerator::GenerateParsingCode( +void ImmutableStringOneofFieldGenerator::GenerateBuilderParsingCode( io::Printer* printer) const { if (CheckUtf8(descriptor_)) { printer->Print(variables_, @@ -954,35 +945,21 @@ void RepeatedImmutableStringFieldGenerat "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_, --- 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 : pu // 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; + 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 RepeatedImmutableStringFieldGenera ~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; + 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; + std::string GetBoxedType() const override; private: const FieldDescriptor* descriptor_;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor