From 32e5deb1ac76feb4f187371c2a7f76c9009b1eff Mon Sep 17 00:00:00 2001 From: Joshua Haberman Date: Tue, 28 Apr 2020 08:40:38 -0700 Subject: [PATCH] Sync from Piper @308829107 PROTOBUF_SYNC_PIPER --- conformance/binary_json_conformance_suite.h | 61 +- conformance/conformance_test.h | 48 +- conformance/text_format_conformance_suite.h | 18 +- csharp/generate_protos.sh | 3 +- .../TestMessagesProto2.cs | 144 +-- .../Unittest.cs | 12 +- .../UnittestProto3Optional.cs | 1072 +++++++++++++++++ .../Proto3OptionalTest.cs | 143 +++ .../Reflection/DescriptorsTest.cs | 50 + .../Reflection/FieldAccessTest.cs | 37 + csharp/src/Google.Protobuf.Test/testprotos.pb | Bin 328798 -> 334214 bytes .../Reflection/FieldDescriptor.cs | 6 + .../Reflection/IFieldAccessor.cs | 3 +- .../Reflection/MessageDescriptor.cs | 24 + .../Reflection/OneofAccessor.cs | 47 +- .../Reflection/OneofDescriptor.cs | 40 +- .../Reflection/SingleFieldAccessor.cs | 36 +- docs/field_presence.md | 476 ++++++++ docs/implementing_proto3_presence.md | 350 ++++++ .../java/com/google/protobuf/Descriptors.java | 45 +- kokoro/linux/dockerfile/test/ruby/Dockerfile | 1 + kokoro/linux/ruby27/build.sh | 18 + kokoro/linux/ruby27/continuous.cfg | 11 + kokoro/linux/ruby27/presubmit.cfg | 11 + kokoro/macos/ruby27/build.sh | 11 + kokoro/macos/ruby27/continuous.cfg | 5 + kokoro/macos/ruby27/presubmit.cfg | 5 + objectivec/DevTools/compile_testing_protos.sh | 1 + objectivec/GPBAny.pbobjc.h | 4 +- objectivec/GPBAny.pbobjc.m | 6 +- objectivec/GPBApi.pbobjc.h | 4 +- objectivec/GPBApi.pbobjc.m | 36 +- objectivec/GPBBootstrap.h | 2 +- objectivec/GPBDescriptor.m | 41 +- objectivec/GPBDescriptor_PackagePrivate.h | 19 +- objectivec/GPBDuration.pbobjc.h | 4 +- objectivec/GPBDuration.pbobjc.m | 6 +- objectivec/GPBEmpty.pbobjc.h | 4 +- objectivec/GPBEmpty.pbobjc.m | 2 +- objectivec/GPBFieldMask.pbobjc.h | 4 +- objectivec/GPBFieldMask.pbobjc.m | 2 +- objectivec/GPBMessage.m | 116 +- objectivec/GPBSourceContext.pbobjc.h | 4 +- objectivec/GPBSourceContext.pbobjc.m | 4 +- objectivec/GPBStruct.pbobjc.h | 4 +- objectivec/GPBStruct.pbobjc.m | 14 +- objectivec/GPBTimestamp.pbobjc.h | 4 +- objectivec/GPBTimestamp.pbobjc.m | 6 +- objectivec/GPBType.pbobjc.h | 4 +- objectivec/GPBType.pbobjc.m | 58 +- objectivec/GPBUtilities.h | 10 + objectivec/GPBUtilities.m | 290 ++--- objectivec/GPBUtilities_PackagePrivate.h | 99 +- objectivec/GPBWrappers.pbobjc.h | 4 +- objectivec/GPBWrappers.pbobjc.m | 36 +- objectivec/Tests/GPBMessageTests+Runtime.m | 260 ++++ .../Tests/GPBMessageTests+Serialization.m | 311 +++++ .../Tests/unittest_runtime_proto3.proto | 28 + python/docs/generate_docs.py | 35 +- .../google/protobuf/internal/containers.rst | 21 + python/docs/index.rst | 1 + .../protobuf/internal/text_format_test.py | 41 +- .../google/protobuf/internal/type_checkers.py | 20 + python/google/protobuf/json_format.py | 21 +- python/google/protobuf/text_format.py | 14 +- ruby/Rakefile | 2 +- .../v3.0.0/tests/repeated_field_test.rb | 1 + ruby/ext/google/protobuf_c/defs.c | 100 +- ruby/ext/google/protobuf_c/encode_decode.c | 6 +- ruby/ext/google/protobuf_c/message.c | 31 +- ruby/ext/google/protobuf_c/protobuf.h | 1 + ruby/ext/google/protobuf_c/storage.c | 95 +- ruby/ext/google/protobuf_c/upb.c | 71 +- ruby/ext/google/protobuf_c/upb.h | 739 +----------- ruby/tests/basic.rb | 114 +- ruby/tests/basic_proto2.rb | 14 +- ruby/tests/basic_test.proto | 36 +- ruby/tests/gc_test.rb | 4 +- ruby/travis-test.sh | 2 +- src/google/protobuf/arena_unittest.cc | 3 + .../protobuf/compiler/cpp/cpp_map_field.cc | 1 + .../compiler/csharp/csharp_field_base.cc | 6 +- .../compiler/csharp/csharp_generator.cc | 7 + .../compiler/csharp/csharp_generator.h | 13 +- .../compiler/csharp/csharp_helpers.cc | 8 +- .../protobuf/compiler/csharp/csharp_helpers.h | 28 + .../compiler/csharp/csharp_message.cc | 101 +- .../compiler/csharp/csharp_message_field.cc | 6 +- .../compiler/csharp/csharp_primitive_field.cc | 69 +- .../compiler/csharp/csharp_wrapper_field.cc | 4 +- .../protobuf/compiler/java/java_enum.cc | 6 +- .../protobuf/compiler/java/java_enum_lite.cc | 6 +- .../protobuf/compiler/java/java_field.cc | 1 + .../protobuf/compiler/java/java_helpers.cc | 1 + .../protobuf/compiler/java/java_helpers.h | 1 + .../compiler/java/java_primitive_field.cc | 1 + .../java/java_primitive_field_lite.cc | 4 +- .../objectivec/objectivec_enum_field.cc | 4 +- .../compiler/objectivec/objectivec_field.cc | 38 +- .../compiler/objectivec/objectivec_field.h | 4 +- .../compiler/objectivec/objectivec_file.cc | 2 +- .../objectivec/objectivec_generator.h | 10 +- .../compiler/objectivec/objectivec_helpers.cc | 12 +- .../compiler/objectivec/objectivec_helpers.h | 4 - .../compiler/objectivec/objectivec_message.cc | 12 +- .../objectivec/objectivec_message_field.cc | 10 - .../objectivec/objectivec_message_field.h | 1 - .../compiler/objectivec/objectivec_oneof.cc | 4 +- .../protobuf/compiler/ruby/ruby_generator.cc | 8 +- .../protobuf/compiler/ruby/ruby_generator.h | 11 +- src/google/protobuf/descriptor.cc | 16 +- src/google/protobuf/descriptor.h | 12 +- src/google/protobuf/descriptor_unittest.cc | 20 +- src/google/protobuf/extension_set_unittest.cc | 6 +- src/google/protobuf/implicit_weak_message.h | 2 +- .../protobuf/io/coded_stream_unittest.cc | 2 +- src/google/protobuf/map_entry.h | 6 +- src/google/protobuf/map_field.cc | 17 +- src/google/protobuf/map_field.h | 2 +- src/google/protobuf/map_field_test.cc | 4 +- src/google/protobuf/map_test_util.h | 45 + src/google/protobuf/map_type_handler.h | 6 +- src/google/protobuf/message.h | 3 + src/google/protobuf/message_lite.h | 3 + src/google/protobuf/metadata_lite.h | 4 +- src/google/protobuf/reflection.h | 11 +- src/google/protobuf/repeated_field.cc | 2 +- src/google/protobuf/repeated_field.h | 145 ++- .../protobuf/repeated_field_unittest.cc | 67 ++ .../protobuf/util/internal/datapiece.cc | 5 +- .../protobuf/util/internal/json_escaping.h | 14 +- .../util/internal/protostream_objectsource.cc | 5 + src/google/protobuf/util/time_util_test.cc | 22 +- src/google/protobuf/wire_format_lite.h | 24 +- tests.sh | 1 + 135 files changed, 4551 insertions(+), 1712 deletions(-) create mode 100644 csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs create mode 100644 csharp/src/Google.Protobuf.Test/Proto3OptionalTest.cs create mode 100644 docs/field_presence.md create mode 100644 docs/implementing_proto3_presence.md create mode 100755 kokoro/linux/ruby27/build.sh create mode 100644 kokoro/linux/ruby27/continuous.cfg create mode 100644 kokoro/linux/ruby27/presubmit.cfg create mode 100755 kokoro/macos/ruby27/build.sh create mode 100644 kokoro/macos/ruby27/continuous.cfg create mode 100644 kokoro/macos/ruby27/presubmit.cfg create mode 100644 python/docs/google/protobuf/internal/containers.rst diff --git a/conformance/binary_json_conformance_suite.h b/conformance/binary_json_conformance_suite.h index aaf8bb4d9e..fb3f571405 100644 --- a/conformance/binary_json_conformance_suite.h +++ b/conformance/binary_json_conformance_suite.h @@ -53,35 +53,34 @@ class BinaryAndJsonConformanceSuite : public ConformanceTestSuite { void RunJsonTestsForStruct(); void RunJsonTestsForValue(); void RunJsonTestsForAny(); - void RunValidJsonTest(const string& test_name, - ConformanceLevel level, - const string& input_json, - const string& equivalent_text_format); + void RunValidJsonTest(const std::string& test_name, ConformanceLevel level, + const std::string& input_json, + const std::string& equivalent_text_format); void RunValidJsonTestWithProtobufInput( - const string& test_name, - ConformanceLevel level, + const std::string& test_name, ConformanceLevel level, const protobuf_test_messages::proto3::TestAllTypesProto3& input, - const string& equivalent_text_format); - void RunValidJsonIgnoreUnknownTest( - const string& test_name, ConformanceLevel level, const string& input_json, - const string& equivalent_text_format); - void RunValidProtobufTest(const string& test_name, ConformanceLevel level, - const string& input_protobuf, - const string& equivalent_text_format, + const std::string& equivalent_text_format); + void RunValidJsonIgnoreUnknownTest(const std::string& test_name, + ConformanceLevel level, + const std::string& input_json, + const std::string& equivalent_text_format); + void RunValidProtobufTest(const std::string& test_name, + ConformanceLevel level, + const std::string& input_protobuf, + const std::string& equivalent_text_format, bool is_proto3); - void RunValidBinaryProtobufTest(const string& test_name, + void RunValidBinaryProtobufTest(const std::string& test_name, ConformanceLevel level, - const string& input_protobuf, + const std::string& input_protobuf, bool is_proto3); - void RunValidBinaryProtobufTest(const string& test_name, + void RunValidBinaryProtobufTest(const std::string& test_name, ConformanceLevel level, - const string& input_protobuf, - const string& expected_protobuf, + const std::string& input_protobuf, + const std::string& expected_protobuf, bool is_proto3); void RunValidProtobufTestWithMessage( - const string& test_name, ConformanceLevel level, - const Message *input, - const string& equivalent_text_format, + const std::string& test_name, ConformanceLevel level, + const Message* input, const std::string& equivalent_text_format, bool is_proto3); bool ParseJsonResponse( @@ -93,20 +92,20 @@ class BinaryAndJsonConformanceSuite : public ConformanceTestSuite { Message* test_message) override; typedef std::function Validator; - void RunValidJsonTestWithValidator(const string& test_name, + void RunValidJsonTestWithValidator(const std::string& test_name, ConformanceLevel level, - const string& input_json, + const std::string& input_json, const Validator& validator); - void ExpectParseFailureForJson(const string& test_name, + void ExpectParseFailureForJson(const std::string& test_name, ConformanceLevel level, - const string& input_json); - void ExpectSerializeFailureForJson(const string& test_name, + const std::string& input_json); + void ExpectSerializeFailureForJson(const std::string& test_name, ConformanceLevel level, - const string& text_format); - void ExpectParseFailureForProtoWithProtoVersion (const string& proto, - const string& test_name, - ConformanceLevel level, - bool is_proto3); + const std::string& text_format); + void ExpectParseFailureForProtoWithProtoVersion(const std::string& proto, + const std::string& test_name, + ConformanceLevel level, + bool is_proto3); void ExpectParseFailureForProto(const std::string& proto, const std::string& test_name, ConformanceLevel level); diff --git a/conformance/conformance_test.h b/conformance/conformance_test.h index 417ec4e3cc..76bd1bc3f1 100644 --- a/conformance/conformance_test.h +++ b/conformance/conformance_test.h @@ -88,7 +88,7 @@ class ForkPipeRunner : public ConformanceTestRunner { const std::vector& suites); ForkPipeRunner(const std::string& executable, - const std::vector& executable_args) + const std::vector& executable_args) : child_pid_(-1), executable_(executable), executable_args_(executable_args) {} @@ -113,7 +113,7 @@ class ForkPipeRunner : public ConformanceTestRunner { int read_fd_; pid_t child_pid_; std::string executable_; - const std::vector executable_args_; + const std::vector executable_args_; std::string current_test_name_; }; @@ -168,9 +168,7 @@ class ConformanceTestSuite { // Gets the flag name to the failure list file. // By default, this would return --failure_list - string GetFailureListFlagName() { - return failure_list_flag_name_; - } + std::string GetFailureListFlagName() { return failure_list_flag_name_; } void SetFailureListFlagName(const std::string& failure_list_flag_name) { failure_list_flag_name_ = failure_list_flag_name; @@ -207,18 +205,18 @@ class ConformanceTestSuite { class ConformanceRequestSetting { public: - ConformanceRequestSetting( - ConformanceLevel level, - conformance::WireFormat input_format, - conformance::WireFormat output_format, - conformance::TestCategory test_category, - const Message& prototype_message, - const string& test_name, const string& input); + ConformanceRequestSetting(ConformanceLevel level, + conformance::WireFormat input_format, + conformance::WireFormat output_format, + conformance::TestCategory test_category, + const Message& prototype_message, + const std::string& test_name, + const std::string& input); virtual ~ConformanceRequestSetting() {} std::unique_ptr NewTestMessage() const; - string GetTestName() const; + std::string GetTestName() const; const conformance::ConformanceRequest& GetRequest() const { return request_; @@ -228,7 +226,7 @@ class ConformanceTestSuite { return level_; } - string ConformanceLevelToString(ConformanceLevel level) const; + std::string ConformanceLevelToString(ConformanceLevel level) const; void SetPrintUnknownFields(bool print_unknown_fields) { request_.set_print_unknown_fields(true); @@ -239,8 +237,9 @@ class ConformanceTestSuite { } protected: - virtual string InputFormatString(conformance::WireFormat format) const; - virtual string OutputFormatString(conformance::WireFormat format) const; + virtual std::string InputFormatString(conformance::WireFormat format) const; + virtual std::string OutputFormatString( + conformance::WireFormat format) const; conformance::ConformanceRequest request_; private: @@ -249,12 +248,12 @@ class ConformanceTestSuite { ::conformance::WireFormat output_format_; const Message& prototype_message_; std::unique_ptr prototype_message_for_compare_; - string test_name_; + std::string test_name_; }; - bool CheckSetEmpty(const std::set& set_to_check, + bool CheckSetEmpty(const std::set& set_to_check, const std::string& write_to_file, const std::string& msg); - string WireFormatToString(conformance::WireFormat wire_format); + std::string WireFormatToString(conformance::WireFormat wire_format); // Parse payload in the response to the given message. Returns true on // success. @@ -264,24 +263,23 @@ class ConformanceTestSuite { Message* test_message) = 0; void VerifyResponse(const ConformanceRequestSetting& setting, - const string& equivalent_wire_format, + const std::string& equivalent_wire_format, const conformance::ConformanceResponse& response, bool need_report_success, bool require_same_wire_format); void ReportSuccess(const std::string& test_name); - void ReportFailure(const string& test_name, - ConformanceLevel level, + void ReportFailure(const std::string& test_name, ConformanceLevel level, const conformance::ConformanceRequest& request, const conformance::ConformanceResponse& response, const char* fmt, ...); - void ReportSkip(const string& test_name, + void ReportSkip(const std::string& test_name, const conformance::ConformanceRequest& request, const conformance::ConformanceResponse& response); void RunValidInputTest(const ConformanceRequestSetting& setting, - const string& equivalent_text_format); + const std::string& equivalent_text_format); void RunValidBinaryInputTest(const ConformanceRequestSetting& setting, - const string& equivalent_wire_format, + const std::string& equivalent_wire_format, bool require_same_wire_format = false); void RunTest(const std::string& test_name, diff --git a/conformance/text_format_conformance_suite.h b/conformance/text_format_conformance_suite.h index dd258f50b8..d68f4aa3ef 100644 --- a/conformance/text_format_conformance_suite.h +++ b/conformance/text_format_conformance_suite.h @@ -42,19 +42,19 @@ class TextFormatConformanceTestSuite : public ConformanceTestSuite { private: void RunSuiteImpl(); - void RunValidTextFormatTest(const string& test_name, ConformanceLevel level, - const string& input); - void RunValidTextFormatTestProto2(const string& test_name, + void RunValidTextFormatTest(const std::string& test_name, + ConformanceLevel level, const std::string& input); + void RunValidTextFormatTestProto2(const std::string& test_name, ConformanceLevel level, - const string& input); - void RunValidTextFormatTestWithMessage(const string& test_name, + const std::string& input); + void RunValidTextFormatTestWithMessage(const std::string& test_name, ConformanceLevel level, - const string& input_text, + const std::string& input_text, const Message& prototype); - void RunValidUnknownTextFormatTest(const string& test_name, + void RunValidUnknownTextFormatTest(const std::string& test_name, const Message& message); - void ExpectParseFailure(const string& test_name, ConformanceLevel level, - const string& input); + void ExpectParseFailure(const std::string& test_name, ConformanceLevel level, + const std::string& input); bool ParseTextFormatResponse(const conformance::ConformanceResponse& response, const ConformanceRequestSetting& setting, Message* test_message); 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 350fb7cde7..1ba1275fb6 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 a35b6e0fe3..25fecb9b29 100644 --- a/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs +++ b/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs @@ -15521,7 +15521,6 @@ namespace Google.Protobuf.TestProtos.Proto2 { public sealed partial class TestOneof : pb::IMessage { 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; } } @@ -15544,7 +15543,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; @@ -16934,21 +16932,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. @@ -17767,7 +17765,6 @@ namespace Google.Protobuf.TestProtos.Proto2 { public sealed partial class TestRequiredOneof : pb::IMessage { 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; } } @@ -17790,7 +17787,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 424d96900574455edba12e63e68a4f765023832e..1758578eb773eb9caffd2c6e5ccbca57f12109f6 100644 GIT binary patch delta 3108 zcmai$%WoT16vjPw#;@!5b(%OxxQa7xS5Gy2v@+a`!nYq_?TEuS7{l4=%XO7QY`S(Wy zAAdLS(V22a&eSvQd-uEA1pOjQbNj8A=V5JlIQ3JIvU`y4a{ug`j>xj+-I6vqXt=gQB4MN1o}GE5DA4Xa;hNH?(&k3TDkgyGqWh?=(# z9@Ka3Zgb{gS%d1mpxU`pX6s9g!SHQGMeg=qH+jk|KUE<6FUody)@(jiFbUe7#O+3F zr)^K-{7Y@9Yy`S3S6ZjFMxBcpD#u!_wOWltXhg~QjNn#KbfM$a#uUEdGYfSw+wJ3n z<~|p*=BK(gSTVxg`uaos*j8dsiTkbN-9!{6{5tU|Mx%0pNODC?F!5!RQS!rRyR9$(4+&UdX%)T1;kkd=nDi2b$`Yt#B^HNIfInT;x6 zFFi>URr9l7l%bkFNC&Df*$I7!UJ~URVqfa``Sfvm?SAXfxw>-V>dK)|8=Gz)+*LOJ zzDzw#yXxBf;oH~Mw>_OC^-;yg)RZel{gn8q{b(PKx)!ZNg{l0JQlhz-HtyO~@T_Vs zoR9mGG)Ohgr9w%Q8!93wK6pOay!p*<+HfaNs6Bq-!W8p?sEClCkG&!y zG?Gt^q7u)BG)Kl9NkPpy2~hx|$?Q0Cb+ z5rHy4Htw{7V1c9^Nd;sDVi0Dsc4`qN(!IqqNHe` zEXr13T#^tsAWO0pH`gS@4akxM>9~j+lqE^=Fm;(^oEP$dvMebcP?iHsXrL^|M@0n4 za{9V66)#g)B*Y8IiiCIpS&uTZw?O#>ABn0c8yo>+U8IEX&&X ztbNZR;@0$x^R`i@u9LjujH1H2tk8*K;nxERAtG;mcuGVlu|E5nLy0lfASFkNfnrEX z3=l&?Vt^RKH-!@rV{%$V=+l@fI(^1`+jv7`w&0XtOx+-&Izx#KS%RU&M&OczVJNXN RD83Db5*rhfHIMyY|6f)34`l!V delta 19 acmZqMEOM_yq@jheg{g(Pg=GtC918$T)CPS3 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..4ce778552b --- /dev/null +++ b/docs/field_presence.md @@ -0,0 +1,476 @@ +# Application note: Field presence + +This application note explains the various presence tracking disciplines 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/java/core/src/main/java/com/google/protobuf/Descriptors.java b/java/core/src/main/java/com/google/protobuf/Descriptors.java index 960ff5da8f..6c0993951a 100644 --- a/java/core/src/main/java/com/google/protobuf/Descriptors.java +++ b/java/core/src/main/java/com/google/protobuf/Descriptors.java @@ -702,6 +702,11 @@ public final class Descriptors { return Collections.unmodifiableList(Arrays.asList(oneofs)); } + /** Get a list of this message type's real oneofs. */ + public List getRealOneofs() { + return Collections.unmodifiableList(Arrays.asList(oneofs).subList(0, realOneofCount)); + } + /** Get a list of this message type's extensions. */ public List getExtensions() { return Collections.unmodifiableList(Arrays.asList(extensions)); @@ -821,6 +826,7 @@ public final class Descriptors { private final FieldDescriptor[] fields; private final FieldDescriptor[] extensions; private final OneofDescriptor[] oneofs; + private final int realOneofCount; // Used to create a placeholder when the type cannot be found. Descriptor(final String fullname) throws DescriptorValidationException { @@ -846,6 +852,7 @@ public final class Descriptors { this.fields = new FieldDescriptor[0]; this.extensions = new FieldDescriptor[0]; this.oneofs = new OneofDescriptor[0]; + this.realOneofCount = 0; // Create a placeholder FileDescriptor to hold this message. this.file = new FileDescriptor(packageName, this); @@ -899,6 +906,18 @@ public final class Descriptors { } } + int syntheticOneofCount = 0; + for (OneofDescriptor oneof : this.oneofs) { + if (oneof.isSynthetic()) { + syntheticOneofCount++; + } else { + if (syntheticOneofCount > 0) { + throw new DescriptorValidationException(this, "Synthetic oneofs must come last."); + } + } + } + this.realOneofCount = this.oneofs.length - syntheticOneofCount; + file.pool.addSymbol(this); } @@ -1125,6 +1144,11 @@ public final class Descriptors { return containingOneof; } + /** Get the field's containing oneof, only if non-synthetic. */ + public OneofDescriptor getRealContainingOneof() { + return containingOneof != null && !containingOneof.isSynthetic() ? containingOneof : null; + } + /** * Returns true if this field was syntactically written with "optional" in the .proto file. * Excludes singular proto3 fields that do not have a label. @@ -1135,22 +1159,23 @@ public final class Descriptors { } /** - * Returns true if this is a non-oneof field that tracks presence. + * Returns true if this field tracks presence, ie. does the field distinguish between "unset" + * and "present with default value." * - *

This includes all "required" and "optional" fields in the .proto file, but excludes oneof - * fields and singular proto3 fields without "optional". + *

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. + *

For fields where hasPresence() == true, the return value of msg.hasField() is semantically + * meaningful. */ - boolean isSingularWithPresence() { + boolean hasPresence() { if (isRepeated()) { return false; } - if (getContainingOneof() != null && !getContainingOneof().isSynthetic()) { - return false; - } - return getType() == Type.MESSAGE || isProto3Optional || file.getSyntax() == Syntax.PROTO2; + return getType() == Type.MESSAGE + || getType() == Type.GROUP + || getContainingOneof() != null + || file.getSyntax() == Syntax.PROTO2; } /** 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/ruby27/build.sh b/kokoro/linux/ruby27/build.sh new file mode 100755 index 0000000000..c38ee36e58 --- /dev/null +++ b/kokoro/linux/ruby27/build.sh @@ -0,0 +1,18 @@ +#!/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/ruby +export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh +export OUTPUT_DIR=testoutput +export TEST_SET="ruby27" +./kokoro/linux/build_and_run_docker.sh diff --git a/kokoro/linux/ruby27/continuous.cfg b/kokoro/linux/ruby27/continuous.cfg new file mode 100644 index 0000000000..9cce8c90e1 --- /dev/null +++ b/kokoro/linux/ruby27/continuous.cfg @@ -0,0 +1,11 @@ +# Config file for running tests in Kokoro + +# Location of the build script in repository +build_file: "protobuf/kokoro/linux/ruby27/build.sh" +timeout_mins: 120 + +action { + define_artifacts { + regex: "**/sponge_log.xml" + } +} diff --git a/kokoro/linux/ruby27/presubmit.cfg b/kokoro/linux/ruby27/presubmit.cfg new file mode 100644 index 0000000000..9cce8c90e1 --- /dev/null +++ b/kokoro/linux/ruby27/presubmit.cfg @@ -0,0 +1,11 @@ +# Config file for running tests in Kokoro + +# Location of the build script in repository +build_file: "protobuf/kokoro/linux/ruby27/build.sh" +timeout_mins: 120 + +action { + define_artifacts { + regex: "**/sponge_log.xml" + } +} 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/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/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 7a1635f64a..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; @@ -281,6 +287,16 @@ void GPBClearMessageField(GPBMessage *self, GPBFieldDescriptor *field) { 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) { NSCAssert(self->messageStorage_ != NULL, @"%@: All messages should have storage (from init)", @@ -325,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. @@ -392,18 +410,16 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, //% [self class], field.name, //% TypeToString(GPBGetFieldDataType(field))); //%#endif -//% GPBFileSyntax syntax = [self descriptor].file.syntax; -//% GPBSet##NAME##IvarWithFieldInternal(self, field, value, syntax); +//% GPBSet##NAME##IvarWithFieldPrivate(self, field, value); //%} //% -//%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) { //% GPBOneofDescriptor *oneof = field->containingOneof_; //% GPBMessageFieldDescription *fieldDesc = field->description_; //% if (oneof) { -//% GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); +//% GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); //% } //%#if defined(DEBUG) && DEBUG //% NSCAssert(self->messageStorage_ != NULL, @@ -416,11 +432,10 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof, //% uint8_t *storage = (uint8_t *)self->messageStorage_; //% 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)); +//% // 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); //%} @@ -515,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__) @@ -532,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]); @@ -592,28 +601,19 @@ void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self, // oneof. GPBOneofDescriptor *oneof = field->containingOneof_; if (oneof) { - 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; } GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, setHasValue); } @@ -695,16 +695,12 @@ int32_t GPBGetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field) { [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - return GPBGetEnumIvarWithFieldInternal(self, field, syntax); -} -int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - GPBFileSyntax syntax) { 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; @@ -725,20 +721,18 @@ void GPBSetMessageEnumField(GPBMessage *self, GPBFieldDescriptor *field, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetInt32IvarWithFieldInternal(self, field, value, syntax); + GPBSetEnumIvarWithFieldPrivate(self, field, value); } -void GPBSetEnumIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, int32_t value, - GPBFileSyntax syntax) { +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. @@ -751,8 +745,7 @@ 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, @@ -794,18 +787,16 @@ void GPBSetMessageBoolField(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetBoolIvarWithFieldInternal(self, field, value, syntax); + GPBSetBoolIvarWithFieldPrivate(self, field, value); } -void GPBSetBoolIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - BOOL value, - GPBFileSyntax syntax) { +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 @@ -814,11 +805,10 @@ 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)); + // 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); } @@ -865,18 +855,16 @@ void GPBSetMessageInt32Field(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetInt32IvarWithFieldInternal(self, field, value, syntax); + GPBSetInt32IvarWithFieldPrivate(self, field, value); } -void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - int32_t value, - GPBFileSyntax syntax) { +void GPBSetInt32IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + int32_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -889,11 +877,10 @@ void GPBSetInt32IvarWithFieldInternal(GPBMessage *self, uint8_t *storage = (uint8_t *)self->messageStorage_; 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)); + // 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); } @@ -941,18 +928,16 @@ void GPBSetMessageUInt32Field(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetUInt32IvarWithFieldInternal(self, field, value, syntax); + GPBSetUInt32IvarWithFieldPrivate(self, field, value); } -void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - uint32_t value, - GPBFileSyntax syntax) { +void GPBSetUInt32IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + uint32_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -965,11 +950,10 @@ void GPBSetUInt32IvarWithFieldInternal(GPBMessage *self, uint8_t *storage = (uint8_t *)self->messageStorage_; 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)); + // 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); } @@ -1017,18 +1001,16 @@ void GPBSetMessageInt64Field(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetInt64IvarWithFieldInternal(self, field, value, syntax); + GPBSetInt64IvarWithFieldPrivate(self, field, value); } -void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - int64_t value, - GPBFileSyntax syntax) { +void GPBSetInt64IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + int64_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1041,11 +1023,10 @@ void GPBSetInt64IvarWithFieldInternal(GPBMessage *self, uint8_t *storage = (uint8_t *)self->messageStorage_; 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)); + // 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); } @@ -1093,18 +1074,16 @@ void GPBSetMessageUInt64Field(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetUInt64IvarWithFieldInternal(self, field, value, syntax); + GPBSetUInt64IvarWithFieldPrivate(self, field, value); } -void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - uint64_t value, - GPBFileSyntax syntax) { +void GPBSetUInt64IvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + uint64_t value) { GPBOneofDescriptor *oneof = field->containingOneof_; GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1117,11 +1096,10 @@ void GPBSetUInt64IvarWithFieldInternal(GPBMessage *self, uint8_t *storage = (uint8_t *)self->messageStorage_; 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)); + // 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); } @@ -1169,18 +1147,16 @@ void GPBSetMessageFloatField(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetFloatIvarWithFieldInternal(self, field, value, syntax); + GPBSetFloatIvarWithFieldPrivate(self, field, value); } -void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - float value, - GPBFileSyntax syntax) { +void GPBSetFloatIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + float value) { GPBOneofDescriptor *oneof = field->containingOneof_; GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1193,11 +1169,10 @@ void GPBSetFloatIvarWithFieldInternal(GPBMessage *self, uint8_t *storage = (uint8_t *)self->messageStorage_; 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)); + // 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); } @@ -1245,18 +1220,16 @@ void GPBSetMessageDoubleField(GPBMessage *self, [self class], field.name, TypeToString(GPBGetFieldDataType(field))); #endif - GPBFileSyntax syntax = [self descriptor].file.syntax; - GPBSetDoubleIvarWithFieldInternal(self, field, value, syntax); + GPBSetDoubleIvarWithFieldPrivate(self, field, value); } -void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, - GPBFieldDescriptor *field, - double value, - GPBFileSyntax syntax) { +void GPBSetDoubleIvarWithFieldPrivate(GPBMessage *self, + GPBFieldDescriptor *field, + double value) { GPBOneofDescriptor *oneof = field->containingOneof_; GPBMessageFieldDescription *fieldDesc = field->description_; if (oneof) { - GPBMaybeClearOneof(self, oneof, fieldDesc->hasIndex, fieldDesc->number); + GPBMaybeClearOneofPrivate(self, oneof, fieldDesc->hasIndex, fieldDesc->number); } #if defined(DEBUG) && DEBUG NSCAssert(self->messageStorage_ != NULL, @@ -1269,11 +1242,10 @@ void GPBSetDoubleIvarWithFieldInternal(GPBMessage *self, uint8_t *storage = (uint8_t *)self->messageStorage_; 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)); + // 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); } @@ -2267,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 1638a8c219..9c29c39c0b 100644 --- a/objectivec/GPBUtilities_PackagePrivate.h +++ b/objectivec/GPBUtilities_PackagePrivate.h @@ -207,104 +207,82 @@ GPBGetHasIvarField(GPBMessage *self, GPBFieldDescriptor *field) { return GPBGetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number); } -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. @@ -331,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 aa5b0dbe75..1dac79754e 100644 --- a/objectivec/Tests/GPBMessageTests+Runtime.m +++ b/objectivec/Tests/GPBMessageTests+Runtime.m @@ -270,6 +270,23 @@ @"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 @@ -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/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/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/internal/text_format_test.py b/python/google/protobuf/internal/text_format_test.py index cdf035b284..9e84185b55 100755 --- a/python/google/protobuf/internal/text_format_test.py +++ b/python/google/protobuf/internal/text_format_test.py @@ -57,6 +57,7 @@ from google.protobuf import unittest_proto3_arena_pb2 from google.protobuf import descriptor_pb2 from google.protobuf.internal import any_test_pb2 as test_extend_any from google.protobuf.internal import message_set_extensions_pb2 +from google.protobuf.internal import test_proto3_optional_pb2 from google.protobuf.internal import test_util from google.protobuf import descriptor_pool from google.protobuf import text_format @@ -176,14 +177,12 @@ class TextFormatMessageToStringTests(TextFormatBase): self.CompareToGoldenText( self.RemoveRedundantZeros(text_format.MessageToString(message)), 'repeated_float: 0\n' - # This should be 0.8 - 'repeated_float: 0.80000001\n' + 'repeated_float: 0.8\n' 'repeated_float: 1\n' 'repeated_float: 1.2\n' 'repeated_float: 1.23\n' 'repeated_float: 1.234\n' - # This should be 1.2345 - 'repeated_float: 1.2345001\n' + 'repeated_float: 1.2345\n' 'repeated_float: 1.23456\n' # Note that these don't use scientific notation. 'repeated_float: 12000000000\n' @@ -400,8 +399,7 @@ class TextFormatMessageToStringTests(TextFormatBase): # 32-bit 1.2 is noisy when extended to 64-bit: # >>> struct.unpack('f', struct.pack('f', 1.2))[0] # 1.2000000476837158 - # TODO(jieluo): change to 1.2 with cl/241634942. - message.payload.optional_float = 1.2000000476837158 + message.payload.optional_float = 1.2 formatted_fields = ['optional_float: 1.2', 'optional_double: -3.45678901234568e-6', 'repeated_float: -5642', 'repeated_double: 7.89e-5'] @@ -422,7 +420,7 @@ class TextFormatMessageToStringTests(TextFormatBase): 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format( *formatted_fields)) - # Test default float_format has 8 valid digits. + # Test default float_format will automatic print shortest float. message.payload.optional_float = 1.2345678912 message.payload.optional_double = 1.2345678912 formatted_fields = ['optional_float: 1.2345679', @@ -434,6 +432,17 @@ class TextFormatMessageToStringTests(TextFormatBase): 'payload {{\n {0}\n {1}\n {2}\n {3}\n}}\n'.format( *formatted_fields)) + message.Clear() + message.payload.optional_float = 1.1000000000011 + self.assertEqual(text_format.MessageToString(message), + 'payload {\n optional_float: 1.1\n}\n') + message.payload.optional_float = 1.00000075e-36 + self.assertEqual(text_format.MessageToString(message), + 'payload {\n optional_float: 1.00000075e-36\n}\n') + message.payload.optional_float = 12345678912345e+11 + self.assertEqual(text_format.MessageToString(message), + 'payload {\n optional_float: 1.234568e+24\n}\n') + def testMessageToString(self, message_module): message = message_module.ForeignMessage() message.c = 123 @@ -1850,6 +1859,24 @@ class Proto3Tests(unittest.TestCase): text_format.Merge(text, message) self.assertEqual(str(e.exception), '3:11 : Expected "}".') + def testProto3Optional(self): + msg = test_proto3_optional_pb2.TestProto3Optional() + self.assertEqual(text_format.MessageToString(msg), '') + msg.optional_int32 = 0 + msg.optional_float = 0.0 + msg.optional_string = '' + msg.optional_nested_message.bb = 0 + text = ('optional_int32: 0\n' + 'optional_float: 0.0\n' + 'optional_string: ""\n' + 'optional_nested_message {\n' + ' bb: 0\n' + '}\n') + self.assertEqual(text_format.MessageToString(msg), text) + msg2 = test_proto3_optional_pb2.TestProto3Optional() + text_format.Parse(text, msg2) + self.assertEqual(text_format.MessageToString(msg2), text) + class TokenizerTest(unittest.TestCase): diff --git a/python/google/protobuf/internal/type_checkers.py b/python/google/protobuf/internal/type_checkers.py index 514a55890c..bfde1c3e36 100644 --- a/python/google/protobuf/internal/type_checkers.py +++ b/python/google/protobuf/internal/type_checkers.py @@ -72,6 +72,26 @@ def TruncateToFourByteFloat(original): return struct.unpack(' "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/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/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/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_unittest.cc b/src/google/protobuf/arena_unittest.cc index 7e5b115633..dd73d792ff 100644 --- a/src/google/protobuf/arena_unittest.cc +++ b/src/google/protobuf/arena_unittest.cc @@ -58,6 +58,9 @@ #include +// Must be included last +#include + using proto2_arena_unittest::ArenaMessage; using protobuf_unittest::TestAllExtensions; using protobuf_unittest::TestAllTypes; diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.cc b/src/google/protobuf/compiler/cpp/cpp_map_field.cc index bea315e957..630c97e94e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.cc @@ -29,6 +29,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include + #include #include #include 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 67f2892919..d71d2c41db 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_++; } } } @@ -222,11 +220,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" @@ -234,8 +233,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())); @@ -382,23 +381,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( @@ -461,9 +461,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( @@ -488,9 +488,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( @@ -589,22 +589,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, @@ -698,8 +700,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; } @@ -709,7 +710,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 add20ab9fe..2183d752c0 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.cc b/src/google/protobuf/compiler/java/java_enum.cc index 5454861f61..8622ff0507 100644 --- a/src/google/protobuf/compiler/java/java_enum.cc +++ b/src/google/protobuf/compiler/java/java_enum.cc @@ -142,9 +142,13 @@ void EnumGenerator::Generate(io::Printer* printer) { vars["number"] = StrCat(descriptor_->value(i)->number()); vars["{"] = ""; vars["}"] = ""; + vars["deprecation"] = descriptor_->value(i)->options().deprecated() + ? "@java.lang.Deprecated " + : ""; WriteEnumValueDocComment(printer, descriptor_->value(i)); printer->Print(vars, - "public static final int ${$$name$_VALUE$}$ = $number$;\n"); + "$deprecation$public static final int ${$$name$_VALUE$}$ = " + "$number$;\n"); printer->Annotate("{", "}", descriptor_->value(i)); } printer->Print("\n"); diff --git a/src/google/protobuf/compiler/java/java_enum_lite.cc b/src/google/protobuf/compiler/java/java_enum_lite.cc index 69bd26c3f9..226fa4fb51 100644 --- a/src/google/protobuf/compiler/java/java_enum_lite.cc +++ b/src/google/protobuf/compiler/java/java_enum_lite.cc @@ -124,9 +124,13 @@ void EnumLiteGenerator::Generate(io::Printer* printer) { vars["number"] = StrCat(descriptor_->value(i)->number()); vars["{"] = ""; vars["}"] = ""; + vars["deprecation"] = descriptor_->value(i)->options().deprecated() + ? "@java.lang.Deprecated " + : ""; WriteEnumValueDocComment(printer, descriptor_->value(i)); printer->Print(vars, - "public static final int ${$$name$_VALUE$}$ = $number$;\n"); + "$deprecation$public static final int ${$$name$_VALUE$}$ = " + "$number$;\n"); printer->Annotate("{", "}", descriptor_->value(i)); } printer->Print("\n"); diff --git a/src/google/protobuf/compiler/java/java_field.cc b/src/google/protobuf/compiler/java/java_field.cc index f6b9635df9..2f775a68a6 100644 --- a/src/google/protobuf/compiler/java/java_field.cc +++ b/src/google/protobuf/compiler/java/java_field.cc @@ -251,6 +251,7 @@ void SetCommonFieldVariables(const FieldDescriptor* descriptor, (*variables)["disambiguated_reason"] = info->disambiguated_reason; (*variables)["constant_name"] = FieldConstantName(descriptor); (*variables)["number"] = StrCat(descriptor->number()); + (*variables)["kt_dsl_builder"] = "_builder"; // These variables are placeholders to pick out the beginning and ends of // identifiers for annotations (when doing so with existing variables would // be ambiguous or impossible). They should never be set to anything but the diff --git a/src/google/protobuf/compiler/java/java_helpers.cc b/src/google/protobuf/compiler/java/java_helpers.cc index a04ff62020..92ec925718 100644 --- a/src/google/protobuf/compiler/java/java_helpers.cc +++ b/src/google/protobuf/compiler/java/java_helpers.cc @@ -428,6 +428,7 @@ const char* BoxedPrimitiveTypeName(const FieldDescriptor* descriptor) { return BoxedPrimitiveTypeName(GetJavaType(descriptor)); } + std::string GetOneofStoredType(const FieldDescriptor* field) { const JavaType javaType = GetJavaType(field); switch (javaType) { diff --git a/src/google/protobuf/compiler/java/java_helpers.h b/src/google/protobuf/compiler/java/java_helpers.h index e44ad15304..046fe7e343 100644 --- a/src/google/protobuf/compiler/java/java_helpers.h +++ b/src/google/protobuf/compiler/java/java_helpers.h @@ -226,6 +226,7 @@ const char* PrimitiveTypeName(JavaType type); // types. const char* BoxedPrimitiveTypeName(JavaType type); + // Get the name of the java enum constant representing this type. E.g., // "INT32" for FieldDescriptor::TYPE_INT32. The enum constant's full // name is "com.google.protobuf.WireFormat.FieldType.INT32". diff --git a/src/google/protobuf/compiler/java/java_primitive_field.cc b/src/google/protobuf/compiler/java/java_primitive_field.cc index 277c8ca952..2572aee79a 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field.cc @@ -296,6 +296,7 @@ void ImmutablePrimitiveFieldGenerator::GenerateBuilderMembers( "}\n"); } + void ImmutablePrimitiveFieldGenerator::GenerateFieldBuilderInitializationCode( io::Printer* printer) const { // noop for primitives 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 ae40bf76f3..53366b295a 100644 --- a/src/google/protobuf/compiler/java/java_primitive_field_lite.cc +++ b/src/google/protobuf/compiler/java/java_primitive_field_lite.cc @@ -32,6 +32,8 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include + #include #include @@ -41,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -300,6 +301,7 @@ void ImmutablePrimitiveFieldLiteGenerator::GenerateBuilderMembers( printer->Annotate("{", "}", descriptor_); } + void ImmutablePrimitiveFieldLiteGenerator::GenerateFieldInfo( io::Printer* printer, std::vector* output) const { WriteIntToUtf16CharSequence(descriptor_->number(), output); 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/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 1ec38c1de5..ff8ba25eb0 100644 --- a/src/google/protobuf/descriptor.cc +++ b/src/google/protobuf/descriptor.cc @@ -1720,6 +1720,18 @@ const EnumValueDescriptor* Descriptor::FindEnumValueByName( } } +const FieldDescriptor* Descriptor::map_key() const { + if (!options().map_entry()) return nullptr; + GOOGLE_DCHECK_EQ(field_count(), 2); + return field(0); +} + +const FieldDescriptor* Descriptor::map_value() const { + if (!options().map_entry()) return nullptr; + GOOGLE_DCHECK_EQ(field_count(), 2); + return field(1); +} + const EnumValueDescriptor* EnumDescriptor::FindValueByName( const std::string& key) const { Symbol result = @@ -6222,8 +6234,8 @@ bool DescriptorBuilder::ValidateMapEntry(FieldDescriptor* field, return false; } - const FieldDescriptor* key = message->field(0); - const FieldDescriptor* value = message->field(1); + const FieldDescriptor* key = message->map_key(); + const FieldDescriptor* value = message->map_value(); if (key->label() != FieldDescriptor::LABEL_OPTIONAL || key->number() != 1 || key->name() != "key") { return false; diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h index f0eb9b6d46..08b03a85f8 100644 --- a/src/google/protobuf/descriptor.h +++ b/src/google/protobuf/descriptor.h @@ -487,6 +487,16 @@ class PROTOBUF_EXPORT Descriptor { // |*out_location| unchanged iff location information was not available. bool GetSourceLocation(SourceLocation* out_location) const; + // Maps -------------------------------------------------------------- + + // Returns the FieldDescriptor for the "key" field. If this isn't a map entry + // field, returns nullptr. + const FieldDescriptor* map_key() const; + + // Returns the FieldDescriptor for the "value" field. If this isn't a map + // entry field, returns nullptr. + const FieldDescriptor* map_value() const; + private: typedef MessageOptions OptionsType; @@ -693,7 +703,7 @@ 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 field tracks presence, ie. does the message + // Returns true if this field tracks presence, ie. does the field // 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". diff --git a/src/google/protobuf/descriptor_unittest.cc b/src/google/protobuf/descriptor_unittest.cc index 4b8bbc1cc5..844d99ce39 100644 --- a/src/google/protobuf/descriptor_unittest.cc +++ b/src/google/protobuf/descriptor_unittest.cc @@ -999,6 +999,22 @@ TEST_F(DescriptorTest, IsMap) { EXPECT_TRUE(map_->message_type()->options().map_entry()); } +TEST_F(DescriptorTest, GetMap) { + const Descriptor* map_desc = map_->message_type(); + const FieldDescriptor* map_key = map_desc->map_key(); + ASSERT_TRUE(map_key != nullptr); + EXPECT_EQ(map_key->name(), "key"); + EXPECT_EQ(map_key->number(), 1); + + const FieldDescriptor* map_value = map_desc->map_value(); + ASSERT_TRUE(map_value != nullptr); + EXPECT_EQ(map_value->name(), "value"); + EXPECT_EQ(map_value->number(), 2); + + EXPECT_EQ(message_->map_key(), nullptr); + EXPECT_EQ(message_->map_value(), nullptr); +} + TEST_F(DescriptorTest, FieldHasDefault) { EXPECT_FALSE(foo_->has_default_value()); EXPECT_FALSE(bar_->has_default_value()); @@ -7184,9 +7200,9 @@ class SourceLocationTest : public testing::Test { DescriptorPool pool_; // tag number of all custom options in above test file - static const int kCustomOptionFieldNumber = 10101; + static constexpr int kCustomOptionFieldNumber = 10101; // tag number of field "a" in message type "A" in above test file - static const int kAFieldNumber = 1; + static constexpr int kAFieldNumber = 1; }; // TODO(adonovan): implement support for option fields and for diff --git a/src/google/protobuf/extension_set_unittest.cc b/src/google/protobuf/extension_set_unittest.cc index 4bdd736d09..ccda9930ec 100644 --- a/src/google/protobuf/extension_set_unittest.cc +++ b/src/google/protobuf/extension_set_unittest.cc @@ -821,7 +821,7 @@ TEST(ExtensionSetTest, SpaceUsedExcludingSelf) { const int old_capacity = \ message.GetRepeatedExtension(unittest::repeated_##type##_extension) \ .Capacity(); \ - EXPECT_GE(old_capacity, kMinRepeatedFieldAllocationSize); \ + EXPECT_GE(old_capacity, kRepeatedFieldLowerClampLimit); \ for (int i = 0; i < 16; ++i) { \ message.AddExtension(unittest::repeated_##type##_extension, value); \ } \ @@ -864,7 +864,7 @@ TEST(ExtensionSetTest, SpaceUsedExcludingSelf) { message.AddExtension(unittest::repeated_string_extension, value); } min_expected_size += - (sizeof(value) + value.size()) * (16 - kMinRepeatedFieldAllocationSize); + (sizeof(value) + value.size()) * (16 - kRepeatedFieldLowerClampLimit); EXPECT_LE(min_expected_size, message.SpaceUsed()); } // Repeated messages @@ -880,7 +880,7 @@ TEST(ExtensionSetTest, SpaceUsedExcludingSelf) { ->CopyFrom(prototype); } min_expected_size += - (16 - kMinRepeatedFieldAllocationSize) * prototype.SpaceUsed(); + (16 - kRepeatedFieldLowerClampLimit) * prototype.SpaceUsed(); EXPECT_LE(min_expected_size, message.SpaceUsed()); } } diff --git a/src/google/protobuf/implicit_weak_message.h b/src/google/protobuf/implicit_weak_message.h index a2ae99d2dc..ec028eb5cd 100644 --- a/src/google/protobuf/implicit_weak_message.h +++ b/src/google/protobuf/implicit_weak_message.h @@ -100,7 +100,7 @@ template class ImplicitWeakTypeHandler { public: typedef MessageLite Type; - static const bool Moveable = false; + static constexpr bool Moveable = false; static inline MessageLite* NewFromPrototype(const MessageLite* prototype, Arena* arena = NULL) { diff --git a/src/google/protobuf/io/coded_stream_unittest.cc b/src/google/protobuf/io/coded_stream_unittest.cc index 7a80aed340..266b902eee 100644 --- a/src/google/protobuf/io/coded_stream_unittest.cc +++ b/src/google/protobuf/io/coded_stream_unittest.cc @@ -132,7 +132,7 @@ namespace { class CodedStreamTest : public testing::Test { protected: // Buffer used during most of the tests. This assumes tests run sequentially. - static const int kBufferSize = 1024 * 64; + static constexpr int kBufferSize = 1024 * 64; static uint8 buffer_[kBufferSize]; }; diff --git a/src/google/protobuf/map_entry.h b/src/google/protobuf/map_entry.h index c5167420bf..4e6a3e34e7 100644 --- a/src/google/protobuf/map_entry.h +++ b/src/google/protobuf/map_entry.h @@ -153,9 +153,9 @@ template > { typedef K Key; typedef V Value; - static const WireFormatLite::FieldType kKeyFieldType = key; - static const WireFormatLite::FieldType kValueFieldType = value; - static const int default_enum_value = default_enum; + static constexpr WireFormatLite::FieldType kKeyFieldType = key; + static constexpr WireFormatLite::FieldType kValueFieldType = value; + static constexpr int default_enum_value = default_enum; }; } // namespace internal diff --git a/src/google/protobuf/map_field.cc b/src/google/protobuf/map_field.cc index d61fddd61c..0949f5810c 100644 --- a/src/google/protobuf/map_field.cc +++ b/src/google/protobuf/map_field.cc @@ -196,8 +196,7 @@ bool DynamicMapField::ContainsMapKey(const MapKey& map_key) const { } void DynamicMapField::AllocateMapValue(MapValueRef* map_val) { - const FieldDescriptor* val_des = - default_entry_->GetDescriptor()->FindFieldByName("value"); + const FieldDescriptor* val_des = default_entry_->GetDescriptor()->map_value(); map_val->SetType(val_des->cpp_type()); // Allocate memory for the MapValueRef, and initialize to // default value. @@ -300,7 +299,7 @@ void DynamicMapField::MergeFrom(const MapFieldBase& other) { // Copy map value const FieldDescriptor* field_descriptor = - default_entry_->GetDescriptor()->FindFieldByName("value"); + default_entry_->GetDescriptor()->map_value(); switch (field_descriptor->cpp_type()) { case FieldDescriptor::CPPTYPE_INT32: { map_val->SetInt32Value(other_it->second.GetInt32Value()); @@ -360,10 +359,8 @@ void DynamicMapField::Swap(MapFieldBase* other) { void DynamicMapField::SyncRepeatedFieldWithMapNoLock() const { const Reflection* reflection = default_entry_->GetReflection(); - const FieldDescriptor* key_des = - default_entry_->GetDescriptor()->FindFieldByName("key"); - const FieldDescriptor* val_des = - default_entry_->GetDescriptor()->FindFieldByName("value"); + const FieldDescriptor* key_des = default_entry_->GetDescriptor()->map_key(); + const FieldDescriptor* val_des = default_entry_->GetDescriptor()->map_value(); if (MapFieldBase::repeated_field_ == NULL) { if (MapFieldBase::arena_ == NULL) { MapFieldBase::repeated_field_ = new RepeatedPtrField(); @@ -448,10 +445,8 @@ void DynamicMapField::SyncRepeatedFieldWithMapNoLock() const { void DynamicMapField::SyncMapWithRepeatedFieldNoLock() const { Map* map = &const_cast(this)->map_; const Reflection* reflection = default_entry_->GetReflection(); - const FieldDescriptor* key_des = - default_entry_->GetDescriptor()->FindFieldByName("key"); - const FieldDescriptor* val_des = - default_entry_->GetDescriptor()->FindFieldByName("value"); + const FieldDescriptor* key_des = default_entry_->GetDescriptor()->map_key(); + const FieldDescriptor* val_des = default_entry_->GetDescriptor()->map_value(); // DynamicMapField owns map values. Need to delete them before clearing // the map. if (MapFieldBase::arena_ == nullptr) { diff --git a/src/google/protobuf/map_field.h b/src/google/protobuf/map_field.h index 002c266174..ea62f88fda 100644 --- a/src/google/protobuf/map_field.h +++ b/src/google/protobuf/map_field.h @@ -443,7 +443,7 @@ class MapField : public TypeDefinedMapFieldBase { // different exposed type in Map's api and repeated field's api. For // details see the comment in the implementation of // SyncMapWithRepeatedFieldNoLock. - static const bool kIsValueEnum = ValueTypeHandler::kIsEnum; + static constexpr bool kIsValueEnum = ValueTypeHandler::kIsEnum; typedef typename MapIf::type CastValueType; public: diff --git a/src/google/protobuf/map_field_test.cc b/src/google/protobuf/map_field_test.cc index 05f83e23fa..5ccb7b0d99 100644 --- a/src/google/protobuf/map_field_test.cc +++ b/src/google/protobuf/map_field_test.cc @@ -107,8 +107,8 @@ class MapFieldBasePrimitiveTest : public ::testing::Test { map_descriptor_ = unittest::TestMap::descriptor() ->FindFieldByName("map_int32_int32") ->message_type(); - key_descriptor_ = map_descriptor_->FindFieldByName("key"); - value_descriptor_ = map_descriptor_->FindFieldByName("value"); + key_descriptor_ = map_descriptor_->map_key(); + value_descriptor_ = map_descriptor_->map_value(); // Build map field map_field_.reset(new MapFieldType); diff --git a/src/google/protobuf/map_test_util.h b/src/google/protobuf/map_test_util.h index 9787d3d1e6..8f0bdcfebe 100644 --- a/src/google/protobuf/map_test_util.h +++ b/src/google/protobuf/map_test_util.h @@ -238,6 +238,51 @@ inline MapReflectionTester::MapReflectionTester( EXPECT_FALSE(map_int32_enum_val_ == nullptr); EXPECT_FALSE(map_int32_foreign_message_key_ == nullptr); EXPECT_FALSE(map_int32_foreign_message_val_ == nullptr); + + std::vector all_map_descriptors = { + map_int32_int32_key_, + map_int32_int32_val_, + map_int64_int64_key_, + map_int64_int64_val_, + map_uint32_uint32_key_, + map_uint32_uint32_val_, + map_uint64_uint64_key_, + map_uint64_uint64_val_, + map_sint32_sint32_key_, + map_sint32_sint32_val_, + map_sint64_sint64_key_, + map_sint64_sint64_val_, + map_fixed32_fixed32_key_, + map_fixed32_fixed32_val_, + map_fixed64_fixed64_key_, + map_fixed64_fixed64_val_, + map_sfixed32_sfixed32_key_, + map_sfixed32_sfixed32_val_, + map_sfixed64_sfixed64_key_, + map_sfixed64_sfixed64_val_, + map_int32_float_key_, + map_int32_float_val_, + map_int32_double_key_, + map_int32_double_val_, + map_bool_bool_key_, + map_bool_bool_val_, + map_string_string_key_, + map_string_string_val_, + map_int32_bytes_key_, + map_int32_bytes_val_, + map_int32_enum_key_, + map_int32_enum_val_, + map_int32_foreign_message_key_, + map_int32_foreign_message_val_}; + for (const FieldDescriptor* fdesc : all_map_descriptors) { + GOOGLE_CHECK(fdesc->containing_type() != nullptr) << fdesc->name(); + if (fdesc->name() == "key") { + EXPECT_EQ(fdesc->containing_type()->map_key(), fdesc); + } else { + EXPECT_EQ(fdesc->name(), "value"); + EXPECT_EQ(fdesc->containing_type()->map_value(), fdesc); + } + } } // Shorthand to get a FieldDescriptor for a field of unittest::TestMap. diff --git a/src/google/protobuf/map_type_handler.h b/src/google/protobuf/map_type_handler.h index f59348eee3..5efc6fb6c0 100644 --- a/src/google/protobuf/map_type_handler.h +++ b/src/google/protobuf/map_type_handler.h @@ -155,13 +155,13 @@ class MapTypeHandler { typedef typename MapWireFieldTypeTraits::TypeOnMemory TypeOnMemory; // Corresponding wire type for field type. - static const WireFormatLite::WireType kWireType = + static constexpr WireFormatLite::WireType kWireType = MapWireFieldTypeTraits::kWireType; // Whether wire type is for message. - static const bool kIsMessage = + static constexpr bool kIsMessage = MapWireFieldTypeTraits::kIsMessage; // Whether wire type is for enum. - static const bool kIsEnum = + static constexpr bool kIsEnum = MapWireFieldTypeTraits::kIsEnum; // Functions used in parsing and serialization. =================== diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h index b6156b8c93..3b752df9e1 100644 --- a/src/google/protobuf/message.h +++ b/src/google/protobuf/message.h @@ -222,6 +222,9 @@ bool CreateUnknownEnumValues(const FieldDescriptor* field); // optimized for speed will want to override these with faster implementations, // but classes optimized for code size may be happy with keeping them. See // the optimize_for option in descriptor.proto. +// +// Users must not derive from this class. Only the protocol compiler and +// the internal library are allowed to create subclasses. class PROTOBUF_EXPORT Message : public MessageLite { public: inline Message() {} diff --git a/src/google/protobuf/message_lite.h b/src/google/protobuf/message_lite.h index d2cea5dc45..32a79bdb2e 100644 --- a/src/google/protobuf/message_lite.h +++ b/src/google/protobuf/message_lite.h @@ -182,6 +182,9 @@ PROTOBUF_EXPORT size_t StringSpaceUsedExcludingSelfLong(const std::string& str); // is best when you only have a small number of message types linked // into your binary, in which case the size of the protocol buffers // runtime itself is the biggest problem. +// +// Users must not derive from this class. Only the protocol compiler and +// the internal library are allowed to create subclasses. class PROTOBUF_EXPORT MessageLite { public: inline MessageLite() {} diff --git a/src/google/protobuf/metadata_lite.h b/src/google/protobuf/metadata_lite.h index dc4ec956b7..781a1f54e7 100644 --- a/src/google/protobuf/metadata_lite.h +++ b/src/google/protobuf/metadata_lite.h @@ -139,8 +139,8 @@ class InternalMetadata { // ptr_ is a Container*. kTagContainer = 1, }; - static const intptr_t kPtrTagMask = 1; - static const intptr_t kPtrValueMask = ~kPtrTagMask; + static constexpr intptr_t kPtrTagMask = 1; + static constexpr intptr_t kPtrValueMask = ~kPtrTagMask; // Accessors for pointer tag and pointer value. PROTOBUF_ALWAYS_INLINE int PtrTag() const { diff --git a/src/google/protobuf/reflection.h b/src/google/protobuf/reflection.h index 6b1e5f2eda..af8eb00ef8 100644 --- a/src/google/protobuf/reflection.h +++ b/src/google/protobuf/reflection.h @@ -471,7 +471,7 @@ class RepeatedFieldRefIterator // RepeatedFieldAccessor type, etc. template struct PrimitiveTraits { - static const bool is_primitive = false; + static constexpr bool is_primitive = false; }; #define DEFINE_PRIMITIVE(TYPE, type) \ template <> \ @@ -497,7 +497,8 @@ struct RefTypeTraits< typedef T AccessorValueType; typedef T IteratorValueType; typedef T* IteratorPointerType; - static const FieldDescriptor::CppType cpp_type = PrimitiveTraits::cpp_type; + static constexpr FieldDescriptor::CppType cpp_type = + PrimitiveTraits::cpp_type; static const Descriptor* GetMessageFieldDescriptor() { return NULL; } }; @@ -510,7 +511,7 @@ struct RefTypeTraits< typedef int32 AccessorValueType; typedef T IteratorValueType; typedef int32* IteratorPointerType; - static const FieldDescriptor::CppType cpp_type = + static constexpr FieldDescriptor::CppType cpp_type = FieldDescriptor::CPPTYPE_ENUM; static const Descriptor* GetMessageFieldDescriptor() { return NULL; } }; @@ -523,7 +524,7 @@ struct RefTypeTraits< typedef std::string AccessorValueType; typedef const std::string IteratorValueType; typedef const std::string* IteratorPointerType; - static const FieldDescriptor::CppType cpp_type = + static constexpr FieldDescriptor::CppType cpp_type = FieldDescriptor::CPPTYPE_STRING; static const Descriptor* GetMessageFieldDescriptor() { return NULL; } }; @@ -547,7 +548,7 @@ struct RefTypeTraits< typedef Message AccessorValueType; typedef const T& IteratorValueType; typedef const T* IteratorPointerType; - static const FieldDescriptor::CppType cpp_type = + static constexpr FieldDescriptor::CppType cpp_type = FieldDescriptor::CPPTYPE_MESSAGE; static const Descriptor* GetMessageFieldDescriptor() { return MessageDescriptorGetter::get(); diff --git a/src/google/protobuf/repeated_field.cc b/src/google/protobuf/repeated_field.cc index bc70be53ab..1b5a0b8d15 100644 --- a/src/google/protobuf/repeated_field.cc +++ b/src/google/protobuf/repeated_field.cc @@ -56,7 +56,7 @@ void** RepeatedPtrFieldBase::InternalExtend(int extend_amount) { } Rep* old_rep = rep_; Arena* arena = GetArena(); - new_size = std::max(kMinRepeatedFieldAllocationSize, + new_size = std::max(internal::kRepeatedFieldLowerClampLimit, std::max(total_size_ * 2, new_size)); GOOGLE_CHECK_LE(new_size, (std::numeric_limits::max() - kRepHeaderSize) / sizeof(old_rep->elements[0])) diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h index e74ffc70b5..9e2788286e 100644 --- a/src/google/protobuf/repeated_field.h +++ b/src/google/protobuf/repeated_field.h @@ -66,6 +66,7 @@ #include +// Must be included last. #include #ifdef SWIG @@ -85,7 +86,16 @@ namespace internal { class MergePartialFromCodedStreamHelper; -static const int kMinRepeatedFieldAllocationSize = 4; +// kRepeatedFieldLowerClampLimit is the smallest size that will be allocated +// when growing a repeated field. +constexpr int kRepeatedFieldLowerClampLimit = 4; + +// kRepeatedFieldUpperClampLimit is the lowest signed integer value that +// overflows when multiplied by 2 (which is undefined behavior). Sizes above +// this will clamp to the maximum int value instead of following exponential +// growth when growing a repeated field. +constexpr int kRepeatedFieldUpperClampLimit = + (std::numeric_limits::max() / 2) + 1; // A utility function for logging that doesn't need any template types. void LogIndexOutOfBounds(int index, int size); @@ -309,7 +319,7 @@ class RepeatedField final { inline void InternalSwap(RepeatedField* other); private: - static const int kInitialSize = 0; + static constexpr int kInitialSize = 0; // A note on the representation here (see also comment below for // RepeatedPtrFieldBase's struct Rep): // @@ -390,6 +400,84 @@ class RepeatedField final { } } } + + // This class is a performance wrapper around RepeatedField::Add(const T&) + // function. In general unless a RepeatedField is a local stack variable LLVM + // has a hard time optimizing Add. The machine code tends to be + // loop: + // mov %size, dword ptr [%repeated_field] // load + // cmp %size, dword ptr [%repeated_field + 4] + // jae fallback + // mov %buffer, qword ptr [%repeated_field + 8] + // mov dword [%buffer + %size * 4], %value + // inc %size // increment + // mov dword ptr [%repeated_field], %size // store + // jmp loop + // + // This puts a load/store in each iteration of the important loop variable + // size. It's a pretty bad compile that happens even in simple cases, but + // largely the presence of the fallback path disturbs the compilers mem-to-reg + // analysis. + // + // This class takes ownership of a repeated field for the duration of it's + // lifetime. The repeated field should not be accessed during this time, ie. + // only access through this class is allowed. This class should always be a + // function local stack variable. Intended use + // + // void AddSequence(const int* begin, const int* end, RepeatedField* out) + // { + // RepeatedFieldAdder adder(out); // Take ownership of out + // for (auto it = begin; it != end; ++it) { + // adder.Add(*it); + // } + // } + // + // Typically due to the fact adder is a local stack variable. The compiler + // will be successful in mem-to-reg transformation and the machine code will + // be loop: cmp %size, %capacity jae fallback mov dword ptr [%buffer + %size * + // 4], %val inc %size jmp loop + // + // The first version executes at 7 cycles per iteration while the second + // version near 1 or 2 cycles. + class FastAdder { + public: + explicit FastAdder(RepeatedField* rf) : repeated_field_(rf) { + if (kIsPod) { + index_ = repeated_field_->current_size_; + capacity_ = repeated_field_->total_size_; + buffer_ = repeated_field_->unsafe_elements(); + } + } + ~FastAdder() { + if (kIsPod) repeated_field_->current_size_ = index_; + } + + void Add(const Element& val) { + if (kIsPod) { + if (index_ == capacity_) { + repeated_field_->current_size_ = index_; + repeated_field_->Reserve(index_ + 1); + capacity_ = repeated_field_->total_size_; + buffer_ = repeated_field_->unsafe_elements(); + } + buffer_[index_++] = val; + } else { + repeated_field_->Add(val); + } + } + + private: + constexpr static bool kIsPod = std::is_pod::value; + RepeatedField* repeated_field_; + int index_; + int capacity_; + Element* buffer_; + + GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(FastAdder); + }; + + friend class TestRepeatedFieldHelper; + friend class ::google::protobuf::internal::ParseContext; }; template @@ -629,7 +717,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { inline Arena* GetArena() const { return arena_; } private: - static const int kInitialSize = 0; + static constexpr int kInitialSize = 0; // A few notes on internal representation: // // We use an indirected approach, with struct Rep, to keep @@ -648,7 +736,7 @@ class PROTOBUF_EXPORT RepeatedPtrFieldBase { int allocated_size; void* elements[1]; }; - static const size_t kRepHeaderSize = sizeof(Rep) - sizeof(void*); + static constexpr size_t kRepHeaderSize = sizeof(Rep) - sizeof(void*); Rep* rep_; template @@ -1240,14 +1328,19 @@ inline void RepeatedField::Set(int index, const Element& value) { template inline void RepeatedField::Add(const Element& value) { - if (current_size_ == total_size_) Reserve(total_size_ + 1); - elements()[current_size_++] = value; + uint32 size = current_size_; + if (static_cast(size) == total_size_) Reserve(total_size_ + 1); + elements()[size] = value; + current_size_ = size + 1; } template inline Element* RepeatedField::Add() { - if (current_size_ == total_size_) Reserve(total_size_ + 1); - return &elements()[current_size_++]; + uint32 size = current_size_; + if (static_cast(size) == total_size_) Reserve(total_size_ + 1); + auto ptr = &elements()[size]; + current_size_ = size + 1; + return ptr; } template @@ -1269,9 +1362,8 @@ inline void RepeatedField::Add(Iter begin, Iter end) { std::copy(begin, end, elements() + size()); current_size_ = reserve + size(); } else { - for (; begin != end; ++begin) { - Add(*begin); - } + FastAdder fast_adder(this); + for (; begin != end; ++begin) fast_adder.Add(*begin); } } @@ -1425,6 +1517,30 @@ inline size_t RepeatedField::SpaceUsedExcludingSelfLong() const { return total_size_ > 0 ? (total_size_ * sizeof(Element) + kRepHeaderSize) : 0; } +namespace internal { +// Returns the new size for a reserved field based on its 'total_size' and the +// requested 'new_size'. The result is clamped to the closed interval: +// [internal::kMinRepeatedFieldAllocationSize, +// std::numeric_limits::max()] +// Requires: +// new_size > total_size && +// (total_size == 0 || +// total_size >= kRepeatedFieldLowerClampLimit) +inline int CalculateReserveSize(int total_size, int new_size) { + if (new_size < kRepeatedFieldLowerClampLimit) { + // Clamp to smallest allowed size. + return kRepeatedFieldLowerClampLimit; + } + if (total_size < kRepeatedFieldUpperClampLimit) { + return std::max(total_size * 2, new_size); + } else { + // Clamp to largest allowed size. + GOOGLE_DCHECK_GT(new_size, kRepeatedFieldUpperClampLimit); + return std::numeric_limits::max(); + } +} +} // namespace internal + // Avoid inlining of Reserve(): new, copy, and delete[] lead to a significant // amount of code bloat. template @@ -1433,8 +1549,7 @@ void RepeatedField::Reserve(int new_size) { Rep* old_rep = total_size_ > 0 ? rep() : NULL; Rep* new_rep; Arena* arena = GetArena(); - new_size = std::max(internal::kMinRepeatedFieldAllocationSize, - std::max(total_size_ * 2, new_size)); + new_size = internal::CalculateReserveSize(total_size_, new_size); GOOGLE_DCHECK_LE( static_cast(new_size), (std::numeric_limits::max() - kRepHeaderSize) / sizeof(Element)) @@ -1448,6 +1563,10 @@ void RepeatedField::Reserve(int new_size) { } new_rep->arena = arena; int old_total_size = total_size_; + // Already known: new_size >= internal::kMinRepeatedFieldAllocationSize + // Maintain invariant: + // total_size_ == 0 || + // total_size_ >= internal::kMinRepeatedFieldAllocationSize total_size_ = new_size; arena_or_elements_ = new_rep->elements; // Invoke placement-new on newly allocated elements. We shouldn't have to do diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc index ca5452b8e9..3d64782785 100644 --- a/src/google/protobuf/repeated_field_unittest.cc +++ b/src/google/protobuf/repeated_field_unittest.cc @@ -38,6 +38,8 @@ #include #include +#include +#include #include #include #include @@ -53,6 +55,9 @@ #include #include +// Must be included last. +#include + namespace google { namespace protobuf { namespace { @@ -268,6 +273,68 @@ TEST(RepeatedField, Resize) { EXPECT_TRUE(field.empty()); } +TEST(RepeatedField, ReserveNothing) { + RepeatedField field; + EXPECT_EQ(0, field.Capacity()); + + field.Reserve(-1); + EXPECT_EQ(0, field.Capacity()); +} + +TEST(RepeatedField, ReserveLowerClamp) { + const int clamped_value = internal::CalculateReserveSize(0, 1); + EXPECT_EQ(internal::kRepeatedFieldLowerClampLimit, clamped_value); + EXPECT_EQ(clamped_value, internal::CalculateReserveSize(clamped_value, 2)); +} + +TEST(RepeatedField, ReserveGrowth) { + // Make sure the field capacity doubles in size on repeated reservation. + for (int size = internal::kRepeatedFieldLowerClampLimit, i = 0; i < 4; + ++i, size *= 2) { + EXPECT_EQ(size * 2, internal::CalculateReserveSize(size, size + 1)); + } +} + +TEST(RepeatedField, ReserveLarge) { + const int old_size = 10; + // This is a size we won't get by doubling: + const int new_size = old_size * 3 + 1; + + // Reserving more than 2x current capacity should grow directly to that size. + EXPECT_EQ(new_size, internal::CalculateReserveSize(old_size, new_size)); +} + +TEST(RepeatedField, ReserveHuge) { + // Largest value that does not clamp to the large limit: + constexpr int non_clamping_limit = std::numeric_limits::max() / 2; + ASSERT_LT(2 * non_clamping_limit, std::numeric_limits::max()); + EXPECT_LT(internal::CalculateReserveSize(non_clamping_limit, + non_clamping_limit + 1), + std::numeric_limits::max()); + + // Smallest size that *will* clamp to the upper limit: + constexpr int min_clamping_size = std::numeric_limits::max() / 2 + 1; + EXPECT_EQ( + internal::CalculateReserveSize(min_clamping_size, min_clamping_size + 1), + std::numeric_limits::max()); + +#ifdef PROTOBUF_TEST_ALLOW_LARGE_ALLOC + // The rest of this test may allocate several GB of memory, so it is only + // built if explicitly requested. + RepeatedField huge_field; + + // Reserve a size for huge_field that will clamp. + huge_field.Reserve(min_clamping_size); + EXPECT_GE(huge_field.Capacity(), min_clamping_size); + ASSERT_LT(huge_field.Capacity(), std::numeric_limits::max() - 1); + + // Allocation may return more memory than we requested. However, the updated + // size must still be clamped to a valid range. + huge_field.Reserve(huge_field.Capacity() + 1); + EXPECT_EQ(huge_field.Capacity(), std::numeric_limits::max()); +#endif // PROTOBUF_TEST_ALLOW_LARGE_ALLOC +} + TEST(RepeatedField, MergeFrom) { RepeatedField source, destination; source.Add(4); diff --git a/src/google/protobuf/util/internal/datapiece.cc b/src/google/protobuf/util/internal/datapiece.cc index 0d886068a6..6ce7dbc1b1 100644 --- a/src/google/protobuf/util/internal/datapiece.cc +++ b/src/google/protobuf/util/internal/datapiece.cc @@ -384,9 +384,8 @@ bool DataPiece::DecodeBase64(StringPiece src, std::string* dest) const { if (Base64Unescape(src, dest)) { if (use_strict_base64_decoding_) { std::string encoded; - Base64Escape( - reinterpret_cast(dest->data()), dest->length(), - &encoded, false); + Base64Escape(reinterpret_cast(dest->data()), + dest->length(), &encoded, false); StringPiece src_no_padding = StringPiece(src).substr( 0, HasSuffixString(src, "=") ? src.find_last_not_of('=') + 1 : src.length()); diff --git a/src/google/protobuf/util/internal/json_escaping.h b/src/google/protobuf/util/internal/json_escaping.h index 4ba765c699..d05c335976 100644 --- a/src/google/protobuf/util/internal/json_escaping.h +++ b/src/google/protobuf/util/internal/json_escaping.h @@ -44,34 +44,34 @@ class JsonEscaping { // The minimum value of a unicode high-surrogate code unit in the utf-16 // encoding. A high-surrogate is also known as a leading-surrogate. // See http://www.unicode.org/glossary/#high_surrogate_code_unit - static const uint16 kMinHighSurrogate = 0xd800; + static constexpr uint16 kMinHighSurrogate = 0xd800; // The maximum value of a unicide high-surrogate code unit in the utf-16 // encoding. A high-surrogate is also known as a leading-surrogate. // See http://www.unicode.org/glossary/#high_surrogate_code_unit - static const uint16 kMaxHighSurrogate = 0xdbff; + static constexpr uint16 kMaxHighSurrogate = 0xdbff; // The minimum value of a unicode low-surrogate code unit in the utf-16 // encoding. A low-surrogate is also known as a trailing-surrogate. // See http://www.unicode.org/glossary/#low_surrogate_code_unit - static const uint16 kMinLowSurrogate = 0xdc00; + static constexpr uint16 kMinLowSurrogate = 0xdc00; // The maximum value of a unicode low-surrogate code unit in the utf-16 // encoding. A low-surrogate is also known as a trailing surrogate. // See http://www.unicode.org/glossary/#low_surrogate_code_unit - static const uint16 kMaxLowSurrogate = 0xdfff; + static constexpr uint16 kMaxLowSurrogate = 0xdfff; // The minimum value of a unicode supplementary code point. // See http://www.unicode.org/glossary/#supplementary_code_point - static const uint32 kMinSupplementaryCodePoint = 0x010000; + static constexpr uint32 kMinSupplementaryCodePoint = 0x010000; // The minimum value of a unicode code point. // See http://www.unicode.org/glossary/#code_point - static const uint32 kMinCodePoint = 0x000000; + static constexpr uint32 kMinCodePoint = 0x000000; // The maximum value of a unicode code point. // See http://www.unicode.org/glossary/#code_point - static const uint32 kMaxCodePoint = 0x10ffff; + static constexpr uint32 kMaxCodePoint = 0x10ffff; JsonEscaping() {} virtual ~JsonEscaping() {} diff --git a/src/google/protobuf/util/internal/protostream_objectsource.cc b/src/google/protobuf/util/internal/protostream_objectsource.cc index 80b1defd7a..71e2d2ea8f 100644 --- a/src/google/protobuf/util/internal/protostream_objectsource.cc +++ b/src/google/protobuf/util/internal/protostream_objectsource.cc @@ -537,6 +537,11 @@ Status ProtoStreamObjectSource::RenderStruct(const ProtoStreamObjectSource* os, ow->StartObject(field_name); while (tag != 0) { field = os->FindAndVerifyField(type, tag); + if (field == nullptr) { + WireFormat::SkipField(os->stream_, tag, nullptr); + tag = os->stream_->ReadTag(); + continue; + } // google.protobuf.Struct has only one field that is a map. Hence we use // RenderMap to render that field. if (os->IsMap(*field)) { diff --git a/src/google/protobuf/util/time_util_test.cc b/src/google/protobuf/util/time_util_test.cc index 9f0d17fc50..b6d3813264 100644 --- a/src/google/protobuf/util/time_util_test.cc +++ b/src/google/protobuf/util/time_util_test.cc @@ -119,10 +119,10 @@ TEST(TimeUtilTest, DurationStringFormat) { // Duration must support range from -315,576,000,000s to +315576000000s // which includes negative values. EXPECT_TRUE(TimeUtil::FromString("315576000000.999999999s", &d)); - EXPECT_EQ(315576000000LL, d.seconds()); + EXPECT_EQ(int64{315576000000}, d.seconds()); EXPECT_EQ(999999999, d.nanos()); EXPECT_TRUE(TimeUtil::FromString("-315576000000.999999999s", &d)); - EXPECT_EQ(-315576000000LL, d.seconds()); + EXPECT_EQ(int64{-315576000000}, d.seconds()); EXPECT_EQ(-999999999, d.nanos()); } @@ -278,20 +278,22 @@ TEST(TimeUtilTest, DurationOperators) { // Multiplication should not overflow if the result fits into the supported // range of Duration (intermediate result may be larger than int64). EXPECT_EQ("315575999684.424s", - TimeUtil::ToString((one_second - one_nano) * 315576000000LL)); + TimeUtil::ToString((one_second - one_nano) * int64{315576000000})); EXPECT_EQ("-315575999684.424s", - TimeUtil::ToString((one_nano - one_second) * 315576000000LL)); - EXPECT_EQ("-315575999684.424s", - TimeUtil::ToString((one_second - one_nano) * (-315576000000LL))); + TimeUtil::ToString((one_nano - one_second) * int64{315576000000})); + EXPECT_EQ("-315575999684.424s", TimeUtil::ToString((one_second - one_nano) * + (int64{-315576000000}))); // Test / and % EXPECT_EQ("0.999999999s", TimeUtil::ToString(a / 2)); EXPECT_EQ("-0.999999999s", TimeUtil::ToString(b / 2)); - Duration large = TimeUtil::SecondsToDuration(315576000000LL) - one_nano; + Duration large = TimeUtil::SecondsToDuration(int64{315576000000}) - one_nano; // We have to handle division with values beyond 64 bits. - EXPECT_EQ("0.999999999s", TimeUtil::ToString(large / 315576000000LL)); - EXPECT_EQ("-0.999999999s", TimeUtil::ToString((-large) / 315576000000LL)); - EXPECT_EQ("-0.999999999s", TimeUtil::ToString(large / (-315576000000LL))); + EXPECT_EQ("0.999999999s", TimeUtil::ToString(large / int64{315576000000})); + EXPECT_EQ("-0.999999999s", + TimeUtil::ToString((-large) / int64{315576000000})); + EXPECT_EQ("-0.999999999s", + TimeUtil::ToString(large / (int64{-315576000000}))); Duration large2 = large + one_nano; EXPECT_EQ(large, large % large2); EXPECT_EQ(-large, (-large) % large2); diff --git a/src/google/protobuf/wire_format_lite.h b/src/google/protobuf/wire_format_lite.h index d022e42bdc..c742fe869e 100644 --- a/src/google/protobuf/wire_format_lite.h +++ b/src/google/protobuf/wire_format_lite.h @@ -156,9 +156,9 @@ class PROTOBUF_EXPORT WireFormatLite { } // Number of bits in a tag which identify the wire type. - static const int kTagTypeBits = 3; + static constexpr int kTagTypeBits = 3; // Mask for those bits. - static const uint32 kTagTypeMask = (1 << kTagTypeBits) - 1; + static constexpr uint32 kTagTypeMask = (1 << kTagTypeBits) - 1; // Helper functions for encoding and decoding tags. (Inlined below and in // _inl.h) @@ -210,9 +210,9 @@ class PROTOBUF_EXPORT WireFormatLite { // required string message = 3; // } // } - static const int kMessageSetItemNumber = 1; - static const int kMessageSetTypeIdNumber = 2; - static const int kMessageSetMessageNumber = 3; + static constexpr int kMessageSetItemNumber = 1; + static constexpr int kMessageSetTypeIdNumber = 2; + static constexpr int kMessageSetMessageNumber = 3; static const int kMessageSetItemStartTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG( kMessageSetItemNumber, WireFormatLite::WIRETYPE_START_GROUP); static const int kMessageSetItemEndTag = GOOGLE_PROTOBUF_WIRE_FORMAT_MAKE_TAG( @@ -685,13 +685,13 @@ class PROTOBUF_EXPORT WireFormatLite { static size_t EnumSize(const RepeatedField& value); // These types always have the same size. - static const size_t kFixed32Size = 4; - static const size_t kFixed64Size = 8; - static const size_t kSFixed32Size = 4; - static const size_t kSFixed64Size = 8; - static const size_t kFloatSize = 4; - static const size_t kDoubleSize = 8; - static const size_t kBoolSize = 1; + static constexpr size_t kFixed32Size = 4; + static constexpr size_t kFixed64Size = 8; + static constexpr size_t kSFixed32Size = 4; + static constexpr size_t kSFixed64Size = 8; + static constexpr size_t kFloatSize = 4; + static constexpr size_t kDoubleSize = 8; + static constexpr size_t kBoolSize = 1; static inline size_t StringSize(const std::string& value); static inline size_t BytesSize(const std::string& value); diff --git a/tests.sh b/tests.sh index 84fb0b124b..faaec76f0a 100755 --- a/tests.sh +++ b/tests.sh @@ -919,6 +919,7 @@ Usage: $0 { cpp | ruby24 | ruby25 | ruby26 | + ruby27 | jruby | ruby_all | php5.5 |