Update Java lite MessageInfo encoding to store ProtoSyntax enum instead of boolean and set/check bit for enum closeness.

This updates kMapWithProto2EnumValue -> kLegacyEnumIsClosedBit s.t. this bit now indicates closedness for enum fields as well, not just maps with enum values.

PiperOrigin-RevId: 535782238
pull/12919/head
Sandy Zhang 2 years ago committed by Copybara-Service
parent 13c06cbb54
commit ad07adbd65
  1. 13
      java/core/src/main/java/com/google/protobuf/ManifestSchemaFactory.java
  2. 68
      java/core/src/main/java/com/google/protobuf/MessageSchema.java
  3. 11
      java/core/src/main/java/com/google/protobuf/RawMessageInfo.java
  4. 10
      src/google/protobuf/compiler/java/helpers.cc

@ -74,7 +74,7 @@ final class ManifestSchemaFactory implements SchemaFactory {
private static <T> Schema<T> newSchema(Class<T> messageType, MessageInfo messageInfo) {
if (GeneratedMessageLite.class.isAssignableFrom(messageType)) {
return isProto2(messageInfo)
return allowExtensions(messageInfo)
? MessageSchema.newSchema(
messageType,
messageInfo,
@ -92,7 +92,7 @@ final class ManifestSchemaFactory implements SchemaFactory {
/* extensionSchema= */ null,
MapFieldSchemas.lite());
}
return isProto2(messageInfo)
return allowExtensions(messageInfo)
? MessageSchema.newSchema(
messageType,
messageInfo,
@ -111,8 +111,13 @@ final class ManifestSchemaFactory implements SchemaFactory {
MapFieldSchemas.full());
}
private static boolean isProto2(MessageInfo messageInfo) {
return messageInfo.getSyntax() == ProtoSyntax.PROTO2;
private static boolean allowExtensions(MessageInfo messageInfo) {
switch (messageInfo.getSyntax()) {
case PROTO3:
return false;
default:
return true;
}
}
private static MessageInfoFactory getDefaultMessageInfoFactory() {

@ -94,6 +94,17 @@ final class MessageSchema<T> implements Schema<T> {
private static final int NO_PRESENCE_SENTINEL = -1 & OFFSET_MASK;
private static final int[] EMPTY_INT_ARRAY = new int[0];
/**
* Bit masks for field type extra feature bits encoded in Java gencode via
* GetExperimentalJavaFieldType in helpers.cc.
*/
private static final int REQUIRED_BIT = 0x100;
private static final int UTF8_CHECK_BIT = 0x200;
private static final int CHECK_INITIALIZED_BIT = 0x400;
private static final int LEGACY_ENUM_IS_CLOSED_BIT = 0x800;
private static final int HAS_HAS_BIT = 0x1000;
/** An offset applied to the field type ID for scalar fields that are a member of a oneof. */
static final int ONEOF_TYPE_OFFSET = 51 /* FieldType.MAP + 1 */;
@ -154,7 +165,7 @@ final class MessageSchema<T> implements Schema<T> {
private final MessageLite defaultInstance;
private final boolean hasExtensions;
private final boolean lite;
private final boolean proto3;
private final ProtoSyntax syntax;
// TODO(xiaofeng): Make both full-runtime and lite-runtime support cached field size.
private final boolean useCachedSizeField;
@ -185,7 +196,7 @@ final class MessageSchema<T> implements Schema<T> {
int minFieldNumber,
int maxFieldNumber,
MessageLite defaultInstance,
boolean proto3,
ProtoSyntax syntax,
boolean useCachedSizeField,
int[] intArray,
int checkInitialized,
@ -201,7 +212,7 @@ final class MessageSchema<T> implements Schema<T> {
this.maxFieldNumber = maxFieldNumber;
this.lite = defaultInstance instanceof GeneratedMessageLite;
this.proto3 = proto3;
this.syntax = syntax;
this.hasExtensions = extensionSchema != null && extensionSchema.hasExtensions(defaultInstance);
this.useCachedSizeField = useCachedSizeField;
@ -252,8 +263,6 @@ final class MessageSchema<T> implements Schema<T> {
UnknownFieldSchema<?, ?> unknownFieldSchema,
ExtensionSchema<?> extensionSchema,
MapFieldSchema mapFieldSchema) {
final boolean isProto3 = messageInfo.getSyntax() == ProtoSyntax.PROTO3;
String info = messageInfo.getStringInfo();
final int length = info.length();
int i = 0;
@ -445,7 +454,7 @@ final class MessageSchema<T> implements Schema<T> {
fieldTypeWithExtraBits = next;
fieldType = fieldTypeWithExtraBits & 0xFF;
if ((fieldTypeWithExtraBits & 0x400) != 0) {
if ((fieldTypeWithExtraBits & CHECK_INITIALIZED_BIT) != 0) {
intArray[checkInitializedPosition++] = bufferIndex;
}
@ -472,7 +481,10 @@ final class MessageSchema<T> implements Schema<T> {
|| oneofFieldType == 17 /* FieldType.GROUP */) {
objects[bufferIndex / INTS_PER_FIELD * 2 + 1] = messageInfoObjects[objectsPosition++];
} else if (oneofFieldType == 12 /* FieldType.ENUM */) {
if (!isProto3) {
// TODO(b/279034699): Remove proto2 check once legacy gencode not setting this bit
// no longer needs to be supported.
if (messageInfo.getSyntax().equals(ProtoSyntax.PROTO2)
|| (fieldTypeWithExtraBits & LEGACY_ENUM_IS_CLOSED_BIT) != 0) {
objects[bufferIndex / INTS_PER_FIELD * 2 + 1] = messageInfoObjects[objectsPosition++];
}
}
@ -515,19 +527,22 @@ final class MessageSchema<T> implements Schema<T> {
} else if (fieldType == 12 /* FieldType.ENUM */
|| fieldType == 30 /* FieldType.ENUM_LIST */
|| fieldType == 44 /* FieldType.ENUM_LIST_PACKED */) {
if (!isProto3) {
// TODO(b/279034699): Remove proto2 check once legacy gencode not setting this bit
// no longer needs to be supported.
if (messageInfo.getSyntax() == ProtoSyntax.PROTO2
|| (fieldTypeWithExtraBits & LEGACY_ENUM_IS_CLOSED_BIT) != 0) {
objects[bufferIndex / INTS_PER_FIELD * 2 + 1] = messageInfoObjects[objectsPosition++];
}
} else if (fieldType == 50 /* FieldType.MAP */) {
intArray[mapFieldIndex++] = bufferIndex;
objects[bufferIndex / INTS_PER_FIELD * 2] = messageInfoObjects[objectsPosition++];
if ((fieldTypeWithExtraBits & 0x800) != 0) {
if ((fieldTypeWithExtraBits & LEGACY_ENUM_IS_CLOSED_BIT) != 0) {
objects[bufferIndex / INTS_PER_FIELD * 2 + 1] = messageInfoObjects[objectsPosition++];
}
}
fieldOffset = (int) unsafe.objectFieldOffset(field);
boolean hasHasBit = (fieldTypeWithExtraBits & 0x1000) == 0x1000;
boolean hasHasBit = (fieldTypeWithExtraBits & HAS_HAS_BIT) != 0;
if (hasHasBit && fieldType <= 17 /* FieldType.GROUP */) {
next = info.charAt(i++);
if (next >= 0xD800) {
@ -567,8 +582,8 @@ final class MessageSchema<T> implements Schema<T> {
buffer[bufferIndex++] = fieldNumber;
buffer[bufferIndex++] =
((fieldTypeWithExtraBits & 0x200) != 0 ? ENFORCE_UTF8_MASK : 0)
| ((fieldTypeWithExtraBits & 0x100) != 0 ? REQUIRED_MASK : 0)
((fieldTypeWithExtraBits & UTF8_CHECK_BIT) != 0 ? ENFORCE_UTF8_MASK : 0)
| ((fieldTypeWithExtraBits & REQUIRED_BIT) != 0 ? REQUIRED_MASK : 0)
| (fieldType << OFFSET_BITS)
| fieldOffset;
buffer[bufferIndex++] = (presenceMaskShift << OFFSET_BITS) | presenceFieldOffset;
@ -580,7 +595,7 @@ final class MessageSchema<T> implements Schema<T> {
minFieldNumber,
maxFieldNumber,
messageInfo.getDefaultInstance(),
isProto3,
messageInfo.getSyntax(),
/* useCachedSizeField= */ false,
intArray,
checkInitialized,
@ -624,7 +639,6 @@ final class MessageSchema<T> implements Schema<T> {
UnknownFieldSchema<?, ?> unknownFieldSchema,
ExtensionSchema<?> extensionSchema,
MapFieldSchema mapFieldSchema) {
final boolean isProto3 = messageInfo.getSyntax() == ProtoSyntax.PROTO3;
FieldInfo[] fis = messageInfo.getFields();
final int minFieldNumber;
final int maxFieldNumber;
@ -714,7 +728,7 @@ final class MessageSchema<T> implements Schema<T> {
minFieldNumber,
maxFieldNumber,
messageInfo.getDefaultInstance(),
isProto3,
messageInfo.getSyntax(),
/* useCachedSizeField= */ true,
combined,
checkInitialized.length,
@ -1458,7 +1472,13 @@ final class MessageSchema<T> implements Schema<T> {
@Override
public int getSerializedSize(T message) {
return proto3 ? getSerializedSizeProto3(message) : getSerializedSizeProto2(message);
switch (syntax) {
case PROTO2:
return getSerializedSizeProto2(message);
case PROTO3:
return getSerializedSizeProto3(message);
}
throw new IllegalArgumentException("Unsupported syntax: " + syntax);
}
@SuppressWarnings("unchecked")
@ -2580,10 +2600,13 @@ final class MessageSchema<T> implements Schema<T> {
if (writer.fieldOrder() == Writer.FieldOrder.DESCENDING) {
writeFieldsInDescendingOrder(message, writer);
} else {
if (proto3) {
switch (syntax) {
case PROTO3:
writeFieldsInAscendingOrderProto3(message, writer);
} else {
break;
case PROTO2:
writeFieldsInAscendingOrderProto2(message, writer);
break;
}
}
}
@ -5477,10 +5500,13 @@ final class MessageSchema<T> implements Schema<T> {
@Override
public void mergeFrom(T message, byte[] data, int position, int limit, Registers registers)
throws IOException {
if (proto3) {
switch (syntax) {
case PROTO3:
parseProto3Message(message, data, position, limit, registers);
} else {
break;
case PROTO2:
parseProto2Message(message, data, position, limit, 0, registers);
break;
}
}
@ -5683,7 +5709,7 @@ final class MessageSchema<T> implements Schema<T> {
message, pos, currentPresenceFieldOffset, currentPresenceField, presenceMask)) {
return false;
}
// If a required message field is set but has no required fields of it's own, we still
// If a required message field is set but has no required fields of its own, we still
// proceed and check the message is initialized. It should be fairly cheap to check these
// messages but is worth documenting.
}

@ -36,6 +36,7 @@ package com.google.protobuf;
*/
@CheckReturnValue
final class RawMessageInfo implements MessageInfo {
private static final int IS_PROTO2_BIT = 0x1;
private final MessageLite defaultInstance;
@ -60,7 +61,7 @@ final class RawMessageInfo implements MessageInfo {
* <p>The integer sequence encoded in the String object has the following layout:
*
* <ul>
* <li>[0]: flags, flags & 0x1 = is proto2?, flags & 0x2 = is message?.
* <li>[0]: flags, flags & 0x1 = is proto2?, flags & 0x2 = is message?
* <li>[1]: field count, if 0, this is the end of the integer sequence and the corresponding
* Object[] array should be null.
* <li>[2]: oneof count
@ -84,7 +85,7 @@ final class RawMessageInfo implements MessageInfo {
* <li>v & 0x0100 = is required?
* <li>v & 0x0200 = is checkUtf8?
* <li>v & 0x0400 = needs isInitialized check?
* <li>v & 0x0800 = is map field with proto2 enum value?
* <li>v & 0x0800 = is enum field or map field enum value with legacy closedness?
* <li>v & 0x1000 = supports presence checking?
* </ul>
* </ul>
@ -212,7 +213,11 @@ final class RawMessageInfo implements MessageInfo {
@Override
public ProtoSyntax getSyntax() {
return (flags & 0x1) == 0x1 ? ProtoSyntax.PROTO2 : ProtoSyntax.PROTO3;
if ((flags & IS_PROTO2_BIT) != 0) {
return ProtoSyntax.PROTO2;
} else {
return ProtoSyntax.PROTO3;
}
}
@Override

@ -920,10 +920,11 @@ int GetExperimentalJavaFieldTypeForPacked(const FieldDescriptor* field) {
int GetExperimentalJavaFieldType(const FieldDescriptor* field) {
static const int kMapFieldType = 50;
static const int kOneofFieldTypeOffset = 51;
static const int kRequiredBit = 0x100;
static const int kUtf8CheckBit = 0x200;
static const int kCheckInitialized = 0x400;
static const int kMapWithProto2EnumValue = 0x800;
static const int kLegacyEnumIsClosedBit = 0x800;
static const int kHasHasBit = 0x1000;
int extra_bits = field->is_required() ? kRequiredBit : 0;
if (field->type() == FieldDescriptor::TYPE_STRING && CheckUtf8(field)) {
@ -936,17 +937,20 @@ int GetExperimentalJavaFieldType(const FieldDescriptor* field) {
if (HasHasbit(field)) {
extra_bits |= kHasHasBit;
}
if (GetJavaType(field) == JAVATYPE_ENUM && !SupportUnknownEnumValue(field)) {
extra_bits |= kLegacyEnumIsClosedBit;
}
if (field->is_map()) {
if (!SupportUnknownEnumValue(MapValueField(field))) {
const FieldDescriptor* value = field->message_type()->map_value();
if (GetJavaType(value) == JAVATYPE_ENUM) {
extra_bits |= kMapWithProto2EnumValue;
extra_bits |= kLegacyEnumIsClosedBit;
}
}
return kMapFieldType | extra_bits;
} else if (field->is_packed()) {
return GetExperimentalJavaFieldTypeForPacked(field);
return GetExperimentalJavaFieldTypeForPacked(field) | extra_bits;
} else if (field->is_repeated()) {
return GetExperimentalJavaFieldTypeForRepeated(field) | extra_bits;
} else if (IsRealOneof(field)) {

Loading…
Cancel
Save