Unify DescriptorMessageInfoFactory.convert proto2 and proto3 codepaths

This handles conversion from descriptors to MessageInfo in a single generic codepath that handles the following differences across syntaxes:
- Enum closedness (proto2)
- Explicit (proto2) vs implicit (proto3) presence
- Required field initialization (proto2)
- UTF8 (proto2/3 specific options)

This also fixes proto3 non-optional messages to encode hasbit, which was updated in cl/542744002

PiperOrigin-RevId: 544358617
pull/13136/head
Sandy Zhang 2 years ago committed by Copybara-Service
parent a95eb31ac4
commit 8ec0295ad7
  1. 340
      java/core/src/main/java/com/google/protobuf/DescriptorMessageInfoFactory.java
  2. 12
      java/core/src/main/java/com/google/protobuf/FieldInfo.java

@ -30,19 +30,20 @@
package com.google.protobuf;
import static com.google.protobuf.FieldInfo.forExplicitPresenceField;
import static com.google.protobuf.FieldInfo.forField;
import static com.google.protobuf.FieldInfo.forFieldWithEnumVerifier;
import static com.google.protobuf.FieldInfo.forLegacyRequiredField;
import static com.google.protobuf.FieldInfo.forMapField;
import static com.google.protobuf.FieldInfo.forOneofMemberField;
import static com.google.protobuf.FieldInfo.forPackedField;
import static com.google.protobuf.FieldInfo.forPackedFieldWithEnumVerifier;
import static com.google.protobuf.FieldInfo.forProto2OptionalField;
import static com.google.protobuf.FieldInfo.forProto2RequiredField;
import static com.google.protobuf.FieldInfo.forRepeatedMessageField;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Descriptors.FieldDescriptor.Type;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Descriptors.OneofDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
@ -122,18 +123,155 @@ final class DescriptorMessageInfoFactory implements MessageInfoFactory {
return getDefaultInstance(messageType).getDescriptorForType();
}
private static MessageInfo convert(Class<?> messageType, Descriptor messageDescriptor) {
switch (messageDescriptor.getFile().getSyntax()) {
private static ProtoSyntax convertSyntax(FileDescriptor.Syntax syntax) {
switch (syntax) {
case PROTO2:
return convertProto2(messageType, messageDescriptor);
return ProtoSyntax.PROTO2;
case PROTO3:
return convertProto3(messageType, messageDescriptor);
return ProtoSyntax.PROTO3;
default:
throw new IllegalArgumentException(
"Unsupported syntax: " + messageDescriptor.getFile().getSyntax());
throw new IllegalArgumentException("Unsupported syntax: " + syntax);
}
}
private static MessageInfo convert(Class<?> messageType, Descriptor messageDescriptor) {
List<FieldDescriptor> fieldDescriptors = messageDescriptor.getFields();
StructuralMessageInfo.Builder builder =
StructuralMessageInfo.newBuilder(fieldDescriptors.size());
builder.withDefaultInstance(getDefaultInstance(messageType));
builder.withSyntax(convertSyntax(messageDescriptor.getFile().getSyntax()));
builder.withMessageSetWireFormat(messageDescriptor.getOptions().getMessageSetWireFormat());
OneofState oneofState = new OneofState();
// Performance optimization to cache presence bits across field iterations.
int bitFieldIndex = 0;
int presenceMask = 1;
Field bitField = null;
// Fields in the descriptor are ordered by the index position in which they appear in the
// proto file. This is the same order used to determine the presence mask used in the
// bitFields. So to determine the appropriate presence mask to be used for a field, we simply
// need to shift the presence mask whenever a presence-checked field is encountered.
for (int i = 0; i < fieldDescriptors.size(); ++i) {
final FieldDescriptor fd = fieldDescriptors.get(i);
boolean enforceUtf8 = fd.needsUtf8Check();
Internal.EnumVerifier enumVerifier = null;
// Enum verifier for closed enums.
if (fd.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM
&& fd.legacyEnumFieldTreatedAsClosed()) {
enumVerifier =
new Internal.EnumVerifier() {
@Override
public boolean isInRange(int number) {
return fd.getEnumType().findValueByNumber(number) != null;
}
};
}
if (fd.getContainingOneof() != null && !fd.getContainingOneof().isSynthetic()) {
// Build a oneof member field for non-synthetic oneofs.
builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8, enumVerifier));
continue;
}
Field field = field(messageType, fd);
int number = fd.getNumber();
FieldType type = getFieldType(fd);
// Handle field with implicit presence.
if (!fd.hasPresence()) {
FieldInfo fieldImplicitPresence;
if (fd.isMapField()) {
// Map field points to an auto-generated message entry type with the definition:
// message MapEntry {
// K key = 1;
// V value = 2;
// }
final FieldDescriptor valueField = fd.getMessageType().findFieldByNumber(2);
if (valueField.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM
&& valueField.legacyEnumFieldTreatedAsClosed()) {
enumVerifier =
new Internal.EnumVerifier() {
@Override
public boolean isInRange(int number) {
return valueField.getEnumType().findValueByNumber(number) != null;
}
};
}
fieldImplicitPresence =
forMapField(
field,
number,
SchemaUtil.getMapDefaultEntry(messageType, fd.getName()),
enumVerifier);
} else if (fd.isRepeated() && fd.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
fieldImplicitPresence =
forRepeatedMessageField(
field, number, type, getTypeForRepeatedMessageField(messageType, fd));
} else if (fd.isPacked()) {
if (enumVerifier != null) {
fieldImplicitPresence =
forPackedFieldWithEnumVerifier(
field, number, type, enumVerifier, cachedSizeField(messageType, fd));
} else {
fieldImplicitPresence =
forPackedField(field, number, type, cachedSizeField(messageType, fd));
}
} else {
if (enumVerifier != null) {
fieldImplicitPresence = forFieldWithEnumVerifier(field, number, type, enumVerifier);
} else {
fieldImplicitPresence = forField(field, number, type, enforceUtf8);
}
}
builder.withField(fieldImplicitPresence);
continue;
}
// Handle field with explicit presence.
FieldInfo fieldExplicitPresence;
if (bitField == null) {
// Lazy-create the next bitfield since we know it must exist.
bitField = bitField(messageType, bitFieldIndex);
}
if (fd.isRequired()) {
fieldExplicitPresence =
forLegacyRequiredField(
field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier);
} else {
fieldExplicitPresence =
forExplicitPresenceField(
field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier);
}
builder.withField(fieldExplicitPresence);
// Update the presence mask for the next iteration. If the shift clears out the mask, we
// will go to the next bitField.
presenceMask <<= 1;
if (presenceMask == 0) {
bitField = null;
presenceMask = 1;
bitFieldIndex++;
}
}
List<Integer> fieldsToCheckIsInitialized = new ArrayList<>();
for (int i = 0; i < fieldDescriptors.size(); ++i) {
FieldDescriptor fd = fieldDescriptors.get(i);
if (fd.isRequired()
|| (fd.getJavaType() == FieldDescriptor.JavaType.MESSAGE
&& needsIsInitializedCheck(fd.getMessageType()))) {
fieldsToCheckIsInitialized.add(fd.getNumber());
}
}
int[] numbers = new int[fieldsToCheckIsInitialized.size()];
for (int i = 0; i < fieldsToCheckIsInitialized.size(); i++) {
numbers[i] = fieldsToCheckIsInitialized.get(i);
}
if (numbers.length > 0) {
builder.withCheckInitialized(numbers);
}
return builder.build();
}
/**
* A helper class to determine whether a message type needs to implement {@code isInitialized()}.
*
@ -281,192 +419,6 @@ final class DescriptorMessageInfoFactory implements MessageInfoFactory {
return isInitializedCheckAnalyzer.needsIsInitializedCheck(descriptor);
}
private static StructuralMessageInfo convertProto2(
Class<?> messageType, Descriptor messageDescriptor) {
List<FieldDescriptor> fieldDescriptors = messageDescriptor.getFields();
StructuralMessageInfo.Builder builder =
StructuralMessageInfo.newBuilder(fieldDescriptors.size());
builder.withDefaultInstance(getDefaultInstance(messageType));
builder.withSyntax(ProtoSyntax.PROTO2);
builder.withMessageSetWireFormat(messageDescriptor.getOptions().getMessageSetWireFormat());
OneofState oneofState = new OneofState();
int bitFieldIndex = 0;
int presenceMask = 1;
Field bitField = null;
// Fields in the descriptor are ordered by the index position in which they appear in the
// proto file. This is the same order used to determine the presence mask used in the
// bitFields. So to determine the appropriate presence mask to be used for a field, we simply
// need to shift the presence mask whenever a presence-checked field is encountered.
for (int i = 0; i < fieldDescriptors.size(); ++i) {
final FieldDescriptor fd = fieldDescriptors.get(i);
boolean enforceUtf8 = fd.getFile().getOptions().getJavaStringCheckUtf8();
Internal.EnumVerifier enumVerifier = null;
if (fd.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM) {
enumVerifier =
new Internal.EnumVerifier() {
@Override
public boolean isInRange(int number) {
return fd.getEnumType().findValueByNumber(number) != null;
}
};
}
if (fd.getContainingOneof() != null) {
// Build a oneof member field.
builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8, enumVerifier));
continue;
}
Field field = field(messageType, fd);
int number = fd.getNumber();
FieldType type = getFieldType(fd);
if (fd.isMapField()) {
// Map field points to an auto-generated message entry type with the definition:
// message MapEntry {
// K key = 1;
// V value = 2;
// }
final FieldDescriptor valueField = fd.getMessageType().findFieldByNumber(2);
if (valueField.getJavaType() == Descriptors.FieldDescriptor.JavaType.ENUM) {
enumVerifier =
new Internal.EnumVerifier() {
@Override
public boolean isInRange(int number) {
return valueField.getEnumType().findValueByNumber(number) != null;
}
};
}
builder.withField(
forMapField(
field,
number,
SchemaUtil.getMapDefaultEntry(messageType, fd.getName()),
enumVerifier));
continue;
}
if (fd.isRepeated()) {
// Repeated fields are not presence-checked.
if (enumVerifier != null) {
if (fd.isPacked()) {
builder.withField(
forPackedFieldWithEnumVerifier(
field, number, type, enumVerifier, cachedSizeField(messageType, fd)));
} else {
builder.withField(forFieldWithEnumVerifier(field, number, type, enumVerifier));
}
} else if (fd.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
builder.withField(
forRepeatedMessageField(
field, number, type, getTypeForRepeatedMessageField(messageType, fd)));
} else {
if (fd.isPacked()) {
builder.withField(
forPackedField(field, number, type, cachedSizeField(messageType, fd)));
} else {
builder.withField(forField(field, number, type, enforceUtf8));
}
}
continue;
}
if (bitField == null) {
// Lazy-create the next bitfield since we know it must exist.
bitField = bitField(messageType, bitFieldIndex);
}
// It's a presence-checked field.
if (fd.isRequired()) {
builder.withField(
forProto2RequiredField(
field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier));
} else {
builder.withField(
forProto2OptionalField(
field, number, type, bitField, presenceMask, enforceUtf8, enumVerifier));
}
// Update the presence mask for the next iteration. If the shift clears out the mask, we
// will go to the next bitField.
presenceMask <<= 1;
if (presenceMask == 0) {
bitField = null;
presenceMask = 1;
bitFieldIndex++;
}
}
List<Integer> fieldsToCheckIsInitialized = new ArrayList<Integer>();
for (int i = 0; i < fieldDescriptors.size(); ++i) {
FieldDescriptor fd = fieldDescriptors.get(i);
if (fd.isRequired()
|| (fd.getJavaType() == FieldDescriptor.JavaType.MESSAGE
&& needsIsInitializedCheck(fd.getMessageType()))) {
fieldsToCheckIsInitialized.add(fd.getNumber());
}
}
int[] numbers = new int[fieldsToCheckIsInitialized.size()];
for (int i = 0; i < fieldsToCheckIsInitialized.size(); i++) {
numbers[i] = fieldsToCheckIsInitialized.get(i);
}
builder.withCheckInitialized(numbers);
return builder.build();
}
private static StructuralMessageInfo convertProto3(
Class<?> messageType, Descriptor messageDescriptor) {
List<FieldDescriptor> fieldDescriptors = messageDescriptor.getFields();
StructuralMessageInfo.Builder builder =
StructuralMessageInfo.newBuilder(fieldDescriptors.size());
builder.withDefaultInstance(getDefaultInstance(messageType));
builder.withSyntax(ProtoSyntax.PROTO3);
OneofState oneofState = new OneofState();
boolean enforceUtf8 = true;
for (int i = 0; i < fieldDescriptors.size(); ++i) {
FieldDescriptor fd = fieldDescriptors.get(i);
if (fd.getContainingOneof() != null && !fd.getContainingOneof().isSynthetic()) {
// Build a oneof member field. But only if it is a real oneof, not a proto3 optional
builder.withField(buildOneofMember(messageType, fd, oneofState, enforceUtf8, null));
continue;
}
if (fd.isMapField()) {
builder.withField(
forMapField(
field(messageType, fd),
fd.getNumber(),
SchemaUtil.getMapDefaultEntry(messageType, fd.getName()),
null));
continue;
}
if (fd.isRepeated() && fd.getJavaType() == FieldDescriptor.JavaType.MESSAGE) {
builder.withField(
forRepeatedMessageField(
field(messageType, fd),
fd.getNumber(),
getFieldType(fd),
getTypeForRepeatedMessageField(messageType, fd)));
continue;
}
if (fd.isPacked()) {
builder.withField(
forPackedField(
field(messageType, fd),
fd.getNumber(),
getFieldType(fd),
cachedSizeField(messageType, fd)));
} else {
builder.withField(
forField(field(messageType, fd), fd.getNumber(), getFieldType(fd), enforceUtf8));
}
}
return builder.build();
}
/** Builds info for a oneof member field. */
private static FieldInfo buildOneofMember(
Class<?> messageType,

@ -178,8 +178,8 @@ final class FieldInfo implements Comparable<FieldInfo> {
cachedSizeField);
}
/** Constructor for a proto2 optional field. */
public static FieldInfo forProto2OptionalField(
/** Constructor for a field with explicit presence (e.g. proto2). */
public static FieldInfo forExplicitPresenceField(
Field field,
int fieldNumber,
FieldType fieldType,
@ -263,8 +263,8 @@ final class FieldInfo implements Comparable<FieldInfo> {
}
}
/** Constructor for a proto2 required field. */
public static FieldInfo forProto2RequiredField(
/** Constructor for a legacy required field. */
public static FieldInfo forLegacyRequiredField(
Field field,
int fieldNumber,
FieldType fieldType,
@ -548,10 +548,10 @@ final class FieldInfo implements Comparable<FieldInfo> {
}
if (presenceField != null) {
if (required) {
return forProto2RequiredField(
return forLegacyRequiredField(
field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier);
} else {
return forProto2OptionalField(
return forExplicitPresenceField(
field, fieldNumber, type, presenceField, presenceMask, enforceUtf8, enumVerifier);
}
}

Loading…
Cancel
Save