PROTOBUF_SYNC_PIPER
pull/7435/head^2
Joshua Haberman 5 years ago
parent bb30225f06
commit 32e5deb1ac
  1. 57
      conformance/binary_json_conformance_suite.h
  2. 40
      conformance/conformance_test.h
  3. 18
      conformance/text_format_conformance_suite.h
  4. 3
      csharp/generate_protos.sh
  5. 144
      csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs
  6. 12
      csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs
  7. 1072
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestProto3Optional.cs
  8. 143
      csharp/src/Google.Protobuf.Test/Proto3OptionalTest.cs
  9. 50
      csharp/src/Google.Protobuf.Test/Reflection/DescriptorsTest.cs
  10. 37
      csharp/src/Google.Protobuf.Test/Reflection/FieldAccessTest.cs
  11. BIN
      csharp/src/Google.Protobuf.Test/testprotos.pb
  12. 6
      csharp/src/Google.Protobuf/Reflection/FieldDescriptor.cs
  13. 3
      csharp/src/Google.Protobuf/Reflection/IFieldAccessor.cs
  14. 24
      csharp/src/Google.Protobuf/Reflection/MessageDescriptor.cs
  15. 45
      csharp/src/Google.Protobuf/Reflection/OneofAccessor.cs
  16. 28
      csharp/src/Google.Protobuf/Reflection/OneofDescriptor.cs
  17. 24
      csharp/src/Google.Protobuf/Reflection/SingleFieldAccessor.cs
  18. 476
      docs/field_presence.md
  19. 350
      docs/implementing_proto3_presence.md
  20. 45
      java/core/src/main/java/com/google/protobuf/Descriptors.java
  21. 1
      kokoro/linux/dockerfile/test/ruby/Dockerfile
  22. 18
      kokoro/linux/ruby27/build.sh
  23. 11
      kokoro/linux/ruby27/continuous.cfg
  24. 11
      kokoro/linux/ruby27/presubmit.cfg
  25. 11
      kokoro/macos/ruby27/build.sh
  26. 5
      kokoro/macos/ruby27/continuous.cfg
  27. 5
      kokoro/macos/ruby27/presubmit.cfg
  28. 1
      objectivec/DevTools/compile_testing_protos.sh
  29. 4
      objectivec/GPBAny.pbobjc.h
  30. 6
      objectivec/GPBAny.pbobjc.m
  31. 4
      objectivec/GPBApi.pbobjc.h
  32. 36
      objectivec/GPBApi.pbobjc.m
  33. 2
      objectivec/GPBBootstrap.h
  34. 41
      objectivec/GPBDescriptor.m
  35. 17
      objectivec/GPBDescriptor_PackagePrivate.h
  36. 4
      objectivec/GPBDuration.pbobjc.h
  37. 6
      objectivec/GPBDuration.pbobjc.m
  38. 4
      objectivec/GPBEmpty.pbobjc.h
  39. 2
      objectivec/GPBEmpty.pbobjc.m
  40. 4
      objectivec/GPBFieldMask.pbobjc.h
  41. 2
      objectivec/GPBFieldMask.pbobjc.m
  42. 116
      objectivec/GPBMessage.m
  43. 4
      objectivec/GPBSourceContext.pbobjc.h
  44. 4
      objectivec/GPBSourceContext.pbobjc.m
  45. 4
      objectivec/GPBStruct.pbobjc.h
  46. 14
      objectivec/GPBStruct.pbobjc.m
  47. 4
      objectivec/GPBTimestamp.pbobjc.h
  48. 6
      objectivec/GPBTimestamp.pbobjc.m
  49. 4
      objectivec/GPBType.pbobjc.h
  50. 58
      objectivec/GPBType.pbobjc.m
  51. 10
      objectivec/GPBUtilities.h
  52. 262
      objectivec/GPBUtilities.m
  53. 77
      objectivec/GPBUtilities_PackagePrivate.h
  54. 4
      objectivec/GPBWrappers.pbobjc.h
  55. 36
      objectivec/GPBWrappers.pbobjc.m
  56. 260
      objectivec/Tests/GPBMessageTests+Runtime.m
  57. 311
      objectivec/Tests/GPBMessageTests+Serialization.m
  58. 28
      objectivec/Tests/unittest_runtime_proto3.proto
  59. 35
      python/docs/generate_docs.py
  60. 21
      python/docs/google/protobuf/internal/containers.rst
  61. 1
      python/docs/index.rst
  62. 41
      python/google/protobuf/internal/text_format_test.py
  63. 20
      python/google/protobuf/internal/type_checkers.py
  64. 21
      python/google/protobuf/json_format.py
  65. 14
      python/google/protobuf/text_format.py
  66. 2
      ruby/Rakefile
  67. 1
      ruby/compatibility_tests/v3.0.0/tests/repeated_field_test.rb
  68. 100
      ruby/ext/google/protobuf_c/defs.c
  69. 6
      ruby/ext/google/protobuf_c/encode_decode.c
  70. 27
      ruby/ext/google/protobuf_c/message.c
  71. 1
      ruby/ext/google/protobuf_c/protobuf.h
  72. 91
      ruby/ext/google/protobuf_c/storage.c
  73. 71
      ruby/ext/google/protobuf_c/upb.c
  74. 739
      ruby/ext/google/protobuf_c/upb.h
  75. 114
      ruby/tests/basic.rb
  76. 14
      ruby/tests/basic_proto2.rb
  77. 36
      ruby/tests/basic_test.proto
  78. 4
      ruby/tests/gc_test.rb
  79. 2
      ruby/travis-test.sh
  80. 3
      src/google/protobuf/arena_unittest.cc
  81. 1
      src/google/protobuf/compiler/cpp/cpp_map_field.cc
  82. 6
      src/google/protobuf/compiler/csharp/csharp_field_base.cc
  83. 7
      src/google/protobuf/compiler/csharp/csharp_generator.cc
  84. 7
      src/google/protobuf/compiler/csharp/csharp_generator.h
  85. 8
      src/google/protobuf/compiler/csharp/csharp_helpers.cc
  86. 28
      src/google/protobuf/compiler/csharp/csharp_helpers.h
  87. 83
      src/google/protobuf/compiler/csharp/csharp_message.cc
  88. 6
      src/google/protobuf/compiler/csharp/csharp_message_field.cc
  89. 69
      src/google/protobuf/compiler/csharp/csharp_primitive_field.cc
  90. 4
      src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc
  91. 6
      src/google/protobuf/compiler/java/java_enum.cc
  92. 6
      src/google/protobuf/compiler/java/java_enum_lite.cc
  93. 1
      src/google/protobuf/compiler/java/java_field.cc
  94. 1
      src/google/protobuf/compiler/java/java_helpers.cc
  95. 1
      src/google/protobuf/compiler/java/java_helpers.h
  96. 1
      src/google/protobuf/compiler/java/java_primitive_field.cc
  97. 4
      src/google/protobuf/compiler/java/java_primitive_field_lite.cc
  98. 4
      src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc
  99. 38
      src/google/protobuf/compiler/objectivec/objectivec_field.cc
  100. 4
      src/google/protobuf/compiler/objectivec/objectivec_field.h
  101. Some files were not shown because too many files have changed in this diff Show More

@ -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,18 +92,18 @@ class BinaryAndJsonConformanceSuite : public ConformanceTestSuite {
Message* test_message) override;
typedef std::function<bool(const Json::Value&)> 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,
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,

@ -88,7 +88,7 @@ class ForkPipeRunner : public ConformanceTestRunner {
const std::vector<ConformanceTestSuite*>& suites);
ForkPipeRunner(const std::string& executable,
const std::vector<string>& executable_args)
const std::vector<std::string>& 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<string> executable_args_;
const std::vector<std::string> 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,
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);
const std::string& test_name,
const std::string& input);
virtual ~ConformanceRequestSetting() {}
std::unique_ptr<Message> 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<Message> prototype_message_for_compare_;
string test_name_;
std::string test_name_;
};
bool CheckSetEmpty(const std::set<string>& set_to_check,
bool CheckSetEmpty(const std::set<std::string>& 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,

@ -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);

@ -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 \

@ -1872,21 +1872,21 @@ namespace ProtobufTestMessages.Proto2 {
/// </summary>
[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;
}
}
/// <summary>Gets whether the "fieldname1" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldname1 {
get { return (_hasBits0 & 2097152) != 0; }
get { return (_hasBits0 & 32768) != 0; }
}
/// <summary>Clears the value of the "fieldname1" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldname1() {
_hasBits0 &= ~2097152;
_hasBits0 &= ~32768;
}
/// <summary>Field number for the "field_name2" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field_name2" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName2 {
get { return (_hasBits0 & 4194304) != 0; }
get { return (_hasBits0 & 65536) != 0; }
}
/// <summary>Clears the value of the "field_name2" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName2() {
_hasBits0 &= ~4194304;
_hasBits0 &= ~65536;
}
/// <summary>Field number for the "_field_name3" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "_field_name3" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName3 {
get { return (_hasBits0 & 8388608) != 0; }
get { return (_hasBits0 & 131072) != 0; }
}
/// <summary>Clears the value of the "_field_name3" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName3() {
_hasBits0 &= ~8388608;
_hasBits0 &= ~131072;
}
/// <summary>Field number for the "field__name4_" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field__name4_" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName4 {
get { return (_hasBits0 & 16777216) != 0; }
get { return (_hasBits0 & 262144) != 0; }
}
/// <summary>Clears the value of the "field__name4_" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName4() {
_hasBits0 &= ~16777216;
_hasBits0 &= ~262144;
}
/// <summary>Field number for the "field0name5" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field0name5" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasField0Name5 {
get { return (_hasBits0 & 33554432) != 0; }
get { return (_hasBits0 & 524288) != 0; }
}
/// <summary>Clears the value of the "field0name5" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearField0Name5() {
_hasBits0 &= ~33554432;
_hasBits0 &= ~524288;
}
/// <summary>Field number for the "field_0_name6" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field_0_name6" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasField0Name6 {
get { return (_hasBits0 & 67108864) != 0; }
get { return (_hasBits0 & 1048576) != 0; }
}
/// <summary>Clears the value of the "field_0_name6" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearField0Name6() {
_hasBits0 &= ~67108864;
_hasBits0 &= ~1048576;
}
/// <summary>Field number for the "fieldName7" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "fieldName7" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName7 {
get { return (_hasBits0 & 134217728) != 0; }
get { return (_hasBits0 & 2097152) != 0; }
}
/// <summary>Clears the value of the "fieldName7" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName7() {
_hasBits0 &= ~134217728;
_hasBits0 &= ~2097152;
}
/// <summary>Field number for the "FieldName8" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "FieldName8" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName8 {
get { return (_hasBits0 & 268435456) != 0; }
get { return (_hasBits0 & 4194304) != 0; }
}
/// <summary>Clears the value of the "FieldName8" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName8() {
_hasBits0 &= ~268435456;
_hasBits0 &= ~4194304;
}
/// <summary>Field number for the "field_Name9" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field_Name9" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName9 {
get { return (_hasBits0 & 536870912) != 0; }
get { return (_hasBits0 & 8388608) != 0; }
}
/// <summary>Clears the value of the "field_Name9" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName9() {
_hasBits0 &= ~536870912;
_hasBits0 &= ~8388608;
}
/// <summary>Field number for the "Field_Name10" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "Field_Name10" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName10 {
get { return (_hasBits0 & 1073741824) != 0; }
get { return (_hasBits0 & 16777216) != 0; }
}
/// <summary>Clears the value of the "Field_Name10" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName10() {
_hasBits0 &= ~1073741824;
_hasBits0 &= ~16777216;
}
/// <summary>Field number for the "FIELD_NAME11" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "FIELD_NAME11" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFIELDNAME11 {
get { return (_hasBits0 & -2147483648) != 0; }
get { return (_hasBits0 & 33554432) != 0; }
}
/// <summary>Clears the value of the "FIELD_NAME11" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFIELDNAME11() {
_hasBits0 &= ~-2147483648;
_hasBits0 &= ~33554432;
}
/// <summary>Field number for the "FIELD_name12" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "FIELD_name12" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFIELDName12 {
get { return (_hasBits1 & 1) != 0; }
get { return (_hasBits0 & 67108864) != 0; }
}
/// <summary>Clears the value of the "FIELD_name12" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFIELDName12() {
_hasBits1 &= ~1;
_hasBits0 &= ~67108864;
}
/// <summary>Field number for the "__field_name13" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "__field_name13" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName13 {
get { return (_hasBits1 & 2) != 0; }
get { return (_hasBits0 & 134217728) != 0; }
}
/// <summary>Clears the value of the "__field_name13" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName13() {
_hasBits1 &= ~2;
_hasBits0 &= ~134217728;
}
/// <summary>Field number for the "__Field_name14" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "__Field_name14" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName14 {
get { return (_hasBits1 & 4) != 0; }
get { return (_hasBits0 & 268435456) != 0; }
}
/// <summary>Clears the value of the "__Field_name14" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName14() {
_hasBits1 &= ~4;
_hasBits0 &= ~268435456;
}
/// <summary>Field number for the "field__name15" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field__name15" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName15 {
get { return (_hasBits1 & 8) != 0; }
get { return (_hasBits0 & 536870912) != 0; }
}
/// <summary>Clears the value of the "field__name15" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName15() {
_hasBits1 &= ~8;
_hasBits0 &= ~536870912;
}
/// <summary>Field number for the "field__Name16" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field__Name16" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName16 {
get { return (_hasBits1 & 16) != 0; }
get { return (_hasBits0 & 1073741824) != 0; }
}
/// <summary>Clears the value of the "field__Name16" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName16() {
_hasBits1 &= ~16;
_hasBits0 &= ~1073741824;
}
/// <summary>Field number for the "field_name17__" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "field_name17__" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName17 {
get { return (_hasBits1 & 32) != 0; }
get { return (_hasBits0 & -2147483648) != 0; }
}
/// <summary>Clears the value of the "field_name17__" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName17() {
_hasBits1 &= ~32;
_hasBits0 &= ~-2147483648;
}
/// <summary>Field number for the "Field_name18__" field.</summary>
@ -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;
}
}
/// <summary>Gets whether the "Field_name18__" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasFieldName18 {
get { return (_hasBits1 & 64) != 0; }
get { return (_hasBits1 & 1) != 0; }
}
/// <summary>Clears the value of the "Field_name18__" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearFieldName18() {
_hasBits1 &= ~64;
_hasBits1 &= ~1;
}
private object oneofField_;

@ -15521,7 +15521,6 @@ namespace Google.Protobuf.TestProtos.Proto2 {
public sealed partial class TestOneof : pb::IMessage<TestOneof> {
private static readonly pb::MessageParser<TestOneof> _parser = new pb::MessageParser<TestOneof>(() => new TestOneof());
private pb::UnknownFieldSet _unknownFields;
private int _hasBits0;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<TestOneof> 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;
}
}
/// <summary>Gets whether the "baz_int" field is set</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public bool HasBazInt {
get { return (_hasBits0 & 16) != 0; }
get { return (_hasBits0 & 1) != 0; }
}
/// <summary>Clears the value of the "baz_int" field</summary>
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public void ClearBazInt() {
_hasBits0 &= ~16;
_hasBits0 &= ~1;
}
/// <summary>Field number for the "baz_string" field.</summary>
@ -17767,7 +17765,6 @@ namespace Google.Protobuf.TestProtos.Proto2 {
public sealed partial class TestRequiredOneof : pb::IMessage<TestRequiredOneof> {
private static readonly pb::MessageParser<TestRequiredOneof> _parser = new pb::MessageParser<TestRequiredOneof>(() => new TestRequiredOneof());
private pb::UnknownFieldSet _unknownFields;
private int _hasBits0;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
public static pb::MessageParser<TestRequiredOneof> 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;

@ -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<ArgumentNullException>(() => 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();
}
}
}

@ -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<FieldDescriptor>("single_nested_enum");
FieldDescriptor foreignMessageField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_foreign_message");
FieldDescriptor importMessageField = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("single_import_message");
FieldDescriptor fieldInOneof = testAllTypesDescriptor.FindDescriptor<FieldDescriptor>("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<OneofDescriptor>("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);
}
}
}
}
}

@ -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<InvalidOperationException>(() => 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()
{

@ -58,6 +58,12 @@ namespace Google.Protobuf.Reflection
/// </summary>
public OneofDescriptor ContainingOneof { get; }
/// <summary>
/// Returns the oneof containing this field if it's a "real" oneof, or <c>null</c> if either this
/// field is not part of a oneof, or the oneof is synthetic.
/// </summary>
public OneofDescriptor RealContainingOneof => ContainingOneof?.IsSynthetic == false ? ContainingOneof : null;
/// <summary>
/// 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 <c>json_name</c> option in the .proto file.

@ -59,7 +59,8 @@ namespace Google.Protobuf.Reflection
object GetValue(IMessage message);
/// <summary>
/// Indicates whether the field in the specified message is set. For proto3 fields, this throws an <see cref="InvalidOperationException"/>
/// Indicates whether the field in the specified message is set.
/// For proto3 fields that aren't explicitly optional, this throws an <see cref="InvalidOperationException"/>
/// </summary>
bool HasValue(IMessage message);

@ -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
/// <value>
/// An unmodifiable list of the "oneof" field collections in this message type.
/// All "real" oneofs (where <see cref="OneofDescriptor.IsSynthetic"/> returns false)
/// come before synthetic ones.
/// </value>
public IList<OneofDescriptor> Oneofs { get; }
/// <summary>
/// The number of real "oneof" descriptors in this message type. Every element in <see cref="Oneofs"/>
/// with an index less than this will have a <see cref="OneofDescriptor.IsSynthetic"/> property value
/// of <c>false</c>; every element with an index greater than or equal to this will have a
/// <see cref="OneofDescriptor.IsSynthetic"/> property value of <c>true</c>.
/// </summary>
public int RealOneofCount { get; }
/// <summary>
/// Finds a field by field name.
/// </summary>

@ -43,19 +43,31 @@ namespace Google.Protobuf.Reflection
{
private readonly Func<IMessage, int> caseDelegate;
private readonly Action<IMessage> clearDelegate;
private OneofDescriptor descriptor;
internal OneofAccessor(PropertyInfo caseProperty, MethodInfo clearMethod, OneofDescriptor descriptor)
private OneofAccessor(OneofDescriptor descriptor, Func<IMessage, int> caseDelegate, Action<IMessage> clearDelegate)
{
if (!caseProperty.CanRead)
{
throw new ArgumentException("Cannot read from property");
Descriptor = descriptor;
this.caseDelegate = caseDelegate;
this.clearDelegate = clearDelegate;
}
this.descriptor = descriptor;
caseDelegate = ReflectionUtil.CreateFuncIMessageInt32(caseProperty.GetGetMethod());
this.descriptor = descriptor;
clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod);
internal static OneofAccessor ForRegularOneof(
OneofDescriptor descriptor,
PropertyInfo caseProperty,
MethodInfo clearMethod) =>
new OneofAccessor(
descriptor,
ReflectionUtil.CreateFuncIMessageInt32(caseProperty.GetGetMethod()),
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));
}
/// <summary>
@ -64,15 +76,12 @@ namespace Google.Protobuf.Reflection
/// <value>
/// The descriptor of the oneof.
/// </value>
public OneofDescriptor Descriptor { get { return descriptor; } }
public OneofDescriptor Descriptor { get; }
/// <summary>
/// Clears the oneof in the specified message.
/// </summary>
public void Clear(IMessage message)
{
clearDelegate(message);
}
public void Clear(IMessage message) => clearDelegate(message);
/// <summary>
/// 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;
}
}
}

@ -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
/// </value>
public IList<FieldDescriptor> Fields { get { return fields; } }
/// <summary>
/// Returns <c>true</c> if this oneof is a synthetic oneof containing a proto3 optional field;
/// <c>false</c> otherwise.
/// </summary>
public bool IsSynthetic { get; }
/// <summary>
/// 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;
}
if (IsSynthetic)
{
return OneofAccessor.ForSyntheticOneof(this);
}
else
{
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 new OneofAccessor(caseProperty, clearMethod, this);
return OneofAccessor.ForRegularOneof(this, caseProperty, clearMethod);
}
}
}
}

@ -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;
@ -73,19 +74,32 @@ namespace Google.Protobuf.Reflection
clearDelegate = message => SetValue(message, defaultValue);
}
else
{
// 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);
}
else
{
MethodInfo hasMethod = property.DeclaringType.GetRuntimeProperty("Has" + property.Name).GetMethod;
if (hasMethod == null) {
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) {
if (clearMethod == null)
{
throw new ArgumentException("Not all required properties/methods are available");
}
clearDelegate = ReflectionUtil.CreateActionIMessage(clearMethod);
}
}
}
public override void Clear(IMessage message)
{

@ -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];
}
```

@ -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;
}
```

@ -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<OneofDescriptor> getRealOneofs() {
return Collections.unmodifiableList(Arrays.asList(oneofs).subList(0, realOneofCount));
}
/** Get a list of this message type's extensions. */
public List<FieldDescriptor> 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."
*
* <p>This includes all "required" and "optional" fields in the .proto file, but excludes oneof
* fields and singular proto3 fields without "optional".
* <p>This includes required, optional, and oneof fields. It excludes maps, repeated fields, and
* singular proto3 fields without "optional".
*
* <p>In implementations that use hasbits, this method will probably indicate whether this field
* uses a hasbit.
* <p>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;
}
/**

@ -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"

@ -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

@ -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"
}
}

@ -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"
}
}

@ -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

@ -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

@ -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

@ -158,6 +158,7 @@ compile_protos() {
--objc_out="${OUTPUT_DIR}/google/protobuf" \
--proto_path=src/google/protobuf/ \
--proto_path=src \
--experimental_allow_proto3_optional \
"$@"
}

@ -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

@ -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";

@ -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

@ -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

@ -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.

@ -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)) {

@ -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,
@ -150,6 +154,12 @@ typedef NS_OPTIONS(uint32_t, GPBDescriptorInitializationFlags) {
// references. The runtime needs to support both until we allow a
// breaking change in the runtime.
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 ()

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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) {

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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,
//%void GPBSet##NAME##IvarWithFieldPrivate(GPBMessage *self,
//% NAME$S GPBFieldDescriptor *field,
//% NAME$S TYPE value,
//% NAME$S GPBFileSyntax syntax) {
//% 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,
void GPBSetRetainedObjectIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
id value, GPBFileSyntax syntax) {
id value) {
NSCAssert(self->messageStorage_ != NULL,
@"%@: All messages should have storage (from init)",
[self class]);
@ -592,29 +601,20 @@ 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;
}
}
GPBSetHasIvar(self, fieldDesc->hasIndex, fieldDesc->number, setHasValue);
}
uint8_t *storage = (uint8_t *)self->messageStorage_;
@ -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,
void GPBSetBoolIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
BOOL value,
GPBFileSyntax syntax) {
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,
void GPBSetInt32IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
int32_t value,
GPBFileSyntax syntax) {
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,
void GPBSetUInt32IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
uint32_t value,
GPBFileSyntax syntax) {
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,
void GPBSetInt64IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
int64_t value,
GPBFileSyntax syntax) {
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,
void GPBSetUInt64IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
uint64_t value,
GPBFileSyntax syntax) {
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,
void GPBSetFloatIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
float value,
GPBFileSyntax syntax) {
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,
void GPBSetDoubleIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
double value,
GPBFileSyntax syntax) {
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

@ -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,
//%void GPBSet##NAME##IvarWithFieldPrivate(GPBMessage *self,
//% NAME$S GPBFieldDescriptor *field,
//% NAME$S TYPE value,
//% NAME$S GPBFileSyntax syntax);
//% 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,
void GPBSetBoolIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
BOOL value,
GPBFileSyntax syntax);
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,
void GPBSetInt32IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
int32_t value,
GPBFileSyntax syntax);
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,
void GPBSetUInt32IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
uint32_t value,
GPBFileSyntax syntax);
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,
void GPBSetInt64IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
int64_t value,
GPBFileSyntax syntax);
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,
void GPBSetUInt64IvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
uint64_t value,
GPBFileSyntax syntax);
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,
void GPBSetFloatIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
float value,
GPBFileSyntax syntax);
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,
void GPBSetDoubleIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
double value,
GPBFileSyntax syntax);
double value);
// 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
//%PDDM-EXPAND-END (7 expansions)
void GPBSetEnumIvarWithFieldInternal(GPBMessage *self,
void GPBSetEnumIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
int32_t value,
GPBFileSyntax syntax);
// clang-format on
//%PDDM-EXPAND-END (8 expansions)
int32_t GPBGetEnumIvarWithFieldInternal(GPBMessage *self,
GPBFieldDescriptor *field,
GPBFileSyntax syntax);
int32_t value);
id GPBGetObjectIvarWithField(GPBMessage *self, GPBFieldDescriptor *field);
void GPBSetObjectIvarWithFieldInternal(GPBMessage *self,
GPBFieldDescriptor *field, id value,
GPBFileSyntax syntax);
void GPBSetRetainedObjectIvarWithFieldInternal(GPBMessage *self,
void GPBSetObjectIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field, id value);
void GPBSetRetainedObjectIvarWithFieldPrivate(GPBMessage *self,
GPBFieldDescriptor *field,
id __attribute__((ns_consumed))
value,
GPBFileSyntax syntax);
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

@ -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

@ -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

@ -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];

@ -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];

@ -119,3 +119,31 @@ message Message3 {
map<int32 , Enum > map_int32_enum = 87;
map<int32 , Message3> 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;
}

@ -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

@ -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
<https://github.com/protocolbuffers/protobuf/tree/master/python>`_ of
the `Protocol Buffers package for Python
<https://developers.google.com/protocol-buffers/docs/pythontutorial>`_.
Some features may not yet be released. Read the documentation for the
latest released package at `googleapis.dev
<https://googleapis.dev/python/protobuf/latest/>`_.
google.protobuf.internal.containers
===================================
.. automodule:: google.protobuf.internal.containers
:members:
:inherited-members:
:undoc-members:

@ -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

@ -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):

@ -72,6 +72,26 @@ def TruncateToFourByteFloat(original):
return struct.unpack('<f', struct.pack('<f', original))[0]
def ToShortestFloat(original):
"""Returns the shortest float that has same value in wire."""
# Return the original value if it is not truncated. This may happen
# if someone mixes this code with an old protobuf runtime.
# TODO(jieluo): Remove it after maybe 2022.
if TruncateToFourByteFloat(original) != original:
return original
# All 4 byte floats have between 6 and 9 significant digits, so we
# start with 6 as the lower bound.
# It has to be iterative because use '.9g' directly can not get rid
# of the noises for most values. For example if set a float_field=0.9
# use '.9g' will print 0.899999976.
precision = 6
rounded = float('{0:.{1}g}'.format(original, precision))
while TruncateToFourByteFloat(rounded) != original:
precision += 1
rounded = float('{0:.{1}g}'.format(original, precision))
return rounded
def SupportsOpenEnums(field_descriptor):
return field_descriptor.containing_type.syntax == "proto3"

@ -283,25 +283,6 @@ class _Printer(object):
def _FieldToJsonObject(self, field, value):
"""Converts field value according to Proto3 JSON Specification."""
def _ToShortestFloat(original):
"""Returns the shortest float that has same value in wire."""
# Return the original value if it is not truncated. This may happen
# if someone mixes this code with an old protobuf runtime.
if type_checkers.TruncateToFourByteFloat(original) != original:
return original
# All 4 byte floats have between 6 and 9 significant digits, so we
# start with 6 as the lower bound.
# It has to be iterative because use '.9g' directly can not get rid
# of the noises for most values. For example if set a float_field=0.9
# use '.9g' will print 0.899999976.
precision = 6
rounded = float('{0:.{1}g}'.format(original, precision))
while type_checkers.TruncateToFourByteFloat(rounded) != original:
precision += 1
rounded = float('{0:.{1}g}'.format(original, precision))
return rounded
if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
return self._MessageToJsonObject(value)
elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM:
@ -337,7 +318,7 @@ class _Printer(object):
if self.float_format:
return float(format(value, self.float_format))
else:
return _ToShortestFloat(value)
return type_checkers.ToShortestFloat(value)
return value

@ -158,9 +158,9 @@ def MessageToString(
determined by the extension number. By default, use the field number
order.
float_format (str): If set, use this to specify float field formatting
(per the "Format Specification Mini-Language"); otherwise, 8 valid
digits is used (default '.8g'). Also affect double field if
double_format is not set but float_format is set.
(per the "Format Specification Mini-Language"); otherwise, shortest float
that has same value in wire will be printed. Also affect double field
if double_format is not set but float_format is set.
double_format (str): If set, use this to specify double field formatting
(per the "Format Specification Mini-Language"); if it is not set but
float_format is set, use float_format. Otherwise, use ``str()``
@ -367,9 +367,9 @@ class _Printer(object):
defined in source code instead of the field number. By default, use the
field number order.
float_format: If set, use this to specify float field formatting
(per the "Format Specification Mini-Language"); otherwise, 8 valid
digits is used (default '.8g'). Also affect double field if
double_format is not set but float_format is set.
(per the "Format Specification Mini-Language"); otherwise, shortest
float that has same value in wire will be printed. Also affect double
field if double_format is not set but float_format is set.
double_format: If set, use this to specify double field formatting
(per the "Format Specification Mini-Language"); if it is not set but
float_format is set, use float_format. Otherwise, str() is used.
@ -630,7 +630,7 @@ class _Printer(object):
if self.float_format is not None:
out.write('{1:{0}}'.format(self.float_format, value))
else:
out.write(str(float(format(value, '.8g'))))
out.write(str(type_checkers.ToShortestFloat(value)))
elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_DOUBLE and
self.double_format is not None):
out.write('{1:{0}}'.format(self.double_format, value))

@ -124,7 +124,7 @@ file "tests/test_ruby_package_proto2.rb" => "tests/test_ruby_package_proto2.prot
end
file "tests/basic_test.rb" => "tests/basic_test.proto" do |file_task|
sh "../src/protoc -I../src -I. --ruby_out=. tests/basic_test.proto"
sh "../src/protoc --experimental_allow_proto3_optional -I../src -I. --ruby_out=. tests/basic_test.proto"
end
file "tests/basic_test_proto2.rb" => "tests/basic_test_proto2.proto" do |file_task|

@ -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

@ -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;
}

@ -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);

@ -242,10 +242,15 @@ 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)) {
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;
*f = test_f;
@ -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,9 +629,8 @@ 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) &&
// 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;
}

@ -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);

@ -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));
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,9 +986,18 @@ void layout_set(MessageLayout* layout,
if (layout->fields[upb_fielddef_index(field)].hasbit !=
MESSAGE_FIELD_NO_HASBIT) {
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);
}
}
}
void layout_init(MessageLayout* layout, void* storage) {
VALUE* value = (VALUE*)CHARPTR_AT(storage, layout->value_offset);
@ -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;

@ -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);
}

@ -211,9 +211,6 @@ int msvc_vsnprintf(char* s, size_t n, const char* format, va_list arg);
#include <string.h>
/*
** 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 <stdint.h>
#include <string.h>
#ifdef __cplusplus
#include <memory>
namespace upb {
class Arena;
class Status;
template <int N> 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<upb_arena, decltype(&upb_arena_free)> 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 <int N> 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<upb_arena, decltype(&upb_arena_free)> 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 <cstring>
#include <memory>
#include <string>
#include <vector>
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 <class T>
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<std::forward_iterator_tag, FieldDefPtr> {
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 <class T>
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 <class T>
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<std::forward_iterator_tag, FieldDefPtr> {
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<std::forward_iterator_tag, OneofDefPtr> {
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<upb_symtab, decltype(&upb_symtab_free)> 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_

@ -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,41 +78,60 @@ 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 = 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
def test_clear_repeated_fields
m = TestMessage.new
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
m.repeated_int32.push(1)
assert_equal [1], m.repeated_int32
m.clear_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")

@ -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")

@ -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;
}

@ -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

@ -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 && \

@ -58,6 +58,9 @@
#include <google/protobuf/stubs/strutil.h>
// Must be included last
#include <google/protobuf/port_def.inc>
using proto2_arena_unittest::ArenaMessage;
using protobuf_unittest::TestAllExtensions;
using protobuf_unittest::TestAllTypes;

@ -29,6 +29,7 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <google/protobuf/compiler/cpp/cpp_map_field.h>
#include <google/protobuf/compiler/cpp/cpp_helpers.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>

@ -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<string, string>* 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"] =

@ -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);

@ -50,11 +50,14 @@ namespace csharp {
// CodeGenerator with the CommandLineInterface in your main() function.
class PROTOC_EXPORT Generator : public CodeGenerator {
public:
virtual bool Generate(
Generator();
~Generator();
bool Generate(
const FileDescriptor* file,
const string& parameter,
GeneratorContext* generator_context,
string* error) const;
string* error) const override;
uint64 GetSupportedFeatures() const override;
};
} // namespace csharp

@ -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);

@ -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

@ -72,19 +72,17 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor,
std::sort(fields_by_number_.begin(), fields_by_number_.end(),
CompareFieldNumbers);
if (IsProto2(descriptor_->file())) {
int primitiveCount = 0;
int presence_bit_count = 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) {
if (RequiresPresenceBit(field)) {
presence_bit_count++;
if (has_bit_field_count_ == 0 || (presence_bit_count % 32) == 0) {
has_bit_field_count_++;
}
}
}
}
}
MessageGenerator::~MessageGenerator() {
}
@ -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<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->GenerateCloningCode(printer);
const FieldDescriptor* field = descriptor_->field(i);
if (field->real_containing_oneof()) {
continue;
}
std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
generator->GenerateCloningCode(printer);
}
// 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);
// 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<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
vars["field_property_name"] = GetPropertyName(field);
printer->Print(
@ -461,7 +461,7 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->WriteEquals(printer);
}
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
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));
}
@ -488,7 +488,7 @@ void MessageGenerator::GenerateFrameworkMethods(io::Printer* printer) {
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->WriteHash(printer);
}
for (int i = 0; i < descriptor_->oneof_decl_count(); i++) {
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));
}
@ -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<FieldGeneratorBase> generator(
CreateFieldGeneratorInternal(descriptor_->field(i)));
generator->GenerateMergingCode(printer);
const FieldDescriptor* field = descriptor_->field(i);
if (field->real_containing_oneof()) {
continue;
}
std::unique_ptr<FieldGeneratorBase> generator(CreateFieldGeneratorInternal(field));
generator->GenerateMergingCode(printer);
}
// 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);
// 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++;
}
}

@ -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_,
"/// <summary>Gets whether the $descriptor_name$ field is set</summary>\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_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");

@ -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_, "/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");
// The "HasFoo" property, where required.
if (SupportsPresenceApi(descriptor_)) {
printer->Print(variables_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\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_, "/// <summary>Clears the value of the \"$descriptor_name$\" field</summary>\n");
// The "ClearFoo" method, where required.
if (SupportsPresenceApi(descriptor_)) {
printer->Print(variables_,
"/// <summary>Clears the value of the \"$descriptor_name$\" field</summary>\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_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");

@ -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_,
"/// <summary>Gets whether the $descriptor_name$ field is set</summary>\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_,
"/// <summary>Gets whether the \"$descriptor_name$\" field is set</summary>\n");

@ -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");

@ -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");

@ -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

@ -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) {

@ -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".

@ -296,6 +296,7 @@ void ImmutablePrimitiveFieldGenerator::GenerateBuilderMembers(
"}\n");
}
void ImmutablePrimitiveFieldGenerator::GenerateFieldBuilderInitializationCode(
io::Printer* printer) const {
// noop for primitives

@ -32,6 +32,8 @@
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include <google/protobuf/compiler/java/java_primitive_field_lite.h>
#include <map>
#include <string>
@ -41,7 +43,6 @@
#include <google/protobuf/compiler/java/java_doc_comment.h>
#include <google/protobuf/compiler/java/java_helpers.h>
#include <google/protobuf/compiler/java/java_name_resolver.h>
#include <google/protobuf/compiler/java/java_primitive_field_lite.h>
#include <google/protobuf/io/printer.h>
#include <google/protobuf/wire_format.h>
#include <google/protobuf/stubs/strutil.h>
@ -300,6 +301,7 @@ void ImmutablePrimitiveFieldLiteGenerator::GenerateBuilderMembers(
printer->Annotate("{", "}", descriptor_);
}
void ImmutablePrimitiveFieldLiteGenerator::GenerateFieldInfo(
io::Printer* printer, std::vector<uint16>* output) const {
WriteIntToUtf16CharSequence(descriptor_->number(), output);

@ -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");
}

@ -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,

@ -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<string, string> 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.

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save