PROTOBUF_SYNC_PIPER
pull/10325/head
Sandy Zhang 3 years ago
parent b2aca61f4d
commit aebdd0885f
  1. 25
      CHANGES.txt
  2. 2
      Protobuf.podspec
  3. 2
      conformance/failure_list_php.txt
  4. 3
      conformance/failure_list_php_c.txt
  5. 3
      conformance/failure_list_ruby.txt
  6. 2
      csharp/Google.Protobuf.Tools.nuspec
  7. 18
      csharp/protos/unittest_issues.proto
  8. 618
      csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
  9. 63
      csharp/src/Google.Protobuf.Test/ParsingPrimitivesTest.cs
  10. 61
      csharp/src/Google.Protobuf.Test/WritingPrimitivesTest.cs
  11. BIN
      csharp/src/Google.Protobuf.Test/testprotos.pb
  12. 2
      csharp/src/Google.Protobuf/Google.Protobuf.csproj
  13. 6
      java/README.md
  14. 2
      java/bom/pom.xml
  15. 2
      java/core/pom.xml
  16. 2
      java/kotlin-lite/pom.xml
  17. 2
      java/kotlin/pom.xml
  18. 5
      java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt
  19. 2
      java/lite.md
  20. 2
      java/lite/pom.xml
  21. 2
      java/pom.xml
  22. 4
      java/protoc/pom.xml
  23. 2
      java/util/pom.xml
  24. 12
      kokoro/macos/php7.0_mac/build.sh
  25. 12
      kokoro/macos/php7.3_mac/build.sh
  26. 20
      kokoro/macos/php74/build.sh
  27. 2
      kokoro/macos/php74/continuous.cfg
  28. 2
      kokoro/macos/php74/presubmit.cfg
  29. 20
      kokoro/macos/php80/build.sh
  30. 2
      kokoro/macos/php80/continuous.cfg
  31. 2
      kokoro/macos/php80/presubmit.cfg
  32. 36
      kokoro/macos/prepare_build_macos_rc
  33. 3
      kokoro/release/ruby/macos/build_artifacts.sh
  34. 1
      kokoro/release/ruby/macos/ruby/ruby_build.sh
  35. 10
      kokoro/release/ruby/macos/ruby/ruby_build_environment.sh
  36. 8
      objectivec/GPBApi.pbobjc.h
  37. 2
      objectivec/GPBFieldMask.pbobjc.h
  38. 4
      objectivec/GPBStruct.pbobjc.h
  39. 14
      objectivec/GPBType.pbobjc.h
  40. 2
      objectivec/GPBUtilities.m
  41. 1
      objectivec/generate_well_known_types.sh
  42. 2
      php/BUILD.bazel
  43. 5
      php/composer.json
  44. 24
      php/ext/google/protobuf/def.c
  45. 90
      php/ext/google/protobuf/names.c
  46. 3
      php/ext/google/protobuf/names.h
  47. 29
      php/ext/google/protobuf/package.xml
  48. 14
      php/ext/google/protobuf/protobuf.c
  49. 2
      php/ext/google/protobuf/protobuf.h
  50. 16
      php/src/Google/Protobuf/Internal/Descriptor.php
  51. 1
      php/src/Google/Protobuf/Internal/DescriptorPool.php
  52. 3
      php/src/Google/Protobuf/Internal/EnumDescriptor.php
  53. 58
      php/src/Google/Protobuf/Internal/GPBUtil.php
  54. 18
      php/tests/GeneratedClassTest.php
  55. 18
      php/tests/PreviouslyGeneratedClassTest.php
  56. 28
      php/tests/generated_previous/GPBMetadata/ProtoPrevious/TestPreviouslyUnreservedMessage.php
  57. 31
      php/tests/generated_previous/Previous/readonly.php
  58. 1
      php/tests/proto/test_reserved_enum_lower.proto
  59. 1
      php/tests/proto/test_reserved_enum_upper.proto
  60. 1
      php/tests/proto/test_reserved_enum_value_lower.proto
  61. 1
      php/tests/proto/test_reserved_enum_value_upper.proto
  62. 1
      php/tests/proto/test_reserved_message_lower.proto
  63. 1
      php/tests/proto/test_reserved_message_upper.proto
  64. 5
      php/tests/proto_previous/test_previously_unreserved_message.proto
  65. 4
      protobuf_deps.bzl
  66. 6
      protobuf_version.bzl
  67. 4
      protoc-artifacts/pom.xml
  68. 2
      ruby/google-protobuf.gemspec
  69. 4
      ruby/pom.xml
  70. 1
      src/Makefile.am
  71. 1
      src/file_lists.cmake
  72. 2
      src/google/protobuf/arena_unittest.cc
  73. 7
      src/google/protobuf/arenastring.cc
  74. 5
      src/google/protobuf/arenaz_sampler.cc
  75. 4
      src/google/protobuf/arenaz_sampler.h
  76. 25
      src/google/protobuf/arenaz_sampler_test.cc
  77. 4
      src/google/protobuf/compiler/command_line_interface.cc
  78. 1
      src/google/protobuf/compiler/cpp/field.cc
  79. 1
      src/google/protobuf/compiler/cpp/field.h
  80. 8
      src/google/protobuf/compiler/cpp/file.cc
  81. 4
      src/google/protobuf/compiler/cpp/helpers.cc
  82. 82
      src/google/protobuf/compiler/cpp/message.cc
  83. 46
      src/google/protobuf/compiler/cpp/parse_function_generator.cc
  84. 25
      src/google/protobuf/compiler/csharp/csharp_helpers.cc
  85. 12
      src/google/protobuf/compiler/java/enum_field.cc
  86. 12
      src/google/protobuf/compiler/java/enum_field_lite.cc
  87. 5
      src/google/protobuf/compiler/java/generator.h
  88. 36
      src/google/protobuf/compiler/java/message_serialization.h
  89. 124
      src/google/protobuf/compiler/java/message_serialization_unittest.cc
  90. 56
      src/google/protobuf/compiler/java/message_serialization_unittest.proto
  91. 9
      src/google/protobuf/compiler/main.cc
  92. 2
      src/google/protobuf/compiler/objectivec/objectivec_field.cc
  93. 151
      src/google/protobuf/compiler/php/php_generator.cc
  94. 6
      src/google/protobuf/compiler/python/generator.h
  95. 71
      src/google/protobuf/compiler/python/pyi_generator.cc
  96. 25
      src/google/protobuf/compiler/python/pyi_generator.h
  97. 162
      src/google/protobuf/generated_message_tctable_impl.h
  98. 73
      src/google/protobuf/generated_message_tctable_lite.cc
  99. 16
      src/google/protobuf/io/printer.h
  100. 2
      src/google/protobuf/message.cc
  101. Some files were not shown because too many files have changed in this diff Show More

@ -14,6 +14,31 @@
* Performance improvement for repeated use of FieldMaskUtil#merge by caching * Performance improvement for repeated use of FieldMaskUtil#merge by caching
constructed FieldMaskTrees. constructed FieldMaskTrees.
2022-07-19 version 21.3 (C++/Java/Python/PHP/Objective-C/C#/Ruby)
C++
* Add header search paths to Protobuf-C++.podspec (#10024)
* Fixed Visual Studio constinit errors (#10232)
* Fix #9947: make the ABI compatible between debug and non-debug builds (#10271)
UPB
* Allow empty package names (fixes behavior regression in 4.21.0)
* Fix a SEGV bug when comparing a non-materialized sub-message (#10208)
* Fix several bugs in descriptor mapping containers (eg. descriptor.services_by_name)
* for x in mapping now yields keys rather than values, to match Python conventions and the behavior of the old library.
* Lookup operations now correctly reject unhashable types as map keys.
* We implement repr() to use the same format as dict.
* Fix maps to use the ScalarMapContainer class when appropriate
* Fix bug when parsing an unknown value in a proto2 enum extension (protocolbuffers/upb#717)
PHP
* Add "readonly" as a keyword for PHP and add previous classnames to descriptor pool (#10041)
Python
* Make //:protobuf_python and //:well_known_types_py_pb2 public (#10118)
Bazel
* Add back a filegroup for :well_known_protos (#10061)
2022-06-27 version 21.2 (C++/Java/Python/PHP/Objective-C/C#/Ruby) 2022-06-27 version 21.2 (C++/Java/Python/PHP/Objective-C/C#/Ruby)
C++ C++
* ArenaString improvements (fix alignment issue) * ArenaString improvements (fix alignment issue)

@ -5,7 +5,7 @@
# dependent projects use the :git notation to refer to the library. # dependent projects use the :git notation to refer to the library.
Pod::Spec.new do |s| Pod::Spec.new do |s|
s.name = 'Protobuf' s.name = 'Protobuf'
s.version = '3.21.2' s.version = '3.21.3'
s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.' s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.'
s.homepage = 'https://github.com/protocolbuffers/protobuf' s.homepage = 'https://github.com/protocolbuffers/protobuf'
s.license = 'BSD-3-Clause' s.license = 'BSD-3-Clause'

@ -8,6 +8,8 @@ Recommended.Proto3.JsonInput.FieldMaskInvalidCharacter
Recommended.Proto3.ProtobufInput.ValidDataOneofBinary.MESSAGE.Merge.ProtobufOutput Recommended.Proto3.ProtobufInput.ValidDataOneofBinary.MESSAGE.Merge.ProtobufOutput
Required.Proto2.JsonInput.StoresDefaultPrimitive.Validator Required.Proto2.JsonInput.StoresDefaultPrimitive.Validator
Required.Proto3.JsonInput.DoubleFieldTooSmall Required.Proto3.JsonInput.DoubleFieldTooSmall
Required.Proto3.JsonInput.DurationNegativeNanos.JsonOutput
Required.Proto3.JsonInput.DurationNegativeNanos.ProtobufOutput
Required.Proto3.JsonInput.FloatFieldTooLarge Required.Proto3.JsonInput.FloatFieldTooLarge
Required.Proto3.JsonInput.FloatFieldTooSmall Required.Proto3.JsonInput.FloatFieldTooSmall
Required.Proto3.JsonInput.Int32FieldNotInteger Required.Proto3.JsonInput.Int32FieldNotInteger

@ -1,2 +1,5 @@
Recommended.Proto2.JsonInput.FieldNameExtension.Validator Recommended.Proto2.JsonInput.FieldNameExtension.Validator
Required.Proto2.JsonInput.StoresDefaultPrimitive.Validator Required.Proto2.JsonInput.StoresDefaultPrimitive.Validator
Required.Proto3.JsonInput.DurationNegativeNanos.JsonOutput
Required.Proto3.JsonInput.DurationNegativeNanos.ProtobufOutput
Required.Proto3.JsonInput.DurationNegativeSeconds.JsonOutput

@ -56,3 +56,6 @@ Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT32.PackedInput.UnpackedOu
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT32.UnpackedInput.UnpackedOutput.ProtobufOutput Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT32.UnpackedInput.UnpackedOutput.ProtobufOutput
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.PackedInput.UnpackedOutput.ProtobufOutput Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.PackedInput.UnpackedOutput.ProtobufOutput
Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.UnpackedInput.UnpackedOutput.ProtobufOutput Recommended.Proto3.ProtobufInput.ValidDataRepeated.UINT64.UnpackedInput.UnpackedOutput.ProtobufOutput
Required.Proto3.JsonInput.DurationNegativeNanos.JsonOutput
Required.Proto3.JsonInput.DurationNegativeNanos.ProtobufOutput
Required.Proto3.JsonInput.DurationNegativeSeconds.JsonOutput

@ -5,7 +5,7 @@
<title>Google Protocol Buffers tools</title> <title>Google Protocol Buffers tools</title>
<summary>Tools for Protocol Buffers - Google's data interchange format.</summary> <summary>Tools for Protocol Buffers - Google's data interchange format.</summary>
<description>See project site for more info.</description> <description>See project site for more info.</description>
<version>3.21.2</version> <version>3.21.3</version>
<authors>Google Inc.</authors> <authors>Google Inc.</authors>
<owners>protobuf-packages</owners> <owners>protobuf-packages</owners>
<licenseUrl>https://github.com/protocolbuffers/protobuf/blob/main/LICENSE</licenseUrl> <licenseUrl>https://github.com/protocolbuffers/protobuf/blob/main/LICENSE</licenseUrl>

@ -169,4 +169,20 @@ message OneofWithNoneName {
string x = 1; string x = 1;
string y = 2; string y = 2;
} }
} }
// Issue 8810
message DisambiguateCommonMembers {
int32 disambiguate_common_members = 1;
int32 types = 2;
int32 descriptor = 3;
int32 equals = 4;
int32 to_string = 5;
int32 get_hash_code = 6;
int32 write_to = 7;
int32 clone = 8;
int32 calculate_size = 9;
int32 merge_from = 10;
int32 on_construction = 11;
int32 parser = 12;
}

@ -56,11 +56,17 @@ namespace UnitTest.Issues.TestProtos {
"dWxhcl9maWVsZBgBIAEoCRIbCg5vcHRpb25hbF9maWVsZBgCIAEoCUgAiAEB", "dWxhcl9maWVsZBgBIAEoCRIbCg5vcHRpb25hbF9maWVsZBgCIAEoCUgAiAEB",
"QhEKD19vcHRpb25hbF9maWVsZCI5ChJPbmVvZldpdGhOb25lRmllbGQSCwoB", "QhEKD19vcHRpb25hbF9maWVsZCI5ChJPbmVvZldpdGhOb25lRmllbGQSCwoB",
"eBgBIAEoCUgAEg4KBG5vbmUYAiABKAlIAEIGCgR0ZXN0IjUKEU9uZW9mV2l0", "eBgBIAEoCUgAEg4KBG5vbmUYAiABKAlIAEIGCgR0ZXN0IjUKEU9uZW9mV2l0",
"aE5vbmVOYW1lEgsKAXgYASABKAlIABILCgF5GAIgASgJSABCBgoEbm9uZSpV", "aE5vbmVOYW1lEgsKAXgYASABKAlIABILCgF5GAIgASgJSABCBgoEbm9uZSKT",
"CgxOZWdhdGl2ZUVudW0SFgoSTkVHQVRJVkVfRU5VTV9aRVJPEAASFgoJRml2", "AgoZRGlzYW1iaWd1YXRlQ29tbW9uTWVtYmVycxIjChtkaXNhbWJpZ3VhdGVf",
"ZUJlbG93EPv//////////wESFQoITWludXNPbmUQ////////////ASouCg5E", "Y29tbW9uX21lbWJlcnMYASABKAUSDQoFdHlwZXMYAiABKAUSEgoKZGVzY3Jp",
"ZXByZWNhdGVkRW51bRITCg9ERVBSRUNBVEVEX1pFUk8QABIHCgNvbmUQAUId", "cHRvchgDIAEoBRIOCgZlcXVhbHMYBCABKAUSEQoJdG9fc3RyaW5nGAUgASgF",
"qgIaVW5pdFRlc3QuSXNzdWVzLlRlc3RQcm90b3NiBnByb3RvMw==")); "EhUKDWdldF9oYXNoX2NvZGUYBiABKAUSEAoId3JpdGVfdG8YByABKAUSDQoF",
"Y2xvbmUYCCABKAUSFgoOY2FsY3VsYXRlX3NpemUYCSABKAUSEgoKbWVyZ2Vf",
"ZnJvbRgKIAEoBRIXCg9vbl9jb25zdHJ1Y3Rpb24YCyABKAUSDgoGcGFyc2Vy",
"GAwgASgFKlUKDE5lZ2F0aXZlRW51bRIWChJORUdBVElWRV9FTlVNX1pFUk8Q",
"ABIWCglGaXZlQmVsb3cQ+///////////ARIVCghNaW51c09uZRD/////////",
"//8BKi4KDkRlcHJlY2F0ZWRFbnVtEhMKD0RFUFJFQ0FURURfWkVSTxAAEgcK",
"A29uZRABQh2qAhpVbml0VGVzdC5Jc3N1ZXMuVGVzdFByb3Rvc2IGcHJvdG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData, descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, }, new pbr::FileDescriptor[] { global::Google.Protobuf.WellKnownTypes.StructReflection.Descriptor, },
new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, null, new pbr::GeneratedClrTypeInfo[] { new pbr::GeneratedClrTypeInfo(new[] {typeof(global::UnitTest.Issues.TestProtos.NegativeEnum), typeof(global::UnitTest.Issues.TestProtos.DeprecatedEnum), }, null, new pbr::GeneratedClrTypeInfo[] {
@ -77,7 +83,8 @@ namespace UnitTest.Issues.TestProtos {
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NullValueNotInOneof), global::UnitTest.Issues.TestProtos.NullValueNotInOneof.Parser, new[]{ "NullValue" }, null, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.NullValueNotInOneof), global::UnitTest.Issues.TestProtos.NullValueNotInOneof.Parser, new[]{ "NullValue" }, null, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.MixedRegularAndOptional), global::UnitTest.Issues.TestProtos.MixedRegularAndOptional.Parser, new[]{ "RegularField", "OptionalField" }, new[]{ "OptionalField" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.MixedRegularAndOptional), global::UnitTest.Issues.TestProtos.MixedRegularAndOptional.Parser, new[]{ "RegularField", "OptionalField" }, new[]{ "OptionalField" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneField), global::UnitTest.Issues.TestProtos.OneofWithNoneField.Parser, new[]{ "X", "None" }, new[]{ "Test" }, null, null, null), new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneField), global::UnitTest.Issues.TestProtos.OneofWithNoneField.Parser, new[]{ "X", "None" }, new[]{ "Test" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneName), global::UnitTest.Issues.TestProtos.OneofWithNoneName.Parser, new[]{ "X", "Y" }, new[]{ "None" }, null, null, null) new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.OneofWithNoneName), global::UnitTest.Issues.TestProtos.OneofWithNoneName.Parser, new[]{ "X", "Y" }, new[]{ "None" }, null, null, null),
new pbr::GeneratedClrTypeInfo(typeof(global::UnitTest.Issues.TestProtos.DisambiguateCommonMembers), global::UnitTest.Issues.TestProtos.DisambiguateCommonMembers.Parser, new[]{ "DisambiguateCommonMembers_", "Types_", "Descriptor_", "Equals_", "ToString_", "GetHashCode_", "WriteTo_", "Clone_", "CalculateSize_", "MergeFrom_", "OnConstruction_", "Parser_" }, null, null, null, null)
})); }));
} }
#endregion #endregion
@ -4347,6 +4354,605 @@ namespace UnitTest.Issues.TestProtos {
} }
/// <summary>
/// Issue 8810
/// </summary>
public sealed partial class DisambiguateCommonMembers : pb::IMessage<DisambiguateCommonMembers>
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
, pb::IBufferMessage
#endif
{
private static readonly pb::MessageParser<DisambiguateCommonMembers> _parser = new pb::MessageParser<DisambiguateCommonMembers>(() => new DisambiguateCommonMembers());
private pb::UnknownFieldSet _unknownFields;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public static pb::MessageParser<DisambiguateCommonMembers> Parser { get { return _parser; } }
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public static pbr::MessageDescriptor Descriptor {
get { return global::UnitTest.Issues.TestProtos.UnittestIssuesReflection.Descriptor.MessageTypes[14]; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
pbr::MessageDescriptor pb::IMessage.Descriptor {
get { return Descriptor; }
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public DisambiguateCommonMembers() {
OnConstruction();
}
partial void OnConstruction();
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public DisambiguateCommonMembers(DisambiguateCommonMembers other) : this() {
disambiguateCommonMembers_ = other.disambiguateCommonMembers_;
types_ = other.types_;
descriptor_ = other.descriptor_;
equals_ = other.equals_;
toString_ = other.toString_;
getHashCode_ = other.getHashCode_;
writeTo_ = other.writeTo_;
clone_ = other.clone_;
calculateSize_ = other.calculateSize_;
mergeFrom_ = other.mergeFrom_;
onConstruction_ = other.onConstruction_;
parser_ = other.parser_;
_unknownFields = pb::UnknownFieldSet.Clone(other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public DisambiguateCommonMembers Clone() {
return new DisambiguateCommonMembers(this);
}
/// <summary>Field number for the "disambiguate_common_members" field.</summary>
public const int DisambiguateCommonMembers_FieldNumber = 1;
private int disambiguateCommonMembers_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int DisambiguateCommonMembers_ {
get { return disambiguateCommonMembers_; }
set {
disambiguateCommonMembers_ = value;
}
}
/// <summary>Field number for the "types" field.</summary>
public const int Types_FieldNumber = 2;
private int types_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int Types_ {
get { return types_; }
set {
types_ = value;
}
}
/// <summary>Field number for the "descriptor" field.</summary>
public const int Descriptor_FieldNumber = 3;
private int descriptor_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int Descriptor_ {
get { return descriptor_; }
set {
descriptor_ = value;
}
}
/// <summary>Field number for the "equals" field.</summary>
public const int Equals_FieldNumber = 4;
private int equals_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int Equals_ {
get { return equals_; }
set {
equals_ = value;
}
}
/// <summary>Field number for the "to_string" field.</summary>
public const int ToString_FieldNumber = 5;
private int toString_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int ToString_ {
get { return toString_; }
set {
toString_ = value;
}
}
/// <summary>Field number for the "get_hash_code" field.</summary>
public const int GetHashCode_FieldNumber = 6;
private int getHashCode_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int GetHashCode_ {
get { return getHashCode_; }
set {
getHashCode_ = value;
}
}
/// <summary>Field number for the "write_to" field.</summary>
public const int WriteTo_FieldNumber = 7;
private int writeTo_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int WriteTo_ {
get { return writeTo_; }
set {
writeTo_ = value;
}
}
/// <summary>Field number for the "clone" field.</summary>
public const int Clone_FieldNumber = 8;
private int clone_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int Clone_ {
get { return clone_; }
set {
clone_ = value;
}
}
/// <summary>Field number for the "calculate_size" field.</summary>
public const int CalculateSize_FieldNumber = 9;
private int calculateSize_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int CalculateSize_ {
get { return calculateSize_; }
set {
calculateSize_ = value;
}
}
/// <summary>Field number for the "merge_from" field.</summary>
public const int MergeFrom_FieldNumber = 10;
private int mergeFrom_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int MergeFrom_ {
get { return mergeFrom_; }
set {
mergeFrom_ = value;
}
}
/// <summary>Field number for the "on_construction" field.</summary>
public const int OnConstruction_FieldNumber = 11;
private int onConstruction_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int OnConstruction_ {
get { return onConstruction_; }
set {
onConstruction_ = value;
}
}
/// <summary>Field number for the "parser" field.</summary>
public const int Parser_FieldNumber = 12;
private int parser_;
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int Parser_ {
get { return parser_; }
set {
parser_ = value;
}
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public override bool Equals(object other) {
return Equals(other as DisambiguateCommonMembers);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public bool Equals(DisambiguateCommonMembers other) {
if (ReferenceEquals(other, null)) {
return false;
}
if (ReferenceEquals(other, this)) {
return true;
}
if (DisambiguateCommonMembers_ != other.DisambiguateCommonMembers_) return false;
if (Types_ != other.Types_) return false;
if (Descriptor_ != other.Descriptor_) return false;
if (Equals_ != other.Equals_) return false;
if (ToString_ != other.ToString_) return false;
if (GetHashCode_ != other.GetHashCode_) return false;
if (WriteTo_ != other.WriteTo_) return false;
if (Clone_ != other.Clone_) return false;
if (CalculateSize_ != other.CalculateSize_) return false;
if (MergeFrom_ != other.MergeFrom_) return false;
if (OnConstruction_ != other.OnConstruction_) return false;
if (Parser_ != other.Parser_) return false;
return Equals(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public override int GetHashCode() {
int hash = 1;
if (DisambiguateCommonMembers_ != 0) hash ^= DisambiguateCommonMembers_.GetHashCode();
if (Types_ != 0) hash ^= Types_.GetHashCode();
if (Descriptor_ != 0) hash ^= Descriptor_.GetHashCode();
if (Equals_ != 0) hash ^= Equals_.GetHashCode();
if (ToString_ != 0) hash ^= ToString_.GetHashCode();
if (GetHashCode_ != 0) hash ^= GetHashCode_.GetHashCode();
if (WriteTo_ != 0) hash ^= WriteTo_.GetHashCode();
if (Clone_ != 0) hash ^= Clone_.GetHashCode();
if (CalculateSize_ != 0) hash ^= CalculateSize_.GetHashCode();
if (MergeFrom_ != 0) hash ^= MergeFrom_.GetHashCode();
if (OnConstruction_ != 0) hash ^= OnConstruction_.GetHashCode();
if (Parser_ != 0) hash ^= Parser_.GetHashCode();
if (_unknownFields != null) {
hash ^= _unknownFields.GetHashCode();
}
return hash;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public override string ToString() {
return pb::JsonFormatter.ToDiagnosticString(this);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public void WriteTo(pb::CodedOutputStream output) {
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
output.WriteRawMessage(this);
#else
if (DisambiguateCommonMembers_ != 0) {
output.WriteRawTag(8);
output.WriteInt32(DisambiguateCommonMembers_);
}
if (Types_ != 0) {
output.WriteRawTag(16);
output.WriteInt32(Types_);
}
if (Descriptor_ != 0) {
output.WriteRawTag(24);
output.WriteInt32(Descriptor_);
}
if (Equals_ != 0) {
output.WriteRawTag(32);
output.WriteInt32(Equals_);
}
if (ToString_ != 0) {
output.WriteRawTag(40);
output.WriteInt32(ToString_);
}
if (GetHashCode_ != 0) {
output.WriteRawTag(48);
output.WriteInt32(GetHashCode_);
}
if (WriteTo_ != 0) {
output.WriteRawTag(56);
output.WriteInt32(WriteTo_);
}
if (Clone_ != 0) {
output.WriteRawTag(64);
output.WriteInt32(Clone_);
}
if (CalculateSize_ != 0) {
output.WriteRawTag(72);
output.WriteInt32(CalculateSize_);
}
if (MergeFrom_ != 0) {
output.WriteRawTag(80);
output.WriteInt32(MergeFrom_);
}
if (OnConstruction_ != 0) {
output.WriteRawTag(88);
output.WriteInt32(OnConstruction_);
}
if (Parser_ != 0) {
output.WriteRawTag(96);
output.WriteInt32(Parser_);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(output);
}
#endif
}
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
void pb::IBufferMessage.InternalWriteTo(ref pb::WriteContext output) {
if (DisambiguateCommonMembers_ != 0) {
output.WriteRawTag(8);
output.WriteInt32(DisambiguateCommonMembers_);
}
if (Types_ != 0) {
output.WriteRawTag(16);
output.WriteInt32(Types_);
}
if (Descriptor_ != 0) {
output.WriteRawTag(24);
output.WriteInt32(Descriptor_);
}
if (Equals_ != 0) {
output.WriteRawTag(32);
output.WriteInt32(Equals_);
}
if (ToString_ != 0) {
output.WriteRawTag(40);
output.WriteInt32(ToString_);
}
if (GetHashCode_ != 0) {
output.WriteRawTag(48);
output.WriteInt32(GetHashCode_);
}
if (WriteTo_ != 0) {
output.WriteRawTag(56);
output.WriteInt32(WriteTo_);
}
if (Clone_ != 0) {
output.WriteRawTag(64);
output.WriteInt32(Clone_);
}
if (CalculateSize_ != 0) {
output.WriteRawTag(72);
output.WriteInt32(CalculateSize_);
}
if (MergeFrom_ != 0) {
output.WriteRawTag(80);
output.WriteInt32(MergeFrom_);
}
if (OnConstruction_ != 0) {
output.WriteRawTag(88);
output.WriteInt32(OnConstruction_);
}
if (Parser_ != 0) {
output.WriteRawTag(96);
output.WriteInt32(Parser_);
}
if (_unknownFields != null) {
_unknownFields.WriteTo(ref output);
}
}
#endif
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public int CalculateSize() {
int size = 0;
if (DisambiguateCommonMembers_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(DisambiguateCommonMembers_);
}
if (Types_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(Types_);
}
if (Descriptor_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(Descriptor_);
}
if (Equals_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(Equals_);
}
if (ToString_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(ToString_);
}
if (GetHashCode_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(GetHashCode_);
}
if (WriteTo_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(WriteTo_);
}
if (Clone_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(Clone_);
}
if (CalculateSize_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(CalculateSize_);
}
if (MergeFrom_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(MergeFrom_);
}
if (OnConstruction_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(OnConstruction_);
}
if (Parser_ != 0) {
size += 1 + pb::CodedOutputStream.ComputeInt32Size(Parser_);
}
if (_unknownFields != null) {
size += _unknownFields.CalculateSize();
}
return size;
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public void MergeFrom(DisambiguateCommonMembers other) {
if (other == null) {
return;
}
if (other.DisambiguateCommonMembers_ != 0) {
DisambiguateCommonMembers_ = other.DisambiguateCommonMembers_;
}
if (other.Types_ != 0) {
Types_ = other.Types_;
}
if (other.Descriptor_ != 0) {
Descriptor_ = other.Descriptor_;
}
if (other.Equals_ != 0) {
Equals_ = other.Equals_;
}
if (other.ToString_ != 0) {
ToString_ = other.ToString_;
}
if (other.GetHashCode_ != 0) {
GetHashCode_ = other.GetHashCode_;
}
if (other.WriteTo_ != 0) {
WriteTo_ = other.WriteTo_;
}
if (other.Clone_ != 0) {
Clone_ = other.Clone_;
}
if (other.CalculateSize_ != 0) {
CalculateSize_ = other.CalculateSize_;
}
if (other.MergeFrom_ != 0) {
MergeFrom_ = other.MergeFrom_;
}
if (other.OnConstruction_ != 0) {
OnConstruction_ = other.OnConstruction_;
}
if (other.Parser_ != 0) {
Parser_ = other.Parser_;
}
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
public void MergeFrom(pb::CodedInputStream input) {
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
input.ReadRawMessage(this);
#else
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
break;
case 8: {
DisambiguateCommonMembers_ = input.ReadInt32();
break;
}
case 16: {
Types_ = input.ReadInt32();
break;
}
case 24: {
Descriptor_ = input.ReadInt32();
break;
}
case 32: {
Equals_ = input.ReadInt32();
break;
}
case 40: {
ToString_ = input.ReadInt32();
break;
}
case 48: {
GetHashCode_ = input.ReadInt32();
break;
}
case 56: {
WriteTo_ = input.ReadInt32();
break;
}
case 64: {
Clone_ = input.ReadInt32();
break;
}
case 72: {
CalculateSize_ = input.ReadInt32();
break;
}
case 80: {
MergeFrom_ = input.ReadInt32();
break;
}
case 88: {
OnConstruction_ = input.ReadInt32();
break;
}
case 96: {
Parser_ = input.ReadInt32();
break;
}
}
}
#endif
}
#if !GOOGLE_PROTOBUF_REFSTRUCT_COMPATIBILITY_MODE
[global::System.Diagnostics.DebuggerNonUserCodeAttribute]
[global::System.CodeDom.Compiler.GeneratedCode("protoc", null)]
void pb::IBufferMessage.InternalMergeFrom(ref pb::ParseContext input) {
uint tag;
while ((tag = input.ReadTag()) != 0) {
switch(tag) {
default:
_unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, ref input);
break;
case 8: {
DisambiguateCommonMembers_ = input.ReadInt32();
break;
}
case 16: {
Types_ = input.ReadInt32();
break;
}
case 24: {
Descriptor_ = input.ReadInt32();
break;
}
case 32: {
Equals_ = input.ReadInt32();
break;
}
case 40: {
ToString_ = input.ReadInt32();
break;
}
case 48: {
GetHashCode_ = input.ReadInt32();
break;
}
case 56: {
WriteTo_ = input.ReadInt32();
break;
}
case 64: {
Clone_ = input.ReadInt32();
break;
}
case 72: {
CalculateSize_ = input.ReadInt32();
break;
}
case 80: {
MergeFrom_ = input.ReadInt32();
break;
}
case 88: {
OnConstruction_ = input.ReadInt32();
break;
}
case 96: {
Parser_ = input.ReadInt32();
break;
}
}
}
}
#endif
}
#endregion #endregion
} }

@ -0,0 +1,63 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2022 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 System;
using System.Linq;
namespace Google.Protobuf.Test;
internal class ParsingPrimitivesTest
{
// Note: test cases use integers rather than bytes as they're easier
// to specify in attributes.
[Test]
[TestCase("\ufffd", 255)]
[TestCase("A\ufffd", 65, 255)]
[TestCase("A\ufffd\ufffdB", 65, 255, 255, 66)]
// Overlong form of "space"
[TestCase("\ufffd\ufffd", 0xc0, 0xa0)]
public void ReadRawString_NonUtf8(string expectedText, params int[] bytes)
{
var context = CreateContext(bytes);
string text = ParsingPrimitives.ReadRawString(ref context.buffer, ref context.state, bytes.Length);
Assert.AreEqual(expectedText, text);
}
private static ParseContext CreateContext(int[] bytes)
{
byte[] actualBytes = bytes.Select(b => (byte) b).ToArray();
ParseContext.Initialize(actualBytes.AsSpan(), out var context);
return context;
}
}

@ -0,0 +1,61 @@
#region Copyright notice and license
// Protocol Buffers - Google's data interchange format
// Copyright 2022 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 System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Google.Protobuf.Test;
internal class WritingPrimitivesTest
{
[Test]
public void WriteRawString_IllFormedUnicodeString()
{
// See https://codeblog.jonskeet.uk/2014/11/07/when-is-a-string-not-a-string/
char c1 = '\u0058';
char c2 = '\ud800';
char c3 = '\u0059';
string text = new string(new[] { c1, c2, c3 });
Span<byte> buffer = new byte[10];
WriteContext.Initialize(ref buffer, out var context);
WritingPrimitives.WriteString(ref context.buffer, ref context.state, text);
// The high surrogate is written out in a "raw" form, surrounded by the ASCII
// characters.
byte[] expectedBytes = { 0x5, 0x58, 0xef, 0xbf, 0xbd, 0x59 };
Assert.AreEqual(expectedBytes, buffer.Slice(0, context.state.position).ToArray());
}
}

@ -4,7 +4,7 @@
<Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description> <Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
<Copyright>Copyright 2015, Google Inc.</Copyright> <Copyright>Copyright 2015, Google Inc.</Copyright>
<AssemblyTitle>Google Protocol Buffers</AssemblyTitle> <AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
<VersionPrefix>3.21.2</VersionPrefix> <VersionPrefix>3.21.3</VersionPrefix>
<LangVersion>10.0</LangVersion> <LangVersion>10.0</LangVersion>
<Authors>Google Inc.</Authors> <Authors>Google Inc.</Authors>
<TargetFrameworks>netstandard1.1;netstandard2.0;net45;net50</TargetFrameworks> <TargetFrameworks>netstandard1.1;netstandard2.0;net45;net50</TargetFrameworks>

@ -23,7 +23,7 @@ If you are using Maven, use the following:
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</dependency> </dependency>
``` ```
@ -37,7 +37,7 @@ protobuf-java-util package:
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId> <artifactId>protobuf-java-util</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</dependency> </dependency>
``` ```
@ -45,7 +45,7 @@ protobuf-java-util package:
If you are using Gradle, add the following to your `build.gradle` file's dependencies: If you are using Gradle, add the following to your `build.gradle` file's dependencies:
``` ```
implementation 'com.google.protobuf:protobuf-java:3.21.2' implementation 'com.google.protobuf:protobuf-java:3.21.3'
``` ```
Again, be sure to check that the version number matches (or is newer than) the version number of protoc that you are using. Again, be sure to check that the version number matches (or is newer than) the version number of protoc that you are using.

@ -4,7 +4,7 @@
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-bom</artifactId> <artifactId>protobuf-bom</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Protocol Buffers [BOM]</name> <name>Protocol Buffers [BOM]</name>

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId> <artifactId>protobuf-parent</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</parent> </parent>
<artifactId>protobuf-java</artifactId> <artifactId>protobuf-java</artifactId>

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId> <artifactId>protobuf-parent</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</parent> </parent>
<artifactId>protobuf-kotlin-lite</artifactId> <artifactId>protobuf-kotlin-lite</artifactId>

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId> <artifactId>protobuf-parent</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</parent> </parent>
<artifactId>protobuf-kotlin</artifactId> <artifactId>protobuf-kotlin</artifactId>

@ -66,6 +66,11 @@ class Proto3Test {
assertThat(optionalNestedMessage).isEqualTo(TestAllTypesKt.nestedMessage { bb = 118 }) assertThat(optionalNestedMessage).isEqualTo(TestAllTypesKt.nestedMessage { bb = 118 })
optionalNestedEnum = NestedEnum.BAZ optionalNestedEnum = NestedEnum.BAZ
assertThat(optionalNestedEnum).isEqualTo(NestedEnum.BAZ) assertThat(optionalNestedEnum).isEqualTo(NestedEnum.BAZ)
assertThat(optionalNestedEnumValue).isEqualTo(3)
optionalNestedEnumValue = 1
assertThat(optionalNestedEnumValue).isEqualTo(1)
assertThat(optionalNestedEnum).isEqualTo(NestedEnum.FOO)
oneofUint32 = 601 oneofUint32 = 601
assertThat(oneofUint32).isEqualTo(601) assertThat(oneofUint32).isEqualTo(601)
} }

@ -29,7 +29,7 @@ protobuf Java Lite runtime. If you are using Maven, include the following:
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-javalite</artifactId> <artifactId>protobuf-javalite</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</dependency> </dependency>
``` ```

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId> <artifactId>protobuf-parent</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</parent> </parent>
<artifactId>protobuf-javalite</artifactId> <artifactId>protobuf-javalite</artifactId>

@ -4,7 +4,7 @@
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId> <artifactId>protobuf-parent</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Protocol Buffers [Parent]</name> <name>Protocol Buffers [Parent]</name>

@ -4,11 +4,11 @@
<parent> <parent>
<groupId>com.google</groupId> <groupId>com.google</groupId>
<artifactId>google</artifactId> <artifactId>google</artifactId>
<version>1</version> <version>5</version>
</parent> </parent>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protoc</artifactId> <artifactId>protoc</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Protobuf Compiler</name> <name>Protobuf Compiler</name>
<description> <description>

@ -4,7 +4,7 @@
<parent> <parent>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId> <artifactId>protobuf-parent</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</parent> </parent>
<artifactId>protobuf-java-util</artifactId> <artifactId>protobuf-java-util</artifactId>

@ -1,12 +0,0 @@
#!/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
# TODO(mkruskal) Re-enable this once we can get a working PHP 7.0 installed.
#./tests.sh php7.0_mac

@ -1,12 +0,0 @@
#!/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
# TODO(mkruskal) Re-enable this once we can get a working PHP 7.0 installed.
#./tests.sh php7.3_mac

@ -0,0 +1,20 @@
#!/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
# Install Dependencies
brew install coreutils php@7.4
# Configure path
PHP_FOLDER=$(find $HOMEBREW_PREFIX -type d -regex ".*php.*/7.4.[0-9]*")
test ! -z "$PHP_FOLDER"
export PATH="$PHP_FOLDER/bin:$PATH"
# Test
./tests.sh php_mac

@ -1,5 +1,5 @@
# Config file for running tests in Kokoro # Config file for running tests in Kokoro
# Location of the build script in repository # Location of the build script in repository
build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh" build_file: "protobuf/kokoro/macos/php74/build.sh"
timeout_mins: 1440 timeout_mins: 1440

@ -1,5 +1,5 @@
# Config file for running tests in Kokoro # Config file for running tests in Kokoro
# Location of the build script in repository # Location of the build script in repository
build_file: "protobuf/kokoro/macos/php7.0_mac/build.sh" build_file: "protobuf/kokoro/macos/php74/build.sh"
timeout_mins: 1440 timeout_mins: 1440

@ -0,0 +1,20 @@
#!/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
# Install Dependencies
brew install coreutils php@8.0
# Configure path
PHP_FOLDER=$(find $HOMEBREW_PREFIX -type d -regex ".*php.*/8.0.[0-9]*")
test ! -z "$PHP_FOLDER"
export PATH="$PHP_FOLDER/bin:$PATH"
# Test
./tests.sh php_mac

@ -1,5 +1,5 @@
# Config file for running tests in Kokoro # Config file for running tests in Kokoro
# Location of the build script in repository # Location of the build script in repository
build_file: "protobuf/kokoro/macos/php7.0_mac/build.sh" build_file: "protobuf/kokoro/macos/php80/build.sh"
timeout_mins: 1440 timeout_mins: 1440

@ -1,5 +1,5 @@
# Config file for running tests in Kokoro # Config file for running tests in Kokoro
# Location of the build script in repository # Location of the build script in repository
build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh" build_file: "protobuf/kokoro/macos/php80/build.sh"
timeout_mins: 1440 timeout_mins: 1440

@ -4,20 +4,16 @@
set -eux set -eux
export HOMEBREW_PREFIX=$(brew --prefix)
## ##
# Select Xcode version # Select Xcode version
export DEVELOPER_DIR=/Applications/Xcode_13.3.1.app/Contents/Developer
# Remember to update the Xcode version when Xcode_11.3.app is not available. sudo xcode-select -s "${DEVELOPER_DIR}"
# If xcode is not available, it will probably encounter the failure for
# "autom4te: need GNU m4 1.4 or later: /usr/bin/m4"
# go/kokoro/userdocs/macos/selecting_xcode.md for more information.
export DEVELOPER_DIR=/Applications/Xcode_11.3.app/Contents/Developer
## ##
# Select C/C++ compilers # Use Python 2 by default (for googletest)
pyenv global 2.7.18
export CC=gcc
export CXX=g++
## ##
# Install Tox # Install Tox
@ -27,14 +23,18 @@ if [[ "${KOKORO_INSTALL_TOX:-}" == "yes" ]] ; then
fi fi
## ##
# Install RVM # Setup RVM
if [[ "${KOKORO_INSTALL_RVM:-}" == "yes" ]] ; then if [[ "${KOKORO_INSTALL_RVM:-}" == "yes" ]] ; then
curl -sSL https://rvm.io/mpapis.asc | gpg --import - git config --global --add safe.directory $HOMEBREW_PREFIX/Library/Taps/homebrew/homebrew-cask
curl -sSL https://rvm.io/pkuczynski.asc | gpg --import - git config --global --add safe.directory $HOMEBREW_PREFIX/Library/Taps/homebrew/homebrew-core
git config --global --add safe.directory $HOMEBREW_PREFIX/Library/Taps/homebrew/homebrew-services
sudo chown -R $(whoami) $HOME/.rvm/
fi
# Old OpenSSL versions cannot handle the SSL certificate used by # "Install" valgrind if it doesn't exist
# https://get.rvm.io, so as a workaround we download RVM directly from ##
# GitHub. See this issue for details: https://github.com/rvm/rvm/issues/5133 if [ ! -x "$(command -v valgrind)" ]; then
curl -sSL https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer | bash -s master --ruby echo "#! /bin/bash" > valgrind
chmod ug+x valgrind
sudo mv valgrind /usr/local/bin/valgrind
fi fi

@ -12,8 +12,5 @@ export ARTIFACT_DIR=$(pwd)/artifacts
# ruby environment # ruby environment
bash kokoro/release/ruby/macos/ruby/ruby_build_environment.sh bash kokoro/release/ruby/macos/ruby/ruby_build_environment.sh
gem install rubygems-update
update_rubygems
# build artifacts # build artifacts
bash kokoro/release/ruby/macos/ruby/ruby_build.sh bash kokoro/release/ruby/macos/ruby/ruby_build.sh

@ -3,7 +3,6 @@
set -ex set -ex
# Build protoc # Build protoc
use_bazel.sh 5.1.1
bazel build //:protoc bazel build //:protoc
export PROTOC=$PWD/bazel-bin/protoc export PROTOC=$PWD/bazel-bin/protoc

@ -4,13 +4,9 @@ set -ex
set +ex # rvm script is very verbose and exits with errorcode set +ex # rvm script is very verbose and exits with errorcode
curl -sSL https://rvm.io/mpapis.asc | gpg --import - # Fix permissions
curl -sSL https://rvm.io/pkuczynski.asc | gpg --import - sudo chown -R $(whoami) $HOME/.rvm/
sudo chown -R $(whoami) /Library/Ruby/
# Old OpenSSL versions cannot handle the SSL certificate used by
# https://get.rvm.io, so as a workaround we download RVM directly from
# GitHub. See this issue for details: https://github.com/rvm/rvm/issues/5133
curl -sSL https://raw.githubusercontent.com/rvm/rvm/master/binscripts/rvm-installer | bash -s master --ruby
source $HOME/.rvm/scripts/rvm source $HOME/.rvm/scripts/rvm
set -e # rvm commands are very verbose set -e # rvm commands are very verbose

@ -74,12 +74,12 @@ GPB_FINAL @interface GPBApi : GPBMessage
/** The methods of this interface, in unspecified order. */ /** The methods of this interface, in unspecified order. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMethod*> *methodsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMethod*> *methodsArray;
/** The number of items in @c methodsArray without causing the array to be created. */ /** The number of items in @c methodsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger methodsArray_Count; @property(nonatomic, readonly) NSUInteger methodsArray_Count;
/** Any metadata attached to the interface. */ /** Any metadata attached to the interface. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
/** The number of items in @c optionsArray without causing the array to be created. */ /** The number of items in @c optionsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger optionsArray_Count; @property(nonatomic, readonly) NSUInteger optionsArray_Count;
/** /**
@ -115,7 +115,7 @@ GPB_FINAL @interface GPBApi : GPBMessage
/** Included interfaces. See [Mixin][]. */ /** Included interfaces. See [Mixin][]. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMixin*> *mixinsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBMixin*> *mixinsArray;
/** The number of items in @c mixinsArray without causing the array to be created. */ /** The number of items in @c mixinsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger mixinsArray_Count; @property(nonatomic, readonly) NSUInteger mixinsArray_Count;
/** The source syntax of the service. */ /** The source syntax of the service. */
@ -169,7 +169,7 @@ GPB_FINAL @interface GPBMethod : GPBMessage
/** Any metadata attached to the method. */ /** Any metadata attached to the method. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
/** The number of items in @c optionsArray without causing the array to be created. */ /** The number of items in @c optionsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger optionsArray_Count; @property(nonatomic, readonly) NSUInteger optionsArray_Count;
/** The source syntax of this method. */ /** The source syntax of this method. */

@ -247,7 +247,7 @@ GPB_FINAL @interface GPBFieldMask : GPBMessage
/** The set of field mask paths. */ /** The set of field mask paths. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *pathsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *pathsArray;
/** The number of items in @c pathsArray without causing the array to be created. */ /** The number of items in @c pathsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger pathsArray_Count; @property(nonatomic, readonly) NSUInteger pathsArray_Count;
@end @end

@ -87,7 +87,7 @@ GPB_FINAL @interface GPBStruct : GPBMessage
/** Unordered map of dynamically typed values. */ /** Unordered map of dynamically typed values. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableDictionary<NSString*, GPBValue*> *fields; @property(nonatomic, readwrite, strong, null_resettable) NSMutableDictionary<NSString*, GPBValue*> *fields;
/** The number of items in @c fields without causing the array to be created. */ /** The number of items in @c fields without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger fields_Count; @property(nonatomic, readonly) NSUInteger fields_Count;
@end @end
@ -178,7 +178,7 @@ GPB_FINAL @interface GPBListValue : GPBMessage
/** Repeated field of dynamically typed values. */ /** Repeated field of dynamically typed values. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBValue*> *valuesArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBValue*> *valuesArray;
/** The number of items in @c valuesArray without causing the array to be created. */ /** The number of items in @c valuesArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger valuesArray_Count; @property(nonatomic, readonly) NSUInteger valuesArray_Count;
@end @end

@ -195,17 +195,17 @@ GPB_FINAL @interface GPBType : GPBMessage
/** The list of fields. */ /** The list of fields. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBField*> *fieldsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBField*> *fieldsArray;
/** The number of items in @c fieldsArray without causing the array to be created. */ /** The number of items in @c fieldsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger fieldsArray_Count; @property(nonatomic, readonly) NSUInteger fieldsArray_Count;
/** The list of types appearing in `oneof` definitions in this type. */ /** The list of types appearing in `oneof` definitions in this type. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *oneofsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<NSString*> *oneofsArray;
/** The number of items in @c oneofsArray without causing the array to be created. */ /** The number of items in @c oneofsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger oneofsArray_Count; @property(nonatomic, readonly) NSUInteger oneofsArray_Count;
/** The protocol buffer options. */ /** The protocol buffer options. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
/** The number of items in @c optionsArray without causing the array to be created. */ /** The number of items in @c optionsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger optionsArray_Count; @property(nonatomic, readonly) NSUInteger optionsArray_Count;
/** The source context. */ /** The source context. */
@ -279,7 +279,7 @@ GPB_FINAL @interface GPBField : GPBMessage
/** The protocol buffer options. */ /** The protocol buffer options. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
/** The number of items in @c optionsArray without causing the array to be created. */ /** The number of items in @c optionsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger optionsArray_Count; @property(nonatomic, readonly) NSUInteger optionsArray_Count;
/** The field JSON name. */ /** The field JSON name. */
@ -334,12 +334,12 @@ GPB_FINAL @interface GPBEnum : GPBMessage
/** Enum value definitions. */ /** Enum value definitions. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBEnumValue*> *enumvalueArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBEnumValue*> *enumvalueArray;
/** The number of items in @c enumvalueArray without causing the array to be created. */ /** The number of items in @c enumvalueArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger enumvalueArray_Count; @property(nonatomic, readonly) NSUInteger enumvalueArray_Count;
/** Protocol buffer options. */ /** Protocol buffer options. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
/** The number of items in @c optionsArray without causing the array to be created. */ /** The number of items in @c optionsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger optionsArray_Count; @property(nonatomic, readonly) NSUInteger optionsArray_Count;
/** The source context. */ /** The source context. */
@ -385,7 +385,7 @@ GPB_FINAL @interface GPBEnumValue : GPBMessage
/** Protocol buffer options. */ /** Protocol buffer options. */
@property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray; @property(nonatomic, readwrite, strong, null_resettable) NSMutableArray<GPBOption*> *optionsArray;
/** The number of items in @c optionsArray without causing the array to be created. */ /** The number of items in @c optionsArray without causing the container to be created. */
@property(nonatomic, readonly) NSUInteger optionsArray_Count; @property(nonatomic, readonly) NSUInteger optionsArray_Count;
@end @end

@ -2248,7 +2248,7 @@ void GPBMaybeClearOneof(GPBMessage *self, GPBOneofDescriptor *oneof,
NSCAssert([[self descriptor] oneofWithName:oneof.name] == oneof, NSCAssert([[self descriptor] oneofWithName:oneof.name] == oneof,
@"OneofDescriptor %@ doesn't appear to be for %@ messages.", @"OneofDescriptor %@ doesn't appear to be for %@ messages.",
oneof.name, [self class]); oneof.name, [self class]);
GPBFieldDescriptor *firstField = oneof->fields_[0]; GPBFieldDescriptor *firstField __unused = oneof->fields_[0];
NSCAssert(firstField->description_->hasIndex == oneofHasIndex, NSCAssert(firstField->description_->hasIndex == oneofHasIndex,
@"Internal error, oneofHasIndex (%d) doesn't match (%d).", @"Internal error, oneofHasIndex (%d) doesn't match (%d).",
firstField->description_->hasIndex, oneofHasIndex); firstField->description_->hasIndex, oneofHasIndex);

@ -73,6 +73,7 @@ for PROTO_FILE in "${RUNTIME_PROTO_FILES[@]}"; do
if ! diff "${ObjCDir}/GPB${OBJC_NAME}${EXT}" "${TMP_DIR}/${DIR}/${OBJC_NAME}${EXT}" > /dev/null 2>&1 ; then if ! diff "${ObjCDir}/GPB${OBJC_NAME}${EXT}" "${TMP_DIR}/${DIR}/${OBJC_NAME}${EXT}" > /dev/null 2>&1 ; then
if [[ "${CHECK_ONLY}" == 1 ]] ; then if [[ "${CHECK_ONLY}" == 1 ]] ; then
echo "ERROR: The WKTs need to be regenerated! Run $0" echo "ERROR: The WKTs need to be regenerated! Run $0"
diff -u "${ObjCDir}/GPB${OBJC_NAME}${EXT}" "${TMP_DIR}/${DIR}/${OBJC_NAME}${EXT}"
exit 1 exit 1
fi fi

@ -12,7 +12,9 @@ pkg_files(
"src/Google/Protobuf/**/*.php", "src/Google/Protobuf/**/*.php",
"tests/*.php", "tests/*.php",
"tests/*.sh", "tests/*.sh",
"tests/generated_previous/**/*.php",
"tests/proto/**/*.proto", "tests/proto/**/*.proto",
"tests/proto_previous/*.proto",
]) + [ ]) + [
"BUILD.bazel", "BUILD.bazel",
"README.md", "README.md",

@ -20,7 +20,10 @@
"autoload-dev": { "autoload-dev": {
"psr-4": { "psr-4": {
"": "tmp" "": "tmp"
} },
"classmap": [
"tests/generated_previous"
]
}, },
"scripts": { "scripts": {
"test_c": "./generate_test_protos.sh && ./tests/compile_extension.sh && php -dextension=ext/google/protobuf/modules/protobuf.so vendor/bin/phpunit --bootstrap tests/force_c_ext.php tests", "test_c": "./generate_test_protos.sh && ./tests/compile_extension.sh && php -dextension=ext/google/protobuf/modules/protobuf.so vendor/bin/phpunit --bootstrap tests/force_c_ext.php tests",

@ -162,7 +162,7 @@ static void EnumDescriptor_FromEnumDef(zval *val, const upb_EnumDef *m) {
ZVAL_NULL(val); ZVAL_NULL(val);
} else { } else {
char *classname = char *classname =
GetPhpClassname(upb_EnumDef_File(m), upb_EnumDef_FullName(m)); GetPhpClassname(upb_EnumDef_File(m), upb_EnumDef_FullName(m), false);
zend_string *str = zend_string_init(classname, strlen(classname), 0); zend_string *str = zend_string_init(classname, strlen(classname), 0);
zend_class_entry *ce = zend_lookup_class(str); // May autoload the class. zend_class_entry *ce = zend_lookup_class(str); // May autoload the class.
@ -499,19 +499,23 @@ static void Descriptor_destructor(zend_object* obj) {
} }
static zend_class_entry *Descriptor_GetGeneratedClass(const upb_MessageDef *m) { static zend_class_entry *Descriptor_GetGeneratedClass(const upb_MessageDef *m) {
char *classname = for (int i = 0; i < 2; ++i) {
GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m)); char *classname =
zend_string *str = zend_string_init(classname, strlen(classname), 0); GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m), (bool)i);
zend_class_entry *ce = zend_lookup_class(str); // May autoload the class. zend_string *str = zend_string_init(classname, strlen(classname), 0);
zend_class_entry *ce = zend_lookup_class(str); // May autoload the class.
zend_string_release (str); zend_string_release (str);
free(classname);
if (!ce) { if (ce) {
zend_error(E_ERROR, "Couldn't load generated class %s", classname); return ce;
}
} }
free(classname); char *classname =
return ce; GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m), false);
zend_error(E_ERROR, "Couldn't load generated class %s", classname);
} }
void Descriptor_FromMessageDef(zval *val, const upb_MessageDef *m) { void Descriptor_FromMessageDef(zval *val, const upb_MessageDef *m) {

@ -82,12 +82,15 @@ const char *const kReservedNames[] = {
"global", "goto", "insteadof", "interface", "isset", "global", "goto", "insteadof", "interface", "isset",
"list", "match", "namespace", "new", "object", "list", "match", "namespace", "new", "object",
"or", "parent", "print", "private", "protected", "or", "parent", "print", "private", "protected",
"public", "require", "require_once", "return", "self", "public", "readonly", "require", "require_once", "return",
"static", "switch", "throw", "trait", "try", "self", "static", "switch", "throw", "trait",
"unset", "use", "var", "while", "xor", "try", "unset", "use", "var", "while",
"yield", "int", "float", "bool", "string", "xor", "yield", "int", "float", "bool",
"true", "false", "null", "void", "iterable", "string", "true", "false", "null", "void",
NULL}; "iterable", NULL};
const char *const kPreviouslyUnreservedNames[] = {
"readonly", NULL};
bool is_reserved_name(const char* name) { bool is_reserved_name(const char* name) {
int i; int i;
@ -99,6 +102,15 @@ bool is_reserved_name(const char* name) {
return false; return false;
} }
bool is_previously_unreserved_name(const char* name) {
for (int i = 0; kPreviouslyUnreservedNames[i]; i++) {
if (strcmp(kPreviouslyUnreservedNames[i], name) == 0) {
return true;
}
}
return false;
}
static char nolocale_tolower(char ch) { static char nolocale_tolower(char ch) {
if (ch >= 'A' && ch <= 'Z') { if (ch >= 'A' && ch <= 'Z') {
return ch - ('A' - 'a'); return ch - ('A' - 'a');
@ -115,17 +127,22 @@ static char nolocale_toupper(char ch) {
} }
} }
static bool is_reserved(const char *segment, int length) { static char *strdup_nolocale_lower(char *str, int length) {
char* lower = malloc(length + 1);
lower[length] = '\0';
for(int i = 0; i < length; ++i) {
lower[i] = nolocale_tolower(str[i]);
}
return lower;
}
static bool is_reserved(const char *segment, int length, bool previous) {
bool result; bool result;
char* lower = calloc(1, length + 1); char* lower = strdup_nolocale_lower(segment, length);
memcpy(lower, segment, length);
int i = 0;
while(lower[i]) {
lower[i] = nolocale_tolower(lower[i]);
i++;
}
lower[length] = 0;
result = is_reserved_name(lower); result = is_reserved_name(lower);
if (result && previous && is_previously_unreserved_name(lower)) {
result = false;
}
free(lower); free(lower);
return result; return result;
} }
@ -133,11 +150,12 @@ static bool is_reserved(const char *segment, int length) {
static void fill_prefix(const char *segment, int length, static void fill_prefix(const char *segment, int length,
const char *prefix_given, const char *prefix_given,
const char *package_name, const char *package_name,
stringsink *classname) { stringsink *classname,
bool previous) {
if (prefix_given != NULL && strcmp(prefix_given, "") != 0) { if (prefix_given != NULL && strcmp(prefix_given, "") != 0) {
stringsink_string(classname, prefix_given, strlen(prefix_given)); stringsink_string(classname, prefix_given, strlen(prefix_given));
} else { } else {
if (is_reserved(segment, length)) { if (is_reserved(segment, length, previous)) {
if (package_name != NULL && if (package_name != NULL &&
strcmp("google.protobuf", package_name) == 0) { strcmp("google.protobuf", package_name) == 0) {
stringsink_string(classname, "GPB", 3); stringsink_string(classname, "GPB", 3);
@ -160,7 +178,7 @@ static void fill_segment(const char *segment, int length,
} }
static void fill_namespace(const char *package, const char *php_namespace, static void fill_namespace(const char *package, const char *php_namespace,
stringsink *classname) { stringsink *classname, bool previous) {
if (php_namespace != NULL) { if (php_namespace != NULL) {
if (strlen(php_namespace) != 0) { if (strlen(php_namespace) != 0) {
stringsink_string(classname, php_namespace, strlen(php_namespace)); stringsink_string(classname, php_namespace, strlen(php_namespace));
@ -174,7 +192,7 @@ static void fill_namespace(const char *package, const char *php_namespace,
while (j < package_len && package[j] != '.') { while (j < package_len && package[j] != '.') {
j++; j++;
} }
fill_prefix(package + i, j - i, "", package, classname); fill_prefix(package + i, j - i, "", package, classname, previous);
fill_segment(package + i, j - i, classname, true); fill_segment(package + i, j - i, classname, true);
stringsink_string(classname, "\\", 1); stringsink_string(classname, "\\", 1);
i = j + 1; i = j + 1;
@ -185,7 +203,8 @@ static void fill_namespace(const char *package, const char *php_namespace,
static void fill_classname(const char *fullname, static void fill_classname(const char *fullname,
const char *package, const char *package,
const char *prefix, const char *prefix,
stringsink *classname) { stringsink *classname,
bool previous) {
int classname_start = 0; int classname_start = 0;
if (package != NULL) { if (package != NULL) {
size_t package_len = strlen(package); size_t package_len = strlen(package);
@ -199,7 +218,7 @@ static void fill_classname(const char *fullname,
while (j < fullname_len && fullname[j] != '.') { while (j < fullname_len && fullname[j] != '.') {
j++; j++;
} }
fill_prefix(fullname + i, j - i, prefix, package, classname); fill_prefix(fullname + i, j - i, prefix, package, classname, previous);
fill_segment(fullname + i, j - i, classname, false); fill_segment(fullname + i, j - i, classname, false);
if (j != fullname_len) { if (j != fullname_len) {
stringsink_string(classname, "\\", 1); stringsink_string(classname, "\\", 1);
@ -215,7 +234,7 @@ char *str_view_dup(upb_StringView str) {
return ret; return ret;
} }
char *GetPhpClassname(const upb_FileDef *file, const char *fullname) { char *GetPhpClassname(const upb_FileDef *file, const char *fullname, bool previous) {
// Prepend '.' to package name to make it absolute. In the 5 additional // Prepend '.' to package name to make it absolute. In the 5 additional
// bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if // bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if
// given message is google.protobuf.Empty. // given message is google.protobuf.Empty.
@ -234,8 +253,8 @@ char *GetPhpClassname(const upb_FileDef *file, const char *fullname) {
stringsink namesink; stringsink namesink;
stringsink_init(&namesink); stringsink_init(&namesink);
fill_namespace(package, php_namespace, &namesink); fill_namespace(package, php_namespace, &namesink, previous);
fill_classname(fullname, package, prefix, &namesink); fill_classname(fullname, package, prefix, &namesink, previous);
stringsink_string(&namesink, "\0", 1); stringsink_string(&namesink, "\0", 1);
ret = strdup(namesink.ptr); ret = strdup(namesink.ptr);
stringsink_uninit(&namesink); stringsink_uninit(&namesink);
@ -243,3 +262,26 @@ char *GetPhpClassname(const upb_FileDef *file, const char *fullname) {
free(prefix); free(prefix);
return ret; return ret;
} }
bool IsPreviouslyUnreservedClassName(const char* fullname) {
const char *classname = strrchr(fullname, '\\');
if (classname) {
classname += 1;
} else {
classname = fullname;
}
if (strncmp(classname, "PB", 2) != 0) {
return false;
}
classname += 2;
int length = strlen(classname);
char* lower = strdup_nolocale_lower(classname, length);
for (int j = 0; kPreviouslyUnreservedNames[j]; j++) {
if (strcmp(kPreviouslyUnreservedNames[j], lower) == 0) {
free(lower);
return true;
}
}
free(lower);
return false;
}

@ -35,6 +35,7 @@
// Translates a protobuf symbol name (eg. foo.bar.Baz) into a PHP class name // Translates a protobuf symbol name (eg. foo.bar.Baz) into a PHP class name
// (eg. \Foo\Bar\Baz). // (eg. \Foo\Bar\Baz).
char *GetPhpClassname(const upb_FileDef *file, const char *fullname); char *GetPhpClassname(const upb_FileDef *file, const char *fullname, bool previous);
bool IsPreviouslyUnreservedClassName(const char* fullname);
#endif // PHP_PROTOBUF_NAMES_H_ #endif // PHP_PROTOBUF_NAMES_H_

@ -5,16 +5,16 @@
<summary>Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.</summary> <summary>Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.</summary>
<description>https://developers.google.com/protocol-buffers/</description> <description>https://developers.google.com/protocol-buffers/</description>
<lead> <lead>
<name>Bo Yang</name> <name>Protobuf Team</name>
<user>stanleycheung</user> <user>protobufpackages</user>
<email>protobuf-opensource@google.com</email> <email>protobuf-packages@google.com</email>
<active>yes</active> <active>yes</active>
</lead> </lead>
<date>2022-06-23</date> <date>2022-07-21</date>
<time>12:12:44</time> <time>10:19:47</time>
<version> <version>
<release>3.21.2</release> <release>3.21.3</release>
<api>3.21.2</api> <api>3.21.3</api>
</version> </version>
<stability> <stability>
<release>stable</release> <release>stable</release>
@ -1358,5 +1358,20 @@ G A release.
<notes> <notes>
</notes> </notes>
</release> </release>
<release>
<version>
<release>3.21.3</release>
<api>3.21.3</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<date>2022-07-21</date>
<time>10:19:47</time>
<license uri="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</license>
<notes>
</notes>
</release>
</changelog> </changelog>
</package> </package>

@ -242,13 +242,19 @@ bool ObjCache_Get(const void *upb_obj, zval *val) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
void NameMap_AddMessage(const upb_MessageDef *m) { void NameMap_AddMessage(const upb_MessageDef *m) {
char *k = GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m)); for (int i = 0; i < 2; ++i) {
zend_hash_str_add_ptr(&PROTOBUF_G(name_msg_cache), k, strlen(k), (void*)m); char *k = GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m), (bool)i);
free(k); zend_hash_str_add_ptr(&PROTOBUF_G(name_msg_cache), k, strlen(k), (void*)m);
if (!IsPreviouslyUnreservedClassName(k)) {
free(k);
return;
}
free(k);
}
} }
void NameMap_AddEnum(const upb_EnumDef *e) { void NameMap_AddEnum(const upb_EnumDef *e) {
char *k = GetPhpClassname(upb_EnumDef_File(e), upb_EnumDef_FullName(e)); char *k = GetPhpClassname(upb_EnumDef_File(e), upb_EnumDef_FullName(e), false);
zend_hash_str_add_ptr(&PROTOBUF_G(name_enum_cache), k, strlen(k), (void*)e); zend_hash_str_add_ptr(&PROTOBUF_G(name_enum_cache), k, strlen(k), (void*)e);
free(k); free(k);
} }

@ -127,7 +127,7 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_setter, 0, 0, 1)
ZEND_ARG_INFO(0, value) ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
#define PHP_PROTOBUF_VERSION "3.21.2" #define PHP_PROTOBUF_VERSION "3.21.3"
// ptr -> PHP object cache. This is a weak map that caches lazily-created // ptr -> PHP object cache. This is a weak map that caches lazily-created
// wrapper objects around upb types: // wrapper objects around upb types:

@ -45,6 +45,7 @@ class Descriptor
private $enum_type = []; private $enum_type = [];
private $klass; private $klass;
private $legacy_klass; private $legacy_klass;
private $previous_klass;
private $options; private $options;
private $oneof_decl = []; private $oneof_decl = [];
@ -162,6 +163,16 @@ class Descriptor
return $this->legacy_klass; return $this->legacy_klass;
} }
public function setPreviouslyUnreservedClass($klass)
{
$this->previous_klass = $klass;
}
public function getPreviouslyUnreservedClass()
{
return $this->previous_klass;
}
public function setOptions($options) public function setOptions($options)
{ {
$this->options = $options; $this->options = $options;
@ -179,6 +190,7 @@ class Descriptor
$message_name_without_package = ""; $message_name_without_package = "";
$classname = ""; $classname = "";
$legacy_classname = ""; $legacy_classname = "";
$previous_classname = "";
$fullname = ""; $fullname = "";
GPBUtil::getFullClassName( GPBUtil::getFullClassName(
$proto, $proto,
@ -187,10 +199,12 @@ class Descriptor
$message_name_without_package, $message_name_without_package,
$classname, $classname,
$legacy_classname, $legacy_classname,
$fullname); $fullname,
$previous_classname);
$desc->setFullName($fullname); $desc->setFullName($fullname);
$desc->setClass($classname); $desc->setClass($classname);
$desc->setLegacyClass($legacy_classname); $desc->setLegacyClass($legacy_classname);
$desc->setPreviouslyUnreservedClass($previous_classname);
$desc->setOptions($proto->getOptions()); $desc->setOptions($proto->getOptions());
foreach ($proto->getField() as $field_proto) { foreach ($proto->getField() as $field_proto) {

@ -96,6 +96,7 @@ class DescriptorPool
$descriptor->getClass(); $descriptor->getClass();
$this->class_to_desc[$descriptor->getClass()] = $descriptor; $this->class_to_desc[$descriptor->getClass()] = $descriptor;
$this->class_to_desc[$descriptor->getLegacyClass()] = $descriptor; $this->class_to_desc[$descriptor->getLegacyClass()] = $descriptor;
$this->class_to_desc[$descriptor->getPreviouslyUnreservedClass()] = $descriptor;
foreach ($descriptor->getNestedType() as $nested_type) { foreach ($descriptor->getNestedType() as $nested_type) {
$this->addDescriptor($nested_type); $this->addDescriptor($nested_type);
} }

@ -101,7 +101,8 @@ class EnumDescriptor
$enum_name_without_package, $enum_name_without_package,
$classname, $classname,
$legacy_classname, $legacy_classname,
$fullname); $fullname,
$unused_previous_classname);
$desc->setFullName($fullname); $desc->setFullName($fullname);
$desc->setClass($classname); $desc->setClass($classname);
$desc->setLegacyClass($legacy_classname); $desc->setLegacyClass($legacy_classname);

@ -285,11 +285,12 @@ class GPBUtil
"include"=>0, "include_once"=>0, "instanceof"=>0, "insteadof"=>0, "include"=>0, "include_once"=>0, "instanceof"=>0, "insteadof"=>0,
"interface"=>0, "isset"=>0, "list"=>0, "match"=>0, "namespace"=>0, "interface"=>0, "isset"=>0, "list"=>0, "match"=>0, "namespace"=>0,
"new"=>0, "or"=>0, "parent"=>0, "print"=>0, "private"=>0, "new"=>0, "or"=>0, "parent"=>0, "print"=>0, "private"=>0,
"protected"=>0,"public"=>0, "require"=>0, "require_once"=>0, "protected"=>0,"public"=>0, "readonly" => 0,"require"=>0,
"return"=>0, "self"=>0, "static"=>0, "switch"=>0, "throw"=>0, "require_once"=>0,"return"=>0, "self"=>0, "static"=>0, "switch"=>0,
"trait"=>0, "try"=>0,"unset"=>0, "use"=>0, "var"=>0, "while"=>0, "throw"=>0,"trait"=>0, "try"=>0,"unset"=>0, "use"=>0, "var"=>0,
"xor"=>0, "yield"=>0, "int"=>0, "float"=>0, "bool"=>0, "string"=>0, "while"=>0,"xor"=>0, "yield"=>0, "int"=>0, "float"=>0, "bool"=>0,
"true"=>0, "false"=>0, "null"=>0, "void"=>0, "iterable"=>0 "string"=>0,"true"=>0, "false"=>0, "null"=>0, "void"=>0,
"iterable"=>0
); );
if (array_key_exists(strtolower($classname), $reserved_words)) { if (array_key_exists(strtolower($classname), $reserved_words)) {
@ -303,6 +304,27 @@ class GPBUtil
return ""; return "";
} }
private static function getPreviouslyUnreservedClassNamePrefix(
$classname,
$file_proto)
{
$previously_unreserved_words = array(
"readonly"=>0
);
if (array_key_exists(strtolower($classname), $previously_unreserved_words)) {
$option = $file_proto->getOptions();
$prefix = is_null($option) ? "" : $option->getPhpClassPrefix();
if ($prefix !== "") {
return $prefix;
}
return "";
}
return self::getClassNamePrefix($classname, $file_proto);
}
public static function getLegacyClassNameWithoutPackage( public static function getLegacyClassNameWithoutPackage(
$name, $name,
$file_proto) $file_proto)
@ -322,6 +344,17 @@ class GPBUtil
return implode('\\', $parts); return implode('\\', $parts);
} }
private static function getPreviouslyUnreservedClassNameWithoutPackage(
$name,
$file_proto)
{
$parts = explode('.', $name);
foreach ($parts as $i => $part) {
$parts[$i] = static::getPreviouslyUnreservedClassNamePrefix($parts[$i], $file_proto) . $parts[$i];
}
return implode('\\', $parts);
}
public static function getFullClassName( public static function getFullClassName(
$proto, $proto,
$containing, $containing,
@ -329,7 +362,8 @@ class GPBUtil
&$message_name_without_package, &$message_name_without_package,
&$classname, &$classname,
&$legacy_classname, &$legacy_classname,
&$fullname) &$fullname,
&$previous_classname)
{ {
// Full name needs to start with '.'. // Full name needs to start with '.'.
$message_name_without_package = $proto->getName(); $message_name_without_package = $proto->getName();
@ -350,6 +384,9 @@ class GPBUtil
$legacy_class_name_without_package = $legacy_class_name_without_package =
static::getLegacyClassNameWithoutPackage( static::getLegacyClassNameWithoutPackage(
$message_name_without_package, $file_proto); $message_name_without_package, $file_proto);
$previous_class_name_without_package =
static::getPreviouslyUnreservedClassNameWithoutPackage(
$message_name_without_package, $file_proto);
$option = $file_proto->getOptions(); $option = $file_proto->getOptions();
if (!is_null($option) && $option->hasPhpNamespace()) { if (!is_null($option) && $option->hasPhpNamespace()) {
@ -358,10 +395,13 @@ class GPBUtil
$classname = $namespace . "\\" . $class_name_without_package; $classname = $namespace . "\\" . $class_name_without_package;
$legacy_classname = $legacy_classname =
$namespace . "\\" . $legacy_class_name_without_package; $namespace . "\\" . $legacy_class_name_without_package;
$previous_classname =
$namespace . "\\" . $previous_class_name_without_package;
return; return;
} else { } else {
$classname = $class_name_without_package; $classname = $class_name_without_package;
$legacy_classname = $legacy_class_name_without_package; $legacy_classname = $legacy_class_name_without_package;
$previous_classname = $previous_class_name_without_package;
return; return;
} }
} }
@ -369,6 +409,7 @@ class GPBUtil
if ($package === "") { if ($package === "") {
$classname = $class_name_without_package; $classname = $class_name_without_package;
$legacy_classname = $legacy_class_name_without_package; $legacy_classname = $legacy_class_name_without_package;
$previous_classname = $previous_class_name_without_package;
} else { } else {
$parts = array_map('ucwords', explode('.', $package)); $parts = array_map('ucwords', explode('.', $package));
foreach ($parts as $i => $part) { foreach ($parts as $i => $part) {
@ -381,6 +422,11 @@ class GPBUtil
$legacy_classname = $legacy_classname =
implode('\\', array_map('ucwords', explode('.', $package))). implode('\\', array_map('ucwords', explode('.', $package))).
"\\".$legacy_class_name_without_package; "\\".$legacy_class_name_without_package;
$previous_classname =
implode('\\', array_map('ucwords', explode('.', $package))).
"\\".self::getPreviouslyUnreservedClassNamePrefix(
$previous_class_name_without_package, $file_proto).
$previous_class_name_without_package;
} }
} }

@ -334,6 +334,18 @@ class GeneratedClassTest extends TestBase
$this->legacyEnum(new TestLegacyMessage\NestedEnum); $this->legacyEnum(new TestLegacyMessage\NestedEnum);
} }
public function testLegacyReadOnlyMessage()
{
$this->assertTrue(class_exists('\Upper\READONLY'));
$this->assertTrue(class_exists('\Lower\readonly'));
}
public function testLegacyReadOnlyEnum()
{
$this->assertTrue(class_exists('\Upper_enum\READONLY'));
$this->assertTrue(class_exists('\Lower_enum\readonly'));
}
private function legacyEnum(TestLegacyMessage_NestedEnum $enum) private function legacyEnum(TestLegacyMessage_NestedEnum $enum)
{ {
// If we made it here without a PHP Fatal error, the typehint worked // If we made it here without a PHP Fatal error, the typehint worked
@ -943,6 +955,7 @@ class GeneratedClassTest extends TestBase
$m = new \Lower\PBprivate(); $m = new \Lower\PBprivate();
$m = new \Lower\PBprotected(); $m = new \Lower\PBprotected();
$m = new \Lower\PBpublic(); $m = new \Lower\PBpublic();
$m = new \Lower\PBreadonly();
$m = new \Lower\PBrequire(); $m = new \Lower\PBrequire();
$m = new \Lower\PBrequire_once(); $m = new \Lower\PBrequire_once();
$m = new \Lower\PBreturn(); $m = new \Lower\PBreturn();
@ -1023,6 +1036,7 @@ class GeneratedClassTest extends TestBase
$m = new \Upper\PBPRIVATE(); $m = new \Upper\PBPRIVATE();
$m = new \Upper\PBPROTECTED(); $m = new \Upper\PBPROTECTED();
$m = new \Upper\PBPUBLIC(); $m = new \Upper\PBPUBLIC();
$m = new \Upper\PBREADONLY();
$m = new \Upper\PBREQUIRE(); $m = new \Upper\PBREQUIRE();
$m = new \Upper\PBREQUIRE_ONCE(); $m = new \Upper\PBREQUIRE_ONCE();
$m = new \Upper\PBRETURN(); $m = new \Upper\PBRETURN();
@ -1104,6 +1118,7 @@ class GeneratedClassTest extends TestBase
$m = new \Lower_enum\PBprotected(); $m = new \Lower_enum\PBprotected();
$m = new \Lower_enum\PBpublic(); $m = new \Lower_enum\PBpublic();
$m = new \Lower_enum\PBrequire(); $m = new \Lower_enum\PBrequire();
$m = new \Lower_enum\PBreadonly();
$m = new \Lower_enum\PBrequire_once(); $m = new \Lower_enum\PBrequire_once();
$m = new \Lower_enum\PBreturn(); $m = new \Lower_enum\PBreturn();
$m = new \Lower_enum\PBself(); $m = new \Lower_enum\PBself();
@ -1183,6 +1198,7 @@ class GeneratedClassTest extends TestBase
$m = new \Upper_enum\PBPRIVATE(); $m = new \Upper_enum\PBPRIVATE();
$m = new \Upper_enum\PBPROTECTED(); $m = new \Upper_enum\PBPROTECTED();
$m = new \Upper_enum\PBPUBLIC(); $m = new \Upper_enum\PBPUBLIC();
$m = new \Upper_enum\PBREADONLY();
$m = new \Upper_enum\PBREQUIRE(); $m = new \Upper_enum\PBREQUIRE();
$m = new \Upper_enum\PBREQUIRE_ONCE(); $m = new \Upper_enum\PBREQUIRE_ONCE();
$m = new \Upper_enum\PBRETURN(); $m = new \Upper_enum\PBRETURN();
@ -1287,6 +1303,7 @@ class GeneratedClassTest extends TestBase
$m = \Lower_enum_value\NotAllowed::iterable; $m = \Lower_enum_value\NotAllowed::iterable;
$m = \Lower_enum_value\NotAllowed::parent; $m = \Lower_enum_value\NotAllowed::parent;
$m = \Lower_enum_value\NotAllowed::self; $m = \Lower_enum_value\NotAllowed::self;
$m = \Lower_enum_value\NotAllowed::readonly;
$m = \Upper_enum_value\NotAllowed::PBABSTRACT; $m = \Upper_enum_value\NotAllowed::PBABSTRACT;
$m = \Upper_enum_value\NotAllowed::PBAND; $m = \Upper_enum_value\NotAllowed::PBAND;
@ -1367,6 +1384,7 @@ class GeneratedClassTest extends TestBase
$m = \Upper_enum_value\NotAllowed::ITERABLE; $m = \Upper_enum_value\NotAllowed::ITERABLE;
$m = \Upper_enum_value\NotAllowed::PARENT; $m = \Upper_enum_value\NotAllowed::PARENT;
$m = \Upper_enum_value\NotAllowed::SELF; $m = \Upper_enum_value\NotAllowed::SELF;
$m = \Upper_enum_value\NotAllowed::READONLY;
$this->assertTrue(true); $this->assertTrue(true);
} }

@ -0,0 +1,18 @@
<?php
require_once('test_base.php');
require_once('test_util.php');
class PreviouslyGeneratedClassTest extends TestBase
{
#########################################################
# Test compatibility for previously unreserved words.
#########################################################
public function testPrefixForReservedWords()
{
$m = new \Previous\readonly();
$this->assertTrue(true);
}
}

@ -0,0 +1,28 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: proto_previous/test_previously_unreserved_message.proto
namespace GPBMetadata\ProtoPrevious;
class TestPreviouslyUnreservedMessage
{
public static $is_initialized = false;
public static function initOnce() {
$pool = \Google\Protobuf\Internal\DescriptorPool::getGeneratedPool();
if (static::$is_initialized == true) {
return;
}
$pool->internalAddGeneratedFile(
'
W
7proto_previous/test_previously_unreserved_message.protoprevious"
readonlybproto3'
, true);
static::$is_initialized = true;
}
}

@ -0,0 +1,31 @@
<?php
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: proto_previous/test_previously_unreserved_message.proto
namespace Previous;
use Google\Protobuf\Internal\GPBType;
use Google\Protobuf\Internal\RepeatedField;
use Google\Protobuf\Internal\GPBUtil;
/**
* Generated from protobuf message <code>previous.readonly</code>
*/
class readonly extends \Google\Protobuf\Internal\Message
{
/**
* Constructor.
*
* @param array $data {
* Optional. Data for populating the Message object.
*
* }
*/
public function __construct($data = NULL) {
\GPBMetadata\ProtoPrevious\TestPreviouslyUnreservedMessage::initOnce();
parent::__construct($data);
}
}

@ -57,6 +57,7 @@ enum print { ZERO51 = 0; }
enum private { ZERO52 = 0; } enum private { ZERO52 = 0; }
enum protected { ZERO53 = 0; } enum protected { ZERO53 = 0; }
enum public { ZERO54 = 0; } enum public { ZERO54 = 0; }
enum readonly { ZERO80 = 0; }
enum require { ZERO55 = 0; } enum require { ZERO55 = 0; }
enum require_once { ZERO56 = 0; } enum require_once { ZERO56 = 0; }
enum return { ZERO57 = 0; } enum return { ZERO57 = 0; }

@ -57,6 +57,7 @@ enum PRINT { ZERO51 = 0; }
enum PRIVATE { ZERO52 = 0; } enum PRIVATE { ZERO52 = 0; }
enum PROTECTED { ZERO53 = 0; } enum PROTECTED { ZERO53 = 0; }
enum PUBLIC { ZERO54 = 0; } enum PUBLIC { ZERO54 = 0; }
enum READONLY { ZERO80 = 0; }
enum REQUIRE { ZERO55 = 0; } enum REQUIRE { ZERO55 = 0; }
enum REQUIRE_ONCE { ZERO56 = 0; } enum REQUIRE_ONCE { ZERO56 = 0; }
enum RETURN { ZERO57 = 0; } enum RETURN { ZERO57 = 0; }

@ -58,6 +58,7 @@ enum NotAllowed {
private = 51; private = 51;
protected = 52; protected = 52;
public = 53; public = 53;
readonly = 79;
require = 54; require = 54;
require_once = 55; require_once = 55;
return = 56; return = 56;

@ -58,6 +58,7 @@ enum NotAllowed {
PRIVATE = 51; PRIVATE = 51;
PROTECTED = 52; PROTECTED = 52;
PUBLIC = 53; PUBLIC = 53;
READONLY = 79;
REQUIRE = 54; REQUIRE = 54;
REQUIRE_ONCE = 55; REQUIRE_ONCE = 55;
RETURN = 56; RETURN = 56;

@ -57,6 +57,7 @@ message print {}
message private {} message private {}
message protected {} message protected {}
message public {} message public {}
message readonly {}
message require {} message require {}
message require_once {} message require_once {}
message return {} message return {}

@ -57,6 +57,7 @@ message PRINT {}
message PRIVATE {} message PRIVATE {}
message PROTECTED {} message PROTECTED {}
message PUBLIC {} message PUBLIC {}
message READONLY {}
message REQUIRE {} message REQUIRE {}
message REQUIRE_ONCE {} message REQUIRE_ONCE {}
message RETURN {} message RETURN {}

@ -0,0 +1,5 @@
syntax = "proto3";
package previous;
message readonly {}

@ -114,6 +114,6 @@ def protobuf_deps():
_github_archive( _github_archive(
name = "upb", name = "upb",
repo = "https://github.com/protocolbuffers/upb", repo = "https://github.com/protocolbuffers/upb",
commit = "04cb5af6b67c80db61f0aee76dcb6d233e51795c", commit = "17b6451684ffcf6e77d10a5def9bf19af57eccd3",
sha256 = "62d3519a7b65d6695e011f2733bfc5d7c6ab77f2bd83cdd2dca449da2e739c7f", sha256 = "655c30a01c8ab56680c154baded548c5df8f726305d3338d0885cbb1f700ec10",
) )

@ -1,3 +1,3 @@
PROTOC_VERSION = '21.2' PROTOC_VERSION = '21.3'
PROTOBUF_JAVA_VERSION = '3.21.2' PROTOBUF_JAVA_VERSION = '3.21.3'
PROTOBUF_PYTHON_VERSION = '4.21.2' PROTOBUF_PYTHON_VERSION = '4.21.3'

@ -4,11 +4,11 @@
<parent> <parent>
<groupId>com.google</groupId> <groupId>com.google</groupId>
<artifactId>google</artifactId> <artifactId>google</artifactId>
<version>1</version> <version>5</version>
</parent> </parent>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protoc</artifactId> <artifactId>protoc</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Protobuf Compiler</name> <name>Protobuf Compiler</name>
<description> <description>

@ -1,6 +1,6 @@
Gem::Specification.new do |s| Gem::Specification.new do |s|
s.name = "google-protobuf" s.name = "google-protobuf"
s.version = "3.21.2" s.version = "3.21.3"
git_tag = "v#{s.version.to_s.sub('.rc.', '-rc')}" # Converts X.Y.Z.rc.N to vX.Y.Z-rcN, used for the git tag git_tag = "v#{s.version.to_s.sub('.rc.', '-rc')}" # Converts X.Y.Z.rc.N to vX.Y.Z-rcN, used for the git tag
s.licenses = ["BSD-3-Clause"] s.licenses = ["BSD-3-Clause"]
s.summary = "Protocol Buffers" s.summary = "Protocol Buffers"

@ -9,7 +9,7 @@
<groupId>com.google.protobuf.jruby</groupId> <groupId>com.google.protobuf.jruby</groupId>
<artifactId>protobuf-jruby</artifactId> <artifactId>protobuf-jruby</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
<name>Protocol Buffer JRuby native extension</name> <name>Protocol Buffer JRuby native extension</name>
<description> <description>
Protocol Buffers are a way of encoding structured data in an efficient yet Protocol Buffers are a way of encoding structured data in an efficient yet
@ -76,7 +76,7 @@
<dependency> <dependency>
<groupId>com.google.protobuf</groupId> <groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId> <artifactId>protobuf-java-util</artifactId>
<version>3.21.2</version> <version>3.21.3</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jruby</groupId> <groupId>org.jruby</groupId>

@ -774,6 +774,7 @@ protobuf_test_SOURCES = \
google/protobuf/compiler/csharp/csharp_generator_unittest.cc \ google/protobuf/compiler/csharp/csharp_generator_unittest.cc \
google/protobuf/compiler/importer_unittest.cc \ google/protobuf/compiler/importer_unittest.cc \
google/protobuf/compiler/java/doc_comment_unittest.cc \ google/protobuf/compiler/java/doc_comment_unittest.cc \
google/protobuf/compiler/java/message_serialization_unittest.cc \
google/protobuf/compiler/java/plugin_unittest.cc \ google/protobuf/compiler/java/plugin_unittest.cc \
google/protobuf/compiler/mock_code_generator.cc \ google/protobuf/compiler/mock_code_generator.cc \
google/protobuf/compiler/mock_code_generator.h \ google/protobuf/compiler/mock_code_generator.h \

@ -771,6 +771,7 @@ set(compiler_test_files
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/importer_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/importer_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/doc_comment_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/doc_comment_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/message_serialization_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/plugin_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/java/plugin_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/objectivec/objectivec_helpers_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/parser_unittest.cc ${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/parser_unittest.cc

@ -1419,7 +1419,7 @@ TEST(ArenaTest, BlockSizeDoubling) {
ASSERT_GT(arena.SpaceAllocated(), first_block_size); ASSERT_GT(arena.SpaceAllocated(), first_block_size);
auto second_block_size = (arena.SpaceAllocated() - first_block_size); auto second_block_size = (arena.SpaceAllocated() - first_block_size);
EXPECT_EQ(second_block_size, 2*first_block_size); EXPECT_GE(second_block_size, 2*first_block_size);
} }
TEST(ArenaTest, Alignment) { TEST(ArenaTest, Alignment) {

@ -50,7 +50,8 @@ namespace internal {
namespace { namespace {
// Enforce that allocated data aligns to at least 8 bytes, and that // TaggedStringPtr::Flags uses the lower 2 bits as tags.
// Enforce that allocated data aligns to at least 4 bytes, and that
// the alignment of the global const string value does as well. // the alignment of the global const string value does as well.
// The alignment guaranteed by `new std::string` depends on both: // The alignment guaranteed by `new std::string` depends on both:
// - new align = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / max_align_t // - new align = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / max_align_t
@ -64,8 +65,8 @@ constexpr size_t kNewAlign = alignof(std::max_align_t);
#endif #endif
constexpr size_t kStringAlign = alignof(std::string); constexpr size_t kStringAlign = alignof(std::string);
static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 8, ""); static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 4, "");
static_assert(alignof(ExplicitlyConstructedArenaString) >= 8, ""); static_assert(alignof(ExplicitlyConstructedArenaString) >= 4, "");
} // namespace } // namespace

@ -72,7 +72,7 @@ void ThreadSafeArenaStats::PrepareForSampling(int64_t stride) {
bytes_used.store(0, std::memory_order_relaxed); bytes_used.store(0, std::memory_order_relaxed);
bytes_allocated.store(0, std::memory_order_relaxed); bytes_allocated.store(0, std::memory_order_relaxed);
bytes_wasted.store(0, std::memory_order_relaxed); bytes_wasted.store(0, std::memory_order_relaxed);
max_bytes_allocated.store(0, std::memory_order_relaxed); max_block_size.store(0, std::memory_order_relaxed);
thread_ids.store(0, std::memory_order_relaxed); thread_ids.store(0, std::memory_order_relaxed);
weight = stride; weight = stride;
// The inliner makes hardcoded skip_count difficult (especially when combined // The inliner makes hardcoded skip_count difficult (especially when combined
@ -87,6 +87,9 @@ void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t used,
info->bytes_used.fetch_add(used, std::memory_order_relaxed); info->bytes_used.fetch_add(used, std::memory_order_relaxed);
info->bytes_allocated.fetch_add(allocated, std::memory_order_relaxed); info->bytes_allocated.fetch_add(allocated, std::memory_order_relaxed);
info->bytes_wasted.fetch_add(wasted, std::memory_order_relaxed); info->bytes_wasted.fetch_add(wasted, std::memory_order_relaxed);
if (info->max_block_size.load(std::memory_order_relaxed) < allocated) {
info->max_block_size.store(allocated, std::memory_order_relaxed);
}
const uint64_t tid = 1ULL << (GetCachedTID() % 63); const uint64_t tid = 1ULL << (GetCachedTID() % 63);
info->thread_ids.fetch_or(tid, std::memory_order_relaxed); info->thread_ids.fetch_or(tid, std::memory_order_relaxed);
} }

@ -69,8 +69,8 @@ struct ThreadSafeArenaStats
std::atomic<size_t> bytes_used; std::atomic<size_t> bytes_used;
std::atomic<size_t> bytes_allocated; std::atomic<size_t> bytes_allocated;
std::atomic<size_t> bytes_wasted; std::atomic<size_t> bytes_wasted;
// Records the largest size an arena ever had. // Records the largest block allocated for the arena.
std::atomic<size_t> max_bytes_allocated; std::atomic<size_t> max_block_size;
// Bit `i` is set to 1 indicates that a thread with `tid % 63 = i` accessed // Bit `i` is set to 1 indicates that a thread with `tid % 63 = i` accessed
// the underlying arena. We use `% 63` as a rudimentary hash to ensure some // the underlying arena. We use `% 63` as a rudimentary hash to ensure some
// bit mixing for thread-ids; `% 64` would only grab the low bits and might // bit mixing for thread-ids; `% 64` would only grab the low bits and might

@ -89,21 +89,21 @@ TEST(ThreadSafeArenaStatsTest, PrepareForSampling) {
EXPECT_EQ(info.bytes_used.load(), 0); EXPECT_EQ(info.bytes_used.load(), 0);
EXPECT_EQ(info.bytes_allocated.load(), 0); EXPECT_EQ(info.bytes_allocated.load(), 0);
EXPECT_EQ(info.bytes_wasted.load(), 0); EXPECT_EQ(info.bytes_wasted.load(), 0);
EXPECT_EQ(info.max_bytes_allocated.load(), 0); EXPECT_EQ(info.max_block_size.load(), 0);
EXPECT_EQ(info.weight, kTestStride); EXPECT_EQ(info.weight, kTestStride);
info.num_allocations.store(1, std::memory_order_relaxed); info.num_allocations.store(1, std::memory_order_relaxed);
info.bytes_used.store(1, std::memory_order_relaxed); info.bytes_used.store(1, std::memory_order_relaxed);
info.bytes_allocated.store(1, std::memory_order_relaxed); info.bytes_allocated.store(1, std::memory_order_relaxed);
info.bytes_wasted.store(1, std::memory_order_relaxed); info.bytes_wasted.store(1, std::memory_order_relaxed);
info.max_bytes_allocated.store(1, std::memory_order_relaxed); info.max_block_size.store(1, std::memory_order_relaxed);
info.PrepareForSampling(2 * kTestStride); info.PrepareForSampling(2 * kTestStride);
EXPECT_EQ(info.num_allocations.load(), 0); EXPECT_EQ(info.num_allocations.load(), 0);
EXPECT_EQ(info.bytes_used.load(), 0); EXPECT_EQ(info.bytes_used.load(), 0);
EXPECT_EQ(info.bytes_allocated.load(), 0); EXPECT_EQ(info.bytes_allocated.load(), 0);
EXPECT_EQ(info.bytes_wasted.load(), 0); EXPECT_EQ(info.bytes_wasted.load(), 0);
EXPECT_EQ(info.max_bytes_allocated.load(), 0); EXPECT_EQ(info.max_block_size.load(), 0);
EXPECT_EQ(info.weight, 2 * kTestStride); EXPECT_EQ(info.weight, 2 * kTestStride);
} }
@ -117,14 +117,29 @@ TEST(ThreadSafeArenaStatsTest, RecordAllocateSlow) {
EXPECT_EQ(info.bytes_used.load(), 100); EXPECT_EQ(info.bytes_used.load(), 100);
EXPECT_EQ(info.bytes_allocated.load(), 128); EXPECT_EQ(info.bytes_allocated.load(), 128);
EXPECT_EQ(info.bytes_wasted.load(), 0); EXPECT_EQ(info.bytes_wasted.load(), 0);
EXPECT_EQ(info.max_bytes_allocated.load(), 0); EXPECT_EQ(info.max_block_size.load(), 128);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256, RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256,
/*wasted=*/28); /*wasted=*/28);
EXPECT_EQ(info.num_allocations.load(), 2); EXPECT_EQ(info.num_allocations.load(), 2);
EXPECT_EQ(info.bytes_used.load(), 200); EXPECT_EQ(info.bytes_used.load(), 200);
EXPECT_EQ(info.bytes_allocated.load(), 384); EXPECT_EQ(info.bytes_allocated.load(), 384);
EXPECT_EQ(info.bytes_wasted.load(), 28); EXPECT_EQ(info.bytes_wasted.load(), 28);
EXPECT_EQ(info.max_bytes_allocated.load(), 0); EXPECT_EQ(info.max_block_size.load(), 256);
}
TEST(ThreadSafeArenaStatsTest, RecordAllocateSlowMaxBlockSizeTest) {
ThreadSafeArenaStats info;
constexpr int64_t kTestStride = 458;
MutexLock l(&info.init_mu);
info.PrepareForSampling(kTestStride);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128, /*wasted=*/0);
EXPECT_EQ(info.max_block_size.load(), 128);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256,
/*wasted=*/28);
EXPECT_EQ(info.max_block_size.load(), 256);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128,
/*wasted=*/28);
EXPECT_EQ(info.max_block_size.load(), 256);
} }
TEST(ThreadSafeArenazSamplerTest, SamplingCorrectness) { TEST(ThreadSafeArenazSamplerTest, SamplingCorrectness) {

@ -398,7 +398,6 @@ class CommandLineInterface::GeneratorContextImpl : public GeneratorContext {
// Get name of all output files. // Get name of all output files.
void GetOutputFilenames(std::vector<std::string>* output_filenames); void GetOutputFilenames(std::vector<std::string>* output_filenames);
// implements GeneratorContext -------------------------------------- // implements GeneratorContext --------------------------------------
io::ZeroCopyOutputStream* Open(const std::string& filename) override; io::ZeroCopyOutputStream* Open(const std::string& filename) override;
io::ZeroCopyOutputStream* OpenForAppend(const std::string& filename) override; io::ZeroCopyOutputStream* OpenForAppend(const std::string& filename) override;
@ -963,6 +962,7 @@ PopulateSingleSimpleDescriptorDatabase(const std::string& descriptor_set_name);
int CommandLineInterface::Run(int argc, const char* const argv[]) { int CommandLineInterface::Run(int argc, const char* const argv[]) {
Clear(); Clear();
switch (ParseArguments(argc, argv)) { switch (ParseArguments(argc, argv)) {
case PARSE_ARGUMENT_DONE_AND_EXIT: case PARSE_ARGUMENT_DONE_AND_EXIT:
return 0; return 0;
@ -1076,7 +1076,6 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
} }
} }
// Write all output to disk.
for (const auto& pair : output_directories) { for (const auto& pair : output_directories) {
const std::string& location = pair.first; const std::string& location = pair.first;
GeneratorContextImpl* directory = pair.second.get(); GeneratorContextImpl* directory = pair.second.get();
@ -1151,7 +1150,6 @@ int CommandLineInterface::Run(int argc, const char* const argv[]) {
// Do not add a default case. // Do not add a default case.
} }
} }
return 0; return 0;
} }

@ -330,7 +330,6 @@ void FieldGenerator::GenerateCopyConstructorCode(io::Printer* printer) const {
} }
} }
void SetCommonOneofFieldVariables( void SetCommonOneofFieldVariables(
const FieldDescriptor* descriptor, const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables) { std::map<std::string, std::string>* variables) {

@ -208,7 +208,6 @@ class FieldGenerator {
virtual bool IsInlined() const { return false; } virtual bool IsInlined() const { return false; }
virtual ArenaDtorNeeds NeedsArenaDestructor() const { virtual ArenaDtorNeeds NeedsArenaDestructor() const {
return ArenaDtorNeeds::kNone; return ArenaDtorNeeds::kNone;
} }

@ -495,12 +495,10 @@ void FileGenerator::GenerateSourceDefaultInstance(int idx,
generator->GenerateInitDefaultSplitInstance(printer); generator->GenerateInitDefaultSplitInstance(printer);
format( format(
"} {}\n" "} {}\n"
" ~$1$() {}\n"
" union {\n" " union {\n"
" $2$ _instance;\n" " $1$ _instance;\n"
" };\n" " };\n"
"};\n", "};\n",
DefaultInstanceType(generator->descriptor_, options_, /*split=*/true),
StrCat(generator->classname_, "::Impl_::Split")); StrCat(generator->classname_, "::Impl_::Split"));
// NO_DESTROY is not necessary for correctness. The empty destructor is // NO_DESTROY is not necessary for correctness. The empty destructor is
// enough. However, the empty destructor fails to be elided in some // enough. However, the empty destructor fails to be elided in some
@ -508,7 +506,7 @@ void FileGenerator::GenerateSourceDefaultInstance(int idx,
// there just to improve performance and binary size in these builds. // there just to improve performance and binary size in these builds.
format( format(
"PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT " "PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT "
"PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 $1$ $2$;\n", "PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 const $1$ $2$;\n",
DefaultInstanceType(generator->descriptor_, options_, /*split=*/true), DefaultInstanceType(generator->descriptor_, options_, /*split=*/true),
DefaultInstanceName(generator->descriptor_, options_, /*split=*/true)); DefaultInstanceName(generator->descriptor_, options_, /*split=*/true));
} }
@ -999,7 +997,7 @@ class FileGenerator::ForwardDeclarations {
const Descriptor* class_desc = p.second; const Descriptor* class_desc = p.second;
format( format(
"struct $1$;\n" "struct $1$;\n"
"$dllexport_decl $extern $1$ $2$;\n", "$dllexport_decl $extern const $1$ $2$;\n",
DefaultInstanceType(class_desc, options, /*split=*/true), DefaultInstanceType(class_desc, options, /*split=*/true),
DefaultInstanceName(class_desc, options, /*split=*/true)); DefaultInstanceName(class_desc, options, /*split=*/true));
} }

@ -176,7 +176,6 @@ static const char* const kKeywordList[] = {
#endif // !PROTOBUF_FUTURE_BREAKING_CHANGES #endif // !PROTOBUF_FUTURE_BREAKING_CHANGES
}; };
static std::unordered_set<std::string>* MakeKeywordsMap() { static std::unordered_set<std::string>* MakeKeywordsMap() {
auto* result = new std::unordered_set<std::string>(); auto* result = new std::unordered_set<std::string>();
for (const auto keyword : kKeywordList) { for (const auto keyword : kKeywordList) {
@ -525,7 +524,6 @@ std::string FieldName(const FieldDescriptor* field) {
return result; return result;
} }
std::string FieldMemberName(const FieldDescriptor* field, bool split) { std::string FieldMemberName(const FieldDescriptor* field, bool split) {
StringPiece prefix = StringPiece prefix =
IsMapEntryMessage(field->containing_type()) ? "" : "_impl_."; IsMapEntryMessage(field->containing_type()) ? "" : "_impl_.";
@ -876,8 +874,6 @@ std::string SafeFunctionName(const Descriptor* descriptor,
bool IsProfileDriven(const Options& options) { bool IsProfileDriven(const Options& options) {
return options.access_info_map != nullptr; return options.access_info_map != nullptr;
} }
bool IsStringInlined(const FieldDescriptor* descriptor, bool IsStringInlined(const FieldDescriptor* descriptor,
const Options& options) { const Options& options) {
(void)descriptor; (void)descriptor;

@ -828,7 +828,6 @@ void MessageGenerator::GenerateFieldAccessorDeclarations(io::Printer* printer) {
// Generate type-specific accessor declarations. // Generate type-specific accessor declarations.
field_generators_.get(field).GenerateAccessorDeclarations(printer); field_generators_.get(field).GenerateAccessorDeclarations(printer);
format("\n"); format("\n");
} }
@ -1238,41 +1237,41 @@ void MessageGenerator::GenerateFieldAccessorDefinitions(io::Printer* printer) {
Formatter::SaveState saver(&format); Formatter::SaveState saver(&format);
format.AddMap(vars); format.AddMap(vars);
// Generate has_$name$() or $name$_size().
if (field->is_repeated()) { // Generate has_$name$() or $name$_size().
if (IsFieldStripped(field, options_)) { if (field->is_repeated()) {
format( if (IsFieldStripped(field, options_)) {
"inline int $classname$::$name$_size() const { " format(
"__builtin_trap(); }\n"); "inline int $classname$::$name$_size() const { "
} else { "__builtin_trap(); }\n");
format(
"inline int $classname$::_internal_$name$_size() const {\n"
" return $field$$1$.size();\n"
"}\n"
"inline int $classname$::$name$_size() const {\n"
"$annotate_size$"
" return _internal_$name$_size();\n"
"}\n",
IsImplicitWeakField(field, options_, scc_analyzer_) &&
field->message_type()
? ".weak"
: "");
}
} else if (field->real_containing_oneof()) {
format.Set("field_name", UnderscoresToCamelCase(field->name(), true));
format.Set("oneof_name", field->containing_oneof()->name());
format.Set("oneof_index",
StrCat(field->containing_oneof()->index()));
GenerateOneofMemberHasBits(field, format);
} else { } else {
// Singular field. format(
GenerateSingularFieldHasBits(field, format); "inline int $classname$::_internal_$name$_size() const {\n"
" return $field$$1$.size();\n"
"}\n"
"inline int $classname$::$name$_size() const {\n"
"$annotate_size$"
" return _internal_$name$_size();\n"
"}\n",
IsImplicitWeakField(field, options_, scc_analyzer_) &&
field->message_type()
? ".weak"
: "");
} }
} else if (field->real_containing_oneof()) {
format.Set("field_name", UnderscoresToCamelCase(field->name(), true));
format.Set("oneof_name", field->containing_oneof()->name());
format.Set("oneof_index",
StrCat(field->containing_oneof()->index()));
GenerateOneofMemberHasBits(field, format);
} else {
// Singular field.
GenerateSingularFieldHasBits(field, format);
}
if (!IsCrossFileMaybeMap(field)) { if (!IsCrossFileMaybeMap(field)) {
GenerateFieldClear(field, true, format); GenerateFieldClear(field, true, format);
} }
// Generate type-specific accessors. // Generate type-specific accessors.
if (!IsFieldStripped(field, options_)) { if (!IsFieldStripped(field, options_)) {
field_generators_.get(field).GenerateInlineAccessorDefinitions(printer); field_generators_.get(field).GenerateInlineAccessorDefinitions(printer);
@ -1760,7 +1759,7 @@ void MessageGenerator::GenerateClassDefinition(io::Printer* printer) {
format( format(
"private:\n" "private:\n"
"inline bool IsSplitMessageDefault() const {\n" "inline bool IsSplitMessageDefault() const {\n"
" return $split$ == reinterpret_cast<Impl_::Split*>(&$1$);\n" " return $split$ == reinterpret_cast<const Impl_::Split*>(&$1$);\n"
"}\n" "}\n"
"PROTOBUF_NOINLINE void PrepareSplitMessageForWrite();\n" "PROTOBUF_NOINLINE void PrepareSplitMessageForWrite();\n"
"public:\n", "public:\n",
@ -1928,6 +1927,8 @@ void MessageGenerator::GenerateClassDefinition(io::Printer* printer) {
" typedef void InternalArenaConstructable_;\n" " typedef void InternalArenaConstructable_;\n"
" typedef void DestructorSkippable_;\n" " typedef void DestructorSkippable_;\n"
"};\n" "};\n"
"static_assert(std::is_trivially_copy_constructible<Split>::value);\n"
"static_assert(std::is_trivially_destructible<Split>::value);\n"
"Split* _split_;\n"); "Split* _split_;\n");
} }
@ -2421,8 +2422,15 @@ void MessageGenerator::GenerateSharedConstructorCode(io::Printer* printer) {
} }
if (ShouldSplit(descriptor_, options_)) { if (ShouldSplit(descriptor_, options_)) {
put_sep(); put_sep();
format("decltype($split$){reinterpret_cast<Impl_::Split*>(&$1$)}", // We can't assign the default split to this->split without the const_cast
DefaultInstanceName(descriptor_, options_, /*split=*/true)); // because the former is a const. The const_cast is safe because we don't
// intend to modify the default split through this pointer, and we also
// expect the default split to be in the rodata section which is protected
// from mutation.
format(
"decltype($split$){const_cast<Impl_::Split*>"
"(reinterpret_cast<const Impl_::Split*>(&$1$))}",
DefaultInstanceName(descriptor_, options_, /*split=*/true));
} }
for (auto oneof : OneOfRange(descriptor_)) { for (auto oneof : OneOfRange(descriptor_)) {
put_sep(); put_sep();
@ -2681,7 +2689,7 @@ void MessageGenerator::GenerateConstexprConstructor(io::Printer* printer) {
} }
if (ShouldSplit(descriptor_, options_)) { if (ShouldSplit(descriptor_, options_)) {
put_sep(); put_sep();
format("/*decltype($split$)*/&$1$._instance", format("/*decltype($split$)*/const_cast<Impl_::Split*>(&$1$._instance)",
DefaultInstanceName(descriptor_, options_, /*split=*/true)); DefaultInstanceName(descriptor_, options_, /*split=*/true));
} }
@ -2866,8 +2874,10 @@ void MessageGenerator::GenerateStructors(io::Printer* printer) {
} }
if (ShouldSplit(descriptor_, options_)) { if (ShouldSplit(descriptor_, options_)) {
put_sep(); put_sep();
format("decltype($split$){reinterpret_cast<Impl_::Split*>(&$1$)}", format(
DefaultInstanceName(descriptor_, options_, /*split=*/true)); "decltype($split$){const_cast<Impl_::Split*>"
"(reinterpret_cast<const Impl_::Split*>(&$1$))}",
DefaultInstanceName(descriptor_, options_, /*split=*/true));
} }
for (auto oneof : OneOfRange(descriptor_)) { for (auto oneof : OneOfRange(descriptor_)) {
put_sep(); put_sep();

@ -83,7 +83,8 @@ int TagSize(uint32_t field_number) {
return 2; return 2;
} }
void PopulateFastFieldEntry(const TailCallTableInfo::FieldEntryInfo& entry, void PopulateFastFieldEntry(const Descriptor* descriptor,
const TailCallTableInfo::FieldEntryInfo& entry,
const Options& options, const Options& options,
TailCallTableInfo::FastFieldInfo& info); TailCallTableInfo::FastFieldInfo& info);
@ -158,6 +159,7 @@ bool IsFieldEligibleForFastParsing(
} }
std::vector<TailCallTableInfo::FastFieldInfo> SplitFastFieldsForSize( std::vector<TailCallTableInfo::FastFieldInfo> SplitFastFieldsForSize(
const Descriptor* descriptor,
const std::vector<TailCallTableInfo::FieldEntryInfo>& field_entries, const std::vector<TailCallTableInfo::FieldEntryInfo>& field_entries,
int table_size_log2, const Options& options, int table_size_log2, const Options& options,
MessageSCCAnalyzer* scc_analyzer) { MessageSCCAnalyzer* scc_analyzer) {
@ -200,7 +202,7 @@ std::vector<TailCallTableInfo::FastFieldInfo> SplitFastFieldsForSize(
GOOGLE_CHECK(info.func_name.empty()) << info.func_name; GOOGLE_CHECK(info.func_name.empty()) << info.func_name;
info.field = field; info.field = field;
info.coded_tag = tag; info.coded_tag = tag;
PopulateFastFieldEntry(entry, options, info); PopulateFastFieldEntry(descriptor, entry, options, info);
// If this field does not have presence, then it can set an out-of-bounds // If this field does not have presence, then it can set an out-of-bounds
// bit (tailcall parsing uses a uint64_t for hasbits, but only stores 32). // bit (tailcall parsing uses a uint64_t for hasbits, but only stores 32).
info.hasbit_idx = HasHasbit(field) ? entry.hasbit_idx : 63; info.hasbit_idx = HasHasbit(field) ? entry.hasbit_idx : 63;
@ -412,8 +414,8 @@ TailCallTableInfo::TailCallTableInfo(
int num_fast_fields = -1; int num_fast_fields = -1;
for (int try_size_log2 : {0, 1, 2, 3, 4, 5}) { for (int try_size_log2 : {0, 1, 2, 3, 4, 5}) {
size_t try_size = 1 << try_size_log2; size_t try_size = 1 << try_size_log2;
auto split_fields = SplitFastFieldsForSize(field_entries, try_size_log2, auto split_fields = SplitFastFieldsForSize(
options, scc_analyzer); descriptor, field_entries, try_size_log2, options, scc_analyzer);
GOOGLE_CHECK_EQ(split_fields.size(), try_size); GOOGLE_CHECK_EQ(split_fields.size(), try_size);
int try_num_fast_fields = 0; int try_num_fast_fields = 0;
for (const auto& info : split_fields) { for (const auto& info : split_fields) {
@ -1667,11 +1669,12 @@ void ParseFunctionGenerator::GenerateFieldSwitch(
namespace { namespace {
void PopulateFastFieldEntry(const TailCallTableInfo::FieldEntryInfo& entry, void PopulateFastFieldEntry(const Descriptor* descriptor,
const TailCallTableInfo::FieldEntryInfo& entry,
const Options& options, const Options& options,
TailCallTableInfo::FastFieldInfo& info) { TailCallTableInfo::FastFieldInfo& info) {
const FieldDescriptor* field = entry.field; const FieldDescriptor* field = entry.field;
std::string name = "::_pbi::TcParser::Fast"; std::string name;
uint8_t aux_idx = static_cast<uint8_t>(entry.aux_idx); uint8_t aux_idx = static_cast<uint8_t>(entry.aux_idx);
switch (field->type()) { switch (field->type()) {
@ -1784,7 +1787,36 @@ void PopulateFastFieldEntry(const TailCallTableInfo::FieldEntryInfo& entry,
// Append the tag length. Fast parsing only handles 1- or 2-byte tags. // Append the tag length. Fast parsing only handles 1- or 2-byte tags.
name.append(TagSize(field->number()) == 1 ? "1" : "2"); name.append(TagSize(field->number()) == 1 ? "1" : "2");
info.func_name = std::move(name); if (name == "V8S1") {
info.func_name = StrCat(
"::_pbi::TcParser::SingularVarintNoZag1<bool, offsetof(", //
ClassName(descriptor), //
", ", //
FieldMemberName(field, /*split=*/false), //
"), ", //
HasHasbit(field) ? entry.hasbit_idx : 63, //
">()");
} else if (name == "V32S1") {
info.func_name = StrCat(
"::_pbi::TcParser::SingularVarintNoZag1<uint32_t, offsetof(", //
ClassName(descriptor), //
", ", //
FieldMemberName(field, /*split=*/false), //
"), ", //
HasHasbit(field) ? entry.hasbit_idx : 63, //
">()");
} else if (name == "V64S1") {
info.func_name = StrCat(
"::_pbi::TcParser::SingularVarintNoZag1<uint64_t, offsetof(", //
ClassName(descriptor), //
", ", //
FieldMemberName(field, /*split=*/false), //
"), ", //
HasHasbit(field) ? entry.hasbit_idx : 63, //
">()");
} else {
info.func_name = StrCat("::_pbi::TcParser::Fast", name);
}
info.aux_idx = aux_idx; info.aux_idx = aux_idx;
} }

@ -379,15 +379,30 @@ std::string GetFieldConstantName(const FieldDescriptor* field) {
} }
std::string GetPropertyName(const FieldDescriptor* descriptor) { std::string GetPropertyName(const FieldDescriptor* descriptor) {
// Names of members declared or overridden in the message.
static const auto& reserved_member_names = *new std::unordered_set<std::string>({
"Types",
"Descriptor",
"Equals",
"ToString",
"GetHashCode",
"WriteTo",
"Clone",
"CalculateSize",
"MergeFrom",
"OnConstruction",
"Parser"
});
// TODO(jtattermusch): consider introducing csharp_property_name field option // TODO(jtattermusch): consider introducing csharp_property_name field option
std::string property_name = UnderscoresToPascalCase(GetFieldName(descriptor)); std::string property_name = UnderscoresToPascalCase(GetFieldName(descriptor));
// Avoid either our own type name or reserved names. Note that not all names // Avoid either our own type name or reserved names.
// are reserved - a field called to_string, write_to etc would still cause a problem.
// There are various ways of ending up with naming collisions, but we try to avoid obvious // There are various ways of ending up with naming collisions, but we try to avoid obvious
// ones. // ones. In particular, we avoid the names of all the members we generate.
// Note that we *don't* add an underscore for MemberwiseClone or GetType. Those generate
// warnings, but not errors; changing the name now could be a breaking change.
if (property_name == descriptor->containing_type()->name() if (property_name == descriptor->containing_type()->name()
|| property_name == "Types" || reserved_member_names.find(property_name) != reserved_member_names.end()) {
|| property_name == "Descriptor") {
property_name += "_"; property_name += "_";
} }
return property_name; return property_name;

@ -281,6 +281,18 @@ void ImmutableEnumFieldGenerator::GenerateKotlinDslMembers(
" $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n"
" }\n"); " }\n");
if (SupportUnknownEnumValue(descriptor_->file())) {
printer->Print(
variables_,
"$kt_deprecation$ var $kt_name$Value: kotlin.Int\n"
" @JvmName(\"${$get$kt_capitalized_name$Value$}$\")\n"
" get() = $kt_dsl_builder$.${$get$capitalized_name$Value$}$()\n"
" @JvmName(\"${$set$kt_capitalized_name$Value$}$\")\n"
" set(value) {\n"
" $kt_dsl_builder$.${$set$capitalized_name$Value$}$(value)\n"
" }\n");
}
WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, WriteFieldAccessorDocComment(printer, descriptor_, CLEARER,
/* builder */ false, /* kdoc */ true); /* builder */ false, /* kdoc */ true);
printer->Print(variables_, printer->Print(variables_,

@ -296,6 +296,18 @@ void ImmutableEnumFieldLiteGenerator::GenerateKotlinDslMembers(
" $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n" " $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\n"
" }\n"); " }\n");
if (SupportUnknownEnumValue(descriptor_->file())) {
printer->Print(
variables_,
"$kt_deprecation$ var $kt_name$Value: kotlin.Int\n"
" @JvmName(\"${$get$kt_capitalized_name$Value$}$\")\n"
" get() = $kt_dsl_builder$.${$get$capitalized_name$Value$}$()\n"
" @JvmName(\"${$set$kt_capitalized_name$Value$}$\")\n"
" set(value) {\n"
" $kt_dsl_builder$.${$set$capitalized_name$Value$}$(value)\n"
" }\n");
}
WriteFieldAccessorDocComment(printer, descriptor_, CLEARER, WriteFieldAccessorDocComment(printer, descriptor_, CLEARER,
/* builder */ false, /* kdoc */ true); /* builder */ false, /* kdoc */ true);
printer->Print(variables_, printer->Print(variables_,

@ -63,7 +63,12 @@ class PROTOC_EXPORT JavaGenerator : public CodeGenerator {
uint64_t GetSupportedFeatures() const override; uint64_t GetSupportedFeatures() const override;
void set_opensource_runtime(bool opensource) {
opensource_runtime_ = opensource;
}
private: private:
bool opensource_runtime_ = true;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JavaGenerator); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JavaGenerator);
}; };

@ -32,6 +32,7 @@
#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_SERIALIZATION_H__ #define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_SERIALIZATION_H__
#include <algorithm> #include <algorithm>
#include <cstddef>
#include <vector> #include <vector>
#include <google/protobuf/io/printer.h> #include <google/protobuf/io/printer.h>
@ -66,20 +67,31 @@ void GenerateSerializeFieldsAndExtensions(
std::sort(sorted_extensions.begin(), sorted_extensions.end(), std::sort(sorted_extensions.begin(), sorted_extensions.end(),
ExtensionRangeOrdering()); ExtensionRangeOrdering());
std::size_t range_idx = 0;
// Merge the fields and the extension ranges, both sorted by field number. // Merge the fields and the extension ranges, both sorted by field number.
for (int i = 0, j = 0; for (int i = 0; i < descriptor->field_count(); ++i) {
i < descriptor->field_count() || j < sorted_extensions.size();) { const FieldDescriptor* field = sorted_fields[i];
if (i == descriptor->field_count()) {
GenerateSerializeExtensionRange(printer, sorted_extensions[j++]); // Collapse all extension ranges up until the next field. This leads to
} else if (j == sorted_extensions.size()) { // shorter and more efficient codegen for messages containing a large
field_generators.get(sorted_fields[i++]) // number of extension ranges without fields in between them.
.GenerateSerializationCode(printer); const Descriptor::ExtensionRange* range = nullptr;
} else if (sorted_fields[i]->number() < sorted_extensions[j]->start) { while (range_idx < sorted_extensions.size() &&
field_generators.get(sorted_fields[i++]) sorted_extensions[range_idx]->end <= field->number()) {
.GenerateSerializationCode(printer); range = sorted_extensions[range_idx++];
} else { }
GenerateSerializeExtensionRange(printer, sorted_extensions[j++]);
if (range != nullptr) {
GenerateSerializeExtensionRange(printer, range);
} }
field_generators.get(field).GenerateSerializationCode(printer);
}
// After serializing all fields, serialize any remaining extensions via a
// single writeUntil call.
if (range_idx < sorted_extensions.size()) {
GenerateSerializeExtensionRange(printer, sorted_extensions.back());
} }
} }

@ -0,0 +1,124 @@
// 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.
#include <cstddef>
#include <string>
#include <utility>
#include <vector>
#include <google/protobuf/stubs/logging.h>
#include <google/protobuf/stubs/common.h>
#include <google/protobuf/testing/file.h>
#include <google/protobuf/testing/file.h>
#include <gmock/gmock.h>
#include <google/protobuf/testing/googletest.h>
#include <gtest/gtest.h>
#include <google/protobuf/compiler/command_line_interface.h>
#include <google/protobuf/compiler/java/generator.h>
#include <google/protobuf/test_util2.h>
namespace google {
namespace protobuf {
namespace compiler {
namespace java {
namespace {
using ::testing::ElementsAre;
// Generates Java code for the specified Java proto, returning the compiler's
// exit status.
int CompileJavaProto(std::string proto_file_name) {
JavaGenerator java_generator;
CommandLineInterface cli;
cli.RegisterGenerator("--java_out", &java_generator, /*help_text=*/"");
std::string proto_path = StrCat(
"--proto_path=",
TestUtil::GetTestDataPath("third_party/protobuf/compiler/java"));
std::string java_out = StrCat("--java_out=", TestTempDir());
const char* argv[] = {
"protoc",
proto_path.c_str(),
java_out.c_str(),
proto_file_name.c_str(),
};
// Open-source codebase does not support ABSL_ARRAYSIZE.
return cli.Run(sizeof(argv) / sizeof(*argv), argv);
}
TEST(MessageSerializationTest, CollapseAdjacentExtensionRanges) {
GOOGLE_CHECK_EQ(CompileJavaProto("message_serialization_unittest.proto"), 0);
std::string java_source;
GOOGLE_CHECK_OK(File::GetContents(
// Open-source codebase does not support file::JoinPath, so we manually
// concatenate instead.
StrCat(TestTempDir(),
"/TestMessageWithManyExtensionRanges.java"),
&java_source, true));
// Open-source codebase does not support constexpr StringPiece.
static constexpr const char kWriteUntilCall[] = "extensionWriter.writeUntil(";
std::vector<std::string> range_ends;
// Open-source codebase does not have Split overload taking a single
// char delimiter.
//
// NOLINTNEXTLINE(abseil-faster-strsplit-delimiter)
for (const auto& line : Split(java_source, "\n")) {
// Extract end position from writeUntil call. (Open-source codebase does not
// support RE2.)
std::size_t write_until_pos = line.find(kWriteUntilCall);
if (write_until_pos == std::string::npos) {
continue;
}
write_until_pos += (sizeof(kWriteUntilCall) - 1);
std::size_t comma_pos = line.find(',', write_until_pos);
if (comma_pos == std::string::npos) {
continue;
}
range_ends.push_back(
std::string(line.substr(write_until_pos, comma_pos - write_until_pos)));
}
EXPECT_THAT(range_ends, ElementsAre("3", "13", "43"));
}
} // namespace
} // namespace java
} // namespace compiler
} // namespace protobuf
} // namespace google

@ -0,0 +1,56 @@
// 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.
syntax = "proto2";
package protobuf_unittest;
option java_multiple_files = true;
option java_package = "";
// Each batch of extension ranges not separated by a non-extension field should
// be serialized using a single ExtensionWriter#writeUntil call.
message TestMessageWithManyExtensionRanges {
// First extension range: ends at field number 3 (exclusive)
extensions 1 to 2;
optional int32 foo = 3;
optional int32 bar = 5;
// Second extension range: ends at field number 13 (exclusive)
extensions 6;
extensions 8;
extensions 10 to 12;
optional int32 baz = 23;
// Third extension range: ends at field number 43 (exclusive)
extensions 42;
}

@ -66,6 +66,10 @@ int ProtobufMain(int argc, char* argv[]) {
cli.RegisterGenerator("--java_out", "--java_opt", &java_generator, cli.RegisterGenerator("--java_out", "--java_opt", &java_generator,
"Generate Java source file."); "Generate Java source file.");
#ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE
java_generator.set_opensource_runtime(true);
#endif
// Proto2 Kotlin // Proto2 Kotlin
java::KotlinGenerator kt_generator; java::KotlinGenerator kt_generator;
cli.RegisterGenerator("--kotlin_out", "--kotlin_opt", &kt_generator, cli.RegisterGenerator("--kotlin_out", "--kotlin_opt", &kt_generator,
@ -76,6 +80,11 @@ int ProtobufMain(int argc, char* argv[]) {
python::Generator py_generator; python::Generator py_generator;
cli.RegisterGenerator("--python_out", "--python_opt", &py_generator, cli.RegisterGenerator("--python_out", "--python_opt", &py_generator,
"Generate Python source file."); "Generate Python source file.");
#ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE
py_generator.set_opensource_runtime(true);
#endif
// Python pyi // Python pyi
python::PyiGenerator pyi_generator; python::PyiGenerator pyi_generator;
cli.RegisterGenerator("--pyi_out", &pyi_generator, cli.RegisterGenerator("--pyi_out", &pyi_generator,

@ -388,7 +388,7 @@ void RepeatedFieldGenerator::GeneratePropertyDeclaration(
"$comments$" "$comments$"
"$array_comment$" "$array_comment$"
"@property(nonatomic, readwrite, strong, null_resettable) $array_property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n" "@property(nonatomic, readwrite, strong, null_resettable) $array_property_type$ *$name$$storage_attribute$$deprecated_attribute$;\n"
"/** The number of items in @c $name$ without causing the array to be created. */\n" "/** The number of items in @c $name$ without causing the container to be created. */\n"
"@property(nonatomic, readonly) NSUInteger $name$_Count$deprecated_attribute$;\n"); "@property(nonatomic, readonly) NSUInteger $name$_Count$deprecated_attribute$;\n");
if (IsInitName(variables_.find("name")->second)) { if (IsInitName(variables_.find("name")->second)) {
// If property name starts with init we need to annotate it to get past ARC. // If property name starts with init we need to annotate it to get past ARC.

@ -48,29 +48,29 @@ const std::string kDescriptorMetadataFile =
const std::string kDescriptorDirName = "Google/Protobuf/Internal"; const std::string kDescriptorDirName = "Google/Protobuf/Internal";
const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal"; const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
const char* const kReservedNames[] = { const char* const kReservedNames[] = {
"abstract", "and", "array", "as", "break", "abstract", "and", "array", "as", "break",
"callable", "case", "catch", "class", "clone", "callable", "case", "catch", "class", "clone",
"const", "continue", "declare", "default", "die", "const", "continue", "declare", "default", "die",
"do", "echo", "else", "elseif", "empty", "do", "echo", "else", "elseif", "empty",
"enddeclare", "endfor", "endforeach", "endif", "endswitch", "enddeclare", "endfor", "endforeach", "endif", "endswitch",
"endwhile", "eval", "exit", "extends", "final", "endwhile", "eval", "exit", "extends", "final",
"finally", "fn", "for", "foreach", "function", "finally", "fn", "for", "foreach", "function",
"global", "goto", "if", "implements", "include", "global", "goto", "if", "implements", "include",
"include_once", "instanceof", "insteadof", "interface", "isset", "include_once", "instanceof", "insteadof", "interface", "isset",
"list", "match", "namespace", "new", "or", "list", "match", "namespace", "new", "or",
"parent", "print", "private", "protected", "public", "parent", "print", "private", "protected", "public",
"require", "require_once", "return", "self", "static", "readonly", "require", "require_once", "return", "self",
"switch", "throw", "trait", "try", "unset", "static", "switch", "throw", "trait", "try",
"use", "var", "while", "xor", "yield", "unset", "use", "var", "while", "xor",
"int", "float", "bool", "string", "true", "yield", "int", "float", "bool", "string",
"false", "null", "void", "iterable"}; "true", "false", "null", "void", "iterable"};
const char* const kValidConstantNames[] = { const char* const kValidConstantNames[] = {
"int", "float", "bool", "string", "true", "int", "float", "bool", "string", "true",
"false", "null", "void", "iterable", "parent", "false", "null", "void", "iterable", "parent",
"self" "self", "readonly"
}; };
const int kReservedNamesSize = 79; const int kReservedNamesSize = 80;
const int kValidConstantNamesSize = 11; const int kValidConstantNamesSize = 12;
const int kFieldSetter = 1; const int kFieldSetter = 1;
const int kFieldGetter = 2; const int kFieldGetter = 2;
const int kFieldProperty = 3; const int kFieldProperty = 3;
@ -407,6 +407,29 @@ std::string GeneratedClassFileName(const DescriptorType* desc,
return result + ".php"; return result + ".php";
} }
template <typename DescriptorType>
std::string LegacyGeneratedClassFileName(const DescriptorType* desc,
const Options& options) {
std::string result = LegacyFullClassName(desc, options);
for (int i = 0; i < result.size(); i++) {
if (result[i] == '\\') {
result[i] = '/';
}
}
return result + ".php";
}
template <typename DescriptorType>
std::string LegacyReadOnlyGeneratedClassFileName(const DescriptorType* desc,
const Options& options) {
std::string php_namespace = RootPhpNamespace(desc, options);
if (!php_namespace.empty()) {
return php_namespace + "/" + desc->name() + ".php";
}
return desc->name() + ".php";
}
std::string GeneratedServiceFileName(const ServiceDescriptor* service, std::string GeneratedServiceFileName(const ServiceDescriptor* service,
const Options& options) { const Options& options) {
std::string result = FullClassName(service, options) + "Interface"; std::string result = FullClassName(service, options) + "Interface";
@ -1252,6 +1275,70 @@ void GenerateMetadataFile(const FileDescriptor* file, const Options& options,
printer.Print("}\n\n"); printer.Print("}\n\n");
} }
template <typename DescriptorType>
void LegacyGenerateClassFile(const FileDescriptor* file,
const DescriptorType* desc, const Options& options,
GeneratorContext* generator_context) {
std::string filename = LegacyGeneratedClassFileName(desc, options);
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(filename));
io::Printer printer(output.get(), '^');
GenerateHead(file, &printer);
std::string php_namespace = RootPhpNamespace(desc, options);
if (!php_namespace.empty()) {
printer.Print(
"namespace ^name^;\n\n",
"name", php_namespace);
}
std::string newname = FullClassName(desc, options);
printer.Print("if (false) {\n");
Indent(&printer);
printer.Print("/**\n");
printer.Print(" * This class is deprecated. Use ^new^ instead.\n",
"new", newname);
printer.Print(" * @deprecated\n");
printer.Print(" */\n");
printer.Print("class ^old^ {}\n",
"old", LegacyGeneratedClassName(desc));
Outdent(&printer);
printer.Print("}\n");
printer.Print("class_exists(^new^::class);\n",
"new", GeneratedClassNameImpl(desc));
printer.Print("@trigger_error('^old^ is deprecated and will be removed in "
"the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n",
"old", LegacyFullClassName(desc, options),
"fullname", newname);
}
template <typename DescriptorType>
void LegacyReadOnlyGenerateClassFile(const FileDescriptor* file,
const DescriptorType* desc, const Options& options,
GeneratorContext* generator_context) {
std::string filename = LegacyReadOnlyGeneratedClassFileName(desc, options);
std::unique_ptr<io::ZeroCopyOutputStream> output(
generator_context->Open(filename));
io::Printer printer(output.get(), '^');
GenerateHead(file, &printer);
std::string php_namespace = RootPhpNamespace(desc, options);
if (!php_namespace.empty()) {
printer.Print(
"namespace ^name^;\n\n",
"name", php_namespace);
}
std::string newname = FullClassName(desc, options);
printer.Print("class_exists(^new^::class); // autoload the new class, which "
"will also create an alias to the deprecated class\n",
"new", GeneratedClassNameImpl(desc));
printer.Print("@trigger_error(__NAMESPACE__ . '\\^old^ is deprecated and will be removed in "
"the next major release. Use ^fullname^ instead', E_USER_DEPRECATED);\n\n",
"old", desc->name(),
"fullname", newname);
}
void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en, void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
const Options& options, const Options& options,
GeneratorContext* generator_context) { GeneratorContext* generator_context) {
@ -1372,6 +1459,19 @@ void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
"new", fullname, "new", fullname,
"old", LegacyFullClassName(en, options)); "old", LegacyFullClassName(en, options));
} }
// Write legacy file for backwards compatibility with "readonly" keywword
std::string lower = en->name();
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
if (lower == "readonly") {
printer.Print(
"// Adding a class alias for backwards compatibility with the \"readonly\" keyword.\n");
printer.Print(
"class_alias(^new^::class, __NAMESPACE__ . '\\^old^');\n\n",
"new", fullname,
"old", en->name());
LegacyReadOnlyGenerateClassFile(file, en, options, generator_context);
}
} }
void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message, void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
@ -1487,6 +1587,19 @@ void GenerateMessageFile(const FileDescriptor* file, const Descriptor* message,
"old", LegacyFullClassName(message, options)); "old", LegacyFullClassName(message, options));
} }
// Write legacy file for backwards compatibility with "readonly" keywword
std::string lower = message->name();
std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
if (lower == "readonly") {
printer.Print(
"// Adding a class alias for backwards compatibility with the \"readonly\" keyword.\n");
printer.Print(
"class_alias(^new^::class, __NAMESPACE__ . '\\^old^');\n\n",
"new", fullname,
"old", message->name());
LegacyReadOnlyGenerateClassFile(file, message, options, generator_context);
}
// Nested messages and enums. // Nested messages and enums.
for (int i = 0; i < message->nested_type_count(); i++) { for (int i = 0; i < message->nested_type_count(); i++) {
GenerateMessageFile(file, message->nested_type(i), options, GenerateMessageFile(file, message->nested_type(i), options,

@ -76,6 +76,10 @@ class PROTOC_EXPORT Generator : public CodeGenerator {
uint64_t GetSupportedFeatures() const override; uint64_t GetSupportedFeatures() const override;
void set_opensource_runtime(bool opensource) {
opensource_runtime_ = opensource;
}
private: private:
void PrintImports() const; void PrintImports() const;
void PrintFileDescriptor() const; void PrintFileDescriptor() const;
@ -172,6 +176,8 @@ class PROTOC_EXPORT Generator : public CodeGenerator {
mutable io::Printer* printer_; // Set in Generate(). Under mutex_. mutable io::Printer* printer_; // Set in Generate(). Under mutex_.
mutable bool pure_python_workable_; mutable bool pure_python_workable_;
bool opensource_runtime_ = true;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator);
}; };

@ -64,19 +64,17 @@ void PyiGenerator::PrintItemMap(
} }
template <typename DescriptorT> template <typename DescriptorT>
std::string PyiGenerator::ModuleLevelName( std::string PyiGenerator::ModuleLevelName(const DescriptorT& descriptor) const {
const DescriptorT& descriptor,
const std::map<std::string, std::string>& import_map) const {
std::string name = NamePrefixedWithNestedTypes(descriptor, "."); std::string name = NamePrefixedWithNestedTypes(descriptor, ".");
if (descriptor.file() != file_) { if (descriptor.file() != file_) {
std::string module_alias; std::string module_alias;
std::string filename = descriptor.file()->name(); std::string filename = descriptor.file()->name();
if (import_map.find(filename) == import_map.end()) { if (import_map_.find(filename) == import_map_.end()) {
std::string module_name = ModuleName(descriptor.file()->name()); std::string module_name = ModuleName(descriptor.file()->name());
std::vector<std::string> tokens = Split(module_name, "."); std::vector<std::string> tokens = Split(module_name, ".");
module_alias = "_" + tokens.back(); module_alias = "_" + tokens.back();
} else { } else {
module_alias = import_map.at(filename); module_alias = import_map_.at(filename);
} }
name = module_alias + "." + name; name = module_alias + "." + name;
} }
@ -156,7 +154,6 @@ void CheckImportModules(const Descriptor* descriptor,
void PyiGenerator::PrintImportForDescriptor( void PyiGenerator::PrintImportForDescriptor(
const FileDescriptor& desc, const FileDescriptor& desc,
std::map<std::string, std::string>* import_map,
std::set<std::string>* seen_aliases) const { std::set<std::string>* seen_aliases) const {
const std::string& filename = desc.name(); const std::string& filename = desc.name();
std::string module_name = StrippedModuleName(filename); std::string module_name = StrippedModuleName(filename);
@ -176,21 +173,20 @@ void PyiGenerator::PrintImportForDescriptor(
} }
printer_->Print("$statement$ as $alias$\n", "statement", printer_->Print("$statement$ as $alias$\n", "statement",
import_statement, "alias", alias); import_statement, "alias", alias);
(*import_map)[filename] = alias; import_map_[filename] = alias;
seen_aliases->insert(alias); seen_aliases->insert(alias);
} }
void PyiGenerator::PrintImports( void PyiGenerator::PrintImports(
std::map<std::string, std::string>* item_map, std::map<std::string, std::string>* item_map) const {
std::map<std::string, std::string>* import_map) const {
// Prints imported dependent _pb2 files. // Prints imported dependent _pb2 files.
std::set<std::string> seen_aliases; std::set<std::string> seen_aliases;
for (int i = 0; i < file_->dependency_count(); ++i) { for (int i = 0; i < file_->dependency_count(); ++i) {
const FileDescriptor* dep = file_->dependency(i); const FileDescriptor* dep = file_->dependency(i);
PrintImportForDescriptor(*dep, import_map, &seen_aliases); PrintImportForDescriptor(*dep, &seen_aliases);
for (int j = 0; j < dep->public_dependency_count(); ++j) { for (int j = 0; j < dep->public_dependency_count(); ++j) {
PrintImportForDescriptor( PrintImportForDescriptor(
*dep->public_dependency(j), import_map, &seen_aliases); *dep->public_dependency(j), &seen_aliases);
} }
} }
@ -277,7 +273,7 @@ void PyiGenerator::PrintImports(
const EnumDescriptor* enum_descriptor = public_dep->enum_type(i); const EnumDescriptor* enum_descriptor = public_dep->enum_type(i);
for (int j = 0; j < enum_descriptor->value_count(); ++j) { for (int j = 0; j < enum_descriptor->value_count(); ++j) {
(*item_map)[enum_descriptor->value(j)->name()] = (*item_map)[enum_descriptor->value(j)->name()] =
ModuleLevelName(*enum_descriptor, *import_map); ModuleLevelName(*enum_descriptor);
} }
} }
// Top level extensions for public imports // Top level extensions for public imports
@ -296,10 +292,9 @@ void PyiGenerator::PrintEnum(const EnumDescriptor& enum_descriptor) const {
// Adds enum value to item map which will be ordered and printed later. // Adds enum value to item map which will be ordered and printed later.
void PyiGenerator::AddEnumValue( void PyiGenerator::AddEnumValue(
const EnumDescriptor& enum_descriptor, const EnumDescriptor& enum_descriptor,
std::map<std::string, std::string>* item_map, std::map<std::string, std::string>* item_map) const {
const std::map<std::string, std::string>& import_map) const {
// enum values // enum values
std::string module_enum_name = ModuleLevelName(enum_descriptor, import_map); std::string module_enum_name = ModuleLevelName(enum_descriptor);
for (int j = 0; j < enum_descriptor.value_count(); ++j) { for (int j = 0; j < enum_descriptor.value_count(); ++j) {
const EnumValueDescriptor* value_descriptor = enum_descriptor.value(j); const EnumValueDescriptor* value_descriptor = enum_descriptor.value(j);
(*item_map)[value_descriptor->name()] = module_enum_name; (*item_map)[value_descriptor->name()] = module_enum_name;
@ -331,8 +326,7 @@ void PyiGenerator::AddExtensions(
// Returns the string format of a field's cpp_type // Returns the string format of a field's cpp_type
std::string PyiGenerator::GetFieldType( std::string PyiGenerator::GetFieldType(
const FieldDescriptor& field_des, const Descriptor& containing_des, const FieldDescriptor& field_des, const Descriptor& containing_des) const {
const std::map<std::string, std::string>& import_map) const {
switch (field_des.cpp_type()) { switch (field_des.cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32: case FieldDescriptor::CPPTYPE_INT32:
case FieldDescriptor::CPPTYPE_UINT32: case FieldDescriptor::CPPTYPE_UINT32:
@ -345,7 +339,7 @@ std::string PyiGenerator::GetFieldType(
case FieldDescriptor::CPPTYPE_BOOL: case FieldDescriptor::CPPTYPE_BOOL:
return "bool"; return "bool";
case FieldDescriptor::CPPTYPE_ENUM: case FieldDescriptor::CPPTYPE_ENUM:
return ModuleLevelName(*field_des.enum_type(), import_map); return ModuleLevelName(*field_des.enum_type());
case FieldDescriptor::CPPTYPE_STRING: case FieldDescriptor::CPPTYPE_STRING:
if (field_des.type() == FieldDescriptor::TYPE_STRING) { if (field_des.type() == FieldDescriptor::TYPE_STRING) {
return "str"; return "str";
@ -356,7 +350,7 @@ std::string PyiGenerator::GetFieldType(
// If the field is inside a nested message and the nested message has the // If the field is inside a nested message and the nested message has the
// same name as a top-level message, then we need to prefix the field type // same name as a top-level message, then we need to prefix the field type
// with the module name for disambiguation. // with the module name for disambiguation.
std::string name = ModuleLevelName(*field_des.message_type(), import_map); std::string name = ModuleLevelName(*field_des.message_type());
if ((containing_des.containing_type() != nullptr && if ((containing_des.containing_type() != nullptr &&
name == containing_des.name())) { name == containing_des.name())) {
std::string module = ModuleName(field_des.file()->name()); std::string module = ModuleName(field_des.file()->name());
@ -371,8 +365,7 @@ std::string PyiGenerator::GetFieldType(
} }
void PyiGenerator::PrintMessage( void PyiGenerator::PrintMessage(
const Descriptor& message_descriptor, bool is_nested, const Descriptor& message_descriptor, bool is_nested) const {
const std::map<std::string, std::string>& import_map) const {
if (!is_nested) { if (!is_nested) {
printer_->Print("\n"); printer_->Print("\n");
} }
@ -431,7 +424,7 @@ void PyiGenerator::PrintMessage(
for (const auto& entry : nested_enums) { for (const auto& entry : nested_enums) {
PrintEnum(*entry); PrintEnum(*entry);
// Adds enum value to item_map which will be ordered and printed later // Adds enum value to item_map which will be ordered and printed later
AddEnumValue(*entry, &item_map, import_map); AddEnumValue(*entry, &item_map);
} }
// Prints nested messages // Prints nested messages
@ -444,7 +437,7 @@ void PyiGenerator::PrintMessage(
SortByName<Descriptor>()); SortByName<Descriptor>());
for (const auto& entry : nested_messages) { for (const auto& entry : nested_messages) {
PrintMessage(*entry, true, import_map); PrintMessage(*entry, true);
} }
// Adds extensions to item_map which will be ordered and printed later // Adds extensions to item_map which will be ordered and printed later
@ -465,16 +458,16 @@ void PyiGenerator::PrintMessage(
field_type = (value_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE field_type = (value_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
? "_containers.MessageMap[" ? "_containers.MessageMap["
: "_containers.ScalarMap["); : "_containers.ScalarMap[");
field_type += GetFieldType(*key_des, message_descriptor, import_map); field_type += GetFieldType(*key_des, message_descriptor);
field_type += ", "; field_type += ", ";
field_type += GetFieldType(*value_des, message_descriptor, import_map); field_type += GetFieldType(*value_des, message_descriptor);
} else { } else {
if (field_des.is_repeated()) { if (field_des.is_repeated()) {
field_type = (field_des.cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE field_type = (field_des.cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
? "_containers.RepeatedCompositeFieldContainer[" ? "_containers.RepeatedCompositeFieldContainer["
: "_containers.RepeatedScalarFieldContainer["); : "_containers.RepeatedScalarFieldContainer[");
} }
field_type += GetFieldType(field_des, message_descriptor, import_map); field_type += GetFieldType(field_des, message_descriptor);
} }
if (field_des.is_repeated()) { if (field_des.is_repeated()) {
@ -513,9 +506,9 @@ void PyiGenerator::PrintMessage(
const Descriptor* map_entry = field_des->message_type(); const Descriptor* map_entry = field_des->message_type();
printer_->Print( printer_->Print(
"_Mapping[$key_type$, $value_type$]", "key_type", "_Mapping[$key_type$, $value_type$]", "key_type",
GetFieldType(*map_entry->field(0), message_descriptor, import_map), GetFieldType(*map_entry->field(0), message_descriptor),
"value_type", "value_type",
GetFieldType(*map_entry->field(1), message_descriptor, import_map)); GetFieldType(*map_entry->field(1), message_descriptor));
} else { } else {
if (field_des->is_repeated()) { if (field_des->is_repeated()) {
printer_->Print("_Iterable["); printer_->Print("_Iterable[");
@ -523,15 +516,15 @@ void PyiGenerator::PrintMessage(
if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer_->Print( printer_->Print(
"_Union[$type_name$, _Mapping]", "type_name", "_Union[$type_name$, _Mapping]", "type_name",
GetFieldType(*field_des, message_descriptor, import_map)); GetFieldType(*field_des, message_descriptor));
} else { } else {
if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) { if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
printer_->Print("_Union[$type_name$, str]", "type_name", printer_->Print("_Union[$type_name$, str]", "type_name",
ModuleLevelName(*field_des->enum_type(), import_map)); ModuleLevelName(*field_des->enum_type()));
} else { } else {
printer_->Print( printer_->Print(
"$type_name$", "type_name", "$type_name$", "type_name",
GetFieldType(*field_des, message_descriptor, import_map)); GetFieldType(*field_des, message_descriptor));
} }
} }
if (field_des->is_repeated()) { if (field_des->is_repeated()) {
@ -553,8 +546,7 @@ void PyiGenerator::PrintMessage(
printer_->Outdent(); printer_->Outdent();
} }
void PyiGenerator::PrintMessages( void PyiGenerator::PrintMessages() const {
const std::map<std::string, std::string>& import_map) const {
// Deterministically order the descriptors. // Deterministically order the descriptors.
std::vector<const Descriptor*> messages; std::vector<const Descriptor*> messages;
messages.reserve(file_->message_type_count()); messages.reserve(file_->message_type_count());
@ -564,7 +556,7 @@ void PyiGenerator::PrintMessages(
std::sort(messages.begin(), messages.end(), SortByName<Descriptor>()); std::sort(messages.begin(), messages.end(), SortByName<Descriptor>());
for (const auto& entry : messages) { for (const auto& entry : messages) {
PrintMessage(*entry, false, import_map); PrintMessage(*entry, false);
} }
} }
@ -591,6 +583,7 @@ bool PyiGenerator::Generate(const FileDescriptor* file,
GeneratorContext* context, GeneratorContext* context,
std::string* error) const { std::string* error) const {
MutexLock lock(&mutex_); MutexLock lock(&mutex_);
import_map_.clear();
// Calculate file name. // Calculate file name.
file_ = file; file_ = file;
std::string filename = std::string filename =
@ -608,21 +601,17 @@ bool PyiGenerator::Generate(const FileDescriptor* file,
// Adds "DESCRIPTOR" into item_map. // Adds "DESCRIPTOR" into item_map.
item_map["DESCRIPTOR"] = "_descriptor.FileDescriptor"; item_map["DESCRIPTOR"] = "_descriptor.FileDescriptor";
// import_map will be a mapping from filename to module alias, e.g. PrintImports(&item_map);
// "google3/foo/bar.py" -> "_bar"
std::map<std::string, std::string> import_map;
PrintImports(&item_map, &import_map);
// Adds top level enum values to item_map. // Adds top level enum values to item_map.
for (int i = 0; i < file_->enum_type_count(); ++i) { for (int i = 0; i < file_->enum_type_count(); ++i) {
AddEnumValue(*file_->enum_type(i), &item_map, import_map); AddEnumValue(*file_->enum_type(i), &item_map);
} }
// Adds top level extensions to item_map. // Adds top level extensions to item_map.
AddExtensions(*file_, &item_map); AddExtensions(*file_, &item_map);
// Prints item map // Prints item map
PrintItemMap(item_map); PrintItemMap(item_map);
PrintMessages(import_map); PrintMessages();
PrintTopLevelEnums(); PrintTopLevelEnums();
if (HasGenericServices(file)) { if (HasGenericServices(file)) {
PrintServices(); PrintServices();

@ -76,37 +76,32 @@ class PROTOC_EXPORT PyiGenerator : public google::protobuf::compiler::CodeGenera
private: private:
void PrintImportForDescriptor(const FileDescriptor& desc, void PrintImportForDescriptor(const FileDescriptor& desc,
std::map<std::string, std::string>* import_map,
std::set<std::string>* seen_aliases) const; std::set<std::string>* seen_aliases) const;
void PrintImports(std::map<std::string, std::string>* item_map, void PrintImports(std::map<std::string, std::string>* item_map) const;
std::map<std::string, std::string>* import_map) const;
void PrintEnum(const EnumDescriptor& enum_descriptor) const; void PrintEnum(const EnumDescriptor& enum_descriptor) const;
void AddEnumValue(const EnumDescriptor& enum_descriptor, void AddEnumValue(const EnumDescriptor& enum_descriptor,
std::map<std::string, std::string>* item_map, std::map<std::string, std::string>* item_map) const;
const std::map<std::string, std::string>& import_map) const;
void PrintTopLevelEnums() const; void PrintTopLevelEnums() const;
template <typename DescriptorT> template <typename DescriptorT>
void AddExtensions(const DescriptorT& descriptor, void AddExtensions(const DescriptorT& descriptor,
std::map<std::string, std::string>* item_map) const; std::map<std::string, std::string>* item_map) const;
void PrintMessages( void PrintMessages() const;
const std::map<std::string, std::string>& import_map) const; void PrintMessage(const Descriptor& message_descriptor, bool is_nested) const;
void PrintMessage(const Descriptor& message_descriptor, bool is_nested,
const std::map<std::string, std::string>& import_map) const;
void PrintServices() const; void PrintServices() const;
void PrintItemMap(const std::map<std::string, std::string>& item_map) const; void PrintItemMap(const std::map<std::string, std::string>& item_map) const;
std::string GetFieldType( std::string GetFieldType(
const FieldDescriptor& field_des, const Descriptor& containing_des, const FieldDescriptor& field_des, const Descriptor& containing_des) const;
const std::map<std::string, std::string>& import_map) const;
template <typename DescriptorT> template <typename DescriptorT>
std::string ModuleLevelName( std::string ModuleLevelName(const DescriptorT& descriptor) const;
const DescriptorT& descriptor,
const std::map<std::string, std::string>& import_map) const;
// Very coarse-grained lock to ensure that Generate() is reentrant. // Very coarse-grained lock to ensure that Generate() is reentrant.
// Guards file_ and printer_. // Guards file_, printer_, and import_map_.
mutable Mutex mutex_; mutable Mutex mutex_;
mutable const FileDescriptor* file_; // Set in Generate(). Under mutex_. mutable const FileDescriptor* file_; // Set in Generate(). Under mutex_.
mutable io::Printer* printer_; // Set in Generate(). Under mutex_. mutable io::Printer* printer_; // Set in Generate(). Under mutex_.
// import_map will be a mapping from filename to module alias, e.g.
// "google3/foo/bar.py" -> "_bar"
mutable std::map<std::string, std::string> import_map_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PyiGenerator); GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(PyiGenerator);
}; };

@ -38,7 +38,6 @@
#include <google/protobuf/port.h> #include <google/protobuf/port.h>
#include <google/protobuf/extension_set.h> #include <google/protobuf/extension_set.h>
#include <google/protobuf/generated_message_tctable_decl.h> #include <google/protobuf/generated_message_tctable_decl.h>
#include <google/protobuf/message_lite.h>
#include <google/protobuf/metadata_lite.h> #include <google/protobuf/metadata_lite.h>
#include <google/protobuf/parse_context.h> #include <google/protobuf/parse_context.h>
#include <google/protobuf/wire_format_lite.h> #include <google/protobuf/wire_format_lite.h>
@ -257,16 +256,6 @@ enum FieldType : uint16_t {
// clang-format on // clang-format on
} // namespace field_layout } // namespace field_layout
// PROTOBUF_TC_PARAM_DECL are the parameters for tailcall functions, it is
// defined in port_def.inc.
//
// Note that this is performance sensitive: changing the parameters will change
// the registers used by the ABI calling convention, which subsequently affects
// register selection logic inside the function.
// PROTOBUF_TC_PARAM_PASS passes values to match PROTOBUF_TC_PARAM_DECL.
#define PROTOBUF_TC_PARAM_PASS msg, ptr, ctx, table, hasbits, data
#ifndef NDEBUG #ifndef NDEBUG
template <size_t align> template <size_t align>
void AlignFail(uintptr_t address) { void AlignFail(uintptr_t address) {
@ -349,6 +338,28 @@ class PROTOBUF_EXPORT TcParser final {
static const char* FastZ64P1(PROTOBUF_TC_PARAM_DECL); static const char* FastZ64P1(PROTOBUF_TC_PARAM_DECL);
static const char* FastZ64P2(PROTOBUF_TC_PARAM_DECL); static const char* FastZ64P2(PROTOBUF_TC_PARAM_DECL);
// Manually unrolled and specialized Varint parsing.
template <typename FieldType, int data_offset, int hasbit_idx>
static const char* SpecializedUnrolledVImpl1(PROTOBUF_TC_PARAM_DECL);
template <typename FieldType, int data_offset, int hasbit_idx>
static constexpr TailCallParseFunc SingularVarintNoZag1() {
if (data_offset < 100) {
return &SpecializedUnrolledVImpl1<FieldType, data_offset, hasbit_idx>;
} else if (sizeof(FieldType) == 1) {
return &FastV8S1;
} else if (sizeof(FieldType) == 4) {
return &FastV32S1;
} else if (sizeof(FieldType) == 8) {
return &FastV64S1;
} else {
static_assert(sizeof(FieldType) == 1 || sizeof(FieldType) == 4 ||
sizeof(FieldType) == 8,
"");
return nullptr;
}
}
// Functions referenced by generated fast tables (closed enum): // Functions referenced by generated fast tables (closed enum):
// E: closed enum (N.B.: open enums use V32, above) // E: closed enum (N.B.: open enums use V32, above)
// r: enum range v: enum validator (_IsValid function) // r: enum range v: enum validator (_IsValid function)
@ -600,6 +611,135 @@ class PROTOBUF_EXPORT TcParser final {
static const char* MpMap(PROTOBUF_TC_PARAM_DECL); static const char* MpMap(PROTOBUF_TC_PARAM_DECL);
}; };
template <typename FieldType, int data_offset, int hasbit_idx>
const char* TcParser::SpecializedUnrolledVImpl1(PROTOBUF_TC_PARAM_DECL) {
using TagType = uint8_t;
// super-early success test...
if (PROTOBUF_PREDICT_TRUE(((data.data) & 0x80FF) == 0)) {
ptr += sizeof(TagType); // Consume tag
if (hasbit_idx < 32) {
hasbits |= (uint64_t{1} << hasbit_idx);
}
uint8_t value = data.data >> 8;
RefAt<FieldType>(msg, data_offset) = value;
ptr += 1;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
if (PROTOBUF_PREDICT_FALSE(data.coded_tag<TagType>() != 0)) {
PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
}
ptr += sizeof(TagType); // Consume tag
if (hasbit_idx < 32) {
hasbits |= (uint64_t{1} << hasbit_idx);
}
// Few registers
auto* out = &RefAt<FieldType>(msg, data_offset);
uint64_t res = 0xFF & (data.data >> 8);
/* if (PROTOBUF_PREDICT_FALSE(res & 0x80)) */ {
res = RotRight7AndReplaceLowByte(res, ptr[1]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
res = RotRight7AndReplaceLowByte(res, ptr[2]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
res = RotRight7AndReplaceLowByte(res, ptr[3]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
res = RotRight7AndReplaceLowByte(res, ptr[4]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
res = RotRight7AndReplaceLowByte(res, ptr[5]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
res = RotRight7AndReplaceLowByte(res, ptr[6]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
res = RotRight7AndReplaceLowByte(res, ptr[7]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
res = RotRight7AndReplaceLowByte(res, ptr[8]);
if (PROTOBUF_PREDICT_FALSE(res & 0x80)) {
if (ptr[9] & 0xFE) return nullptr;
res = RotateLeft(res, -7) & ~1;
res += ptr[9] & 1;
*out = RotateLeft(res, 63);
ptr += 10;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 56);
ptr += 9;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 49);
ptr += 8;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 42);
ptr += 7;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 35);
ptr += 6;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 28);
ptr += 5;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 21);
ptr += 4;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 14);
ptr += 3;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = RotateLeft(res, 7);
ptr += 2;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
*out = res;
ptr += 1;
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
// Dispatch to the designated parse function
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::TagDispatch(
PROTOBUF_TC_PARAM_DECL) {
const auto coded_tag = UnalignedLoad<uint16_t>(ptr);
const size_t idx = coded_tag & table->fast_idx_mask;
PROTOBUF_ASSUME((idx & 7) == 0);
auto* fast_entry = table->fast_entry(idx >> 3);
data = fast_entry->bits;
data.data ^= coded_tag;
PROTOBUF_MUSTTAIL return fast_entry->target(PROTOBUF_TC_PARAM_PASS);
}
// We can only safely call from field to next field if the call is optimized
// to a proper tail call. Otherwise we blow through stack. Clang and gcc
// reliably do this optimization in opt mode, but do not perform this in debug
// mode. Luckily the structure of the algorithm is such that it's always
// possible to just return and use the enclosing parse loop as a trampoline.
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToTagDispatch(
PROTOBUF_TC_PARAM_DECL) {
constexpr bool always_return = !PROTOBUF_TAILCALL;
if (always_return || !ctx->DataAvailable(ptr)) {
PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_MUSTTAIL return TagDispatch(PROTOBUF_TC_PARAM_PASS);
}
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToParseLoop(
PROTOBUF_TC_PARAM_DECL) {
(void)data;
(void)ctx;
SyncHasbits(msg, hasbits, table);
return ptr;
}
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::Error(
PROTOBUF_TC_PARAM_DECL) {
(void)data;
(void)ctx;
(void)ptr;
SyncHasbits(msg, hasbits, table);
return nullptr;
}
} // namespace internal } // namespace internal
} // namespace protobuf } // namespace protobuf
} // namespace google } // namespace google

@ -30,6 +30,7 @@
#include <cstdint> #include <cstdint>
#include <numeric> #include <numeric>
#include <type_traits>
#include <google/protobuf/extension_set.h> #include <google/protobuf/extension_set.h>
#include <google/protobuf/generated_message_tctable_decl.h> #include <google/protobuf/generated_message_tctable_decl.h>
@ -84,56 +85,13 @@ PROTOBUF_NOINLINE const char* TcParser::ParseLoop(
// TODO(b/64614992): remove this asm // TODO(b/64614992): remove this asm
asm("" : "+r"(table)); asm("" : "+r"(table));
#endif #endif
ptr = TagDispatch(msg, ptr, ctx, table - 1, 0, {}); ptr = TagDispatch(msg, ptr, ctx, {}, table - 1, 0);
if (ptr == nullptr) break; if (ptr == nullptr) break;
if (ctx->LastTag() != 1) break; // Ended on terminating tag if (ctx->LastTag() != 1) break; // Ended on terminating tag
} }
return ptr; return ptr;
} }
// Dispatch to the designated parse function
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::TagDispatch(
PROTOBUF_TC_PARAM_DECL) {
const auto coded_tag = UnalignedLoad<uint16_t>(ptr);
const size_t idx = coded_tag & table->fast_idx_mask;
PROTOBUF_ASSUME((idx & 7) == 0);
auto* fast_entry = table->fast_entry(idx >> 3);
data = fast_entry->bits;
data.data ^= coded_tag;
PROTOBUF_MUSTTAIL return fast_entry->target(PROTOBUF_TC_PARAM_PASS);
}
// We can only safely call from field to next field if the call is optimized
// to a proper tail call. Otherwise we blow through stack. Clang and gcc
// reliably do this optimization in opt mode, but do not perform this in debug
// mode. Luckily the structure of the algorithm is such that it's always
// possible to just return and use the enclosing parse loop as a trampoline.
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToTagDispatch(
PROTOBUF_TC_PARAM_DECL) {
constexpr bool always_return = !PROTOBUF_TAILCALL;
if (always_return || !ctx->DataAvailable(ptr)) {
PROTOBUF_MUSTTAIL return ToParseLoop(PROTOBUF_TC_PARAM_PASS);
}
PROTOBUF_MUSTTAIL return TagDispatch(PROTOBUF_TC_PARAM_PASS);
}
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::ToParseLoop(
PROTOBUF_TC_PARAM_DECL) {
(void)data;
(void)ctx;
SyncHasbits(msg, hasbits, table);
return ptr;
}
inline PROTOBUF_ALWAYS_INLINE const char* TcParser::Error(
PROTOBUF_TC_PARAM_DECL) {
(void)data;
(void)ctx;
(void)ptr;
SyncHasbits(msg, hasbits, table);
return nullptr;
}
// On the fast path, a (matching) 1-byte tag already has the decoded value. // On the fast path, a (matching) 1-byte tag already has the decoded value.
static uint32_t FastDecodeTag(uint8_t coded_tag) { static uint32_t FastDecodeTag(uint8_t coded_tag) {
return coded_tag; return coded_tag;
@ -875,8 +833,31 @@ PROTOBUF_NOINLINE const char* TcParser::SingularVarBigint(
} }
const char* TcParser::FastV8S1(PROTOBUF_TC_PARAM_DECL) { const char* TcParser::FastV8S1(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularVarint<bool, uint8_t>( // Special case for a varint bool field with a tag of 1 byte:
PROTOBUF_TC_PARAM_PASS); // The coded_tag() field will actually contain the value too and we can check
// both at the same time.
auto coded_tag = data.coded_tag<uint16_t>();
if (PROTOBUF_PREDICT_TRUE(coded_tag == 0x0000 || coded_tag == 0x0100)) {
auto& field = RefAt<bool>(msg, data.offset());
// Note: we use `data.data` because Clang generates suboptimal code when
// using coded_tag.
// In x86_64 this uses the CH register to read the second byte out of
// `data`.
uint8_t value = data.data >> 8;
// The assume allows using a mov instead of test+setne.
PROTOBUF_ASSUME(value <= 1);
field = static_cast<bool>(value);
ptr += 2; // Consume the tag and the value.
hasbits |= (uint64_t{1} << data.hasbit_idx());
PROTOBUF_MUSTTAIL return ToTagDispatch(PROTOBUF_TC_PARAM_PASS);
}
// If it didn't match above either the tag is wrong, or the value is encoded
// non-canonically.
// Jump to MiniParse as wrong tag is the most probable reason.
PROTOBUF_MUSTTAIL return MiniParse(PROTOBUF_TC_PARAM_PASS);
} }
const char* TcParser::FastV8S2(PROTOBUF_TC_PARAM_DECL) { const char* TcParser::FastV8S2(PROTOBUF_TC_PARAM_DECL) {
PROTOBUF_MUSTTAIL return SingularVarint<bool, uint16_t>( PROTOBUF_MUSTTAIL return SingularVarint<bool, uint16_t>(

@ -251,7 +251,8 @@ class PROTOBUF_EXPORT Printer {
template <typename... Args> template <typename... Args>
void Print(const char* text, const Args&... args) { void Print(const char* text, const Args&... args) {
std::map<std::string, std::string> vars; std::map<std::string, std::string> vars;
PrintInternal(&vars, text, args...); FillMap(&vars, args...);
Print(vars, text);
} }
// Indent text by two spaces. After calling Indent(), two spaces will be // Indent text by two spaces. After calling Indent(), two spaces will be
@ -299,18 +300,13 @@ class PROTOBUF_EXPORT Printer {
void Annotate(const char* begin_varname, const char* end_varname, void Annotate(const char* begin_varname, const char* end_varname,
const std::string& file_path, const std::vector<int>& path); const std::string& file_path, const std::vector<int>& path);
// Base case void FillMap(std::map<std::string, std::string>* vars) {}
void PrintInternal(std::map<std::string, std::string>* vars,
const char* text) {
Print(*vars, text);
}
template <typename... Args> template <typename... Args>
void PrintInternal(std::map<std::string, std::string>* vars, const char* text, void FillMap(std::map<std::string, std::string>* vars, const std::string& key,
const char* key, const std::string& value, const std::string& value, const Args&... args) {
const Args&... args) {
(*vars)[key] = value; (*vars)[key] = value;
PrintInternal(vars, text, args...); FillMap(vars, args...);
} }
// Copy size worth of bytes from data to buffer_. // Copy size worth of bytes from data to buffer_.

@ -214,7 +214,7 @@ uint64_t Message::GetInvariantPerBuild(uint64_t salt) {
} }
namespace internal { namespace internal {
void* CreateSplitMessageGeneric(Arena* arena, void* default_split, void* CreateSplitMessageGeneric(Arena* arena, const void* default_split,
size_t size) { size_t size) {
void* split = void* split =
(arena == nullptr) ? ::operator new(size) : arena->AllocateAligned(size); (arena == nullptr) ? ::operator new(size) : arena->AllocateAligned(size);

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

Loading…
Cancel
Save