@@ -374,6 +374,44 @@ static void SingularSetRetainable(id msg, TYPE_Retainable value, size_t offset,
374374
375375#undef REPEATED_GETTER_IMP
376376
377+ // Getter for singular enum fields. Intercepts tagged pointers for unknown values
378+ // and returns the UNRECOGNIZED singleton instead.
379+ static IMP GetSingularGetterImpEnum (size_t offset, CGPHasLocator hasLoc, id defaultValue,
380+ CGPFieldDescriptor *field) {
381+ CGPEnumDescriptor *enumType = [field getEnumType ];
382+ id unrecognizedSingleton =
383+ ((CGPEnumValueDescriptor *)enumType->values_ ->buffer_ [enumType->values_->size_ - 1 ])->enum_ ;
384+
385+ return imp_implementationWithBlock (^id (id msg) {
386+ if (GetHas (msg, hasLoc)) {
387+ id value = *FIELD_PTR (id , msg, offset);
388+ uintptr_t stored_val = (uintptr_t )(__bridge void *)value;
389+ if (stored_val & 0x1 ) {
390+ return unrecognizedSingleton;
391+ }
392+ return value;
393+ }
394+ return defaultValue;
395+ });
396+ }
397+
398+ // Getter for repeated enum fields. Intercepts tagged pointers for unknown values
399+ // and returns the UNRECOGNIZED singleton instead.
400+ static IMP GetRepeatedGetterImpEnum (size_t offset, CGPFieldDescriptor *field) {
401+ CGPEnumDescriptor *enumType = [field getEnumType ];
402+ id unrecognizedSingleton =
403+ ((CGPEnumValueDescriptor *)enumType->values_ ->buffer_ [enumType->values_->size_ - 1 ])->enum_ ;
404+
405+ return imp_implementationWithBlock (^id (id msg, jint idx) {
406+ id value = CGPRepeatedFieldGetId (REPEATED_FIELD_PTR (msg, offset), idx);
407+ uintptr_t stored_val = (uintptr_t )(__bridge void *)value;
408+ if (stored_val & 0x1 ) {
409+ return unrecognizedSingleton;
410+ }
411+ return value;
412+ });
413+ }
414+
377415static BOOL AddGetterMethod (Class cls, SEL sel, CGPFieldDescriptor *field) {
378416 BOOL repeated = CGPFieldIsRepeated (field);
379417 IMP imp = NULL ;
@@ -388,7 +426,30 @@ static BOOL AddGetterMethod(Class cls, SEL sel, CGPFieldDescriptor *field) {
388426 strcpy (encoding, @encode (TYPE_##NAME)); \
389427 break ;
390428
391- SWITCH_TYPES_NO_ENUM (CGPFieldGetJavaType (field), ADD_GETTER_METHOD_CASE)
429+ // We expand the switch manually instead of using SWITCH_TYPES_NO_ENUM
430+ // to handle ENUM fields specially and avoid RETAIN_AND_AUTORELEASE on tagged pointers.
431+ switch (CGPFieldGetJavaType (field)) {
432+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_INT:
433+ ADD_GETTER_METHOD_CASE (Int)
434+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_LONG:
435+ ADD_GETTER_METHOD_CASE (Long)
436+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_FLOAT:
437+ ADD_GETTER_METHOD_CASE (Float)
438+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_DOUBLE:
439+ ADD_GETTER_METHOD_CASE (Double)
440+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BOOLEAN:
441+ ADD_GETTER_METHOD_CASE (Bool)
442+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_STRING:
443+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_BYTE_STRING:
444+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_MESSAGE:
445+ ADD_GETTER_METHOD_CASE (Id)
446+ case ComGoogleProtobufDescriptors_FieldDescriptor_JavaType_Enum_ENUM:
447+ imp = repeated ? GetRepeatedGetterImpEnum (offset, field)
448+ : GetSingularGetterImpEnum (offset, hasLoc, field->data_ ->defaultValue .valueId ,
449+ field);
450+ strcpy (encoding, @encode (id ));
451+ break ;
452+ }
392453
393454#undef ADD_GETTER_METHOD_CASE
394455
@@ -1579,11 +1640,37 @@ static inline BOOL ReadEnumValueDescriptor(CGPCodedInputStream *input, CGPEnumDe
15791640 return YES ;
15801641}
15811642
1643+ // Reads an enum value from the stream and resolves it to a Java enum instance.
1644+ //
1645+ // This function is preserves unknown enum values in proto3 (open enums)
1646+ // by storing them as tagged integers instead of falling back to the UNRECOGNIZED
1647+ // singleton.
1648+ //
1649+ // See CGPEnumGetIntValue in Descriptors_PackagePrivate.h for details on the
1650+ // tagged representation and why it is safe with ARC and PAC.
15821651static BOOL ReadEnumJavaValue (CGPCodedInputStream *input, CGPEnumDescriptor *enumType,
15831652 id *javaValue) {
1584- CGPEnumValueDescriptor *valueDescriptor;
1585- if (!ReadEnumValueDescriptor (input, enumType, &valueDescriptor)) return NO ;
1586- *javaValue = valueDescriptor == nil ? nil : valueDescriptor->enum_ ;
1653+ jint value;
1654+ if (!CGPReadEnum (input, &value)) return NO ;
1655+
1656+ CGPEnumValueDescriptor *valueDescriptor = CGPEnumValueDescriptorFromInt (enumType, value);
1657+
1658+ if (valueDescriptor == nil ) {
1659+ // Closed enum (proto2) and value was not found. We store nil.
1660+ *javaValue = nil ;
1661+ } else if (!enumType->is_closed_ &&
1662+ valueDescriptor == enumType->values_ ->buffer_ [enumType->values_->size_ - 1 ]) {
1663+ // Open enum (proto3) and the value was not found, so CGPEnumValueDescriptorFromInt
1664+ // returned the UNRECOGNIZED descriptor (which is always the last element in values_).
1665+ //
1666+ // We store the raw value in the upper 32 bits and set the lowest bit to 1.
1667+ // This preserves the value for serialization while remaining safe from ARC.
1668+ *javaValue = (id )(ARCBRIDGE void *)(((uintptr_t )value << 32 ) | 0x1 );
1669+ } else {
1670+ // Found a valid known descriptor. Store the singleton pointer.
1671+ *javaValue = valueDescriptor->enum_ ;
1672+ }
1673+
15871674 return YES ;
15881675}
15891676
0 commit comments