diff --git a/Makefile.am b/Makefile.am index 620fd0bdca..b873446611 100644 --- a/Makefile.am +++ b/Makefile.am @@ -124,6 +124,7 @@ csharp_EXTRA_DIST= \ csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs \ csharp/src/Google.Protobuf.Test/LegacyGeneratedCodeTest.cs \ csharp/src/Google.Protobuf.Test/MessageParsingHelpers.cs \ + csharp/src/Google.Protobuf.Test/Proto3OptionalTest.cs \ csharp/src/Google.Protobuf.Test/ReadOnlySequenceFactory.cs \ csharp/src/Google.Protobuf.Test/Reflection/CustomOptionsTest.cs \ csharp/src/Google.Protobuf.Test/Reflection/DescriptorDeclarationTest.cs \ @@ -151,6 +152,7 @@ csharp_EXTRA_DIST= \ csharp/src/Google.Protobuf.Test.TestProtos/UnittestImport.cs \ csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs \ csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3.cs \ + csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs \ csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs \ csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs \ csharp/src/Google.Protobuf.Test/WellKnownTypes/AnyTest.cs \ diff --git a/README.md b/README.md index 0e5ae61e26..2f42c6c934 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ how to install protobuf runtime for that specific language: |--------------------------------------|-------------------------------------------------------------|--------|-------|---------| | C++ (include C++ runtime and protoc) | [src](src) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-cpp_distcheck.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fcpp_distcheck%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-bazel.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fbazel%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-dist_install.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fdist_install%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fcpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-cpp_distcheck.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fcpp_distcheck%2Fcontinuous) | [![Build status](https://ci.appveyor.com/api/projects/status/73ctee6ua4w2ruin?svg=true)](https://ci.appveyor.com/project/protobuf/protobuf) | | Java | [java](java) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-java_compatibility.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjava_compatibility%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-java_jdk7.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjava_jdk7%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-java_oracle7.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjava_oracle7%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-java_linkage_monitor.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjava_linkage_monitor%2Fcontinuous) | | | -| Python | [python](python) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python27.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython27%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python33.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython33%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python34.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython34%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python35.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython35%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python36.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython36%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python37.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython37%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python_compatibility.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython_compatibility%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python27_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython27_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python33_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython33_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python34_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython34_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python35_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython35_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python36_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython36_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python37_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython37_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python-release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython_release%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python-release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython_release%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/windows-python-release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fwindows%2Fpython_release%2Fcontinuous) | +| Python | [python](python) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python27.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython27%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python35.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython35%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python36.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython36%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python37.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython37%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python_compatibility.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython_compatibility%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python27_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython27_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python35_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython35_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python36_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython36_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python37_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython37_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-python-release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fpython_release%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python_cpp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython_cpp%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-python-release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fpython_release%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/windows-python-release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fwindows%2Fpython_release%2Fcontinuous) | | Objective-C | [objectivec](objectivec) | | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_cocoapods_integration.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_cocoapods_integration%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_ios_debug.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_ios_debug%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_ios_release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_ios_release%2Fcontinuous)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-objectivec_osx.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fobjectivec_osx%2Fcontinuous) | | | C# | [csharp](csharp) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-csharp.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fcsharp%2Fcontinuous) | | [![Build status](https://ci.appveyor.com/api/projects/status/73ctee6ua4w2ruin?svg=true)](https://ci.appveyor.com/project/protobuf/protobuf)
[![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/windows-csharp-release.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fwindows%2Fcsharp_release%2Fcontinuous) | | JavaScript | [js](js) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/linux-javascript.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fubuntu%2Fjavascript%2Fcontinuous) | [![Build status](https://storage.googleapis.com/protobuf-kokoro-results/status-badge/macos-javascript.png)](https://fusion.corp.google.com/projectanalysis/current/KOKORO/prod:protobuf%2Fgithub%2Fmaster%2Fmacos%2Fjavascript%2Fcontinuous) | | diff --git a/csharp/generate_protos.sh b/csharp/generate_protos.sh index e6687c308e..0508584d7e 100755 --- a/csharp/generate_protos.sh +++ b/csharp/generate_protos.sh @@ -63,7 +63,8 @@ $PROTOC -Isrc -Icsharp/protos \ csharp/protos/unittest_issue6936_c.proto \ src/google/protobuf/unittest_well_known_types.proto \ src/google/protobuf/test_messages_proto3.proto \ - src/google/protobuf/test_messages_proto2.proto + src/google/protobuf/test_messages_proto2.proto \ + src/google/protobuf/unittest_proto3_optional.proto # AddressBook sample protos $PROTOC -Iexamples -Isrc --csharp_out=csharp/src/AddressBook \ diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs index 93ca68c59b..1befe2ee77 100644 --- a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs +++ b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs @@ -1872,21 +1872,21 @@ namespace ProtobufTestMessages.Proto2 { /// [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int Fieldname1 { - get { if ((_hasBits0 & 2097152) != 0) { return fieldname1_; } else { return Fieldname1DefaultValue; } } + get { if ((_hasBits0 & 32768) != 0) { return fieldname1_; } else { return Fieldname1DefaultValue; } } set { - _hasBits0 |= 2097152; + _hasBits0 |= 32768; fieldname1_ = value; } } /// Gets whether the "fieldname1" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldname1 { - get { return (_hasBits0 & 2097152) != 0; } + get { return (_hasBits0 & 32768) != 0; } } /// Clears the value of the "fieldname1" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldname1() { - _hasBits0 &= ~2097152; + _hasBits0 &= ~32768; } /// Field number for the "field_name2" field. @@ -1896,21 +1896,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName2_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName2 { - get { if ((_hasBits0 & 4194304) != 0) { return fieldName2_; } else { return FieldName2DefaultValue; } } + get { if ((_hasBits0 & 65536) != 0) { return fieldName2_; } else { return FieldName2DefaultValue; } } set { - _hasBits0 |= 4194304; + _hasBits0 |= 65536; fieldName2_ = value; } } /// Gets whether the "field_name2" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName2 { - get { return (_hasBits0 & 4194304) != 0; } + get { return (_hasBits0 & 65536) != 0; } } /// Clears the value of the "field_name2" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName2() { - _hasBits0 &= ~4194304; + _hasBits0 &= ~65536; } /// Field number for the "_field_name3" field. @@ -1920,21 +1920,21 @@ namespace ProtobufTestMessages.Proto2 { private int FieldName3_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName3 { - get { if ((_hasBits0 & 8388608) != 0) { return FieldName3_; } else { return FieldName3DefaultValue; } } + get { if ((_hasBits0 & 131072) != 0) { return FieldName3_; } else { return FieldName3DefaultValue; } } set { - _hasBits0 |= 8388608; + _hasBits0 |= 131072; FieldName3_ = value; } } /// Gets whether the "_field_name3" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName3 { - get { return (_hasBits0 & 8388608) != 0; } + get { return (_hasBits0 & 131072) != 0; } } /// Clears the value of the "_field_name3" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName3() { - _hasBits0 &= ~8388608; + _hasBits0 &= ~131072; } /// Field number for the "field__name4_" field. @@ -1944,21 +1944,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName4_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName4 { - get { if ((_hasBits0 & 16777216) != 0) { return fieldName4_; } else { return FieldName4DefaultValue; } } + get { if ((_hasBits0 & 262144) != 0) { return fieldName4_; } else { return FieldName4DefaultValue; } } set { - _hasBits0 |= 16777216; + _hasBits0 |= 262144; fieldName4_ = value; } } /// Gets whether the "field__name4_" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName4 { - get { return (_hasBits0 & 16777216) != 0; } + get { return (_hasBits0 & 262144) != 0; } } /// Clears the value of the "field__name4_" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName4() { - _hasBits0 &= ~16777216; + _hasBits0 &= ~262144; } /// Field number for the "field0name5" field. @@ -1968,21 +1968,21 @@ namespace ProtobufTestMessages.Proto2 { private int field0Name5_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int Field0Name5 { - get { if ((_hasBits0 & 33554432) != 0) { return field0Name5_; } else { return Field0Name5DefaultValue; } } + get { if ((_hasBits0 & 524288) != 0) { return field0Name5_; } else { return Field0Name5DefaultValue; } } set { - _hasBits0 |= 33554432; + _hasBits0 |= 524288; field0Name5_ = value; } } /// Gets whether the "field0name5" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasField0Name5 { - get { return (_hasBits0 & 33554432) != 0; } + get { return (_hasBits0 & 524288) != 0; } } /// Clears the value of the "field0name5" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearField0Name5() { - _hasBits0 &= ~33554432; + _hasBits0 &= ~524288; } /// Field number for the "field_0_name6" field. @@ -1992,21 +1992,21 @@ namespace ProtobufTestMessages.Proto2 { private int field0Name6_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int Field0Name6 { - get { if ((_hasBits0 & 67108864) != 0) { return field0Name6_; } else { return Field0Name6DefaultValue; } } + get { if ((_hasBits0 & 1048576) != 0) { return field0Name6_; } else { return Field0Name6DefaultValue; } } set { - _hasBits0 |= 67108864; + _hasBits0 |= 1048576; field0Name6_ = value; } } /// Gets whether the "field_0_name6" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasField0Name6 { - get { return (_hasBits0 & 67108864) != 0; } + get { return (_hasBits0 & 1048576) != 0; } } /// Clears the value of the "field_0_name6" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearField0Name6() { - _hasBits0 &= ~67108864; + _hasBits0 &= ~1048576; } /// Field number for the "fieldName7" field. @@ -2016,21 +2016,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName7_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName7 { - get { if ((_hasBits0 & 134217728) != 0) { return fieldName7_; } else { return FieldName7DefaultValue; } } + get { if ((_hasBits0 & 2097152) != 0) { return fieldName7_; } else { return FieldName7DefaultValue; } } set { - _hasBits0 |= 134217728; + _hasBits0 |= 2097152; fieldName7_ = value; } } /// Gets whether the "fieldName7" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName7 { - get { return (_hasBits0 & 134217728) != 0; } + get { return (_hasBits0 & 2097152) != 0; } } /// Clears the value of the "fieldName7" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName7() { - _hasBits0 &= ~134217728; + _hasBits0 &= ~2097152; } /// Field number for the "FieldName8" field. @@ -2040,21 +2040,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName8_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName8 { - get { if ((_hasBits0 & 268435456) != 0) { return fieldName8_; } else { return FieldName8DefaultValue; } } + get { if ((_hasBits0 & 4194304) != 0) { return fieldName8_; } else { return FieldName8DefaultValue; } } set { - _hasBits0 |= 268435456; + _hasBits0 |= 4194304; fieldName8_ = value; } } /// Gets whether the "FieldName8" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName8 { - get { return (_hasBits0 & 268435456) != 0; } + get { return (_hasBits0 & 4194304) != 0; } } /// Clears the value of the "FieldName8" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName8() { - _hasBits0 &= ~268435456; + _hasBits0 &= ~4194304; } /// Field number for the "field_Name9" field. @@ -2064,21 +2064,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName9_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName9 { - get { if ((_hasBits0 & 536870912) != 0) { return fieldName9_; } else { return FieldName9DefaultValue; } } + get { if ((_hasBits0 & 8388608) != 0) { return fieldName9_; } else { return FieldName9DefaultValue; } } set { - _hasBits0 |= 536870912; + _hasBits0 |= 8388608; fieldName9_ = value; } } /// Gets whether the "field_Name9" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName9 { - get { return (_hasBits0 & 536870912) != 0; } + get { return (_hasBits0 & 8388608) != 0; } } /// Clears the value of the "field_Name9" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName9() { - _hasBits0 &= ~536870912; + _hasBits0 &= ~8388608; } /// Field number for the "Field_Name10" field. @@ -2088,21 +2088,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName10_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName10 { - get { if ((_hasBits0 & 1073741824) != 0) { return fieldName10_; } else { return FieldName10DefaultValue; } } + get { if ((_hasBits0 & 16777216) != 0) { return fieldName10_; } else { return FieldName10DefaultValue; } } set { - _hasBits0 |= 1073741824; + _hasBits0 |= 16777216; fieldName10_ = value; } } /// Gets whether the "Field_Name10" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName10 { - get { return (_hasBits0 & 1073741824) != 0; } + get { return (_hasBits0 & 16777216) != 0; } } /// Clears the value of the "Field_Name10" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName10() { - _hasBits0 &= ~1073741824; + _hasBits0 &= ~16777216; } /// Field number for the "FIELD_NAME11" field. @@ -2112,21 +2112,21 @@ namespace ProtobufTestMessages.Proto2 { private int fIELDNAME11_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FIELDNAME11 { - get { if ((_hasBits0 & -2147483648) != 0) { return fIELDNAME11_; } else { return FIELDNAME11DefaultValue; } } + get { if ((_hasBits0 & 33554432) != 0) { return fIELDNAME11_; } else { return FIELDNAME11DefaultValue; } } set { - _hasBits0 |= -2147483648; + _hasBits0 |= 33554432; fIELDNAME11_ = value; } } /// Gets whether the "FIELD_NAME11" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFIELDNAME11 { - get { return (_hasBits0 & -2147483648) != 0; } + get { return (_hasBits0 & 33554432) != 0; } } /// Clears the value of the "FIELD_NAME11" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFIELDNAME11() { - _hasBits0 &= ~-2147483648; + _hasBits0 &= ~33554432; } /// Field number for the "FIELD_name12" field. @@ -2136,21 +2136,21 @@ namespace ProtobufTestMessages.Proto2 { private int fIELDName12_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FIELDName12 { - get { if ((_hasBits1 & 1) != 0) { return fIELDName12_; } else { return FIELDName12DefaultValue; } } + get { if ((_hasBits0 & 67108864) != 0) { return fIELDName12_; } else { return FIELDName12DefaultValue; } } set { - _hasBits1 |= 1; + _hasBits0 |= 67108864; fIELDName12_ = value; } } /// Gets whether the "FIELD_name12" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFIELDName12 { - get { return (_hasBits1 & 1) != 0; } + get { return (_hasBits0 & 67108864) != 0; } } /// Clears the value of the "FIELD_name12" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFIELDName12() { - _hasBits1 &= ~1; + _hasBits0 &= ~67108864; } /// Field number for the "__field_name13" field. @@ -2160,21 +2160,21 @@ namespace ProtobufTestMessages.Proto2 { private int FieldName13_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName13 { - get { if ((_hasBits1 & 2) != 0) { return FieldName13_; } else { return FieldName13DefaultValue; } } + get { if ((_hasBits0 & 134217728) != 0) { return FieldName13_; } else { return FieldName13DefaultValue; } } set { - _hasBits1 |= 2; + _hasBits0 |= 134217728; FieldName13_ = value; } } /// Gets whether the "__field_name13" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName13 { - get { return (_hasBits1 & 2) != 0; } + get { return (_hasBits0 & 134217728) != 0; } } /// Clears the value of the "__field_name13" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName13() { - _hasBits1 &= ~2; + _hasBits0 &= ~134217728; } /// Field number for the "__Field_name14" field. @@ -2184,21 +2184,21 @@ namespace ProtobufTestMessages.Proto2 { private int FieldName14_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName14 { - get { if ((_hasBits1 & 4) != 0) { return FieldName14_; } else { return FieldName14DefaultValue; } } + get { if ((_hasBits0 & 268435456) != 0) { return FieldName14_; } else { return FieldName14DefaultValue; } } set { - _hasBits1 |= 4; + _hasBits0 |= 268435456; FieldName14_ = value; } } /// Gets whether the "__Field_name14" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName14 { - get { return (_hasBits1 & 4) != 0; } + get { return (_hasBits0 & 268435456) != 0; } } /// Clears the value of the "__Field_name14" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName14() { - _hasBits1 &= ~4; + _hasBits0 &= ~268435456; } /// Field number for the "field__name15" field. @@ -2208,21 +2208,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName15_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName15 { - get { if ((_hasBits1 & 8) != 0) { return fieldName15_; } else { return FieldName15DefaultValue; } } + get { if ((_hasBits0 & 536870912) != 0) { return fieldName15_; } else { return FieldName15DefaultValue; } } set { - _hasBits1 |= 8; + _hasBits0 |= 536870912; fieldName15_ = value; } } /// Gets whether the "field__name15" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName15 { - get { return (_hasBits1 & 8) != 0; } + get { return (_hasBits0 & 536870912) != 0; } } /// Clears the value of the "field__name15" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName15() { - _hasBits1 &= ~8; + _hasBits0 &= ~536870912; } /// Field number for the "field__Name16" field. @@ -2232,21 +2232,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName16_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName16 { - get { if ((_hasBits1 & 16) != 0) { return fieldName16_; } else { return FieldName16DefaultValue; } } + get { if ((_hasBits0 & 1073741824) != 0) { return fieldName16_; } else { return FieldName16DefaultValue; } } set { - _hasBits1 |= 16; + _hasBits0 |= 1073741824; fieldName16_ = value; } } /// Gets whether the "field__Name16" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName16 { - get { return (_hasBits1 & 16) != 0; } + get { return (_hasBits0 & 1073741824) != 0; } } /// Clears the value of the "field__Name16" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName16() { - _hasBits1 &= ~16; + _hasBits0 &= ~1073741824; } /// Field number for the "field_name17__" field. @@ -2256,21 +2256,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName17_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName17 { - get { if ((_hasBits1 & 32) != 0) { return fieldName17_; } else { return FieldName17DefaultValue; } } + get { if ((_hasBits0 & -2147483648) != 0) { return fieldName17_; } else { return FieldName17DefaultValue; } } set { - _hasBits1 |= 32; + _hasBits0 |= -2147483648; fieldName17_ = value; } } /// Gets whether the "field_name17__" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName17 { - get { return (_hasBits1 & 32) != 0; } + get { return (_hasBits0 & -2147483648) != 0; } } /// Clears the value of the "field_name17__" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName17() { - _hasBits1 &= ~32; + _hasBits0 &= ~-2147483648; } /// Field number for the "Field_name18__" field. @@ -2280,21 +2280,21 @@ namespace ProtobufTestMessages.Proto2 { private int fieldName18_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int FieldName18 { - get { if ((_hasBits1 & 64) != 0) { return fieldName18_; } else { return FieldName18DefaultValue; } } + get { if ((_hasBits1 & 1) != 0) { return fieldName18_; } else { return FieldName18DefaultValue; } } set { - _hasBits1 |= 64; + _hasBits1 |= 1; fieldName18_ = value; } } /// Gets whether the "Field_name18__" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasFieldName18 { - get { return (_hasBits1 & 64) != 0; } + get { return (_hasBits1 & 1) != 0; } } /// Clears the value of the "Field_name18__" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearFieldName18() { - _hasBits1 &= ~64; + _hasBits1 &= ~1; } private object oneofField_; diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs b/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs index b7dc08d3f7..b258167593 100644 --- a/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs +++ b/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs @@ -15806,7 +15806,6 @@ namespace Google.Protobuf.TestProtos.Proto2 { public sealed partial class TestOneof : pb::IMessage, pb::IBufferMessage { private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new TestOneof()); private pb::UnknownFieldSet _unknownFields; - private int _hasBits0; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public static pb::MessageParser Parser { get { return _parser; } } @@ -15829,7 +15828,6 @@ namespace Google.Protobuf.TestProtos.Proto2 { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public TestOneof(TestOneof other) : this() { - _hasBits0 = other._hasBits0; switch (other.FooCase) { case FooOneofCase.FooInt: FooInt = other.FooInt; @@ -17239,21 +17237,21 @@ namespace Google.Protobuf.TestProtos.Proto2 { private int bazInt_; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public int BazInt { - get { if ((_hasBits0 & 16) != 0) { return bazInt_; } else { return BazIntDefaultValue; } } + get { if ((_hasBits0 & 1) != 0) { return bazInt_; } else { return BazIntDefaultValue; } } set { - _hasBits0 |= 16; + _hasBits0 |= 1; bazInt_ = value; } } /// Gets whether the "baz_int" field is set [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public bool HasBazInt { - get { return (_hasBits0 & 16) != 0; } + get { return (_hasBits0 & 1) != 0; } } /// Clears the value of the "baz_int" field [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public void ClearBazInt() { - _hasBits0 &= ~16; + _hasBits0 &= ~1; } /// Field number for the "baz_string" field. @@ -18087,7 +18085,6 @@ namespace Google.Protobuf.TestProtos.Proto2 { public sealed partial class TestRequiredOneof : pb::IMessage, pb::IBufferMessage { private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new TestRequiredOneof()); private pb::UnknownFieldSet _unknownFields; - private int _hasBits0; [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public static pb::MessageParser Parser { get { return _parser; } } @@ -18110,7 +18107,6 @@ namespace Google.Protobuf.TestProtos.Proto2 { [global::System.Diagnostics.DebuggerNonUserCodeAttribute] public TestRequiredOneof(TestRequiredOneof other) : this() { - _hasBits0 = other._hasBits0; switch (other.FooCase) { case FooOneofCase.FooInt: FooInt = other.FooInt; diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs new file mode 100644 index 0000000000..cbad5899e5 --- /dev/null +++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs @@ -0,0 +1,1072 @@ +// +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: google/protobuf/unittest_proto3_optional.proto +// +#pragma warning disable 1591, 0612, 3021 +#region Designer generated code + +using pb = global::Google.Protobuf; +using pbc = global::Google.Protobuf.Collections; +using pbr = global::Google.Protobuf.Reflection; +using scg = global::System.Collections.Generic; +namespace ProtobufUnittest { + + /// Holder for reflection information generated from google/protobuf/unittest_proto3_optional.proto + public static partial class UnittestProto3OptionalReflection { + + #region Descriptor + /// File descriptor for google/protobuf/unittest_proto3_optional.proto + public static pbr::FileDescriptor Descriptor { + get { return descriptor; } + } + private static pbr::FileDescriptor descriptor; + + static UnittestProto3OptionalReflection() { + byte[] descriptorData = global::System.Convert.FromBase64String( + string.Concat( + "Ci5nb29nbGUvcHJvdG9idWYvdW5pdHRlc3RfcHJvdG8zX29wdGlvbmFsLnBy", + "b3RvEhFwcm90b2J1Zl91bml0dGVzdCKBCgoSVGVzdFByb3RvM09wdGlvbmFs", + "EhsKDm9wdGlvbmFsX2ludDMyGAEgASgFSACIAQESGwoOb3B0aW9uYWxfaW50", + "NjQYAiABKANIAYgBARIcCg9vcHRpb25hbF91aW50MzIYAyABKA1IAogBARIc", + "Cg9vcHRpb25hbF91aW50NjQYBCABKARIA4gBARIcCg9vcHRpb25hbF9zaW50", + "MzIYBSABKBFIBIgBARIcCg9vcHRpb25hbF9zaW50NjQYBiABKBJIBYgBARId", + "ChBvcHRpb25hbF9maXhlZDMyGAcgASgHSAaIAQESHQoQb3B0aW9uYWxfZml4", + "ZWQ2NBgIIAEoBkgHiAEBEh4KEW9wdGlvbmFsX3NmaXhlZDMyGAkgASgPSAiI", + "AQESHgoRb3B0aW9uYWxfc2ZpeGVkNjQYCiABKBBICYgBARIbCg5vcHRpb25h", + "bF9mbG9hdBgLIAEoAkgKiAEBEhwKD29wdGlvbmFsX2RvdWJsZRgMIAEoAUgL", + "iAEBEhoKDW9wdGlvbmFsX2Jvb2wYDSABKAhIDIgBARIcCg9vcHRpb25hbF9z", + "dHJpbmcYDiABKAlIDYgBARIbCg5vcHRpb25hbF9ieXRlcxgPIAEoDEgOiAEB", + "Eh4KDW9wdGlvbmFsX2NvcmQYECABKAlCAggBSA+IAQESWQoXb3B0aW9uYWxf", + "bmVzdGVkX21lc3NhZ2UYEiABKAsyMy5wcm90b2J1Zl91bml0dGVzdC5UZXN0", + "UHJvdG8zT3B0aW9uYWwuTmVzdGVkTWVzc2FnZUgQiAEBElkKE2xhenlfbmVz", + "dGVkX21lc3NhZ2UYEyABKAsyMy5wcm90b2J1Zl91bml0dGVzdC5UZXN0UHJv", + "dG8zT3B0aW9uYWwuTmVzdGVkTWVzc2FnZUICKAFIEYgBARJTChRvcHRpb25h", + "bF9uZXN0ZWRfZW51bRgVIAEoDjIwLnByb3RvYnVmX3VuaXR0ZXN0LlRlc3RQ", + "cm90bzNPcHRpb25hbC5OZXN0ZWRFbnVtSBKIAQEaJwoNTmVzdGVkTWVzc2Fn", + "ZRIPCgJiYhgBIAEoBUgAiAEBQgUKA19iYiJKCgpOZXN0ZWRFbnVtEg8KC1VO", + "U1BFQ0lGSUVEEAASBwoDRk9PEAESBwoDQkFSEAISBwoDQkFaEAMSEAoDTkVH", + "EP///////////wFCEQoPX29wdGlvbmFsX2ludDMyQhEKD19vcHRpb25hbF9p", + "bnQ2NEISChBfb3B0aW9uYWxfdWludDMyQhIKEF9vcHRpb25hbF91aW50NjRC", + "EgoQX29wdGlvbmFsX3NpbnQzMkISChBfb3B0aW9uYWxfc2ludDY0QhMKEV9v", + "cHRpb25hbF9maXhlZDMyQhMKEV9vcHRpb25hbF9maXhlZDY0QhQKEl9vcHRp", + "b25hbF9zZml4ZWQzMkIUChJfb3B0aW9uYWxfc2ZpeGVkNjRCEQoPX29wdGlv", + "bmFsX2Zsb2F0QhIKEF9vcHRpb25hbF9kb3VibGVCEAoOX29wdGlvbmFsX2Jv", + "b2xCEgoQX29wdGlvbmFsX3N0cmluZ0IRCg9fb3B0aW9uYWxfYnl0ZXNCEAoO", + "X29wdGlvbmFsX2NvcmRCGgoYX29wdGlvbmFsX25lc3RlZF9tZXNzYWdlQhYK", + "FF9sYXp5X25lc3RlZF9tZXNzYWdlQhcKFV9vcHRpb25hbF9uZXN0ZWRfZW51", + "bUIlCiFjb20uZ29vZ2xlLnByb3RvYnVmLnRlc3RpbmcucHJvdG9QAWIGcHJv", + "dG8z")); + descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, + new pbr::FileDescriptor[] { }, + new pbr::GeneratedClrTypeInfo(null, null, new pbr::GeneratedClrTypeInfo[] { + new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufUnittest.TestProto3Optional), global::ProtobufUnittest.TestProto3Optional.Parser, new[]{ "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalCord", "OptionalNestedMessage", "LazyNestedMessage", "OptionalNestedEnum" }, new[]{ "OptionalInt32", "OptionalInt64", "OptionalUint32", "OptionalUint64", "OptionalSint32", "OptionalSint64", "OptionalFixed32", "OptionalFixed64", "OptionalSfixed32", "OptionalSfixed64", "OptionalFloat", "OptionalDouble", "OptionalBool", "OptionalString", "OptionalBytes", "OptionalCord", "OptionalNestedMessage", "LazyNestedMessage", "OptionalNestedEnum" }, new[]{ typeof(global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum) }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(typeof(global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage), global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage.Parser, new[]{ "Bb" }, new[]{ "Bb" }, null, null, null)}) + })); + } + #endregion + + } + #region Messages + public sealed partial class TestProto3Optional : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new TestProto3Optional()); + private pb::UnknownFieldSet _unknownFields; + private int _hasBits0; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::ProtobufUnittest.UnittestProto3OptionalReflection.Descriptor.MessageTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public TestProto3Optional() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public TestProto3Optional(TestProto3Optional other) : this() { + _hasBits0 = other._hasBits0; + optionalInt32_ = other.optionalInt32_; + optionalInt64_ = other.optionalInt64_; + optionalUint32_ = other.optionalUint32_; + optionalUint64_ = other.optionalUint64_; + optionalSint32_ = other.optionalSint32_; + optionalSint64_ = other.optionalSint64_; + optionalFixed32_ = other.optionalFixed32_; + optionalFixed64_ = other.optionalFixed64_; + optionalSfixed32_ = other.optionalSfixed32_; + optionalSfixed64_ = other.optionalSfixed64_; + optionalFloat_ = other.optionalFloat_; + optionalDouble_ = other.optionalDouble_; + optionalBool_ = other.optionalBool_; + optionalString_ = other.optionalString_; + optionalBytes_ = other.optionalBytes_; + optionalCord_ = other.optionalCord_; + optionalNestedMessage_ = other.optionalNestedMessage_ != null ? other.optionalNestedMessage_.Clone() : null; + lazyNestedMessage_ = other.lazyNestedMessage_ != null ? other.lazyNestedMessage_.Clone() : null; + optionalNestedEnum_ = other.optionalNestedEnum_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public TestProto3Optional Clone() { + return new TestProto3Optional(this); + } + + /// Field number for the "optional_int32" field. + public const int OptionalInt32FieldNumber = 1; + private int optionalInt32_; + /// + /// Singular + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int OptionalInt32 { + get { if ((_hasBits0 & 1) != 0) { return optionalInt32_; } else { return 0; } } + set { + _hasBits0 |= 1; + optionalInt32_ = value; + } + } + /// Gets whether the "optional_int32" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalInt32 { + get { return (_hasBits0 & 1) != 0; } + } + /// Clears the value of the "optional_int32" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalInt32() { + _hasBits0 &= ~1; + } + + /// Field number for the "optional_int64" field. + public const int OptionalInt64FieldNumber = 2; + private long optionalInt64_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public long OptionalInt64 { + get { if ((_hasBits0 & 2) != 0) { return optionalInt64_; } else { return 0L; } } + set { + _hasBits0 |= 2; + optionalInt64_ = value; + } + } + /// Gets whether the "optional_int64" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalInt64 { + get { return (_hasBits0 & 2) != 0; } + } + /// Clears the value of the "optional_int64" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalInt64() { + _hasBits0 &= ~2; + } + + /// Field number for the "optional_uint32" field. + public const int OptionalUint32FieldNumber = 3; + private uint optionalUint32_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public uint OptionalUint32 { + get { if ((_hasBits0 & 4) != 0) { return optionalUint32_; } else { return 0; } } + set { + _hasBits0 |= 4; + optionalUint32_ = value; + } + } + /// Gets whether the "optional_uint32" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalUint32 { + get { return (_hasBits0 & 4) != 0; } + } + /// Clears the value of the "optional_uint32" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalUint32() { + _hasBits0 &= ~4; + } + + /// Field number for the "optional_uint64" field. + public const int OptionalUint64FieldNumber = 4; + private ulong optionalUint64_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public ulong OptionalUint64 { + get { if ((_hasBits0 & 8) != 0) { return optionalUint64_; } else { return 0UL; } } + set { + _hasBits0 |= 8; + optionalUint64_ = value; + } + } + /// Gets whether the "optional_uint64" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalUint64 { + get { return (_hasBits0 & 8) != 0; } + } + /// Clears the value of the "optional_uint64" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalUint64() { + _hasBits0 &= ~8; + } + + /// Field number for the "optional_sint32" field. + public const int OptionalSint32FieldNumber = 5; + private int optionalSint32_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int OptionalSint32 { + get { if ((_hasBits0 & 16) != 0) { return optionalSint32_; } else { return 0; } } + set { + _hasBits0 |= 16; + optionalSint32_ = value; + } + } + /// Gets whether the "optional_sint32" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalSint32 { + get { return (_hasBits0 & 16) != 0; } + } + /// Clears the value of the "optional_sint32" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalSint32() { + _hasBits0 &= ~16; + } + + /// Field number for the "optional_sint64" field. + public const int OptionalSint64FieldNumber = 6; + private long optionalSint64_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public long OptionalSint64 { + get { if ((_hasBits0 & 32) != 0) { return optionalSint64_; } else { return 0L; } } + set { + _hasBits0 |= 32; + optionalSint64_ = value; + } + } + /// Gets whether the "optional_sint64" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalSint64 { + get { return (_hasBits0 & 32) != 0; } + } + /// Clears the value of the "optional_sint64" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalSint64() { + _hasBits0 &= ~32; + } + + /// Field number for the "optional_fixed32" field. + public const int OptionalFixed32FieldNumber = 7; + private uint optionalFixed32_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public uint OptionalFixed32 { + get { if ((_hasBits0 & 64) != 0) { return optionalFixed32_; } else { return 0; } } + set { + _hasBits0 |= 64; + optionalFixed32_ = value; + } + } + /// Gets whether the "optional_fixed32" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalFixed32 { + get { return (_hasBits0 & 64) != 0; } + } + /// Clears the value of the "optional_fixed32" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalFixed32() { + _hasBits0 &= ~64; + } + + /// Field number for the "optional_fixed64" field. + public const int OptionalFixed64FieldNumber = 8; + private ulong optionalFixed64_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public ulong OptionalFixed64 { + get { if ((_hasBits0 & 128) != 0) { return optionalFixed64_; } else { return 0UL; } } + set { + _hasBits0 |= 128; + optionalFixed64_ = value; + } + } + /// Gets whether the "optional_fixed64" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalFixed64 { + get { return (_hasBits0 & 128) != 0; } + } + /// Clears the value of the "optional_fixed64" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalFixed64() { + _hasBits0 &= ~128; + } + + /// Field number for the "optional_sfixed32" field. + public const int OptionalSfixed32FieldNumber = 9; + private int optionalSfixed32_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int OptionalSfixed32 { + get { if ((_hasBits0 & 256) != 0) { return optionalSfixed32_; } else { return 0; } } + set { + _hasBits0 |= 256; + optionalSfixed32_ = value; + } + } + /// Gets whether the "optional_sfixed32" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalSfixed32 { + get { return (_hasBits0 & 256) != 0; } + } + /// Clears the value of the "optional_sfixed32" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalSfixed32() { + _hasBits0 &= ~256; + } + + /// Field number for the "optional_sfixed64" field. + public const int OptionalSfixed64FieldNumber = 10; + private long optionalSfixed64_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public long OptionalSfixed64 { + get { if ((_hasBits0 & 512) != 0) { return optionalSfixed64_; } else { return 0L; } } + set { + _hasBits0 |= 512; + optionalSfixed64_ = value; + } + } + /// Gets whether the "optional_sfixed64" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalSfixed64 { + get { return (_hasBits0 & 512) != 0; } + } + /// Clears the value of the "optional_sfixed64" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalSfixed64() { + _hasBits0 &= ~512; + } + + /// Field number for the "optional_float" field. + public const int OptionalFloatFieldNumber = 11; + private float optionalFloat_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public float OptionalFloat { + get { if ((_hasBits0 & 1024) != 0) { return optionalFloat_; } else { return 0F; } } + set { + _hasBits0 |= 1024; + optionalFloat_ = value; + } + } + /// Gets whether the "optional_float" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalFloat { + get { return (_hasBits0 & 1024) != 0; } + } + /// Clears the value of the "optional_float" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalFloat() { + _hasBits0 &= ~1024; + } + + /// Field number for the "optional_double" field. + public const int OptionalDoubleFieldNumber = 12; + private double optionalDouble_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public double OptionalDouble { + get { if ((_hasBits0 & 2048) != 0) { return optionalDouble_; } else { return 0D; } } + set { + _hasBits0 |= 2048; + optionalDouble_ = value; + } + } + /// Gets whether the "optional_double" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalDouble { + get { return (_hasBits0 & 2048) != 0; } + } + /// Clears the value of the "optional_double" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalDouble() { + _hasBits0 &= ~2048; + } + + /// Field number for the "optional_bool" field. + public const int OptionalBoolFieldNumber = 13; + private bool optionalBool_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool OptionalBool { + get { if ((_hasBits0 & 4096) != 0) { return optionalBool_; } else { return false; } } + set { + _hasBits0 |= 4096; + optionalBool_ = value; + } + } + /// Gets whether the "optional_bool" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalBool { + get { return (_hasBits0 & 4096) != 0; } + } + /// Clears the value of the "optional_bool" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalBool() { + _hasBits0 &= ~4096; + } + + /// Field number for the "optional_string" field. + public const int OptionalStringFieldNumber = 14; + private string optionalString_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string OptionalString { + get { return optionalString_ ?? ""; } + set { + optionalString_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + /// Gets whether the "optional_string" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalString { + get { return optionalString_ != null; } + } + /// Clears the value of the "optional_string" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalString() { + optionalString_ = null; + } + + /// Field number for the "optional_bytes" field. + public const int OptionalBytesFieldNumber = 15; + private pb::ByteString optionalBytes_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public pb::ByteString OptionalBytes { + get { return optionalBytes_ ?? pb::ByteString.Empty; } + set { + optionalBytes_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + /// Gets whether the "optional_bytes" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalBytes { + get { return optionalBytes_ != null; } + } + /// Clears the value of the "optional_bytes" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalBytes() { + optionalBytes_ = null; + } + + /// Field number for the "optional_cord" field. + public const int OptionalCordFieldNumber = 16; + private string optionalCord_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public string OptionalCord { + get { return optionalCord_ ?? ""; } + set { + optionalCord_ = pb::ProtoPreconditions.CheckNotNull(value, "value"); + } + } + /// Gets whether the "optional_cord" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalCord { + get { return optionalCord_ != null; } + } + /// Clears the value of the "optional_cord" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalCord() { + optionalCord_ = null; + } + + /// Field number for the "optional_nested_message" field. + public const int OptionalNestedMessageFieldNumber = 18; + private global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage optionalNestedMessage_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage OptionalNestedMessage { + get { return optionalNestedMessage_; } + set { + optionalNestedMessage_ = value; + } + } + + /// Field number for the "lazy_nested_message" field. + public const int LazyNestedMessageFieldNumber = 19; + private global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage lazyNestedMessage_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage LazyNestedMessage { + get { return lazyNestedMessage_; } + set { + lazyNestedMessage_ = value; + } + } + + /// Field number for the "optional_nested_enum" field. + public const int OptionalNestedEnumFieldNumber = 21; + private global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum optionalNestedEnum_; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum OptionalNestedEnum { + get { if ((_hasBits0 & 8192) != 0) { return optionalNestedEnum_; } else { return global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum.Unspecified; } } + set { + _hasBits0 |= 8192; + optionalNestedEnum_ = value; + } + } + /// Gets whether the "optional_nested_enum" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasOptionalNestedEnum { + get { return (_hasBits0 & 8192) != 0; } + } + /// Clears the value of the "optional_nested_enum" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearOptionalNestedEnum() { + _hasBits0 &= ~8192; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as TestProto3Optional); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(TestProto3Optional other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (OptionalInt32 != other.OptionalInt32) return false; + if (OptionalInt64 != other.OptionalInt64) return false; + if (OptionalUint32 != other.OptionalUint32) return false; + if (OptionalUint64 != other.OptionalUint64) return false; + if (OptionalSint32 != other.OptionalSint32) return false; + if (OptionalSint64 != other.OptionalSint64) return false; + if (OptionalFixed32 != other.OptionalFixed32) return false; + if (OptionalFixed64 != other.OptionalFixed64) return false; + if (OptionalSfixed32 != other.OptionalSfixed32) return false; + if (OptionalSfixed64 != other.OptionalSfixed64) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.Equals(OptionalFloat, other.OptionalFloat)) return false; + if (!pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.Equals(OptionalDouble, other.OptionalDouble)) return false; + if (OptionalBool != other.OptionalBool) return false; + if (OptionalString != other.OptionalString) return false; + if (OptionalBytes != other.OptionalBytes) return false; + if (OptionalCord != other.OptionalCord) return false; + if (!object.Equals(OptionalNestedMessage, other.OptionalNestedMessage)) return false; + if (!object.Equals(LazyNestedMessage, other.LazyNestedMessage)) return false; + if (OptionalNestedEnum != other.OptionalNestedEnum) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (HasOptionalInt32) hash ^= OptionalInt32.GetHashCode(); + if (HasOptionalInt64) hash ^= OptionalInt64.GetHashCode(); + if (HasOptionalUint32) hash ^= OptionalUint32.GetHashCode(); + if (HasOptionalUint64) hash ^= OptionalUint64.GetHashCode(); + if (HasOptionalSint32) hash ^= OptionalSint32.GetHashCode(); + if (HasOptionalSint64) hash ^= OptionalSint64.GetHashCode(); + if (HasOptionalFixed32) hash ^= OptionalFixed32.GetHashCode(); + if (HasOptionalFixed64) hash ^= OptionalFixed64.GetHashCode(); + if (HasOptionalSfixed32) hash ^= OptionalSfixed32.GetHashCode(); + if (HasOptionalSfixed64) hash ^= OptionalSfixed64.GetHashCode(); + if (HasOptionalFloat) hash ^= pbc::ProtobufEqualityComparers.BitwiseSingleEqualityComparer.GetHashCode(OptionalFloat); + if (HasOptionalDouble) hash ^= pbc::ProtobufEqualityComparers.BitwiseDoubleEqualityComparer.GetHashCode(OptionalDouble); + if (HasOptionalBool) hash ^= OptionalBool.GetHashCode(); + if (HasOptionalString) hash ^= OptionalString.GetHashCode(); + if (HasOptionalBytes) hash ^= OptionalBytes.GetHashCode(); + if (HasOptionalCord) hash ^= OptionalCord.GetHashCode(); + if (optionalNestedMessage_ != null) hash ^= OptionalNestedMessage.GetHashCode(); + if (lazyNestedMessage_ != null) hash ^= LazyNestedMessage.GetHashCode(); + if (HasOptionalNestedEnum) hash ^= OptionalNestedEnum.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (HasOptionalInt32) { + output.WriteRawTag(8); + output.WriteInt32(OptionalInt32); + } + if (HasOptionalInt64) { + output.WriteRawTag(16); + output.WriteInt64(OptionalInt64); + } + if (HasOptionalUint32) { + output.WriteRawTag(24); + output.WriteUInt32(OptionalUint32); + } + if (HasOptionalUint64) { + output.WriteRawTag(32); + output.WriteUInt64(OptionalUint64); + } + if (HasOptionalSint32) { + output.WriteRawTag(40); + output.WriteSInt32(OptionalSint32); + } + if (HasOptionalSint64) { + output.WriteRawTag(48); + output.WriteSInt64(OptionalSint64); + } + if (HasOptionalFixed32) { + output.WriteRawTag(61); + output.WriteFixed32(OptionalFixed32); + } + if (HasOptionalFixed64) { + output.WriteRawTag(65); + output.WriteFixed64(OptionalFixed64); + } + if (HasOptionalSfixed32) { + output.WriteRawTag(77); + output.WriteSFixed32(OptionalSfixed32); + } + if (HasOptionalSfixed64) { + output.WriteRawTag(81); + output.WriteSFixed64(OptionalSfixed64); + } + if (HasOptionalFloat) { + output.WriteRawTag(93); + output.WriteFloat(OptionalFloat); + } + if (HasOptionalDouble) { + output.WriteRawTag(97); + output.WriteDouble(OptionalDouble); + } + if (HasOptionalBool) { + output.WriteRawTag(104); + output.WriteBool(OptionalBool); + } + if (HasOptionalString) { + output.WriteRawTag(114); + output.WriteString(OptionalString); + } + if (HasOptionalBytes) { + output.WriteRawTag(122); + output.WriteBytes(OptionalBytes); + } + if (HasOptionalCord) { + output.WriteRawTag(130, 1); + output.WriteString(OptionalCord); + } + if (optionalNestedMessage_ != null) { + output.WriteRawTag(146, 1); + output.WriteMessage(OptionalNestedMessage); + } + if (lazyNestedMessage_ != null) { + output.WriteRawTag(154, 1); + output.WriteMessage(LazyNestedMessage); + } + if (HasOptionalNestedEnum) { + output.WriteRawTag(168, 1); + output.WriteEnum((int) OptionalNestedEnum); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (HasOptionalInt32) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(OptionalInt32); + } + if (HasOptionalInt64) { + size += 1 + pb::CodedOutputStream.ComputeInt64Size(OptionalInt64); + } + if (HasOptionalUint32) { + size += 1 + pb::CodedOutputStream.ComputeUInt32Size(OptionalUint32); + } + if (HasOptionalUint64) { + size += 1 + pb::CodedOutputStream.ComputeUInt64Size(OptionalUint64); + } + if (HasOptionalSint32) { + size += 1 + pb::CodedOutputStream.ComputeSInt32Size(OptionalSint32); + } + if (HasOptionalSint64) { + size += 1 + pb::CodedOutputStream.ComputeSInt64Size(OptionalSint64); + } + if (HasOptionalFixed32) { + size += 1 + 4; + } + if (HasOptionalFixed64) { + size += 1 + 8; + } + if (HasOptionalSfixed32) { + size += 1 + 4; + } + if (HasOptionalSfixed64) { + size += 1 + 8; + } + if (HasOptionalFloat) { + size += 1 + 4; + } + if (HasOptionalDouble) { + size += 1 + 8; + } + if (HasOptionalBool) { + size += 1 + 1; + } + if (HasOptionalString) { + size += 1 + pb::CodedOutputStream.ComputeStringSize(OptionalString); + } + if (HasOptionalBytes) { + size += 1 + pb::CodedOutputStream.ComputeBytesSize(OptionalBytes); + } + if (HasOptionalCord) { + size += 2 + pb::CodedOutputStream.ComputeStringSize(OptionalCord); + } + if (optionalNestedMessage_ != null) { + size += 2 + pb::CodedOutputStream.ComputeMessageSize(OptionalNestedMessage); + } + if (lazyNestedMessage_ != null) { + size += 2 + pb::CodedOutputStream.ComputeMessageSize(LazyNestedMessage); + } + if (HasOptionalNestedEnum) { + size += 2 + pb::CodedOutputStream.ComputeEnumSize((int) OptionalNestedEnum); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(TestProto3Optional other) { + if (other == null) { + return; + } + if (other.HasOptionalInt32) { + OptionalInt32 = other.OptionalInt32; + } + if (other.HasOptionalInt64) { + OptionalInt64 = other.OptionalInt64; + } + if (other.HasOptionalUint32) { + OptionalUint32 = other.OptionalUint32; + } + if (other.HasOptionalUint64) { + OptionalUint64 = other.OptionalUint64; + } + if (other.HasOptionalSint32) { + OptionalSint32 = other.OptionalSint32; + } + if (other.HasOptionalSint64) { + OptionalSint64 = other.OptionalSint64; + } + if (other.HasOptionalFixed32) { + OptionalFixed32 = other.OptionalFixed32; + } + if (other.HasOptionalFixed64) { + OptionalFixed64 = other.OptionalFixed64; + } + if (other.HasOptionalSfixed32) { + OptionalSfixed32 = other.OptionalSfixed32; + } + if (other.HasOptionalSfixed64) { + OptionalSfixed64 = other.OptionalSfixed64; + } + if (other.HasOptionalFloat) { + OptionalFloat = other.OptionalFloat; + } + if (other.HasOptionalDouble) { + OptionalDouble = other.OptionalDouble; + } + if (other.HasOptionalBool) { + OptionalBool = other.OptionalBool; + } + if (other.HasOptionalString) { + OptionalString = other.OptionalString; + } + if (other.HasOptionalBytes) { + OptionalBytes = other.OptionalBytes; + } + if (other.HasOptionalCord) { + OptionalCord = other.OptionalCord; + } + if (other.optionalNestedMessage_ != null) { + if (optionalNestedMessage_ == null) { + OptionalNestedMessage = new global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage(); + } + OptionalNestedMessage.MergeFrom(other.OptionalNestedMessage); + } + if (other.lazyNestedMessage_ != null) { + if (lazyNestedMessage_ == null) { + LazyNestedMessage = new global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage(); + } + LazyNestedMessage.MergeFrom(other.LazyNestedMessage); + } + if (other.HasOptionalNestedEnum) { + OptionalNestedEnum = other.OptionalNestedEnum; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + OptionalInt32 = input.ReadInt32(); + break; + } + case 16: { + OptionalInt64 = input.ReadInt64(); + break; + } + case 24: { + OptionalUint32 = input.ReadUInt32(); + break; + } + case 32: { + OptionalUint64 = input.ReadUInt64(); + break; + } + case 40: { + OptionalSint32 = input.ReadSInt32(); + break; + } + case 48: { + OptionalSint64 = input.ReadSInt64(); + break; + } + case 61: { + OptionalFixed32 = input.ReadFixed32(); + break; + } + case 65: { + OptionalFixed64 = input.ReadFixed64(); + break; + } + case 77: { + OptionalSfixed32 = input.ReadSFixed32(); + break; + } + case 81: { + OptionalSfixed64 = input.ReadSFixed64(); + break; + } + case 93: { + OptionalFloat = input.ReadFloat(); + break; + } + case 97: { + OptionalDouble = input.ReadDouble(); + break; + } + case 104: { + OptionalBool = input.ReadBool(); + break; + } + case 114: { + OptionalString = input.ReadString(); + break; + } + case 122: { + OptionalBytes = input.ReadBytes(); + break; + } + case 130: { + OptionalCord = input.ReadString(); + break; + } + case 146: { + if (optionalNestedMessage_ == null) { + OptionalNestedMessage = new global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage(); + } + input.ReadMessage(OptionalNestedMessage); + break; + } + case 154: { + if (lazyNestedMessage_ == null) { + LazyNestedMessage = new global::ProtobufUnittest.TestProto3Optional.Types.NestedMessage(); + } + input.ReadMessage(LazyNestedMessage); + break; + } + case 168: { + OptionalNestedEnum = (global::ProtobufUnittest.TestProto3Optional.Types.NestedEnum) input.ReadEnum(); + break; + } + } + } + } + + #region Nested types + /// Container for nested types declared in the TestProto3Optional message type. + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static partial class Types { + public enum NestedEnum { + [pbr::OriginalName("UNSPECIFIED")] Unspecified = 0, + [pbr::OriginalName("FOO")] Foo = 1, + [pbr::OriginalName("BAR")] Bar = 2, + [pbr::OriginalName("BAZ")] Baz = 3, + /// + /// Intentionally negative. + /// + [pbr::OriginalName("NEG")] Neg = -1, + } + + public sealed partial class NestedMessage : pb::IMessage { + private static readonly pb::MessageParser _parser = new pb::MessageParser(() => new NestedMessage()); + private pb::UnknownFieldSet _unknownFields; + private int _hasBits0; + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pb::MessageParser Parser { get { return _parser; } } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public static pbr::MessageDescriptor Descriptor { + get { return global::ProtobufUnittest.TestProto3Optional.Descriptor.NestedTypes[0]; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + pbr::MessageDescriptor pb::IMessage.Descriptor { + get { return Descriptor; } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public NestedMessage() { + OnConstruction(); + } + + partial void OnConstruction(); + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public NestedMessage(NestedMessage other) : this() { + _hasBits0 = other._hasBits0; + bb_ = other.bb_; + _unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public NestedMessage Clone() { + return new NestedMessage(this); + } + + /// Field number for the "bb" field. + public const int BbFieldNumber = 1; + private int bb_; + /// + /// The field name "b" fails to compile in proto1 because it conflicts with + /// a local variable named "b" in one of the generated methods. Doh. + /// This file needs to compile in proto1 to test backwards-compatibility. + /// + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int Bb { + get { if ((_hasBits0 & 1) != 0) { return bb_; } else { return 0; } } + set { + _hasBits0 |= 1; + bb_ = value; + } + } + /// Gets whether the "bb" field is set + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool HasBb { + get { return (_hasBits0 & 1) != 0; } + } + /// Clears the value of the "bb" field + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void ClearBb() { + _hasBits0 &= ~1; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override bool Equals(object other) { + return Equals(other as NestedMessage); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public bool Equals(NestedMessage other) { + if (ReferenceEquals(other, null)) { + return false; + } + if (ReferenceEquals(other, this)) { + return true; + } + if (Bb != other.Bb) return false; + return Equals(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override int GetHashCode() { + int hash = 1; + if (HasBb) hash ^= Bb.GetHashCode(); + if (_unknownFields != null) { + hash ^= _unknownFields.GetHashCode(); + } + return hash; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public override string ToString() { + return pb::JsonFormatter.ToDiagnosticString(this); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void WriteTo(pb::CodedOutputStream output) { + if (HasBb) { + output.WriteRawTag(8); + output.WriteInt32(Bb); + } + if (_unknownFields != null) { + _unknownFields.WriteTo(output); + } + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public int CalculateSize() { + int size = 0; + if (HasBb) { + size += 1 + pb::CodedOutputStream.ComputeInt32Size(Bb); + } + if (_unknownFields != null) { + size += _unknownFields.CalculateSize(); + } + return size; + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(NestedMessage other) { + if (other == null) { + return; + } + if (other.HasBb) { + Bb = other.Bb; + } + _unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields); + } + + [global::System.Diagnostics.DebuggerNonUserCodeAttribute] + public void MergeFrom(pb::CodedInputStream input) { + uint tag; + while ((tag = input.ReadTag()) != 0) { + switch(tag) { + default: + _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input); + break; + case 8: { + Bb = input.ReadInt32(); + break; + } + } + } + } + + } + + } + #endregion + + } + + #endregion + +} + +#endregion Designer generated code diff --git a/csharp/src/Google.Protobuf.Test/Proto3OptionalTest.cs b/csharp/src/Google.Protobuf.Test/Proto3OptionalTest.cs new file mode 100644 index 0000000000..20c1846da4 --- /dev/null +++ b/csharp/src/Google.Protobuf.Test/Proto3OptionalTest.cs @@ -0,0 +1,143 @@ +#region Copyright notice and license +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion + +using NUnit.Framework; +using ProtobufUnittest; +using System; +using System.IO; + +namespace Google.Protobuf.Test +{ + class Proto3OptionalTest + { + [Test] + public void OptionalInt32FieldLifecycle() + { + var message = new TestProto3Optional(); + Assert.IsFalse(message.HasOptionalInt32); + Assert.AreEqual(0, message.OptionalInt32); + + message.OptionalInt32 = 5; + Assert.IsTrue(message.HasOptionalInt32); + Assert.AreEqual(5, message.OptionalInt32); + + message.OptionalInt32 = 0; + Assert.IsTrue(message.HasOptionalInt32); + Assert.AreEqual(0, message.OptionalInt32); + + message.ClearOptionalInt32(); + Assert.IsFalse(message.HasOptionalInt32); + Assert.AreEqual(0, message.OptionalInt32); + } + + [Test] + public void OptionalStringFieldLifecycle() + { + var message = new TestProto3Optional(); + Assert.IsFalse(message.HasOptionalString); + Assert.AreEqual("", message.OptionalString); + + message.OptionalString = "x"; + Assert.IsTrue(message.HasOptionalString); + Assert.AreEqual("x", message.OptionalString); + + message.OptionalString = ""; + Assert.IsTrue(message.HasOptionalString); + Assert.AreEqual("", message.OptionalString); + + message.ClearOptionalString(); + Assert.IsFalse(message.HasOptionalString); + Assert.AreEqual("", message.OptionalString); + + Assert.Throws(() => message.OptionalString = null); + } + + [Test] + public void Clone() + { + var original = new TestProto3Optional { OptionalInt64 = 0L }; + + var clone = original.Clone(); + Assert.False(clone.HasOptionalInt32); + Assert.AreEqual(0, clone.OptionalInt32); + Assert.True(clone.HasOptionalInt64); + Assert.AreEqual(0L, clone.OptionalInt64); + } + + [Test] + public void Serialization_NotSet() + { + var stream = new MemoryStream(); + var message = new TestProto3Optional(); + message.WriteTo(stream); + Assert.AreEqual(0, stream.Length); + } + + [Test] + public void Serialization_SetToDefault() + { + var stream = new MemoryStream(); + var message = new TestProto3Optional { OptionalInt32 = 0 }; + message.WriteTo(stream); + Assert.AreEqual(2, stream.Length); // Tag and value + } + + [Test] + public void Serialization_Roundtrip() + { + var original = new TestProto3Optional { OptionalInt64 = 0L, OptionalFixed32 = 5U }; + var stream = new MemoryStream(); + original.WriteTo(stream); + stream.Position = 0; + var deserialized = TestProto3Optional.Parser.ParseFrom(stream); + + Assert.AreEqual(0, deserialized.OptionalInt32); + Assert.IsFalse(deserialized.HasOptionalInt32); + + Assert.AreEqual(0L, deserialized.OptionalInt64); + Assert.IsTrue(deserialized.HasOptionalInt64); + + Assert.AreEqual(5U, deserialized.OptionalFixed32); + Assert.IsTrue(deserialized.HasOptionalFixed32); + } + + [Test] + public void Equality_IgnoresPresence() + { + var message1 = new TestProto3Optional { OptionalInt32 = 0 }; + var message2 = new TestProto3Optional(); + + Assert.IsTrue(message1.Equals(message2)); + message1.ClearOptionalInt32(); + } + } +} diff --git a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs index 482db535e2..ebb8394d23 100644 --- a/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs +++ b/csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs @@ -32,6 +32,7 @@ using Google.Protobuf.TestProtos; using NUnit.Framework; +using ProtobufUnittest; using System; using System.Collections.Generic; using System.Linq; @@ -247,6 +248,7 @@ namespace Google.Protobuf.Reflection FieldDescriptor enumField = testAllTypesDescriptor.FindDescriptor("single_nested_enum"); FieldDescriptor foreignMessageField = testAllTypesDescriptor.FindDescriptor("single_foreign_message"); FieldDescriptor importMessageField = testAllTypesDescriptor.FindDescriptor("single_import_message"); + FieldDescriptor fieldInOneof = testAllTypesDescriptor.FindDescriptor("oneof_string"); Assert.AreEqual("single_int32", primitiveField.Name); Assert.AreEqual("protobuf_unittest3.TestAllTypes.single_int32", @@ -268,6 +270,10 @@ namespace Google.Protobuf.Reflection Assert.AreEqual("single_import_message", importMessageField.Name); Assert.AreEqual(FieldType.Message, importMessageField.FieldType); Assert.AreEqual(importMessageDescriptor, importMessageField.MessageType); + + // For a field in a regular onoef, ContainingOneof and RealContainingOneof should be the same. + Assert.AreEqual("oneof_field", fieldInOneof.ContainingOneof.Name); + Assert.AreSame(fieldInOneof.ContainingOneof, fieldInOneof.RealContainingOneof); } [Test] @@ -318,6 +324,7 @@ namespace Google.Protobuf.Reflection public void OneofDescriptor() { OneofDescriptor descriptor = TestAllTypes.Descriptor.FindDescriptor("oneof_field"); + Assert.IsFalse(descriptor.IsSynthetic); Assert.AreEqual("oneof_field", descriptor.Name); Assert.AreEqual("protobuf_unittest3.TestAllTypes.oneof_field", descriptor.FullName); @@ -383,5 +390,48 @@ namespace Google.Protobuf.Reflection var importingDescriptor = TestProtos.OldGenerator.OldExtensions1Reflection.Descriptor; Assert.NotNull(importingDescriptor); } + + [Test] + public void Proto3OptionalDescriptors() + { + var descriptor = TestProto3Optional.Descriptor; + var field = descriptor.Fields[TestProto3Optional.OptionalInt32FieldNumber]; + Assert.NotNull(field.ContainingOneof); + Assert.IsTrue(field.ContainingOneof.IsSynthetic); + Assert.Null(field.RealContainingOneof); + } + + + [Test] + public void SyntheticOneofReflection() + { + // Expect every oneof in TestProto3Optional to be synthetic + var proto3OptionalDescriptor = TestProto3Optional.Descriptor; + Assert.AreEqual(0, proto3OptionalDescriptor.RealOneofCount); + foreach (var oneof in proto3OptionalDescriptor.Oneofs) + { + Assert.True(oneof.IsSynthetic); + } + + // Expect no oneof in the original proto3 unit test file to be synthetic. + foreach (var descriptor in ProtobufTestMessages.Proto3.TestMessagesProto3Reflection.Descriptor.MessageTypes) + { + Assert.AreEqual(descriptor.Oneofs.Count, descriptor.RealOneofCount); + foreach (var oneof in descriptor.Oneofs) + { + Assert.False(oneof.IsSynthetic); + } + } + + // Expect no oneof in the original proto2 unit test file to be synthetic. + foreach (var descriptor in ProtobufTestMessages.Proto2.TestMessagesProto2Reflection.Descriptor.MessageTypes) + { + Assert.AreEqual(descriptor.Oneofs.Count, descriptor.RealOneofCount); + foreach (var oneof in descriptor.Oneofs) + { + Assert.False(oneof.IsSynthetic); + } + } + } } } diff --git a/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs b/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs index 9651ec30d8..0d4034c5b1 100644 --- a/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs +++ b/csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs @@ -38,6 +38,7 @@ using System.Collections; using System.Collections.Generic; using static Google.Protobuf.TestProtos.Proto2.UnittestExtensions; +using ProtobufUnittest; namespace Google.Protobuf.Reflection { @@ -104,6 +105,21 @@ namespace Google.Protobuf.Reflection Assert.Throws(() => fields[TestProtos.TestAllTypes.SingleBoolFieldNumber].Accessor.HasValue(message)); } + [Test] + public void HasValue_Proto3Optional() + { + IMessage message = new TestProto3Optional + { + OptionalInt32 = 0, + LazyNestedMessage = new TestProto3Optional.Types.NestedMessage() + }; + var fields = message.Descriptor.Fields; + Assert.IsFalse(fields[TestProto3Optional.OptionalInt64FieldNumber].Accessor.HasValue(message)); + Assert.IsFalse(fields[TestProto3Optional.OptionalNestedMessageFieldNumber].Accessor.HasValue(message)); + Assert.IsTrue(fields[TestProto3Optional.LazyNestedMessageFieldNumber].Accessor.HasValue(message)); + Assert.IsTrue(fields[TestProto3Optional.OptionalInt32FieldNumber].Accessor.HasValue(message)); + } + [Test] public void HasValue() { @@ -225,6 +241,27 @@ namespace Google.Protobuf.Reflection Assert.AreEqual(0, mapMessage.MapStringString.Count); } + [Test] + public void Clear_Proto3Optional() + { + TestProto3Optional message = new TestProto3Optional + { + OptionalInt32 = 0, + OptionalNestedMessage = new TestProto3Optional.Types.NestedMessage() + }; + var primitiveField = TestProto3Optional.Descriptor.Fields[TestProto3Optional.OptionalInt32FieldNumber]; + var messageField = TestProto3Optional.Descriptor.Fields[TestProto3Optional.OptionalNestedMessageFieldNumber]; + + Assert.True(message.HasOptionalInt32); + Assert.NotNull(message.OptionalNestedMessage); + + primitiveField.Accessor.Clear(message); + messageField.Accessor.Clear(message); + + Assert.False(message.HasOptionalInt32); + Assert.Null(message.OptionalNestedMessage); + } + [Test] public void FieldDescriptor_ByName() { diff --git a/csharp/src/Google.Protobuf.Test/testprotos.pb b/csharp/src/Google.Protobuf.Test/testprotos.pb index f5e1befca3..1758578eb7 100644 Binary files a/csharp/src/Google.Protobuf.Test/testprotos.pb and b/csharp/src/Google.Protobuf.Test/testprotos.pb differ diff --git a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs index 5abd0dd785..ef3f1b6b66 100644 --- a/csharp/src/Google.Protobuf/Reflection/Descriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/Descriptor.cs @@ -2104,10 +2104,21 @@ namespace Google.Protobuf.Reflection { /// If true, this is a proto3 "optional". When a proto3 field is optional, it /// tracks presence regardless of field type. /// - /// For message fields this doesn't create any semantic change, since - /// non-repeated message fields always track presence. However it still + /// When proto3_optional is true, this field must be belong to a oneof to + /// signal to old proto3 clients that presence is tracked for this field. This + /// oneof is known as a "synthetic" oneof, and this field must be its sole + /// member (each proto3 optional field gets its own synthetic oneof). Synthetic + /// oneofs exist in the descriptor only, and do not generate any API. Synthetic + /// oneofs must be ordered after all "real" oneofs. + /// + /// For message fields, proto3_optional doesn't create any semantic change, + /// since non-repeated message fields always track presence. However it still /// indicates the semantic detail of whether the user wrote "optional" or not. - /// This can be useful for round-tripping the .proto file. + /// This can be useful for round-tripping the .proto file. For consistency we + /// give message fields a synthetic oneof also, even though it is not required + /// to track presence. This is especially important because the parser can't + /// tell if a field is a message or an enum, so it must always create a + /// synthetic oneof. /// /// Proto2 optional fields do not set this flag, because they already indicate /// optional with `LABEL_OPTIONAL`. diff --git a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs index ddd671aadb..69bab4f010 100644 --- a/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs @@ -58,6 +58,12 @@ namespace Google.Protobuf.Reflection /// public OneofDescriptor ContainingOneof { get; } + /// + /// Returns the oneof containing this field if it's a "real" oneof, or null if either this + /// field is not part of a oneof, or the oneof is synthetic. + /// + public OneofDescriptor RealContainingOneof => ContainingOneof?.IsSynthetic == false ? ContainingOneof : null; + /// /// The effective JSON name for this field. This is usually the lower-camel-cased form of the field name, /// but can be overridden using the json_name option in the .proto file. diff --git a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs index b48c4f9df5..d73427b73b 100644 --- a/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs @@ -59,7 +59,8 @@ namespace Google.Protobuf.Reflection object GetValue(IMessage message); /// - /// Indicates whether the field in the specified message is set. For proto3 fields, this throws an + /// Indicates whether the field in the specified message is set. + /// For proto3 fields that aren't explicitly optional, this throws an /// bool HasValue(IMessage message); diff --git a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs index eda1965c54..6217081fbc 100644 --- a/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs @@ -80,6 +80,20 @@ namespace Google.Protobuf.Reflection (oneof, index) => new OneofDescriptor(oneof, file, this, index, generatedCodeInfo?.OneofNames[index])); + int syntheticOneofCount = 0; + foreach (var oneof in Oneofs) + { + if (oneof.IsSynthetic) + { + syntheticOneofCount++; + } + else if (syntheticOneofCount != 0) + { + throw new ArgumentException("All synthetic oneofs should come after real oneofs"); + } + } + RealOneofCount = Oneofs.Count - syntheticOneofCount; + NestedTypes = DescriptorUtil.ConvertAndMakeReadOnly( proto.NestedType, (type, index) => @@ -234,9 +248,19 @@ namespace Google.Protobuf.Reflection /// /// An unmodifiable list of the "oneof" field collections in this message type. + /// All "real" oneofs (where returns false) + /// come before synthetic ones. /// public IList Oneofs { get; } + /// + /// The number of real "oneof" descriptors in this message type. Every element in + /// with an index less than this will have a property value + /// of false; every element with an index greater than or equal to this will have a + /// property value of true. + /// + public int RealOneofCount { get; } + /// /// Finds a field by field name. /// diff --git a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs index f4bf628a6f..4e040c17ea 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs @@ -43,19 +43,31 @@ namespace Google.Protobuf.Reflection { private readonly Func caseDelegate; private readonly Action clearDelegate; - private OneofDescriptor descriptor; - internal OneofAccessor(PropertyInfo caseProperty, MethodInfo clearMethod, OneofDescriptor descriptor) + private OneofAccessor(OneofDescriptor descriptor, Func caseDelegate, Action clearDelegate) { - if (!caseProperty.CanRead) - { - throw new ArgumentException("Cannot read from property"); - } - this.descriptor = descriptor; - caseDelegate = ReflectionUtil.CreateFuncIMessageInt32(caseProperty.GetGetMethod()); + Descriptor = descriptor; + this.caseDelegate = caseDelegate; + this.clearDelegate = clearDelegate; + } + + internal static OneofAccessor ForRegularOneof( + OneofDescriptor descriptor, + PropertyInfo caseProperty, + MethodInfo clearMethod) => + new OneofAccessor( + descriptor, + ReflectionUtil.CreateFuncIMessageInt32(caseProperty.GetGetMethod()), + ReflectionUtil.CreateActionIMessage(clearMethod)); - this.descriptor = descriptor; - clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod); + internal static OneofAccessor ForSyntheticOneof(OneofDescriptor descriptor) + { + // Note: descriptor.Fields will be null when this method is called, because we haven't + // cross-linked yet. But by the time the delgates are called by user code, all will be + // well. (That's why we capture the descriptor itself rather than a field.) + return new OneofAccessor(descriptor, + message => descriptor.Fields[0].Accessor.HasValue(message) ? descriptor.Fields[0].FieldNumber : 0, + message => descriptor.Fields[0].Accessor.Clear(message)); } /// @@ -64,15 +76,12 @@ namespace Google.Protobuf.Reflection /// /// The descriptor of the oneof. /// - public OneofDescriptor Descriptor { get { return descriptor; } } + public OneofDescriptor Descriptor { get; } /// /// Clears the oneof in the specified message. /// - public void Clear(IMessage message) - { - clearDelegate(message); - } + public void Clear(IMessage message) => clearDelegate(message); /// /// Indicates which field in the oneof is set for specified message @@ -80,11 +89,9 @@ namespace Google.Protobuf.Reflection public FieldDescriptor GetCaseFieldDescriptor(IMessage message) { int fieldNumber = caseDelegate(message); - if (fieldNumber > 0) - { - return descriptor.ContainingType.FindFieldByNumber(fieldNumber); - } - return null; + return fieldNumber > 0 + ? Descriptor.ContainingType.FindFieldByNumber(fieldNumber) + : null; } } } diff --git a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs index 1e30b92ed6..7cceabd7c3 100644 --- a/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs +++ b/csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs @@ -33,6 +33,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Linq; using Google.Protobuf.Collections; using Google.Protobuf.Compatibility; @@ -54,8 +55,13 @@ namespace Google.Protobuf.Reflection { this.proto = proto; containingType = parent; - file.DescriptorPool.AddSymbol(this); + + // It's useful to determine whether or not this is a synthetic oneof before cross-linking. That means + // diving into the proto directly rather than using FieldDescriptor, but that's okay. + var firstFieldInOneof = parent.Proto.Field.FirstOrDefault(fieldProto => fieldProto.OneofIndex == index); + IsSynthetic = firstFieldInOneof?.Proto3Optional ?? false; + accessor = CreateAccessor(clrName); } @@ -83,6 +89,12 @@ namespace Google.Protobuf.Reflection /// public IList Fields { get { return fields; } } + /// + /// Returns true if this oneof is a synthetic oneof containing a proto3 optional field; + /// false otherwise. + /// + public bool IsSynthetic { get; } + /// /// Gets an accessor for reflective access to the values associated with the oneof /// in a particular message. @@ -146,18 +158,28 @@ namespace Google.Protobuf.Reflection { return null; } - var caseProperty = containingType.ClrType.GetProperty(clrName + "Case"); - if (caseProperty == null) + if (IsSynthetic) { - throw new DescriptorValidationException(this, $"Property {clrName}Case not found in {containingType.ClrType}"); + return OneofAccessor.ForSyntheticOneof(this); } - var clearMethod = containingType.ClrType.GetMethod("Clear" + clrName); - if (clearMethod == null) + else { - throw new DescriptorValidationException(this, $"Method Clear{clrName} not found in {containingType.ClrType}"); + var caseProperty = containingType.ClrType.GetProperty(clrName + "Case"); + if (caseProperty == null) + { + throw new DescriptorValidationException(this, $"Property {clrName}Case not found in {containingType.ClrType}"); + } + if (!caseProperty.CanRead) + { + throw new ArgumentException($"Cannot read from property {clrName}Case in {containingType.ClrType}"); + } + var clearMethod = containingType.ClrType.GetMethod("Clear" + clrName); + if (clearMethod == null) + { + throw new DescriptorValidationException(this, $"Method Clear{clrName} not found in {containingType.ClrType}"); + } + return OneofAccessor.ForRegularOneof(this, caseProperty, clearMethod); } - - return new OneofAccessor(caseProperty, clearMethod, this); } } } diff --git a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs index de10226511..ed844bc51d 100644 --- a/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs +++ b/csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs @@ -57,10 +57,11 @@ namespace Google.Protobuf.Reflection throw new ArgumentException("Not all required properties/methods available"); } setValueDelegate = ReflectionUtil.CreateActionIMessageObject(property.GetSetMethod()); - if (descriptor.File.Syntax == Syntax.Proto3) + if (descriptor.File.Syntax == Syntax.Proto3 && !descriptor.Proto.Proto3Optional) { - hasDelegate = message => { - throw new InvalidOperationException("HasValue is not implemented for proto3 fields"); + hasDelegate = message => + { + throw new InvalidOperationException("HasValue is not implemented for non-optional proto3 fields"); }; var clrType = property.PropertyType; @@ -74,16 +75,29 @@ namespace Google.Protobuf.Reflection } else { - MethodInfo hasMethod = property.DeclaringType.GetRuntimeProperty("Has" + property.Name).GetMethod; - if (hasMethod == null) { - throw new ArgumentException("Not all required properties/methods are available"); + // For message fields, just compare with null and set to null. + // For primitive fields, use the Has/Clear methods. + + if (descriptor.FieldType == FieldType.Message) + { + hasDelegate = message => GetValue(message) != null; + clearDelegate = message => SetValue(message, null); } - hasDelegate = ReflectionUtil.CreateFuncIMessageBool(hasMethod); - MethodInfo clearMethod = property.DeclaringType.GetRuntimeMethod("Clear" + property.Name, ReflectionUtil.EmptyTypes); - if (clearMethod == null) { - throw new ArgumentException("Not all required properties/methods are available"); + else + { + MethodInfo hasMethod = property.DeclaringType.GetRuntimeProperty("Has" + property.Name).GetMethod; + if (hasMethod == null) + { + throw new ArgumentException("Not all required properties/methods are available"); + } + hasDelegate = ReflectionUtil.CreateFuncIMessageBool(hasMethod); + MethodInfo clearMethod = property.DeclaringType.GetRuntimeMethod("Clear" + property.Name, ReflectionUtil.EmptyTypes); + if (clearMethod == null) + { + throw new ArgumentException("Not all required properties/methods are available"); + } + clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod); } - clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod); } } diff --git a/docs/field_presence.md b/docs/field_presence.md new file mode 100644 index 0000000000..0491b820d6 --- /dev/null +++ b/docs/field_presence.md @@ -0,0 +1,476 @@ +# Application note: Field presence + +This application note explains the various presence tracking disciplies for protobuf fields. It also explains how to enable experimental support for explicit presence tracking for singular proto3 fields with basic types. + +## Background + +_Field presence_ is the notion of whether a protobuf field has a value. There are two different manifestations of presence for protobufs: _no presence_, where the generated message API stores field values (only), and _explicit presence_, where the API also stores whether or not a field has been set. + +Historically, proto2 has mostly followed _explicit presence_, while proto3 exposes only _no presence_ semantics. Singular proto3 fields of basic types (numeric, string, bytes, and enums) which are defined with the `optional` label have _explicit presence_, like proto2 (this is an experimental feature added as of release 3.12, and must be enabled by passing a flag to `protoc`). + +### Presence disciplines + +_Presence disciplines_ define the semantics for translating between the _API representation_ and the _serialized representation_. The _no presence_ discipline relies upon the field value itself to make decisions at (de)serialization time, while the _explicit presence_ discipline relies upon the explicit tracking state instead. + +### Presence in _tag-value stream_ (wire format) serialization + +The wire format is a stream of tagged, _self-delimiting_ values. By definition, the wire format represents a sequence of _present_ values. In other words, every value found within a serialization represents a _present_ field; furthermore, the serialization contains no information about not-present values. + +The generated API for a proto message includes (de)serialization definitions which translate between API types and a stream of definitionally _present_ (tag, value) pairs. This translation is designed to be forward- and backward-compatibile across changes to the message definition; however, this compatibility introduces some (perhaps surprising) considerations when deserializing wire-formatted messages: + +- When serializing, fields with _no presence_ are not serialized if they contain their default value. + - For numeric types, the default is 0. + - For enums, the default is the zero-valued enumerator. + - For strings, bytes, and repeated fields, the default is the zero-length value. + - For messages, the default is the language-specific null value. +- "Empty" length-delimited values (such as empty strings) can be validly represented in serialized values: the field is "present," in the sense that it appears in the wire format. However, if the generated API does not track presence, then these values may not be re-serialized; i.e., the empty field may be "not present" after a serialization round-trip. +- When deserializing, duplicate field values may be handled in different ways depending on the field definition. + - Duplicate `repeated` fields are typically appended to the field's API representation. (Note that serializing a _packed_ repeated field produces only one, length-delimited value in the tag stream.) + - Duplicate `optional` field values follow the rule that "the last one wins." +- `oneof` fields expose the API-level invariant that only one field is set at a time. However, the wire format may include multiple (tag, value) pairs which notionally belong to the `oneof`. Similar to `optional` fields, the generated API follows the "last one wins" rule. +- Out-of-range values are not returned for enum fields in generated proto2 APIs. However, out-of-range values may be stored as _unknown fields_ in the API, even though the wire-format tag was recognized. + +### Presence in _named-field mapping_ formats + +Protobufs can be represented in human-readable, textual forms. Two notable formats are TextFormat (the output format produced by generated message `DebugString` methods) and JSON. + +These formats have correctness requirements of their own, and are generally stricter than _tagged-value stream_ formats. However, TextFormat more closely mimics the semantics of the wire format, and does, in certain cases, provide similar semantics (for example, appending repeated name-value mappings to a repeated field). In particular, similar to the wire format, TextFormat only includes fields which are present. + +JSON is a much stricter format, however, and cannot validly represent some semantics of the wire format or TextFormat. + +- Notably, JSON _elements_ are semantically unordered, and each member must have a unique name. This is different from TextFormat rules for repeated fields. +- JSON may include fields that are "not present," unlike the _no presence_ discipline for other formats: + - JSON defines a `null` value, which may be used to represent a _defined but not-present field_. + - Repeated field values may be included in the formatted output, even if they are equal to the default (an empty list). +- Because JSON elements are unordered, there is no way to unambiguously interpret the "last one wins" rule. + - In most cases, this is fine: JSON elements must have unique names: repeated field values are not valid JSON, so they do not need to be resolved as they are for TextFormat. + - However, this means that it may not be possible to interpret `oneof` fields unambiguously: if multiple cases are present, they are unordered. + +In theory, JSON _can_ represent presence in a semantic-preserving fashion. In practice, however, presence correctness can vary depending upon implementation choices, especially if JSON was chosen as a means to interoperate with clients not using protobufs. + +### Presence in proto2 APIs + +This table outlines whether presence is tracked for fields in proto2 APIs (both for generated APIs and using dynamic reflection): + +Field type | Explicit Presence +-------------------------------------------- | ----------------- +Singular numeric (integer or floating point) | ✔️ +Singular enum | ✔️ +Singular string or bytes | ✔️ +Singular message | ✔️ +Repeated | +Oneofs | ✔️ +Maps | + +Singular fields (of all types) track presence explicitly in the generated API. The generated message interface includes methods to query presence of fields. For example, the field `foo` has a corresponding `has_foo` method. (The specific name follows the same language-specific naming convention as the field accessors.) These methods are sometimes referred to as "hazzers" within the protobuf implementation. + +Similar to singular fields, `oneof` fields explicitly track which one of the members, if any, contains a value. For example, consider this example `oneof`: + +``` +oneof foo { + int32 a = 1; + float b = 2; +} +``` + +Depending on the target language, the generated API would generally include several methods: + +- A hazzer for the oneof: `has_foo` +- A _oneof case_ method: `foo` +- Hazzers for the members: `has_a`, `has_b` +- Getters for the members: `a`, `b` + +Repeated fields and maps do not track presence: there is no distinction between an _empty_ and a _not-present_ repeated field. + +### Presence in proto3 APIs + +This table outlines whether presence is tracked for fields in proto3 APIs (both for generated APIs and using dynamic reflection): + +Field type | `optional` | Explicit Presence +-------------------------------------------- | ---------- | ----------------- +Singular numeric (integer or floating point) | No | +Singular enum | No | +Singular string or bytes | No | +Singular numeric (integer or floating point) | Yes | ✔️ +Singular enum | Yes | ✔️ +Singular string or bytes | Yes | ✔️ +Singular message | Yes | ✔️ +Singular message | No | ✔️ +Repeated | N/A | +Oneofs | N/A | ✔️ +Maps | N/A | + +Similar to proto2 APIs, proto3 does not track presence explicitly for repeated fields. Without the `optional` label, proto3 APIs do not track presence for basic types (numeric, string, bytes, and enums), either. (Note that `optional` for proto3 fields is only experimentally available as of release 3.12.) Oneof fields affirmatively expose presence, although the same set of hazzer methods may not generated as in proto2 APIs. + +Under the _no presence_ discipline, the default value is synonymous with "not present" for purposes of serialization. To notionally "clear" a field (so it won't be serialized), an API user would set it to the default value. + +The default value for enum-typed fields under _no presence_ is the corresponding 0-valued enumerator. Under proto3 syntax rules, all enum types are required to have an enumerator value which maps to 0. By convention, this is an `UNKNOWN` or similarly-named enumerator. If the zero value is notionally outside the domain of valid values for the application, this behavior can be thought of as tantamount to _explicit presence_. + +## Semantic differences + +The _no presence_ serialization discipline results in visible differences from the _explicit presence_ tracking discipline, when the default value is set. For a singular field with numeric, enum, or string type: + +- _No presence_ discipline: + - Default values are not serialized. + - Default values are _not_ merged-from. + - To "clear" a field, it is set to its default value. + - The default value may mean: + - the field was explicitly set to its default value, which is valid in the application-specific domain of values; + - the field was notionally "cleared" by setting its default; or + - the field was never set. +- _Explicit presence_ discipline: + - Explicitly set values are always serialized, including default values. + - Un-set fields are never merged-from. + - Explicitly set fields -- including default values -- _are_ merged-from. + - A generated `has_foo` method indicates whether or not the field `foo` has been set (and not cleared). + - A generated `clear_foo` method must be used to clear (i.e., un-set) the value. + +### Considerations for merging + +Under the _no presence_ rules, it is effectively impossible for a target field to merge-from its default value (using the protobuf's API merging functions). This is because default values are skipped, simliar to the _no presence_ serialization discipline. Merging only updates the target (merged-to) message using the non-skipped values from the update (merged-from) message. + +The difference in merging behavior has further implications for protocols which rely on partial "patch" updates. If field presence is not tracked, then an update patch alone cannot represent an update to the default value, because only non-default values are merged-from. + +Updating to set a default value in this case requires some external mechanism, such as `FieldMask`. However, if presence _is_ tracked, then all explicitly-set values -- even default values -- will be merged into the target. + +### Considerations for change-compatibility + +Changing a field between _explicit presence_ and _no presence_ is a binary-compatible change for serialized values in wire format. However, the serialized representation of the message may differ, depending on which version of the message definition was used for serialization. Specifically, when a "sender" explicitly sets a field to its default value: + +- The serialized value following _no presence_ discipline does not contain the default value, even though it was explicitly set. +- The serialized value following _explicit presence_ discipline contains every "present" field, even if it contains the default value. + +This change may or may not be safe, depending on the application's semantics. For example, consider two clients with different versions of a message definition. + +Client A uses this definition of the message, which follows the _explicit presence_ serialization discipline for field `foo`: + +``` +syntax = "proto3"; +message Msg { + optional int32 foo = 1; +} +``` + +Client B uses a definition of the same message, except that it follows the _no presence_ discipline: + +``` +syntax = "proto3"; +message Msg { + int32 foo = 1; +} +``` + +Now, consider a scenario where client A observes `foo`'s presence as the clients repeatedly exchange the "same" message by deserializing and reserializing: + +``` +// Client A: +Msg m_a; +m_a.set_foo(1); // non-default value +assert(m_a.has_foo()); // OK +Send(m_a.SerializeAsString()); // to client B + +// Client B: +Msg m_b; +m_b.ParseFromString(Receive()); // from client A +assert(m_b.foo() == 1); // OK +Send(m_b.SerializeAsString()); // to client A + +// Client A: +m_a.ParseFromString(Receive()); // from client B +assert(m_a.foo() == 1); // OK +assert(m_a.has_foo()); // OK +m_a.set_foo(0); // default value +Send(m_a.SerializeAsString()); // to client B + +// Client B: +Msg m_b; +m_b.ParseFromString(Receive()); // from client A +assert(m_b.foo() == 0); // OK +Send(m_b.SerializeAsString()); // to client A + +// Client A: +m_a.ParseFromString(Receive()); // from client B +assert(m_a.foo() == 0); // OK +assert(m_a.has_foo()); // FAIL +``` + +If client A depends on _explicit presence_ for `foo`, then a "round trip" through client B will be lossy from the perspective of client A. In the example, this is not a safe change: client A requires (by `assert`) that the field is present; even without any modifications through the API, that requirement fails in a value- and peer-dependent case. + +## How to enable _explicit presence_ in proto3 + +These are the general steps to use the experimental field tracking support for proto3: + +1. Add an `optional` field to a `.proto` file. +1. Run `protoc` (from release 3.12 or later) with an extra flag to recognize `optional` (i.e,. explicit presence) in proto3 files. +1. Use the generated "hazzer" methods and "clear" methods in application code, instead of comparing or setting default values. + +### `.proto` file changes + +This is an example of a proto3 message with fields which follow both _no presence_ and _explicit presence_ semantics: + +``` +syntax = "proto3"; +package example; + +message MyMessage { + // No presence: + int32 not_tracked = 1; + + // Explicit presence: + optional int32 tracked = 2; +} +``` + +### `protoc` invocation + +To enable presence tracking for proto3 messages, pass the `--experimental_allow_proto3_optional` flag to protoc. Without this flag, the `optional` label is an error in files using proto3 syntax. This flag is available in protobuf release 3.12 or later (or at HEAD, if you are reading this application note from Git). + +### Using the generated code + +The generated code for proto3 fields with _explicit presence_ (the `optional` label) will be the same as it would be in a proto2 file. + +This is the definition used in the "no presence" examples below: + +``` +syntax = "proto3"; +package example; +message Msg { + int32 foo = 1; +} +``` + +This is the definition used in the "explicit presence" examples below: + +``` +syntax = "proto3"; +package example; +message Msg { + optional int32 foo = 1; +} +``` + +In the examples, a function `GetProto` constructs and returns a message of type `Msg` with unspecified contents. + +#### C++ example + +No presence: + +``` +Msg m = GetProto(); +if (m.foo() != 0) { + // "Clear" the field: + m.set_foo(0); +} else { + // Default value: field may not have been present. + m.set_foo(1); +} +``` + +Explicit presence: + +``` +Msg m = GetProto(); +if (m.has_foo()) { + // Clear the field: + m.clear_foo(); +} else { + // Field is not present, so set it. + m.set_foo(1); +} +``` + +#### C# example + +No presence: + +``` +var m = GetProto(); +if (m.Foo != 0) { + // "Clear" the field: + m.Foo = 0; +} else { + // Default value: field may not have been present. + m.Foo = 1; +} +``` + +Explicit presence: + +``` +var m = GetProto(); +if (m.HasFoo) { + // Clear the field: + m.ClearFoo(); +} else { + // Field is not present, so set it. + m.Foo = 1; +} +``` + +#### Go example + +No presence: + +``` +m := GetProto() +if (m.GetFoo() != 0) { + // "Clear" the field: + m.Foo = 0; +} else { + // Default value: field may not have been present. + m.Foo = 1; +} +``` + +Explicit presence: + +``` +m := GetProto() +if (m.HasFoo()) { + // Clear the field: + m.Foo = nil +} else { + // Field is not present, so set it. + m.Foo = proto.Int32(1); +} +``` + +#### Java example + +These examples use a `Builder` to demonstrate clearing. Simply checking presence and getting values from a `Builder` follows the same API as the message type. + +No presence: + +``` +Msg.Builder m = GetProto().toBuilder(); +if (m.getFoo() != 0) { + // "Clear" the field: + m.setFoo(0); +} else { + // Default value: field may not have been present. + m.setFoo(1); +} +``` + +Explicit presence: + +``` +Msg.Builder m = GetProto().toBuilder(); +if (m.hasFoo()) { + // Clear the field: + m.clearFoo() +} else { + // Field is not present, so set it. + m.setFoo(1); +} +``` + +#### Python example + +No presence: + +``` +m = example.Msg() +if m.foo != 0: + // "Clear" the field: + m.foo = 0 +else: + // Default value: field may not have been present. + m.foo = 1 +``` + +Explicit presence: + +``` +m = example.Msg() +if m.HasField('foo'): + // Clear the field: + m.ClearField('foo') +else: + // Field is not present, so set it. + m.foo = 1 +``` + +#### Ruby example + +No presence: + +``` +m = Msg.new +if m.foo != 0 + // "Clear" the field: + m.foo = 0 +else + // Default value: field may not have been present. + m.foo = 1 +end +``` + +Explicit presence: + +``` +m = Msg.new +if m.has_foo? + // Clear the field: + m.clear_foo +else + // Field is not present, so set it. + m.foo = 1 +end +``` + +#### Javascript example + +No presence: + +``` +var m = new Msg(); +if (m.getFoo() != 0) { + // "Clear" the field: + m.setFoo(0); +} else { + // Default value: field may not have been present. + m.setFoo(1); +} +``` + +Explicit presence: + +``` +var m = new Msg(); +if (m.hasFoo()) { + // Clear the field: + m.clearFoo() +} else { + // Field is not present, so set it. + m.setFoo(1); +} +``` + +#### Objective C example + +No presence: + +``` +Msg *m = [[Msg alloc] init]; +if (m.foo != 0) { + // "Clear" the field: + m.foo = 0; +} else { + // Default value: field may not have been present. + m.foo = 1; +} +``` + +Explicit presence: + +``` +Msg *m = [[Msg alloc] init]; +if (m.hasFoo()) { + // Clear the field: + [m clearFoo]; +} else { + // Field is not present, so set it. + [m setFoo:1]; +} +``` diff --git a/docs/implementing_proto3_presence.md b/docs/implementing_proto3_presence.md new file mode 100644 index 0000000000..08f9c51321 --- /dev/null +++ b/docs/implementing_proto3_presence.md @@ -0,0 +1,350 @@ +# How To Implement Field Presence for Proto3 + +Protobuf release 3.12 adds experimental support for `optional` fields in +proto3. Proto3 optional fields track presence like in proto2. For background +information about what presence tracking means, please see +[docs/field_presence](field_presence.md). + +This document is targeted at developers who own or maintain protobuf code +generators. All code generators will need to be updated to support proto3 +optional fields. First-party code generators developed by Google are being +updated already. However third-party code generators will need to be updated +independently by their authors. This includes: + +- implementations of Protocol Buffers for other languges. +- alternate implementations of Protocol Buffers that target specialized use + cases. +- code generators that implement some utility code on top of protobuf generated + classes. + +While this document speaks in terms of "code generators", these same principles +apply to implementations that dynamically generate a protocol buffer API "on the +fly", directly from a descriptor, in languages that support this kind of usage. + +## Updating a Code Generator + +When a user adds an `optional` field to proto3, this is internally rewritten as +a one-field oneof, for backward-compatibility with reflection-based algorithms: + +```protobuf +syntax = "proto3"; + +message Foo { + // Experimental feature, not generally supported yet! + optional int32 foo = 1; + + // Internally rewritten to: + // oneof _foo { + // int32 foo = 1 [proto3_optional=true]; + // } + // + // We call _foo a "synthetic" oneof, since it was not created by the user. +} +``` + +As a result, the main two goals when updating a code generator are: + +1. Give `optional` fields like `foo` normal field presence, as described in + [docs/field_presence](field_presence.md) If your implementation already + supports proto2, a proto3 `optional` field should use exactly the same API + and internal implementation as proto2 `optional`. +2. Avoid generating any oneof-based accessors for the synthetic oneof. Its only + purpose is to make reflection-based algorithms work properly if they are + not aware of proto3 presence. The synthetic oneof should not appear anywhere + in the generated API. + +### Satisfying the Experimental Check + +If you try to run `protoc` on a file with proto3 `optional` fields, you will get +an error because the feature is still experimental: + +``` +$ cat test.proto +syntax = "proto3"; + +message Foo { + // Experimental feature, not generally supported yet! + optional int32 a = 1; +} +$ protoc --cpp_out=. test.proto +test.proto: This file contains proto3 optional fields, but --experimental_allow_proto3_optional was not set. +``` + +There are two options for getting around this error: + +1. Pass `--experimental_allow_proto3_optional` to protoc. +2. Make your filename (or a directory name) contain the string + `test_proto3_optional`. This indicates that the proto file is specifically + for testing proto3 optional support, so the check is suppressed. + +These options are demonstrated below: + +``` +# One option: +$ ./src/protoc test.proto --cpp_out=. --experimental_allow_proto3_optional + +# Another option: +$ cp test.proto test_proto3_optional.proto +$ ./src/protoc test_proto3_optional.proto --cpp_out=. +$ +``` + +The experimental check will be removed in a future release, once we are ready +to make this feature generally available. Ideally this will happen for the 3.13 +release of protobuf, sometime in mid-2020, but there is not a specific date set +for this yet. Some of the timing will depend on feedback we get from the +community, so if you have questions or concerns please get in touch via a +GitHub issue. + +### Signaling That Your Code Generator Supports Proto3 Optional + +If you now try to invoke your own code generator with the test proto, you will +run into a different error: + +``` +$ ./src/protoc test_proto3_optional.proto --my_codegen_out=. +test_proto3_optional.proto: is a proto3 file that contains optional fields, but +code generator --my_codegen_out hasn't been updated to support optional fields in +proto3. Please ask the owner of this code generator to support proto3 optional. +``` + +This check exists to make sure that code generators get a chance to update +before they are used with proto3 `optional` fields. Without this check an old +code generator might emit obsolete generated APIs (like accessors for a +synthetic oneof) and users could start depending on these. That would create +a legacy migration burden once a code generator actually implements the feature. + +To signal that your code generator supports `optional` fields in proto3, you +need to tell `protoc` what features you support. The method for doing this +depends on whether you are using the C++ +`google::protobuf::compiler::CodeGenerator` +framework or not. + +If you are using the CodeGenerator framework: + +```c++ +class MyCodeGenerator : public google::protobuf::compiler::CodeGenerator { + // Add this method. + uint64_t GetSupportedFeatures() const override { + // Indicate that this code generator supports proto3 optional fields. + // (Note: don't release your code generator with this flag set until you + // have actually added and tested your proto3 support!) + return FEATURE_PROTO3_OPTIONAL; + } +} +``` + +If you are generating code using raw `CodeGeneratorRequest` and +`CodeGeneratorResponse` messages from `plugin.proto`, the change will be very +similar: + +```c++ +void GenerateResponse() { + CodeGeneratorResponse response; + response.set_supported_features(CodeGeneratorResponse::FEATURE_PROTO3_OPTIONAL); + + // Generate code... +} +``` + +Once you have added this, you should now be able to successfully use your code +generator to generate a file containing proto3 optional fields: + +``` +$ ./src/protoc test_proto3_optional.proto --my_codegen_out=. +``` + +### Updating Your Code Generator + +Now to actually add support for proto3 optional to your code generator. The goal +is to recognize proto3 optional fields as optional, and suppress any output from +synthetic oneofs. + +If your code generator does not currently support proto2, you will need to +design an API and implementation for supporting presence in scalar fields. +Generally this means: + +- allocating a bit inside the generated class to represent whether a given field + is present or not. +- exposing a `has_foo()` method for each field to return the value of this bit. +- make the parser set this bit when a value is parsed from the wire. +- make the serializer test this bit to decide whether to serialize. + +If your code generator already supports proto2, then most of your work is +already done. All you need to do is make sure that proto3 optional fields have +exactly the same API and behave in exactly the same way as proto2 optional +fields. + +From experience updating several of Google's code generators, most of the +updates that are required fall into one of several patterns. Here we will show +the patterns in terms of the C++ CodeGenerator framework. If you are using +`CodeGeneratorRequest` and `CodeGeneratorReply` directly, you can translate the +C++ examples to your own language, referencing the C++ implementation of these +methods where required. + +#### To test whether a field should have presence + +Old: + +```c++ +bool MessageHasPresence(const google::protobuf::Descriptor* message) { + return message->file()->syntax() == + google::protobuf::FileDescriptor::SYNTAX_PROTO2; +} +``` + +New: + +```c++ +// Presence is no longer a property of a message, it's a property of individual +// fields. +bool FieldHasPresence(const google::protobuf::FieldDescriptor* field) { + return field->has_presence(); + // Note, the above will return true for fields in a oneof. + // If you want to filter out oneof fields, write this instead: + // return field->has_presence && !field->real_containing_oneof() +} +``` + +#### To test whether a field is a member of a oneof + +Old: + +```c++ +bool FieldIsInOneof(const google::protobuf::FielDescriptor* field) { + return field->containing_oneof() != nullptr; +} +``` + +New: + +```c++ +bool FieldIsInOneof(const google::protobuf::FielDescriptor* field) { + // real_containing_oneof() returns nullptr for synthetic oneofs. + return field->real_containing_oneof() != nullptr; +} +``` + +#### To iterate over all oneofs + +Old: + +```c++ +bool IterateOverOneofs(const google::protobuf::Descriptor* message) { + for (int i = 0; i < message->oneof_decl_count(); i++) { + const google::protobuf::OneofDescriptor* oneof = message->oneof(i); + // ... + } +} +``` + +New: + +```c++ +bool IterateOverOneofs(const google::protobuf::Descriptor* message) { + // Real oneofs are always first, and real_oneof_decl_count() will return the + // total number of oneofs, excluding synthetic oneofs. + for (int i = 0; i < message->real_oneof_decl_count(); i++) { + const google::protobuf::OneofDescriptor* oneof = message->oneof(i); + // ... + } +} +``` + +## Updating Reflection + +If your implementation offers reflection, there are a few other changes to make: + +### API Changes + +The API for reflecting over fields and oneofs should make the following changes. +These match the changes implemented in C++ reflection. + +1. Add a `FieldDescriptor::has_presence()` method returning `bool` + (adjusted to your language's naming convention). This should return true + for all fields that have explicit presence, as documented in + [docs/field_presence](field_presence.md). In particular, this includes + fields in a oneof, proto2 scalar fields, and proto3 `optional` fields. + This accessor will allow users to query what fields have presence without + thinking about the difference between proto2 and proto3. +2. As a corollary of (1), please do *not* expose an accessor for the + `FieldDescriptorProto.proto3_optional` field. We want to avoid having + users implement any proto2/proto3-specific logic. Users should use the + `has_presence()` function instead. +3. You may also wish to add a `FieldDescriptor::has_optional_keyword()` method + returning `bool`, which indicates whether the `optional` keyword is present. + Message fields will always return `true` for `has_presence()`, so this method + can allow a user to know whether the user wrote `optional` or not. It can + occasionally be useful to have this information, even though it does not + change the presence semantics of the field. +4. If your reflection API may be used for a code generator, you may wish to + implement methods to help users tell the difference between real and + synthetic oneofs. In particular: + - `OneofDescriptor::is_synthetic()`: returns true if this is a synthetic + oneof. + - `FieldDescriptor::real_containing_oneof()`: like `containing_oneof()`, + but returns `nullptr` if the oneof is synthetic. + - `Descriptor::real_oneof_decl_count()`: like `oneof_decl_count()`, but + returns the number of real oneofs only. + +### Implementation Changes + +Proto3 `optional` fields and synthetic oneofs must work correctly when +reflected on. Specifically: + +1. Reflection for synthetic oneofs should work properly. Even though synthetic + oneofs do not really exist in the message, you can still make reflection work + as if they did. In particular, you can make a method like + `Reflection::HasOneof()` or `Reflection::GetOneofFieldDescriptor()` look at + the hasbit to determine if the oneof is present or not. +2. Reflection for proto3 optional fields should work properly. For example, a + method like `Reflection::HasField()` should know to look for the hasbit for a + proto3 `optional` field. It should not be fooled by the synthetic oneof into + thinking that there is a `case` member for the oneof. + +Once you have updated reflection to work properly with proto3 `optional` and +synthetic oneofs, any code that *uses* your reflection interface should work +properly with no changes. This is the benefit of using synthetic oneofs. + +In particular, if you have a reflection-based implementation of protobuf text +format or JSON, it should properly support proto3 optional fields without any +changes to the code. The fields will look like they all belong to a one-field +oneof, and existing proto3 reflection code should know how to test presence for +fields in a oneof. + +So the best way to test your reflection changes is to try round-tripping a +message through text format, JSON, or some other reflection-based parser and +serializer, if you have one. + +### Validating Descriptors + +If your reflection implementation supports loading descriptors at runtime, +you must verify that all synthetic oneofs are ordered after all "real" oneofs. + +Here is the code that implements this validation step in C++, for inspiration: + +```c++ + // Validation that runs for each message. + // Synthetic oneofs must be last. + int first_synthetic = -1; + for (int i = 0; i < message->oneof_decl_count(); i++) { + const OneofDescriptor* oneof = message->oneof_decl(i); + if (oneof->is_synthetic()) { + if (first_synthetic == -1) { + first_synthetic = i; + } + } else { + if (first_synthetic != -1) { + AddError(message->full_name(), proto.oneof_decl(i), + DescriptorPool::ErrorCollector::OTHER, + "Synthetic oneofs must be after all other oneofs"); + } + } + } + + if (first_synthetic == -1) { + message->real_oneof_decl_count_ = message->oneof_decl_count_; + } else { + message->real_oneof_decl_count_ = first_synthetic; + } +``` diff --git a/docs/third_party.md b/docs/third_party.md index 528dc6e220..11a6efb5b1 100644 --- a/docs/third_party.md +++ b/docs/third_party.md @@ -82,6 +82,7 @@ These are projects we know about implementing Protocol Buffers for other program * Prolog: http://www.swi-prolog.org/pldoc/package/protobufs.html * Python: https://github.com/google/protobuf (Google-official implementation) * Python: https://github.com/eigenein/protobuf +* Python: https://github.com/danielgtaylor/python-betterproto * R: http://cran.r-project.org/package=RProtoBuf * Ruby: http://code.google.com/p/ruby-protobuf/ * Ruby: http://github.com/mozy/ruby-protocol-buffers diff --git a/java/pom.xml b/java/pom.xml index 2889f90117..d1928ec4f5 100644 --- a/java/pom.xml +++ b/java/pom.xml @@ -93,12 +93,12 @@ com.google.guava guava - 28.2-android + 29.0-android com.google.guava guava-testlib - 28.2-android + 29.0-android test diff --git a/kokoro/linux/dockerfile/push_testing_images.sh b/kokoro/linux/dockerfile/push_testing_images.sh index 9a2983c829..2d82babba4 100755 --- a/kokoro/linux/dockerfile/push_testing_images.sh +++ b/kokoro/linux/dockerfile/push_testing_images.sh @@ -8,7 +8,7 @@ cd kokoro/linux/dockerfile DOCKERHUB_ORGANIZATION=protobuftesting -for DOCKERFILE_DIR in test/* release/* +for DOCKERFILE_DIR in test/* do # Generate image name based on Dockerfile checksum. That works well as long # as can count on dockerfiles being written in a way that changing the logical diff --git a/kokoro/linux/dockerfile/release/ruby_rake_compiler/Dockerfile b/kokoro/linux/dockerfile/release/ruby_rake_compiler/Dockerfile deleted file mode 100644 index 866b289343..0000000000 --- a/kokoro/linux/dockerfile/release/ruby_rake_compiler/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM grpctesting/rake-compiler-dock_53c22085d091183c528303791e7771359f699bcf - -RUN /bin/bash -l -c "gem update --system '2.7.9' && gem install bundler" diff --git a/kokoro/linux/dockerfile/test/python27/Dockerfile b/kokoro/linux/dockerfile/test/python27/Dockerfile new file mode 100644 index 0000000000..e41e49a6e5 --- /dev/null +++ b/kokoro/linux/dockerfile/test/python27/Dockerfile @@ -0,0 +1,23 @@ +FROM python:2.7-buster + +# Install dependencies. We start with the basic ones require to build protoc +# and the C++ build +RUN apt-get update && apt-get install -y \ + autoconf \ + autotools-dev \ + build-essential \ + bzip2 \ + ccache \ + curl \ + gcc \ + git \ + libc6 \ + libc6-dbg \ + libc6-dev \ + libgtest-dev \ + libtool \ + make \ + parallel \ + time \ + wget \ + && apt-get clean diff --git a/kokoro/linux/dockerfile/test/python35/Dockerfile b/kokoro/linux/dockerfile/test/python35/Dockerfile new file mode 100644 index 0000000000..3ea4c9e188 --- /dev/null +++ b/kokoro/linux/dockerfile/test/python35/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.5-buster + +# Install dependencies. We start with the basic ones require to build protoc +# and the C++ build +RUN apt-get update && apt-get install -y \ + autoconf \ + autotools-dev \ + build-essential \ + bzip2 \ + ccache \ + curl \ + gcc \ + git \ + libc6 \ + libc6-dbg \ + libc6-dev \ + libgtest-dev \ + libtool \ + make \ + parallel \ + time \ + wget \ + && apt-get clean diff --git a/kokoro/linux/dockerfile/test/python36/Dockerfile b/kokoro/linux/dockerfile/test/python36/Dockerfile new file mode 100644 index 0000000000..436846065b --- /dev/null +++ b/kokoro/linux/dockerfile/test/python36/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.6-buster + +# Install dependencies. We start with the basic ones require to build protoc +# and the C++ build +RUN apt-get update && apt-get install -y \ + autoconf \ + autotools-dev \ + build-essential \ + bzip2 \ + ccache \ + curl \ + gcc \ + git \ + libc6 \ + libc6-dbg \ + libc6-dev \ + libgtest-dev \ + libtool \ + make \ + parallel \ + time \ + wget \ + && apt-get clean diff --git a/kokoro/linux/dockerfile/test/python37/Dockerfile b/kokoro/linux/dockerfile/test/python37/Dockerfile new file mode 100644 index 0000000000..c711eb86a5 --- /dev/null +++ b/kokoro/linux/dockerfile/test/python37/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.7-buster + +# Install dependencies. We start with the basic ones require to build protoc +# and the C++ build +RUN apt-get update && apt-get install -y \ + autoconf \ + autotools-dev \ + build-essential \ + bzip2 \ + ccache \ + curl \ + gcc \ + git \ + libc6 \ + libc6-dbg \ + libc6-dev \ + libgtest-dev \ + libtool \ + make \ + parallel \ + time \ + wget \ + && apt-get clean diff --git a/kokoro/linux/dockerfile/test/python38/Dockerfile b/kokoro/linux/dockerfile/test/python38/Dockerfile new file mode 100644 index 0000000000..48a7be5e05 --- /dev/null +++ b/kokoro/linux/dockerfile/test/python38/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.8-buster + +# Install dependencies. We start with the basic ones require to build protoc +# and the C++ build +RUN apt-get update && apt-get install -y \ + autoconf \ + autotools-dev \ + build-essential \ + bzip2 \ + ccache \ + curl \ + gcc \ + git \ + libc6 \ + libc6-dbg \ + libc6-dev \ + libgtest-dev \ + libtool \ + make \ + parallel \ + time \ + wget \ + && apt-get clean diff --git a/kokoro/linux/dockerfile/test/python_jessie/Dockerfile b/kokoro/linux/dockerfile/test/python_jessie/Dockerfile deleted file mode 100644 index b5a53a3e5f..0000000000 --- a/kokoro/linux/dockerfile/test/python_jessie/Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM debian:jessie - -# Install dependencies. We start with the basic ones require to build protoc -# and the C++ build -RUN apt-get update && apt-get install -y \ - autoconf \ - autotools-dev \ - build-essential \ - bzip2 \ - ccache \ - curl \ - gcc \ - git \ - libc6 \ - libc6-dbg \ - libc6-dev \ - libgtest-dev \ - libtool \ - make \ - parallel \ - time \ - wget \ - && apt-get clean - -# Install python dependencies -RUN apt-get update && apt-get install -y \ - python-setuptools \ - python-all-dev \ - python3-all-dev \ - python-pip - -# Install Python packages from PyPI -RUN pip install --upgrade pip==10.0.1 -RUN pip install virtualenv -RUN pip install six==1.10.0 twisted==17.5.0 - -# Install pip and virtualenv for Python 3.4 -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.4 -RUN python3.4 -m pip install virtualenv diff --git a/kokoro/linux/dockerfile/test/python_stretch/Dockerfile b/kokoro/linux/dockerfile/test/python_stretch/Dockerfile deleted file mode 100644 index 10790b9c3e..0000000000 --- a/kokoro/linux/dockerfile/test/python_stretch/Dockerfile +++ /dev/null @@ -1,49 +0,0 @@ -FROM debian:stretch - -# Install dependencies. We start with the basic ones require to build protoc -# and the C++ build -RUN apt-get update && apt-get install -y \ - autoconf \ - autotools-dev \ - build-essential \ - bzip2 \ - ccache \ - curl \ - gcc \ - git \ - libc6 \ - libc6-dbg \ - libc6-dev \ - libgtest-dev \ - libtool \ - make \ - parallel \ - time \ - wget \ - && apt-get clean - -# Install Python 2.7 -RUN apt-get update && apt-get install -y python2.7 python-all-dev -RUN curl https://bootstrap.pypa.io/get-pip.py | python2.7 - -# Install python dependencies -RUN apt-get update && apt-get install -y \ - python-setuptools \ - python-pip - -# Add Debian 'testing' repository -RUN echo 'deb http://ftp.de.debian.org/debian testing main' >> /etc/apt/sources.list -RUN echo 'APT::Default-Release "stable";' | tee -a /etc/apt/apt.conf.d/00local - -# Install Python3 -RUN apt-get update && apt-get -t testing install -y \ - python3.5 \ - python3.6 \ - python3.7 \ - python3.8 \ - python3-all-dev - -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.5 -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.6 -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.7 -RUN curl https://bootstrap.pypa.io/get-pip.py | python3.8 diff --git a/kokoro/linux/dockerfile/test/ruby/Dockerfile b/kokoro/linux/dockerfile/test/ruby/Dockerfile index 41bfedeb33..9037da715f 100644 --- a/kokoro/linux/dockerfile/test/ruby/Dockerfile +++ b/kokoro/linux/dockerfile/test/ruby/Dockerfile @@ -32,6 +32,7 @@ RUN /bin/bash -l -c "rvm install 2.3.8" RUN /bin/bash -l -c "rvm install 2.4.5" RUN /bin/bash -l -c "rvm install 2.5.1" RUN /bin/bash -l -c "rvm install 2.6.0" +RUN /bin/bash -l -c "rvm install 2.7.0" RUN /bin/bash -l -c "echo 'gem: --no-ri --no-rdoc' > ~/.gemrc" RUN /bin/bash -l -c "echo 'export PATH=/usr/local/rvm/bin:$PATH' >> ~/.bashrc" diff --git a/kokoro/linux/python27/build.sh b/kokoro/linux/python27/build.sh index 8c40ba3604..41531a15ef 100755 --- a/kokoro/linux/python27/build.sh +++ b/kokoro/linux/python27/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_jessie +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python27 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python27" diff --git a/kokoro/linux/python27_cpp/build.sh b/kokoro/linux/python27_cpp/build.sh index 2ff09a21fd..1a972ee9f2 100755 --- a/kokoro/linux/python27_cpp/build.sh +++ b/kokoro/linux/python27_cpp/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_jessie +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python27 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python27_cpp" diff --git a/kokoro/linux/python33/build.sh b/kokoro/linux/python33/build.sh deleted file mode 100755 index 84a9acdb73..0000000000 --- a/kokoro/linux/python33/build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# This is the top-level script we give to Kokoro as the entry point for -# running the "pull request" project: -# -# This script selects a specific Dockerfile (for building a Docker image) and -# a script to run inside that image. Then we delegate to the general -# build_and_run_docker.sh script. - -# Change to repo root -cd $(dirname $0)/../../.. - -export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_jessie -export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh -export OUTPUT_DIR=testoutput -export TEST_SET="python33" -./kokoro/linux/build_and_run_docker.sh diff --git a/kokoro/linux/python33/continuous.cfg b/kokoro/linux/python33/continuous.cfg deleted file mode 100644 index e2fc4136f0..0000000000 --- a/kokoro/linux/python33/continuous.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Config file for running tests in Kokoro - -# Location of the build script in repository -build_file: "protobuf/kokoro/linux/python/build.sh" -timeout_mins: 120 - -action { - define_artifacts { - regex: "**/sponge_log.xml" - } -} diff --git a/kokoro/linux/python33/presubmit.cfg b/kokoro/linux/python33/presubmit.cfg deleted file mode 100644 index e2fc4136f0..0000000000 --- a/kokoro/linux/python33/presubmit.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Config file for running tests in Kokoro - -# Location of the build script in repository -build_file: "protobuf/kokoro/linux/python/build.sh" -timeout_mins: 120 - -action { - define_artifacts { - regex: "**/sponge_log.xml" - } -} diff --git a/kokoro/linux/python33_cpp/build.sh b/kokoro/linux/python33_cpp/build.sh deleted file mode 100755 index ad33e89d25..0000000000 --- a/kokoro/linux/python33_cpp/build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# This is the top-level script we give to Kokoro as the entry point for -# running the "pull request" project: -# -# This script selects a specific Dockerfile (for building a Docker image) and -# a script to run inside that image. Then we delegate to the general -# build_and_run_docker.sh script. - -# Change to repo root -cd $(dirname $0)/../../.. - -export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_jessie -export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh -export OUTPUT_DIR=testoutput -export TEST_SET="python33_cpp" -./kokoro/linux/build_and_run_docker.sh diff --git a/kokoro/linux/python33_cpp/continuous.cfg b/kokoro/linux/python33_cpp/continuous.cfg deleted file mode 100644 index b1b0e550ff..0000000000 --- a/kokoro/linux/python33_cpp/continuous.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Config file for running tests in Kokoro - -# Location of the build script in repository -build_file: "protobuf/kokoro/linux/python_cpp/build.sh" -timeout_mins: 120 - -action { - define_artifacts { - regex: "**/sponge_log.xml" - } -} diff --git a/kokoro/linux/python33_cpp/presubmit.cfg b/kokoro/linux/python33_cpp/presubmit.cfg deleted file mode 100644 index b1b0e550ff..0000000000 --- a/kokoro/linux/python33_cpp/presubmit.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Config file for running tests in Kokoro - -# Location of the build script in repository -build_file: "protobuf/kokoro/linux/python_cpp/build.sh" -timeout_mins: 120 - -action { - define_artifacts { - regex: "**/sponge_log.xml" - } -} diff --git a/kokoro/linux/python34_cpp/build.sh b/kokoro/linux/python34_cpp/build.sh deleted file mode 100755 index e4590ffbad..0000000000 --- a/kokoro/linux/python34_cpp/build.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash -# -# This is the top-level script we give to Kokoro as the entry point for -# running the "pull request" project: -# -# This script selects a specific Dockerfile (for building a Docker image) and -# a script to run inside that image. Then we delegate to the general -# build_and_run_docker.sh script. - -# Change to repo root -cd $(dirname $0)/../../.. - -export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_jessie -export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh -export OUTPUT_DIR=testoutput -export TEST_SET="python34_cpp" -./kokoro/linux/build_and_run_docker.sh diff --git a/kokoro/linux/python34_cpp/continuous.cfg b/kokoro/linux/python34_cpp/continuous.cfg deleted file mode 100644 index b1b0e550ff..0000000000 --- a/kokoro/linux/python34_cpp/continuous.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Config file for running tests in Kokoro - -# Location of the build script in repository -build_file: "protobuf/kokoro/linux/python_cpp/build.sh" -timeout_mins: 120 - -action { - define_artifacts { - regex: "**/sponge_log.xml" - } -} diff --git a/kokoro/linux/python34_cpp/presubmit.cfg b/kokoro/linux/python34_cpp/presubmit.cfg deleted file mode 100644 index b1b0e550ff..0000000000 --- a/kokoro/linux/python34_cpp/presubmit.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Config file for running tests in Kokoro - -# Location of the build script in repository -build_file: "protobuf/kokoro/linux/python_cpp/build.sh" -timeout_mins: 120 - -action { - define_artifacts { - regex: "**/sponge_log.xml" - } -} diff --git a/kokoro/linux/python35/build.sh b/kokoro/linux/python35/build.sh index f978e2ad11..66ea03f036 100755 --- a/kokoro/linux/python35/build.sh +++ b/kokoro/linux/python35/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python35 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python35" diff --git a/kokoro/linux/python35_cpp/build.sh b/kokoro/linux/python35_cpp/build.sh index 2a79246d59..4d0dbd4986 100755 --- a/kokoro/linux/python35_cpp/build.sh +++ b/kokoro/linux/python35_cpp/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python35 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python35_cpp" diff --git a/kokoro/linux/python36/build.sh b/kokoro/linux/python36/build.sh index 633145bcec..a483efc302 100755 --- a/kokoro/linux/python36/build.sh +++ b/kokoro/linux/python36/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python36 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python36" diff --git a/kokoro/linux/python36_cpp/build.sh b/kokoro/linux/python36_cpp/build.sh index 8e120ff37c..eb71bda92c 100755 --- a/kokoro/linux/python36_cpp/build.sh +++ b/kokoro/linux/python36_cpp/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python36 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python36_cpp" diff --git a/kokoro/linux/python37/build.sh b/kokoro/linux/python37/build.sh index 554aa09448..2117a271ce 100755 --- a/kokoro/linux/python37/build.sh +++ b/kokoro/linux/python37/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python37 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python37" diff --git a/kokoro/linux/python37_cpp/build.sh b/kokoro/linux/python37_cpp/build.sh index 8fdc8f9339..3126b481e3 100755 --- a/kokoro/linux/python37_cpp/build.sh +++ b/kokoro/linux/python37_cpp/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python37 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python37_cpp" diff --git a/kokoro/linux/python38/build.sh b/kokoro/linux/python38/build.sh index 9609939a48..299c7ba6f3 100755 --- a/kokoro/linux/python38/build.sh +++ b/kokoro/linux/python38/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python38 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python38" diff --git a/kokoro/linux/python38_cpp/build.sh b/kokoro/linux/python38_cpp/build.sh index c5cd6eeb42..b43859b988 100755 --- a/kokoro/linux/python38_cpp/build.sh +++ b/kokoro/linux/python38_cpp/build.sh @@ -11,7 +11,7 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_stretch +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python38 export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput export TEST_SET="python38_cpp" diff --git a/kokoro/linux/python34/build.sh b/kokoro/linux/ruby27/build.sh similarity index 85% rename from kokoro/linux/python34/build.sh rename to kokoro/linux/ruby27/build.sh index 25dfd5dfcd..c38ee36e58 100755 --- a/kokoro/linux/python34/build.sh +++ b/kokoro/linux/ruby27/build.sh @@ -11,8 +11,8 @@ cd $(dirname $0)/../../.. export DOCKERHUB_ORGANIZATION=protobuftesting -export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/python_jessie +export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/ruby export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh export OUTPUT_DIR=testoutput -export TEST_SET="python34" +export TEST_SET="ruby27" ./kokoro/linux/build_and_run_docker.sh diff --git a/kokoro/linux/python34/continuous.cfg b/kokoro/linux/ruby27/continuous.cfg similarity index 76% rename from kokoro/linux/python34/continuous.cfg rename to kokoro/linux/ruby27/continuous.cfg index e2fc4136f0..9cce8c90e1 100644 --- a/kokoro/linux/python34/continuous.cfg +++ b/kokoro/linux/ruby27/continuous.cfg @@ -1,7 +1,7 @@ # Config file for running tests in Kokoro # Location of the build script in repository -build_file: "protobuf/kokoro/linux/python/build.sh" +build_file: "protobuf/kokoro/linux/ruby27/build.sh" timeout_mins: 120 action { diff --git a/kokoro/linux/python34/presubmit.cfg b/kokoro/linux/ruby27/presubmit.cfg similarity index 76% rename from kokoro/linux/python34/presubmit.cfg rename to kokoro/linux/ruby27/presubmit.cfg index e2fc4136f0..9cce8c90e1 100644 --- a/kokoro/linux/python34/presubmit.cfg +++ b/kokoro/linux/ruby27/presubmit.cfg @@ -1,7 +1,7 @@ # Config file for running tests in Kokoro # Location of the build script in repository -build_file: "protobuf/kokoro/linux/python/build.sh" +build_file: "protobuf/kokoro/linux/ruby27/build.sh" timeout_mins: 120 action { diff --git a/kokoro/macos/ruby27/build.sh b/kokoro/macos/ruby27/build.sh new file mode 100755 index 0000000000..16bcbd6cbd --- /dev/null +++ b/kokoro/macos/ruby27/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# +# Build file to set up and run tests + +# Change to repo root +cd $(dirname $0)/../../.. + +# Prepare worker environment to run tests +source kokoro/macos/prepare_build_macos_rc + +./tests.sh ruby27 diff --git a/kokoro/macos/ruby27/continuous.cfg b/kokoro/macos/ruby27/continuous.cfg new file mode 100644 index 0000000000..b10b455da3 --- /dev/null +++ b/kokoro/macos/ruby27/continuous.cfg @@ -0,0 +1,5 @@ +# Config file for running tests in Kokoro + +# Location of the build script in repository +build_file: "protobuf/kokoro/macos/ruby27/build.sh" +timeout_mins: 1440 diff --git a/kokoro/macos/ruby27/presubmit.cfg b/kokoro/macos/ruby27/presubmit.cfg new file mode 100644 index 0000000000..b10b455da3 --- /dev/null +++ b/kokoro/macos/ruby27/presubmit.cfg @@ -0,0 +1,5 @@ +# Config file for running tests in Kokoro + +# Location of the build script in repository +build_file: "protobuf/kokoro/macos/ruby27/build.sh" +timeout_mins: 1440 diff --git a/kokoro/release/ruby/linux/prepare_build.sh b/kokoro/release/ruby/linux/prepare_build.sh index f2257c3c75..91d186898c 100755 --- a/kokoro/release/ruby/linux/prepare_build.sh +++ b/kokoro/release/ruby/linux/prepare_build.sh @@ -7,12 +7,6 @@ echo 'DOCKER_OPTS="${DOCKER_OPTS} --graph=/tmpfs/docker"' | sudo tee --append /e echo 'DOCKER_OPTS="${DOCKER_OPTS} --registry-mirror=https://mirror.gcr.io"' | sudo tee --append /etc/default/docker sudo service docker restart -# Download Docker images from DockerHub -DOCKERHUB_ORGANIZATION=protobuftesting -DOCKERFILE_DIR=kokoro/linux/dockerfile/release/ruby_rake_compiler -DOCKERFILE_PREFIX=$(basename $DOCKERFILE_DIR) -export RAKE_COMPILER_DOCK_IMAGE=${DOCKERHUB_ORGANIZATION}/${DOCKERFILE_PREFIX}_$(sha1sum $DOCKERFILE_DIR/Dockerfile | cut -f1 -d\ ) - # All artifacts come here mkdir artifacts export ARTIFACT_DIR=$(pwd)/artifacts diff --git a/kokoro/release/ruby/linux/ruby/ruby_build.sh b/kokoro/release/ruby/linux/ruby/ruby_build.sh index 9fc42b13eb..95f1dea906 100755 --- a/kokoro/release/ruby/linux/ruby/ruby_build.sh +++ b/kokoro/release/ruby/linux/ruby/ruby_build.sh @@ -11,6 +11,7 @@ fi umask 0022 pushd ruby +gem install bundler -v 2.1.4 bundle install && bundle exec rake gem:native ls pkg mv pkg/* $ARTIFACT_DIR diff --git a/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh b/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh index 5776e3c349..a4bf9e051e 100755 --- a/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh +++ b/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh @@ -6,7 +6,11 @@ set +ex # rvm script is very verbose and exits with errorcode source $HOME/.rvm/scripts/rvm set -e # rvm commands are very verbose time rvm install 2.5.0 -rvm use 2.5.0 --default +rvm use 2.5.0 +gem install rake-compiler --no-document +gem install bundler --no-document +time rvm install 2.7.0 +rvm use 2.7.0 --default gem install rake-compiler --no-document gem install bundler --no-document rvm osx-ssl-certs status all @@ -17,13 +21,13 @@ rm -rf ~/.rake-compiler CROSS_RUBY=$(mktemp tmpfile.XXXXXXXX) -curl https://raw.githubusercontent.com/rake-compiler/rake-compiler/v1.0.3/tasks/bin/cross-ruby.rake > "$CROSS_RUBY" +curl https://raw.githubusercontent.com/rake-compiler/rake-compiler/v1.1.0/tasks/bin/cross-ruby.rake > "$CROSS_RUBY" # See https://github.com/grpc/grpc/issues/12161 for verconf.h patch details patch "$CROSS_RUBY" << EOF --- cross-ruby.rake 2018-04-10 11:32:16.000000000 -0700 +++ patched 2018-04-10 11:40:25.000000000 -0700 -@@ -133,8 +133,10 @@ +@@ -141,8 +141,10 @@ "--host=#{MINGW_HOST}", "--target=#{MINGW_TARGET}", "--build=#{RUBY_BUILD}", @@ -35,9 +39,9 @@ patch "$CROSS_RUBY" << EOF '--with-ext=' ] -@@ -151,6 +153,7 @@ +@@ -159,6 +161,7 @@ # make - file "#{USER_HOME}/builds/#{MINGW_HOST}/#{RUBY_CC_VERSION}/ruby.exe" => ["#{USER_HOME}/builds/#{MINGW_HOST}/#{RUBY_CC_VERSION}/Makefile"] do |t| + file "#{build_dir}/ruby.exe" => ["#{build_dir}/Makefile"] do |t| chdir File.dirname(t.prerequisites.first) do + sh "test -s verconf.h || rm -f verconf.h" # if verconf.h has size 0, make sure it gets re-built by make sh MAKE @@ -47,10 +51,25 @@ EOF MAKE="make -j8" +set +x # rvm commands are very verbose +rvm use 2.7.0 +set -x +ruby --version | grep 'ruby 2.7.0' +for v in 2.7.0 ; do + ccache -c + rake -f "$CROSS_RUBY" cross-ruby VERSION="$v" HOST=x86_64-darwin11 MAKE="$MAKE" +done +set +x +rvm use 2.5.0 +set -x +ruby --version | grep 'ruby 2.5.0' for v in 2.6.0 2.5.1 2.4.0 2.3.0 ; do ccache -c rake -f "$CROSS_RUBY" cross-ruby VERSION="$v" HOST=x86_64-darwin11 MAKE="$MAKE" done +set +x +rvm use 2.7.0 +set -x sed 's/x86_64-darwin-11/universal-darwin/' ~/.rake-compiler/config.yml > "$CROSS_RUBY" mv "$CROSS_RUBY" ~/.rake-compiler/config.yml diff --git a/objectivec/DevTools/compile_testing_protos.sh b/objectivec/DevTools/compile_testing_protos.sh index dc1d6d2e98..d04c5c522b 100755 --- a/objectivec/DevTools/compile_testing_protos.sh +++ b/objectivec/DevTools/compile_testing_protos.sh @@ -158,6 +158,7 @@ compile_protos() { --objc_out="${OUTPUT_DIR}/google/protobuf" \ --proto_path=src/google/protobuf/ \ --proto_path=src \ + --experimental_allow_proto3_optional \ "$@" } diff --git a/objectivec/DevTools/full_mac_build.sh b/objectivec/DevTools/full_mac_build.sh index 5584b0ae44..84ed8e9914 100755 --- a/objectivec/DevTools/full_mac_build.sh +++ b/objectivec/DevTools/full_mac_build.sh @@ -290,12 +290,9 @@ if [[ "${DO_XCODE_IOS_TESTS}" == "yes" ]] ; then ) ;; 11.*) + # Dropped 32bit as Apple doesn't seem support the simulators either. XCODEBUILD_TEST_BASE_IOS+=( - -destination "platform=iOS Simulator,name=iPhone 4s,OS=8.1" # 32bit -destination "platform=iOS Simulator,name=iPhone 8,OS=latest" # 64bit - # 10.x also seems to often fail running destinations in parallel (with - # 32bit one include atleast) - -disable-concurrent-destination-testing ) ;; * ) diff --git a/objectivec/GPBAny.pbobjc.h b/objectivec/GPBAny.pbobjc.h index 233cc270ea..288d552356 100644 --- a/objectivec/GPBAny.pbobjc.h +++ b/objectivec/GPBAny.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBAny.pbobjc.m b/objectivec/GPBAny.pbobjc.m index 06b892e233..a5143f15dc 100644 --- a/objectivec/GPBAny.pbobjc.m +++ b/objectivec/GPBAny.pbobjc.m @@ -72,7 +72,7 @@ typedef struct GPBAny__storage_ { .number = GPBAny_FieldNumber_TypeURL, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBAny__storage_, typeURL), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -81,7 +81,7 @@ typedef struct GPBAny__storage_ { .number = GPBAny_FieldNumber_Value, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBAny__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeBytes, }, }; @@ -92,7 +92,7 @@ typedef struct GPBAny__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBAny__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS static const char *extraTextFormatInfo = "\001\001\004\241!!\000"; diff --git a/objectivec/GPBApi.pbobjc.h b/objectivec/GPBApi.pbobjc.h index c47a01c572..287c0516d0 100644 --- a/objectivec/GPBApi.pbobjc.h +++ b/objectivec/GPBApi.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBApi.pbobjc.m b/objectivec/GPBApi.pbobjc.m index bdd35bb765..5915ce1122 100644 --- a/objectivec/GPBApi.pbobjc.m +++ b/objectivec/GPBApi.pbobjc.m @@ -96,7 +96,7 @@ typedef struct GPBApi__storage_ { .number = GPBApi_FieldNumber_Name, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBApi__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -123,7 +123,7 @@ typedef struct GPBApi__storage_ { .number = GPBApi_FieldNumber_Version, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBApi__storage_, version), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -150,7 +150,7 @@ typedef struct GPBApi__storage_ { .number = GPBApi_FieldNumber_Syntax, .hasIndex = 3, .offset = (uint32_t)offsetof(GPBApi__storage_, syntax), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeEnum, }, }; @@ -161,7 +161,7 @@ typedef struct GPBApi__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBApi__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -175,13 +175,13 @@ typedef struct GPBApi__storage_ { int32_t GPBApi_Syntax_RawValue(GPBApi *message) { GPBDescriptor *descriptor = [GPBApi descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBApi_FieldNumber_Syntax]; - return GPBGetMessageInt32Field(message, field); + return GPBGetMessageRawEnumField(message, field); } void SetGPBApi_Syntax_RawValue(GPBApi *message, int32_t value) { GPBDescriptor *descriptor = [GPBApi descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBApi_FieldNumber_Syntax]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); + GPBSetMessageRawEnumField(message, field, value); } #pragma mark - GPBMethod @@ -217,7 +217,7 @@ typedef struct GPBMethod__storage_ { .number = GPBMethod_FieldNumber_Name, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBMethod__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -226,7 +226,7 @@ typedef struct GPBMethod__storage_ { .number = GPBMethod_FieldNumber_RequestTypeURL, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBMethod__storage_, requestTypeURL), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -235,7 +235,7 @@ typedef struct GPBMethod__storage_ { .number = GPBMethod_FieldNumber_RequestStreaming, .hasIndex = 2, .offset = 3, // Stored in _has_storage_ to save space. - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeBool, }, { @@ -244,7 +244,7 @@ typedef struct GPBMethod__storage_ { .number = GPBMethod_FieldNumber_ResponseTypeURL, .hasIndex = 4, .offset = (uint32_t)offsetof(GPBMethod__storage_, responseTypeURL), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -253,7 +253,7 @@ typedef struct GPBMethod__storage_ { .number = GPBMethod_FieldNumber_ResponseStreaming, .hasIndex = 5, .offset = 6, // Stored in _has_storage_ to save space. - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeBool, }, { @@ -271,7 +271,7 @@ typedef struct GPBMethod__storage_ { .number = GPBMethod_FieldNumber_Syntax, .hasIndex = 7, .offset = (uint32_t)offsetof(GPBMethod__storage_, syntax), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeEnum, }, }; @@ -282,7 +282,7 @@ typedef struct GPBMethod__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBMethod__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS static const char *extraTextFormatInfo = "\002\002\007\244\241!!\000\004\010\244\241!!\000"; @@ -301,13 +301,13 @@ typedef struct GPBMethod__storage_ { int32_t GPBMethod_Syntax_RawValue(GPBMethod *message) { GPBDescriptor *descriptor = [GPBMethod descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBMethod_FieldNumber_Syntax]; - return GPBGetMessageInt32Field(message, field); + return GPBGetMessageRawEnumField(message, field); } void SetGPBMethod_Syntax_RawValue(GPBMethod *message, int32_t value) { GPBDescriptor *descriptor = [GPBMethod descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBMethod_FieldNumber_Syntax]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); + GPBSetMessageRawEnumField(message, field, value); } #pragma mark - GPBMixin @@ -335,7 +335,7 @@ typedef struct GPBMixin__storage_ { .number = GPBMixin_FieldNumber_Name, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBMixin__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -344,7 +344,7 @@ typedef struct GPBMixin__storage_ { .number = GPBMixin_FieldNumber_Root, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBMixin__storage_, root), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, }; @@ -355,7 +355,7 @@ typedef struct GPBMixin__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBMixin__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBBootstrap.h b/objectivec/GPBBootstrap.h index 198ff9c71d..ea5986b8bf 100644 --- a/objectivec/GPBBootstrap.h +++ b/objectivec/GPBBootstrap.h @@ -132,7 +132,7 @@ // Current library runtime version. // - Gets bumped when the runtime makes changes to the interfaces between the // generated code and runtime (things added/removed, etc). -#define GOOGLE_PROTOBUF_OBJC_VERSION 30003 +#define GOOGLE_PROTOBUF_OBJC_VERSION 30004 // Minimum runtime version supported for compiling/running against. // - Gets changed when support for the older generated code is dropped. diff --git a/objectivec/GPBDescriptor.m b/objectivec/GPBDescriptor.m index 130f1b96a8..c29b95539f 100644 --- a/objectivec/GPBDescriptor.m +++ b/objectivec/GPBDescriptor.m @@ -128,6 +128,8 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, (flags & GPBDescriptorInitializationFlag_FieldsWithDefault) != 0; BOOL usesClassRefs = (flags & GPBDescriptorInitializationFlag_UsesClassRefs) != 0; + BOOL proto3OptionalKnown = + (flags & GPBDescriptorInitializationFlag_Proto3OptionalKnown) != 0; void *desc; for (uint32_t i = 0; i < fieldCount; ++i) { @@ -146,6 +148,7 @@ static NSArray *NewFieldsArrayForHasIndex(int hasIndex, [[GPBFieldDescriptor alloc] initWithFieldDescription:desc includesDefault:fieldsIncludeDefault usesClassRefs:usesClassRefs + proto3OptionalKnown:proto3OptionalKnown syntax:syntax]; [fields addObject:fieldDescriptor]; [fieldDescriptor release]; @@ -488,6 +491,7 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { - (instancetype)initWithFieldDescription:(void *)description includesDefault:(BOOL)includesDefault usesClassRefs:(BOOL)usesClassRefs + proto3OptionalKnown:(BOOL)proto3OptionalKnown syntax:(GPBFileSyntax)syntax { if ((self = [super init])) { GPBMessageFieldDescription *coreDesc; @@ -504,20 +508,34 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { BOOL isMessage = GPBDataTypeIsMessage(dataType); BOOL isMapOrArray = GPBFieldIsMapOrArray(self); + // If proto3 optionals weren't known (i.e. generated code from an + // older version), compute the flag for the rest of the runtime. + if (!proto3OptionalKnown) { + // If it was... + // - proto3 syntax + // - not repeated/map + // - not in a oneof (negative has index) + // - not a message (the flag doesn't make sense for messages) + BOOL clearOnZero = ((syntax == GPBFileSyntaxProto3) && + !isMapOrArray && + (coreDesc->hasIndex >= 0) && + !isMessage); + if (clearOnZero) { + coreDesc->flags |= GPBFieldClearHasIvarOnZero; + } + } + if (isMapOrArray) { // map<>/repeated fields get a *Count property (inplace of a has*) to // support checking if there are any entries without triggering // autocreation. hasOrCountSel_ = SelFromStrings(NULL, coreDesc->name, "_Count", NO); } else { - // If there is a positive hasIndex, then: - // - All fields types for proto2 messages get has* selectors. - // - Only message fields for proto3 messages get has* selectors. - // Note: the positive check is to handle oneOfs, we can't check - // containingOneof_ because it isn't set until after initialization. + // It is a single field; it gets has/setHas selectors if... + // - not in a oneof (negative has index) + // - not clearing on zero if ((coreDesc->hasIndex >= 0) && - (coreDesc->hasIndex != GPBNoHasBit) && - ((syntax != GPBFileSyntaxProto3) || isMessage)) { + ((coreDesc->flags & GPBFieldClearHasIvarOnZero) == 0)) { hasOrCountSel_ = SelFromStrings("has", coreDesc->name, NULL, NO); setHasSel_ = SelFromStrings("setHas", coreDesc->name, NULL, YES); } @@ -567,15 +585,6 @@ uint32_t GPBFieldAlternateTag(GPBFieldDescriptor *self) { return self; } -- (instancetype)initWithFieldDescription:(void *)description - includesDefault:(BOOL)includesDefault - syntax:(GPBFileSyntax)syntax { - return [self initWithFieldDescription:description - includesDefault:includesDefault - usesClassRefs:NO - syntax:syntax]; -} - - (void)dealloc { if (description_->dataType == GPBDataTypeBytes && !(description_->flags & GPBFieldRepeated)) { diff --git a/objectivec/GPBDescriptor_PackagePrivate.h b/objectivec/GPBDescriptor_PackagePrivate.h index 09c1202a07..b3d673043c 100644 --- a/objectivec/GPBDescriptor_PackagePrivate.h +++ b/objectivec/GPBDescriptor_PackagePrivate.h @@ -45,6 +45,10 @@ typedef NS_OPTIONS(uint16_t, GPBFieldFlags) { GPBFieldOptional = 1 << 3, GPBFieldHasDefaultValue = 1 << 4, + // Indicate that the field should "clear" when set to zero value. This is the + // proto3 non optional behavior for singular data (ints, data, string, enum) + // fields. + GPBFieldClearHasIvarOnZero = 1 << 5, // Indicates the field needs custom handling for the TextFormat name, if not // set, the name can be derived from the ObjC name. GPBFieldTextFormatNameCustom = 1 << 6, @@ -149,7 +153,13 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) { // This is used as a stopgap as we move from using class names to class // references. The runtime needs to support both until we allow a // breaking change in the runtime. - GPBDescriptorInitializationFlag_UsesClassRefs = 1 << 2, + GPBDescriptorInitializationFlag_UsesClassRefs = 1 << 2, + + // This flag is used to indicate that the generated sources already contain + // the `GPBFieldClearHasIvarOnZero` flag and it doesn't have to be computed + // at startup. This allows older generated code to still work with the + // current runtime library. + GPBDescriptorInitializationFlag_Proto3OptionalKnown = 1 << 3, }; @interface GPBDescriptor () { @@ -225,14 +235,9 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) { - (instancetype)initWithFieldDescription:(void *)description includesDefault:(BOOL)includesDefault usesClassRefs:(BOOL)usesClassRefs + proto3OptionalKnown:(BOOL)proto3OptionalKnown syntax:(GPBFileSyntax)syntax; -// Deprecated. Equivalent to calling above with `usesClassRefs = NO`. -- (instancetype)initWithFieldDescription:(void *)description - includesDefault:(BOOL)includesDefault - syntax:(GPBFileSyntax)syntax; - - @end @interface GPBEnumDescriptor () diff --git a/objectivec/GPBDuration.pbobjc.h b/objectivec/GPBDuration.pbobjc.h index 6ff27092cd..88527f520d 100644 --- a/objectivec/GPBDuration.pbobjc.h +++ b/objectivec/GPBDuration.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBDuration.pbobjc.m b/objectivec/GPBDuration.pbobjc.m index 465831e604..d3cc7e31ca 100644 --- a/objectivec/GPBDuration.pbobjc.m +++ b/objectivec/GPBDuration.pbobjc.m @@ -72,7 +72,7 @@ typedef struct GPBDuration__storage_ { .number = GPBDuration_FieldNumber_Seconds, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBDuration__storage_, seconds), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt64, }, { @@ -81,7 +81,7 @@ typedef struct GPBDuration__storage_ { .number = GPBDuration_FieldNumber_Nanos, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBDuration__storage_, nanos), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt32, }, }; @@ -92,7 +92,7 @@ typedef struct GPBDuration__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBDuration__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBEmpty.pbobjc.h b/objectivec/GPBEmpty.pbobjc.h index 9673400376..45600ead86 100644 --- a/objectivec/GPBEmpty.pbobjc.h +++ b/objectivec/GPBEmpty.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBEmpty.pbobjc.m b/objectivec/GPBEmpty.pbobjc.m index 29aa389b3e..df3e398170 100644 --- a/objectivec/GPBEmpty.pbobjc.m +++ b/objectivec/GPBEmpty.pbobjc.m @@ -68,7 +68,7 @@ typedef struct GPBEmpty__storage_ { fields:NULL fieldCount:0 storageSize:sizeof(GPBEmpty__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBFieldMask.pbobjc.h b/objectivec/GPBFieldMask.pbobjc.h index fa52ed0d3b..3028b775dc 100644 --- a/objectivec/GPBFieldMask.pbobjc.h +++ b/objectivec/GPBFieldMask.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBFieldMask.pbobjc.m b/objectivec/GPBFieldMask.pbobjc.m index 9f119fc164..3605f89d80 100644 --- a/objectivec/GPBFieldMask.pbobjc.m +++ b/objectivec/GPBFieldMask.pbobjc.m @@ -81,7 +81,7 @@ typedef struct GPBFieldMask__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBFieldMask__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBMessage.m b/objectivec/GPBMessage.m index 973c18b423..140c6a29de 100644 --- a/objectivec/GPBMessage.m +++ b/objectivec/GPBMessage.m @@ -99,15 +99,13 @@ static id CreateArrayForField(GPBFieldDescriptor *field, GPBMessage *autocreator) __attribute__((ns_returns_retained)); static id GetOrCreateArrayIvarWithField(GPBMessage *self, - GPBFieldDescriptor *field, - GPBFileSyntax syntax); + GPBFieldDescriptor *field); static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); static id CreateMapForField(GPBFieldDescriptor *field, GPBMessage *autocreator) __attribute__((ns_returns_retained)); static id GetOrCreateMapIvarWithField(GPBMessage *self, - GPBFieldDescriptor *field, - GPBFileSyntax syntax); + GPBFieldDescriptor *field); static id GetMapIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); static NSMutableDictionary *CloneExtensionMap(NSDictionary *extensionMap, NSZone *zone) @@ -560,10 +558,10 @@ static id CreateMapForField(GPBFieldDescriptor *field, #if !defined(__clang_analyzer__) // These functions are blocked from the analyzer because the analyzer sees the -// GPBSetRetainedObjectIvarWithFieldInternal() call as consuming the array/map, +// GPBSetRetainedObjectIvarWithFieldPrivate() call as consuming the array/map, // so use of the array/map after the call returns is flagged as a use after // free. -// But GPBSetRetainedObjectIvarWithFieldInternal() is "consuming" the retain +// But GPBSetRetainedObjectIvarWithFieldPrivate() is "consuming" the retain // count be holding onto the object (it is transferring it), the object is // still valid after returning from the call. The other way to avoid this // would be to add a -retain/-autorelease, but that would force every @@ -571,14 +569,13 @@ static id CreateMapForField(GPBFieldDescriptor *field, // and performance hit. static id GetOrCreateArrayIvarWithField(GPBMessage *self, - GPBFieldDescriptor *field, - GPBFileSyntax syntax) { + GPBFieldDescriptor *field) { id array = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!array) { // No lock needed, this is called from places expecting to mutate // so no threading protection is needed. array = CreateArrayForField(field, nil); - GPBSetRetainedObjectIvarWithFieldInternal(self, field, array, syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, array); } return array; } @@ -602,14 +599,13 @@ static id GetArrayIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { } static id GetOrCreateMapIvarWithField(GPBMessage *self, - GPBFieldDescriptor *field, - GPBFileSyntax syntax) { + GPBFieldDescriptor *field) { id dict = GPBGetObjectIvarWithFieldNoAutocreate(self, field); if (!dict) { // No lock needed, this is called from places expecting to mutate // so no threading protection is needed. dict = CreateMapForField(field, nil); - GPBSetRetainedObjectIvarWithFieldInternal(self, field, dict, syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, dict); } return dict; } @@ -668,9 +664,8 @@ void GPBBecomeVisibleToAutocreator(GPBMessage *self) { // This will recursively make all parent messages visible until it reaches a // super-creator that's visible. if (self->autocreatorField_) { - GPBFileSyntax syntax = [self->autocreator_ descriptor].file.syntax; - GPBSetObjectIvarWithFieldInternal(self->autocreator_, - self->autocreatorField_, self, syntax); + GPBSetObjectIvarWithFieldPrivate(self->autocreator_, + self->autocreatorField_, self); } else { [self->autocreator_ setExtension:self->autocreatorExtension_ value:self]; } @@ -936,8 +931,6 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { // Copy all the storage... memcpy(message->messageStorage_, messageStorage_, descriptor->storageSize_); - GPBFileSyntax syntax = descriptor.file.syntax; - // Loop over the fields doing fixup... for (GPBFieldDescriptor *field in descriptor->fields_) { if (GPBFieldIsMapOrArray(field)) { @@ -1005,8 +998,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { // We retain here because the memcpy picked up the pointer value and // the next call to SetRetainedObject... will release the current value. [value retain]; - GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue, - syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(message, field, newValue); } } else if (GPBFieldDataTypeIsMessage(field)) { // For object types, if we have a value, copy it. If we don't, @@ -1018,8 +1010,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { // We retain here because the memcpy picked up the pointer value and // the next call to SetRetainedObject... will release the current value. [value retain]; - GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue, - syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(message, field, newValue); } else { uint8_t *storage = (uint8_t *)message->messageStorage_; id *typePtr = (id *)&storage[field->description_->offset]; @@ -1033,8 +1024,7 @@ static GPBUnknownFieldSet *GetOrMakeUnknownFields(GPBMessage *self) { // We retain here because the memcpy picked up the pointer value and // the next call to SetRetainedObject... will release the current value. [value retain]; - GPBSetRetainedObjectIvarWithFieldInternal(message, field, newValue, - syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(message, field, newValue); } else { // memcpy took care of the rest of the primitive fields if they were set. } @@ -2161,13 +2151,13 @@ static void MergeSingleFieldFromCodedInputStream( #define CASE_SINGLE_POD(NAME, TYPE, FUNC_TYPE) \ case GPBDataType##NAME: { \ TYPE val = GPBCodedInputStreamRead##NAME(&input->state_); \ - GPBSet##FUNC_TYPE##IvarWithFieldInternal(self, field, val, syntax); \ + GPBSet##FUNC_TYPE##IvarWithFieldPrivate(self, field, val); \ break; \ } #define CASE_SINGLE_OBJECT(NAME) \ case GPBDataType##NAME: { \ id val = GPBCodedInputStreamReadRetained##NAME(&input->state_); \ - GPBSetRetainedObjectIvarWithFieldInternal(self, field, val, syntax); \ + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, val); \ break; \ } CASE_SINGLE_POD(Bool, BOOL, Bool) @@ -2198,7 +2188,7 @@ static void MergeSingleFieldFromCodedInputStream( } else { GPBMessage *message = [[field.msgClass alloc] init]; [input readMessage:message extensionRegistry:extensionRegistry]; - GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, message); } break; } @@ -2217,7 +2207,7 @@ static void MergeSingleFieldFromCodedInputStream( [input readGroup:GPBFieldNumber(field) message:message extensionRegistry:extensionRegistry]; - GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, message); } break; } @@ -2226,7 +2216,7 @@ static void MergeSingleFieldFromCodedInputStream( int32_t val = GPBCodedInputStreamReadEnum(&input->state_); if (GPBHasPreservingUnknownEnumSemantics(syntax) || [field isValidEnumValue:val]) { - GPBSetInt32IvarWithFieldInternal(self, field, val, syntax); + GPBSetInt32IvarWithFieldPrivate(self, field, val); } else { GPBUnknownFieldSet *unknownFields = GetOrMakeUnknownFields(self); [unknownFields mergeVarintField:GPBFieldNumber(field) value:val]; @@ -2240,7 +2230,7 @@ static void MergeRepeatedPackedFieldFromCodedInputStream( GPBCodedInputStream *input) { GPBDataType fieldDataType = GPBGetFieldDataType(field); GPBCodedInputStreamState *state = &input->state_; - id genericArray = GetOrCreateArrayIvarWithField(self, field, syntax); + id genericArray = GetOrCreateArrayIvarWithField(self, field); int32_t length = GPBCodedInputStreamReadInt32(state); size_t limit = GPBCodedInputStreamPushLimit(state, length); while (GPBCodedInputStreamBytesUntilLimit(state) > 0) { @@ -2293,7 +2283,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( GPBMessage *self, GPBFieldDescriptor *field, GPBFileSyntax syntax, GPBCodedInputStream *input, GPBExtensionRegistry *extensionRegistry) { GPBCodedInputStreamState *state = &input->state_; - id genericArray = GetOrCreateArrayIvarWithField(self, field, syntax); + id genericArray = GetOrCreateArrayIvarWithField(self, field); switch (GPBGetFieldDataType(field)) { #define CASE_REPEATED_NOT_PACKED_POD(NAME, TYPE, ARRAY_TYPE) \ case GPBDataType##NAME: { \ @@ -2395,7 +2385,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( } else { // fieldType == GPBFieldTypeMap // GPB*Dictionary or NSDictionary, exact type doesn't matter at this // point. - id map = GetOrCreateMapIvarWithField(self, fieldDescriptor, syntax); + id map = GetOrCreateMapIvarWithField(self, fieldDescriptor); [input readMapEntry:map extensionRegistry:extensionRegistry field:fieldDescriptor @@ -2469,7 +2459,6 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( GPBBecomeVisibleToAutocreator(self); GPBDescriptor *descriptor = [[self class] descriptor]; - GPBFileSyntax syntax = descriptor.file.syntax; for (GPBFieldDescriptor *field in descriptor->fields_) { GPBFieldType fieldType = field.fieldType; @@ -2483,44 +2472,44 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( GPBDataType fieldDataType = GPBGetFieldDataType(field); switch (fieldDataType) { case GPBDataTypeBool: - GPBSetBoolIvarWithFieldInternal( - self, field, GPBGetMessageBoolField(other, field), syntax); + GPBSetBoolIvarWithFieldPrivate( + self, field, GPBGetMessageBoolField(other, field)); break; case GPBDataTypeSFixed32: case GPBDataTypeEnum: case GPBDataTypeInt32: case GPBDataTypeSInt32: - GPBSetInt32IvarWithFieldInternal( - self, field, GPBGetMessageInt32Field(other, field), syntax); + GPBSetInt32IvarWithFieldPrivate( + self, field, GPBGetMessageInt32Field(other, field)); break; case GPBDataTypeFixed32: case GPBDataTypeUInt32: - GPBSetUInt32IvarWithFieldInternal( - self, field, GPBGetMessageUInt32Field(other, field), syntax); + GPBSetUInt32IvarWithFieldPrivate( + self, field, GPBGetMessageUInt32Field(other, field)); break; case GPBDataTypeSFixed64: case GPBDataTypeInt64: case GPBDataTypeSInt64: - GPBSetInt64IvarWithFieldInternal( - self, field, GPBGetMessageInt64Field(other, field), syntax); + GPBSetInt64IvarWithFieldPrivate( + self, field, GPBGetMessageInt64Field(other, field)); break; case GPBDataTypeFixed64: case GPBDataTypeUInt64: - GPBSetUInt64IvarWithFieldInternal( - self, field, GPBGetMessageUInt64Field(other, field), syntax); + GPBSetUInt64IvarWithFieldPrivate( + self, field, GPBGetMessageUInt64Field(other, field)); break; case GPBDataTypeFloat: - GPBSetFloatIvarWithFieldInternal( - self, field, GPBGetMessageFloatField(other, field), syntax); + GPBSetFloatIvarWithFieldPrivate( + self, field, GPBGetMessageFloatField(other, field)); break; case GPBDataTypeDouble: - GPBSetDoubleIvarWithFieldInternal( - self, field, GPBGetMessageDoubleField(other, field), syntax); + GPBSetDoubleIvarWithFieldPrivate( + self, field, GPBGetMessageDoubleField(other, field)); break; case GPBDataTypeBytes: case GPBDataTypeString: { id otherVal = GPBGetObjectIvarWithFieldNoAutocreate(other, field); - GPBSetObjectIvarWithFieldInternal(self, field, otherVal, syntax); + GPBSetObjectIvarWithFieldPrivate(self, field, otherVal); break; } case GPBDataTypeMessage: @@ -2532,8 +2521,7 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( [message mergeFrom:otherVal]; } else { GPBMessage *message = [otherVal copy]; - GPBSetRetainedObjectIvarWithFieldInternal(self, field, message, - syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, message); } break; } @@ -2547,17 +2535,17 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( GPBDataType fieldDataType = field->description_->dataType; if (GPBDataTypeIsObject(fieldDataType)) { NSMutableArray *resultArray = - GetOrCreateArrayIvarWithField(self, field, syntax); + GetOrCreateArrayIvarWithField(self, field); [resultArray addObjectsFromArray:otherArray]; } else if (fieldDataType == GPBDataTypeEnum) { GPBEnumArray *resultArray = - GetOrCreateArrayIvarWithField(self, field, syntax); + GetOrCreateArrayIvarWithField(self, field); [resultArray addRawValuesFromArray:otherArray]; } else { // The array type doesn't matter, that all implement // -addValuesFromArray:. GPBInt32Array *resultArray = - GetOrCreateArrayIvarWithField(self, field, syntax); + GetOrCreateArrayIvarWithField(self, field); [resultArray addValuesFromArray:otherArray]; } } @@ -2571,19 +2559,19 @@ static void MergeRepeatedNotPackedFieldFromCodedInputStream( if (GPBDataTypeIsObject(keyDataType) && GPBDataTypeIsObject(valueDataType)) { NSMutableDictionary *resultDict = - GetOrCreateMapIvarWithField(self, field, syntax); + GetOrCreateMapIvarWithField(self, field); [resultDict addEntriesFromDictionary:otherDict]; } else if (valueDataType == GPBDataTypeEnum) { // The exact type doesn't matter, just need to know it is a // GPB*EnumDictionary. GPBInt32EnumDictionary *resultDict = - GetOrCreateMapIvarWithField(self, field, syntax); + GetOrCreateMapIvarWithField(self, field); [resultDict addRawEntriesFromDictionary:otherDict]; } else { // The exact type doesn't matter, they all implement // -addEntriesFromDictionary:. GPBInt32Int32Dictionary *resultDict = - GetOrCreateMapIvarWithField(self, field, syntax); + GetOrCreateMapIvarWithField(self, field); [resultDict addEntriesFromDictionary:otherDict]; } } @@ -3115,14 +3103,13 @@ static void ResolveIvarGet(__unsafe_unretained GPBFieldDescriptor *field, // See comment about __unsafe_unretained on ResolveIvarGet. static void ResolveIvarSet(__unsafe_unretained GPBFieldDescriptor *field, - GPBFileSyntax syntax, ResolveIvarAccessorMethodResult *result) { GPBDataType fieldDataType = GPBGetFieldDataType(field); switch (fieldDataType) { #define CASE_SET(NAME, TYPE, TRUE_NAME) \ case GPBDataType##NAME: { \ result->impToAdd = imp_implementationWithBlock(^(id obj, TYPE value) { \ - return GPBSet##TRUE_NAME##IvarWithFieldInternal(obj, field, value, syntax); \ + return GPBSet##TRUE_NAME##IvarWithFieldPrivate(obj, field, value); \ }); \ result->encodingSelector = @selector(set##NAME:); \ break; \ @@ -3130,7 +3117,7 @@ static void ResolveIvarSet(__unsafe_unretained GPBFieldDescriptor *field, #define CASE_SET_COPY(NAME) \ case GPBDataType##NAME: { \ result->impToAdd = imp_implementationWithBlock(^(id obj, id value) { \ - return GPBSetRetainedObjectIvarWithFieldInternal(obj, field, [value copy], syntax); \ + return GPBSetRetainedObjectIvarWithFieldPrivate(obj, field, [value copy]); \ }); \ result->encodingSelector = @selector(set##NAME:); \ break; \ @@ -3177,7 +3164,7 @@ static void ResolveIvarSet(__unsafe_unretained GPBFieldDescriptor *field, ResolveIvarGet(field, &result); break; } else if (sel == field->setSel_) { - ResolveIvarSet(field, descriptor.file.syntax, &result); + ResolveIvarSet(field, &result); break; } else if (sel == field->hasOrCountSel_) { int32_t index = GPBFieldHasIndex(field); @@ -3227,9 +3214,8 @@ static void ResolveIvarSet(__unsafe_unretained GPBFieldDescriptor *field, } else if (sel == field->setSel_) { // Local for syntax so the block can directly capture it and not the // full lookup. - const GPBFileSyntax syntax = descriptor.file.syntax; result.impToAdd = imp_implementationWithBlock(^(id obj, id value) { - GPBSetObjectIvarWithFieldInternal(obj, field, value, syntax); + GPBSetObjectIvarWithFieldPrivate(obj, field, value); }); result.encodingSelector = @selector(setArray:); break; @@ -3334,9 +3320,7 @@ id GPBGetMessageRepeatedField(GPBMessage *self, GPBFieldDescriptor *field) { [self class], field.name]; } #endif - GPBDescriptor *descriptor = [[self class] descriptor]; - GPBFileSyntax syntax = descriptor.file.syntax; - return GetOrCreateArrayIvarWithField(self, field, syntax); + return GetOrCreateArrayIvarWithField(self, field); } // Only exists for public api, no core code should use this. @@ -3348,9 +3332,7 @@ id GPBGetMessageMapField(GPBMessage *self, GPBFieldDescriptor *field) { [self class], field.name]; } #endif - GPBDescriptor *descriptor = [[self class] descriptor]; - GPBFileSyntax syntax = descriptor.file.syntax; - return GetOrCreateMapIvarWithField(self, field, syntax); + return GetOrCreateMapIvarWithField(self, field); } id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field) { diff --git a/objectivec/GPBSourceContext.pbobjc.h b/objectivec/GPBSourceContext.pbobjc.h index 90263b1070..7a103362b5 100644 --- a/objectivec/GPBSourceContext.pbobjc.h +++ b/objectivec/GPBSourceContext.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBSourceContext.pbobjc.m b/objectivec/GPBSourceContext.pbobjc.m index d37a4252a4..b3e6fa759c 100644 --- a/objectivec/GPBSourceContext.pbobjc.m +++ b/objectivec/GPBSourceContext.pbobjc.m @@ -70,7 +70,7 @@ typedef struct GPBSourceContext__storage_ { .number = GPBSourceContext_FieldNumber_FileName, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBSourceContext__storage_, fileName), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, }; @@ -81,7 +81,7 @@ typedef struct GPBSourceContext__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBSourceContext__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBStruct.pbobjc.h b/objectivec/GPBStruct.pbobjc.h index b465c3bee5..e36df3f1a3 100644 --- a/objectivec/GPBStruct.pbobjc.h +++ b/objectivec/GPBStruct.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBStruct.pbobjc.m b/objectivec/GPBStruct.pbobjc.m index dbdcbe1df0..554046a9fe 100644 --- a/objectivec/GPBStruct.pbobjc.m +++ b/objectivec/GPBStruct.pbobjc.m @@ -126,7 +126,7 @@ typedef struct GPBStruct__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBStruct__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -226,7 +226,7 @@ typedef struct GPBValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; static const char *oneofs[] = { "kind", }; @@ -246,19 +246,19 @@ typedef struct GPBValue__storage_ { int32_t GPBValue_NullValue_RawValue(GPBValue *message) { GPBDescriptor *descriptor = [GPBValue descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBValue_FieldNumber_NullValue]; - return GPBGetMessageInt32Field(message, field); + return GPBGetMessageRawEnumField(message, field); } void SetGPBValue_NullValue_RawValue(GPBValue *message, int32_t value) { GPBDescriptor *descriptor = [GPBValue descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBValue_FieldNumber_NullValue]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); + GPBSetMessageRawEnumField(message, field, value); } void GPBValue_ClearKindOneOfCase(GPBValue *message) { - GPBDescriptor *descriptor = [message descriptor]; + GPBDescriptor *descriptor = [GPBValue descriptor]; GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:0]; - GPBMaybeClearOneof(message, oneof, -1, 0); + GPBClearOneof(message, oneof); } #pragma mark - GPBListValue @@ -294,7 +294,7 @@ typedef struct GPBListValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBListValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBTimestamp.pbobjc.h b/objectivec/GPBTimestamp.pbobjc.h index 1118c778b9..92f0bac886 100644 --- a/objectivec/GPBTimestamp.pbobjc.h +++ b/objectivec/GPBTimestamp.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBTimestamp.pbobjc.m b/objectivec/GPBTimestamp.pbobjc.m index 450d131b6c..736a75d19f 100644 --- a/objectivec/GPBTimestamp.pbobjc.m +++ b/objectivec/GPBTimestamp.pbobjc.m @@ -72,7 +72,7 @@ typedef struct GPBTimestamp__storage_ { .number = GPBTimestamp_FieldNumber_Seconds, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBTimestamp__storage_, seconds), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt64, }, { @@ -81,7 +81,7 @@ typedef struct GPBTimestamp__storage_ { .number = GPBTimestamp_FieldNumber_Nanos, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBTimestamp__storage_, nanos), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt32, }, }; @@ -92,7 +92,7 @@ typedef struct GPBTimestamp__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBTimestamp__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBType.pbobjc.h b/objectivec/GPBType.pbobjc.h index 4b6cd9c521..747e15d455 100644 --- a/objectivec/GPBType.pbobjc.h +++ b/objectivec/GPBType.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBType.pbobjc.m b/objectivec/GPBType.pbobjc.m index 827270af5f..70dae31c68 100644 --- a/objectivec/GPBType.pbobjc.m +++ b/objectivec/GPBType.pbobjc.m @@ -132,7 +132,7 @@ typedef struct GPBType__storage_ { .number = GPBType_FieldNumber_Name, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBType__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -177,7 +177,7 @@ typedef struct GPBType__storage_ { .number = GPBType_FieldNumber_Syntax, .hasIndex = 2, .offset = (uint32_t)offsetof(GPBType__storage_, syntax), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeEnum, }, }; @@ -188,7 +188,7 @@ typedef struct GPBType__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBType__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -202,13 +202,13 @@ typedef struct GPBType__storage_ { int32_t GPBType_Syntax_RawValue(GPBType *message) { GPBDescriptor *descriptor = [GPBType descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBType_FieldNumber_Syntax]; - return GPBGetMessageInt32Field(message, field); + return GPBGetMessageRawEnumField(message, field); } void SetGPBType_Syntax_RawValue(GPBType *message, int32_t value) { GPBDescriptor *descriptor = [GPBType descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBType_FieldNumber_Syntax]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); + GPBSetMessageRawEnumField(message, field, value); } #pragma mark - GPBField @@ -251,7 +251,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_Kind, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBField__storage_, kind), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeEnum, }, { @@ -260,7 +260,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_Cardinality, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBField__storage_, cardinality), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeEnum, }, { @@ -269,7 +269,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_Number, .hasIndex = 2, .offset = (uint32_t)offsetof(GPBField__storage_, number), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt32, }, { @@ -278,7 +278,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_Name, .hasIndex = 3, .offset = (uint32_t)offsetof(GPBField__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -287,7 +287,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_TypeURL, .hasIndex = 4, .offset = (uint32_t)offsetof(GPBField__storage_, typeURL), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldTextFormatNameCustom | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -296,7 +296,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_OneofIndex, .hasIndex = 5, .offset = (uint32_t)offsetof(GPBField__storage_, oneofIndex), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt32, }, { @@ -305,7 +305,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_Packed, .hasIndex = 6, .offset = 7, // Stored in _has_storage_ to save space. - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeBool, }, { @@ -323,7 +323,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_JsonName, .hasIndex = 8, .offset = (uint32_t)offsetof(GPBField__storage_, jsonName), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -332,7 +332,7 @@ typedef struct GPBField__storage_ { .number = GPBField_FieldNumber_DefaultValue, .hasIndex = 9, .offset = (uint32_t)offsetof(GPBField__storage_, defaultValue), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, }; @@ -343,7 +343,7 @@ typedef struct GPBField__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBField__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if !GPBOBJC_SKIP_MESSAGE_TEXTFORMAT_EXTRAS static const char *extraTextFormatInfo = "\001\006\004\241!!\000"; @@ -362,25 +362,25 @@ typedef struct GPBField__storage_ { int32_t GPBField_Kind_RawValue(GPBField *message) { GPBDescriptor *descriptor = [GPBField descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Kind]; - return GPBGetMessageInt32Field(message, field); + return GPBGetMessageRawEnumField(message, field); } void SetGPBField_Kind_RawValue(GPBField *message, int32_t value) { GPBDescriptor *descriptor = [GPBField descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Kind]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); + GPBSetMessageRawEnumField(message, field, value); } int32_t GPBField_Cardinality_RawValue(GPBField *message) { GPBDescriptor *descriptor = [GPBField descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Cardinality]; - return GPBGetMessageInt32Field(message, field); + return GPBGetMessageRawEnumField(message, field); } void SetGPBField_Cardinality_RawValue(GPBField *message, int32_t value) { GPBDescriptor *descriptor = [GPBField descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBField_FieldNumber_Cardinality]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); + GPBSetMessageRawEnumField(message, field, value); } #pragma mark - Enum GPBField_Kind @@ -528,7 +528,7 @@ typedef struct GPBEnum__storage_ { .number = GPBEnum_FieldNumber_Name, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBEnum__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -564,7 +564,7 @@ typedef struct GPBEnum__storage_ { .number = GPBEnum_FieldNumber_Syntax, .hasIndex = 2, .offset = (uint32_t)offsetof(GPBEnum__storage_, syntax), - .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor), + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldHasEnumDescriptor | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeEnum, }, }; @@ -575,7 +575,7 @@ typedef struct GPBEnum__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBEnum__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -589,13 +589,13 @@ typedef struct GPBEnum__storage_ { int32_t GPBEnum_Syntax_RawValue(GPBEnum *message) { GPBDescriptor *descriptor = [GPBEnum descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBEnum_FieldNumber_Syntax]; - return GPBGetMessageInt32Field(message, field); + return GPBGetMessageRawEnumField(message, field); } void SetGPBEnum_Syntax_RawValue(GPBEnum *message, int32_t value) { GPBDescriptor *descriptor = [GPBEnum descriptor]; GPBFieldDescriptor *field = [descriptor fieldWithNumber:GPBEnum_FieldNumber_Syntax]; - GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax); + GPBSetMessageRawEnumField(message, field, value); } #pragma mark - GPBEnumValue @@ -625,7 +625,7 @@ typedef struct GPBEnumValue__storage_ { .number = GPBEnumValue_FieldNumber_Name, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBEnumValue__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -634,7 +634,7 @@ typedef struct GPBEnumValue__storage_ { .number = GPBEnumValue_FieldNumber_Number, .hasIndex = 1, .offset = (uint32_t)offsetof(GPBEnumValue__storage_, number), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt32, }, { @@ -654,7 +654,7 @@ typedef struct GPBEnumValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBEnumValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -690,7 +690,7 @@ typedef struct GPBOption__storage_ { .number = GPBOption_FieldNumber_Name, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBOption__storage_, name), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, { @@ -710,7 +710,7 @@ typedef struct GPBOption__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBOption__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/GPBUtilities.h b/objectivec/GPBUtilities.h index ef4f8552b9..75759b2334 100644 --- a/objectivec/GPBUtilities.h +++ b/objectivec/GPBUtilities.h @@ -34,6 +34,8 @@ #import "GPBMessage.h" #import "GPBRuntimeTypes.h" +@class GPBOneofDescriptor; + CF_EXTERN_C_BEGIN NS_ASSUME_NONNULL_BEGIN @@ -92,6 +94,14 @@ BOOL GPBMessageHasFieldSet(GPBMessage *self, GPBFieldDescriptor *field); **/ void GPBClearMessageField(GPBMessage *self, GPBFieldDescriptor *field); +/** + * Clears the given oneof field for the given message. + * + * @param self The message for which to clear the field. + * @param oneof The oneof to clear. + **/ +void GPBClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof); + //%PDDM-EXPAND GPB_ACCESSORS() // This block of code is generated, do not edit it directly. // clang-format off diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m index bf1b926217..ee79d00192 100644 --- a/objectivec/GPBUtilities.m +++ b/objectivec/GPBUtilities.m @@ -62,6 +62,12 @@ static GPBDataType BaseDataType(GPBDataType type) __attribute__ ((unused)); // Marked unused because currently only called from asserts/debug. static NSString *TypeToString(GPBDataType dataType) __attribute__ ((unused)); +// Helper for clearing oneofs. +static void GPBMaybeClearOneofPrivate(GPBMessage *self, + GPBOneofDescriptor *oneof, + int32_t oneofHasIndex, + uint32_t fieldNumberNotToClear); + NSData *GPBEmptyNSData(void) { static dispatch_once_t onceToken; static NSData *defaultNSData = nil; @@ -267,17 +273,28 @@ void GPBClearMessageField(GPBMessage *self, GPBFieldDescriptor *field) { return; } + GPBMessageFieldDescription *fieldDesc = field->description_; if (GPBFieldStoresObject(field)) { // Object types are handled slightly differently, they need to be released. uint8_t *storage = (uint8_t *)self->messageStorage_; - id *typePtr = (id *)&storage[field->description_->offset]; + id *typePtr = (id *)&storage[fieldDesc->offset]; [*typePtr release]; *typePtr = nil; } else { // POD types just need to clear the has bit as the Get* method will // fetch the default when needed. } - GPBSetHasIvarField(self, field, NO); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, NO); +} + +void GPBClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof) { + #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] oneofWithName:oneof.name] == oneof, + @"OneofDescriptor %@ doesn't appear to be for %@ messages.", + oneof.name, [self class]); + #endif + GPBFieldDescriptor *firstField = oneof->fields_[0]; + GPBMaybeClearOneofPrivate(self, oneof, firstField->description_->hasIndex, 0); } BOOL GPBGetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber) { @@ -324,8 +341,10 @@ void GPBSetHasIvar(GPBMessage *self, int32_t idx, uint32_t fieldNumber, } } -void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, - int32_t oneofHasIndex, uint32_t fieldNumberNotToClear) { +static void GPBMaybeClearOneofPrivate(GPBMessage *self, + GPBOneofDescriptor *oneof, + int32_t oneofHasIndex, + uint32_t fieldNumberNotToClear) { uint32_t fieldNumberSet = GPBGetHasOneof(self, oneofHasIndex); if ((fieldNumberSet == fieldNumberNotToClear) || (fieldNumberSet == 0)) { // Do nothing/nothing set in the oneof. @@ -356,6 +375,9 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, //%TYPE GPBGetMessage##NAME##Field(GPBMessage *self, //% TYPE$S NAME$S GPBFieldDescriptor *field) { //%#if defined(DEBUG) && DEBUG +//% NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, +//% @"FieldDescriptor %@ doesn't appear to be for %@ messages.", +//% field.name, [self class]); //% NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), //% GPBDataType##NAME), //% @"Attempting to get value of TYPE from field %@ " @@ -377,15 +399,10 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, //% NAME$S GPBFieldDescriptor *field, //% NAME$S TYPE value) { //% if (self == nil || field == nil) return; -//% GPBFileSyntax syntax = [self descriptor].file.syntax; -//% GPBSet##NAME##IvarWithFieldInternal(self, field, value, syntax); -//%} -//% -//%void GPBSet##NAME##IvarWithFieldInternal(GPBMessage *self, -//% NAME$S GPBFieldDescriptor *field, -//% NAME$S TYPE value, -//% NAME$S GPBFileSyntax syntax) { //%#if defined(DEBUG) && DEBUG +//% NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, +//% @"FieldDescriptor %@ doesn't appear to be for %@ messages.", +//% field.name, [self class]); //% NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), //% GPBDataType##NAME), //% @"Attempting to set field %@ of %@ which is of type %@ with " @@ -393,10 +410,16 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, //% [self class], field.name, //% TypeToString(GPBGetFieldDataType(field))); //%#endif +//% GPBSet##NAME##IvarWithFieldPrivate(self, field, value); +//%} +//% +//%void GPBSet##NAME##IvarWithFieldPrivate(GPBMessage *self, +//% NAME$S GPBFieldDescriptor *field, +//% NAME$S TYPE value) { //% GPBOneofDescriptor *oneof = field->containingOneof_; +//% GPBMessageFieldDescription *fieldDesc = field->description_; //% if (oneof) { -//% GPBMessageFieldDescription *fieldDesc = field->description_; -//% GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); +//% GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); //% } //%#if defined(DEBUG) && DEBUG //% NSCAssert(self->messageStorage_ != NULL, @@ -407,14 +430,13 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, //% if (self->messageStorage_ == NULL) return; //%#endif //% uint8_t *storage = (uint8_t *)self->messageStorage_; -//% TYPE *typePtr = (TYPE *)&storage[field->description_->offset]; +//% TYPE *typePtr = (TYPE *)&storage[fieldDesc->offset]; //% *typePtr = value; -//% // proto2: any value counts as having been set; proto3, it -//% // has to be a non zero value or be in a oneof. -//% BOOL hasValue = ((syntax == GPBFileSyntaxProto2) -//% || (value != (TYPE)0) -//% || (field->containingOneof_ != NULL)); -//% GPBSetHasIvarField(self, field, hasValue); +//% // If the value is zero, then we only count the field as "set" if the field +//% // shouldn't auto clear on zero. +//% BOOL hasValue = ((value != (TYPE)0) +//% || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); +//% GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); //% GPBBecomeVisibleToAutocreator(self); //%} //% @@ -508,16 +530,14 @@ void GPBClearAutocreatedMessageIvarWithField(GPBMessage *self, static void GPBSetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field, id value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetRetainedObjectIvarWithFieldInternal(self, field, [value retain], - syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, [value retain]); } static void GPBSetCopyObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field, id value); // GPBSetCopyObjectIvarWithField is blocked from the analyzer because it flags -// a leak for the -copy even though GPBSetRetainedObjectIvarWithFieldInternal +// a leak for the -copy even though GPBSetRetainedObjectIvarWithFieldPrivate // is marked as consuming the value. Note: For some reason this doesn't happen // with the -retain in GPBSetObjectIvarWithField. #if !defined(__clang_analyzer__) @@ -525,22 +545,18 @@ static void GPBSetCopyObjectIvarWithField(GPBMessage *self, static void GPBSetCopyObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field, id value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetRetainedObjectIvarWithFieldInternal(self, field, [value copy], - syntax); + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, [value copy]); } #endif // !defined(__clang_analyzer__) -void GPBSetObjectIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, id value, - GPBFileSyntax syntax) { - GPBSetRetainedObjectIvarWithFieldInternal(self, field, [value retain], - syntax); +void GPBSetObjectIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, id value) { + GPBSetRetainedObjectIvarWithFieldPrivate(self, field, [value retain]); } -void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - id value, GPBFileSyntax syntax) { +void GPBSetRetainedObjectIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + id value) { NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", [self class]); @@ -579,39 +595,30 @@ void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self, // valueData/valueMessage. } #endif // DEBUG + GPBMessageFieldDescription *fieldDesc = field->description_; if (!isMapOrArray) { // Non repeated/map can be in an oneof, clear any existing value from the // oneof. GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } // Clear "has" if they are being set to nil. BOOL setHasValue = (value != nil); - // Under proto3, Bytes & String fields get cleared by resetting them to - // their default (empty) values, so if they are set to something of length - // zero, they are being cleared. - if ((syntax == GPBFileSyntaxProto3) && !fieldIsMessage && + // If the field should clear on a "zero" value, then check if the string/data + // was zero length, and clear instead. + if (((fieldDesc->flags & GPBFieldClearHasIvarOnZero) != 0) && ([value length] == 0)) { - // Except, if the field was in a oneof, then it still gets recorded as - // having been set so the state of the oneof can be serialized back out. - if (!oneof) { - setHasValue = NO; - } - if (setHasValue) { - NSCAssert(value != nil, @"Should never be setting has for nil"); - } else { - // The value passed in was retained, it must be released since we - // aren't saving anything in the field. - [value release]; - value = nil; - } + setHasValue = NO; + // The value passed in was retained, it must be released since we + // aren't saving anything in the field. + [value release]; + value = nil; } - GPBSetHasIvarField(self, field, setHasValue); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, setHasValue); } uint8_t *storage = (uint8_t *)self->messageStorage_; - id *typePtr = (id *)&storage[field->description_->offset]; + id *typePtr = (id *)&storage[fieldDesc->offset]; id oldValue = *typePtr; @@ -678,23 +685,22 @@ id GPBGetObjectIvarWithFieldNoAutocreate(GPBMessage *self, // Only exists for public api, no core code should use this. int32_t GPBGetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field) { - GPBFileSyntax syntax = [self descriptor].file.syntax; - return GPBGetEnumIvarWithFieldInternal(self, field, syntax); -} + #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); + NSCAssert(GPBGetFieldDataType(field) == GPBDataTypeEnum, + @"Attempting to get value of type Enum from field %@ " + @"of %@ which is of type %@.", + [self class], field.name, + TypeToString(GPBGetFieldDataType(field))); + #endif -int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - GPBFileSyntax syntax) { -#if defined(DEBUG) && DEBUG - NSCAssert(GPBGetFieldDataType(field) == GPBDataTypeEnum, - @"Attempting to get value of type Enum from field %@ " - @"of %@ which is of type %@.", - [self class], field.name, - TypeToString(GPBGetFieldDataType(field))); -#endif int32_t result = GPBGetMessageInt32Field(self, field); // If this is presevering unknown enums, make sure the value is valid before // returning it. + + GPBFileSyntax syntax = [self descriptor].file.syntax; if (GPBHasPreservingUnknownEnumSemantics(syntax) && ![field isValidEnumValue:result]) { result = kGPBUnrecognizedEnumeratorValue; @@ -705,27 +711,28 @@ int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self, // Only exists for public api, no core code should use this. void GPBSetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field, int32_t value) { - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetInt32IvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetEnumIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, int32_t value, - GPBFileSyntax syntax) { -#if defined(DEBUG) && DEBUG - NSCAssert(GPBGetFieldDataType(field) == GPBDataTypeEnum, - @"Attempting to set field %@ of %@ which is of type %@ with " - @"value of type Enum.", - [self class], field.name, - TypeToString(GPBGetFieldDataType(field))); -#endif + #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); + NSCAssert(GPBGetFieldDataType(field) == GPBDataTypeEnum, + @"Attempting to set field %@ of %@ which is of type %@ with " + @"value of type Enum.", + [self class], field.name, + TypeToString(GPBGetFieldDataType(field))); + #endif + GPBSetEnumIvarWithFieldPrivate(self, field, value); +} + +void GPBSetEnumIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, int32_t value) { // Don't allow in unknown values. Proto3 can use the Raw method. if (![field isValidEnumValue:value]) { [NSException raise:NSInvalidArgumentException format:@"%@.%@: Attempt to set an unknown enum value (%d)", [self class], field.name, value]; } - GPBSetInt32IvarWithFieldInternal(self, field, value, syntax); + GPBSetInt32IvarWithFieldPrivate(self, field, value); } // Only exists for public api, no core code should use this. @@ -738,13 +745,15 @@ int32_t GPBGetMessageRawEnumField(GPBMessage *self, // Only exists for public api, no core code should use this. void GPBSetMessageRawEnumField(GPBMessage *self, GPBFieldDescriptor *field, int32_t value) { - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetInt32IvarWithFieldInternal(self, field, value, syntax); + GPBSetInt32IvarWithFieldPrivate(self, field, value); } BOOL GPBGetMessageBoolField(GPBMessage *self, GPBFieldDescriptor *field) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeBool), @"Attempting to get value of type bool from field %@ " @"of %@ which is of type %@.", @@ -768,25 +777,26 @@ void GPBSetMessageBoolField(GPBMessage *self, GPBFieldDescriptor *field, BOOL value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetBoolIvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetBoolIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - BOOL value, - GPBFileSyntax syntax) { -#if defined(DEBUG) && DEBUG - NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeBool), - @"Attempting to set field %@ of %@ which is of type %@ with " - @"value of type bool.", - [self class], field.name, - TypeToString(GPBGetFieldDataType(field))); -#endif + #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); + NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeBool), + @"Attempting to set field %@ of %@ which is of type %@ with " + @"value of type bool.", + [self class], field.name, + TypeToString(GPBGetFieldDataType(field))); + #endif + GPBSetBoolIvarWithFieldPrivate(self, field, value); +} + +void GPBSetBoolIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + BOOL value) { GPBMessageFieldDescription *fieldDesc = field->description_; GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } // Bools are stored in the has bits to avoid needing explicit space in the @@ -795,12 +805,11 @@ void GPBSetBoolIvarWithFieldInternal(GPBMessage *self, // the offset is never negative) GPBSetHasIvar(self, (int32_t)(fieldDesc->offset), fieldDesc->number, value); - // proto2: any value counts as having been set; proto3, it - // has to be a non zero value or be in a oneof. - BOOL hasValue = ((syntax == GPBFileSyntaxProto2) - || (value != (BOOL)0) - || (field->containingOneof_ != NULL)); - GPBSetHasIvarField(self, field, hasValue); + // If the value is zero, then we only count the field as "set" if the field + // shouldn't auto clear on zero. + BOOL hasValue = ((value != (BOOL)0) + || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); GPBBecomeVisibleToAutocreator(self); } @@ -811,6 +820,9 @@ void GPBSetBoolIvarWithFieldInternal(GPBMessage *self, int32_t GPBGetMessageInt32Field(GPBMessage *self, GPBFieldDescriptor *field) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeInt32), @"Attempting to get value of int32_t from field %@ " @@ -832,15 +844,10 @@ void GPBSetMessageInt32Field(GPBMessage *self, GPBFieldDescriptor *field, int32_t value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetInt32IvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - int32_t value, - GPBFileSyntax syntax) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeInt32), @"Attempting to set field %@ of %@ which is of type %@ with " @@ -848,10 +855,16 @@ void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif + GPBSetInt32IvarWithFieldPrivate(self, field, value); +} + +void GPBSetInt32IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + int32_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; + GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -862,14 +875,13 @@ void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, if (self->messageStorage_ == NULL) return; #endif uint8_t *storage = (uint8_t *)self->messageStorage_; - int32_t *typePtr = (int32_t *)&storage[field->description_->offset]; + int32_t *typePtr = (int32_t *)&storage[fieldDesc->offset]; *typePtr = value; - // proto2: any value counts as having been set; proto3, it - // has to be a non zero value or be in a oneof. - BOOL hasValue = ((syntax == GPBFileSyntaxProto2) - || (value != (int32_t)0) - || (field->containingOneof_ != NULL)); - GPBSetHasIvarField(self, field, hasValue); + // If the value is zero, then we only count the field as "set" if the field + // shouldn't auto clear on zero. + BOOL hasValue = ((value != (int32_t)0) + || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); GPBBecomeVisibleToAutocreator(self); } @@ -881,6 +893,9 @@ void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, uint32_t GPBGetMessageUInt32Field(GPBMessage *self, GPBFieldDescriptor *field) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeUInt32), @"Attempting to get value of uint32_t from field %@ " @@ -902,15 +917,10 @@ void GPBSetMessageUInt32Field(GPBMessage *self, GPBFieldDescriptor *field, uint32_t value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetUInt32IvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - uint32_t value, - GPBFileSyntax syntax) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeUInt32), @"Attempting to set field %@ of %@ which is of type %@ with " @@ -918,10 +928,16 @@ void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif + GPBSetUInt32IvarWithFieldPrivate(self, field, value); +} + +void GPBSetUInt32IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + uint32_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; + GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -932,14 +948,13 @@ void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, if (self->messageStorage_ == NULL) return; #endif uint8_t *storage = (uint8_t *)self->messageStorage_; - uint32_t *typePtr = (uint32_t *)&storage[field->description_->offset]; + uint32_t *typePtr = (uint32_t *)&storage[fieldDesc->offset]; *typePtr = value; - // proto2: any value counts as having been set; proto3, it - // has to be a non zero value or be in a oneof. - BOOL hasValue = ((syntax == GPBFileSyntaxProto2) - || (value != (uint32_t)0) - || (field->containingOneof_ != NULL)); - GPBSetHasIvarField(self, field, hasValue); + // If the value is zero, then we only count the field as "set" if the field + // shouldn't auto clear on zero. + BOOL hasValue = ((value != (uint32_t)0) + || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); GPBBecomeVisibleToAutocreator(self); } @@ -951,6 +966,9 @@ void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, int64_t GPBGetMessageInt64Field(GPBMessage *self, GPBFieldDescriptor *field) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeInt64), @"Attempting to get value of int64_t from field %@ " @@ -972,15 +990,10 @@ void GPBSetMessageInt64Field(GPBMessage *self, GPBFieldDescriptor *field, int64_t value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetInt64IvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - int64_t value, - GPBFileSyntax syntax) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeInt64), @"Attempting to set field %@ of %@ which is of type %@ with " @@ -988,10 +1001,16 @@ void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif + GPBSetInt64IvarWithFieldPrivate(self, field, value); +} + +void GPBSetInt64IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + int64_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; + GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1002,14 +1021,13 @@ void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, if (self->messageStorage_ == NULL) return; #endif uint8_t *storage = (uint8_t *)self->messageStorage_; - int64_t *typePtr = (int64_t *)&storage[field->description_->offset]; + int64_t *typePtr = (int64_t *)&storage[fieldDesc->offset]; *typePtr = value; - // proto2: any value counts as having been set; proto3, it - // has to be a non zero value or be in a oneof. - BOOL hasValue = ((syntax == GPBFileSyntaxProto2) - || (value != (int64_t)0) - || (field->containingOneof_ != NULL)); - GPBSetHasIvarField(self, field, hasValue); + // If the value is zero, then we only count the field as "set" if the field + // shouldn't auto clear on zero. + BOOL hasValue = ((value != (int64_t)0) + || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); GPBBecomeVisibleToAutocreator(self); } @@ -1021,6 +1039,9 @@ void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, uint64_t GPBGetMessageUInt64Field(GPBMessage *self, GPBFieldDescriptor *field) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeUInt64), @"Attempting to get value of uint64_t from field %@ " @@ -1042,15 +1063,10 @@ void GPBSetMessageUInt64Field(GPBMessage *self, GPBFieldDescriptor *field, uint64_t value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetUInt64IvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - uint64_t value, - GPBFileSyntax syntax) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeUInt64), @"Attempting to set field %@ of %@ which is of type %@ with " @@ -1058,10 +1074,16 @@ void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif + GPBSetUInt64IvarWithFieldPrivate(self, field, value); +} + +void GPBSetUInt64IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + uint64_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; + GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1072,14 +1094,13 @@ void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, if (self->messageStorage_ == NULL) return; #endif uint8_t *storage = (uint8_t *)self->messageStorage_; - uint64_t *typePtr = (uint64_t *)&storage[field->description_->offset]; + uint64_t *typePtr = (uint64_t *)&storage[fieldDesc->offset]; *typePtr = value; - // proto2: any value counts as having been set; proto3, it - // has to be a non zero value or be in a oneof. - BOOL hasValue = ((syntax == GPBFileSyntaxProto2) - || (value != (uint64_t)0) - || (field->containingOneof_ != NULL)); - GPBSetHasIvarField(self, field, hasValue); + // If the value is zero, then we only count the field as "set" if the field + // shouldn't auto clear on zero. + BOOL hasValue = ((value != (uint64_t)0) + || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); GPBBecomeVisibleToAutocreator(self); } @@ -1091,6 +1112,9 @@ void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, float GPBGetMessageFloatField(GPBMessage *self, GPBFieldDescriptor *field) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeFloat), @"Attempting to get value of float from field %@ " @@ -1112,15 +1136,10 @@ void GPBSetMessageFloatField(GPBMessage *self, GPBFieldDescriptor *field, float value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetFloatIvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - float value, - GPBFileSyntax syntax) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeFloat), @"Attempting to set field %@ of %@ which is of type %@ with " @@ -1128,10 +1147,16 @@ void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif + GPBSetFloatIvarWithFieldPrivate(self, field, value); +} + +void GPBSetFloatIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + float value) { GPBOneofDescriptor *oneof = field->containingOneof_; + GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1142,14 +1167,13 @@ void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, if (self->messageStorage_ == NULL) return; #endif uint8_t *storage = (uint8_t *)self->messageStorage_; - float *typePtr = (float *)&storage[field->description_->offset]; + float *typePtr = (float *)&storage[fieldDesc->offset]; *typePtr = value; - // proto2: any value counts as having been set; proto3, it - // has to be a non zero value or be in a oneof. - BOOL hasValue = ((syntax == GPBFileSyntaxProto2) - || (value != (float)0) - || (field->containingOneof_ != NULL)); - GPBSetHasIvarField(self, field, hasValue); + // If the value is zero, then we only count the field as "set" if the field + // shouldn't auto clear on zero. + BOOL hasValue = ((value != (float)0) + || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); GPBBecomeVisibleToAutocreator(self); } @@ -1161,6 +1185,9 @@ void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, double GPBGetMessageDoubleField(GPBMessage *self, GPBFieldDescriptor *field) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeDouble), @"Attempting to get value of double from field %@ " @@ -1182,15 +1209,10 @@ void GPBSetMessageDoubleField(GPBMessage *self, GPBFieldDescriptor *field, double value) { if (self == nil || field == nil) return; - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetDoubleIvarWithFieldInternal(self, field, value, syntax); -} - -void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - double value, - GPBFileSyntax syntax) { #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] fieldWithNumber:field.number] == field, + @"FieldDescriptor %@ doesn't appear to be for %@ messages.", + field.name, [self class]); NSCAssert(DataTypesEquivalent(GPBGetFieldDataType(field), GPBDataTypeDouble), @"Attempting to set field %@ of %@ which is of type %@ with " @@ -1198,10 +1220,16 @@ void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif + GPBSetDoubleIvarWithFieldPrivate(self, field, value); +} + +void GPBSetDoubleIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + double value) { GPBOneofDescriptor *oneof = field->containingOneof_; + GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1212,14 +1240,13 @@ void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, if (self->messageStorage_ == NULL) return; #endif uint8_t *storage = (uint8_t *)self->messageStorage_; - double *typePtr = (double *)&storage[field->description_->offset]; + double *typePtr = (double *)&storage[fieldDesc->offset]; *typePtr = value; - // proto2: any value counts as having been set; proto3, it - // has to be a non zero value or be in a oneof. - BOOL hasValue = ((syntax == GPBFileSyntaxProto2) - || (value != (double)0) - || (field->containingOneof_ != NULL)); - GPBSetHasIvarField(self, field, hasValue); + // If the value is zero, then we only count the field as "set" if the field + // shouldn't auto clear on zero. + BOOL hasValue = ((value != (double)0) + || ((fieldDesc->flags & GPBFieldClearHasIvarOnZero) == 0)); + GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, hasValue); GPBBecomeVisibleToAutocreator(self); } @@ -2212,8 +2239,36 @@ NSString *GPBDecodeTextFormatName(const uint8_t *decodeData, int32_t key, return result; } +#pragma mark Legacy methods old generated code calls + +// Shim from the older generated code into the runtime. +void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, + GPBFieldDescriptor *field, + int32_t value, + GPBFileSyntax syntax) { +#pragma unused(syntax) + GPBSetMessageInt32Field(self, field, value); +} + +void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, + int32_t oneofHasIndex, uint32_t fieldNumberNotToClear) { +#pragma unused(fieldNumberNotToClear) + #if defined(DEBUG) && DEBUG + NSCAssert([[self descriptor] oneofWithName:oneof.name] == oneof, + @"OneofDescriptor %@ doesn't appear to be for %@ messages.", + oneof.name, [self class]); + GPBFieldDescriptor *firstField = oneof->fields_[0]; + NSCAssert(firstField->description_->hasIndex == oneofHasIndex, + @"Internal error, oneofHasIndex (%d) doesn't match (%d).", + firstField->description_->hasIndex, oneofHasIndex); + #endif + GPBMaybeClearOneofPrivate(self, oneof, oneofHasIndex, 0); +} + #pragma clang diagnostic pop +#pragma mark Misc Helpers + BOOL GPBClassHasSel(Class aClass, SEL sel) { // NOTE: We have to use class_copyMethodList, all other runtime method // lookups actually also resolve the method implementation and this diff --git a/objectivec/GPBUtilities_PackagePrivate.h b/objectivec/GPBUtilities_PackagePrivate.h index a20789632d..9c29c39c0b 100644 --- a/objectivec/GPBUtilities_PackagePrivate.h +++ b/objectivec/GPBUtilities_PackagePrivate.h @@ -206,110 +206,83 @@ GPBGetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field) { GPBMessageFieldDescription *fieldDesc = field->description_; return GPBGetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number); } -GPB_INLINE void GPBSetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field, - BOOL value) { - GPBMessageFieldDescription *fieldDesc = field->description_; - GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, value); -} - -void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, - int32_t oneofHasIndex, uint32_t fieldNumberNotToClear); #pragma clang diagnostic pop //%PDDM-DEFINE GPB_IVAR_SET_DECL(NAME, TYPE) -//%void GPBSet##NAME##IvarWithFieldInternal(GPBMessage *self, -//% NAME$S GPBFieldDescriptor *field, -//% NAME$S TYPE value, -//% NAME$S GPBFileSyntax syntax); +//%void GPBSet##NAME##IvarWithFieldPrivate(GPBMessage *self, +//% NAME$S GPBFieldDescriptor *field, +//% NAME$S TYPE value); //%PDDM-EXPAND GPB_IVAR_SET_DECL(Bool, BOOL) // This block of code is generated, do not edit it directly. // clang-format off -void GPBSetBoolIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - BOOL value, - GPBFileSyntax syntax); +void GPBSetBoolIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + BOOL value); // clang-format on //%PDDM-EXPAND GPB_IVAR_SET_DECL(Int32, int32_t) // This block of code is generated, do not edit it directly. // clang-format off -void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - int32_t value, - GPBFileSyntax syntax); +void GPBSetInt32IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + int32_t value); // clang-format on //%PDDM-EXPAND GPB_IVAR_SET_DECL(UInt32, uint32_t) // This block of code is generated, do not edit it directly. // clang-format off -void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - uint32_t value, - GPBFileSyntax syntax); +void GPBSetUInt32IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + uint32_t value); // clang-format on //%PDDM-EXPAND GPB_IVAR_SET_DECL(Int64, int64_t) // This block of code is generated, do not edit it directly. // clang-format off -void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - int64_t value, - GPBFileSyntax syntax); +void GPBSetInt64IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + int64_t value); // clang-format on //%PDDM-EXPAND GPB_IVAR_SET_DECL(UInt64, uint64_t) // This block of code is generated, do not edit it directly. // clang-format off -void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - uint64_t value, - GPBFileSyntax syntax); +void GPBSetUInt64IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + uint64_t value); // clang-format on //%PDDM-EXPAND GPB_IVAR_SET_DECL(Float, float) // This block of code is generated, do not edit it directly. // clang-format off -void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - float value, - GPBFileSyntax syntax); +void GPBSetFloatIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + float value); // clang-format on //%PDDM-EXPAND GPB_IVAR_SET_DECL(Double, double) // This block of code is generated, do not edit it directly. // clang-format off -void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - double value, - GPBFileSyntax syntax); -// clang-format on -//%PDDM-EXPAND GPB_IVAR_SET_DECL(Enum, int32_t) -// This block of code is generated, do not edit it directly. -// clang-format off - -void GPBSetEnumIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - int32_t value, - GPBFileSyntax syntax); +void GPBSetDoubleIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + double value); // clang-format on -//%PDDM-EXPAND-END (8 expansions) +//%PDDM-EXPAND-END (7 expansions) -int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - GPBFileSyntax syntax); +void GPBSetEnumIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + int32_t value); id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field); -void GPBSetObjectIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, id value, - GPBFileSyntax syntax); -void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - id __attribute__((ns_consumed)) - value, - GPBFileSyntax syntax); +void GPBSetObjectIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, id value); +void GPBSetRetainedObjectIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + id __attribute__((ns_consumed)) + value); // GPBGetObjectIvarWithField will automatically create the field (message) if // it doesn't exist. GPBGetObjectIvarWithFieldNoAutocreate will return nil. @@ -336,6 +309,15 @@ const char *GPBMessageEncodingForSelector(SEL selector, BOOL instanceSel); NSString *GPBDecodeTextFormatName(const uint8_t *decodeData, int32_t key, NSString *inputString); + +// Shims from the older generated code into the runtime. +void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, + GPBFieldDescriptor *field, + int32_t value, + GPBFileSyntax syntax); +void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, + int32_t oneofHasIndex, uint32_t fieldNumberNotToClear); + // A series of selectors that are used solely to get @encoding values // for them by the dynamic protobuf runtime code. See // GPBMessageEncodingForSelector for details. GPBRootObject conforms to diff --git a/objectivec/GPBWrappers.pbobjc.h b/objectivec/GPBWrappers.pbobjc.h index cc377f2920..713bafc89b 100644 --- a/objectivec/GPBWrappers.pbobjc.h +++ b/objectivec/GPBWrappers.pbobjc.h @@ -17,10 +17,10 @@ #import "GPBRootObject.h" #endif -#if GOOGLE_PROTOBUF_OBJC_VERSION < 30003 +#if GOOGLE_PROTOBUF_OBJC_VERSION < 30004 #error This file was generated by a newer version of protoc which is incompatible with your Protocol Buffer library sources. #endif -#if 30003 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION +#if 30004 < GOOGLE_PROTOBUF_OBJC_MIN_SUPPORTED_VERSION #error This file was generated by an older version of protoc which is incompatible with your Protocol Buffer library sources. #endif diff --git a/objectivec/GPBWrappers.pbobjc.m b/objectivec/GPBWrappers.pbobjc.m index 5de7a83a2f..32201d4d83 100644 --- a/objectivec/GPBWrappers.pbobjc.m +++ b/objectivec/GPBWrappers.pbobjc.m @@ -70,7 +70,7 @@ typedef struct GPBDoubleValue__storage_ { .number = GPBDoubleValue_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBDoubleValue__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeDouble, }, }; @@ -81,7 +81,7 @@ typedef struct GPBDoubleValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBDoubleValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -115,7 +115,7 @@ typedef struct GPBFloatValue__storage_ { .number = GPBFloatValue_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBFloatValue__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeFloat, }, }; @@ -126,7 +126,7 @@ typedef struct GPBFloatValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBFloatValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -160,7 +160,7 @@ typedef struct GPBInt64Value__storage_ { .number = GPBInt64Value_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBInt64Value__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt64, }, }; @@ -171,7 +171,7 @@ typedef struct GPBInt64Value__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBInt64Value__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -205,7 +205,7 @@ typedef struct GPBUInt64Value__storage_ { .number = GPBUInt64Value_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBUInt64Value__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeUInt64, }, }; @@ -216,7 +216,7 @@ typedef struct GPBUInt64Value__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBUInt64Value__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -250,7 +250,7 @@ typedef struct GPBInt32Value__storage_ { .number = GPBInt32Value_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBInt32Value__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeInt32, }, }; @@ -261,7 +261,7 @@ typedef struct GPBInt32Value__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBInt32Value__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -295,7 +295,7 @@ typedef struct GPBUInt32Value__storage_ { .number = GPBUInt32Value_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBUInt32Value__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeUInt32, }, }; @@ -306,7 +306,7 @@ typedef struct GPBUInt32Value__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBUInt32Value__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -339,7 +339,7 @@ typedef struct GPBBoolValue__storage_ { .number = GPBBoolValue_FieldNumber_Value, .hasIndex = 0, .offset = 1, // Stored in _has_storage_ to save space. - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeBool, }, }; @@ -350,7 +350,7 @@ typedef struct GPBBoolValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBBoolValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -384,7 +384,7 @@ typedef struct GPBStringValue__storage_ { .number = GPBStringValue_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBStringValue__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeString, }, }; @@ -395,7 +395,7 @@ typedef struct GPBStringValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBStringValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG @@ -429,7 +429,7 @@ typedef struct GPBBytesValue__storage_ { .number = GPBBytesValue_FieldNumber_Value, .hasIndex = 0, .offset = (uint32_t)offsetof(GPBBytesValue__storage_, value), - .flags = GPBFieldOptional, + .flags = (GPBFieldFlags)(GPBFieldOptional | GPBFieldClearHasIvarOnZero), .dataType = GPBDataTypeBytes, }, }; @@ -440,7 +440,7 @@ typedef struct GPBBytesValue__storage_ { fields:fields fieldCount:(uint32_t)(sizeof(fields) / sizeof(GPBMessageFieldDescription)) storageSize:sizeof(GPBBytesValue__storage_) - flags:GPBDescriptorInitializationFlag_UsesClassRefs]; + flags:(GPBDescriptorInitializationFlags)(GPBDescriptorInitializationFlag_UsesClassRefs | GPBDescriptorInitializationFlag_Proto3OptionalKnown)]; #if defined(DEBUG) && DEBUG NSAssert(descriptor == nil, @"Startup recursed!"); #endif // DEBUG diff --git a/objectivec/Tests/GPBMessageTests+Runtime.m b/objectivec/Tests/GPBMessageTests+Runtime.m index 190ff514d2..1dac79754e 100644 --- a/objectivec/Tests/GPBMessageTests+Runtime.m +++ b/objectivec/Tests/GPBMessageTests+Runtime.m @@ -252,7 +252,7 @@ // build the selector, i.e. - repeatedInt32Array_Count SEL countSel = NSSelectorFromString( [NSString stringWithFormat:@"repeated%@Array_Count", name]); - XCTAssertTrue([Message2 instancesRespondToSelector:countSel], @"field: %@", + XCTAssertTrue([Message3 instancesRespondToSelector:countSel], @"field: %@", name); } @@ -264,12 +264,29 @@ NSSelectorFromString([NSString stringWithFormat:@"hasOneof%@", name]); SEL setHasSel = NSSelectorFromString( [NSString stringWithFormat:@"setHasOneof%@:", name]); - XCTAssertFalse([Message2 instancesRespondToSelector:hasSel], @"field: %@", + XCTAssertFalse([Message3 instancesRespondToSelector:hasSel], @"field: %@", name); - XCTAssertFalse([Message2 instancesRespondToSelector:setHasSel], + XCTAssertFalse([Message3 instancesRespondToSelector:setHasSel], @"field: %@", name); } + // Single Optional fields + // - has*/setHas* thanks to the optional keyword in proto3, they exist + // for primitive types. + // - has*/setHas* valid for Message. + + for (NSString *name in names) { + // build the selector, i.e. - hasOptionalInt32/setHasOptionalInt32: + SEL hasSel = NSSelectorFromString( + [NSString stringWithFormat:@"hasOptional%@", name]); + SEL setHasSel = NSSelectorFromString( + [NSString stringWithFormat:@"setHasOptional%@:", name]); + XCTAssertTrue([Message3Optional instancesRespondToSelector:hasSel], @"field: %@", + name); + XCTAssertTrue([Message3Optional instancesRespondToSelector:setHasSel], + @"field: %@", name); + } + // map<> fields // - no has*/setHas* // - *Count @@ -302,14 +319,14 @@ [NSString stringWithFormat:@"hasMap%@", name]); SEL setHasSel = NSSelectorFromString( [NSString stringWithFormat:@"setHasMap%@:", name]); - XCTAssertFalse([Message2 instancesRespondToSelector:hasSel], @"field: %@", + XCTAssertFalse([Message3 instancesRespondToSelector:hasSel], @"field: %@", name); - XCTAssertFalse([Message2 instancesRespondToSelector:setHasSel], + XCTAssertFalse([Message3 instancesRespondToSelector:setHasSel], @"field: %@", name); // build the selector, i.e. - mapInt32Int32Count SEL countSel = NSSelectorFromString( [NSString stringWithFormat:@"map%@_Count", name]); - XCTAssertTrue([Message2 instancesRespondToSelector:countSel], @"field: %@", + XCTAssertTrue([Message3 instancesRespondToSelector:countSel], @"field: %@", name); } } @@ -1002,6 +1019,249 @@ //%PDDM-EXPAND-END PROTO3_TEST_HAS_FIELDS() } +- (void)testProto3SingleOptionalFieldHasBehavior { + // + // Setting to any value including the default (0) should result in true. + // + +//%PDDM-DEFINE PROTO3_TEST_OPTIONAL_HAS_FIELD(FIELD, NON_ZERO_VALUE, ZERO_VALUE) +//% { // optional##FIELD +//% Message3Optional *msg = [[Message3Optional alloc] init]; +//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD)); +//% msg.optional##FIELD = NON_ZERO_VALUE; +//% XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD)); +//% msg.hasOptional##FIELD = NO; +//% XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD)); +//% msg.optional##FIELD = ZERO_VALUE; +//% XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_Optional##FIELD)); +//% [msg release]; +//% } +//% +//%PDDM-DEFINE PROTO3_TEST_OPTIONAL_HAS_FIELDS() +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Int32, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Int64, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Uint32, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Uint64, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sint32, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sint64, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Fixed32, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Fixed64, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sfixed32, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Sfixed64, 1, 0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Float, 1.0f, 0.0f) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Double, 1.0, 0.0) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Bool, YES, NO) +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(String, @"foo", @"") +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Bytes, [@"foo" dataUsingEncoding:NSUTF8StringEncoding], [NSData data]) +//% // +//% // Test doesn't apply to optionalMessage (no groups in proto3). +//% // +//% +//%PROTO3_TEST_OPTIONAL_HAS_FIELD(Enum, Message3Optional_Enum_Bar, Message3Optional_Enum_Foo) +//%PDDM-EXPAND PROTO3_TEST_OPTIONAL_HAS_FIELDS() +// This block of code is generated, do not edit it directly. +// clang-format off + + { // optionalInt32 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32)); + msg.optionalInt32 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32)); + msg.hasOptionalInt32 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32)); + msg.optionalInt32 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt32)); + [msg release]; + } + + { // optionalInt64 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64)); + msg.optionalInt64 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64)); + msg.hasOptionalInt64 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64)); + msg.optionalInt64 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalInt64)); + [msg release]; + } + + { // optionalUint32 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32)); + msg.optionalUint32 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32)); + msg.hasOptionalUint32 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32)); + msg.optionalUint32 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint32)); + [msg release]; + } + + { // optionalUint64 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64)); + msg.optionalUint64 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64)); + msg.hasOptionalUint64 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64)); + msg.optionalUint64 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalUint64)); + [msg release]; + } + + { // optionalSint32 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32)); + msg.optionalSint32 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32)); + msg.hasOptionalSint32 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32)); + msg.optionalSint32 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint32)); + [msg release]; + } + + { // optionalSint64 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64)); + msg.optionalSint64 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64)); + msg.hasOptionalSint64 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64)); + msg.optionalSint64 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSint64)); + [msg release]; + } + + { // optionalFixed32 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32)); + msg.optionalFixed32 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32)); + msg.hasOptionalFixed32 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32)); + msg.optionalFixed32 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed32)); + [msg release]; + } + + { // optionalFixed64 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64)); + msg.optionalFixed64 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64)); + msg.hasOptionalFixed64 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64)); + msg.optionalFixed64 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFixed64)); + [msg release]; + } + + { // optionalSfixed32 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32)); + msg.optionalSfixed32 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32)); + msg.hasOptionalSfixed32 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32)); + msg.optionalSfixed32 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed32)); + [msg release]; + } + + { // optionalSfixed64 + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64)); + msg.optionalSfixed64 = 1; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64)); + msg.hasOptionalSfixed64 = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64)); + msg.optionalSfixed64 = 0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalSfixed64)); + [msg release]; + } + + { // optionalFloat + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat)); + msg.optionalFloat = 1.0f; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat)); + msg.hasOptionalFloat = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat)); + msg.optionalFloat = 0.0f; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalFloat)); + [msg release]; + } + + { // optionalDouble + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble)); + msg.optionalDouble = 1.0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble)); + msg.hasOptionalDouble = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble)); + msg.optionalDouble = 0.0; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalDouble)); + [msg release]; + } + + { // optionalBool + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool)); + msg.optionalBool = YES; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool)); + msg.hasOptionalBool = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool)); + msg.optionalBool = NO; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBool)); + [msg release]; + } + + { // optionalString + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString)); + msg.optionalString = @"foo"; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString)); + msg.hasOptionalString = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString)); + msg.optionalString = @""; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalString)); + [msg release]; + } + + { // optionalBytes + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes)); + msg.optionalBytes = [@"foo" dataUsingEncoding:NSUTF8StringEncoding]; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes)); + msg.hasOptionalBytes = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes)); + msg.optionalBytes = [NSData data]; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalBytes)); + [msg release]; + } + + // + // Test doesn't apply to optionalMessage (no groups in proto3). + // + + { // optionalEnum + Message3Optional *msg = [[Message3Optional alloc] init]; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum)); + msg.optionalEnum = Message3Optional_Enum_Bar; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum)); + msg.hasOptionalEnum = NO; + XCTAssertFalse(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum)); + msg.optionalEnum = Message3Optional_Enum_Foo; + XCTAssertTrue(GPBMessageHasFieldNumberSet(msg, Message3Optional_FieldNumber_OptionalEnum)); + [msg release]; + } + +// clang-format on +//%PDDM-EXPAND-END PROTO3_TEST_OPTIONAL_HAS_FIELDS() +} + - (void)testAccessingProto2UnknownEnumValues { Message2 *msg = [[Message2 alloc] init]; diff --git a/objectivec/Tests/GPBMessageTests+Serialization.m b/objectivec/Tests/GPBMessageTests+Serialization.m index ef6e589e39..6f20797aee 100644 --- a/objectivec/Tests/GPBMessageTests+Serialization.m +++ b/objectivec/Tests/GPBMessageTests+Serialization.m @@ -109,6 +109,317 @@ [msg release]; } +- (void)testProto3SerializationHandlingOptionals { + // + // Proto3 optionals should be just like proto2, zero values also get serialized. + // + +//%PDDM-DEFINE PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(FIELD, ZERO_VALUE, EXPECTED_LEN) +//% { // optional##FIELD +//% Message3Optional *msg = [[Message3Optional alloc] init]; +//% NSData *data = [msg data]; +//% XCTAssertEqual([data length], 0U); +//% msg.optional##FIELD = ZERO_VALUE; +//% data = [msg data]; +//% XCTAssertEqual(data.length, EXPECTED_LEN); +//% NSError *err = nil; +//% Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; +//% XCTAssertNotNil(msg2); +//% XCTAssertNil(err); +//% XCTAssertTrue(msg2.hasOptional##FIELD); +//% XCTAssertEqualObjects(msg, msg2); +//% [msg release]; +//% } +//% +//%PDDM-DEFINE PROTO3_TEST_SERIALIZE_OPTIONAL_FIELDS() +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Int32, 0, 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Int64, 0, 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Uint32, 0, 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Uint64, 0, 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sint32, 0, 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sint64, 0, 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Fixed32, 0, 5) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Fixed64, 0, 9) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sfixed32, 0, 5) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Sfixed64, 0, 9) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Float, 0.0f, 5) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Double, 0.0, 9) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Bool, NO, 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(String, @"", 2) +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Bytes, [NSData data], 2) +//% // +//% // Test doesn't apply to optionalMessage (no groups in proto3). +//% // +//% +//%PROTO3_TEST_SERIALIZE_OPTIONAL_FIELD(Enum, Message3Optional_Enum_Foo, 3) +//%PDDM-EXPAND PROTO3_TEST_SERIALIZE_OPTIONAL_FIELDS() +// This block of code is generated, do not edit it directly. +// clang-format off + + { // optionalInt32 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalInt32 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalInt32); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalInt64 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalInt64 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalInt64); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalUint32 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalUint32 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalUint32); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalUint64 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalUint64 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalUint64); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalSint32 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalSint32 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalSint32); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalSint64 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalSint64 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalSint64); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalFixed32 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalFixed32 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 5); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalFixed32); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalFixed64 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalFixed64 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 9); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalFixed64); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalSfixed32 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalSfixed32 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 5); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalSfixed32); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalSfixed64 + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalSfixed64 = 0; + data = [msg data]; + XCTAssertEqual(data.length, 9); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalSfixed64); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalFloat + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalFloat = 0.0f; + data = [msg data]; + XCTAssertEqual(data.length, 5); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalFloat); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalDouble + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalDouble = 0.0; + data = [msg data]; + XCTAssertEqual(data.length, 9); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalDouble); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalBool + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalBool = NO; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalBool); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalString + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalString = @""; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalString); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + { // optionalBytes + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalBytes = [NSData data]; + data = [msg data]; + XCTAssertEqual(data.length, 2); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalBytes); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + + // + // Test doesn't apply to optionalMessage (no groups in proto3). + // + + { // optionalEnum + Message3Optional *msg = [[Message3Optional alloc] init]; + NSData *data = [msg data]; + XCTAssertEqual([data length], 0U); + msg.optionalEnum = Message3Optional_Enum_Foo; + data = [msg data]; + XCTAssertEqual(data.length, 3); + NSError *err = nil; + Message3Optional *msg2 = [Message3Optional parseFromData:data error:&err]; + XCTAssertNotNil(msg2); + XCTAssertNil(err); + XCTAssertTrue(msg2.hasOptionalEnum); + XCTAssertEqualObjects(msg, msg2); + [msg release]; + } + +// clang-format on +//%PDDM-EXPAND-END PROTO3_TEST_SERIALIZE_OPTIONAL_FIELDS() +} + - (void)testProto2UnknownEnumToUnknownField { Message3 *orig = [[Message3 alloc] init]; diff --git a/objectivec/Tests/GPBSwiftTests.swift b/objectivec/Tests/GPBSwiftTests.swift index cedf0e487e..03d7510680 100644 --- a/objectivec/Tests/GPBSwiftTests.swift +++ b/objectivec/Tests/GPBSwiftTests.swift @@ -355,7 +355,7 @@ class GPBBridgeTests: XCTestCase { msg.oneof = nil XCTAssertEqual(msg.oneof.optionalInt32, Int32(0)) // Default XCTAssertEqual(msg.oOneOfCase, Message2_O_OneOfCase.gpbUnsetOneOfCase) -} + } func testProto3OneOfSupport() { let msg = Message3() diff --git a/objectivec/Tests/unittest_runtime_proto3.proto b/objectivec/Tests/unittest_runtime_proto3.proto index ad2e3620d3..c2ee5fb52f 100644 --- a/objectivec/Tests/unittest_runtime_proto3.proto +++ b/objectivec/Tests/unittest_runtime_proto3.proto @@ -119,3 +119,31 @@ message Message3 { map map_int32_enum = 87; map map_int32_message = 88; } + +message Message3Optional { + enum Enum { + FOO = 0; + BAR = 1; + BAZ = 2; + EXTRA_3 = 30; + } + + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional sint32 optional_sint32 = 5; + optional sint64 optional_sint64 = 6; + optional fixed32 optional_fixed32 = 7; + optional fixed64 optional_fixed64 = 8; + optional sfixed32 optional_sfixed32 = 9; + optional sfixed64 optional_sfixed64 = 10; + optional float optional_float = 11; + optional double optional_double = 12; + optional bool optional_bool = 13; + optional string optional_string = 14; + optional bytes optional_bytes = 15; + // No 'group' in proto3. + optional Message3 optional_message = 18; + optional Enum optional_enum = 19; +} diff --git a/php/src/Google/Protobuf/Internal/FieldDescriptorProto.php b/php/src/Google/Protobuf/Internal/FieldDescriptorProto.php index e00488e12e..b43d988141 100644 --- a/php/src/Google/Protobuf/Internal/FieldDescriptorProto.php +++ b/php/src/Google/Protobuf/Internal/FieldDescriptorProto.php @@ -96,10 +96,20 @@ class FieldDescriptorProto extends \Google\Protobuf\Internal\Message /** * If true, this is a proto3 "optional". When a proto3 field is optional, it * tracks presence regardless of field type. - * For message fields this doesn't create any semantic change, since - * non-repeated message fields always track presence. However it still + * When proto3_optional is true, this field must be belong to a oneof to + * signal to old proto3 clients that presence is tracked for this field. This + * oneof is known as a "synthetic" oneof, and this field must be its sole + * member (each proto3 optional field gets its own synthetic oneof). Synthetic + * oneofs exist in the descriptor only, and do not generate any API. Synthetic + * oneofs must be ordered after all "real" oneofs. + * For message fields, proto3_optional doesn't create any semantic change, + * since non-repeated message fields always track presence. However it still * indicates the semantic detail of whether the user wrote "optional" or not. - * This can be useful for round-tripping the .proto file. + * This can be useful for round-tripping the .proto file. For consistency we + * give message fields a synthetic oneof also, even though it is not required + * to track presence. This is especially important because the parser can't + * tell if a field is a message or an enum, so it must always create a + * synthetic oneof. * Proto2 optional fields do not set this flag, because they already indicate * optional with `LABEL_OPTIONAL`. * @@ -147,10 +157,20 @@ class FieldDescriptorProto extends \Google\Protobuf\Internal\Message * @type bool $proto3_optional * If true, this is a proto3 "optional". When a proto3 field is optional, it * tracks presence regardless of field type. - * For message fields this doesn't create any semantic change, since - * non-repeated message fields always track presence. However it still + * When proto3_optional is true, this field must be belong to a oneof to + * signal to old proto3 clients that presence is tracked for this field. This + * oneof is known as a "synthetic" oneof, and this field must be its sole + * member (each proto3 optional field gets its own synthetic oneof). Synthetic + * oneofs exist in the descriptor only, and do not generate any API. Synthetic + * oneofs must be ordered after all "real" oneofs. + * For message fields, proto3_optional doesn't create any semantic change, + * since non-repeated message fields always track presence. However it still * indicates the semantic detail of whether the user wrote "optional" or not. - * This can be useful for round-tripping the .proto file. + * This can be useful for round-tripping the .proto file. For consistency we + * give message fields a synthetic oneof also, even though it is not required + * to track presence. This is especially important because the parser can't + * tell if a field is a message or an enum, so it must always create a + * synthetic oneof. * Proto2 optional fields do not set this flag, because they already indicate * optional with `LABEL_OPTIONAL`. * } @@ -495,10 +515,20 @@ class FieldDescriptorProto extends \Google\Protobuf\Internal\Message /** * If true, this is a proto3 "optional". When a proto3 field is optional, it * tracks presence regardless of field type. - * For message fields this doesn't create any semantic change, since - * non-repeated message fields always track presence. However it still + * When proto3_optional is true, this field must be belong to a oneof to + * signal to old proto3 clients that presence is tracked for this field. This + * oneof is known as a "synthetic" oneof, and this field must be its sole + * member (each proto3 optional field gets its own synthetic oneof). Synthetic + * oneofs exist in the descriptor only, and do not generate any API. Synthetic + * oneofs must be ordered after all "real" oneofs. + * For message fields, proto3_optional doesn't create any semantic change, + * since non-repeated message fields always track presence. However it still * indicates the semantic detail of whether the user wrote "optional" or not. - * This can be useful for round-tripping the .proto file. + * This can be useful for round-tripping the .proto file. For consistency we + * give message fields a synthetic oneof also, even though it is not required + * to track presence. This is especially important because the parser can't + * tell if a field is a message or an enum, so it must always create a + * synthetic oneof. * Proto2 optional fields do not set this flag, because they already indicate * optional with `LABEL_OPTIONAL`. * @@ -513,10 +543,20 @@ class FieldDescriptorProto extends \Google\Protobuf\Internal\Message /** * If true, this is a proto3 "optional". When a proto3 field is optional, it * tracks presence regardless of field type. - * For message fields this doesn't create any semantic change, since - * non-repeated message fields always track presence. However it still + * When proto3_optional is true, this field must be belong to a oneof to + * signal to old proto3 clients that presence is tracked for this field. This + * oneof is known as a "synthetic" oneof, and this field must be its sole + * member (each proto3 optional field gets its own synthetic oneof). Synthetic + * oneofs exist in the descriptor only, and do not generate any API. Synthetic + * oneofs must be ordered after all "real" oneofs. + * For message fields, proto3_optional doesn't create any semantic change, + * since non-repeated message fields always track presence. However it still * indicates the semantic detail of whether the user wrote "optional" or not. - * This can be useful for round-tripping the .proto file. + * This can be useful for round-tripping the .proto file. For consistency we + * give message fields a synthetic oneof also, even though it is not required + * to track presence. This is especially important because the parser can't + * tell if a field is a message or an enum, so it must always create a + * synthetic oneof. * Proto2 optional fields do not set this flag, because they already indicate * optional with `LABEL_OPTIONAL`. * diff --git a/python/docs/generate_docs.py b/python/docs/generate_docs.py index 07debedd18..e024aaa479 100755 --- a/python/docs/generate_docs.py +++ b/python/docs/generate_docs.py @@ -67,12 +67,22 @@ DOCS_DIR = pathlib.Path(__file__).parent.resolve() PYTHON_DIR = DOCS_DIR.parent SOURCE_DIR = PYTHON_DIR / "google" / "protobuf" SOURCE_POSIX = SOURCE_DIR.as_posix() + +# Modules which are always included: +INCLUDED_MODULES = ( + "google.protobuf.internal.containers", +) + +# Packages to ignore, including all modules (unless in INCLUDED_MODULES): IGNORED_PACKAGES = ( "compiler", + "docs", "internal", "pyext", "util", ) + +# Ignored module stems in all packages (unless in INCLUDED_MODULES): IGNORED_MODULES = ( "any_test_pb2", "api_pb2", @@ -81,6 +91,7 @@ IGNORED_MODULES = ( "test_messages_proto3_pb2", "test_messages_proto2", ) + TOC_REGEX = re.compile( r"\.\. START REFTOC.*\.\. END REFTOC\.\n", flags=re.DOTALL, @@ -120,20 +131,28 @@ AUTOMODULE_TEMPLATE = """.. DO NOT EDIT, generated by generate_docs.py. def find_modules(): modules = [] for module_path in SOURCE_DIR.glob("**/*.py"): - package_posix = module_path.parent.as_posix() - if any(ignored in package_posix for ignored in IGNORED_PACKAGES): + # Determine the (dotted) relative package and module names. + package_path = module_path.parent.relative_to(PYTHON_DIR) + if package_path == SOURCE_DIR: + package_name = "" + module_name = module_path.stem + else: + package_name = package_path.as_posix().replace("/", ".") + module_name = package_name + "." + module_path.stem + + # Filter: first, accept anything in the whitelist; then, reject anything + # at package level, then module name level. + if any(include == module_name for include in INCLUDED_MODULES): + pass + elif any(ignored in package_name for ignored in IGNORED_PACKAGES): continue - if any(ignored in module_path.stem for ignored in IGNORED_MODULES): + elif any(ignored in module_path.stem for ignored in IGNORED_MODULES): continue - package_name = "google.protobuf{}".format( - package_posix[len(SOURCE_POSIX) :].replace("/", ".") - ) if module_path.name == "__init__.py": modules.append(package_name) else: - module_name = module_path.stem - modules.append("{}.{}".format(package_name, module_name)) + modules.append(module_name) return modules diff --git a/python/docs/google/protobuf/internal/containers.rst b/python/docs/google/protobuf/internal/containers.rst new file mode 100644 index 0000000000..c3b8e594a7 --- /dev/null +++ b/python/docs/google/protobuf/internal/containers.rst @@ -0,0 +1,21 @@ +.. DO NOT EDIT, generated by generate_docs.py. + +.. ifconfig:: build_env == 'readthedocs' + + .. warning:: + + You are reading the documentation for the `latest committed changes + `_ of + the `Protocol Buffers package for Python + `_. + Some features may not yet be released. Read the documentation for the + latest released package at `googleapis.dev + `_. + +google.protobuf.internal.containers +=================================== + +.. automodule:: google.protobuf.internal.containers + :members: + :inherited-members: + :undoc-members: diff --git a/python/docs/index.rst b/python/docs/index.rst index 57f7ce70ce..5535b398a9 100644 --- a/python/docs/index.rst +++ b/python/docs/index.rst @@ -38,6 +38,7 @@ Modules and Packages google/protobuf/duration_pb2 google/protobuf/empty_pb2 google/protobuf/field_mask_pb2 + google/protobuf/internal/containers google/protobuf/json_format google/protobuf/message google/protobuf/message_factory diff --git a/python/google/protobuf/pyext/message.cc b/python/google/protobuf/pyext/message.cc index e39764d0eb..41ac2cabb5 100644 --- a/python/google/protobuf/pyext/message.cc +++ b/python/google/protobuf/pyext/message.cc @@ -33,11 +33,14 @@ #include +#include // A Python header file. + #include #include #include #include -#include // A Python header file. + +#include #ifndef PyVarObject_HEAD_INIT #define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size, @@ -456,7 +459,7 @@ static PyObject* GetClassAttribute(CMessageClass *self, PyObject* name) { Py_ssize_t attr_size; static const char kSuffix[] = "_FIELD_NUMBER"; if (PyString_AsStringAndSize(name, &attr, &attr_size) >= 0 && - strings::EndsWith(StringPiece(attr, attr_size), kSuffix)) { + HasSuffixString(StringPiece(attr, attr_size), kSuffix)) { std::string field_name(attr, attr_size - sizeof(kSuffix) + 1); LowerString(&field_name); @@ -1424,8 +1427,7 @@ bool CheckHasPresence(const FieldDescriptor* field_descriptor, bool in_oneof) { return false; } - if (field_descriptor->containing_oneof() == NULL && - !field_descriptor->is_singular_with_presence()) { + if (!field_descriptor->has_presence()) { PyErr_Format(PyExc_ValueError, "Can't test non-optional, non-submessage field \"%s.%s\" for " "presence in proto3.", @@ -2168,19 +2170,21 @@ static PyObject* RichCompare(CMessage* self, PyObject* other, int opid) { // If other is not a message, it cannot be equal. if (!PyObject_TypeCheck(other, CMessage_Type)) { equals = false; - } - const google::protobuf::Message* other_message = - reinterpret_cast(other)->message; - // If messages don't have the same descriptors, they are not equal. - if (equals && - self->message->GetDescriptor() != other_message->GetDescriptor()) { - equals = false; - } - // Check the message contents. - if (equals && !google::protobuf::util::MessageDifferencer::Equals( - *self->message, - *reinterpret_cast(other)->message)) { - equals = false; + } else { + // Otherwise, we have a CMessage whose message we can inspect. + const google::protobuf::Message* other_message = + reinterpret_cast(other)->message; + // If messages don't have the same descriptors, they are not equal. + if (equals && + self->message->GetDescriptor() != other_message->GetDescriptor()) { + equals = false; + } + // Check the message contents. + if (equals && + !google::protobuf::util::MessageDifferencer::Equals( + *self->message, *reinterpret_cast(other)->message)) { + equals = false; + } } if (equals ^ (opid == Py_EQ)) { diff --git a/python/google/protobuf/text_format.py b/python/google/protobuf/text_format.py index 9c2b26720e..b0b22f4642 100644 --- a/python/google/protobuf/text_format.py +++ b/python/google/protobuf/text_format.py @@ -120,20 +120,21 @@ class TextWriter(object): return self._writer.getvalue() -def MessageToString(message, - as_utf8=False, - as_one_line=False, - use_short_repeated_primitives=False, - pointy_brackets=False, - use_index_order=False, - float_format=None, - double_format=None, - use_field_number=False, - descriptor_pool=None, - indent=0, - message_formatter=None, - print_unknown_fields=False, - force_colon=False): +def MessageToString( + message, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + indent=0, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): # type: (...) -> str """Convert protobuf message to text format. @@ -329,21 +330,22 @@ WIRETYPE_START_GROUP = 3 class _Printer(object): """Text format printer for protocol message.""" - def __init__(self, - out, - indent=0, - as_utf8=False, - as_one_line=False, - use_short_repeated_primitives=False, - pointy_brackets=False, - use_index_order=False, - float_format=None, - double_format=None, - use_field_number=False, - descriptor_pool=None, - message_formatter=None, - print_unknown_fields=False, - force_colon=False): + def __init__( + self, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): """Initialize the Printer. Double values can be formatted compactly with 15 digits of precision diff --git a/ruby/Rakefile b/ruby/Rakefile index ad70e3107c..2aa7743e20 100644 --- a/ruby/Rakefile +++ b/ruby/Rakefile @@ -70,13 +70,18 @@ else task 'gem:windows' do require 'rake_compiler_dock' - RakeCompilerDock.sh "bundle && IN_DOCKER=true rake cross native gem RUBY_CC_VERSION=2.6.0:2.5.0:2.4.0:2.3.0" + ['x86-mingw32', 'x64-mingw32', 'x86_64-linux', 'x86-linux'].each do |plat| + RakeCompilerDock.sh <<-"EOT", platform: plat + bundle && \ + IN_DOCKER=true rake native:#{plat} pkg/#{spec.full_name}-#{plat}.gem RUBY_CC_VERSION=2.7.0:2.6.0:2.5.0:2.4.0:2.3.0 + EOT + end end if RUBY_PLATFORM =~ /darwin/ task 'gem:native' do system "rake genproto" - system "rake cross native gem RUBY_CC_VERSION=2.6.0:2.5.1:2.4.0:2.3.0" + system "rake cross native gem RUBY_CC_VERSION=2.7.0:2.6.0:2.5.1:2.4.0:2.3.0" end else task 'gem:native' => [:genproto, 'gem:windows'] @@ -119,7 +124,7 @@ file "tests/test_ruby_package_proto2.rb" => "tests/test_ruby_package_proto2.prot end file "tests/basic_test.rb" => "tests/basic_test.proto" do |file_task| - sh "../src/protoc -I../src -I. --ruby_out=. tests/basic_test.proto" + sh "../src/protoc --experimental_allow_proto3_optional -I../src -I. --ruby_out=. tests/basic_test.proto" end file "tests/basic_test_proto2.rb" => "tests/basic_test_proto2.proto" do |file_task| diff --git a/ruby/compatibility_tests/v3.0.0/tests/repeated_field_test.rb b/ruby/compatibility_tests/v3.0.0/tests/repeated_field_test.rb index 4ebb731a83..b4a158f37c 100755 --- a/ruby/compatibility_tests/v3.0.0/tests/repeated_field_test.rb +++ b/ruby/compatibility_tests/v3.0.0/tests/repeated_field_test.rb @@ -20,6 +20,7 @@ class RepeatedFieldTest < Test::Unit::TestCase :iter_for_each_with_index, :dimensions, :copy_data, :copy_data_simple, :nitems, :iter_for_reverse_each, :indexes, :append, :prepend] arr_methods -= [:union, :difference, :filter!] + arr_methods -= [:intersection, :deconstruct] # ruby 2.7 methods we can ignore arr_methods.each do |method_name| assert m.repeated_string.respond_to?(method_name) == true, "does not respond to #{method_name}" end diff --git a/ruby/ext/google/protobuf_c/defs.c b/ruby/ext/google/protobuf_c/defs.c index babdfa9711..1a09cc5ffb 100644 --- a/ruby/ext/google/protobuf_c/defs.c +++ b/ruby/ext/google/protobuf_c/defs.c @@ -1100,7 +1100,7 @@ VALUE FieldDescriptor_get(VALUE _self, VALUE msg_rb) { * FieldDescriptor.has?(message) => boolean * * Returns whether the value is set on the given message. Raises an - * exception when calling with proto syntax 3. + * exception when calling for fields that do not have presence. */ VALUE FieldDescriptor_has(VALUE _self, VALUE msg_rb) { DEFINE_SELF(FieldDescriptor, self, _self); @@ -1434,6 +1434,7 @@ void MessageBuilderContext_register(VALUE module) { rb_define_method(klass, "initialize", MessageBuilderContext_initialize, 2); rb_define_method(klass, "optional", MessageBuilderContext_optional, -1); + rb_define_method(klass, "proto3_optional", MessageBuilderContext_proto3_optional, -1); rb_define_method(klass, "required", MessageBuilderContext_required, -1); rb_define_method(klass, "repeated", MessageBuilderContext_repeated, -1); rb_define_method(klass, "map", MessageBuilderContext_map, -1); @@ -1469,7 +1470,8 @@ VALUE MessageBuilderContext_initialize(VALUE _self, static void msgdef_add_field(VALUE msgbuilder_rb, upb_label_t label, VALUE name, VALUE type, VALUE number, VALUE type_class, - VALUE options, int oneof_index) { + VALUE options, int oneof_index, + bool proto3_optional) { DEFINE_SELF(MessageBuilderContext, self, msgbuilder_rb); FileBuilderContext* file_context = ruby_to_FileBuilderContext(self->file_builder); @@ -1489,6 +1491,10 @@ static void msgdef_add_field(VALUE msgbuilder_rb, upb_label_t label, VALUE name, google_protobuf_FieldDescriptorProto_set_type( field_proto, (int)ruby_to_descriptortype(type)); + if (proto3_optional) { + google_protobuf_FieldDescriptorProto_set_proto3_optional(field_proto, true); + } + if (type_class != Qnil) { Check_Type(type_class, T_STRING); @@ -1574,7 +1580,38 @@ VALUE MessageBuilderContext_optional(int argc, VALUE* argv, VALUE _self) { } msgdef_add_field(_self, UPB_LABEL_OPTIONAL, name, type, number, type_class, - options, -1); + options, -1, false); + + return Qnil; +} + +/* + * call-seq: + * MessageBuilderContext.proto3_optional(name, type, number, + * type_class = nil, options = nil) + * + * Defines a true proto3 optional field (that tracks presence) on this message + * type with the given type, tag number, and type class (for message and enum + * fields). The type must be a Ruby symbol (as accepted by + * FieldDescriptor#type=) and the type_class must be a string, if present (as + * accepted by FieldDescriptor#submsg_name=). + */ +VALUE MessageBuilderContext_proto3_optional(int argc, VALUE* argv, + VALUE _self) { + VALUE name, type, number; + VALUE type_class, options = Qnil; + + rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options); + + // Allow passing (name, type, number, options) or + // (name, type, number, type_class, options) + if (argc == 4 && RB_TYPE_P(type_class, T_HASH)) { + options = type_class; + type_class = Qnil; + } + + msgdef_add_field(_self, UPB_LABEL_OPTIONAL, name, type, number, type_class, + options, -1, true); return Qnil; } @@ -1607,7 +1644,7 @@ VALUE MessageBuilderContext_required(int argc, VALUE* argv, VALUE _self) { } msgdef_add_field(_self, UPB_LABEL_REQUIRED, name, type, number, type_class, - options, -1); + options, -1, false); return Qnil; } @@ -1633,7 +1670,7 @@ VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self) { type_class = (argc > 3) ? argv[3] : Qnil; msgdef_add_field(_self, UPB_LABEL_REPEATED, name, type, number, type_class, - Qnil, -1); + Qnil, -1, false); return Qnil; } @@ -1758,6 +1795,56 @@ VALUE MessageBuilderContext_oneof(VALUE _self, VALUE name) { return Qnil; } +void MessageBuilderContext_add_synthetic_oneofs(VALUE _self) { + DEFINE_SELF(MessageBuilderContext, self, _self); + FileBuilderContext* file_context = + ruby_to_FileBuilderContext(self->file_builder); + size_t field_count, oneof_count; + google_protobuf_FieldDescriptorProto** fields = + google_protobuf_DescriptorProto_mutable_field(self->msg_proto, &field_count); + const google_protobuf_OneofDescriptorProto*const* oneofs = + google_protobuf_DescriptorProto_oneof_decl(self->msg_proto, &oneof_count); + VALUE names = rb_hash_new(); + VALUE underscore = rb_str_new2("_"); + size_t i; + + // We have to build a set of all names, to ensure that synthetic oneofs are + // not creating conflicts. + for (i = 0; i < field_count; i++) { + upb_strview name = google_protobuf_FieldDescriptorProto_name(fields[i]); + rb_hash_aset(names, rb_str_new(name.data, name.size), Qtrue); + } + for (i = 0; i < oneof_count; i++) { + upb_strview name = google_protobuf_OneofDescriptorProto_name(oneofs[i]); + rb_hash_aset(names, rb_str_new(name.data, name.size), Qtrue); + } + + for (i = 0; i < field_count; i++) { + google_protobuf_OneofDescriptorProto* oneof_proto; + VALUE oneof_name; + upb_strview field_name; + + if (!google_protobuf_FieldDescriptorProto_proto3_optional(fields[i])) { + continue; + } + + // Prepend '_' until we are no longer conflicting. + field_name = google_protobuf_FieldDescriptorProto_name(fields[i]); + oneof_name = rb_str_new(field_name.data, field_name.size); + while (rb_hash_lookup(names, oneof_name) != Qnil) { + oneof_name = rb_str_plus(underscore, oneof_name); + } + + rb_hash_aset(names, oneof_name, Qtrue); + google_protobuf_FieldDescriptorProto_set_oneof_index(fields[i], + oneof_count++); + oneof_proto = google_protobuf_DescriptorProto_add_oneof_decl( + self->msg_proto, file_context->arena); + google_protobuf_OneofDescriptorProto_set_name( + oneof_proto, FileBuilderContext_strdup(self->file_builder, oneof_name)); + } +} + // ----------------------------------------------------------------------------- // OneofBuilderContext. // ----------------------------------------------------------------------------- @@ -1829,7 +1916,7 @@ VALUE OneofBuilderContext_optional(int argc, VALUE* argv, VALUE _self) { rb_scan_args(argc, argv, "32", &name, &type, &number, &type_class, &options); msgdef_add_field(self->message_builder, UPB_LABEL_OPTIONAL, name, type, - number, type_class, options, self->oneof_index); + number, type_class, options, self->oneof_index, false); return Qnil; } @@ -2033,6 +2120,7 @@ VALUE FileBuilderContext_add_message(VALUE _self, VALUE name) { VALUE ctx = rb_class_new_instance(2, args, cMessageBuilderContext); VALUE block = rb_block_proc(); rb_funcall_with_block(ctx, rb_intern("instance_eval"), 0, NULL, block); + MessageBuilderContext_add_synthetic_oneofs(ctx); return Qnil; } diff --git a/ruby/ext/google/protobuf_c/encode_decode.c b/ruby/ext/google/protobuf_c/encode_decode.c index 4a1d724d8f..a58a5d2f84 100644 --- a/ruby/ext/google/protobuf_c/encode_decode.c +++ b/ruby/ext/google/protobuf_c/encode_decode.c @@ -933,7 +933,7 @@ void add_handlers_for_message(const void *closure, upb_handlers *h) { !upb_msg_field_done(&i); upb_msg_field_next(&i)) { const upb_fielddef *f = upb_msg_iter_field(&i); - const upb_oneofdef *oneof = upb_fielddef_containingoneof(f); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(f); size_t offset = get_field_offset(desc->layout, f); if (oneof) { @@ -1506,7 +1506,7 @@ static void putmsg(VALUE msg_rb, const Descriptor* desc, !upb_msg_field_done(&i); upb_msg_field_next(&i)) { upb_fielddef *f = upb_msg_iter_field(&i); - const upb_oneofdef *oneof = upb_fielddef_containingoneof(f); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(f); bool is_matching_oneof = false; uint32_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + @@ -1714,7 +1714,7 @@ static void discard_unknown(VALUE msg_rb, const Descriptor* desc) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { upb_fielddef *f = upb_msg_iter_field(&it); - const upb_oneofdef *oneof = upb_fielddef_containingoneof(f); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(f); uint32_t offset = desc->layout->fields[upb_fielddef_index(f)].offset + sizeof(MessageHeader); diff --git a/ruby/ext/google/protobuf_c/message.c b/ruby/ext/google/protobuf_c/message.c index f57501cc65..0050506d3b 100644 --- a/ruby/ext/google/protobuf_c/message.c +++ b/ruby/ext/google/protobuf_c/message.c @@ -242,9 +242,14 @@ static int extract_method_call(VALUE method_name, MessageHeader* self, // Method calls like 'has_foo?' are not allowed if field "foo" does not have // a hasbit (e.g. repeated fields or non-message type fields for proto3 // syntax). - if (accessor_type == METHOD_PRESENCE && test_f != NULL && - !upb_fielddef_haspresence(test_f)) { - return METHOD_UNKNOWN; + if (accessor_type == METHOD_PRESENCE && test_f != NULL) { + if (!upb_fielddef_haspresence(test_f)) return METHOD_UNKNOWN; + + // TODO(haberman): remove this case, allow for proto3 oneofs. + if (upb_fielddef_realcontainingoneof(test_f) && + upb_filedef_syntax(upb_fielddef_file(test_f)) == UPB_SYNTAX_PROTO3) { + return METHOD_UNKNOWN; + } } *o = test_o; @@ -605,11 +610,17 @@ VALUE Message_inspect(VALUE _self) { */ VALUE Message_to_h(VALUE _self) { MessageHeader* self; - VALUE hash; + VALUE hash = rb_hash_new(); upb_msg_field_iter it; + bool is_proto2; TypedData_Get_Struct(_self, MessageHeader, &Message_type, self); - hash = rb_hash_new(); + // We currently have a few behaviors that are specific to proto2. + // This is unfortunate, we should key behaviors off field attributes (like + // whether a field has presence), not proto2 vs. proto3. We should see if we + // can change this without breaking users. + is_proto2 = + upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2; for (upb_msg_field_begin(&it, self->descriptor->msgdef); !upb_msg_field_done(&it); @@ -618,10 +629,9 @@ VALUE Message_to_h(VALUE _self) { VALUE msg_value; VALUE msg_key; - // For proto2, do not include fields which are not set. - if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 && - field_contains_hasbit(self->descriptor->layout, field) && - !layout_has(self->descriptor->layout, Message_data(self), field)) { + // Do not include fields that are not present (oneof or optional fields). + if (is_proto2 && upb_fielddef_haspresence(field) && + !layout_has(self->descriptor->layout, Message_data(self), field)) { continue; } @@ -631,8 +641,7 @@ VALUE Message_to_h(VALUE _self) { msg_value = Map_to_h(msg_value); } else if (upb_fielddef_label(field) == UPB_LABEL_REPEATED) { msg_value = RepeatedField_to_ary(msg_value); - if (upb_msgdef_syntax(self->descriptor->msgdef) == UPB_SYNTAX_PROTO2 && - RARRAY_LEN(msg_value) == 0) { + if (is_proto2 && RARRAY_LEN(msg_value) == 0) { continue; } diff --git a/ruby/ext/google/protobuf_c/protobuf.h b/ruby/ext/google/protobuf_c/protobuf.h index ae9895c537..0ec78fc4fb 100644 --- a/ruby/ext/google/protobuf_c/protobuf.h +++ b/ruby/ext/google/protobuf_c/protobuf.h @@ -285,6 +285,7 @@ VALUE MessageBuilderContext_initialize(VALUE _self, VALUE _file_builder, VALUE name); VALUE MessageBuilderContext_optional(int argc, VALUE* argv, VALUE _self); +VALUE MessageBuilderContext_proto3_optional(int argc, VALUE* argv, VALUE _self); VALUE MessageBuilderContext_required(int argc, VALUE* argv, VALUE _self); VALUE MessageBuilderContext_repeated(int argc, VALUE* argv, VALUE _self); VALUE MessageBuilderContext_map(int argc, VALUE* argv, VALUE _self); diff --git a/ruby/ext/google/protobuf_c/storage.c b/ruby/ext/google/protobuf_c/storage.c index 739c0a7765..a3805c96a3 100644 --- a/ruby/ext/google/protobuf_c/storage.c +++ b/ruby/ext/google/protobuf_c/storage.c @@ -496,11 +496,14 @@ void create_layout(Descriptor* desc) { const upb_msgdef *msgdef = desc->msgdef; MessageLayout* layout = ALLOC(MessageLayout); int nfields = upb_msgdef_numfields(msgdef); - int noneofs = upb_msgdef_numoneofs(msgdef); + int noneofs = upb_msgdef_numrealoneofs(msgdef); upb_msg_field_iter it; upb_msg_oneof_iter oit; size_t off = 0; size_t hasbit = 0; + int i; + + (void)i; layout->empty_template = NULL; layout->desc = desc; @@ -513,11 +516,22 @@ void create_layout(Descriptor* desc) { layout->oneofs = ALLOC_N(MessageOneof, noneofs); } +#ifndef NDEBUG + for (i = 0; i < nfields; i++) { + layout->fields[i].offset = -1; + } + + for (i = 0; i < noneofs; i++) { + layout->oneofs[i].offset = -1; + } +#endif + for (upb_msg_field_begin(&it, msgdef); !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - if (upb_fielddef_haspresence(field)) { + if (upb_fielddef_haspresence(field) && + !upb_fielddef_realcontainingoneof(field)) { layout->fields[upb_fielddef_index(field)].hasbit = hasbit++; } else { layout->fields[upb_fielddef_index(field)].hasbit = @@ -540,7 +554,7 @@ void create_layout(Descriptor* desc) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - if (upb_fielddef_containingoneof(field) || !upb_fielddef_isseq(field) || + if (upb_fielddef_realcontainingoneof(field) || !upb_fielddef_isseq(field) || upb_fielddef_ismap(field)) { continue; } @@ -555,7 +569,7 @@ void create_layout(Descriptor* desc) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - if (upb_fielddef_containingoneof(field) || !upb_fielddef_isseq(field) || + if (upb_fielddef_realcontainingoneof(field) || !upb_fielddef_isseq(field) || !upb_fielddef_ismap(field)) { continue; } @@ -572,7 +586,7 @@ void create_layout(Descriptor* desc) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - if (upb_fielddef_containingoneof(field) || !is_value_field(field) || + if (upb_fielddef_realcontainingoneof(field) || !is_value_field(field) || upb_fielddef_isseq(field)) { continue; } @@ -589,7 +603,7 @@ void create_layout(Descriptor* desc) { const upb_fielddef* field = upb_msg_iter_field(&it); size_t field_size; - if (upb_fielddef_containingoneof(field) || is_value_field(field)) { + if (upb_fielddef_realcontainingoneof(field) || is_value_field(field)) { continue; } @@ -624,6 +638,10 @@ void create_layout(Descriptor* desc) { // Always allocate NATIVE_SLOT_MAX_SIZE bytes, but share the slot between // all fields. size_t field_size = NATIVE_SLOT_MAX_SIZE; + + if (upb_oneofdef_issynthetic(oneof)) continue; + assert(upb_oneofdef_index(oneof) < noneofs); + // Align the offset. off = align_up_to(off, field_size); // Assign all fields in the oneof this same offset. @@ -643,6 +661,8 @@ void create_layout(Descriptor* desc) { upb_msg_oneof_next(&oit)) { const upb_oneofdef* oneof = upb_msg_iter_oneof(&oit); size_t field_size = sizeof(uint32_t); + if (upb_oneofdef_issynthetic(oneof)) continue; + assert(upb_oneofdef_index(oneof) < noneofs); // Align the offset. off = (off + field_size - 1) & ~(field_size - 1); layout->oneofs[upb_oneofdef_index(oneof)].case_offset = off; @@ -652,6 +672,16 @@ void create_layout(Descriptor* desc) { layout->size = off; layout->msgdef = msgdef; +#ifndef NDEBUG + for (i = 0; i < nfields; i++) { + assert(layout->fields[i].offset != -1); + } + + for (i = 0; i < noneofs; i++) { + assert(layout->oneofs[i].offset != -1); + } +#endif + // Create the empty message template. layout->empty_template = ALLOC_N(char, layout->size); memset(layout->empty_template, 0, layout->size); @@ -725,10 +755,7 @@ static bool slot_is_hasbit_set(MessageLayout* layout, const void* storage, const upb_fielddef* field) { size_t hasbit = layout->fields[upb_fielddef_index(field)].hasbit; - if (hasbit == MESSAGE_FIELD_NO_HASBIT) { - return false; - } - + assert(field_contains_hasbit(layout, field)); return DEREF_OFFSET( (uint8_t*)storage, hasbit / 8, char) & (1 << (hasbit % 8)); } @@ -736,15 +763,21 @@ static bool slot_is_hasbit_set(MessageLayout* layout, VALUE layout_has(MessageLayout* layout, const void* storage, const upb_fielddef* field) { - assert(field_contains_hasbit(layout, field)); - return slot_is_hasbit_set(layout, storage, field) ? Qtrue : Qfalse; + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(field); + assert(upb_fielddef_haspresence(field)); + if (oneof) { + uint32_t oneof_case = slot_read_oneof_case(layout, storage, oneof); + return oneof_case == upb_fielddef_number(field) ? Qtrue : Qfalse; + } else { + return slot_is_hasbit_set(layout, storage, field) ? Qtrue : Qfalse; + } } void layout_clear(MessageLayout* layout, const void* storage, const upb_fielddef* field) { void* memory = slot_memory(layout, storage, field); - const upb_oneofdef* oneof = upb_fielddef_containingoneof(field); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(field); if (field_contains_hasbit(layout, field)) { slot_clear_hasbit(layout, storage, field); @@ -837,7 +870,7 @@ VALUE layout_get(MessageLayout* layout, const void* storage, const upb_fielddef* field) { void* memory = slot_memory(layout, storage, field); - const upb_oneofdef* oneof = upb_fielddef_containingoneof(field); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(field); bool field_set; if (field_contains_hasbit(layout, field)) { field_set = slot_is_hasbit_set(layout, storage, field); @@ -910,7 +943,7 @@ void layout_set(MessageLayout* layout, const upb_fielddef* field, VALUE val) { void* memory = slot_memory(layout, storage, field); - const upb_oneofdef* oneof = upb_fielddef_containingoneof(field); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(field); if (oneof) { uint32_t* oneof_case = slot_oneof_case(layout, storage, oneof); @@ -953,7 +986,16 @@ void layout_set(MessageLayout* layout, if (layout->fields[upb_fielddef_index(field)].hasbit != MESSAGE_FIELD_NO_HASBIT) { - slot_set_hasbit(layout, storage, field); + if (val == Qnil) { + // No other field type has a hasbit and allows nil assignment. + if (upb_fielddef_type(field) != UPB_TYPE_MESSAGE) { + fprintf(stderr, "field: %s\n", upb_fielddef_fullname(field)); + } + assert(upb_fielddef_type(field) == UPB_TYPE_MESSAGE); + slot_clear_hasbit(layout, storage, field); + } else { + slot_set_hasbit(layout, storage, field); + } } } @@ -972,7 +1014,7 @@ void layout_init(MessageLayout* layout, void* storage) { void layout_mark(MessageLayout* layout, void* storage) { VALUE* values = (VALUE*)CHARPTR_AT(storage, layout->value_offset); - int noneofs = upb_msgdef_numoneofs(layout->msgdef); + int noneofs = upb_msgdef_numrealoneofs(layout->msgdef); int i; for (i = 0; i < layout->value_count; i++) { @@ -994,7 +1036,7 @@ void layout_dup(MessageLayout* layout, void* to, void* from) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - const upb_oneofdef* oneof = upb_fielddef_containingoneof(field); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(field); void* to_memory = slot_memory(layout, to, field); void* from_memory = slot_memory(layout, from, field); @@ -1028,7 +1070,7 @@ void layout_deep_copy(MessageLayout* layout, void* to, void* from) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - const upb_oneofdef* oneof = upb_fielddef_containingoneof(field); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(field); void* to_memory = slot_memory(layout, to, field); void* from_memory = slot_memory(layout, from, field); @@ -1068,7 +1110,7 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { !upb_msg_field_done(&it); upb_msg_field_next(&it)) { const upb_fielddef* field = upb_msg_iter_field(&it); - const upb_oneofdef* oneof = upb_fielddef_containingoneof(field); + const upb_oneofdef* oneof = upb_fielddef_realcontainingoneof(field); void* msg1_memory = slot_memory(layout, msg1, field); void* msg2_memory = slot_memory(layout, msg2, field); @@ -1095,9 +1137,16 @@ VALUE layout_eq(MessageLayout* layout, void* msg1, void* msg2) { return Qfalse; } } else { - if (slot_is_hasbit_set(layout, msg1, field) != - slot_is_hasbit_set(layout, msg2, field) || - !native_slot_eq(upb_fielddef_type(field), + if (field_contains_hasbit(layout, field) && + slot_is_hasbit_set(layout, msg1, field) != + slot_is_hasbit_set(layout, msg2, field)) { + // TODO(haberman): I don't think we should actually care about hasbits + // here: an unset default should be able to equal a set default. But we + // can address this later (will also have to make sure defaults are + // being properly set when hasbit is clear). + return Qfalse; + } + if (!native_slot_eq(upb_fielddef_type(field), field_type_class(layout, field), msg1_memory, msg2_memory)) { return Qfalse; diff --git a/ruby/ext/google/protobuf_c/upb.c b/ruby/ext/google/protobuf_c/upb.c index ffa55fbb2a..61e86fcf10 100644 --- a/ruby/ext/google/protobuf_c/upb.c +++ b/ruby/ext/google/protobuf_c/upb.c @@ -3032,6 +3032,7 @@ struct upb_msgdef { const upb_oneofdef *oneofs; int field_count; int oneof_count; + int real_oneof_count; /* Is this a map-entry message? */ bool map_entry; @@ -3203,11 +3204,14 @@ static uint32_t upb_handlers_selectorcount(const upb_fielddef *f) { return ret; } +static void upb_status_setoom(upb_status *status) { + upb_status_seterrmsg(status, "out of memory"); +} + static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { /* Sort fields. upb internally relies on UPB_TYPE_MESSAGE fields having the * lowest indexes, but we do not publicly guarantee this. */ upb_msg_field_iter j; - upb_msg_oneof_iter k; int i; uint32_t selector; int n = upb_msgdef_numfields(m); @@ -3248,14 +3252,38 @@ static bool assign_msg_indices(upb_msgdef *m, upb_status *s) { } m->selector_count = selector; - for(upb_msg_oneof_begin(&k, m), i = 0; - !upb_msg_oneof_done(&k); - upb_msg_oneof_next(&k), i++) { - upb_oneofdef *o = (upb_oneofdef*)upb_msg_iter_oneof(&k); - o->index = i; + upb_gfree(fields); + return true; +} + +static bool check_oneofs(upb_msgdef *m, upb_status *s) { + int i; + int first_synthetic = -1; + upb_oneofdef *mutable_oneofs = (upb_oneofdef*)m->oneofs; + + for (i = 0; i < m->oneof_count; i++) { + mutable_oneofs[i].index = i; + + if (upb_oneofdef_issynthetic(&mutable_oneofs[i])) { + if (first_synthetic == -1) { + first_synthetic = i; + } + } else { + if (first_synthetic != -1) { + upb_status_seterrf( + s, "Synthetic oneofs must be after all other oneofs: %s", + upb_oneofdef_name(&mutable_oneofs[i])); + return false; + } + } + } + + if (first_synthetic == -1) { + m->real_oneof_count = m->oneof_count; + } else { + m->real_oneof_count = first_synthetic; } - upb_gfree(fields); return true; } @@ -3440,6 +3468,10 @@ uint32_t upb_fielddef_selectorbase(const upb_fielddef *f) { return f->selector_base; } +const upb_filedef *upb_fielddef_file(const upb_fielddef *f) { + return f->file; +} + const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f) { return f->msgdef; } @@ -3448,6 +3480,11 @@ const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f) { return f->oneof; } +const upb_oneofdef *upb_fielddef_realcontainingoneof(const upb_fielddef *f) { + if (!f->oneof || upb_oneofdef_issynthetic(f->oneof)) return NULL; + return f->oneof; +} + static void chkdefaulttype(const upb_fielddef *f, int ctype) { UPB_UNUSED(f); UPB_UNUSED(ctype); @@ -3544,9 +3581,8 @@ bool upb_fielddef_hassubdef(const upb_fielddef *f) { bool upb_fielddef_haspresence(const upb_fielddef *f) { if (upb_fielddef_isseq(f)) return false; - if (upb_fielddef_issubmsg(f)) return true; - if (f->proto3_optional_) return true; - return f->file->syntax == UPB_SYNTAX_PROTO2; + return upb_fielddef_issubmsg(f) || upb_fielddef_containingoneof(f) || + f->file->syntax == UPB_SYNTAX_PROTO2; } static bool between(int32_t x, int32_t low, int32_t high) { @@ -3651,6 +3687,10 @@ int upb_msgdef_numoneofs(const upb_msgdef *m) { return m->oneof_count; } +int upb_msgdef_numrealoneofs(const upb_msgdef *m) { + return m->real_oneof_count; +} + const upb_msglayout *upb_msgdef_layout(const upb_msgdef *m) { return m->layout; } @@ -3749,7 +3789,7 @@ uint32_t upb_oneofdef_index(const upb_oneofdef *o) { return o->index; } -bool upb_oneofdef_synthetic(const upb_oneofdef *o) { +bool upb_oneofdef_issynthetic(const upb_oneofdef *o) { upb_inttable_iter iter; const upb_fielddef *f; upb_inttable_begin(&iter, &o->itof); @@ -3941,7 +3981,7 @@ static bool make_layout(const upb_symtab *symtab, const upb_msgdef *m) { submsgs[field->submsg_index] = subm->layout; } - if (upb_fielddef_haspresence(f) && !upb_fielddef_containingoneof(f)) { + if (upb_fielddef_haspresence(f) && !upb_fielddef_realcontainingoneof(f)) { /* We don't use hasbit 0, so that 0 can indicate "no presence" in the * table. This wastes one hasbit, but we don't worry about it for now. */ field->presence = ++hasbit; @@ -3960,7 +4000,7 @@ static bool make_layout(const upb_symtab *symtab, const upb_msgdef *m) { size_t field_size = upb_msg_fielddefsize(f); size_t index = upb_fielddef_index(f); - if (upb_fielddef_containingoneof(f)) { + if (upb_fielddef_realcontainingoneof(f)) { /* Oneofs are handled separately below. */ continue; } @@ -3975,6 +4015,8 @@ static bool make_layout(const upb_symtab *symtab, const upb_msgdef *m) { const upb_oneofdef* o = upb_msg_iter_oneof(&oit); upb_oneof_iter fit; + if (upb_oneofdef_issynthetic(o)) continue; + size_t case_size = sizeof(uint32_t); /* Could potentially optimize this. */ size_t field_size = 0; uint32_t case_offset; @@ -4587,6 +4629,7 @@ static bool create_msgdef(symtab_addctx *ctx, const char *prefix, } CHK(assign_msg_indices(m, ctx->status)); + CHK(check_oneofs(m, ctx->status)); assign_msg_wellknowntype(m); upb_inttable_compact2(&m->itof, ctx->alloc); @@ -9649,7 +9692,7 @@ static bool multipart_text(upb_json_parser *p, const char *buf, size_t len, /* Note: this invalidates the accumulate buffer! Call only after reading its * contents. */ static void multipart_end(upb_json_parser *p) { - UPB_ASSERT(p->multipart_state != MULTIPART_INACTIVE); + /* UPB_ASSERT(p->multipart_state != MULTIPART_INACTIVE); */ p->multipart_state = MULTIPART_INACTIVE; accumulate_clear(p); } diff --git a/ruby/ext/google/protobuf_c/upb.h b/ruby/ext/google/protobuf_c/upb.h index a34637925b..bdc20ebeef 100644 --- a/ruby/ext/google/protobuf_c/upb.h +++ b/ruby/ext/google/protobuf_c/upb.h @@ -211,9 +211,6 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg); #include /* ** This file contains shared definitions that are widely used across upb. -** -** This is a mixed C/C++ interface that offers a full API to both languages. -** See the top-level README for more information. */ #ifndef UPB_H_ @@ -226,23 +223,13 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg); #include #include + #ifdef __cplusplus -#include -namespace upb { -class Arena; -class Status; -template class InlinedArena; -} +extern "C" { #endif - /* upb_status *****************************************************************/ -/* upb_status represents a success or failure status and error message. - * It owns no resources and allocates no memory, so it should work - * even in OOM situations. */ - -/* The maximum length of an error message before it will get truncated. */ #define UPB_STATUS_MAX_MESSAGE 127 typedef struct { @@ -250,59 +237,15 @@ typedef struct { char msg[UPB_STATUS_MAX_MESSAGE]; /* Error message; NULL-terminated. */ } upb_status; -#ifdef __cplusplus -extern "C" { -#endif - const char *upb_status_errmsg(const upb_status *status); bool upb_ok(const upb_status *status); -/* Any of the functions that write to a status object allow status to be NULL, - * to support use cases where the function's caller does not care about the - * status message. */ +/* These are no-op if |status| is NULL. */ void upb_status_clear(upb_status *status); void upb_status_seterrmsg(upb_status *status, const char *msg); void upb_status_seterrf(upb_status *status, const char *fmt, ...); void upb_status_vseterrf(upb_status *status, const char *fmt, va_list args); -UPB_INLINE void upb_status_setoom(upb_status *status) { - upb_status_seterrmsg(status, "out of memory"); -} - -#ifdef __cplusplus -} /* extern "C" */ - -class upb::Status { - public: - Status() { upb_status_clear(&status_); } - - upb_status* ptr() { return &status_; } - - /* Returns true if there is no error. */ - bool ok() const { return upb_ok(&status_); } - - /* Guaranteed to be NULL-terminated. */ - const char *error_message() const { return upb_status_errmsg(&status_); } - - /* The error message will be truncated if it is longer than - * UPB_STATUS_MAX_MESSAGE-4. */ - void SetErrorMessage(const char *msg) { upb_status_seterrmsg(&status_, msg); } - void SetFormattedErrorMessage(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - upb_status_vseterrf(&status_, fmt, args); - va_end(args); - } - - /* Resets the status to a successful state with no message. */ - void Clear() { upb_status_clear(&status_); } - - private: - upb_status status_; -}; - -#endif /* __cplusplus */ - /** upb_strview ************************************************************/ typedef struct { @@ -369,16 +312,8 @@ UPB_INLINE void upb_free(upb_alloc *alloc, void *ptr) { /* The global allocator used by upb. Uses the standard malloc()/free(). */ -#ifdef __cplusplus -extern "C" { -#endif - extern upb_alloc upb_alloc_global; -#ifdef __cplusplus -} /* extern "C" */ -#endif - /* Functions that hard-code the global malloc. * * We still get benefit because we can put custom logic into our global @@ -415,10 +350,6 @@ typedef void upb_cleanup_func(void *ud); struct upb_arena; typedef struct upb_arena upb_arena; -#ifdef __cplusplus -extern "C" { -#endif - typedef struct { /* We implement the allocator interface. * This must be the first member of upb_arena! */ @@ -468,64 +399,6 @@ UPB_INLINE upb_arena *upb_arena_new(void) { return upb_arena_init(NULL, 0, &upb_alloc_global); } -#ifdef __cplusplus -} /* extern "C" */ - -class upb::Arena { - public: - /* A simple arena with no initial memory block and the default allocator. */ - Arena() : ptr_(upb_arena_new(), upb_arena_free) {} - - upb_arena* ptr() { return ptr_.get(); } - - /* Allows this arena to be used as a generic allocator. - * - * The arena does not need free() calls so when using Arena as an allocator - * it is safe to skip them. However they are no-ops so there is no harm in - * calling free() either. */ - upb_alloc *allocator() { return upb_arena_alloc(ptr_.get()); } - - /* Add a cleanup function to run when the arena is destroyed. - * Returns false on out-of-memory. */ - bool AddCleanup(void *ud, upb_cleanup_func* func) { - return upb_arena_addcleanup(ptr_.get(), ud, func); - } - - /* Total number of bytes that have been allocated. It is undefined what - * Realloc() does to &arena_ counter. */ - size_t BytesAllocated() const { return upb_arena_bytesallocated(ptr_.get()); } - - private: - std::unique_ptr ptr_; -}; - -#endif - -/* upb::InlinedArena **********************************************************/ - -/* upb::InlinedArena seeds the arenas with a predefined amount of memory. No - * heap memory will be allocated until the initial block is exceeded. - * - * These types only exist in C++ */ - -#ifdef __cplusplus - -template class upb::InlinedArena : public upb::Arena { - public: - InlinedArena() : ptr_(upb_arena_new(&initial_block_, N, &upb_alloc_global)) {} - - upb_arena* ptr() { return ptr_.get(); } - - private: - InlinedArena(const InlinedArena*) = delete; - InlinedArena& operator=(const InlinedArena*) = delete; - - std::unique_ptr ptr_; - char initial_block_[N]; -}; - -#endif /* __cplusplus */ - /* Constants ******************************************************************/ /* Generic function type. */ @@ -545,20 +418,15 @@ typedef enum { * types defined in descriptor.proto, which gives INT32 and SINT32 separate * types (we distinguish the two with the "integer encoding" enum below). */ typedef enum { - /* Types stored in 1 byte. */ UPB_TYPE_BOOL = 1, - /* Types stored in 4 bytes. */ UPB_TYPE_FLOAT = 2, UPB_TYPE_INT32 = 3, UPB_TYPE_UINT32 = 4, UPB_TYPE_ENUM = 5, /* Enum values are int32. */ - /* Types stored as void* (probably 4 or 8 bytes). */ UPB_TYPE_MESSAGE = 6, - /* Types stored as 8 bytes. */ UPB_TYPE_DOUBLE = 7, UPB_TYPE_INT64 = 8, UPB_TYPE_UINT64 = 9, - /* Types stored as upb_strview (2 * void*) (probably 8 or 16 bytes). */ UPB_TYPE_STRING = 10, UPB_TYPE_BYTES = 11 } upb_fieldtype_t; @@ -615,6 +483,10 @@ typedef enum { #define UPB_MAP_BEGIN -1 +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* UPB_H_ */ @@ -3208,38 +3080,23 @@ UPB_INLINE void google_protobuf_GeneratedCodeInfo_Annotation_set_end(google_prot ** Defs are upb's internal representation of the constructs that can appear ** in a .proto file: ** -** - upb::MessageDefPtr (upb_msgdef): describes a "message" construct. -** - upb::FieldDefPtr (upb_fielddef): describes a message field. -** - upb::FileDefPtr (upb_filedef): describes a .proto file and its defs. -** - upb::EnumDefPtr (upb_enumdef): describes an enum. -** - upb::OneofDefPtr (upb_oneofdef): describes a oneof. +** - upb_msgdef: describes a "message" construct. +** - upb_fielddef: describes a message field. +** - upb_filedef: describes a .proto file and its defs. +** - upb_enumdef: describes an enum. +** - upb_oneofdef: describes a oneof. ** ** TODO: definitions of services. -** -** This is a mixed C/C++ interface that offers a full API to both languages. -** See the top-level README for more information. */ #ifndef UPB_DEF_H_ #define UPB_DEF_H_ -#ifdef __cplusplus -#include -#include -#include -#include - -namespace upb { -class EnumDefPtr; -class FieldDefPtr; -class FileDefPtr; -class MessageDefPtr; -class OneofDefPtr; -class SymbolTable; -} -#endif +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ struct upb_enumdef; typedef struct upb_enumdef upb_enumdef; @@ -3291,10 +3148,6 @@ typedef enum { * protobuf wire format. */ #define UPB_MAX_FIELDNUMBER ((1 << 29) - 1) -#ifdef __cplusplus -extern "C" { -#endif - const char *upb_fielddef_fullname(const upb_fielddef *f); upb_fieldtype_t upb_fielddef_type(const upb_fielddef *f); upb_descriptortype_t upb_fielddef_descriptortype(const upb_fielddef *f); @@ -3305,8 +3158,10 @@ const char *upb_fielddef_jsonname(const upb_fielddef *f); bool upb_fielddef_isextension(const upb_fielddef *f); bool upb_fielddef_lazy(const upb_fielddef *f); bool upb_fielddef_packed(const upb_fielddef *f); +const upb_filedef *upb_fielddef_file(const upb_fielddef *f); const upb_msgdef *upb_fielddef_containingtype(const upb_fielddef *f); const upb_oneofdef *upb_fielddef_containingoneof(const upb_fielddef *f); +const upb_oneofdef *upb_fielddef_realcontainingoneof(const upb_fielddef *f); uint32_t upb_fielddef_index(const upb_fielddef *f); bool upb_fielddef_issubmsg(const upb_fielddef *f); bool upb_fielddef_isstring(const upb_fielddef *f); @@ -3330,130 +3185,15 @@ const upb_msglayout_field *upb_fielddef_layout(const upb_fielddef *f); /* Internal only. */ uint32_t upb_fielddef_selectorbase(const upb_fielddef *f); -#ifdef __cplusplus -} /* extern "C" */ - -/* A upb_fielddef describes a single field in a message. It is most often - * found as a part of a upb_msgdef, but can also stand alone to represent - * an extension. */ -class upb::FieldDefPtr { - public: - FieldDefPtr() : ptr_(nullptr) {} - explicit FieldDefPtr(const upb_fielddef *ptr) : ptr_(ptr) {} - - const upb_fielddef* ptr() const { return ptr_; } - explicit operator bool() const { return ptr_ != nullptr; } - - typedef upb_fieldtype_t Type; - typedef upb_label_t Label; - typedef upb_descriptortype_t DescriptorType; - - const char* full_name() const { return upb_fielddef_fullname(ptr_); } - - Type type() const { return upb_fielddef_type(ptr_); } - Label label() const { return upb_fielddef_label(ptr_); } - const char* name() const { return upb_fielddef_name(ptr_); } - const char* json_name() const { return upb_fielddef_jsonname(ptr_); } - uint32_t number() const { return upb_fielddef_number(ptr_); } - bool is_extension() const { return upb_fielddef_isextension(ptr_); } - - /* For UPB_TYPE_MESSAGE fields only where is_tag_delimited() == false, - * indicates whether this field should have lazy parsing handlers that yield - * the unparsed string for the submessage. - * - * TODO(haberman): I think we want to move this into a FieldOptions container - * when we add support for custom options (the FieldOptions struct will - * contain both regular FieldOptions like "lazy" *and* custom options). */ - bool lazy() const { return upb_fielddef_lazy(ptr_); } - - /* For non-string, non-submessage fields, this indicates whether binary - * protobufs are encoded in packed or non-packed format. - * - * TODO(haberman): see note above about putting options like this into a - * FieldOptions container. */ - bool packed() const { return upb_fielddef_packed(ptr_); } - - /* An integer that can be used as an index into an array of fields for - * whatever message this field belongs to. Guaranteed to be less than - * f->containing_type()->field_count(). May only be accessed once the def has - * been finalized. */ - uint32_t index() const { return upb_fielddef_index(ptr_); } - - /* The MessageDef to which this field belongs. - * - * If this field has been added to a MessageDef, that message can be retrieved - * directly (this is always the case for frozen FieldDefs). - * - * If the field has not yet been added to a MessageDef, you can set the name - * of the containing type symbolically instead. This is mostly useful for - * extensions, where the extension is declared separately from the message. */ - MessageDefPtr containing_type() const; - - /* The OneofDef to which this field belongs, or NULL if this field is not part - * of a oneof. */ - OneofDefPtr containing_oneof() const; - - /* The field's type according to the enum in descriptor.proto. This is not - * the same as UPB_TYPE_*, because it distinguishes between (for example) - * INT32 and SINT32, whereas our "type" enum does not. This return of - * descriptor_type() is a function of type(), integer_format(), and - * is_tag_delimited(). */ - DescriptorType descriptor_type() const { - return upb_fielddef_descriptortype(ptr_); - } - - /* Convenient field type tests. */ - bool IsSubMessage() const { return upb_fielddef_issubmsg(ptr_); } - bool IsString() const { return upb_fielddef_isstring(ptr_); } - bool IsSequence() const { return upb_fielddef_isseq(ptr_); } - bool IsPrimitive() const { return upb_fielddef_isprimitive(ptr_); } - bool IsMap() const { return upb_fielddef_ismap(ptr_); } - - /* Returns the non-string default value for this fielddef, which may either - * be something the client set explicitly or the "default default" (0 for - * numbers, empty for strings). The field's type indicates the type of the - * returned value, except for enum fields that are still mutable. - * - * Requires that the given function matches the field's current type. */ - int64_t default_int64() const { return upb_fielddef_defaultint64(ptr_); } - int32_t default_int32() const { return upb_fielddef_defaultint32(ptr_); } - uint64_t default_uint64() const { return upb_fielddef_defaultuint64(ptr_); } - uint32_t default_uint32() const { return upb_fielddef_defaultuint32(ptr_); } - bool default_bool() const { return upb_fielddef_defaultbool(ptr_); } - float default_float() const { return upb_fielddef_defaultfloat(ptr_); } - double default_double() const { return upb_fielddef_defaultdouble(ptr_); } - - /* The resulting string is always NULL-terminated. If non-NULL, the length - * will be stored in *len. */ - const char *default_string(size_t * len) const { - return upb_fielddef_defaultstr(ptr_, len); - } - - /* Returns the enum or submessage def for this field, if any. The field's - * type must match (ie. you may only call enum_subdef() for fields where - * type() == UPB_TYPE_ENUM). */ - EnumDefPtr enum_subdef() const; - MessageDefPtr message_subdef() const; - - private: - const upb_fielddef *ptr_; -}; - -#endif /* __cplusplus */ - /* upb_oneofdef ***************************************************************/ -#ifdef __cplusplus -extern "C" { -#endif - typedef upb_inttable_iter upb_oneof_iter; const char *upb_oneofdef_name(const upb_oneofdef *o); const upb_msgdef *upb_oneofdef_containingtype(const upb_oneofdef *o); int upb_oneofdef_numfields(const upb_oneofdef *o); uint32_t upb_oneofdef_index(const upb_oneofdef *o); -bool upb_oneofdef_synthetic(const upb_oneofdef *o); +bool upb_oneofdef_issynthetic(const upb_oneofdef *o); /* Oneof lookups: * - ntof: look up a field by name. @@ -3480,92 +3220,6 @@ void upb_oneof_iter_setdone(upb_oneof_iter *iter); bool upb_oneof_iter_isequal(const upb_oneof_iter *iter1, const upb_oneof_iter *iter2); -#ifdef __cplusplus -} /* extern "C" */ - -/* Class that represents a oneof. */ -class upb::OneofDefPtr { - public: - OneofDefPtr() : ptr_(nullptr) {} - explicit OneofDefPtr(const upb_oneofdef *ptr) : ptr_(ptr) {} - - const upb_oneofdef* ptr() const { return ptr_; } - explicit operator bool() { return ptr_ != nullptr; } - - /* Returns the MessageDef that owns this OneofDef. */ - MessageDefPtr containing_type() const; - - /* Returns the name of this oneof. This is the name used to look up the oneof - * by name once added to a message def. */ - const char* name() const { return upb_oneofdef_name(ptr_); } - - /* Returns the number of fields currently defined in the oneof. */ - int field_count() const { return upb_oneofdef_numfields(ptr_); } - - /* Looks up by name. */ - FieldDefPtr FindFieldByName(const char *name, size_t len) const { - return FieldDefPtr(upb_oneofdef_ntof(ptr_, name, len)); - } - FieldDefPtr FindFieldByName(const char* name) const { - return FieldDefPtr(upb_oneofdef_ntofz(ptr_, name)); - } - - template - FieldDefPtr FindFieldByName(const T& str) const { - return FindFieldByName(str.c_str(), str.size()); - } - - /* Looks up by tag number. */ - FieldDefPtr FindFieldByNumber(uint32_t num) const { - return FieldDefPtr(upb_oneofdef_itof(ptr_, num)); - } - - class const_iterator - : public std::iterator { - public: - void operator++() { upb_oneof_next(&iter_); } - - FieldDefPtr operator*() const { - return FieldDefPtr(upb_oneof_iter_field(&iter_)); - } - - bool operator!=(const const_iterator& other) const { - return !upb_oneof_iter_isequal(&iter_, &other.iter_); - } - - bool operator==(const const_iterator& other) const { - return upb_oneof_iter_isequal(&iter_, &other.iter_); - } - - private: - friend class OneofDefPtr; - - const_iterator() {} - explicit const_iterator(OneofDefPtr o) { - upb_oneof_begin(&iter_, o.ptr()); - } - static const_iterator end() { - const_iterator iter; - upb_oneof_iter_setdone(&iter.iter_); - return iter; - } - - upb_oneof_iter iter_; - }; - - const_iterator begin() const { return const_iterator(*this); } - const_iterator end() const { return const_iterator::end(); } - - private: - const upb_oneofdef *ptr_; -}; - -inline upb::OneofDefPtr upb::FieldDefPtr::containing_oneof() const { - return OneofDefPtr(upb_fielddef_containingoneof(ptr_)); -} - -#endif /* __cplusplus */ - /* upb_msgdef *****************************************************************/ typedef upb_inttable_iter upb_msg_field_iter; @@ -3587,26 +3241,21 @@ typedef upb_strtable_iter upb_msg_oneof_iter; #define UPB_TIMESTAMP_SECONDS 1 #define UPB_TIMESTAMP_NANOS 2 -#ifdef __cplusplus -extern "C" { -#endif - const char *upb_msgdef_fullname(const upb_msgdef *m); const upb_filedef *upb_msgdef_file(const upb_msgdef *m); const char *upb_msgdef_name(const upb_msgdef *m); +int upb_msgdef_numfields(const upb_msgdef *m); int upb_msgdef_numoneofs(const upb_msgdef *m); +int upb_msgdef_numrealoneofs(const upb_msgdef *m); upb_syntax_t upb_msgdef_syntax(const upb_msgdef *m); bool upb_msgdef_mapentry(const upb_msgdef *m); upb_wellknowntype_t upb_msgdef_wellknowntype(const upb_msgdef *m); bool upb_msgdef_isnumberwrapper(const upb_msgdef *m); -bool upb_msgdef_setsyntax(upb_msgdef *m, upb_syntax_t syntax); const upb_fielddef *upb_msgdef_itof(const upb_msgdef *m, uint32_t i); const upb_fielddef *upb_msgdef_ntof(const upb_msgdef *m, const char *name, size_t len); const upb_oneofdef *upb_msgdef_ntoo(const upb_msgdef *m, const char *name, size_t len); -int upb_msgdef_numfields(const upb_msgdef *m); -int upb_msgdef_numoneofs(const upb_msgdef *m); const upb_msglayout *upb_msgdef_layout(const upb_msgdef *m); const upb_fielddef *_upb_msgdef_field(const upb_msgdef *m, int i); @@ -3671,194 +3320,6 @@ void upb_msg_oneof_iter_setdone(upb_msg_oneof_iter * iter); bool upb_msg_oneof_iter_isequal(const upb_msg_oneof_iter *iter1, const upb_msg_oneof_iter *iter2); -#ifdef __cplusplus -} /* extern "C" */ - -/* Structure that describes a single .proto message type. */ -class upb::MessageDefPtr { - public: - MessageDefPtr() : ptr_(nullptr) {} - explicit MessageDefPtr(const upb_msgdef *ptr) : ptr_(ptr) {} - - const upb_msgdef *ptr() const { return ptr_; } - explicit operator bool() const { return ptr_ != nullptr; } - - const char* full_name() const { return upb_msgdef_fullname(ptr_); } - const char* name() const { return upb_msgdef_name(ptr_); } - - /* The number of fields that belong to the MessageDef. */ - int field_count() const { return upb_msgdef_numfields(ptr_); } - - /* The number of oneofs that belong to the MessageDef. */ - int oneof_count() const { return upb_msgdef_numoneofs(ptr_); } - - upb_syntax_t syntax() const { return upb_msgdef_syntax(ptr_); } - - /* These return null pointers if the field is not found. */ - FieldDefPtr FindFieldByNumber(uint32_t number) const { - return FieldDefPtr(upb_msgdef_itof(ptr_, number)); - } - FieldDefPtr FindFieldByName(const char* name, size_t len) const { - return FieldDefPtr(upb_msgdef_ntof(ptr_, name, len)); - } - FieldDefPtr FindFieldByName(const char *name) const { - return FieldDefPtr(upb_msgdef_ntofz(ptr_, name)); - } - - template - FieldDefPtr FindFieldByName(const T& str) const { - return FindFieldByName(str.c_str(), str.size()); - } - - OneofDefPtr FindOneofByName(const char* name, size_t len) const { - return OneofDefPtr(upb_msgdef_ntoo(ptr_, name, len)); - } - - OneofDefPtr FindOneofByName(const char *name) const { - return OneofDefPtr(upb_msgdef_ntooz(ptr_, name)); - } - - template - OneofDefPtr FindOneofByName(const T &str) const { - return FindOneofByName(str.c_str(), str.size()); - } - - /* Is this message a map entry? */ - bool mapentry() const { return upb_msgdef_mapentry(ptr_); } - - /* Return the type of well known type message. UPB_WELLKNOWN_UNSPECIFIED for - * non-well-known message. */ - upb_wellknowntype_t wellknowntype() const { - return upb_msgdef_wellknowntype(ptr_); - } - - /* Whether is a number wrapper. */ - bool isnumberwrapper() const { return upb_msgdef_isnumberwrapper(ptr_); } - - /* Iteration over fields. The order is undefined. */ - class const_field_iterator - : public std::iterator { - public: - void operator++() { upb_msg_field_next(&iter_); } - - FieldDefPtr operator*() const { - return FieldDefPtr(upb_msg_iter_field(&iter_)); - } - - bool operator!=(const const_field_iterator &other) const { - return !upb_msg_field_iter_isequal(&iter_, &other.iter_); - } - - bool operator==(const const_field_iterator &other) const { - return upb_msg_field_iter_isequal(&iter_, &other.iter_); - } - - private: - friend class MessageDefPtr; - - explicit const_field_iterator() {} - - explicit const_field_iterator(MessageDefPtr msg) { - upb_msg_field_begin(&iter_, msg.ptr()); - } - - static const_field_iterator end() { - const_field_iterator iter; - upb_msg_field_iter_setdone(&iter.iter_); - return iter; - } - - upb_msg_field_iter iter_; - }; - - /* Iteration over oneofs. The order is undefined. */ - class const_oneof_iterator - : public std::iterator { - public: - - void operator++() { upb_msg_oneof_next(&iter_); } - - OneofDefPtr operator*() const { - return OneofDefPtr(upb_msg_iter_oneof(&iter_)); - } - - bool operator!=(const const_oneof_iterator& other) const { - return !upb_msg_oneof_iter_isequal(&iter_, &other.iter_); - } - - bool operator==(const const_oneof_iterator &other) const { - return upb_msg_oneof_iter_isequal(&iter_, &other.iter_); - } - - private: - friend class MessageDefPtr; - - const_oneof_iterator() {} - - explicit const_oneof_iterator(MessageDefPtr msg) { - upb_msg_oneof_begin(&iter_, msg.ptr()); - } - - static const_oneof_iterator end() { - const_oneof_iterator iter; - upb_msg_oneof_iter_setdone(&iter.iter_); - return iter; - } - - upb_msg_oneof_iter iter_; - }; - - class ConstFieldAccessor { - public: - explicit ConstFieldAccessor(const upb_msgdef* md) : md_(md) {} - const_field_iterator begin() { return MessageDefPtr(md_).field_begin(); } - const_field_iterator end() { return MessageDefPtr(md_).field_end(); } - private: - const upb_msgdef* md_; - }; - - class ConstOneofAccessor { - public: - explicit ConstOneofAccessor(const upb_msgdef* md) : md_(md) {} - const_oneof_iterator begin() { return MessageDefPtr(md_).oneof_begin(); } - const_oneof_iterator end() { return MessageDefPtr(md_).oneof_end(); } - private: - const upb_msgdef* md_; - }; - - const_field_iterator field_begin() const { - return const_field_iterator(*this); - } - - const_field_iterator field_end() const { return const_field_iterator::end(); } - - const_oneof_iterator oneof_begin() const { - return const_oneof_iterator(*this); - } - - const_oneof_iterator oneof_end() const { return const_oneof_iterator::end(); } - - ConstFieldAccessor fields() const { return ConstFieldAccessor(ptr()); } - ConstOneofAccessor oneofs() const { return ConstOneofAccessor(ptr()); } - - private: - const upb_msgdef* ptr_; -}; - -inline upb::MessageDefPtr upb::FieldDefPtr::message_subdef() const { - return MessageDefPtr(upb_fielddef_msgsubdef(ptr_)); -} - -inline upb::MessageDefPtr upb::FieldDefPtr::containing_type() const { - return MessageDefPtr(upb_fielddef_containingtype(ptr_)); -} - -inline upb::MessageDefPtr upb::OneofDefPtr::containing_type() const { - return MessageDefPtr(upb_oneofdef_containingtype(ptr_)); -} - -#endif /* __cplusplus */ - /* upb_enumdef ****************************************************************/ typedef upb_strtable_iter upb_enum_iter; @@ -3893,75 +3354,8 @@ bool upb_enum_done(upb_enum_iter *iter); const char *upb_enum_iter_name(upb_enum_iter *iter); int32_t upb_enum_iter_number(upb_enum_iter *iter); -#ifdef __cplusplus - -class upb::EnumDefPtr { - public: - EnumDefPtr() : ptr_(nullptr) {} - explicit EnumDefPtr(const upb_enumdef* ptr) : ptr_(ptr) {} - - const upb_enumdef* ptr() const { return ptr_; } - explicit operator bool() const { return ptr_ != nullptr; } - - const char* full_name() const { return upb_enumdef_fullname(ptr_); } - const char* name() const { return upb_enumdef_name(ptr_); } - - /* The value that is used as the default when no field default is specified. - * If not set explicitly, the first value that was added will be used. - * The default value must be a member of the enum. - * Requires that value_count() > 0. */ - int32_t default_value() const { return upb_enumdef_default(ptr_); } - - /* Returns the number of values currently defined in the enum. Note that - * multiple names can refer to the same number, so this may be greater than - * the total number of unique numbers. */ - int value_count() const { return upb_enumdef_numvals(ptr_); } - - /* Lookups from name to integer, returning true if found. */ - bool FindValueByName(const char *name, int32_t *num) const { - return upb_enumdef_ntoiz(ptr_, name, num); - } - - /* Finds the name corresponding to the given number, or NULL if none was - * found. If more than one name corresponds to this number, returns the - * first one that was added. */ - const char *FindValueByNumber(int32_t num) const { - return upb_enumdef_iton(ptr_, num); - } - - /* Iteration over name/value pairs. The order is undefined. - * Adding an enum val invalidates any iterators. - * - * TODO: make compatible with range-for, with elements as pairs? */ - class Iterator { - public: - explicit Iterator(EnumDefPtr e) { upb_enum_begin(&iter_, e.ptr()); } - - int32_t number() { return upb_enum_iter_number(&iter_); } - const char *name() { return upb_enum_iter_name(&iter_); } - bool Done() { return upb_enum_done(&iter_); } - void Next() { return upb_enum_next(&iter_); } - - private: - upb_enum_iter iter_; - }; - - private: - const upb_enumdef *ptr_; -}; - -inline upb::EnumDefPtr upb::FieldDefPtr::enum_subdef() const { - return EnumDefPtr(upb_fielddef_enumsubdef(ptr_)); -} - -#endif /* __cplusplus */ - /* upb_filedef ****************************************************************/ -#ifdef __cplusplus -extern "C" { -#endif - const char *upb_filedef_name(const upb_filedef *f); const char *upb_filedef_package(const upb_filedef *f); const char *upb_filedef_phpprefix(const upb_filedef *f); @@ -3974,57 +3368,8 @@ const upb_filedef *upb_filedef_dep(const upb_filedef *f, int i); const upb_msgdef *upb_filedef_msg(const upb_filedef *f, int i); const upb_enumdef *upb_filedef_enum(const upb_filedef *f, int i); -#ifdef __cplusplus -} /* extern "C" */ - -/* Class that represents a .proto file with some things defined in it. - * - * Many users won't care about FileDefs, but they are necessary if you want to - * read the values of file-level options. */ -class upb::FileDefPtr { - public: - explicit FileDefPtr(const upb_filedef *ptr) : ptr_(ptr) {} - - const upb_filedef* ptr() const { return ptr_; } - explicit operator bool() const { return ptr_ != nullptr; } - - /* Get/set name of the file (eg. "foo/bar.proto"). */ - const char* name() const { return upb_filedef_name(ptr_); } - - /* Package name for definitions inside the file (eg. "foo.bar"). */ - const char* package() const { return upb_filedef_package(ptr_); } - - /* Sets the php class prefix which is prepended to all php generated classes - * from this .proto. Default is empty. */ - const char* phpprefix() const { return upb_filedef_phpprefix(ptr_); } - - /* Use this option to change the namespace of php generated classes. Default - * is empty. When this option is empty, the package name will be used for - * determining the namespace. */ - const char* phpnamespace() const { return upb_filedef_phpnamespace(ptr_); } - - /* Syntax for the file. Defaults to proto2. */ - upb_syntax_t syntax() const { return upb_filedef_syntax(ptr_); } - - /* Get the list of dependencies from the file. These are returned in the - * order that they were added to the FileDefPtr. */ - int dependency_count() const { return upb_filedef_depcount(ptr_); } - const FileDefPtr dependency(int index) const { - return FileDefPtr(upb_filedef_dep(ptr_, index)); - } - - private: - const upb_filedef* ptr_; -}; - -#endif /* __cplusplus */ - /* upb_symtab *****************************************************************/ -#ifdef __cplusplus -extern "C" { -#endif - upb_symtab *upb_symtab_new(void); void upb_symtab_free(upb_symtab* s); const upb_msgdef *upb_symtab_lookupmsg(const upb_symtab *s, const char *sym); @@ -4047,53 +3392,11 @@ typedef struct upb_def_init { bool _upb_symtab_loaddefinit(upb_symtab *s, const upb_def_init *init); + #ifdef __cplusplus } /* extern "C" */ - -/* Non-const methods in upb::SymbolTable are NOT thread-safe. */ -class upb::SymbolTable { - public: - SymbolTable() : ptr_(upb_symtab_new(), upb_symtab_free) {} - explicit SymbolTable(upb_symtab* s) : ptr_(s, upb_symtab_free) {} - - const upb_symtab* ptr() const { return ptr_.get(); } - upb_symtab* ptr() { return ptr_.get(); } - - /* Finds an entry in the symbol table with this exact name. If not found, - * returns NULL. */ - MessageDefPtr LookupMessage(const char *sym) const { - return MessageDefPtr(upb_symtab_lookupmsg(ptr_.get(), sym)); - } - - EnumDefPtr LookupEnum(const char *sym) const { - return EnumDefPtr(upb_symtab_lookupenum(ptr_.get(), sym)); - } - - FileDefPtr LookupFile(const char *name) const { - return FileDefPtr(upb_symtab_lookupfile(ptr_.get(), name)); - } - - /* TODO: iteration? */ - - /* Adds the given serialized FileDescriptorProto to the pool. */ - FileDefPtr AddFile(const google_protobuf_FileDescriptorProto *file_proto, - Status *status) { - return FileDefPtr( - upb_symtab_addfile(ptr_.get(), file_proto, status->ptr())); - } - - private: - std::unique_ptr ptr_; -}; - -UPB_INLINE const char* upb_safecstr(const std::string& str) { - UPB_ASSERT(str.size() == std::strlen(str.c_str())); - return str.c_str(); -} - #endif /* __cplusplus */ - #endif /* UPB_DEF_H_ */ #ifndef UPB_REFLECTION_H_ diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec index bf60b6ae9e..cd9af71b6e 100644 --- a/ruby/google-protobuf.gemspec +++ b/ruby/google-protobuf.gemspec @@ -17,13 +17,13 @@ Gem::Specification.new do |s| else s.files += Dir.glob('ext/**/*') s.extensions= ["ext/google/protobuf_c/extconf.rb"] - s.add_development_dependency "rake-compiler-dock", "~> 0.6.0" + s.add_development_dependency "rake-compiler-dock", ">= 1.0.1", "< 2.0" end s.test_files = ["tests/basic.rb", "tests/stress.rb", "tests/generated_code_test.rb"] s.required_ruby_version = '>= 2.3' - s.add_development_dependency "rake-compiler", "~> 0.9.5" + s.add_development_dependency "rake-compiler", "~> 1.1.0" s.add_development_dependency "test-unit", '~> 3.0', '>= 3.0.9' s.add_development_dependency "rubygems-tasks", "~> 0.2.4" end diff --git a/ruby/tests/basic.rb b/ruby/tests/basic.rb index d1a66a6169..687e1fb934 100755 --- a/ruby/tests/basic.rb +++ b/ruby/tests/basic.rb @@ -32,11 +32,11 @@ module BasicTest include CommonTests def test_has_field - m = TestMessage.new - assert !m.has_optional_msg? - m.optional_msg = TestMessage2.new - assert m.has_optional_msg? - assert TestMessage.descriptor.lookup('optional_msg').has?(m) + m = TestSingularFields.new + assert !m.has_singular_msg? + m.singular_msg = TestMessage2.new + assert m.has_singular_msg? + assert TestSingularFields.descriptor.lookup('singular_msg').has?(m) m = OneofMessage.new assert !m.has_my_oneof? @@ -45,32 +45,31 @@ module BasicTest assert_raise NoMethodError do m.has_a? end - assert_raise ArgumentError do - OneofMessage.descriptor.lookup('a').has?(m) - end + assert_true OneofMessage.descriptor.lookup('a').has?(m) - m = TestMessage.new + m = TestSingularFields.new assert_raise NoMethodError do - m.has_optional_int32? + m.has_singular_int32? end assert_raise ArgumentError do - TestMessage.descriptor.lookup('optional_int32').has?(m) + TestSingularFields.descriptor.lookup('singular_int32').has?(m) end assert_raise NoMethodError do - m.has_optional_string? + m.has_singular_string? end assert_raise ArgumentError do - TestMessage.descriptor.lookup('optional_string').has?(m) + TestSingularFields.descriptor.lookup('singular_string').has?(m) end assert_raise NoMethodError do - m.has_optional_bool? + m.has_singular_bool? end assert_raise ArgumentError do - TestMessage.descriptor.lookup('optional_bool').has?(m) + TestSingularFields.descriptor.lookup('singular_bool').has?(m) end + m = TestMessage.new assert_raise NoMethodError do m.has_repeated_msg? end @@ -79,40 +78,59 @@ module BasicTest end end + def test_no_presence + m = TestSingularFields.new + + # Explicitly setting to zero does not cause anything to be serialized. + m.singular_int32 = 0 + assert_equal "", TestSingularFields.encode(m) + + # Explicitly setting to a non-zero value *does* cause serialization. + m.singular_int32 = 1 + assert_not_equal "", TestSingularFields.encode(m) + + m.singular_int32 = 0 + assert_equal "", TestSingularFields.encode(m) + end + def test_set_clear_defaults - m = TestMessage.new + m = TestSingularFields.new + + m.singular_int32 = -42 + assert_equal -42, m.singular_int32 + m.clear_singular_int32 + assert_equal 0, m.singular_int32 + + m.singular_int32 = 50 + assert_equal 50, m.singular_int32 + TestSingularFields.descriptor.lookup('singular_int32').clear(m) + assert_equal 0, m.singular_int32 + + m.singular_string = "foo bar" + assert_equal "foo bar", m.singular_string + m.clear_singular_string + assert_equal "", m.singular_string + + m.singular_string = "foo" + assert_equal "foo", m.singular_string + TestSingularFields.descriptor.lookup('singular_string').clear(m) + assert_equal "", m.singular_string + + m.singular_msg = TestMessage2.new(:foo => 42) + assert_equal TestMessage2.new(:foo => 42), m.singular_msg + assert m.has_singular_msg? + m.clear_singular_msg + assert_equal nil, m.singular_msg + assert !m.has_singular_msg? + + m.singular_msg = TestMessage2.new(:foo => 42) + assert_equal TestMessage2.new(:foo => 42), m.singular_msg + TestSingularFields.descriptor.lookup('singular_msg').clear(m) + assert_equal nil, m.singular_msg + end - m.optional_int32 = -42 - assert_equal -42, m.optional_int32 - m.clear_optional_int32 - assert_equal 0, m.optional_int32 - - m.optional_int32 = 50 - assert_equal 50, m.optional_int32 - TestMessage.descriptor.lookup('optional_int32').clear(m) - assert_equal 0, m.optional_int32 - - m.optional_string = "foo bar" - assert_equal "foo bar", m.optional_string - m.clear_optional_string - assert_equal "", m.optional_string - - m.optional_string = "foo" - assert_equal "foo", m.optional_string - TestMessage.descriptor.lookup('optional_string').clear(m) - assert_equal "", m.optional_string - - m.optional_msg = TestMessage2.new(:foo => 42) - assert_equal TestMessage2.new(:foo => 42), m.optional_msg - assert m.has_optional_msg? - m.clear_optional_msg - assert_equal nil, m.optional_msg - assert !m.has_optional_msg? - - m.optional_msg = TestMessage2.new(:foo => 42) - assert_equal TestMessage2.new(:foo => 42), m.optional_msg - TestMessage.descriptor.lookup('optional_msg').clear(m) - assert_equal nil, m.optional_msg + def test_clear_repeated_fields + m = TestMessage.new m.repeated_int32.push(1) assert_equal [1], m.repeated_int32 @@ -128,6 +146,7 @@ module BasicTest m.a = "foo" assert_equal "foo", m.a assert m.has_my_oneof? + assert_equal :a, m.my_oneof m.clear_a assert !m.has_my_oneof? @@ -143,7 +162,6 @@ module BasicTest assert !m.has_my_oneof? end - def test_initialization_map_errors e = assert_raise ArgumentError do TestMessage.new(:hello => "world") diff --git a/ruby/tests/basic_proto2.rb b/ruby/tests/basic_proto2.rb index 4c7ddd55b9..2d30a08944 100755 --- a/ruby/tests/basic_proto2.rb +++ b/ruby/tests/basic_proto2.rb @@ -73,10 +73,11 @@ module BasicTestProto2 m = OneofMessage.new assert !m.has_my_oneof? m.a = "foo" + assert m.has_my_oneof? + assert_equal :a, m.my_oneof assert m.has_a? assert OneofMessage.descriptor.lookup('a').has?(m) assert_equal "foo", m.a - assert m.has_my_oneof? assert !m.has_b? assert !OneofMessage.descriptor.lookup('b').has?(m) assert !m.has_c? @@ -197,6 +198,17 @@ module BasicTestProto2 assert !m.has_my_oneof? end + def test_assign_nil + m = TestMessageDefaults.new + m.optional_msg = TestMessage2.new(:foo => 42) + + assert_equal TestMessage2.new(:foo => 42), m.optional_msg + assert m.has_optional_msg? + m.optional_msg = nil + assert_equal nil, m.optional_msg + assert !m.has_optional_msg? + end + def test_initialization_map_errors e = assert_raise ArgumentError do TestMessage.new(:hello => "world") diff --git a/ruby/tests/basic_test.proto b/ruby/tests/basic_test.proto index a918540075..6086879d09 100644 --- a/ruby/tests/basic_test.proto +++ b/ruby/tests/basic_test.proto @@ -21,17 +21,17 @@ message Baz { } message TestMessage { - int32 optional_int32 = 1; - int64 optional_int64 = 2; - uint32 optional_uint32 = 3; - uint64 optional_uint64 = 4; - bool optional_bool = 5; - float optional_float = 6; - double optional_double = 7; - string optional_string = 8; - bytes optional_bytes = 9; - TestMessage2 optional_msg = 10; - TestEnum optional_enum = 11; + optional int32 optional_int32 = 1; + optional int64 optional_int64 = 2; + optional uint32 optional_uint32 = 3; + optional uint64 optional_uint64 = 4; + optional bool optional_bool = 5; + optional float optional_float = 6; + optional double optional_double = 7; + optional string optional_string = 8; + optional bytes optional_bytes = 9; + optional TestMessage2 optional_msg = 10; + optional TestEnum optional_enum = 11; repeated int32 repeated_int32 = 12; repeated int64 repeated_int64 = 13; @@ -46,6 +46,20 @@ message TestMessage { repeated TestEnum repeated_enum = 22; } +message TestSingularFields { + int32 singular_int32 = 1; + int64 singular_int64 = 2; + uint32 singular_uint32 = 3; + uint64 singular_uint64 = 4; + bool singular_bool = 5; + float singular_float = 6; + double singular_double = 7; + string singular_string = 8; + bytes singular_bytes = 9; + TestMessage2 singular_msg = 10; + TestEnum singular_enum = 11; +} + message TestMessage2 { int32 foo = 1; } diff --git a/ruby/tests/common_tests.rb b/ruby/tests/common_tests.rb index 67781467a1..1f5013a529 100644 --- a/ruby/tests/common_tests.rb +++ b/ruby/tests/common_tests.rb @@ -1739,7 +1739,7 @@ module CommonTests m.freeze frozen_error = assert_raise(FrozenErrorType) { m.optional_int32 = 20 } - assert_equal "can't modify frozen #{proto_module}::TestMessage", frozen_error.message + assert_match "can't modify frozen #{proto_module}::TestMessage", frozen_error.message assert_equal 10, m.optional_int32 assert_equal true, m.frozen? diff --git a/ruby/tests/gc_test.rb b/ruby/tests/gc_test.rb index 55b96289e8..6ef4e2e301 100755 --- a/ruby/tests/gc_test.rb +++ b/ruby/tests/gc_test.rb @@ -4,7 +4,9 @@ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) old_gc = GC.stress -GC.stress = 0x01 | 0x04 +# Ruby 2.7.0 - 2.7.1 has a GC bug in its parser, so turn off stress for now +# See https://bugs.ruby-lang.org/issues/16807 +GC.stress = 0x01 | 0x04 unless RUBY_VERSION.match?(/^2\.7\./) require 'generated_code_pb' require 'generated_code_proto2_pb' GC.stress = old_gc diff --git a/ruby/tests/repeated_field_test.rb b/ruby/tests/repeated_field_test.rb index ced9de8381..6307447bc3 100755 --- a/ruby/tests/repeated_field_test.rb +++ b/ruby/tests/repeated_field_test.rb @@ -20,6 +20,7 @@ class RepeatedFieldTest < Test::Unit::TestCase :iter_for_each_with_index, :dimensions, :copy_data, :copy_data_simple, :nitems, :iter_for_reverse_each, :indexes, :append, :prepend] arr_methods -= [:union, :difference, :filter!] + arr_methods -= [:intersection, :deconstruct] # ruby 2.7 methods we can ignore arr_methods.each do |method_name| assert m.repeated_string.respond_to?(method_name) == true, "does not respond to #{method_name}" end diff --git a/ruby/travis-test.sh b/ruby/travis-test.sh index 6dec0c906a..b39a6c5d12 100755 --- a/ruby/travis-test.sh +++ b/ruby/travis-test.sh @@ -16,7 +16,7 @@ test_version() { git clean -f && \ gem install bundler && bundle && \ rake test" - elif [ "$version" == "ruby-2.6.0" ] ; then + elif [ "$version" == "ruby-2.6.0" -o "$version" == "ruby-2.7.0" ] ; then bash --login -c \ "rvm install $version && rvm use $version && \ which ruby && \ diff --git a/src/google/protobuf/arena_impl.h b/src/google/protobuf/arena_impl.h index d7b7ed73ed..5a95124c4f 100644 --- a/src/google/protobuf/arena_impl.h +++ b/src/google/protobuf/arena_impl.h @@ -135,6 +135,8 @@ class PROTOBUF_EXPORT ArenaImpl { void AddCleanup(void* elem, void (*cleanup)(void*)); private: + friend class ArenaBenchmark; + void* AllocateAlignedFallback(size_t n); void* AllocateAlignedAndAddCleanupFallback(size_t n, void (*cleanup)(void*)); void AddCleanupFallback(void* elem, void (*cleanup)(void*)); diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc index ed64289bdd..b55070fa8f 100644 --- a/src/google/protobuf/compiler/command_line_interface.cc +++ b/src/google/protobuf/compiler/command_line_interface.cc @@ -1105,14 +1105,18 @@ PopulateSingleSimpleDescriptorDatabase(const std::string& descriptor_set_name) { bool CommandLineInterface::AllowProto3Optional( const FileDescriptor& file) const { if (allow_proto3_optional_) return true; + // Whitelist all ads protos. Ads is an early adopter of this feature. if (file.name().find("google/ads/googleads") != std::string::npos) { return true; } - if (file.name() == "google/protobuf/unittest_proto3_optional.proto" || - file.name() == "google/protobuf/internal/test_proto3_optional.proto") { + + // Whitelist all protos testing proto3 optional. + if (file.name().find("test_proto3_optional") != std::string::npos) { return true; } + + return false; } diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc index 6a73ab8048..f95e14e58b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -156,7 +156,7 @@ FieldGenerator* FieldGeneratorMap::MakeGenerator( default: return new RepeatedPrimitiveFieldGenerator(field, options); } - } else if (InRealOneof(field)) { + } else if (field->real_containing_oneof()) { switch (field->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: return new MessageOneofFieldGenerator(field, options, scc_analyzer); diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index 61f81429ca..e2961e50e3 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -34,6 +34,7 @@ #include +#include #include #include #include diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index 7aeffb0f00..976823afa8 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -1151,7 +1151,7 @@ bool IsImplicitWeakField(const FieldDescriptor* field, const Options& options, return UsingImplicitWeakFields(field->file(), options) && field->type() == FieldDescriptor::TYPE_MESSAGE && !field->is_required() && !field->is_map() && !field->is_extension() && - !InRealOneof(field) && + !field->real_containing_oneof() && !IsWellKnownMessage(field->message_type()->file()) && field->message_type()->file()->name() != "net/proto2/proto/descriptor.proto" && @@ -1474,7 +1474,7 @@ class ParseLoopGenerator { GetOptimizeFor(field->file(), options_) != FileOptions::LITE_RUNTIME && // For now only use arena string for strings with empty defaults. field->default_value_string().empty() && - !IsStringInlined(field, options_) && !InRealOneof(field) && + !IsStringInlined(field, options_) && !field->real_containing_oneof() && ctype == FieldOptions::STRING) { GenerateArenaString(field); } else { @@ -1580,7 +1580,7 @@ class ParseLoopGenerator { FieldName(field)); } } else if (IsLazy(field, options_)) { - if (InRealOneof(field)) { + if (field->real_containing_oneof()) { format_( "if (!_internal_has_$1$()) {\n" " clear_$2$();\n" @@ -1684,7 +1684,7 @@ class ParseLoopGenerator { field->type() == FieldDescriptor::TYPE_SINT64)) { zigzag = "ZigZag"; } - if (field->is_repeated() || InRealOneof(field)) { + if (field->is_repeated() || field->real_containing_oneof()) { std::string prefix = field->is_repeated() ? "add" : "set"; format_( "_internal_$1$_$2$($pi_ns$::ReadVarint$3$$4$(&ptr));\n" @@ -1706,7 +1706,7 @@ class ParseLoopGenerator { case WireFormatLite::WIRETYPE_FIXED32: case WireFormatLite::WIRETYPE_FIXED64: { std::string type = PrimitiveTypeName(options_, field->cpp_type()); - if (field->is_repeated() || InRealOneof(field)) { + if (field->is_repeated() || field->real_containing_oneof()) { std::string prefix = field->is_repeated() ? "add" : "set"; format_( "_internal_$1$_$2$($pi_ns$::UnalignedLoad<$3$>(ptr));\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index ec57cb4a5c..988e6092c1 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -444,23 +444,6 @@ inline bool HasHasbit(const FieldDescriptor* field) { !field->options().weak(); } -inline bool InRealOneof(const FieldDescriptor* field) { - return field->containing_oneof() && - !field->containing_oneof()->is_synthetic(); -} - -// In practice all synthetic oneofs should be at the end of the list, but we -// decline to depend on this for correctness of the function. -inline int RealOneofCount(const Descriptor* descriptor) { - int count = 0; - for (int i = 0; i < descriptor->oneof_decl_count(); i++) { - if (!descriptor->oneof_decl(i)->is_synthetic()) { - count++; - } - } - return count; -} - // Returns true if 'enum' semantics are such that unknown values are preserved // in the enum field itself, rather than going to the UnknownFieldSet. inline bool HasPreservingUnknownEnumSemantics(const FieldDescriptor* field) { @@ -886,14 +869,6 @@ struct OneOfRangeImpl { using value_type = const OneofDescriptor*; using difference_type = int; - explicit Iterator(const Descriptor* _descriptor) - : idx(-1), descriptor(_descriptor) { - Next(); - } - - Iterator(int _idx, const Descriptor* _descriptor) - : idx(_idx), descriptor(_descriptor) {} - value_type operator*() { return descriptor->oneof_decl(idx); } friend bool operator==(const Iterator& a, const Iterator& b) { @@ -905,23 +880,18 @@ struct OneOfRangeImpl { } Iterator& operator++() { - Next(); + idx++; return *this; } - void Next() { - do { - idx++; - } while (idx < descriptor->oneof_decl_count() && - descriptor->oneof_decl(idx)->is_synthetic()); - } - int idx; const Descriptor* descriptor; }; - Iterator begin() const { return Iterator(descriptor); } - Iterator end() const { return {descriptor->oneof_decl_count(), descriptor}; } + Iterator begin() const { return {0, descriptor}; } + Iterator end() const { + return {descriptor->real_oneof_decl_count(), descriptor}; + } const Descriptor* descriptor; }; diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 33be14694e..1cc089196e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -226,7 +226,7 @@ bool EmitFieldNonDefaultCondition(io::Printer* printer, } format.Indent(); return true; - } else if (InRealOneof(field)) { + } else if (field->real_containing_oneof()) { format("if (_internal_has_$name$()) {\n"); format.Indent(); return true; @@ -282,7 +282,7 @@ void CollectMapInfo(const Options& options, const Descriptor* descriptor, bool HasPrivateHasMethod(const FieldDescriptor* field) { // Only for oneofs in message types with no field presence. has_$name$(), // based on the oneof case, is still useful internally for generated code. - return (!HasFieldPresence(field->file()) && InRealOneof(field)); + return (!HasFieldPresence(field->file()) && field->real_containing_oneof()); } // TODO(ckennelly): Cull these exclusions if/when these protos do not have @@ -597,7 +597,7 @@ MessageGenerator::MessageGenerator( if (IsWeak(field, options_)) { num_weak_fields_++; - } else if (!InRealOneof(field)) { + } else if (!field->real_containing_oneof()) { optimized_order_.push_back(field); } } @@ -677,7 +677,7 @@ void MessageGenerator::AddGenerators( void MessageGenerator::GenerateFieldAccessorDeclarations(io::Printer* printer) { Formatter format(printer, variables_); // optimized_fields_ does not contain fields where - // InRealOneof(field) == true + // field->real_containing_oneof() // so we need to iterate over those as well. // // We place the non-oneof fields in optimized_order_, as that controls the @@ -689,7 +689,7 @@ void MessageGenerator::GenerateFieldAccessorDeclarations(io::Printer* printer) { ordered_fields.insert(ordered_fields.begin(), optimized_order_.begin(), optimized_order_.end()); for (auto field : FieldRange(descriptor_)) { - if (!InRealOneof(field) && !field->options().weak() && + if (!field->real_containing_oneof() && !field->options().weak() && IsFieldUsed(field, options_)) { continue; } @@ -922,7 +922,7 @@ void MessageGenerator::GenerateFieldClear(const FieldDescriptor* field, format.Indent(); - if (InRealOneof(field)) { + if (field->real_containing_oneof()) { // Clear this field only if it is the active field in this oneof, // otherwise ignore format("if (_internal_has_$name$()) {\n"); @@ -983,7 +983,7 @@ void MessageGenerator::GenerateFieldAccessorDefinitions(io::Printer* printer) { ? ".weak" : ""); } - } else if (InRealOneof(field)) { + } else if (field->real_containing_oneof()) { format.Set("field_name", UnderscoresToCamelCase(field->name(), true)); format.Set("oneof_name", field->containing_oneof()->name()); format.Set("oneof_index", @@ -1485,7 +1485,7 @@ void MessageGenerator::GenerateClassDefinition(io::Printer* printer) { for (auto field : FieldRange(descriptor_)) { // set_has_***() generated in all oneofs. if (!field->is_repeated() && !field->options().weak() && - InRealOneof(field)) { + field->real_containing_oneof()) { format("void set_has_$1$();\n", FieldName(field)); } } @@ -1594,12 +1594,11 @@ void MessageGenerator::GenerateClassDefinition(io::Printer* printer) { } // Generate _oneof_case_. - int count = RealOneofCount(descriptor_); - if (count > 0) { + if (descriptor_->real_oneof_decl_count() > 0) { format( "$uint32$ _oneof_case_[$1$];\n" "\n", - count); + descriptor_->real_oneof_decl_count()); } if (num_weak_fields_) { @@ -1695,7 +1694,7 @@ bool MessageGenerator::GenerateParseTable(io::Printer* printer, size_t offset, format("PROTOBUF_FIELD_OFFSET($classtype$, _has_bits_),\n"); } - if (RealOneofCount(descriptor_) > 0) { + if (descriptor_->real_oneof_decl_count() > 0) { format("PROTOBUF_FIELD_OFFSET($classtype$, _oneof_case_),\n"); } else { format("-1, // no _oneof_case_\n"); @@ -1755,7 +1754,7 @@ uint32 CalcFieldNum(const FieldGenerator& generator, } } - if (InRealOneof(field)) { + if (field->real_containing_oneof()) { return internal::FieldMetadata::CalculateType( type, internal::FieldMetadata::kOneOf); } else if (field->is_packed()) { @@ -1764,7 +1763,7 @@ uint32 CalcFieldNum(const FieldGenerator& generator, } else if (field->is_repeated()) { return internal::FieldMetadata::CalculateType( type, internal::FieldMetadata::kRepeated); - } else if (HasHasbit(field) || InRealOneof(field) || is_a_map) { + } else if (HasHasbit(field) || field->real_containing_oneof() || is_a_map) { return internal::FieldMetadata::CalculateType( type, internal::FieldMetadata::kPresence); } else { @@ -1859,7 +1858,7 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { } std::string classfieldname = FieldName(field); - if (InRealOneof(field)) { + if (field->real_containing_oneof()) { classfieldname = field->containing_oneof()->name(); } format.Set("field_name", classfieldname); @@ -1895,7 +1894,7 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { type = internal::FieldMetadata::kSpecial; ptr = "reinterpret_cast(::" + variables_["proto_ns"] + "::internal::LazyFieldSerializer"; - if (InRealOneof(field)) { + if (field->real_containing_oneof()) { ptr += "OneOf"; } else if (!HasHasbit(field)) { ptr += "NoPresence"; @@ -1912,7 +1911,7 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { "reinterpret_cast(::$proto_ns$::internal::WeakFieldSerializer)},\n", tag); - } else if (InRealOneof(field)) { + } else if (field->real_containing_oneof()) { format.Set("oneofoffset", sizeof(uint32) * field->containing_oneof()->index()); format( @@ -1972,10 +1971,10 @@ void MessageGenerator::GenerateDefaultInstanceInitializer( if (!field->is_repeated() && !IsLazy(field, options_) && field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && - (!InRealOneof(field) || + (!field->real_containing_oneof() || HasDescriptorMethods(descriptor_->file(), options_))) { std::string name; - if (InRealOneof(field) || field->options().weak()) { + if (field->real_containing_oneof() || field->options().weak()) { name = "_" + classname_ + "_default_instance_."; } else { name = @@ -2007,7 +2006,7 @@ void MessageGenerator::GenerateDefaultInstanceInitializer( " $1$::internal_default_instance());\n", FieldMessageTypeName(field, options_)); } - } else if (InRealOneof(field) && + } else if (field->real_containing_oneof() && HasDescriptorMethods(descriptor_->file(), options_)) { field_generators_.get(field).GenerateConstructorCode(printer); } @@ -2118,7 +2117,7 @@ void MessageGenerator::GenerateClassMethods(io::Printer* printer) { Formatter::SaveState saver(&format); std::map vars; SetCommonFieldVariables(field, &vars, options_); - if (InRealOneof(field)) { + if (field->real_containing_oneof()) { SetCommonOneofFieldVariables(field, &vars); } format.AddMap(vars); @@ -2129,7 +2128,7 @@ void MessageGenerator::GenerateClassMethods(io::Printer* printer) { GenerateStructors(printer); format("\n"); - if (RealOneofCount(descriptor_) > 0) { + if (descriptor_->real_oneof_decl_count() > 0) { GenerateOneofClear(printer); format("\n"); } @@ -2258,8 +2257,8 @@ size_t MessageGenerator::GenerateParseOffsets(io::Printer* printer) { processing_type |= static_cast( field->is_repeated() ? internal::kRepeatedMask : 0); - processing_type |= - static_cast(InRealOneof(field) ? internal::kOneofMask : 0); + processing_type |= static_cast( + field->real_containing_oneof() ? internal::kOneofMask : 0); if (field->is_map()) { processing_type = internal::TYPE_MAP; @@ -2269,7 +2268,7 @@ size_t MessageGenerator::GenerateParseOffsets(io::Printer* printer) { WireFormat::TagSize(field->number(), field->type()); std::map vars; - if (InRealOneof(field)) { + if (field->real_containing_oneof()) { vars["name"] = field->containing_oneof()->name(); vars["presence"] = StrCat(field->containing_oneof()->index()); } else { @@ -2400,7 +2399,7 @@ std::pair MessageGenerator::GenerateOffsets( } else { format("~0u, // no _extensions_\n"); } - if (RealOneofCount(descriptor_) > 0) { + if (descriptor_->real_oneof_decl_count() > 0) { format("PROTOBUF_FIELD_OFFSET($classtype$, _oneof_case_[0]),\n"); } else { format("~0u, // no _oneof_case_\n"); @@ -2418,13 +2417,13 @@ std::pair MessageGenerator::GenerateOffsets( } const int kNumGenericOffsets = 5; // the number of fixed offsets above const size_t offsets = kNumGenericOffsets + descriptor_->field_count() + - RealOneofCount(descriptor_) - num_stripped; + descriptor_->real_oneof_decl_count() - num_stripped; size_t entries = offsets; for (auto field : FieldRange(descriptor_)) { if (!IsFieldUsed(field, options_)) { continue; } - if (InRealOneof(field) || field->options().weak()) { + if (field->real_containing_oneof() || field->options().weak()) { format("offsetof($classtype$DefaultTypeInternal, $1$_)", FieldName(field)); } else { @@ -2444,7 +2443,7 @@ std::pair MessageGenerator::GenerateOffsets( format("PROTOBUF_FIELD_OFFSET($classtype$, $1$_),\n", oneof->name()); count++; } - GOOGLE_CHECK_EQ(count, RealOneofCount(descriptor_)); + GOOGLE_CHECK_EQ(count, descriptor_->real_oneof_decl_count()); if (IsMapEntryMessage(descriptor_)) { entries += 2; @@ -2656,7 +2655,7 @@ void MessageGenerator::GenerateStructors(io::Printer* printer) { for (auto field : optimized_order_) { GOOGLE_DCHECK(IsFieldUsed(field, options_)); bool has_arena_constructor = field->is_repeated(); - if (!InRealOneof(field) && + if (!field->real_containing_oneof() && (IsLazy(field, options_) || IsStringPiece(field, options_))) { has_arena_constructor = true; } @@ -3122,8 +3121,7 @@ void MessageGenerator::GenerateSwap(io::Printer* printer) { format("swap($1$_, other->$1$_);\n", oneof->name()); } - int count = RealOneofCount(descriptor_); - for (int i = 0; i < count; i++) { + for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) { format("swap(_oneof_case_[$1$], other->_oneof_case_[$1$]);\n", i); } @@ -3572,7 +3570,7 @@ void MessageGenerator::GenerateSerializeWithCachedSizesBody( if (eager_ || MustFlush(field)) { Flush(); } - if (!InRealOneof(field)) { + if (!field->real_containing_oneof()) { // TODO(ckennelly): Defer non-oneof fields similarly to oneof fields. if (!field->options().weak() && !field->is_repeated() && !eager_) { @@ -4014,7 +4012,7 @@ void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { } else if (field->options().weak()) { continue; } else { - GOOGLE_CHECK(!InRealOneof(field)); + GOOGLE_CHECK(!field->real_containing_oneof()); format( "if (_internal_has_$1$()) {\n" " if (!$1$_->IsInitialized()) return false;\n" @@ -4054,7 +4052,7 @@ void MessageGenerator::GenerateIsInitialized(io::Printer* printer) { field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE && !ShouldIgnoreRequiredFieldCheck(field, options_) && scc_analyzer_->HasRequiredFields(field->message_type())) { - GOOGLE_CHECK(!(field->options().weak() || !InRealOneof(field))); + GOOGLE_CHECK(!(field->options().weak() || !field->real_containing_oneof())); if (field->options().weak()) { // Just skip. } else { diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 43fe860620..a21c4c7fbb 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -501,7 +501,7 @@ void StringFieldGenerator::GenerateMessageClearingCode( void StringFieldGenerator::GenerateMergingCode(io::Printer* printer) const { Formatter format(printer, variables_); - if (SupportsArenas(descriptor_) || InRealOneof(descriptor_)) { + if (SupportsArenas(descriptor_) || descriptor_->real_containing_oneof()) { // TODO(gpike): improve this format("_internal_set_$name$(from._internal_$name$());\n"); } else { @@ -545,7 +545,7 @@ void StringFieldGenerator::GenerateCopyConstructorCode( format.Indent(); - if (SupportsArenas(descriptor_) || InRealOneof(descriptor_)) { + if (SupportsArenas(descriptor_) || descriptor_->real_containing_oneof()) { // TODO(gpike): improve this format( "$name$_.Set$lite$($default_variable$, from._internal_$name$(),\n" diff --git a/src/google/protobuf/compiler/csharp/csharp_field_base.cc b/src/google/protobuf/compiler/csharp/csharp_field_base.cc index 454f4cb144..c69e24807b 100644 --- a/src/google/protobuf/compiler/csharp/csharp_field_base.cc +++ b/src/google/protobuf/compiler/csharp/csharp_field_base.cc @@ -96,13 +96,13 @@ void FieldGeneratorBase::SetCommonFieldVariables( (*variables)["default_value"] = default_value(); (*variables)["capitalized_type_name"] = capitalized_type_name(); (*variables)["number"] = number(); - if (has_default_value() && !IsProto2(descriptor_->file())) { + if (has_default_value() && !SupportsPresenceApi(descriptor_)) { (*variables)["name_def_message"] = (*variables)["name"] + "_ = " + (*variables)["default_value"]; } else { (*variables)["name_def_message"] = (*variables)["name"] + "_"; } - if (IsProto2(descriptor_->file())) { + if (SupportsPresenceApi(descriptor_)) { (*variables)["has_property_check"] = "Has" + (*variables)["property_name"]; (*variables)["other_has_property_check"] = "other.Has" + (*variables)["property_name"]; (*variables)["has_not_property_check"] = "!" + (*variables)["has_property_check"]; @@ -125,7 +125,7 @@ void FieldGeneratorBase::SetCommonFieldVariables( void FieldGeneratorBase::SetCommonOneofFieldVariables( std::map* variables) { (*variables)["oneof_name"] = oneof_name(); - if (IsProto2(descriptor_->file())) { + if (SupportsPresenceApi(descriptor_)) { (*variables)["has_property_check"] = "Has" + property_name(); } else { (*variables)["has_property_check"] = diff --git a/src/google/protobuf/compiler/csharp/csharp_generator.cc b/src/google/protobuf/compiler/csharp/csharp_generator.cc index b335522032..c2170f17d1 100644 --- a/src/google/protobuf/compiler/csharp/csharp_generator.cc +++ b/src/google/protobuf/compiler/csharp/csharp_generator.cc @@ -48,6 +48,13 @@ namespace protobuf { namespace compiler { namespace csharp { +Generator::Generator() {} +Generator::~Generator() {} + +uint64 Generator::GetSupportedFeatures() const { + return CodeGenerator::Feature::FEATURE_PROTO3_OPTIONAL; +} + void GenerateFile(const FileDescriptor* file, io::Printer* printer, const Options* options) { ReflectionClassGenerator reflectionClassGenerator(file, options); diff --git a/src/google/protobuf/compiler/csharp/csharp_generator.h b/src/google/protobuf/compiler/csharp/csharp_generator.h index da72e0e776..76806db16c 100644 --- a/src/google/protobuf/compiler/csharp/csharp_generator.h +++ b/src/google/protobuf/compiler/csharp/csharp_generator.h @@ -50,11 +50,14 @@ namespace csharp { // CodeGenerator with the CommandLineInterface in your main() function. class PROTOC_EXPORT Generator : public CodeGenerator { public: - virtual bool Generate( - const FileDescriptor* file, - const string& parameter, - GeneratorContext* generator_context, - string* error) const; + Generator(); + ~Generator(); + bool Generate( + const FileDescriptor* file, + const string& parameter, + GeneratorContext* generator_context, + string* error) const override; + uint64 GetSupportedFeatures() const override; }; } // namespace csharp diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/src/google/protobuf/compiler/csharp/csharp_helpers.cc index 98aa246c28..c7a0d4fa98 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.cc +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.cc @@ -515,13 +515,13 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, } } else { if (IsWrapperType(descriptor)) { - if (descriptor->containing_oneof()) { + if (descriptor->real_containing_oneof()) { return new WrapperOneofFieldGenerator(descriptor, presenceIndex, options); } else { return new WrapperFieldGenerator(descriptor, presenceIndex, options); } } else { - if (descriptor->containing_oneof()) { + if (descriptor->real_containing_oneof()) { return new MessageOneofFieldGenerator(descriptor, presenceIndex, options); } else { return new MessageFieldGenerator(descriptor, presenceIndex, options); @@ -532,7 +532,7 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, if (descriptor->is_repeated()) { return new RepeatedEnumFieldGenerator(descriptor, presenceIndex, options); } else { - if (descriptor->containing_oneof()) { + if (descriptor->real_containing_oneof()) { return new EnumOneofFieldGenerator(descriptor, presenceIndex, options); } else { return new EnumFieldGenerator(descriptor, presenceIndex, options); @@ -542,7 +542,7 @@ FieldGeneratorBase* CreateFieldGenerator(const FieldDescriptor* descriptor, if (descriptor->is_repeated()) { return new RepeatedPrimitiveFieldGenerator(descriptor, presenceIndex, options); } else { - if (descriptor->containing_oneof()) { + if (descriptor->real_containing_oneof()) { return new PrimitiveOneofFieldGenerator(descriptor, presenceIndex, options); } else { return new PrimitiveFieldGenerator(descriptor, presenceIndex, options); diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h index 6354e9e6aa..d295bbe803 100644 --- a/src/google/protobuf/compiler/csharp/csharp_helpers.h +++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h @@ -158,6 +158,34 @@ inline bool IsProto2(const FileDescriptor* descriptor) { return descriptor->syntax() == FileDescriptor::SYNTAX_PROTO2; } +inline bool SupportsPresenceApi(const FieldDescriptor* descriptor) { + // We don't use descriptor->is_singular_with_presence() as C# has slightly + // different behavior to other languages. + + if (IsProto2(descriptor->file())) { + // We generate Has/Clear for oneof fields in C#, as well as for messages. + // It's possible that we shouldn't, but stopping doing so would be a + // breaking change for proto2. Fortunately the decision is moot for + // onoeof in proto3: you can't use "optional" inside a oneof. + // Proto2: every singular field has presence. (Even fields in oneofs.) + return !descriptor->is_repeated(); + } else { + // Proto3: only for explictly-optional fields that aren't messages. + // (Repeated fields can never be explicitly optional, so we don't need + // to check for that.) Currently we can't get at proto3_optional directly, + // but we can use has_optional_keyword() as a surrogate check. + return descriptor->has_optional_keyword() && + descriptor->type() != FieldDescriptor::TYPE_MESSAGE; + } +} + +inline bool RequiresPresenceBit(const FieldDescriptor* descriptor) { + return SupportsPresenceApi(descriptor) && + !IsNullable(descriptor) && + !descriptor->is_extension() && + !descriptor->real_containing_oneof(); +} + } // namespace csharp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/csharp/csharp_message.cc b/src/google/protobuf/compiler/csharp/csharp_message.cc index cf00e8c7e2..4ff0c7b17a 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message.cc @@ -72,15 +72,13 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, std::sort(fields_by_number_.begin(), fields_by_number_.end(), CompareFieldNumbers); - if (IsProto2(descriptor_->file())) { - int primitiveCount = 0; - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - if (!IsNullable(field)) { - primitiveCount++; - if (has_bit_field_count_ == 0 || (primitiveCount % 32) == 0) { - has_bit_field_count_++; - } + int presence_bit_count = 0; + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + if (RequiresPresenceBit(field)) { + presence_bit_count++; + if (has_bit_field_count_ == 0 || (presence_bit_count % 32) == 0) { + has_bit_field_count_++; } } } @@ -223,11 +221,12 @@ void MessageGenerator::Generate(io::Printer* printer) { printer->Print("\n"); } - // oneof properties - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false); - vars["property_name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true); - vars["original_name"] = descriptor_->oneof_decl(i)->name(); + // oneof properties (for real oneofs, which come before synthetic ones) + for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) { + const OneofDescriptor* oneof = descriptor_->oneof_decl(i); + vars["name"] = UnderscoresToCamelCase(oneof->name(), false); + vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true); + vars["original_name"] = oneof->name(); printer->Print( vars, "private object $name$_;\n" @@ -235,8 +234,8 @@ void MessageGenerator::Generate(io::Printer* printer) { "public enum $property_name$OneofCase {\n"); printer->Indent(); printer->Print("None = 0,\n"); - for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { - const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + for (int j = 0; j < oneof->field_count(); j++) { + const FieldDescriptor* field = oneof->field(j); printer->Print("$field_property_name$ = $index$,\n", "field_property_name", GetPropertyName(field), "index", StrCat(field->number())); @@ -383,23 +382,24 @@ void MessageGenerator::GenerateCloningCode(io::Printer* printer) { for (int i = 0; i < has_bit_field_count_; i++) { printer->Print("_hasBits$i$ = other._hasBits$i$;\n", "i", StrCat(i)); } - // Clone non-oneof fields first + // Clone non-oneof fields first (treating optional proto3 fields as non-oneof) for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - std::unique_ptr generator( - CreateFieldGeneratorInternal(descriptor_->field(i))); - generator->GenerateCloningCode(printer); + const FieldDescriptor* field = descriptor_->field(i); + if (field->real_containing_oneof()) { + continue; } - } - // Clone just the right field for each oneof - for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { - vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false); - vars["property_name"] = UnderscoresToCamelCase( - descriptor_->oneof_decl(i)->name(), true); + std::unique_ptr generator(CreateFieldGeneratorInternal(field)); + generator->GenerateCloningCode(printer); + } + // Clone just the right field for each real oneof + for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i) { + const OneofDescriptor* oneof = descriptor_->oneof_decl(i); + vars["name"] = UnderscoresToCamelCase(oneof->name(), false); + vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true); printer->Print(vars, "switch (other.$property_name$Case) {\n"); printer->Indent(); - for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { - const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + for (int j = 0; j < oneof->field_count(); j++) { + const FieldDescriptor* field = oneof->field(j); std::unique_ptr generator(CreateFieldGeneratorInternal(field)); vars["field_property_name"] = GetPropertyName(field); printer->Print( @@ -462,9 +462,9 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) { CreateFieldGeneratorInternal(descriptor_->field(i))); generator->WriteEquals(printer); } - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print("if ($property_name$Case != other.$property_name$Case) return false;\n", - "property_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true)); + for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) { + printer->Print("if ($property_name$Case != other.$property_name$Case) return false;\n", + "property_name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true)); } if (has_extension_ranges_) { printer->Print( @@ -489,9 +489,9 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) { CreateFieldGeneratorInternal(descriptor_->field(i))); generator->WriteHash(printer); } - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { - printer->Print("hash ^= (int) $name$Case_;\n", - "name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false)); + for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) { + printer->Print("hash ^= (int) $name$Case_;\n", + "name", UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false)); } if (has_extension_ranges_) { printer->Print( @@ -590,22 +590,24 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { "if (other == null) {\n" " return;\n" "}\n"); - // Merge non-oneof fields + // Merge non-oneof fields, treating optional proto3 fields as normal fields for (int i = 0; i < descriptor_->field_count(); i++) { - if (!descriptor_->field(i)->containing_oneof()) { - std::unique_ptr generator( - CreateFieldGeneratorInternal(descriptor_->field(i))); - generator->GenerateMergingCode(printer); + const FieldDescriptor* field = descriptor_->field(i); + if (field->real_containing_oneof()) { + continue; } - } - // Merge oneof fields - for (int i = 0; i < descriptor_->oneof_decl_count(); ++i) { - vars["name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), false); - vars["property_name"] = UnderscoresToCamelCase(descriptor_->oneof_decl(i)->name(), true); + std::unique_ptr generator(CreateFieldGeneratorInternal(field)); + generator->GenerateMergingCode(printer); + } + // Merge oneof fields (for non-synthetic oneofs) + for (int i = 0; i < descriptor_->real_oneof_decl_count(); ++i) { + const OneofDescriptor* oneof = descriptor_->oneof_decl(i); + vars["name"] = UnderscoresToCamelCase(oneof->name(), false); + vars["property_name"] = UnderscoresToCamelCase(oneof->name(), true); printer->Print(vars, "switch (other.$property_name$Case) {\n"); printer->Indent(); - for (int j = 0; j < descriptor_->oneof_decl(i)->field_count(); j++) { - const FieldDescriptor* field = descriptor_->oneof_decl(i)->field(j); + for (int j = 0; j < oneof->field_count(); j++) { + const FieldDescriptor* field = oneof->field(j); vars["field_property_name"] = GetPropertyName(field); printer->Print( vars, @@ -706,8 +708,7 @@ void MessageGenerator::GenerateMergingMethods(io::Printer* printer) { // it's a waste of space to track presence for all values, so we only track them if they're not nullable int MessageGenerator::GetPresenceIndex(const FieldDescriptor* descriptor) { - if (IsNullable(descriptor) || !IsProto2(descriptor->file()) || - descriptor->is_extension()) { + if (!RequiresPresenceBit(descriptor)) { return -1; } @@ -717,7 +718,7 @@ int MessageGenerator::GetPresenceIndex(const FieldDescriptor* descriptor) { if (field == descriptor) { return index; } - if (!IsNullable(field)) { + if (RequiresPresenceBit(field)) { index++; } } diff --git a/src/google/protobuf/compiler/csharp/csharp_message_field.cc b/src/google/protobuf/compiler/csharp/csharp_message_field.cc index 4125798312..034fbd9242 100644 --- a/src/google/protobuf/compiler/csharp/csharp_message_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_message_field.cc @@ -53,7 +53,7 @@ MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor, int presenceIndex, const Options *options) : FieldGeneratorBase(descriptor, presenceIndex, options) { - if (!IsProto2(descriptor_->file())) { + if (!SupportsPresenceApi(descriptor_)) { variables_["has_property_check"] = name() + "_ != null"; variables_["has_not_property_check"] = name() + "_ == null"; } @@ -77,7 +77,7 @@ void MessageFieldGenerator::GenerateMembers(io::Printer* printer) { " $name$_ = value;\n" " }\n" "}\n"); - if (IsProto2(descriptor_->file())) { + if (SupportsPresenceApi(descriptor_)) { printer->Print( variables_, "/// Gets whether the $descriptor_name$ field is set\n"); @@ -228,7 +228,7 @@ void MessageOneofFieldGenerator::GenerateMembers(io::Printer* printer) { " $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n" " }\n" "}\n"); - if (IsProto2(descriptor_->file())) { + if (SupportsPresenceApi(descriptor_)) { printer->Print( variables_, "/// Gets whether the \"$descriptor_name$\" field is set\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc index eb7f70dda6..9df1dd6abd 100644 --- a/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_primitive_field.cc @@ -53,7 +53,7 @@ PrimitiveFieldGenerator::PrimitiveFieldGenerator( // TODO(jonskeet): Make this cleaner... is_value_type = descriptor->type() != FieldDescriptor::TYPE_STRING && descriptor->type() != FieldDescriptor::TYPE_BYTES; - if (!is_value_type && !IsProto2(descriptor_->file())) { + if (!is_value_type && !SupportsPresenceApi(descriptor_)) { variables_["has_property_check"] = variables_["property_name"] + ".Length != 0"; variables_["other_has_property_check"] = "other." + variables_["property_name"] + ".Length != 0"; } @@ -63,42 +63,65 @@ PrimitiveFieldGenerator::~PrimitiveFieldGenerator() { } void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { - // TODO(jonskeet): Work out whether we want to prevent the fields from ever being - // null, or whether we just handle it, in the cases of bytes and string. - // (Basically, should null-handling code be in the getter or the setter?) + + // Note: in multiple places, this code assumes that all fields + // that support presence are either nullable, or use a presence field bit. + // Fields which are oneof members are not generated here; they're generated in PrimitiveOneofFieldGenerator below. + // Extensions are not generated here either. + + + // Proto2 allows different default values to be specified. These are retained + // via static fields. They don't particularly need to be, but we don't need + // to change that. In Proto3 the default value we don't generate these + // fields, just using the literal instead. if (IsProto2(descriptor_->file())) { + // Note: "private readonly static" isn't as idiomatic as + // "private static readonly", but changing this now would create a lot of + // churn in generated code with near-to-zero benefit. printer->Print( variables_, "private readonly static $type_name$ $property_name$DefaultValue = $default_value$;\n\n"); + variables_["default_value_access"] = + variables_["property_name"] + "DefaultValue"; + } else { + variables_["default_value_access"] = variables_["default_value"]; } + // Declare the field itself. printer->Print( variables_, "private $type_name$ $name_def_message$;\n"); WritePropertyDocComment(printer, descriptor_); AddPublicMemberAttributes(printer); - if (IsProto2(descriptor_->file())) { - if (presenceIndex_ == -1) { + + // Most of the work is done in the property: + // Declare the property itself (the same for all options) + printer->Print(variables_, "$access_level$ $type_name$ $property_name$ {\n"); + + // Specify the "getter", which may need to check for a presence field. + if (SupportsPresenceApi(descriptor_)) { + if (IsNullable(descriptor_)) { printer->Print( variables_, - "$access_level$ $type_name$ $property_name$ {\n" - " get { return $name$_ ?? $property_name$DefaultValue; }\n" - " set {\n"); + " get { return $name$_ ?? $default_value_access$; }\n"); } else { printer->Print( variables_, - "$access_level$ $type_name$ $property_name$ {\n" - " get { if ($has_field_check$) { return $name$_; } else { return $property_name$DefaultValue; } }\n" - " set {\n"); + // Note: it's possible that this could be rewritten as a + // conditional ?: expression, but there's no significant benefit + // to changing it. + " get { if ($has_field_check$) { return $name$_; } else { return $default_value_access$; } }\n"); } } else { printer->Print( variables_, - "$access_level$ $type_name$ $property_name$ {\n" - " get { return $name$_; }\n" - " set {\n"); + " get { return $name$_; }\n"); } + + // Specify the "setter", which may need to set a field bit as well as the + // value. + printer->Print(" set {\n"); if (presenceIndex_ != -1) { printer->Print( variables_, @@ -116,8 +139,11 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { printer->Print( " }\n" "}\n"); - if (IsProto2(descriptor_->file())) { - printer->Print(variables_, "/// Gets whether the \"$descriptor_name$\" field is set\n"); + + // The "HasFoo" property, where required. + if (SupportsPresenceApi(descriptor_)) { + printer->Print(variables_, + "/// Gets whether the \"$descriptor_name$\" field is set\n"); AddPublicMemberAttributes(printer); printer->Print( variables_, @@ -133,8 +159,11 @@ void PrimitiveFieldGenerator::GenerateMembers(io::Printer* printer) { "$has_field_check$; }\n}\n"); } } - if (IsProto2(descriptor_->file())) { - printer->Print(variables_, "/// Clears the value of the \"$descriptor_name$\" field\n"); + + // The "ClearFoo" method, where required. + if (SupportsPresenceApi(descriptor_)) { + printer->Print(variables_, + "/// Clears the value of the \"$descriptor_name$\" field\n"); AddPublicMemberAttributes(printer); printer->Print( variables_, @@ -270,7 +299,7 @@ void PrimitiveOneofFieldGenerator::GenerateMembers(io::Printer* printer) { " $oneof_name$Case_ = $oneof_property_name$OneofCase.$property_name$;\n" " }\n" "}\n"); - if (IsProto2(descriptor_->file())) { + if (SupportsPresenceApi(descriptor_)) { printer->Print( variables_, "/// Gets whether the \"$descriptor_name$\" field is set\n"); diff --git a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc index fb067abfc9..cada81cd9a 100644 --- a/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc +++ b/src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc @@ -81,7 +81,7 @@ void WrapperFieldGenerator::GenerateMembers(io::Printer* printer) { " $name$_ = value;\n" " }\n" "}\n\n"); - if (IsProto2(descriptor_->file())) { + if (SupportsPresenceApi(descriptor_)) { printer->Print( variables_, "/// Gets whether the $descriptor_name$ field is set\n"); @@ -219,7 +219,7 @@ void WrapperOneofFieldGenerator::GenerateMembers(io::Printer* printer) { " $oneof_name$Case_ = value == null ? $oneof_property_name$OneofCase.None : $oneof_property_name$OneofCase.$property_name$;\n" " }\n" "}\n"); - if (IsProto2(descriptor_->file())) { + if (SupportsPresenceApi(descriptor_)) { printer->Print( variables_, "/// Gets whether the \"$descriptor_name$\" field is set\n"); diff --git a/src/google/protobuf/compiler/java/java_enum_field_lite.cc b/src/google/protobuf/compiler/java/java_enum_field_lite.cc index 7966931cfa..23aea9b20b 100644 --- a/src/google/protobuf/compiler/java/java_enum_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_field_lite.cc @@ -103,9 +103,6 @@ void SetEnumVariables(const FieldDescriptor* descriptor, int messageBitIndex, ".getNumber()"; } - // For repeated builders, the underlying list tracks mutability state. - (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; - (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -572,9 +569,11 @@ void RepeatedImmutableEnumFieldLiteGenerator::GenerateMembers( printer->Print( variables_, "private void ensure$capitalized_name$IsMutable() {\n" - " if (!$is_mutable$) {\n" + // Use a temporary to avoid a redundant iget-object. + " com.google.protobuf.Internal.IntList tmp = $name$_;\n" + " if (!tmp.isModifiable()) {\n" " $name$_ =\n" - " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n" " }\n" "}\n"); WriteFieldAccessorDocComment(printer, descriptor_, LIST_INDEXED_SETTER); diff --git a/src/google/protobuf/compiler/java/java_file.cc b/src/google/protobuf/compiler/java/java_file.cc index bc4bbe8f6f..2fc7aadff6 100644 --- a/src/google/protobuf/compiler/java/java_file.cc +++ b/src/google/protobuf/compiler/java/java_file.cc @@ -94,7 +94,9 @@ bool CollectExtensions(const Message& message, FieldDescriptorSet* extensions) { reflection->ListFields(message, &fields); for (int i = 0; i < fields.size(); i++) { - if (fields[i]->is_extension()) extensions->insert(fields[i]); + if (fields[i]->is_extension()) { + extensions->insert(fields[i]); + } if (GetJavaType(fields[i]) == JAVATYPE_MESSAGE) { if (fields[i]->is_repeated()) { diff --git a/src/google/protobuf/compiler/java/java_message_field_lite.cc b/src/google/protobuf/compiler/java/java_message_field_lite.cc index 41d33eefcf..b17859d6b3 100644 --- a/src/google/protobuf/compiler/java/java_message_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_message_field_lite.cc @@ -89,9 +89,6 @@ void SetMessageVariables(const FieldDescriptor* descriptor, int messageBitIndex, (*variables)["name"] + "_ != null"; } - // For repeated builders, the underlying list tracks mutability state. - (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; - (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -532,9 +529,11 @@ void RepeatedImmutableMessageFieldLiteGenerator::GenerateMembers( printer->Print( variables_, "private void ensure$capitalized_name$IsMutable() {\n" - " if (!$is_mutable$) {\n" + // Use a temporary to avoid a redundant iget-object. + " com.google.protobuf.Internal.ProtobufList<$type$> tmp = $name$_;\n" + " if (!tmp.isModifiable()) {\n" " $name$_ =\n" - " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n" " }\n" "}\n" "\n"); diff --git a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc index 65d0bd0d66..ae40bf76f3 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -164,9 +164,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, } } - // For repeated builders, the underlying list tracks mutability state. - (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; - (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -511,9 +508,11 @@ void RepeatedImmutablePrimitiveFieldLiteGenerator::GenerateMembers( printer->Print( variables_, "private void ensure$capitalized_name$IsMutable() {\n" - " if (!$is_mutable$) {\n" + // Use a temporary to avoid a redundant iget-object. + " $field_list_type$ tmp = $name$_;\n" + " if (!tmp.isModifiable()) {\n" " $name$_ =\n" - " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n" " }\n" "}\n"); diff --git a/src/google/protobuf/compiler/java/java_string_field_lite.cc b/src/google/protobuf/compiler/java/java_string_field_lite.cc index 25e46d9e3f..432a789551 100644 --- a/src/google/protobuf/compiler/java/java_string_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_string_field_lite.cc @@ -104,9 +104,6 @@ void SetPrimitiveVariables(const FieldDescriptor* descriptor, "!" + (*variables)["name"] + "_.isEmpty()"; } - // For repeated builders, the underlying list tracks mutability state. - (*variables)["is_mutable"] = (*variables)["name"] + "_.isModifiable()"; - (*variables)["get_has_field_bit_from_local"] = GenerateGetBitFromLocal(builderBitIndex); (*variables)["set_has_field_bit_to_local"] = @@ -567,9 +564,12 @@ void RepeatedImmutableStringFieldLiteGenerator::GenerateMembers( printer->Print( variables_, "private void ensure$capitalized_name$IsMutable() {\n" - " if (!$is_mutable$) {\n" + // Use a temporary to avoid a redundant iget-object. + " com.google.protobuf.Internal.ProtobufList tmp =\n" + " $name$_;" + " if (!tmp.isModifiable()) {\n" " $name$_ =\n" - " com.google.protobuf.GeneratedMessageLite.mutableCopy($name$_);\n" + " com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n" " }\n" "}\n"); diff --git a/src/google/protobuf/compiler/mock_code_generator.h b/src/google/protobuf/compiler/mock_code_generator.h index 302296d56d..70a840e6da 100644 --- a/src/google/protobuf/compiler/mock_code_generator.h +++ b/src/google/protobuf/compiler/mock_code_generator.h @@ -106,9 +106,8 @@ class MockCodeGenerator : public CodeGenerator { // implements CodeGenerator ---------------------------------------- - virtual bool Generate(const FileDescriptor* file, - const std::string& parameter, GeneratorContext* context, - std::string* error) const override; + bool Generate(const FileDescriptor* file, const std::string& parameter, + GeneratorContext* context, std::string* error) const override; uint64 GetSupportedFeatures() const override; void SuppressFeatures(uint64 features); diff --git a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc index 9890d0a140..3893801fa4 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc @@ -104,13 +104,13 @@ void EnumFieldGenerator::GenerateCFunctionImplementations( "int32_t $owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message) {\n" " GPBDescriptor *descriptor = [$owning_message_class$ descriptor];\n" " GPBFieldDescriptor *field = [descriptor fieldWithNumber:$field_number_name$];\n" - " return GPBGetMessageInt32Field(message, field);\n" + " return GPBGetMessageRawEnumField(message, field);\n" "}\n" "\n" "void Set$owning_message_class$_$capitalized_name$_RawValue($owning_message_class$ *message, int32_t value) {\n" " GPBDescriptor *descriptor = [$owning_message_class$ descriptor];\n" " GPBFieldDescriptor *field = [descriptor fieldWithNumber:$field_number_name$];\n" - " GPBSetInt32IvarWithFieldInternal(message, field, value, descriptor.file.syntax);\n" + " GPBSetMessageRawEnumField(message, field, value);\n" "}\n" "\n"); } diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_field.cc index 829f4258f9..e8360a515b 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_field.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_field.cc @@ -91,6 +91,14 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, if (descriptor->type() == FieldDescriptor::TYPE_ENUM) { field_flags.push_back("GPBFieldHasEnumDescriptor"); } + // It will clear on a zero value if... + // - not repeated/map + // - doesn't have presence + bool clear_on_zero = + (!descriptor->is_repeated() && !descriptor->has_presence()); + if (clear_on_zero) { + field_flags.push_back("GPBFieldClearHasIvarOnZero"); + } (*variables)["fieldflags"] = BuildFlagsString(FLAGTYPE_FIELD, field_flags); @@ -238,13 +246,18 @@ void FieldGenerator::SetExtraRuntimeHasBitsBase(int index_base) { } void FieldGenerator::SetOneofIndexBase(int index_base) { - if (descriptor_->containing_oneof() != NULL) { - int index = descriptor_->containing_oneof()->index() + index_base; + const OneofDescriptor *oneof = descriptor_->real_containing_oneof(); + if (oneof != NULL) { + int index = oneof->index() + index_base; // Flip the sign to mark it as a oneof. variables_["has_index"] = StrCat(-index); } } +bool FieldGenerator::WantsHasProperty(void) const { + return descriptor_->has_presence() && !descriptor_->real_containing_oneof(); +} + void FieldGenerator::FinishInitialization(void) { // If "property_type" wasn't set, make it "storage_type". if ((variables_.find("property_type") == variables_.end()) && @@ -289,20 +302,8 @@ void SingleFieldGenerator::GeneratePropertyImplementation( } } -bool SingleFieldGenerator::WantsHasProperty(void) const { - if (descriptor_->containing_oneof() != NULL) { - // If in a oneof, it uses the oneofcase instead of a has bit. - return false; - } - if (HasFieldPresence(descriptor_->file())) { - // In proto1/proto2, every field has a has_$name$() method. - return true; - } - return false; -} - bool SingleFieldGenerator::RuntimeUsesHasBit(void) const { - if (descriptor_->containing_oneof() != NULL) { + if (descriptor_->real_containing_oneof()) { // The oneof tracks what is set instead. return false; } @@ -402,13 +403,8 @@ void RepeatedFieldGenerator::GeneratePropertyDeclaration( printer->Print("\n"); } -bool RepeatedFieldGenerator::WantsHasProperty(void) const { - // Consumer check the array size/existence rather than a has bit. - return false; -} - bool RepeatedFieldGenerator::RuntimeUsesHasBit(void) const { - return false; // The array having anything is what is used. + return false; // The array (or map/dict) having anything is what is used. } FieldGeneratorMap::FieldGeneratorMap(const Descriptor* descriptor, diff --git a/src/google/protobuf/compiler/objectivec/objectivec_field.h b/src/google/protobuf/compiler/objectivec/objectivec_field.h index 68c470a50b..2ebe55b2fc 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_field.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_field.h @@ -96,7 +96,7 @@ class FieldGenerator { FieldGenerator(const FieldDescriptor* descriptor, const Options& options); virtual void FinishInitialization(void); - virtual bool WantsHasProperty(void) const = 0; + bool WantsHasProperty(void) const; const FieldDescriptor* descriptor_; std::map variables_; @@ -119,7 +119,6 @@ class SingleFieldGenerator : public FieldGenerator { protected: SingleFieldGenerator(const FieldDescriptor* descriptor, const Options& options); - virtual bool WantsHasProperty(void) const; }; // Subclass with common support for when the field ends up as an ObjC Object. @@ -156,7 +155,6 @@ class RepeatedFieldGenerator : public ObjCObjFieldGenerator { RepeatedFieldGenerator(const FieldDescriptor* descriptor, const Options& options); virtual void FinishInitialization(void); - virtual bool WantsHasProperty(void) const; }; // Convenience class which constructs FieldGenerators for a Descriptor. diff --git a/src/google/protobuf/compiler/objectivec/objectivec_file.cc b/src/google/protobuf/compiler/objectivec/objectivec_file.cc index db58249ded..04733ca24c 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_file.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_file.cc @@ -52,7 +52,7 @@ namespace objectivec { namespace { // This is also found in GPBBootstrap.h, and needs to be kept in sync. -const int32 GOOGLE_PROTOBUF_OBJC_VERSION = 30003; +const int32 GOOGLE_PROTOBUF_OBJC_VERSION = 30004; const char* kHeaderExtension = ".pbobjc.h"; diff --git a/src/google/protobuf/compiler/objectivec/objectivec_generator.h b/src/google/protobuf/compiler/objectivec/objectivec_generator.h index d10efc20ef..b09e2b2d3f 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_generator.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_generator.h @@ -57,15 +57,19 @@ class PROTOC_EXPORT ObjectiveCGenerator : public CodeGenerator { ObjectiveCGenerator& operator=(const ObjectiveCGenerator&) = delete; // implements CodeGenerator ---------------------------------------- - bool HasGenerateAll() const; + bool HasGenerateAll() const override; bool Generate(const FileDescriptor* file, const string& parameter, GeneratorContext* context, - string* error) const; + string* error) const override; bool GenerateAll(const std::vector& files, const string& parameter, GeneratorContext* context, - string* error) const; + string* error) const override; + + uint64 GetSupportedFeatures() const override { + return FEATURE_PROTO3_OPTIONAL; + } }; } // namespace objectivec diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc index 638d7b70c9..df11689f61 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.cc @@ -1301,14 +1301,14 @@ class DecodeDataBuilder { } private: - static const uint8 kAddUnderscore = 0x80; + static constexpr uint8 kAddUnderscore = 0x80; - static const uint8 kOpAsIs = 0x00; - static const uint8 kOpFirstUpper = 0x40; - static const uint8 kOpFirstLower = 0x20; - static const uint8 kOpAllUpper = 0x60; + static constexpr uint8 kOpAsIs = 0x00; + static constexpr uint8 kOpFirstUpper = 0x40; + static constexpr uint8 kOpFirstLower = 0x20; + static constexpr uint8 kOpAllUpper = 0x60; - static const int kMaxSegmentLen = 0x1f; + static constexpr int kMaxSegmentLen = 0x1f; void AddChar(const char desired) { ++segment_len_; diff --git a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h index c6a2c7733b..5f91f4e4ed 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_helpers.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_helpers.h @@ -126,10 +126,6 @@ string PROTOC_EXPORT ObjCClass(const string& class_name); // be refrerred to by ObjCClass. string PROTOC_EXPORT ObjCClassDeclaration(const string& class_name); -inline bool HasFieldPresence(const FileDescriptor* file) { - return file->syntax() != FileDescriptor::SYNTAX_PROTO3; -} - inline bool HasPreservingUnknownEnumSemantics(const FileDescriptor* file) { return file->syntax() == FileDescriptor::SYNTAX_PROTO3; } diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message.cc b/src/google/protobuf/compiler/objectivec/objectivec_message.cc index b5d8128a99..0684021c2a 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_message.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_message.cc @@ -185,7 +185,7 @@ MessageGenerator::MessageGenerator(const string& root_classname, new ExtensionGenerator(class_name_, descriptor_->extension(i))); } - for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { + for (int i = 0; i < descriptor_->real_oneof_decl_count(); i++) { OneofGenerator* generator = new OneofGenerator(descriptor_->oneof_decl(i)); oneof_generators_.emplace_back(generator); } @@ -339,11 +339,12 @@ void MessageGenerator::GenerateMessageHeader(io::Printer* printer) { "deprecated_attribute", deprecated_attribute_, "comments", message_comments); - std::vector seen_oneofs(descriptor_->oneof_decl_count(), 0); + std::vector seen_oneofs(oneof_generators_.size(), 0); for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); - if (field->containing_oneof() != NULL) { - const int oneof_index = field->containing_oneof()->index(); + const OneofDescriptor *oneof = field->real_containing_oneof(); + if (oneof) { + const int oneof_index = oneof->index(); if (!seen_oneofs[oneof_index]) { seen_oneofs[oneof_index] = 1; oneof_generators_[oneof_index]->GeneratePublicCasePropertyDeclaration( @@ -443,7 +444,7 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { field_generators_.SetOneofIndexBase(sizeof_has_storage); // sizeof_has_storage needs enough bits for the single fields that aren't in // any oneof, and then one int32 for each oneof (to store the field number). - sizeof_has_storage += descriptor_->oneof_decl_count(); + sizeof_has_storage += oneof_generators_.size(); printer->Print( "\n" @@ -515,6 +516,7 @@ void MessageGenerator::GenerateSource(io::Printer* printer) { std::vector init_flags; init_flags.push_back("GPBDescriptorInitializationFlag_UsesClassRefs"); + init_flags.push_back("GPBDescriptorInitializationFlag_Proto3OptionalKnown"); if (need_defaults) { init_flags.push_back("GPBDescriptorInitializationFlag_FieldsWithDefault"); } diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc b/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc index 2730e7591d..7bf33f4c12 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_message_field.cc @@ -77,16 +77,6 @@ void MessageFieldGenerator::DetermineObjectiveCClassDefinitions( fwd_decls->insert(ObjCClassDeclaration(variable("storage_type"))); } -bool MessageFieldGenerator::WantsHasProperty(void) const { - if (descriptor_->containing_oneof() != NULL) { - // If in a oneof, it uses the oneofcase instead of a has bit. - return false; - } - // In both proto2 & proto3, message fields have a has* property to tell - // when it is a non default value. - return true; -} - RepeatedMessageFieldGenerator::RepeatedMessageFieldGenerator( const FieldDescriptor* descriptor, const Options& options) : RepeatedFieldGenerator(descriptor, options) { diff --git a/src/google/protobuf/compiler/objectivec/objectivec_message_field.h b/src/google/protobuf/compiler/objectivec/objectivec_message_field.h index 692f94c026..a53c4a540c 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_message_field.h +++ b/src/google/protobuf/compiler/objectivec/objectivec_message_field.h @@ -52,7 +52,6 @@ class MessageFieldGenerator : public ObjCObjFieldGenerator { MessageFieldGenerator& operator=(const MessageFieldGenerator&) = delete; virtual ~MessageFieldGenerator(); - virtual bool WantsHasProperty(void) const; public: virtual void DetermineForwardDeclarations(std::set* fwd_decls) const; diff --git a/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc b/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc index 5b37c4e945..badebf55b9 100644 --- a/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc +++ b/src/google/protobuf/compiler/objectivec/objectivec_oneof.cc @@ -120,9 +120,9 @@ void OneofGenerator::GenerateClearFunctionImplementation(io::Printer* printer) { printer->Print( variables_, "void $owning_message_class$_Clear$capitalized_name$OneOfCase($owning_message_class$ *message) {\n" - " GPBDescriptor *descriptor = [message descriptor];\n" + " GPBDescriptor *descriptor = [$owning_message_class$ descriptor];\n" " GPBOneofDescriptor *oneof = [descriptor.oneofs objectAtIndex:$raw_index$];\n" - " GPBMaybeClearOneof(message, oneof, $index$, 0);\n" + " GPBClearOneof(message, oneof);\n" "}\n"); } diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index a0f5a2cac4..d92cd55873 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -783,8 +783,7 @@ bool Parser::ParseMessageDefinition( } for (auto& field : *message->mutable_field()) { - if (field.proto3_optional() && - field.type() != FieldDescriptorProto::TYPE_MESSAGE) { + if (field.proto3_optional()) { std::string oneof_name = field.name(); // Prepend 'XXXXX_' until we are no longer conflicting. diff --git a/src/google/protobuf/compiler/python/python_generator.h b/src/google/protobuf/compiler/python/python_generator.h index 82e5a785f5..fb1aee1617 100644 --- a/src/google/protobuf/compiler/python/python_generator.h +++ b/src/google/protobuf/compiler/python/python_generator.h @@ -69,10 +69,9 @@ class PROTOC_EXPORT Generator : public CodeGenerator { virtual ~Generator(); // CodeGenerator methods. - virtual bool Generate(const FileDescriptor* file, - const std::string& parameter, - GeneratorContext* generator_context, - std::string* error) const override; + bool Generate(const FileDescriptor* file, const std::string& parameter, + GeneratorContext* generator_context, + std::string* error) const override; uint64 GetSupportedFeatures() const override; diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.cc b/src/google/protobuf/compiler/ruby/ruby_generator.cc index 8c0aed1c71..cf61d9995a 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.cc +++ b/src/google/protobuf/compiler/ruby/ruby_generator.cc @@ -77,6 +77,10 @@ std::string GetOutputFilename(const std::string& proto_file) { } std::string LabelForField(const FieldDescriptor* field) { + if (field->has_optional_keyword() && + field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) { + return "proto3_optional"; + } switch (field->label()) { case FieldDescriptor::LABEL_OPTIONAL: return "optional"; case FieldDescriptor::LABEL_REQUIRED: return "required"; @@ -255,12 +259,12 @@ bool GenerateMessage(const Descriptor* message, io::Printer* printer, for (int i = 0; i < message->field_count(); i++) { const FieldDescriptor* field = message->field(i); - if (!field->containing_oneof()) { + if (!field->real_containing_oneof()) { GenerateField(field, printer); } } - for (int i = 0; i < message->oneof_decl_count(); i++) { + for (int i = 0; i < message->real_oneof_decl_count(); i++) { const OneofDescriptor* oneof = message->oneof_decl(i); GenerateOneof(oneof, printer); } diff --git a/src/google/protobuf/compiler/ruby/ruby_generator.h b/src/google/protobuf/compiler/ruby/ruby_generator.h index 731a81a52d..ea4f30a5c5 100644 --- a/src/google/protobuf/compiler/ruby/ruby_generator.h +++ b/src/google/protobuf/compiler/ruby/ruby_generator.h @@ -49,11 +49,12 @@ namespace ruby { // Ruby output, you can do so by registering an instance of this // CodeGenerator with the CommandLineInterface in your main() function. class PROTOC_EXPORT Generator : public CodeGenerator { - virtual bool Generate( - const FileDescriptor* file, - const string& parameter, - GeneratorContext* generator_context, - string* error) const; + bool Generate(const FileDescriptor* file, const string& parameter, + GeneratorContext* generator_context, + string* error) const override; + uint64 GetSupportedFeatures() const override { + return FEATURE_PROTO3_OPTIONAL; + } }; } // namespace ruby diff --git a/src/google/protobuf/descriptor.cc b/src/google/protobuf/descriptor.cc index b94e925e45..1ec38c1de5 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -5500,6 +5500,42 @@ void DescriptorBuilder::CrossLinkMessage(Descriptor* message, message->field(i); } } + + for (int i = 0; i < message->field_count(); i++) { + const FieldDescriptor* field = message->field(i); + if (field->proto3_optional_) { + if (!field->containing_oneof() || + !field->containing_oneof()->is_synthetic()) { + AddError(message->full_name(), proto.field(i), + DescriptorPool::ErrorCollector::OTHER, + "Fields with proto3_optional set must be " + "a member of a one-field oneof"); + } + } + } + + // Synthetic oneofs must be last. + int first_synthetic = -1; + for (int i = 0; i < message->oneof_decl_count(); i++) { + const OneofDescriptor* oneof = message->oneof_decl(i); + if (oneof->is_synthetic()) { + if (first_synthetic == -1) { + first_synthetic = i; + } + } else { + if (first_synthetic != -1) { + AddError(message->full_name(), proto.oneof_decl(i), + DescriptorPool::ErrorCollector::OTHER, + "Synthetic oneofs must be after all other oneofs"); + } + } + } + + if (first_synthetic == -1) { + message->real_oneof_decl_count_ = message->oneof_decl_count_; + } else { + message->real_oneof_decl_count_ = first_synthetic; + } } void DescriptorBuilder::CrossLinkExtensionRange( diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index e84bc3eac5..f0eb9b6d46 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -337,6 +337,10 @@ class PROTOBUF_EXPORT Descriptor { // The number of oneofs in this message type. int oneof_decl_count() const; + // The number of oneofs in this message type, excluding synthetic oneofs. + // Real oneofs always come first, so iterating up to real_oneof_decl_cout() + // will yield all real oneofs. + int real_oneof_decl_count() const; // Get a oneof by index, where 0 <= index < oneof_decl_count(). // These are returned in the order they were defined in the .proto file. const OneofDescriptor* oneof_decl(int index) const; @@ -526,6 +530,7 @@ class PROTOBUF_EXPORT Descriptor { int field_count_; int oneof_decl_count_; + int real_oneof_decl_count_; int nested_type_count_; int enum_type_count_; int extension_range_count_; @@ -688,13 +693,14 @@ class PROTOBUF_EXPORT FieldDescriptor { // .proto file. Excludes singular proto3 fields that do not have a label. bool has_optional_keyword() const; - // Returns true if this is a non-oneof field that tracks presence. - // This includes all "required" and "optional" fields in the .proto file, - // but excludes oneof fields and singular proto3 fields without "optional". + // Returns true if this field tracks presence, ie. does the message + // distinguish between "unset" and "present with default value." + // This includes required, optional, and oneof fields. It excludes maps, + // repeated fields, and singular proto3 fields without "optional". // - // In implementations that use hasbits, this method will probably indicate - // whether this field uses a hasbit. - bool is_singular_with_presence() const; + // For fields where has_presence() == true, the return value of + // Reflection::HasField() is semantically meaningful. + bool has_presence() const; // Index of this field within the message's field array, or the file or // extension scope's extensions array. @@ -745,6 +751,10 @@ class PROTOBUF_EXPORT FieldDescriptor { // nullptr. const OneofDescriptor* containing_oneof() const; + // If the field is a member of a non-synthetic oneof, returns the descriptor + // for the oneof, otherwise returns nullptr. + const OneofDescriptor* real_containing_oneof() const; + // If the field is a member of a oneof, returns the index in that oneof. int index_in_oneof() const; @@ -1972,6 +1982,7 @@ PROTOBUF_DEFINE_ACCESSOR(Descriptor, containing_type, const Descriptor*) PROTOBUF_DEFINE_ACCESSOR(Descriptor, field_count, int) PROTOBUF_DEFINE_ACCESSOR(Descriptor, oneof_decl_count, int) +PROTOBUF_DEFINE_ACCESSOR(Descriptor, real_oneof_decl_count, int) PROTOBUF_DEFINE_ACCESSOR(Descriptor, nested_type_count, int) PROTOBUF_DEFINE_ACCESSOR(Descriptor, enum_type_count, int) @@ -2166,10 +2177,15 @@ inline bool FieldDescriptor::has_optional_keyword() const { !containing_oneof()); } -inline bool FieldDescriptor::is_singular_with_presence() const { +inline const OneofDescriptor* FieldDescriptor::real_containing_oneof() const { + return containing_oneof_ && !containing_oneof_->is_synthetic() + ? containing_oneof_ + : nullptr; +} + +inline bool FieldDescriptor::has_presence() const { if (is_repeated()) return false; - if (containing_oneof() && !containing_oneof()->is_synthetic()) return false; - return cpp_type() == CPPTYPE_MESSAGE || proto3_optional_ || + return cpp_type() == CPPTYPE_MESSAGE || containing_oneof() || file()->syntax() == FileDescriptor::SYNTAX_PROTO2; } diff --git a/src/google/protobuf/descriptor.proto b/src/google/protobuf/descriptor.proto index deb8f6894d..d29fdec5e5 100644 --- a/src/google/protobuf/descriptor.proto +++ b/src/google/protobuf/descriptor.proto @@ -217,10 +217,21 @@ message FieldDescriptorProto { // If true, this is a proto3 "optional". When a proto3 field is optional, it // tracks presence regardless of field type. // - // For message fields this doesn't create any semantic change, since - // non-repeated message fields always track presence. However it still + // When proto3_optional is true, this field must be belong to a oneof to + // signal to old proto3 clients that presence is tracked for this field. This + // oneof is known as a "synthetic" oneof, and this field must be its sole + // member (each proto3 optional field gets its own synthetic oneof). Synthetic + // oneofs exist in the descriptor only, and do not generate any API. Synthetic + // oneofs must be ordered after all "real" oneofs. + // + // For message fields, proto3_optional doesn't create any semantic change, + // since non-repeated message fields always track presence. However it still // indicates the semantic detail of whether the user wrote "optional" or not. - // This can be useful for round-tripping the .proto file. + // This can be useful for round-tripping the .proto file. For consistency we + // give message fields a synthetic oneof also, even though it is not required + // to track presence. This is especially important because the parser can't + // tell if a field is a message or an enum, so it must always create a + // synthetic oneof. // // Proto2 optional fields do not set this flag, because they already indicate // optional with `LABEL_OPTIONAL`. diff --git a/src/google/protobuf/dynamic_message.cc b/src/google/protobuf/dynamic_message.cc index 971d5b7ab4..fd5237172f 100644 --- a/src/google/protobuf/dynamic_message.cc +++ b/src/google/protobuf/dynamic_message.cc @@ -737,7 +737,7 @@ const Message* DynamicMessageFactory::GetPrototypeNoLock( uint32* has_bits_indices = new uint32[type->field_count()]; for (int i = 0; i < type->field_count(); i++) { // Initialize to -1, fields that need a hasbit will overwrite. - has_bits_indices[i] = -1; + has_bits_indices[i] = static_cast(-1); } type_info->has_bits_indices.reset(has_bits_indices); } diff --git a/src/google/protobuf/generated_message_reflection.h b/src/google/protobuf/generated_message_reflection.h index 7aab17f8a9..e2eae772c6 100644 --- a/src/google/protobuf/generated_message_reflection.h +++ b/src/google/protobuf/generated_message_reflection.h @@ -169,7 +169,7 @@ struct ReflectionSchema { // Bit index within the bit array of hasbits. Bit order is low-to-high. uint32 HasBitIndex(const FieldDescriptor* field) const { - if (has_bits_offset_ == -1) return -1; + if (has_bits_offset_ == -1) return static_cast(-1); GOOGLE_DCHECK(HasHasbits()); return has_bit_indices_[field->index()]; } diff --git a/src/google/protobuf/generated_message_table_driven_lite.h b/src/google/protobuf/generated_message_table_driven_lite.h index 80f215880c..ae13b363ef 100644 --- a/src/google/protobuf/generated_message_table_driven_lite.h +++ b/src/google/protobuf/generated_message_table_driven_lite.h @@ -280,9 +280,13 @@ static inline bool HandleString(io::CodedInputStream* input, MessageLite* msg, } utf8_string_data = field->Get(); } break; + default: + PROTOBUF_ASSUME(false); } break; } + default: + PROTOBUF_ASSUME(false); } if (kValidateUtf8) { @@ -322,6 +326,8 @@ inline bool HandleEnum(const ParseTable& table, io::CodedInputStream* input, SetOneofField(msg, presence, presence_index, offset, field_number, value); break; + default: + PROTOBUF_ASSUME(false); } } else { UnknownFieldHandler::Varint(msg, table, tag, value); @@ -406,9 +412,6 @@ bool MergePartialFromCodedStreamInlined(MessageLite* msg, const unsigned char processing_type = data->processing_type; if (data->normal_wiretype == static_cast(wire_type)) { - // TODO(ckennelly): Use a computed goto on GCC/LLVM or otherwise eliminate - // the bounds check on processing_type. - switch (processing_type) { #define HANDLE_TYPE(TYPE, CPPTYPE) \ case (WireFormatLite::TYPE_##TYPE): { \ @@ -739,7 +742,7 @@ bool MergePartialFromCodedStreamInlined(MessageLite* msg, return true; } default: - break; + PROTOBUF_ASSUME(false); } } else if (data->packed_wiretype == static_cast(wire_type)) { // Non-packable fields have their packed_wiretype masked with @@ -751,8 +754,6 @@ bool MergePartialFromCodedStreamInlined(MessageLite* msg, GOOGLE_DCHECK_NE(TYPE_BYTES_INLINED | kRepeatedMask, processing_type); GOOGLE_DCHECK_NE(TYPE_STRING_INLINED | kRepeatedMask, processing_type); - // TODO(ckennelly): Use a computed goto on GCC/LLVM. - // // Mask out kRepeatedMask bit, allowing the jump table to be smaller. switch (static_cast(processing_type ^ kRepeatedMask)) { @@ -825,7 +826,7 @@ bool MergePartialFromCodedStreamInlined(MessageLite* msg, GOOGLE_DCHECK(false); return false; default: - break; + PROTOBUF_ASSUME(false); } } else { if (wire_type == WireFormatLite::WIRETYPE_END_GROUP) { diff --git a/src/google/protobuf/proto3_arena_unittest.cc b/src/google/protobuf/proto3_arena_unittest.cc index 8e6e015287..27fb58b44c 100644 --- a/src/google/protobuf/proto3_arena_unittest.cc +++ b/src/google/protobuf/proto3_arena_unittest.cc @@ -218,7 +218,7 @@ TEST(Proto3OptionalTest, OptionalFieldDescriptor) { for (int i = 0; i < d->field_count(); i++) { const FieldDescriptor* f = d->field(i); EXPECT_TRUE(f->has_optional_keyword()) << f->full_name(); - EXPECT_TRUE(f->is_singular_with_presence()) << f->full_name(); + EXPECT_TRUE(f->has_presence()) << f->full_name(); EXPECT_TRUE(f->containing_oneof()) << f->full_name(); } } @@ -470,16 +470,8 @@ TEST(Proto3OptionalTest, ReflectiveSwapRoundTrip) { TEST(Proto3OptionalTest, PlainFields) { const Descriptor* d = TestAllTypes::descriptor(); - for (int i = 0; i < d->field_count(); i++) { - const FieldDescriptor* f = d->field(i); - EXPECT_FALSE(f->has_optional_keyword()) << f->full_name(); - if (f->is_optional() && f->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) { - EXPECT_FALSE(f->is_singular_with_presence()) << f->full_name(); - } - } - - EXPECT_FALSE( - d->FindFieldByName("oneof_nested_message")->is_singular_with_presence()); + EXPECT_FALSE(d->FindFieldByName("optional_int32")->has_presence()); + EXPECT_TRUE(d->FindFieldByName("oneof_nested_message")->has_presence()); } } // namespace diff --git a/src/google/protobuf/stubs/statusor.h b/src/google/protobuf/stubs/statusor.h index e8e403a677..c02e89a9f0 100644 --- a/src/google/protobuf/stubs/statusor.h +++ b/src/google/protobuf/stubs/statusor.h @@ -153,6 +153,7 @@ class StatusOr { // If you need to initialize a T object from the stored value, // ConsumeValueOrDie() may be more efficient. const T& ValueOrDie() const; + const T& value () const; private: Status status_; @@ -254,6 +255,14 @@ inline const T& StatusOr::ValueOrDie() const { } return value_; } + +template +inline const T& StatusOr::value() const { + if (!status_.ok()) { + internal::StatusOrHelper::Crash(status_); + } + return value_; +} } // namespace util } // namespace protobuf } // namespace google diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index 6c18b6cd21..0d886068a6 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -173,7 +173,7 @@ StatusOr DataPiece::ToDouble() const { if (str_ == "-Infinity") return -std::numeric_limits::infinity(); if (str_ == "NaN") return std::numeric_limits::quiet_NaN(); StatusOr value = StringToNumber(safe_strtod); - if (value.ok() && !std::isfinite(value.ValueOrDie())) { + if (value.ok() && !std::isfinite(value.value())) { // safe_strtod converts out-of-range values to +inf/-inf, but we want // to report them as errors. return InvalidArgument(StrCat("\"", str_, "\"")); @@ -289,7 +289,7 @@ StatusOr DataPiece::ToEnum(const google::protobuf::Enum* enum_type, StatusOr int_value = ToInt32(); if (int_value.ok()) { if (const google::protobuf::EnumValue* enum_value = - FindEnumValueByNumberOrNull(enum_type, int_value.ValueOrDie())) { + FindEnumValueByNumberOrNull(enum_type, int_value.value())) { return enum_value->number(); } } diff --git a/src/google/protobuf/util/internal/default_value_objectwriter.cc b/src/google/protobuf/util/internal/default_value_objectwriter.cc index 828c86887e..a78a862eb0 100644 --- a/src/google/protobuf/util/internal/default_value_objectwriter.cc +++ b/src/google/protobuf/util/internal/default_value_objectwriter.cc @@ -52,7 +52,7 @@ T ConvertTo(StringPiece value, StatusOr (DataPiece::*converter_fn)() const, T default_value) { if (value.empty()) return default_value; StatusOr result = (DataPiece(value, true).*converter_fn)(); - return result.ok() ? result.ValueOrDie() : default_value; + return result.ok() ? result.value() : default_value; } } // namespace @@ -290,7 +290,7 @@ const google::protobuf::Type* DefaultValueObjectWriter::Node::GetMapValueType( if (!sub_type.ok()) { GOOGLE_LOG(WARNING) << "Cannot resolve type '" << sub_field.type_url() << "'."; } else { - return sub_type.ValueOrDie(); + return sub_type.value(); } break; } @@ -354,7 +354,7 @@ void DefaultValueObjectWriter::Node::PopulateChildren( // "field" is of an unknown type. GOOGLE_LOG(WARNING) << "Cannot resolve type '" << field.type_url() << "'."; } else { - const google::protobuf::Type* found_type = found_result.ValueOrDie(); + const google::protobuf::Type* found_type = found_result.value(); is_map = IsMap(field, *found_type); if (!is_map) { @@ -587,7 +587,7 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, name == "@type") { util::StatusOr data_string = data.ToString(); if (data_string.ok()) { - const std::string& string_value = data_string.ValueOrDie(); + const std::string& string_value = data_string.value(); // If the type of current_ is "Any" and its "@type" field is being set // here, sets the type of current_ to be the type specified by the // "@type". @@ -596,7 +596,7 @@ void DefaultValueObjectWriter::RenderDataPiece(StringPiece name, if (!found_type.ok()) { GOOGLE_LOG(WARNING) << "Failed to resolve type '" << string_value << "'."; } else { - current_->set_type(found_type.ValueOrDie()); + current_->set_type(found_type.value()); } current_->set_is_any(true); // If the "@type" field is placed after other fields, we should populate diff --git a/src/google/protobuf/util/internal/object_writer.cc b/src/google/protobuf/util/internal/object_writer.cc index 72f658745b..b7667b6b16 100644 --- a/src/google/protobuf/util/internal/object_writer.cc +++ b/src/google/protobuf/util/internal/object_writer.cc @@ -42,35 +42,35 @@ void ObjectWriter::RenderDataPieceTo(const DataPiece& data, StringPiece name, ObjectWriter* ow) { switch (data.type()) { case DataPiece::TYPE_INT32: { - ow->RenderInt32(name, data.ToInt32().ValueOrDie()); + ow->RenderInt32(name, data.ToInt32().value()); break; } case DataPiece::TYPE_INT64: { - ow->RenderInt64(name, data.ToInt64().ValueOrDie()); + ow->RenderInt64(name, data.ToInt64().value()); break; } case DataPiece::TYPE_UINT32: { - ow->RenderUint32(name, data.ToUint32().ValueOrDie()); + ow->RenderUint32(name, data.ToUint32().value()); break; } case DataPiece::TYPE_UINT64: { - ow->RenderUint64(name, data.ToUint64().ValueOrDie()); + ow->RenderUint64(name, data.ToUint64().value()); break; } case DataPiece::TYPE_DOUBLE: { - ow->RenderDouble(name, data.ToDouble().ValueOrDie()); + ow->RenderDouble(name, data.ToDouble().value()); break; } case DataPiece::TYPE_FLOAT: { - ow->RenderFloat(name, data.ToFloat().ValueOrDie()); + ow->RenderFloat(name, data.ToFloat().value()); break; } case DataPiece::TYPE_BOOL: { - ow->RenderBool(name, data.ToBool().ValueOrDie()); + ow->RenderBool(name, data.ToBool().value()); break; } case DataPiece::TYPE_STRING: { - ow->RenderString(name, data.ToString().ValueOrDie()); + ow->RenderString(name, data.ToString().value()); break; } case DataPiece::TYPE_BYTES: { diff --git a/src/google/protobuf/util/internal/proto_writer.cc b/src/google/protobuf/util/internal/proto_writer.cc index f2ee022456..ea95932fd6 100644 --- a/src/google/protobuf/util/internal/proto_writer.cc +++ b/src/google/protobuf/util/internal/proto_writer.cc @@ -124,7 +124,7 @@ inline Status WriteInt32(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr i32 = data.ToInt32(); if (i32.ok()) { - WireFormatLite::WriteInt32(field_number, i32.ValueOrDie(), stream); + WireFormatLite::WriteInt32(field_number, i32.value(), stream); } return i32.status(); } @@ -134,7 +134,7 @@ inline Status WriteSFixed32(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr i32 = data.ToInt32(); if (i32.ok()) { - WireFormatLite::WriteSFixed32(field_number, i32.ValueOrDie(), stream); + WireFormatLite::WriteSFixed32(field_number, i32.value(), stream); } return i32.status(); } @@ -144,7 +144,7 @@ inline Status WriteSInt32(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr i32 = data.ToInt32(); if (i32.ok()) { - WireFormatLite::WriteSInt32(field_number, i32.ValueOrDie(), stream); + WireFormatLite::WriteSInt32(field_number, i32.value(), stream); } return i32.status(); } @@ -154,7 +154,7 @@ inline Status WriteFixed32(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr u32 = data.ToUint32(); if (u32.ok()) { - WireFormatLite::WriteFixed32(field_number, u32.ValueOrDie(), stream); + WireFormatLite::WriteFixed32(field_number, u32.value(), stream); } return u32.status(); } @@ -164,7 +164,7 @@ inline Status WriteUInt32(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr u32 = data.ToUint32(); if (u32.ok()) { - WireFormatLite::WriteUInt32(field_number, u32.ValueOrDie(), stream); + WireFormatLite::WriteUInt32(field_number, u32.value(), stream); } return u32.status(); } @@ -174,7 +174,7 @@ inline Status WriteInt64(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr i64 = data.ToInt64(); if (i64.ok()) { - WireFormatLite::WriteInt64(field_number, i64.ValueOrDie(), stream); + WireFormatLite::WriteInt64(field_number, i64.value(), stream); } return i64.status(); } @@ -184,7 +184,7 @@ inline Status WriteSFixed64(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr i64 = data.ToInt64(); if (i64.ok()) { - WireFormatLite::WriteSFixed64(field_number, i64.ValueOrDie(), stream); + WireFormatLite::WriteSFixed64(field_number, i64.value(), stream); } return i64.status(); } @@ -194,7 +194,7 @@ inline Status WriteSInt64(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr i64 = data.ToInt64(); if (i64.ok()) { - WireFormatLite::WriteSInt64(field_number, i64.ValueOrDie(), stream); + WireFormatLite::WriteSInt64(field_number, i64.value(), stream); } return i64.status(); } @@ -204,7 +204,7 @@ inline Status WriteFixed64(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr u64 = data.ToUint64(); if (u64.ok()) { - WireFormatLite::WriteFixed64(field_number, u64.ValueOrDie(), stream); + WireFormatLite::WriteFixed64(field_number, u64.value(), stream); } return u64.status(); } @@ -214,7 +214,7 @@ inline Status WriteUInt64(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr u64 = data.ToUint64(); if (u64.ok()) { - WireFormatLite::WriteUInt64(field_number, u64.ValueOrDie(), stream); + WireFormatLite::WriteUInt64(field_number, u64.value(), stream); } return u64.status(); } @@ -224,7 +224,7 @@ inline Status WriteDouble(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr d = data.ToDouble(); if (d.ok()) { - WireFormatLite::WriteDouble(field_number, d.ValueOrDie(), stream); + WireFormatLite::WriteDouble(field_number, d.value(), stream); } return d.status(); } @@ -234,7 +234,7 @@ inline Status WriteFloat(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr f = data.ToFloat(); if (f.ok()) { - WireFormatLite::WriteFloat(field_number, f.ValueOrDie(), stream); + WireFormatLite::WriteFloat(field_number, f.value(), stream); } return f.status(); } @@ -244,7 +244,7 @@ inline Status WriteBool(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr b = data.ToBool(); if (b.ok()) { - WireFormatLite::WriteBool(field_number, b.ValueOrDie(), stream); + WireFormatLite::WriteBool(field_number, b.value(), stream); } return b.status(); } @@ -264,7 +264,7 @@ inline Status WriteString(int field_number, const DataPiece& data, CodedOutputStream* stream) { StatusOr s = data.ToString(); if (s.ok()) { - WireFormatLite::WriteString(field_number, s.ValueOrDie(), stream); + WireFormatLite::WriteString(field_number, s.value(), stream); } return s.status(); } @@ -602,7 +602,7 @@ Status ProtoWriter::WriteEnum(int field_number, const DataPiece& data, case_insensitive_enum_parsing, ignore_unknown_values, &is_unknown_enum_value); if (e.ok() && !is_unknown_enum_value) { - WireFormatLite::WriteEnum(field_number, e.ValueOrDie(), stream); + WireFormatLite::WriteEnum(field_number, e.value(), stream); } return e.status(); } @@ -704,8 +704,8 @@ ProtoWriter* ProtoWriter::RenderPrimitiveField( break; } default: // TYPE_GROUP or TYPE_MESSAGE - status = Status(util::error::INVALID_ARGUMENT, - data.ToString().ValueOrDie()); + status = + Status(util::error::INVALID_ARGUMENT, data.ToString().value()); } if (!status.ok()) { diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc index fad62f5a23..80b1defd7a 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -647,7 +647,7 @@ Status ProtoStreamObjectSource::RenderAny(const ProtoStreamObjectSource* os, resolved_type.status().message()); } // nested_type cannot be null at this time. - const google::protobuf::Type* nested_type = resolved_type.ValueOrDie(); + const google::protobuf::Type* nested_type = resolved_type.value(); io::ArrayInputStream zero_copy_stream(value.data(), value.size()); io::CodedInputStream in_stream(&zero_copy_stream); diff --git a/src/google/protobuf/util/internal/protostream_objectsource.h b/src/google/protobuf/util/internal/protostream_objectsource.h index a83031d17c..c3125ff6f1 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.h +++ b/src/google/protobuf/util/internal/protostream_objectsource.h @@ -160,6 +160,9 @@ class PROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { const google::protobuf::Field& field) const; + // Returns the input stream. + io::CodedInputStream* stream() const { return stream_; } + private: ProtoStreamObjectSource(io::CodedInputStream* stream, const TypeInfo* typeinfo, @@ -281,7 +284,7 @@ class PROTOBUF_EXPORT ProtoStreamObjectSource : public ObjectSource { StringPiece field_name) const; // Input stream to read from. Ownership rests with the caller. - io::CodedInputStream* stream_; + mutable io::CodedInputStream* stream_; // Type information for all the types used in the descriptor. Used to find // google::protobuf::Type of nested messages/enums. diff --git a/src/google/protobuf/util/internal/protostream_objectwriter.cc b/src/google/protobuf/util/internal/protostream_objectwriter.cc index 752c3f0b77..527d5e70da 100644 --- a/src/google/protobuf/util/internal/protostream_objectwriter.cc +++ b/src/google/protobuf/util/internal/protostream_objectwriter.cc @@ -334,7 +334,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { invalid_ = true; return; } - type_url_ = s.ValueOrDie(); + type_url_ = s.value(); } // Resolve the type url, and report an error if we failed to resolve it. StatusOr resolved_type = @@ -345,7 +345,7 @@ void ProtoStreamObjectWriter::AnyWriter::StartAny(const DataPiece& value) { return; } // At this point, type is never null. - const google::protobuf::Type* type = resolved_type.ValueOrDie(); + const google::protobuf::Type* type = resolved_type.value(); well_known_type_render_ = FindTypeRenderer(type_url_); if (well_known_type_render_ != nullptr || @@ -897,7 +897,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, if (int_value.ok()) { ow->ProtoWriter::RenderDataPiece( "string_value", - DataPiece(SimpleDtoa(int_value.ValueOrDie()), true)); + DataPiece(SimpleDtoa(int_value.value()), true)); return Status(); } } @@ -910,7 +910,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, if (int_value.ok()) { ow->ProtoWriter::RenderDataPiece( "string_value", - DataPiece(SimpleDtoa(int_value.ValueOrDie()), true)); + DataPiece(SimpleDtoa(int_value.value()), true)); return Status(); } } @@ -924,8 +924,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, StatusOr int_value = data.ToInt64(); if (int_value.ok()) { ow->ProtoWriter::RenderDataPiece( - "string_value", - DataPiece(StrCat(int_value.ValueOrDie()), true)); + "string_value", DataPiece(StrCat(int_value.value()), true)); return Status(); } } @@ -939,8 +938,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, StatusOr int_value = data.ToUint64(); if (int_value.ok()) { ow->ProtoWriter::RenderDataPiece( - "string_value", - DataPiece(StrCat(int_value.ValueOrDie()), true)); + "string_value", DataPiece(StrCat(int_value.value()), true)); return Status(); } } @@ -953,7 +951,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, if (float_value.ok()) { ow->ProtoWriter::RenderDataPiece( "string_value", - DataPiece(SimpleDtoa(float_value.ValueOrDie()), true)); + DataPiece(SimpleDtoa(float_value.value()), true)); return Status(); } } @@ -966,7 +964,7 @@ Status ProtoStreamObjectWriter::RenderStructValue(ProtoStreamObjectWriter* ow, if (double_value.ok()) { ow->ProtoWriter::RenderDataPiece( "string_value", - DataPiece(SimpleDtoa(double_value.ValueOrDie()), true)); + DataPiece(SimpleDtoa(double_value.value()), true)); return Status(); } } diff --git a/src/google/protobuf/util/internal/type_info.cc b/src/google/protobuf/util/internal/type_info.cc index 818df64172..e8b2103855 100644 --- a/src/google/protobuf/util/internal/type_info.cc +++ b/src/google/protobuf/util/internal/type_info.cc @@ -81,7 +81,7 @@ class TypeInfoForTypeResolver : public TypeInfo { const google::protobuf::Type* GetTypeByTypeUrl( StringPiece type_url) const override { StatusOrType result = ResolveTypeUrl(type_url); - return result.ok() ? result.ValueOrDie() : NULL; + return result.ok() ? result.value() : NULL; } const google::protobuf::Enum* GetEnumByTypeUrl( @@ -89,7 +89,7 @@ class TypeInfoForTypeResolver : public TypeInfo { std::map::iterator it = cached_enums_.find(type_url); if (it != cached_enums_.end()) { - return it->second.ok() ? it->second.ValueOrDie() : NULL; + return it->second.ok() ? it->second.value() : NULL; } // Stores the string value so it can be referenced using StringPiece in the // cached_enums_ map. @@ -102,7 +102,7 @@ class TypeInfoForTypeResolver : public TypeInfo { StatusOrEnum result = status.ok() ? StatusOrEnum(enum_type.release()) : StatusOrEnum(status); cached_enums_[string_type_url] = result; - return result.ok() ? result.ValueOrDie() : NULL; + return result.ok() ? result.value() : NULL; } const google::protobuf::Field* FindField( @@ -134,7 +134,7 @@ class TypeInfoForTypeResolver : public TypeInfo { cached_types->begin(); it != cached_types->end(); ++it) { if (it->second.ok()) { - delete it->second.ValueOrDie(); + delete it->second.value(); } } } diff --git a/tests.sh b/tests.sh index 9f485fee09..faaec76f0a 100755 --- a/tests.sh +++ b/tests.sh @@ -436,6 +436,10 @@ build_ruby26() { internal_build_cpp # For conformance tests. cd ruby && bash travis-test.sh ruby-2.6.0 && cd .. } +build_ruby27() { + internal_build_cpp # For conformance tests. + cd ruby && bash travis-test.sh ruby-2.7.0 && cd .. +} build_javascript() { internal_build_cpp @@ -915,6 +919,7 @@ Usage: $0 { cpp | ruby24 | ruby25 | ruby26 | + ruby27 | jruby | ruby_all | php5.5 |