Merge pull request #10282 from casperisfine/ruby-usable-ivar
Ruby: use a valid instance variable name for `descriptor`
diff --git a/.gitignore b/.gitignore
index d8b0bd3..cc881f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -19,6 +19,23 @@
m4/lt~obsolete.m4
autom4te.cache
+# CMake-generated files
+.ninja_deps
+.ninja_logs
+cmake/protobuf/*.cmake
+cmake_install.cmake
+CMakeCache.txt
+CTestTestfile.cmake
+CMakeFiles/*
+Testing/Temporary/*
+
+/core
+/protoc
+/test_plugin
+/tests
+/lite-test
+/protoc-*.*
+
# downloaded files
/gmock
@@ -41,6 +58,7 @@
*.la
src/.libs
*.so
+*.a
.dirstamp
diff --git a/BUILD.bazel b/BUILD.bazel
index 1a305e7..a14476a 100644
--- a/BUILD.bazel
+++ b/BUILD.bazel
@@ -266,7 +266,7 @@
alias(
name = "well_known_types_py_pb2",
actual = "//python:well_known_types_py_pb2",
- visibility = ["@upb//:__subpackages__"],
+ visibility = ["//visibility:public"],
)
alias(
diff --git a/CHANGES.txt b/CHANGES.txt
index 1b83184..d8ed3e9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,18 +1,64 @@
2022-07-01 Unreleased version
+
C++
+ * cpp_generated_lib_linked support is removed in protoc
* Reduced .pb.o object file size slightly by explicitly instantiating
InternalMetadata templates in the runtime.
* Add C++20 keywords guarded by PROTOBUF_FUTURE_CPP20_KEYWORDS
* Fixed crash in ThreadLocalStorage for pre-C++17 compilers on 32-bit ARM.
* Clarified that JSON API non-OK statuses are not a stable API.
+ * Added a default implementation of MessageDifferencer::Reporter methods.
+ * proto2::MapPair is now an alias to std::pair.
+ * Hide C++ RepeatedField::UnsafeArenaSwap
Kotlin
+ * Suppress deprecation warnings in Kotlin generated code.
* Kotlin generated code comments now use kdoc format instead of javadoc.
* Escape keywords in package names in proto generated code
+ * Add Kotlin enum int value getters and setters
Java
* Performance improvement for repeated use of FieldMaskUtil#merge by caching
constructed FieldMaskTrees.
+ * Optimized Java proto serialization gencode for protos having many extension ranges with few fields in between.
+
+ Python
+ * Changes ordering of printed fields in .pyi files from lexicographic to the same ordering found in the proto descriptor.
+
+ Compiler
+ * Print full path name of source .proto file on error
+
+
+2022-07-25 version 21.4 (C++/Java/Python/PHP/Objective-C/C#/Ruby)
+
+ C++
+ * Reduce the required alignment of ArenaString from 8 to 4 (#10298)
+
+
+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)
C++
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 04cb330..d5d2009 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -57,9 +57,11 @@
option(protobuf_BUILD_TESTS "Build tests" ON)
option(protobuf_BUILD_CONFORMANCE "Build conformance tests" OFF)
option(protobuf_BUILD_EXAMPLES "Build examples" OFF)
+option(protobuf_BUILD_PROTOBUF_BINARIES "Build protobuf libraries and protoc compiler" ON)
option(protobuf_BUILD_PROTOC_BINARIES "Build libprotoc and protoc compiler" ON)
option(protobuf_BUILD_LIBPROTOC "Build libprotoc" OFF)
option(protobuf_DISABLE_RTTI "Remove runtime type information in the binaries" OFF)
+option(protobuf_TEST_XML_OUTDIR "Output directory for XML logs from tests." "")
if (BUILD_SHARED_LIBS)
set(protobuf_BUILD_SHARED_LIBS_DEFAULT ON)
else (BUILD_SHARED_LIBS)
@@ -81,6 +83,9 @@
if (protobuf_BUILD_PROTOC_BINARIES OR protobuf_BUILD_TESTS)
set(protobuf_BUILD_LIBPROTOC ON)
endif ()
+if (NOT protobuf_BUILD_PROTOBUF_BINARIES)
+ set(protobuf_INSTALL OFF)
+endif()
# Path to main configure script
set(protobuf_CONFIGURE_SCRIPT "${protobuf_SOURCE_DIR}/configure.ac")
@@ -242,12 +247,12 @@
if (MSVC)
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
# Build with multiple processes
- add_definitions(/MP)
+ add_compile_options(/MP)
endif()
# Set source file and execution character sets to UTF-8
- add_definitions(/utf-8)
+ add_compile_options(/utf-8)
# MSVC warning suppressions
- add_definitions(
+ add_compile_options(
/wd4065 # switch statement contains 'default' but no 'case' labels
/wd4244 # 'conversion' conversion from 'type1' to 'type2', possible loss of data
/wd4251 # 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
@@ -262,23 +267,16 @@
/wd4996 # The compiler encountered a deprecated declaration.
)
# Allow big object
- add_definitions(/bigobj)
+ add_compile_options(/bigobj)
string(REPLACE "/" "\\" PROTOBUF_SOURCE_WIN32_PATH ${protobuf_SOURCE_DIR})
string(REPLACE "/" "\\" PROTOBUF_BINARY_WIN32_PATH ${protobuf_BINARY_DIR})
string(REPLACE "." "," protobuf_RC_FILEVERSION "${protobuf_VERSION}")
- configure_file(${protobuf_SOURCE_DIR}/cmake/extract_includes.bat.in extract_includes.bat)
# Suppress linker warnings about files with no symbols defined.
- set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4221")
+ string(APPEND CMAKE_STATIC_LINKER_FLAGS " /ignore:4221")
- if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
- # Configure Resource Compiler
- enable_language(RC)
- # use English language (0x409) in resource compiler
- set(rc_flags "/l0x409")
- # fix rc.exe invocations because of usage of add_definitions()
- set(CMAKE_RC_COMPILE_OBJECT "<CMAKE_RC_COMPILER> ${rc_flags} <DEFINES> /fo<OBJECT> <SOURCE>")
- endif()
+ # use English language (0x409) in resource compiler
+ string(APPEND CMAKE_RC_FLAGS " -l0x409")
# Generate the version.rc file used elsewhere.
configure_file(${protobuf_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
@@ -304,28 +302,49 @@
add_definitions(-DUNICODE -D_UNICODE)
endif (protobuf_UNICODE)
-include(${protobuf_SOURCE_DIR}/cmake/libprotobuf-lite.cmake)
-include(${protobuf_SOURCE_DIR}/cmake/libprotobuf.cmake)
-if (protobuf_BUILD_LIBPROTOC)
- include(${protobuf_SOURCE_DIR}/cmake/libprotoc.cmake)
-endif (protobuf_BUILD_LIBPROTOC)
-if (protobuf_BUILD_PROTOC_BINARIES)
- include(${protobuf_SOURCE_DIR}/cmake/protoc.cmake)
- if (NOT DEFINED protobuf_PROTOC_EXE)
- set(protobuf_PROTOC_EXE protoc)
- endif (NOT DEFINED protobuf_PROTOC_EXE)
-endif (protobuf_BUILD_PROTOC_BINARIES)
+if (protobuf_BUILD_PROTOBUF_BINARIES)
+ include(${protobuf_SOURCE_DIR}/cmake/libprotobuf-lite.cmake)
+ if (NOT DEFINED protobuf_LIB_PROTOBUF_LITE)
+ set(protobuf_LIB_PROTOBUF_LITE libprotobuf-lite)
+ endif ()
+ include(${protobuf_SOURCE_DIR}/cmake/libprotobuf.cmake)
+ if (NOT DEFINED protobuf_LIB_PROTOBUF)
+ set(protobuf_LIB_PROTOBUF libprotobuf)
+ endif ()
+ if (protobuf_BUILD_LIBPROTOC)
+ include(${protobuf_SOURCE_DIR}/cmake/libprotoc.cmake)
+ if (NOT DEFINED protobuf_LIB_PROTOC)
+ set(protobuf_LIB_PROTOC libprotoc)
+ endif ()
+ endif ()
+ if (protobuf_BUILD_PROTOC_BINARIES)
+ include(${protobuf_SOURCE_DIR}/cmake/protoc.cmake)
+ if (NOT DEFINED protobuf_PROTOC_EXE)
+ set(protobuf_PROTOC_EXE protoc)
+ endif ()
+ endif ()
+else ()
+ find_package(Protobuf NO_MODULE)
+ if (Protobuf_FOUND)
+ set(protobuf_PROTOC_EXE protobuf::protoc)
+ set(protobuf_LIB_PROTOC protobuf::libprotoc)
+ set(protobuf_LIB_PROTOBUF protobuf::libprotobuf)
+ set(protobuf_LIB_PROTOBUF_LITE protobuf::libprotobuf-lite)
+ message(STATUS "CMake installation of Protobuf found.")
+ endif ()
+endif ()
-# Ensure we have a protoc executable if we need one
+# Ensure we have a protoc executable and protobuf libraries if we need one
if (protobuf_BUILD_TESTS OR protobuf_BUILD_CONFORMANCE OR protobuf_BUILD_EXAMPLES)
if (NOT DEFINED protobuf_PROTOC_EXE)
- find_program(protobuf_PROTOC_EXE protoc)
- if (NOT protobuf_PROTOC_EXE)
- message(FATAL "Build requires 'protoc' but binary not found and not building protoc.")
- endif ()
+ find_program(protobuf_PROTOC_EXE protoc REQUIRED)
+ message(STATUS "Found system ${protobuf_PROTOC_EXE}.")
endif ()
if(protobuf_VERBOSE)
message(STATUS "Using protoc : ${protobuf_PROTOC_EXE}")
+ message(STATUS "Using libprotobuf : ${protobuf_LIB_PROTOBUF}")
+ message(STATUS "Using libprotobuf-lite : ${protobuf_LIB_PROTOBUF_LITE}")
+ message(STATUS "Using libprotoc : ${protobuf_LIB_PROTOC}")
endif(protobuf_VERBOSE)
endif ()
diff --git a/Makefile.am b/Makefile.am
index 32e63f2..acc72c2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -131,6 +131,7 @@
csharp/src/Google.Protobuf.Test/GeneratedMessageTest.Proto2.cs \
csharp/src/Google.Protobuf.Test/Google.Protobuf.Test.csproj \
csharp/src/Google.Protobuf.Test/IssuesTest.cs \
+ csharp/src/Google.Protobuf.Test/JsonFormatterSettingsTest.cs \
csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs \
csharp/src/Google.Protobuf.Test/JsonParserTest.cs \
csharp/src/Google.Protobuf.Test/JsonTokenizerTest.cs \
@@ -960,6 +961,8 @@
php/tests/EncodeDecodeTest.php \
php/tests/force_c_ext.php \
php/tests/gdb_test.sh \
+ php/tests/generated_previous/GPBMetadata/ProtoPrevious/TestPreviouslyUnreservedMessage.php \
+ php/tests/generated_previous/Previous/readonly.php \
php/tests/GeneratedClassTest.php \
php/tests/GeneratedPhpdocTest.php \
php/tests/GeneratedServiceTest.php \
@@ -969,6 +972,7 @@
php/tests/multirequest.php \
php/tests/multirequest.sh \
php/tests/PhpImplementationTest.php \
+ php/tests/PreviouslyGeneratedClassTest.php \
php/tests/proto/empty/echo.proto \
php/tests/proto/test.proto \
php/tests/proto/test_descriptors.proto \
@@ -987,6 +991,7 @@
php/tests/proto/test_service.proto \
php/tests/proto/test_service_namespace.proto \
php/tests/proto/test_wrapper_type_setters.proto \
+ php/tests/proto_previous/test_previously_unreserved_message.proto \
php/tests/test_base.php \
php/tests/test_util.php \
php/tests/valgrind.supp \
@@ -1216,7 +1221,6 @@
cmake/README.md \
cmake/conformance.cmake \
cmake/examples.cmake \
- cmake/extract_includes.bat.in \
cmake/install.cmake \
cmake/libprotobuf-lite.cmake \
cmake/libprotobuf.cmake \
diff --git a/Protobuf-C++.podspec b/Protobuf-C++.podspec
index c8a516b..858a861 100644
--- a/Protobuf-C++.podspec
+++ b/Protobuf-C++.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Protobuf-C++'
- s.version = '3.21.2'
+ s.version = '3.21.4'
s.summary = 'Protocol Buffers v3 runtime library for C++.'
s.homepage = 'https://github.com/google/protobuf'
s.license = 'BSD-3-Clause'
diff --git a/Protobuf.podspec b/Protobuf.podspec
index 85f1dc6..e41f39a 100644
--- a/Protobuf.podspec
+++ b/Protobuf.podspec
@@ -5,7 +5,7 @@
# dependent projects use the :git notation to refer to the library.
Pod::Spec.new do |s|
s.name = 'Protobuf'
- s.version = '3.21.2'
+ s.version = '3.21.4'
s.summary = 'Protocol Buffers v.3 runtime library for Objective-C.'
s.homepage = 'https://github.com/protocolbuffers/protobuf'
s.license = 'BSD-3-Clause'
diff --git a/cmake/README.md b/cmake/README.md
index ce3e680..4a4259e 100644
--- a/cmake/README.md
+++ b/cmake/README.md
@@ -1,15 +1,19 @@
-This directory contains *CMake* files that can be used to build protobuf
-with *MSVC* on *Windows*. You can build the project from *Command Prompt*
-and using an *Visual Studio* IDE.
+This directory contains *CMake* files that can be used to build protobuf.
-You need to have [CMake](http://www.cmake.org), [Visual Studio](https://www.visualstudio.com)
-and optionally [Git](http://git-scm.com) installed on your computer before proceeding.
+You need to have [CMake](http://www.cmake.org) and
+[Git](http://git-scm.com) installed on your computer before proceeding.
-Most of the instructions will be given to the *Сommand Prompt*, but the same
-actions can be performed using appropriate GUI tools.
+Most of the instructions will be given using CMake's command-line interface, but
+the same actions can be performed using appropriate GUI tools.
-Environment Setup
-=================
+# Windows Builds
+
+On Windows, you can build the project from *Command Prompt* and using an
+*Visual Studio* IDE. You will also need to have
+[Visual Studio](https://www.visualstudio.com) installed on your computer before
+proceeding.
+
+## Environment Setup
Open the appropriate *Command Prompt* from the *Start* menu.
@@ -42,8 +46,7 @@
Good. Now you are ready to continue.
-Getting Sources
-===============
+## Getting Sources
You can get the latest stable source packages from the release page:
@@ -76,8 +79,7 @@
Good. Now you are ready for *CMake* configuration.
-CMake Configuration
-===================
+## CMake Configuration
*CMake* supports a lot of different
[generators](http://www.cmake.org/cmake/help/latest/manual/cmake-generators.7.html)
@@ -137,8 +139,7 @@
It will generate *Visual Studio* solution file *protobuf.sln* in current directory.
-Unit Tests
-----------
+### Unit Tests
Unit tests are being built along with the rest of protobuf. The unit tests require Google Mock (now a part of Google Test).
@@ -178,8 +179,7 @@
-Dprotobuf_BUILD_TESTS=OFF ^
C:\Path\to\src\protobuf
-Compiling
-=========
+## Compiling
The standard way to compile a *CMake* project is `cmake --build <directory>`.
@@ -206,8 +206,7 @@
And wait for the compilation to finish.
-Testing
-=======
+## Testing
To run unit-tests, first you must compile protobuf as described above.
Then run:
@@ -259,8 +258,7 @@
If all tests are passed, safely continue.
-Installing
-==========
+## Installing
To install protobuf to the *install* folder you've specified in the configuration step, you need to build the `install` target:
@@ -292,8 +290,7 @@
debug build of libprotobufd.lib with "d" postfix. Similarly, release builds should link against
release libprotobuf.lib library.
-DLLs vs. static linking
-=======================
+## DLLs vs. static linking
Static linking is now the default for the Protocol Buffer libraries. Due to
issues with Win32's use of a separate heap for each DLL, as well as binary
@@ -318,8 +315,7 @@
public interface, and that you statically link protocol buffers into your
library.
-ZLib support
-============
+## ZLib support
If you want to include GzipInputStream and GzipOutputStream
(google/protobuf/io/gzip_stream.h) in libprotobuf, you will need to do a few
@@ -369,8 +365,7 @@
Build and testing protobuf as usual.
-Notes on Compiler Warnings
-==========================
+## Notes on Compiler Warnings
The following warnings have been disabled while building the protobuf libraries
and compiler. You may have to disable some of them in your own project as
@@ -397,3 +392,23 @@
nevertheless. So, we disable it. Unfortunately, this warning will also be
produced when compiling code which merely uses protocol buffers, meaning you
may have to disable it in your code too.
+
+# Linux Builds
+
+Building with CMake works very similarly on Linux. Instead of Visual Studio,
+you will need to have gcc or clang installed to handle the C++ builds. CMake
+will generate Makefiles by default, but can also be configured to use Ninja. To
+build Protobuf, you will need to run (from the source directory):
+
+ cmake .
+ cmake --build . --parallel 10
+
+Protobuf can be tested and installed with CMake:
+
+ ctest --verbose
+ sudo cmake --install .
+
+or directly with the generated Makefiles:
+
+ make VERBOSE=1 test
+ sudo make install
diff --git a/cmake/conformance.cmake b/cmake/conformance.cmake
index d6c435a..338b275 100644
--- a/cmake/conformance.cmake
+++ b/cmake/conformance.cmake
@@ -24,6 +24,9 @@
${protobuf_SOURCE_DIR}/conformance/conformance.pb.cc
${protobuf_SOURCE_DIR}/conformance/conformance_test.cc
${protobuf_SOURCE_DIR}/conformance/conformance_test_runner.cc
+ ${protobuf_SOURCE_DIR}/conformance/conformance_test_main.cc
+ ${protobuf_SOURCE_DIR}/conformance/text_format_conformance_suite.cc
+ ${protobuf_SOURCE_DIR}/conformance/text_format_conformance_suite.h
${protobuf_SOURCE_DIR}/conformance/third_party/jsoncpp/json.h
${protobuf_SOURCE_DIR}/conformance/third_party/jsoncpp/jsoncpp.cpp
${protobuf_SOURCE_DIR}/src/google/protobuf/test_messages_proto2.pb.cc
@@ -45,5 +48,13 @@
conformance_cpp
PUBLIC ${protobuf_SOURCE_DIR}/conformance)
-target_link_libraries(conformance_test_runner libprotobuf)
-target_link_libraries(conformance_cpp libprotobuf)
+target_link_libraries(conformance_test_runner ${protobuf_LIB_PROTOBUF})
+target_link_libraries(conformance_cpp ${protobuf_LIB_PROTOBUF})
+
+add_test(NAME conformance_cpp_test
+ COMMAND ${CMAKE_CURRENT_BINARY_DIR}/conformance_test_runner
+ --failure_list ${protobuf_SOURCE_DIR}/conformance/failure_list_cpp.txt
+ --text_format_failure_list ${protobuf_SOURCE_DIR}/conformance/text_format_failure_list_cpp.txt
+ --output_dir ${protobuf_TEST_XML_OUTDIR}
+ ${CMAKE_CURRENT_BINARY_DIR}/conformance_cpp
+ DEPENDS conformance_test_runner conformance_cpp)
diff --git a/cmake/examples.cmake b/cmake/examples.cmake
index 3b83d2b..07cfad2 100644
--- a/cmake/examples.cmake
+++ b/cmake/examples.cmake
@@ -30,7 +30,7 @@
# Add examples as an external project.
# sub_directory cannot be used because the find_package(protobuf) call would cause failures with redefined targets.
add_examples_build(examples "-Dprotobuf_DIR:PATH=${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_CMAKEDIR}")
-add_dependencies(examples libprotobuf protoc)
+add_dependencies(examples ${protobuf_LIB_PROTOBUF} ${protobuf_PROTOC_EXE})
option(protobuf_BUILD_EXAMPLES_MULTITEST "Build Examples in multiple configurations. Useful for testing." OFF)
mark_as_advanced(protobuf_BUILD_EXAMPLES_MULTITEST)
@@ -42,7 +42,7 @@
"-Dprotobuf_DIR:PATH=${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_CMAKEDIR}"
"-Dprotobuf_MODULE_COMPATIBLE:BOOL=TRUE"
)
- add_dependencies(examples-legacy libprotobuf protoc)
+ add_dependencies(examples-legacy ${protobuf_LIB_PROTOBUF} ${protobuf_PROTOC_EXE})
#Build using the installed library.
add_examples_build(examples-installed
diff --git a/cmake/extract_includes.bat.in b/cmake/extract_includes.bat.in
deleted file mode 100644
index 1292829..0000000
--- a/cmake/extract_includes.bat.in
+++ /dev/null
@@ -1,144 +0,0 @@
-mkdir include
-mkdir include\google
-mkdir include\google\protobuf
-mkdir include\google\protobuf\compiler
-mkdir include\google\protobuf\compiler\cpp
-mkdir include\google\protobuf\compiler\csharp
-mkdir include\google\protobuf\compiler\java
-mkdir include\google\protobuf\compiler\objectivec
-mkdir include\google\protobuf\compiler\php
-mkdir include\google\protobuf\compiler\python
-mkdir include\google\protobuf\compiler\ruby
-mkdir include\google\protobuf\io
-mkdir include\google\protobuf\stubs
-mkdir include\google\protobuf\util
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.h" include\google\protobuf\any.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.pb.h" include\google\protobuf\any.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\api.pb.h" include\google\protobuf\api.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arena.h" include\google\protobuf\arena.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arena_impl.h" include\google\protobuf\arena_impl.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arenastring.h" include\google\protobuf\arenastring.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\arenaz_sampler.h" include\google\protobuf\arenaz_sampler.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\code_generator.h" include\google\protobuf\compiler\code_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\command_line_interface.h" include\google\protobuf\compiler\command_line_interface.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\cpp\file.h" include\google\protobuf\compiler\cpp\file.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\cpp\cpp_generator.h" include\google\protobuf\compiler\cpp\cpp_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\cpp\generator.h" include\google\protobuf\compiler\cpp\generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\cpp\helpers.h" include\google\protobuf\compiler\cpp\helpers.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\cpp\names.h" include\google\protobuf\compiler\cpp\names.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\csharp\csharp_doc_comment.h" include\google\protobuf\compiler\csharp\csharp_doc_comment.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\csharp\csharp_generator.h" include\google\protobuf\compiler\csharp\csharp_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\csharp\csharp_names.h" include\google\protobuf\compiler\csharp\csharp_names.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\csharp\csharp_options.h" include\google\protobuf\compiler\csharp\csharp_options.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\importer.h" include\google\protobuf\compiler\importer.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\generator.h" include\google\protobuf\compiler\java\generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\java_generator.h" include\google\protobuf\compiler\java\java_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\kotlin_generator.h" include\google\protobuf\compiler\java\kotlin_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\java\names.h" include\google\protobuf\compiler\java\names.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_generator.h" include\google\protobuf\compiler\objectivec\objectivec_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\objectivec\objectivec_helpers.h" include\google\protobuf\compiler\objectivec\objectivec_helpers.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\parser.h" include\google\protobuf\compiler\parser.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\php\php_generator.h" include\google\protobuf\compiler\php\php_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.h" include\google\protobuf\compiler\plugin.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.pb.h" include\google\protobuf\compiler\plugin.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\python\generator.h" include\google\protobuf\compiler\python\generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\python\pyi_generator.h" include\google\protobuf\compiler\python\pyi_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\python\python_generator.h" include\google\protobuf\compiler\python\python_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\ruby\ruby_generator.h" include\google\protobuf\compiler\ruby\ruby_generator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\descriptor.h" include\google\protobuf\descriptor.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\descriptor.pb.h" include\google\protobuf\descriptor.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\descriptor_database.h" include\google\protobuf\descriptor_database.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\duration.pb.h" include\google\protobuf\duration.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\dynamic_message.h" include\google\protobuf\dynamic_message.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\empty.pb.h" include\google\protobuf\empty.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\endian.h" include\google\protobuf\endian.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\explicitly_constructed.h" include\google\protobuf\explicitly_constructed.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\extension_set.h" include\google\protobuf\extension_set.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\extension_set_inl.h" include\google\protobuf\extension_set_inl.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\field_access_listener.h" include\google\protobuf\field_access_listener.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\field_mask.pb.h" include\google\protobuf\field_mask.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_enum_reflection.h" include\google\protobuf\generated_enum_reflection.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_enum_util.h" include\google\protobuf\generated_enum_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_bases.h" include\google\protobuf\generated_message_bases.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_reflection.h" include\google\protobuf\generated_message_reflection.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_tctable_decl.h" include\google\protobuf\generated_message_tctable_decl.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_tctable_impl.h" include\google\protobuf\generated_message_tctable_impl.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\generated_message_util.h" include\google\protobuf\generated_message_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\has_bits.h" include\google\protobuf\has_bits.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\implicit_weak_message.h" include\google\protobuf\implicit_weak_message.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\inlined_string_field.h" include\google\protobuf\inlined_string_field.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\coded_stream.h" include\google\protobuf\io\coded_stream.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\gzip_stream.h" include\google\protobuf\io\gzip_stream.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\io_win32.h" include\google\protobuf\io\io_win32.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\printer.h" include\google\protobuf\io\printer.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\strtod.h" include\google\protobuf\io\strtod.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\tokenizer.h" include\google\protobuf\io\tokenizer.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\zero_copy_stream.h" include\google\protobuf\io\zero_copy_stream.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\zero_copy_stream_impl.h" include\google\protobuf\io\zero_copy_stream_impl.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\io\zero_copy_stream_impl_lite.h" include\google\protobuf\io\zero_copy_stream_impl_lite.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\map.h" include\google\protobuf\map.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\map_entry.h" include\google\protobuf\map_entry.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\map_entry_lite.h" include\google\protobuf\map_entry_lite.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\map_field.h" include\google\protobuf\map_field.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\map_field_inl.h" include\google\protobuf\map_field_inl.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\map_field_lite.h" include\google\protobuf\map_field_lite.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\map_type_handler.h" include\google\protobuf\map_type_handler.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\message.h" include\google\protobuf\message.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\message_lite.h" include\google\protobuf\message_lite.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\metadata.h" include\google\protobuf\metadata.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\metadata_lite.h" include\google\protobuf\metadata_lite.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\parse_context.h" include\google\protobuf\parse_context.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\port.h" include\google\protobuf\port.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\port_def.inc" include\google\protobuf\port_def.inc
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\port_undef.inc" include\google\protobuf\port_undef.inc
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\reflection.h" include\google\protobuf\reflection.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\reflection_ops.h" include\google\protobuf\reflection_ops.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\repeated_field.h" include\google\protobuf\repeated_field.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\repeated_ptr_field.h" include\google\protobuf\repeated_ptr_field.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\service.h" include\google\protobuf\service.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\source_context.pb.h" include\google\protobuf\source_context.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\struct.pb.h" include\google\protobuf\struct.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\bytestream.h" include\google\protobuf\stubs\bytestream.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\callback.h" include\google\protobuf\stubs\callback.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\casts.h" include\google\protobuf\stubs\casts.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\common.h" include\google\protobuf\stubs\common.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\hash.h" include\google\protobuf\stubs\hash.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\logging.h" include\google\protobuf\stubs\logging.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\macros.h" include\google\protobuf\stubs\macros.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\map_util.h" include\google\protobuf\stubs\map_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\mutex.h" include\google\protobuf\stubs\mutex.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\once.h" include\google\protobuf\stubs\once.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\platform_macros.h" include\google\protobuf\stubs\platform_macros.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\port.h" include\google\protobuf\stubs\port.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\status.h" include\google\protobuf\stubs\status.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\stl_util.h" include\google\protobuf\stubs\stl_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\stringpiece.h" include\google\protobuf\stubs\stringpiece.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\strutil.h" include\google\protobuf\stubs\strutil.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\stubs\template_util.h" include\google\protobuf\stubs\template_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\text_format.h" include\google\protobuf\text_format.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\timestamp.pb.h" include\google\protobuf\timestamp.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\type.pb.h" include\google\protobuf\type.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\unknown_field_set.h" include\google\protobuf\unknown_field_set.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\delimited_message_util.h" include\google\protobuf\util\delimited_message_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\field_comparator.h" include\google\protobuf\util\field_comparator.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\field_mask_util.h" include\google\protobuf\util\field_mask_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\json_util.h" include\google\protobuf\util\json_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\message_differencer.h" include\google\protobuf\util\message_differencer.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\time_util.h" include\google\protobuf\util\time_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\type_resolver.h" include\google\protobuf\util\type_resolver.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\util\type_resolver_util.h" include\google\protobuf\util\type_resolver_util.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\wire_format.h" include\google\protobuf\wire_format.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\wire_format_lite.h" include\google\protobuf\wire_format_lite.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\wrappers.pb.h" include\google\protobuf\wrappers.pb.h
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\any.proto" include\google\protobuf\any.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\api.proto" include\google\protobuf\api.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\compiler\plugin.proto" include\google\protobuf\compiler\plugin.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\descriptor.proto" include\google\protobuf\descriptor.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\duration.proto" include\google\protobuf\duration.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\empty.proto" include\google\protobuf\empty.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\field_mask.proto" include\google\protobuf\field_mask.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\source_context.proto" include\google\protobuf\source_context.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\struct.proto" include\google\protobuf\struct.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\timestamp.proto" include\google\protobuf\timestamp.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\type.proto" include\google\protobuf\type.proto
-copy "${PROTOBUF_SOURCE_WIN32_PATH}\..\src\google\protobuf\wrappers.proto" include\google\protobuf\wrappers.proto
diff --git a/cmake/install.cmake b/cmake/install.cmake
index 2da8170..cf24e30 100644
--- a/cmake/install.cmake
+++ b/cmake/install.cmake
@@ -43,25 +43,23 @@
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/protobuf.pc ${CMAKE_CURRENT_BINARY_DIR}/protobuf-lite.pc DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
-file(STRINGS ${protobuf_SOURCE_DIR}/cmake/extract_includes.bat.in _extract_strings
- REGEX "^copy")
-foreach(_extract_string ${_extract_strings})
- string(REGEX REPLACE "^.* .+ include\\\\(.+)$" "\\1"
- _header ${_extract_string})
- string(REPLACE "\\" "/" _header ${_header})
+include(${protobuf_SOURCE_DIR}/src/file_lists.cmake)
+set(protobuf_HEADERS
+ ${libprotobuf_hdrs}
+ ${libprotoc_hdrs}
+ ${wkt_protos_files}
+ ${descriptor_proto_proto_srcs}
+ ${plugin_proto_proto_srcs}
+)
+foreach(_header ${protobuf_HEADERS})
+ string(REPLACE "${protobuf_SOURCE_DIR}/src" "" _header ${_header})
get_filename_component(_extract_from "${protobuf_SOURCE_DIR}/src/${_header}" ABSOLUTE)
get_filename_component(_extract_name ${_header} NAME)
get_filename_component(_extract_to "${CMAKE_INSTALL_INCLUDEDIR}/${_header}" DIRECTORY)
- if(EXISTS "${_extract_from}")
- install(FILES "${_extract_from}"
- DESTINATION "${_extract_to}"
- COMPONENT protobuf-headers
- RENAME "${_extract_name}")
- else()
- message(AUTHOR_WARNING "The file \"${_extract_from}\" is listed in "
- "\"${protobuf_SOURCE_DIR}/cmake/extract_includes.bat.in\" "
- "but there not exists. The file will not be installed.")
- endif()
+ install(FILES "${_extract_from}"
+ DESTINATION "${_extract_to}"
+ COMPONENT protobuf-headers
+ RENAME "${_extract_name}")
endforeach()
# Internal function for parsing auto tools scripts
diff --git a/cmake/libprotobuf-lite.cmake b/cmake/libprotobuf-lite.cmake
index 9b1de67..276c99c 100644
--- a/cmake/libprotobuf-lite.cmake
+++ b/cmake/libprotobuf-lite.cmake
@@ -30,7 +30,6 @@
endif()
set_target_properties(libprotobuf-lite PROPERTIES
VERSION ${protobuf_VERSION}
- SOVERSION 32
OUTPUT_NAME ${LIB_PREFIX}protobuf-lite
DEBUG_POSTFIX "${protobuf_DEBUG_POSTFIX}")
add_library(protobuf::libprotobuf-lite ALIAS libprotobuf-lite)
diff --git a/cmake/libprotobuf.cmake b/cmake/libprotobuf.cmake
index aa3efc9..0f34d20 100644
--- a/cmake/libprotobuf.cmake
+++ b/cmake/libprotobuf.cmake
@@ -33,7 +33,6 @@
endif()
set_target_properties(libprotobuf PROPERTIES
VERSION ${protobuf_VERSION}
- SOVERSION 32
OUTPUT_NAME ${LIB_PREFIX}protobuf
DEBUG_POSTFIX "${protobuf_DEBUG_POSTFIX}")
add_library(protobuf::libprotobuf ALIAS libprotobuf)
diff --git a/cmake/libprotoc.cmake b/cmake/libprotoc.cmake
index 72b4a1a..91e8bbe 100644
--- a/cmake/libprotoc.cmake
+++ b/cmake/libprotoc.cmake
@@ -24,7 +24,6 @@
set_target_properties(libprotoc PROPERTIES
COMPILE_DEFINITIONS LIBPROTOC_EXPORTS
VERSION ${protobuf_VERSION}
- SOVERSION 32
OUTPUT_NAME ${LIB_PREFIX}protoc
DEBUG_POSTFIX "${protobuf_DEBUG_POSTFIX}")
add_library(protobuf::libprotoc ALIAS libprotoc)
diff --git a/cmake/tests.cmake b/cmake/tests.cmake
index be1d429..6a1d1c0 100644
--- a/cmake/tests.cmake
+++ b/cmake/tests.cmake
@@ -1,6 +1,7 @@
option(protobuf_USE_EXTERNAL_GTEST "Use external Google Test (i.e. not the one in third_party/googletest)" OFF)
-option(protobuf_TEST_XML_OUTDIR "Output directory for XML logs from tests." "")
+option(protobuf_REMOVE_INSTALLED_HEADERS
+ "Remove local headers so that installed ones are used instead" OFF)
option(protobuf_ABSOLUTE_TEST_PLUGIN_PATH
"Using absolute test_plugin path in tests" ON)
@@ -77,10 +78,9 @@
add_library(protobuf-lite-test-common STATIC
${lite_test_util_srcs} ${lite_test_proto_files})
-target_link_libraries(protobuf-lite-test-common libprotobuf-lite GTest::gmock)
+target_link_libraries(protobuf-lite-test-common ${protobuf_LIB_PROTOBUF_LITE} GTest::gmock)
set(common_test_files
- ${lite_test_util_srcs}
${test_util_hdrs}
${test_util_srcs}
${mock_code_generator_srcs}
@@ -89,7 +89,7 @@
add_library(protobuf-test-common STATIC
${common_test_files} ${tests_proto_files})
-target_link_libraries(protobuf-test-common libprotobuf GTest::gmock)
+target_link_libraries(protobuf-test-common ${protobuf_LIB_PROTOBUF} GTest::gmock)
set(tests_files
${protobuf_test_files}
@@ -130,7 +130,7 @@
/wd4146 # unary minus operator applied to unsigned type, result still unsigned
)
endif()
-target_link_libraries(tests protobuf-lite-test-common protobuf-test-common libprotoc libprotobuf GTest::gmock_main)
+target_link_libraries(tests protobuf-lite-test-common protobuf-test-common ${protobuf_LIB_PROTOC} ${protobuf_LIB_PROTOBUF} GTest::gmock_main)
set(test_plugin_files
${test_plugin_files}
@@ -139,19 +139,50 @@
)
add_executable(test_plugin ${test_plugin_files})
-target_link_libraries(test_plugin libprotoc libprotobuf GTest::gmock)
+target_link_libraries(test_plugin ${protobuf_LIB_PROTOC} ${protobuf_LIB_PROTOBUF} GTest::gmock)
add_executable(lite-test ${protobuf_lite_test_files})
-target_link_libraries(lite-test protobuf-lite-test-common libprotobuf-lite GTest::gmock_main)
+target_link_libraries(lite-test protobuf-lite-test-common ${protobuf_LIB_PROTOBUF_LITE} GTest::gmock_main)
add_test(NAME lite-test
COMMAND lite-test ${protobuf_GTEST_ARGS})
add_custom_target(check
COMMAND tests
- DEPENDS tests test_plugin
+ DEPENDS tests lite-test test_plugin
WORKING_DIRECTORY ${protobuf_SOURCE_DIR})
add_test(NAME check
- COMMAND tests ${protobuf_GTEST_ARGS}
- WORKING_DIRECTORY "${protobuf_SOURCE_DIR}")
+ COMMAND tests ${protobuf_GTEST_ARGS})
+
+# For test purposes, remove headers that should already be installed. This
+# prevents accidental conflicts and also version skew (since local headers take
+# precedence over installed headers).
+add_custom_target(save-installed-headers)
+add_custom_target(remove-installed-headers)
+add_custom_target(restore-installed-headers)
+
+# Explicitly skip the bootstrapping headers as it's directly used in tests
+set(_installed_hdrs ${libprotobuf_hdrs} ${libprotoc_hdrs})
+list(REMOVE_ITEM _installed_hdrs
+ "${protobuf_SOURCE_DIR}/src/google/protobuf/descriptor.pb.h"
+ "${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/plugin.pb.h")
+
+foreach(_hdr ${_installed_hdrs})
+ string(REPLACE "${protobuf_SOURCE_DIR}/src" "" _file ${_hdr})
+ set(_tmp_file "${CMAKE_BINARY_DIR}/tmp-install-test/${_file}")
+ add_custom_command(TARGET remove-installed-headers PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E remove -f "${_hdr}")
+ add_custom_command(TARGET save-installed-headers PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E
+ copy "${_hdr}" "${_tmp_file}" || true)
+ add_custom_command(TARGET restore-installed-headers PRE_BUILD
+ COMMAND ${CMAKE_COMMAND} -E
+ copy "${_tmp_file}" "${_hdr}")
+endforeach()
+
+add_dependencies(remove-installed-headers save-installed-headers)
+if(protobuf_REMOVE_INSTALLED_HEADERS)
+ add_dependencies(protobuf-lite-test-common remove-installed-headers)
+ add_dependencies(protobuf-test-common remove-installed-headers)
+endif()
diff --git a/configure.ac b/configure.ac
index c55a6a2..64ddb3d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -17,7 +17,7 @@
# In the SVN trunk, the version should always be the next anticipated release
# version with the "-pre" suffix. (We used to use "-SNAPSHOT" but this pushed
# the size of one file name in the dist tarfile over the 99-char limit.)
-AC_INIT([Protocol Buffers],[3.21.2],[protobuf@googlegroups.com],[protobuf])
+AC_INIT([Protocol Buffers],[3.21.4],[protobuf@googlegroups.com],[protobuf])
AM_MAINTAINER_MODE([enable])
diff --git a/csharp/Google.Protobuf.Tools.nuspec b/csharp/Google.Protobuf.Tools.nuspec
index ea05597..90033cf 100644
--- a/csharp/Google.Protobuf.Tools.nuspec
+++ b/csharp/Google.Protobuf.Tools.nuspec
@@ -5,7 +5,7 @@
<title>Google Protocol Buffers tools</title>
<summary>Tools for Protocol Buffers - Google's data interchange format.</summary>
<description>See project site for more info.</description>
- <version>3.21.2</version>
+ <version>3.21.4</version>
<authors>Google Inc.</authors>
<owners>protobuf-packages</owners>
<licenseUrl>https://github.com/protocolbuffers/protobuf/blob/main/LICENSE</licenseUrl>
diff --git a/csharp/build_release.sh b/csharp/build_release.sh
index 93f318f..f9fff22 100755
--- a/csharp/build_release.sh
+++ b/csharp/build_release.sh
@@ -12,6 +12,3 @@
# Builds Google.Protobuf NuGet packages
dotnet restore src/Google.Protobuf.sln
dotnet pack -c Release src/Google.Protobuf.sln -p:ContinuousIntegrationBuild=true
-
-# This requires built protoc executables as specified in the nusepc
-nuget pack Google.Protobuf.Tools.nuspec
diff --git a/csharp/protos/unittest_issues.proto b/csharp/protos/unittest_issues.proto
index f46c20e..45df7a1 100644
--- a/csharp/protos/unittest_issues.proto
+++ b/csharp/protos/unittest_issues.proto
@@ -169,4 +169,20 @@
string x = 1;
string y = 2;
}
-}
\ No newline at end of file
+}
+
+// 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;
+}
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs b/csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs
index 57e59a9..d3284a4 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/MapUnittestProto3.cs
@@ -580,23 +580,23 @@
if (other == null) {
return;
}
- mapInt32Int32_.Add(other.mapInt32Int32_);
- mapInt64Int64_.Add(other.mapInt64Int64_);
- mapUint32Uint32_.Add(other.mapUint32Uint32_);
- mapUint64Uint64_.Add(other.mapUint64Uint64_);
- mapSint32Sint32_.Add(other.mapSint32Sint32_);
- mapSint64Sint64_.Add(other.mapSint64Sint64_);
- mapFixed32Fixed32_.Add(other.mapFixed32Fixed32_);
- mapFixed64Fixed64_.Add(other.mapFixed64Fixed64_);
- mapSfixed32Sfixed32_.Add(other.mapSfixed32Sfixed32_);
- mapSfixed64Sfixed64_.Add(other.mapSfixed64Sfixed64_);
- mapInt32Float_.Add(other.mapInt32Float_);
- mapInt32Double_.Add(other.mapInt32Double_);
- mapBoolBool_.Add(other.mapBoolBool_);
- mapStringString_.Add(other.mapStringString_);
- mapInt32Bytes_.Add(other.mapInt32Bytes_);
- mapInt32Enum_.Add(other.mapInt32Enum_);
- mapInt32ForeignMessage_.Add(other.mapInt32ForeignMessage_);
+ mapInt32Int32_.MergeFrom(other.mapInt32Int32_);
+ mapInt64Int64_.MergeFrom(other.mapInt64Int64_);
+ mapUint32Uint32_.MergeFrom(other.mapUint32Uint32_);
+ mapUint64Uint64_.MergeFrom(other.mapUint64Uint64_);
+ mapSint32Sint32_.MergeFrom(other.mapSint32Sint32_);
+ mapSint64Sint64_.MergeFrom(other.mapSint64Sint64_);
+ mapFixed32Fixed32_.MergeFrom(other.mapFixed32Fixed32_);
+ mapFixed64Fixed64_.MergeFrom(other.mapFixed64Fixed64_);
+ mapSfixed32Sfixed32_.MergeFrom(other.mapSfixed32Sfixed32_);
+ mapSfixed64Sfixed64_.MergeFrom(other.mapSfixed64Sfixed64_);
+ mapInt32Float_.MergeFrom(other.mapInt32Float_);
+ mapInt32Double_.MergeFrom(other.mapInt32Double_);
+ mapBoolBool_.MergeFrom(other.mapBoolBool_);
+ mapStringString_.MergeFrom(other.mapStringString_);
+ mapInt32Bytes_.MergeFrom(other.mapInt32Bytes_);
+ mapInt32Enum_.MergeFrom(other.mapInt32Enum_);
+ mapInt32ForeignMessage_.MergeFrom(other.mapInt32ForeignMessage_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -1100,7 +1100,7 @@
if (other == null) {
return;
}
- mapInt32Message_.Add(other.mapInt32Message_);
+ mapInt32Message_.MergeFrom(other.mapInt32Message_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -1298,8 +1298,8 @@
if (other == null) {
return;
}
- map1_.Add(other.map1_);
- map2_.Add(other.map2_);
+ map1_.MergeFrom(other.map1_);
+ map2_.MergeFrom(other.map2_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -1723,21 +1723,21 @@
if (other == null) {
return;
}
- mapInt32Int32_.Add(other.mapInt32Int32_);
- mapInt64Int64_.Add(other.mapInt64Int64_);
- mapUint32Uint32_.Add(other.mapUint32Uint32_);
- mapUint64Uint64_.Add(other.mapUint64Uint64_);
- mapSint32Sint32_.Add(other.mapSint32Sint32_);
- mapSint64Sint64_.Add(other.mapSint64Sint64_);
- mapFixed32Fixed32_.Add(other.mapFixed32Fixed32_);
- mapFixed64Fixed64_.Add(other.mapFixed64Fixed64_);
- mapSfixed32Sfixed32_.Add(other.mapSfixed32Sfixed32_);
- mapSfixed64Sfixed64_.Add(other.mapSfixed64Sfixed64_);
- mapInt32Float_.Add(other.mapInt32Float_);
- mapInt32Double_.Add(other.mapInt32Double_);
- mapBoolBool_.Add(other.mapBoolBool_);
- mapInt32Enum_.Add(other.mapInt32Enum_);
- mapInt32ForeignMessage_.Add(other.mapInt32ForeignMessage_);
+ mapInt32Int32_.MergeFrom(other.mapInt32Int32_);
+ mapInt64Int64_.MergeFrom(other.mapInt64Int64_);
+ mapUint32Uint32_.MergeFrom(other.mapUint32Uint32_);
+ mapUint64Uint64_.MergeFrom(other.mapUint64Uint64_);
+ mapSint32Sint32_.MergeFrom(other.mapSint32Sint32_);
+ mapSint64Sint64_.MergeFrom(other.mapSint64Sint64_);
+ mapFixed32Fixed32_.MergeFrom(other.mapFixed32Fixed32_);
+ mapFixed64Fixed64_.MergeFrom(other.mapFixed64Fixed64_);
+ mapSfixed32Sfixed32_.MergeFrom(other.mapSfixed32Sfixed32_);
+ mapSfixed64Sfixed64_.MergeFrom(other.mapSfixed64Sfixed64_);
+ mapInt32Float_.MergeFrom(other.mapInt32Float_);
+ mapInt32Double_.MergeFrom(other.mapInt32Double_);
+ mapBoolBool_.MergeFrom(other.mapBoolBool_);
+ mapInt32Enum_.MergeFrom(other.mapInt32Enum_);
+ mapInt32ForeignMessage_.MergeFrom(other.mapInt32ForeignMessage_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -2031,7 +2031,7 @@
if (other == null) {
return;
}
- type_.Add(other.type_);
+ type_.MergeFrom(other.type_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -2224,7 +2224,7 @@
if (other == null) {
return;
}
- entry_.Add(other.entry_);
+ entry_.MergeFrom(other.entry_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs
index 08b1aaf..d573455 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto2.cs
@@ -4344,25 +4344,25 @@
unpackedDouble_.Add(other.unpackedDouble_);
unpackedBool_.Add(other.unpackedBool_);
unpackedNestedEnum_.Add(other.unpackedNestedEnum_);
- mapInt32Int32_.Add(other.mapInt32Int32_);
- mapInt64Int64_.Add(other.mapInt64Int64_);
- mapUint32Uint32_.Add(other.mapUint32Uint32_);
- mapUint64Uint64_.Add(other.mapUint64Uint64_);
- mapSint32Sint32_.Add(other.mapSint32Sint32_);
- mapSint64Sint64_.Add(other.mapSint64Sint64_);
- mapFixed32Fixed32_.Add(other.mapFixed32Fixed32_);
- mapFixed64Fixed64_.Add(other.mapFixed64Fixed64_);
- mapSfixed32Sfixed32_.Add(other.mapSfixed32Sfixed32_);
- mapSfixed64Sfixed64_.Add(other.mapSfixed64Sfixed64_);
- mapInt32Float_.Add(other.mapInt32Float_);
- mapInt32Double_.Add(other.mapInt32Double_);
- mapBoolBool_.Add(other.mapBoolBool_);
- mapStringString_.Add(other.mapStringString_);
- mapStringBytes_.Add(other.mapStringBytes_);
- mapStringNestedMessage_.Add(other.mapStringNestedMessage_);
- mapStringForeignMessage_.Add(other.mapStringForeignMessage_);
- mapStringNestedEnum_.Add(other.mapStringNestedEnum_);
- mapStringForeignEnum_.Add(other.mapStringForeignEnum_);
+ mapInt32Int32_.MergeFrom(other.mapInt32Int32_);
+ mapInt64Int64_.MergeFrom(other.mapInt64Int64_);
+ mapUint32Uint32_.MergeFrom(other.mapUint32Uint32_);
+ mapUint64Uint64_.MergeFrom(other.mapUint64Uint64_);
+ mapSint32Sint32_.MergeFrom(other.mapSint32Sint32_);
+ mapSint64Sint64_.MergeFrom(other.mapSint64Sint64_);
+ mapFixed32Fixed32_.MergeFrom(other.mapFixed32Fixed32_);
+ mapFixed64Fixed64_.MergeFrom(other.mapFixed64Fixed64_);
+ mapSfixed32Sfixed32_.MergeFrom(other.mapSfixed32Sfixed32_);
+ mapSfixed64Sfixed64_.MergeFrom(other.mapSfixed64Sfixed64_);
+ mapInt32Float_.MergeFrom(other.mapInt32Float_);
+ mapInt32Double_.MergeFrom(other.mapInt32Double_);
+ mapBoolBool_.MergeFrom(other.mapBoolBool_);
+ mapStringString_.MergeFrom(other.mapStringString_);
+ mapStringBytes_.MergeFrom(other.mapStringBytes_);
+ mapStringNestedMessage_.MergeFrom(other.mapStringNestedMessage_);
+ mapStringForeignMessage_.MergeFrom(other.mapStringForeignMessage_);
+ mapStringNestedEnum_.MergeFrom(other.mapStringNestedEnum_);
+ mapStringForeignEnum_.MergeFrom(other.mapStringForeignEnum_);
if (other.HasData) {
if (!HasData) {
Data = new global::ProtobufTestMessages.Proto2.TestAllTypesProto2.Types.Data();
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
index 520216f..74e2a57 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/TestMessagesProto3.cs
@@ -3753,25 +3753,25 @@
unpackedDouble_.Add(other.unpackedDouble_);
unpackedBool_.Add(other.unpackedBool_);
unpackedNestedEnum_.Add(other.unpackedNestedEnum_);
- mapInt32Int32_.Add(other.mapInt32Int32_);
- mapInt64Int64_.Add(other.mapInt64Int64_);
- mapUint32Uint32_.Add(other.mapUint32Uint32_);
- mapUint64Uint64_.Add(other.mapUint64Uint64_);
- mapSint32Sint32_.Add(other.mapSint32Sint32_);
- mapSint64Sint64_.Add(other.mapSint64Sint64_);
- mapFixed32Fixed32_.Add(other.mapFixed32Fixed32_);
- mapFixed64Fixed64_.Add(other.mapFixed64Fixed64_);
- mapSfixed32Sfixed32_.Add(other.mapSfixed32Sfixed32_);
- mapSfixed64Sfixed64_.Add(other.mapSfixed64Sfixed64_);
- mapInt32Float_.Add(other.mapInt32Float_);
- mapInt32Double_.Add(other.mapInt32Double_);
- mapBoolBool_.Add(other.mapBoolBool_);
- mapStringString_.Add(other.mapStringString_);
- mapStringBytes_.Add(other.mapStringBytes_);
- mapStringNestedMessage_.Add(other.mapStringNestedMessage_);
- mapStringForeignMessage_.Add(other.mapStringForeignMessage_);
- mapStringNestedEnum_.Add(other.mapStringNestedEnum_);
- mapStringForeignEnum_.Add(other.mapStringForeignEnum_);
+ mapInt32Int32_.MergeFrom(other.mapInt32Int32_);
+ mapInt64Int64_.MergeFrom(other.mapInt64Int64_);
+ mapUint32Uint32_.MergeFrom(other.mapUint32Uint32_);
+ mapUint64Uint64_.MergeFrom(other.mapUint64Uint64_);
+ mapSint32Sint32_.MergeFrom(other.mapSint32Sint32_);
+ mapSint64Sint64_.MergeFrom(other.mapSint64Sint64_);
+ mapFixed32Fixed32_.MergeFrom(other.mapFixed32Fixed32_);
+ mapFixed64Fixed64_.MergeFrom(other.mapFixed64Fixed64_);
+ mapSfixed32Sfixed32_.MergeFrom(other.mapSfixed32Sfixed32_);
+ mapSfixed64Sfixed64_.MergeFrom(other.mapSfixed64Sfixed64_);
+ mapInt32Float_.MergeFrom(other.mapInt32Float_);
+ mapInt32Double_.MergeFrom(other.mapInt32Double_);
+ mapBoolBool_.MergeFrom(other.mapBoolBool_);
+ mapStringString_.MergeFrom(other.mapStringString_);
+ mapStringBytes_.MergeFrom(other.mapStringBytes_);
+ mapStringNestedMessage_.MergeFrom(other.mapStringNestedMessage_);
+ mapStringForeignMessage_.MergeFrom(other.mapStringForeignMessage_);
+ mapStringNestedEnum_.MergeFrom(other.mapStringNestedEnum_);
+ mapStringForeignEnum_.MergeFrom(other.mapStringForeignEnum_);
if (other.optionalBoolWrapper_ != null) {
if (optionalBoolWrapper_ == null || other.OptionalBoolWrapper != false) {
OptionalBoolWrapper = other.OptionalBoolWrapper;
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs b/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs
index c1f43ce..7f1aca1 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/Unittest.cs
@@ -24112,7 +24112,7 @@
if (other == null) {
return;
}
- foo_.Add(other.foo_);
+ foo_.MergeFrom(other.foo_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
@@ -30708,7 +30708,7 @@
}
OptionalGroup.MergeFrom(other.OptionalGroup);
}
- stringStringMap_.Add(other.stringStringMap_);
+ stringStringMap_.MergeFrom(other.stringStringMap_);
switch (other.OneofFieldCase) {
case OneofFieldOneofCase.OneofUint32:
OneofUint32 = other.OneofUint32;
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
index 3fe2dd6..d65a656 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestIssues.cs
@@ -56,11 +56,17 @@
"dWxhcl9maWVsZBgBIAEoCRIbCg5vcHRpb25hbF9maWVsZBgCIAEoCUgAiAEB",
"QhEKD19vcHRpb25hbF9maWVsZCI5ChJPbmVvZldpdGhOb25lRmllbGQSCwoB",
"eBgBIAEoCUgAEg4KBG5vbmUYAiABKAlIAEIGCgR0ZXN0IjUKEU9uZW9mV2l0",
- "aE5vbmVOYW1lEgsKAXgYASABKAlIABILCgF5GAIgASgJSABCBgoEbm9uZSpV",
- "CgxOZWdhdGl2ZUVudW0SFgoSTkVHQVRJVkVfRU5VTV9aRVJPEAASFgoJRml2",
- "ZUJlbG93EPv//////////wESFQoITWludXNPbmUQ////////////ASouCg5E",
- "ZXByZWNhdGVkRW51bRITCg9ERVBSRUNBVEVEX1pFUk8QABIHCgNvbmUQAUId",
- "qgIaVW5pdFRlc3QuSXNzdWVzLlRlc3RQcm90b3NiBnByb3RvMw=="));
+ "aE5vbmVOYW1lEgsKAXgYASABKAlIABILCgF5GAIgASgJSABCBgoEbm9uZSKT",
+ "AgoZRGlzYW1iaWd1YXRlQ29tbW9uTWVtYmVycxIjChtkaXNhbWJpZ3VhdGVf",
+ "Y29tbW9uX21lbWJlcnMYASABKAUSDQoFdHlwZXMYAiABKAUSEgoKZGVzY3Jp",
+ "cHRvchgDIAEoBRIOCgZlcXVhbHMYBCABKAUSEQoJdG9fc3RyaW5nGAUgASgF",
+ "EhUKDWdldF9oYXNoX2NvZGUYBiABKAUSEAoId3JpdGVfdG8YByABKAUSDQoF",
+ "Y2xvbmUYCCABKAUSFgoOY2FsY3VsYXRlX3NpemUYCSABKAUSEgoKbWVyZ2Vf",
+ "ZnJvbRgKIAEoBRIXCg9vbl9jb25zdHJ1Y3Rpb24YCyABKAUSDgoGcGFyc2Vy",
+ "GAwgASgFKlUKDE5lZ2F0aXZlRW51bRIWChJORUdBVElWRV9FTlVNX1pFUk8Q",
+ "ABIWCglGaXZlQmVsb3cQ+///////////ARIVCghNaW51c09uZRD/////////",
+ "//8BKi4KDkRlcHJlY2F0ZWRFbnVtEhMKD0RFUFJFQ0FURURfWkVSTxAAEgcK",
+ "A29uZRABQh2qAhpVbml0VGVzdC5Jc3N1ZXMuVGVzdFByb3Rvc2IGcHJvdG8z"));
descriptor = pbr::FileDescriptor.FromGeneratedCode(descriptorData,
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[] {
@@ -77,7 +83,8 @@
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.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
@@ -4347,6 +4354,605 @@
}
+ /// <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
}
diff --git a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs
index 3ec8d35..50b9046 100644
--- a/csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs
+++ b/csharp/src/Google.Protobuf.Test.TestProtos/UnittestWellKnownTypes.cs
@@ -3258,24 +3258,24 @@
if (other == null) {
return;
}
- anyField_.Add(other.anyField_);
- apiField_.Add(other.apiField_);
- durationField_.Add(other.durationField_);
- emptyField_.Add(other.emptyField_);
- fieldMaskField_.Add(other.fieldMaskField_);
- sourceContextField_.Add(other.sourceContextField_);
- structField_.Add(other.structField_);
- timestampField_.Add(other.timestampField_);
- typeField_.Add(other.typeField_);
- doubleField_.Add(other.doubleField_);
- floatField_.Add(other.floatField_);
- int64Field_.Add(other.int64Field_);
- uint64Field_.Add(other.uint64Field_);
- int32Field_.Add(other.int32Field_);
- uint32Field_.Add(other.uint32Field_);
- boolField_.Add(other.boolField_);
- stringField_.Add(other.stringField_);
- bytesField_.Add(other.bytesField_);
+ anyField_.MergeFrom(other.anyField_);
+ apiField_.MergeFrom(other.apiField_);
+ durationField_.MergeFrom(other.durationField_);
+ emptyField_.MergeFrom(other.emptyField_);
+ fieldMaskField_.MergeFrom(other.fieldMaskField_);
+ sourceContextField_.MergeFrom(other.sourceContextField_);
+ structField_.MergeFrom(other.structField_);
+ timestampField_.MergeFrom(other.timestampField_);
+ typeField_.MergeFrom(other.typeField_);
+ doubleField_.MergeFrom(other.doubleField_);
+ floatField_.MergeFrom(other.floatField_);
+ int64Field_.MergeFrom(other.int64Field_);
+ uint64Field_.MergeFrom(other.uint64Field_);
+ int32Field_.MergeFrom(other.int32Field_);
+ uint32Field_.MergeFrom(other.uint32Field_);
+ boolField_.MergeFrom(other.boolField_);
+ stringField_.MergeFrom(other.stringField_);
+ bytesField_.MergeFrom(other.bytesField_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
diff --git a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
index 8387291..17c5249 100644
--- a/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
+++ b/csharp/src/Google.Protobuf.Test/GeneratedMessageTest.cs
@@ -36,6 +36,7 @@
using NUnit.Framework;
using System.Linq;
using Google.Protobuf.WellKnownTypes;
+using Google.Protobuf.Collections;
namespace Google.Protobuf
{
@@ -795,5 +796,44 @@
EqualityTester.AssertInequality(message1, message2);
EqualityTester.AssertEquality(message1, message3);
}
+
+ [Test]
+ [TestCase(false)]
+ [TestCase(true)]
+ public void MapFieldMerging(bool direct)
+ {
+ var message1 = new TestMap
+ {
+ MapStringString =
+ {
+ { "x1", "y1" },
+ { "common", "message1" }
+ }
+ };
+ var message2 = new TestMap
+ {
+ MapStringString =
+ {
+ { "x2", "y2" },
+ { "common", "message2" }
+ }
+ };
+ if (direct)
+ {
+ message1.MergeFrom(message2);
+ }
+ else
+ {
+ message1.MergeFrom(message2.ToByteArray());
+ }
+
+ var expected = new MapField<string, string>
+ {
+ { "x1", "y1" },
+ { "x2", "y2" },
+ { "common", "message2" }
+ };
+ Assert.AreEqual(expected, message1.MapStringString);
+ }
}
}
\ No newline at end of file
diff --git a/csharp/src/Google.Protobuf.Test/JsonFormatterSettingsTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterSettingsTest.cs
new file mode 100644
index 0000000..f7ea97c
--- /dev/null
+++ b/csharp/src/Google.Protobuf.Test/JsonFormatterSettingsTest.cs
@@ -0,0 +1,111 @@
+#region Copyright notice and license
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2008 Google Inc. All rights reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#endregion
+
+using Google.Protobuf.Reflection;
+using NUnit.Framework;
+
+// For WrapInQuotes
+
+namespace Google.Protobuf
+{
+ public class JsonFormatterSettingsTest
+ {
+ [Test]
+ public void WithIndentation()
+ {
+ var settings = JsonFormatter.Settings.Default.WithIndentation("\t");
+ Assert.AreEqual("\t", settings.Indentation);
+ }
+
+ [Test]
+ public void WithTypeRegistry()
+ {
+ var typeRegistry = TypeRegistry.Empty;
+ var settings = JsonFormatter.Settings.Default.WithTypeRegistry(typeRegistry);
+ Assert.AreEqual(typeRegistry, settings.TypeRegistry);
+ }
+
+ [Test]
+ public void WithFormatDefaultValues()
+ {
+ var settingsWith = JsonFormatter.Settings.Default.WithFormatDefaultValues(true);
+ Assert.AreEqual(true, settingsWith.FormatDefaultValues);
+
+ var settingsWithout = JsonFormatter.Settings.Default.WithFormatDefaultValues(false);
+ Assert.AreEqual(false, settingsWithout.FormatDefaultValues);
+ }
+
+ [Test]
+ public void WithFormatEnumsAsIntegers()
+ {
+ var settingsWith = JsonFormatter.Settings.Default.WithFormatEnumsAsIntegers(true);
+ Assert.AreEqual(true, settingsWith.FormatEnumsAsIntegers);
+
+ var settingsWithout = JsonFormatter.Settings.Default.WithFormatEnumsAsIntegers(false);
+ Assert.AreEqual(false, settingsWithout.FormatEnumsAsIntegers);
+ }
+
+ [Test]
+ public void WithMethodsPreserveExistingSettings()
+ {
+ var typeRegistry = TypeRegistry.Empty;
+ var baseSettings = JsonFormatter.Settings.Default
+ .WithIndentation("\t")
+ .WithFormatDefaultValues(true)
+ .WithFormatEnumsAsIntegers(true)
+ .WithTypeRegistry(typeRegistry)
+ .WithPreserveProtoFieldNames(true);
+
+ var settings1 = baseSettings.WithIndentation("\t");
+ var settings2 = baseSettings.WithFormatDefaultValues(true);
+ var settings3 = baseSettings.WithFormatEnumsAsIntegers(true);
+ var settings4 = baseSettings.WithTypeRegistry(typeRegistry);
+ var settings5 = baseSettings.WithPreserveProtoFieldNames(true);
+
+ AssertAreEqual(baseSettings, settings1);
+ AssertAreEqual(baseSettings, settings2);
+ AssertAreEqual(baseSettings, settings3);
+ AssertAreEqual(baseSettings, settings4);
+ AssertAreEqual(baseSettings, settings5);
+ }
+
+ private static void AssertAreEqual(JsonFormatter.Settings settings, JsonFormatter.Settings other)
+ {
+ Assert.AreEqual(settings.Indentation, other.Indentation);
+ Assert.AreEqual(settings.FormatDefaultValues, other.FormatDefaultValues);
+ Assert.AreEqual(settings.FormatEnumsAsIntegers, other.FormatEnumsAsIntegers);
+ Assert.AreEqual(settings.TypeRegistry, other.TypeRegistry);
+ }
+ }
+}
diff --git a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
index 714c78c..f4dfde2 100644
--- a/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
+++ b/csharp/src/Google.Protobuf.Test/JsonFormatterTest.cs
@@ -675,6 +675,200 @@
}
[Test]
+ public void WriteValueWithIndentation_EmptyMessage()
+ {
+ var value = new TestEmptyMessage();
+
+ AssertWriteValue(value, "{}", JsonFormatter.Settings.Default.WithIndentation());
+ }
+
+ [Test]
+ public void WriteValueWithIndentation_NestedTestAllTypes()
+ {
+ var value = new NestedTestAllTypes
+ {
+ Payload = new TestAllTypes
+ {
+ SingleBool = true,
+ SingleInt32 = 100,
+ SingleString = "multiple fields",
+ RepeatedString = { "string1", "string2" },
+ },
+ Child = new NestedTestAllTypes
+ {
+ Payload = new TestAllTypes
+ {
+ SingleString = "single field",
+ },
+ },
+ RepeatedChild =
+ {
+ new NestedTestAllTypes { Payload = new TestAllTypes { SingleString = "child 1", RepeatedString = { "string" } } },
+ new NestedTestAllTypes { Payload = new TestAllTypes { SingleString = "child 2" } },
+ },
+ };
+
+ const string expectedJson = @"
+{
+ 'child': {
+ 'payload': {
+ 'singleString': 'single field'
+ }
+ },
+ 'payload': {
+ 'singleInt32': 100,
+ 'singleBool': true,
+ 'singleString': 'multiple fields',
+ 'repeatedString': [
+ 'string1',
+ 'string2'
+ ]
+ },
+ 'repeatedChild': [
+ {
+ 'payload': {
+ 'singleString': 'child 1',
+ 'repeatedString': [
+ 'string'
+ ]
+ }
+ },
+ {
+ 'payload': {
+ 'singleString': 'child 2'
+ }
+ }
+ ]
+}";
+ AssertWriteValue(value, expectedJson, JsonFormatter.Settings.Default.WithIndentation());
+ }
+
+ [Test]
+ public void WriteValueWithIndentation_WellKnownTypes()
+ {
+ var value = new TestWellKnownTypes
+ {
+ StructField = new Struct
+ {
+ Fields =
+ {
+ { "string", Value.ForString("foo") },
+ { "numbers", Value.ForList(Value.ForNumber(1), Value.ForNumber(2), Value.ForNumber(3)) },
+ { "emptyList", Value.ForList() },
+ { "emptyStruct", Value.ForStruct(new Struct()) },
+ },
+ },
+ };
+
+ const string expectedJson = @"
+{
+ 'structField': {
+ 'string': 'foo',
+ 'numbers': [
+ 1,
+ 2,
+ 3
+ ],
+ 'emptyList': [],
+ 'emptyStruct': {}
+ }
+}";
+ AssertWriteValue(value, expectedJson, JsonFormatter.Settings.Default.WithIndentation());
+ }
+
+ [Test]
+ public void WriteValueWithIndentation_StructSingleField()
+ {
+ var value = new Struct { Fields = { { "structField1", Value.ForString("structFieldValue1") } } };
+
+ const string expectedJson = @"
+{
+ 'structField1': 'structFieldValue1'
+}";
+ AssertWriteValue(value, expectedJson, JsonFormatter.Settings.Default.WithIndentation());
+ }
+
+ [Test]
+ public void WriteValueWithIndentation_StructMultipleFields()
+ {
+ var value = new Struct
+ {
+ Fields =
+ {
+ { "structField1", Value.ForString("structFieldValue1") },
+ { "structField2", Value.ForString("structFieldValue2") },
+ { "structField3", Value.ForString("structFieldValue3") },
+ },
+ };
+
+ const string expectedJson = @"
+{
+ 'structField1': 'structFieldValue1',
+ 'structField2': 'structFieldValue2',
+ 'structField3': 'structFieldValue3'
+}";
+ AssertWriteValue(value, expectedJson, JsonFormatter.Settings.Default.WithIndentation());
+ }
+
+ [Test]
+ public void FormatWithIndentation_EmbeddedMessage()
+ {
+ var value = new TestAllTypes { SingleInt32 = 100, SingleInt64 = 3210987654321L };
+ var formatter = new JsonFormatter(JsonFormatter.Settings.Default.WithIndentation());
+ var valueJson = formatter.Format(value, indentationLevel: 1);
+
+ var actualJson = $@"
+{{
+ ""data"": {valueJson}
+}}";
+ const string expectedJson = @"
+{
+ 'data': {
+ 'singleInt32': 100,
+ 'singleInt64': '3210987654321'
+ }
+}";
+ AssertJson(expectedJson, actualJson.Trim());
+ }
+
+ [Test]
+ public void WriteValueWithIndentation_Map()
+ {
+ var value = new TestMap
+ {
+ MapStringString =
+ {
+ { "key1", "value1" },
+ { "key2", "value2" },
+ },
+ };
+
+ const string expectedJson = @"
+{
+ 'mapStringString': {
+ 'key1': 'value1',
+ 'key2': 'value2'
+ }
+}";
+
+ AssertWriteValue(value, expectedJson, JsonFormatter.Settings.Default.WithIndentation());
+ }
+
+ [Test]
+ public void WriteValueWithIndentation_List()
+ {
+ var value = new RepeatedField<int> { 1, 2, 3 };
+ AssertWriteValue(value, "[\n 1,\n 2,\n 3\n]", JsonFormatter.Settings.Default.WithIndentation());
+ }
+
+ [Test]
+ public void WriteValueWithIndentation_CustomIndentation()
+ {
+ var value = new RepeatedField<int> { 1, 2, 3 };
+ AssertWriteValue(value, "[\n\t1,\n\t2,\n\t3\n]", JsonFormatter.Settings.Default.WithIndentation("\t"));
+ }
+
+ [Test]
public void Proto2_DefaultValuesWritten()
{
var value = new ProtobufTestMessages.Proto2.TestAllTypesProto2() { FieldName13 = 0 };
@@ -683,7 +877,7 @@
private static void AssertWriteValue(object value, string expectedJson, JsonFormatter.Settings settings = null)
{
- var writer = new StringWriter();
+ var writer = new StringWriter { NewLine = "\n" };
new JsonFormatter(settings ?? JsonFormatter.Settings.Default).WriteValue(writer, value);
string actual = writer.ToString();
AssertJson(expectedJson, actual);
@@ -691,13 +885,17 @@
/// <summary>
/// Checks that the actual JSON is the same as the expected JSON - but after replacing
- /// all apostrophes in the expected JSON with double quotes. This basically makes the tests easier
- /// to read.
+ /// all apostrophes in the expected JSON with double quotes, trimming leading whitespace and normalizing new lines.
+ /// This basically makes the tests easier to read.
/// </summary>
+ /// <remarks>
+ /// Line endings are normalized because indented JSON strings are generated with system-specific line endings,
+ /// while line endings in the test cases are hard-coded, but may be converted during source checkout, depending
+ /// on git settings, causing unpredictability in the test results otherwise.</remarks>
private static void AssertJson(string expectedJsonWithApostrophes, string actualJson)
{
- var expectedJson = expectedJsonWithApostrophes.Replace("'", "\"");
- Assert.AreEqual(expectedJson, actualJson);
+ var expectedJson = expectedJsonWithApostrophes.Replace("'", "\"").Replace("\r\n", "\n").TrimStart();
+ Assert.AreEqual(expectedJson, actualJson.Replace("\r\n", "\n"));
}
}
}
diff --git a/csharp/src/Google.Protobuf.Test/testprotos.pb b/csharp/src/Google.Protobuf.Test/testprotos.pb
index 5f41bc4..bdfe1cc 100644
--- a/csharp/src/Google.Protobuf.Test/testprotos.pb
+++ b/csharp/src/Google.Protobuf.Test/testprotos.pb
Binary files differ
diff --git a/csharp/src/Google.Protobuf/Collections/MapField.cs b/csharp/src/Google.Protobuf/Collections/MapField.cs
index f0124ee..09afb75 100644
--- a/csharp/src/Google.Protobuf/Collections/MapField.cs
+++ b/csharp/src/Google.Protobuf/Collections/MapField.cs
@@ -238,6 +238,21 @@
}
/// <summary>
+ /// Adds the specified entries to the map, replacing any existing entries with the same keys.
+ /// The keys and values are not automatically cloned.
+ /// </summary>
+ /// <remarks>This method primarily exists to be called from MergeFrom methods in generated classes for messages.</remarks>
+ /// <param name="entries">The entries to add to the map.</param>
+ public void MergeFrom(IDictionary<TKey, TValue> entries)
+ {
+ ProtoPreconditions.CheckNotNull(entries, nameof(entries));
+ foreach (var pair in entries)
+ {
+ this[pair.Key] = pair.Value;
+ }
+ }
+
+ /// <summary>
/// Returns an enumerator that iterates through the collection.
/// </summary>
/// <returns>
diff --git a/csharp/src/Google.Protobuf/Google.Protobuf.csproj b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
index 37d86ce..c95ecad 100644
--- a/csharp/src/Google.Protobuf/Google.Protobuf.csproj
+++ b/csharp/src/Google.Protobuf/Google.Protobuf.csproj
@@ -4,7 +4,7 @@
<Description>C# runtime library for Protocol Buffers - Google's data interchange format.</Description>
<Copyright>Copyright 2015, Google Inc.</Copyright>
<AssemblyTitle>Google Protocol Buffers</AssemblyTitle>
- <VersionPrefix>3.21.2</VersionPrefix>
+ <VersionPrefix>3.21.4</VersionPrefix>
<LangVersion>10.0</LangVersion>
<Authors>Google Inc.</Authors>
<TargetFrameworks>netstandard1.1;netstandard2.0;net45;net50</TargetFrameworks>
diff --git a/csharp/src/Google.Protobuf/JsonFormatter.cs b/csharp/src/Google.Protobuf/JsonFormatter.cs
index 2ef10ee..9cecb6a 100644
--- a/csharp/src/Google.Protobuf/JsonFormatter.cs
+++ b/csharp/src/Google.Protobuf/JsonFormatter.cs
@@ -63,7 +63,12 @@
internal const string AnyDiagnosticValueField = "@value";
internal const string AnyWellKnownTypeValueField = "value";
private const string NameValueSeparator = ": ";
- private const string PropertySeparator = ", ";
+ private const string ValueSeparator = ", ";
+ private const string MultilineValueSeparator = ",";
+ private const char ObjectOpenBracket = '{';
+ private const char ObjectCloseBracket = '}';
+ private const char ListBracketOpen = '[';
+ private const char ListBracketClose = ']';
/// <summary>
/// Returns a formatter using the default settings.
@@ -140,11 +145,26 @@
/// Formats the specified message as JSON.
/// </summary>
/// <param name="message">The message to format.</param>
+ /// <remarks>This method delegates to <c>Format(IMessage, int)</c> with <c>indentationLevel = 0</c>.</remarks>
/// <returns>The formatted message.</returns>
- public string Format(IMessage message)
+ public string Format(IMessage message) => Format(message, indentationLevel: 0);
+
+ /// <summary>
+ /// Formats the specified message as JSON.
+ /// </summary>
+ /// <param name="message">The message to format.</param>
+ /// <param name="indentationLevel">Indentation level to start at.</param>
+ /// <remarks>To keep consistent indentation when embedding a message inside another JSON string, set <paramref name="indentationLevel"/>. E.g:
+ /// <code>
+ /// var response = $@"{{
+ /// ""data"": { Format(message, indentationLevel: 1) }
+ /// }}"</code>
+ /// </remarks>
+ /// <returns>The formatted message.</returns>
+ public string Format(IMessage message, int indentationLevel)
{
var writer = new StringWriter();
- Format(message, writer);
+ Format(message, writer, indentationLevel);
return writer.ToString();
}
@@ -153,19 +173,29 @@
/// </summary>
/// <param name="message">The message to format.</param>
/// <param name="writer">The TextWriter to write the formatted message to.</param>
+ /// <remarks>This method delegates to <c>Format(IMessage, TextWriter, int)</c> with <c>indentationLevel = 0</c>.</remarks>
/// <returns>The formatted message.</returns>
- public void Format(IMessage message, TextWriter writer)
+ public void Format(IMessage message, TextWriter writer) => Format(message, writer, indentationLevel: 0);
+
+ /// <summary>
+ /// Formats the specified message as JSON. When <see cref="Settings.Indentation"/> is not null, start indenting at the specified <paramref name="indentationLevel"/>.
+ /// </summary>
+ /// <param name="message">The message to format.</param>
+ /// <param name="writer">The TextWriter to write the formatted message to.</param>
+ /// <param name="indentationLevel">Indentation level to start at.</param>
+ /// <remarks>To keep consistent indentation when embedding a message inside another JSON string, set <paramref name="indentationLevel"/>.</remarks>
+ public void Format(IMessage message, TextWriter writer, int indentationLevel)
{
ProtoPreconditions.CheckNotNull(message, nameof(message));
ProtoPreconditions.CheckNotNull(writer, nameof(writer));
if (message.Descriptor.IsWellKnownType)
{
- WriteWellKnownTypeValue(writer, message.Descriptor, message);
+ WriteWellKnownTypeValue(writer, message.Descriptor, message, indentationLevel);
}
else
{
- WriteMessage(writer, message);
+ WriteMessage(writer, message, indentationLevel);
}
}
@@ -192,7 +222,7 @@
return diagnosticFormatter.Format(message);
}
- private void WriteMessage(TextWriter writer, IMessage message)
+ private void WriteMessage(TextWriter writer, IMessage message, int indentationLevel)
{
if (message == null)
{
@@ -207,12 +237,13 @@
return;
}
}
- writer.Write("{ ");
- bool writtenFields = WriteMessageFields(writer, message, false);
- writer.Write(writtenFields ? " }" : "}");
+
+ WriteBracketOpen(writer, ObjectOpenBracket);
+ bool writtenFields = WriteMessageFields(writer, message, false, indentationLevel + 1);
+ WriteBracketClose(writer, ObjectCloseBracket, writtenFields, indentationLevel);
}
- private bool WriteMessageFields(TextWriter writer, IMessage message, bool assumeFirstFieldWritten)
+ private bool WriteMessageFields(TextWriter writer, IMessage message, bool assumeFirstFieldWritten, int indentationLevel)
{
var fields = message.Descriptor.Fields;
bool first = !assumeFirstFieldWritten;
@@ -226,10 +257,8 @@
continue;
}
- if (!first)
- {
- writer.Write(PropertySeparator);
- }
+ MaybeWriteValueSeparator(writer, first);
+ MaybeWriteValueWhitespace(writer, indentationLevel);
if (settings.PreserveProtoFieldNames)
{
@@ -240,13 +269,23 @@
WriteString(writer, accessor.Descriptor.JsonName);
}
writer.Write(NameValueSeparator);
- WriteValue(writer, value);
+ WriteValue(writer, value, indentationLevel);
first = false;
}
return !first;
}
+ private void MaybeWriteValueSeparator(TextWriter writer, bool first)
+ {
+ if (first)
+ {
+ return;
+ }
+
+ writer.Write(settings.Indentation == null ? ValueSeparator : MultilineValueSeparator);
+ }
+
/// <summary>
/// Determines whether or not a field value should be serialized according to the field,
/// its value in the message, and the settings of this formatter.
@@ -342,7 +381,19 @@
/// </summary>
/// <param name="writer">The writer to write the value to. Must not be null.</param>
/// <param name="value">The value to write. May be null.</param>
- public void WriteValue(TextWriter writer, object value)
+ /// <remarks>Delegates to <c>WriteValue(TextWriter, object, int)</c> with <c>indentationLevel = 0</c>.</remarks>
+ public void WriteValue(TextWriter writer, object value) => WriteValue(writer, value, 0);
+
+ /// <summary>
+ /// Writes a single value to the given writer as JSON. Only types understood by
+ /// Protocol Buffers can be written in this way. This method is only exposed for
+ /// advanced use cases; most users should be using <see cref="Format(IMessage)"/>
+ /// or <see cref="Format(IMessage, TextWriter)"/>.
+ /// </summary>
+ /// <param name="writer">The writer to write the value to. Must not be null.</param>
+ /// <param name="value">The value to write. May be null.</param>
+ /// <param name="indentationLevel">The current indentationLevel. Not used when <see cref="Settings.Indentation"/> is null.</param>
+ public void WriteValue(TextWriter writer, object value, int indentationLevel)
{
if (value == null || value is NullValue)
{
@@ -365,11 +416,11 @@
}
else if (value is IDictionary dictionary)
{
- WriteDictionary(writer, dictionary);
+ WriteDictionary(writer, dictionary, indentationLevel);
}
else if (value is IList list)
{
- WriteList(writer, list);
+ WriteList(writer, list, indentationLevel);
}
else if (value is int || value is uint)
{
@@ -418,7 +469,7 @@
}
else if (value is IMessage message)
{
- Format(message, writer);
+ Format(message, writer, indentationLevel);
}
else
{
@@ -432,7 +483,7 @@
/// values are using the embedded well-known types, in order to allow for dynamic messages
/// in the future.
/// </summary>
- private void WriteWellKnownTypeValue(TextWriter writer, MessageDescriptor descriptor, object value)
+ private void WriteWellKnownTypeValue(TextWriter writer, MessageDescriptor descriptor, object value, int indentationLevel)
{
// Currently, we can never actually get here, because null values are always handled by the caller. But if we *could*,
// this would do the right thing.
@@ -472,26 +523,26 @@
}
if (descriptor.FullName == Struct.Descriptor.FullName)
{
- WriteStruct(writer, (IMessage)value);
+ WriteStruct(writer, (IMessage)value, indentationLevel);
return;
}
if (descriptor.FullName == ListValue.Descriptor.FullName)
{
var fieldAccessor = descriptor.Fields[ListValue.ValuesFieldNumber].Accessor;
- WriteList(writer, (IList)fieldAccessor.GetValue((IMessage)value));
+ WriteList(writer, (IList)fieldAccessor.GetValue((IMessage)value), indentationLevel);
return;
}
if (descriptor.FullName == Value.Descriptor.FullName)
{
- WriteStructFieldValue(writer, (IMessage)value);
+ WriteStructFieldValue(writer, (IMessage)value, indentationLevel);
return;
}
if (descriptor.FullName == Any.Descriptor.FullName)
{
- WriteAny(writer, (IMessage)value);
+ WriteAny(writer, (IMessage)value, indentationLevel);
return;
}
- WriteMessage(writer, (IMessage)value);
+ WriteMessage(writer, (IMessage)value, indentationLevel);
}
private void WriteTimestamp(TextWriter writer, IMessage value)
@@ -519,7 +570,7 @@
writer.Write(FieldMask.ToJson(paths, DiagnosticOnly));
}
- private void WriteAny(TextWriter writer, IMessage value)
+ private void WriteAny(TextWriter writer, IMessage value, int indentationLevel)
{
if (DiagnosticOnly)
{
@@ -536,23 +587,23 @@
throw new InvalidOperationException($"Type registry has no descriptor for type name '{typeName}'");
}
IMessage message = descriptor.Parser.ParseFrom(data);
- writer.Write("{ ");
+ WriteBracketOpen(writer, ObjectOpenBracket);
WriteString(writer, AnyTypeUrlField);
writer.Write(NameValueSeparator);
WriteString(writer, typeUrl);
if (descriptor.IsWellKnownType)
{
- writer.Write(PropertySeparator);
+ writer.Write(ValueSeparator);
WriteString(writer, AnyWellKnownTypeValueField);
writer.Write(NameValueSeparator);
- WriteWellKnownTypeValue(writer, descriptor, message);
+ WriteWellKnownTypeValue(writer, descriptor, message, indentationLevel);
}
else
{
- WriteMessageFields(writer, message, true);
+ WriteMessageFields(writer, message, true, indentationLevel);
}
- writer.Write(" }");
+ WriteBracketClose(writer, ObjectCloseBracket, true, indentationLevel);
}
private void WriteDiagnosticOnlyAny(TextWriter writer, IMessage value)
@@ -563,7 +614,7 @@
WriteString(writer, AnyTypeUrlField);
writer.Write(NameValueSeparator);
WriteString(writer, typeUrl);
- writer.Write(PropertySeparator);
+ writer.Write(ValueSeparator);
WriteString(writer, AnyDiagnosticValueField);
writer.Write(NameValueSeparator);
writer.Write('"');
@@ -572,9 +623,9 @@
writer.Write(" }");
}
- private void WriteStruct(TextWriter writer, IMessage message)
+ private void WriteStruct(TextWriter writer, IMessage message, int indentationLevel)
{
- writer.Write("{ ");
+ WriteBracketOpen(writer, ObjectOpenBracket);
IDictionary fields = (IDictionary) message.Descriptor.Fields[Struct.FieldsFieldNumber].Accessor.GetValue(message);
bool first = true;
foreach (DictionaryEntry entry in fields)
@@ -586,19 +637,17 @@
throw new InvalidOperationException("Struct fields cannot have an empty key or a null value.");
}
- if (!first)
- {
- writer.Write(PropertySeparator);
- }
+ MaybeWriteValueSeparator(writer, first);
+ MaybeWriteValueWhitespace(writer, indentationLevel + 1);
WriteString(writer, key);
writer.Write(NameValueSeparator);
- WriteStructFieldValue(writer, value);
+ WriteStructFieldValue(writer, value, indentationLevel + 1);
first = false;
}
- writer.Write(first ? "}" : " }");
+ WriteBracketClose(writer, ObjectCloseBracket, !first, indentationLevel);
}
- private void WriteStructFieldValue(TextWriter writer, IMessage message)
+ private void WriteStructFieldValue(TextWriter writer, IMessage message, int indentationLevel)
{
var specifiedField = message.Descriptor.Oneofs[0].Accessor.GetCaseFieldDescriptor(message);
if (specifiedField == null)
@@ -619,7 +668,7 @@
case Value.ListValueFieldNumber:
// Structs and ListValues are nested messages, and already well-known types.
var nestedMessage = (IMessage) specifiedField.Accessor.GetValue(message);
- WriteWellKnownTypeValue(writer, nestedMessage.Descriptor, nestedMessage);
+ WriteWellKnownTypeValue(writer, nestedMessage.Descriptor, nestedMessage, indentationLevel);
return;
case Value.NullValueFieldNumber:
WriteNull(writer);
@@ -629,33 +678,30 @@
}
}
- internal void WriteList(TextWriter writer, IList list)
+ internal void WriteList(TextWriter writer, IList list, int indentationLevel = 0)
{
- writer.Write("[ ");
+ WriteBracketOpen(writer, ListBracketOpen);
+
bool first = true;
foreach (var value in list)
{
- if (!first)
- {
- writer.Write(PropertySeparator);
- }
- WriteValue(writer, value);
+ MaybeWriteValueSeparator(writer, first);
+ MaybeWriteValueWhitespace(writer, indentationLevel + 1);
+ WriteValue(writer, value, indentationLevel + 1);
first = false;
}
- writer.Write(first ? "]" : " ]");
+
+ WriteBracketClose(writer, ListBracketClose, !first, indentationLevel);
}
- internal void WriteDictionary(TextWriter writer, IDictionary dictionary)
+ internal void WriteDictionary(TextWriter writer, IDictionary dictionary, int indentationLevel = 0)
{
- writer.Write("{ ");
+ WriteBracketOpen(writer, ObjectOpenBracket);
+
bool first = true;
// This will box each pair. Could use IDictionaryEnumerator, but that's ugly in terms of disposal.
foreach (DictionaryEntry pair in dictionary)
{
- if (!first)
- {
- writer.Write(PropertySeparator);
- }
string keyText;
if (pair.Key is string s)
{
@@ -677,12 +723,16 @@
}
throw new ArgumentException("Unhandled dictionary key type: " + pair.Key.GetType());
}
+
+ MaybeWriteValueSeparator(writer, first);
+ MaybeWriteValueWhitespace(writer, indentationLevel + 1);
WriteString(writer, keyText);
writer.Write(NameValueSeparator);
WriteValue(writer, pair.Value);
first = false;
}
- writer.Write(first ? "}" : " }");
+
+ WriteBracketClose(writer, ObjectCloseBracket, !first, indentationLevel);
}
/// <summary>
@@ -766,6 +816,49 @@
writer.Write(Hex[(c >> 0) & 0xf]);
}
+ private void WriteBracketOpen(TextWriter writer, char openChar)
+ {
+ writer.Write(openChar);
+ if (settings.Indentation == null)
+ {
+ writer.Write(' ');
+ }
+ }
+
+ private void WriteBracketClose(TextWriter writer, char closeChar, bool hasFields, int indentationLevel)
+ {
+ if (hasFields)
+ {
+ if (settings.Indentation != null)
+ {
+ writer.WriteLine();
+ WriteIndentation(writer, indentationLevel);
+ }
+ else
+ {
+ writer.Write(" ");
+ }
+ }
+
+ writer.Write(closeChar);
+ }
+
+ private void MaybeWriteValueWhitespace(TextWriter writer, int indentationLevel)
+ {
+ if (settings.Indentation != null) {
+ writer.WriteLine();
+ WriteIndentation(writer, indentationLevel);
+ }
+ }
+
+ private void WriteIndentation(TextWriter writer, int indentationLevel)
+ {
+ for (int i = 0; i < indentationLevel; i++)
+ {
+ writer.Write(settings.Indentation);
+ }
+ }
+
/// <summary>
/// Settings controlling JSON formatting.
/// </summary>
@@ -806,6 +899,10 @@
/// </summary>
public bool PreserveProtoFieldNames { get; }
+ /// <summary>
+ /// Indentation string, used for formatting. Setting null disables indentation.
+ /// </summary>
+ public string Indentation { get; }
/// <summary>
/// Creates a new <see cref="Settings"/> object with the specified formatting of default values
@@ -833,40 +930,54 @@
/// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages. TypeRegistry.Empty will be used if it is null.</param>
/// <param name="formatEnumsAsIntegers"><c>true</c> to format the enums as integers; <c>false</c> to format enums as enum names.</param>
/// <param name="preserveProtoFieldNames"><c>true</c> to preserve proto field names; <c>false</c> to convert them to lowerCamelCase.</param>
+ /// <param name="indentation">The indentation string to use for multi-line formatting. <c>null</c> to disable multi-line format.</param>
private Settings(bool formatDefaultValues,
TypeRegistry typeRegistry,
bool formatEnumsAsIntegers,
- bool preserveProtoFieldNames)
+ bool preserveProtoFieldNames,
+ string indentation = null)
{
FormatDefaultValues = formatDefaultValues;
TypeRegistry = typeRegistry ?? TypeRegistry.Empty;
FormatEnumsAsIntegers = formatEnumsAsIntegers;
PreserveProtoFieldNames = preserveProtoFieldNames;
+ Indentation = indentation;
}
/// <summary>
/// Creates a new <see cref="Settings"/> object with the specified formatting of default values and the current settings.
/// </summary>
/// <param name="formatDefaultValues"><c>true</c> if default values (0, empty strings etc) should be formatted; <c>false</c> otherwise.</param>
- public Settings WithFormatDefaultValues(bool formatDefaultValues) => new Settings(formatDefaultValues, TypeRegistry, FormatEnumsAsIntegers, PreserveProtoFieldNames);
+ public Settings WithFormatDefaultValues(bool formatDefaultValues) => new Settings(formatDefaultValues, TypeRegistry, FormatEnumsAsIntegers, PreserveProtoFieldNames, Indentation);
/// <summary>
/// Creates a new <see cref="Settings"/> object with the specified type registry and the current settings.
/// </summary>
/// <param name="typeRegistry">The <see cref="TypeRegistry"/> to use when formatting <see cref="Any"/> messages.</param>
- public Settings WithTypeRegistry(TypeRegistry typeRegistry) => new Settings(FormatDefaultValues, typeRegistry, FormatEnumsAsIntegers, PreserveProtoFieldNames);
+ public Settings WithTypeRegistry(TypeRegistry typeRegistry) => new Settings(FormatDefaultValues, typeRegistry, FormatEnumsAsIntegers, PreserveProtoFieldNames, Indentation);
/// <summary>
/// Creates a new <see cref="Settings"/> object with the specified enums formatting option and the current settings.
/// </summary>
/// <param name="formatEnumsAsIntegers"><c>true</c> to format the enums as integers; <c>false</c> to format enums as enum names.</param>
- public Settings WithFormatEnumsAsIntegers(bool formatEnumsAsIntegers) => new Settings(FormatDefaultValues, TypeRegistry, formatEnumsAsIntegers, PreserveProtoFieldNames);
+ public Settings WithFormatEnumsAsIntegers(bool formatEnumsAsIntegers) => new Settings(FormatDefaultValues, TypeRegistry, formatEnumsAsIntegers, PreserveProtoFieldNames, Indentation);
/// <summary>
/// Creates a new <see cref="Settings"/> object with the specified field name formatting option and the current settings.
/// </summary>
/// <param name="preserveProtoFieldNames"><c>true</c> to preserve proto field names; <c>false</c> to convert them to lowerCamelCase.</param>
- public Settings WithPreserveProtoFieldNames(bool preserveProtoFieldNames) => new Settings(FormatDefaultValues, TypeRegistry, FormatEnumsAsIntegers, preserveProtoFieldNames);
+ public Settings WithPreserveProtoFieldNames(bool preserveProtoFieldNames) => new Settings(FormatDefaultValues, TypeRegistry, FormatEnumsAsIntegers, preserveProtoFieldNames, Indentation);
+
+ /// <summary>
+ /// Creates a new <see cref="Settings"/> object with the specified indentation and the current settings.
+ /// </summary>
+ /// <param name="indentation">The string to output for each level of indentation (nesting). The default is two spaces per level. Use null to disable indentation entirely.</param>
+ /// <remarks>A non-null value for <see cref="Indentation"/> will insert additional line-breaks to the JSON output.
+ /// Each line will contain either a single value, or braces. The default line-break is determined by <see cref="Environment.NewLine"/>,
+ /// which is <c>"\n"</c> on Unix platforms, and <c>"\r\n"</c> on Windows. If <see cref="JsonFormatter"/> seems to produce empty lines,
+ /// you need to pass a <see cref="TextWriter"/> that uses a <c>"\n"</c> newline. See <see cref="JsonFormatter.Format(Google.Protobuf.IMessage, TextWriter)"/>.
+ /// </remarks>
+ public Settings WithIndentation(string indentation = " ") => new Settings(FormatDefaultValues, TypeRegistry, FormatEnumsAsIntegers, PreserveProtoFieldNames, indentation);
}
// Effectively a cache of mapping from enum values to the original name as specified in the proto file,
diff --git a/csharp/src/Google.Protobuf/MessageParser.cs b/csharp/src/Google.Protobuf/MessageParser.cs
index 66907d4..5710292 100644
--- a/csharp/src/Google.Protobuf/MessageParser.cs
+++ b/csharp/src/Google.Protobuf/MessageParser.cs
@@ -171,6 +171,10 @@
/// <summary>
/// Parses a message from the given JSON.
/// </summary>
+ /// <remarks>This method always uses the default JSON parser; it is not affected by <see cref="WithDiscardUnknownFields(bool)"/>.
+ /// To ignore unknown fields when parsing JSON, create a <see cref="JsonParser"/> using a <see cref="JsonParser.Settings"/>
+ /// with <see cref="JsonParser.Settings.IgnoreUnknownFields"/> set to true and call <see cref="JsonParser.Parse{T}(string)"/> directly.
+ /// </remarks>
/// <param name="json">The JSON to parse.</param>
/// <returns>The parsed message.</returns>
/// <exception cref="InvalidJsonException">The JSON does not comply with RFC 7159</exception>
@@ -203,6 +207,9 @@
/// <summary>
/// Creates a new message parser which optionally discards unknown fields when parsing.
/// </summary>
+ /// <remarks>Note that this does not affect the behavior of <see cref="ParseJson(string)"/>
+ /// at all. To ignore unknown fields when parsing JSON, create a <see cref="JsonParser"/> using a <see cref="JsonParser.Settings"/>
+ /// with <see cref="JsonParser.Settings.IgnoreUnknownFields"/> set to true and call <see cref="JsonParser.Parse{T}(string)"/> directly.</remarks>
/// <param name="discardUnknownFields">Whether or not to discard unknown fields when parsing.</param>
/// <returns>A newly configured message parser.</returns>
public MessageParser WithDiscardUnknownFields(bool discardUnknownFields) =>
diff --git a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
index 8c1eec5..aa25686 100644
--- a/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
+++ b/csharp/src/Google.Protobuf/WellKnownTypes/Struct.cs
@@ -212,7 +212,7 @@
if (other == null) {
return;
}
- fields_.Add(other.fields_);
+ fields_.MergeFrom(other.fields_);
_unknownFields = pb::UnknownFieldSet.MergeFrom(_unknownFields, other._unknownFields);
}
diff --git a/docs/third_party.md b/docs/third_party.md
index d8478e9..d9ff970 100644
--- a/docs/third_party.md
+++ b/docs/third_party.md
@@ -203,3 +203,4 @@
* [vim-protolint: A protobuf linter for Vim](https://github.com/yoheimuta/vim-protolint)
* [super-linter: Protocol Buffer lint as GitHub Action](https://github.com/github/super-linter)
* [protoc-gen-fieldmask - A plugin to generate static type fieldmask paths](https://github.com/idodod/protoc-gen-fieldmask)
+* [protoc-gen-bq-schema - A protoc plugin to generate BigQuery schema files](https://github.com/GoogleCloudPlatform/protoc-gen-bq-schema)
diff --git a/examples/Makefile b/examples/Makefile
index 1ed7f67..2a64b64 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -13,13 +13,14 @@
clean:
rm -f add_person_cpp list_people_cpp add_person_java list_people_java add_person_python list_people_python
- rm -f javac_middleman AddPerson*.class ListPeople*.class com/example/tutorial/*.class
- rm -f protoc_middleman addressbook.pb.cc addressbook.pb.h addressbook_pb2.py com/example/tutorial/AddressBookProtos.java
+ rm -f javac_middleman AddPerson*.class ListPeople*.class com/example/tutorial/protos/*.class
+ rm -f protoc_middleman addressbook.pb.cc addressbook.pb.h addressbook_pb2.py com/example/tutorial/protos/*.java
rm -f *.pyc
rm -f go/tutorialpb/*.pb.go add_person_go list_people_go
rm -f protoc_middleman_dart dart_tutorial/*.pb*.dart
rmdir dart_tutorial 2>/dev/null || true
rmdir tutorial 2>/dev/null || true
+ rmdir com/example/tutorial/protos 2>/dev/null || true
rmdir com/example/tutorial 2>/dev/null || true
rmdir com/example 2>/dev/null || true
rmdir com 2>/dev/null || true
@@ -63,7 +64,7 @@
cd go && go test ./cmd/list_people
javac_middleman: AddPerson.java ListPeople.java protoc_middleman
- javac -cp $$CLASSPATH AddPerson.java ListPeople.java com/example/tutorial/AddressBookProtos.java
+ javac -cp $$CLASSPATH AddPerson.java ListPeople.java com/example/tutorial/protos/*.java
@touch javac_middleman
add_person_java: javac_middleman
diff --git a/java/README.md b/java/README.md
index 7e71e66..5e3ded4 100644
--- a/java/README.md
+++ b/java/README.md
@@ -23,7 +23,7 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</dependency>
```
@@ -37,7 +37,7 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</dependency>
```
@@ -45,7 +45,7 @@
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.4'
```
Again, be sure to check that the version number matches (or is newer than) the version number of protoc that you are using.
diff --git a/java/bom/pom.xml b/java/bom/pom.xml
index b4c3465..4cccfb3 100644
--- a/java/bom/pom.xml
+++ b/java/bom/pom.xml
@@ -4,7 +4,7 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-bom</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
<packaging>pom</packaging>
<name>Protocol Buffers [BOM]</name>
diff --git a/java/core/pom.xml b/java/core/pom.xml
index 845ff88..d5eacd6 100644
--- a/java/core/pom.xml
+++ b/java/core/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</parent>
<artifactId>protobuf-java</artifactId>
diff --git a/java/kotlin-lite/pom.xml b/java/kotlin-lite/pom.xml
index 3fec5ec..2dc4309 100644
--- a/java/kotlin-lite/pom.xml
+++ b/java/kotlin-lite/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</parent>
<artifactId>protobuf-kotlin-lite</artifactId>
diff --git a/java/kotlin/pom.xml b/java/kotlin/pom.xml
index 43debaf..7a350f7 100644
--- a/java/kotlin/pom.xml
+++ b/java/kotlin/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</parent>
<artifactId>protobuf-kotlin</artifactId>
diff --git a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt
index 1645cfb..8ce0094 100644
--- a/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt
+++ b/java/kotlin/src/test/kotlin/com/google/protobuf/Proto3Test.kt
@@ -66,6 +66,11 @@
assertThat(optionalNestedMessage).isEqualTo(TestAllTypesKt.nestedMessage { bb = 118 })
optionalNestedEnum = NestedEnum.BAZ
assertThat(optionalNestedEnum).isEqualTo(NestedEnum.BAZ)
+ assertThat(optionalNestedEnumValue).isEqualTo(3)
+ optionalNestedEnumValue = 1
+ assertThat(optionalNestedEnumValue).isEqualTo(1)
+ assertThat(optionalNestedEnum).isEqualTo(NestedEnum.FOO)
+
oneofUint32 = 601
assertThat(oneofUint32).isEqualTo(601)
}
diff --git a/java/lite.md b/java/lite.md
index 1ad0019..d4b4fc8 100644
--- a/java/lite.md
+++ b/java/lite.md
@@ -29,7 +29,7 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-javalite</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</dependency>
```
diff --git a/java/lite/pom.xml b/java/lite/pom.xml
index 7c635ec..20381c6 100644
--- a/java/lite/pom.xml
+++ b/java/lite/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</parent>
<artifactId>protobuf-javalite</artifactId>
diff --git a/java/pom.xml b/java/pom.xml
index 34475a4..99abcf8 100644
--- a/java/pom.xml
+++ b/java/pom.xml
@@ -4,7 +4,7 @@
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
<packaging>pom</packaging>
<name>Protocol Buffers [Parent]</name>
diff --git a/java/protoc/pom.xml b/java/protoc/pom.xml
index aa6bc58..b2cd0d5 100644
--- a/java/protoc/pom.xml
+++ b/java/protoc/pom.xml
@@ -4,11 +4,11 @@
<parent>
<groupId>com.google</groupId>
<artifactId>google</artifactId>
- <version>1</version>
+ <version>5</version>
</parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protoc</artifactId>
- <version>3.21.2</version>
+ <version>3.21.3</version>
<packaging>pom</packaging>
<name>Protobuf Compiler</name>
<description>
diff --git a/java/util/pom.xml b/java/util/pom.xml
index 53f90ce..f634047 100644
--- a/java/util/pom.xml
+++ b/java/util/pom.xml
@@ -4,7 +4,7 @@
<parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-parent</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</parent>
<artifactId>protobuf-java-util</artifactId>
diff --git a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
index ea8a933..0b09fcc 100644
--- a/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
+++ b/java/util/src/main/java/com/google/protobuf/util/Timestamps.java
@@ -344,7 +344,7 @@
if (date instanceof java.sql.Timestamp) {
java.sql.Timestamp sqlTimestamp = (java.sql.Timestamp) date;
long time = sqlTimestamp.getTime();
- long integralSeconds = (time < 0) ? time / 1000L - 1 : time / 1000L ; // truncate the fractional seconds
+ long integralSeconds = (time < 0 && time % 1000 != 0) ? time / 1000L - 1 : time / 1000L ; // truncate the fractional seconds
return Timestamp.newBuilder()
.setSeconds(integralSeconds)
.setNanos(sqlTimestamp.getNanos())
diff --git a/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java b/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java
index 7d0d821..51f7a85 100644
--- a/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java
+++ b/java/util/src/test/java/com/google/protobuf/util/TimestampsTest.java
@@ -484,6 +484,13 @@
}
@Test
+ public void testFromSqlTimestamp_beforeEpochWholeSecond() {
+ Date date = new java.sql.Timestamp(-2000);
+ Timestamp timestamp = Timestamps.fromDate(date);
+ assertThat(Timestamps.toString(timestamp)).isEqualTo("1969-12-31T23:59:58Z");
+ }
+
+ @Test
public void testTimeOperations() throws Exception {
Timestamp start = Timestamps.parse("0001-01-01T00:00:00Z");
Timestamp end = Timestamps.parse("9999-12-31T23:59:59.999999999Z");
diff --git a/kokoro/linux/bazel_distcheck/build.sh b/kokoro/linux/bazel_distcheck/build.sh
deleted file mode 100755
index a50b175..0000000
--- a/kokoro/linux/bazel_distcheck/build.sh
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/bin/bash
-#
-# Build file to set up and run tests using bazel-build dist archive
-#
-# Note that the builds use WORKSPACE to fetch external sources, not
-# git submodules.
-
-set -eu
-
-use_bazel.sh 5.0.0 || true
-bazel version
-
-# Change to repo root
-cd $(dirname $0)/../../..
-
-# Get kokoro scripts from repo root by default.
-: ${SCRIPT_ROOT:=$(pwd)}
-source ${SCRIPT_ROOT}/kokoro/common/pyenv.sh
-
-# Build distribution archive
-echo "============================================================"
-echo -e "[[ $(date) ]] Building distribution archive...\n"
-${SCRIPT_ROOT}/kokoro/common/bazel_wrapper.sh build //pkg:dist_all_tar
-DIST_ARCHIVE=$(readlink $(bazel info bazel-bin)/pkg/dist_all_tar.tar.gz)
-bazel shutdown
-
-# Extract the dist archive.
-echo "============================================================"
-echo -e "[[ $(date) ]] Extracting distribution archive...\n"
-
-# Construct temp directory for running the dist build.
-# If you want to run locally and keep the build dir, create a directory
-# and pass it in the DIST_WORK_ROOT env var.
-if [[ -z ${DIST_WORK_ROOT:-} ]]; then
- : ${DIST_WORK_ROOT:=$(mktemp -d)}
- function dist_cleanup() {
- (( $BASH_SUBSHELL == 0 )) && rm -rf ${DIST_WORK_ROOT}
- }
- trap dist_cleanup EXIT
-fi
-
-DIST_WORKSPACE=${DIST_WORK_ROOT}/protobuf
-mkdir -p ${DIST_WORKSPACE}
-tar -C ${DIST_WORKSPACE} --strip-components=1 -axf bazel-bin/pkg/dist_all_tar.tar.gz
-
-echo "============================================================"
-echo -e "[[ $(date) ]] Building extracted archive...\n"
-
-cd ${DIST_WORKSPACE}
-
-bazel_args=(
- test
- --keep_going
- --test_output=errors
- --
- //...
- -//objectivec/... # only works on macOS
- -//csharp/... # release builds require external dependencies
- @com_google_protobuf_examples//...
-)
-${SCRIPT_ROOT}/kokoro/common/bazel_wrapper.sh "${bazel_args[@]}"
diff --git a/kokoro/linux/bazel_distcheck/common.cfg b/kokoro/linux/bazel_distcheck/common.cfg
deleted file mode 100644
index 6b18488..0000000
--- a/kokoro/linux/bazel_distcheck/common.cfg
+++ /dev/null
@@ -1,9 +0,0 @@
-# Common config shared by presubmit and continuous.
-
-bazel_setting: {
- project_id: "protobuf-build"
- bes_backend_address: "buildeventservice.googleapis.com"
- foundry_backend_address: "remotebuildexecution.googleapis.com"
- upsalite_frontend_address: "https://source.cloud.google.com"
- local_execution: true
-}
diff --git a/kokoro/linux/bazel_distcheck/continuous.cfg b/kokoro/linux/bazel_distcheck/continuous.cfg
deleted file mode 100644
index 4ea8b21..0000000
--- a/kokoro/linux/bazel_distcheck/continuous.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/bazel_distcheck/build.sh"
-timeout_mins: 15
diff --git a/kokoro/linux/bazel_distcheck/presubmit.cfg b/kokoro/linux/bazel_distcheck/presubmit.cfg
deleted file mode 100644
index 4ea8b21..0000000
--- a/kokoro/linux/bazel_distcheck/presubmit.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/bazel_distcheck/build.sh"
-timeout_mins: 15
diff --git a/kokoro/linux/cmake/build.sh b/kokoro/linux/cmake/build.sh
new file mode 100755
index 0000000..523253d
--- /dev/null
+++ b/kokoro/linux/cmake/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Build file to set up and run tests using CMake
+
+set -eux
+
+# Change to repo root
+cd $(dirname $0)/../../..
+GIT_REPO_ROOT=`pwd`
+
+CONTAINER_IMAGE=gcr.io/protobuf-build/cmake/linux@sha256:79e6ed9d7f3f8e56167a3309a521e5b7e6a212bfb19855c65ee1cbb6f9099671
+
+# Update git submodules
+git submodule update --init --recursive
+
+tmpfile=$(mktemp -u)
+
+docker run \
+ --cidfile $tmpfile \
+ -v $GIT_REPO_ROOT:/workspace \
+ $CONTAINER_IMAGE \
+ /test.sh -Dprotobuf_BUILD_CONFORMANCE=ON
+
+# Save logs for Kokoro
+docker cp \
+ `cat $tmpfile`:/workspace/logs $KOKORO_ARTIFACTS_DIR
diff --git a/kokoro/linux/cmake/continuous.cfg b/kokoro/linux/cmake/continuous.cfg
new file mode 100644
index 0000000..f03bd39
--- /dev/null
+++ b/kokoro/linux/cmake/continuous.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cmake/presubmit.cfg b/kokoro/linux/cmake/presubmit.cfg
new file mode 100644
index 0000000..f03bd39
--- /dev/null
+++ b/kokoro/linux/cmake/presubmit.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cmake_distcheck/build.sh b/kokoro/linux/cmake_distcheck/build.sh
deleted file mode 100755
index 116e40b..0000000
--- a/kokoro/linux/cmake_distcheck/build.sh
+++ /dev/null
@@ -1,60 +0,0 @@
-#!/bin/bash
-#
-# Build file to set up and run tests based on distribution archive
-
-set -eux
-
-# Change to repo root
-cd $(dirname $0)/../../..
-
-#
-# Update git submodules
-#
-git submodule update --init --recursive
-
-#
-# Build distribution archive
-#
-# TODO: this should use Bazel-built dist archives.
-date ; ./autogen.sh
-date ; ./configure
-date ; make dist
-date
-
-DIST_ARCHIVE=( $(ls protobuf-*.tar.gz) )
-if (( ${#DIST_ARCHIVE[@]} != 1 )); then
- echo >&2 "Distribution archive not found. ${#DIST_ARCHIVE[@]} matches:"
- echo >&2 "${DIST_ARCHIVE[@]}"
- exit 1
-fi
-
-#
-# Check for all expected files
-#
-kokoro/common/check_missing_dist_files.sh ${DIST_ARCHIVE}
-
-#
-# Extract to a temporary directory
-#
-if [[ -z ${DIST_WORK_ROOT:-} ]]; then
- # If you want to preserve the extracted sources, set the DIST_WORK_ROOT
- # environment variable to an existing directory that should be used.
- DIST_WORK_ROOT=$(mktemp -d)
- function cleanup_work_root() {
- echo "Cleaning up temporary directory ${DIST_WORK_ROOT}..."
- rm -rf ${DIST_WORK_ROOT}
- }
- trap cleanup_work_root EXIT
-fi
-
-tar -C ${DIST_WORK_ROOT} --strip-components=1 -axf ${DIST_ARCHIVE}
-
-#
-# Run tests using extracted sources
-#
-SOURCE_DIR=${DIST_WORK_ROOT} \
-CMAKE_GENERATOR=Ninja \
-CTEST_PARALLEL_LEVEL=$(nproc) \
-kokoro/common/cmake.sh
-
-echo "PASS"
diff --git a/kokoro/linux/cmake_distcheck/continuous.cfg b/kokoro/linux/cmake_distcheck/continuous.cfg
deleted file mode 100644
index 6ef4c89..0000000
--- a/kokoro/linux/cmake_distcheck/continuous.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/cmake_distcheck/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/linux/cmake_distcheck/presubmit.cfg b/kokoro/linux/cmake_distcheck/presubmit.cfg
deleted file mode 100644
index 6ef4c89..0000000
--- a/kokoro/linux/cmake_distcheck/presubmit.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/cmake_distcheck/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/linux/cmake_install/build.sh b/kokoro/linux/cmake_install/build.sh
new file mode 100755
index 0000000..7fdf267
--- /dev/null
+++ b/kokoro/linux/cmake_install/build.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Build file to build, install, and test using CMake.
+
+set -eux
+
+# Change to repo root
+cd $(dirname $0)/../../..
+GIT_REPO_ROOT=`pwd`
+
+CONTAINER_IMAGE=gcr.io/protobuf-build/cmake/linux@sha256:79e6ed9d7f3f8e56167a3309a521e5b7e6a212bfb19855c65ee1cbb6f9099671
+
+# Update git submodules
+git submodule update --init --recursive
+
+tmpfile=$(mktemp -u)
+
+docker run \
+ --cidfile $tmpfile \
+ -v $GIT_REPO_ROOT:/workspace \
+ $CONTAINER_IMAGE \
+ "/install.sh && /test.sh \
+ -Dprotobuf_REMOVE_INSTALLED_HEADERS=ON \
+ -Dprotobuf_BUILD_PROTOBUF_BINARIES=OFF \
+ -Dprotobuf_BUILD_CONFORMANCE=ON"
+
+
+# Save logs for Kokoro
+docker cp \
+ `cat $tmpfile`:/workspace/logs $KOKORO_ARTIFACTS_DIR
diff --git a/kokoro/linux/cmake_install/continuous.cfg b/kokoro/linux/cmake_install/continuous.cfg
new file mode 100644
index 0000000..f1ae0b3
--- /dev/null
+++ b/kokoro/linux/cmake_install/continuous.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake_install/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cmake_install/presubmit.cfg b/kokoro/linux/cmake_install/presubmit.cfg
new file mode 100644
index 0000000..f1ae0b3
--- /dev/null
+++ b/kokoro/linux/cmake_install/presubmit.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake_install/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cmake_ninja/build.sh b/kokoro/linux/cmake_ninja/build.sh
new file mode 100755
index 0000000..21cc01e
--- /dev/null
+++ b/kokoro/linux/cmake_ninja/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Build file to set up and run tests using CMake with the Ninja generator.
+
+set -eux
+
+# Change to repo root
+cd $(dirname $0)/../../..
+GIT_REPO_ROOT=`pwd`
+
+CONTAINER_IMAGE=gcr.io/protobuf-build/cmake/linux@sha256:79e6ed9d7f3f8e56167a3309a521e5b7e6a212bfb19855c65ee1cbb6f9099671
+
+# Update git submodules
+git submodule update --init --recursive
+
+tmpfile=$(mktemp -u)
+
+docker run \
+ --cidfile $tmpfile \
+ -v $GIT_REPO_ROOT:/workspace \
+ $CONTAINER_IMAGE \
+ /test.sh -G Ninja -Dprotobuf_BUILD_CONFORMANCE=ON
+
+# Save logs for Kokoro
+docker cp \
+ `cat $tmpfile`:/workspace/logs $KOKORO_ARTIFACTS_DIR
diff --git a/kokoro/linux/cmake_ninja/continuous.cfg b/kokoro/linux/cmake_ninja/continuous.cfg
new file mode 100644
index 0000000..144fc90
--- /dev/null
+++ b/kokoro/linux/cmake_ninja/continuous.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake_ninja/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cmake_ninja/presubmit.cfg b/kokoro/linux/cmake_ninja/presubmit.cfg
new file mode 100644
index 0000000..144fc90
--- /dev/null
+++ b/kokoro/linux/cmake_ninja/presubmit.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake_ninja/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cmake_shared/build.sh b/kokoro/linux/cmake_shared/build.sh
new file mode 100755
index 0000000..1fda3df
--- /dev/null
+++ b/kokoro/linux/cmake_shared/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+#
+# Build file to set up and run tests via CMake using shared libraries
+
+set -eux
+
+# Change to repo root
+cd $(dirname $0)/../../..
+GIT_REPO_ROOT=`pwd`
+
+CONTAINER_IMAGE=gcr.io/protobuf-build/cmake/linux@sha256:79e6ed9d7f3f8e56167a3309a521e5b7e6a212bfb19855c65ee1cbb6f9099671
+
+# Update git submodules
+git submodule update --init --recursive
+
+tmpfile=$(mktemp -u)
+
+docker run \
+ --cidfile $tmpfile \
+ -v $GIT_REPO_ROOT:/workspace \
+ $CONTAINER_IMAGE \
+ /test.sh -Dprotobuf_BUILD_CONFORMANCE=ON -Dprotobuf_BUILD_SHARED_LIBS=ON
+
+# Save logs for Kokoro
+docker cp \
+ `cat $tmpfile`:/workspace/logs $KOKORO_ARTIFACTS_DIR
diff --git a/kokoro/linux/cmake_shared/continuous.cfg b/kokoro/linux/cmake_shared/continuous.cfg
new file mode 100644
index 0000000..f03bd39
--- /dev/null
+++ b/kokoro/linux/cmake_shared/continuous.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cmake_shared/presubmit.cfg b/kokoro/linux/cmake_shared/presubmit.cfg
new file mode 100644
index 0000000..f03bd39
--- /dev/null
+++ b/kokoro/linux/cmake_shared/presubmit.cfg
@@ -0,0 +1,11 @@
+# Config file for running tests in Kokoro
+
+# Location of the build script in repository
+build_file: "protobuf/kokoro/linux/cmake/build.sh"
+timeout_mins: 1440
+
+action {
+ define_artifacts {
+ regex: "**/sponge_log.*"
+ }
+}
diff --git a/kokoro/linux/cpp_distcheck/build.sh b/kokoro/linux/cpp_distcheck/build.sh
deleted file mode 100755
index a28843e..0000000
--- a/kokoro/linux/cpp_distcheck/build.sh
+++ /dev/null
@@ -1,25 +0,0 @@
-#!/bin/bash
-#
-# Build file to set up and run tests
-
-set -ex # exit immediately on error
-
-# Change to repo root
-cd $(dirname $0)/../../..
-
-./tests.sh cpp_distcheck
-
-# Run tests under release docker image.
-DOCKER_IMAGE_NAME=protobuf/protoc_$(sha1sum protoc-artifacts/Dockerfile | cut -f1 -d " ")
-until docker pull $DOCKER_IMAGE_NAME; do sleep 10; done
-
-docker run -v $(pwd):/var/local/protobuf --rm $DOCKER_IMAGE_NAME \
- bash -l /var/local/protobuf/tests.sh cpp || FAILED="true"
-
-# This directory is owned by root. We need to delete it, because otherwise
-# Kokoro will attempt to rsync it and fail with a permission error.
-rm -rf src/core
-
-if [ "$FAILED" = "true" ]; then
- exit 1
-fi
diff --git a/kokoro/linux/cpp_distcheck/continuous.cfg b/kokoro/linux/cpp_distcheck/continuous.cfg
deleted file mode 100644
index 4289f6a..0000000
--- a/kokoro/linux/cpp_distcheck/continuous.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/cpp_distcheck/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/linux/cpp_distcheck/presubmit.cfg b/kokoro/linux/cpp_distcheck/presubmit.cfg
deleted file mode 100644
index 4289f6a..0000000
--- a/kokoro/linux/cpp_distcheck/presubmit.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/cpp_distcheck/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/linux/dist_install/build.sh b/kokoro/linux/dist_install/build.sh
deleted file mode 100755
index c456ee8..0000000
--- a/kokoro/linux/dist_install/build.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/bash
-#
-# Build file to set up and run tests
-
-set -ex # exit immediately on error
-
-# Change to repo root
-cd $(dirname $0)/../../..
-
-export DOCKERHUB_ORGANIZATION=protobuftesting
-export DOCKERFILE_DIR=kokoro/linux/dockerfile/test/java_stretch
-export DOCKER_RUN_SCRIPT=kokoro/linux/pull_request_in_docker.sh
-export OUTPUT_DIR=testoutput
-export TEST_SET="dist_install"
-./kokoro/linux/build_and_run_docker.sh
diff --git a/kokoro/linux/dist_install/continuous.cfg b/kokoro/linux/dist_install/continuous.cfg
deleted file mode 100644
index b1e0b20..0000000
--- a/kokoro/linux/dist_install/continuous.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/dist_install/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/linux/dist_install/presubmit.cfg b/kokoro/linux/dist_install/presubmit.cfg
deleted file mode 100644
index b1e0b20..0000000
--- a/kokoro/linux/dist_install/presubmit.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/linux/dist_install/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/macos/cpp_distcheck/build.sh b/kokoro/macos/cpp_distcheck/build.sh
deleted file mode 100755
index d729b63..0000000
--- a/kokoro/macos/cpp_distcheck/build.sh
+++ /dev/null
@@ -1,11 +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
-
-./tests.sh cpp_distcheck
diff --git a/kokoro/macos/cpp_distcheck/continuous.cfg b/kokoro/macos/cpp_distcheck/continuous.cfg
deleted file mode 100644
index 89441bc..0000000
--- a/kokoro/macos/cpp_distcheck/continuous.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/cpp_distcheck/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/macos/cpp_distcheck/presubmit.cfg b/kokoro/macos/cpp_distcheck/presubmit.cfg
deleted file mode 100644
index 89441bc..0000000
--- a/kokoro/macos/cpp_distcheck/presubmit.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/cpp_distcheck/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/macos/php7.0_mac/build.sh b/kokoro/macos/php7.0_mac/build.sh
deleted file mode 100755
index c6717e0..0000000
--- a/kokoro/macos/php7.0_mac/build.sh
+++ /dev/null
@@ -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
diff --git a/kokoro/macos/php7.0_mac/continuous.cfg b/kokoro/macos/php7.0_mac/continuous.cfg
deleted file mode 100644
index c2c1811..0000000
--- a/kokoro/macos/php7.0_mac/continuous.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.0_mac/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/macos/php7.0_mac/presubmit.cfg b/kokoro/macos/php7.0_mac/presubmit.cfg
deleted file mode 100644
index c2c1811..0000000
--- a/kokoro/macos/php7.0_mac/presubmit.cfg
+++ /dev/null
@@ -1,5 +0,0 @@
-# Config file for running tests in Kokoro
-
-# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.0_mac/build.sh"
-timeout_mins: 1440
diff --git a/kokoro/macos/php7.3_mac/build.sh b/kokoro/macos/php7.3_mac/build.sh
deleted file mode 100755
index 2688ddb..0000000
--- a/kokoro/macos/php7.3_mac/build.sh
+++ /dev/null
@@ -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
diff --git a/kokoro/macos/php74/build.sh b/kokoro/macos/php74/build.sh
new file mode 100755
index 0000000..ff39657
--- /dev/null
+++ b/kokoro/macos/php74/build.sh
@@ -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
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/macos/php74/continuous.cfg
similarity index 65%
rename from kokoro/macos/php7.3_mac/continuous.cfg
rename to kokoro/macos/php74/continuous.cfg
index 9a71745..cf7e80b 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/macos/php74/continuous.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# 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
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/macos/php74/presubmit.cfg
similarity index 65%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/macos/php74/presubmit.cfg
index 9a71745..cf7e80b 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/macos/php74/presubmit.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# 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
diff --git a/kokoro/macos/php80/build.sh b/kokoro/macos/php80/build.sh
new file mode 100755
index 0000000..84e2c46
--- /dev/null
+++ b/kokoro/macos/php80/build.sh
@@ -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
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/macos/php80/continuous.cfg
similarity index 65%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/macos/php80/continuous.cfg
index 9a71745..ded43e6 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/macos/php80/continuous.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# 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
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/macos/php80/presubmit.cfg
similarity index 65%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/macos/php80/presubmit.cfg
index 9a71745..ded43e6 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/macos/php80/presubmit.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# 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
diff --git a/kokoro/macos/prepare_build_macos_rc b/kokoro/macos/prepare_build_macos_rc
index 8e0a87e..7ec2832 100755
--- a/kokoro/macos/prepare_build_macos_rc
+++ b/kokoro/macos/prepare_build_macos_rc
@@ -4,20 +4,16 @@
set -eux
+export HOMEBREW_PREFIX=$(brew --prefix)
+
##
# Select Xcode version
-
-# Remember to update the Xcode version when Xcode_11.3.app is not available.
-# 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
+export DEVELOPER_DIR=/Applications/Xcode_13.3.1.app/Contents/Developer
+sudo xcode-select -s "${DEVELOPER_DIR}"
##
-# Select C/C++ compilers
-
-export CC=gcc
-export CXX=g++
+# Use Python 2 by default (for googletest)
+pyenv global 2.7.18
##
# Install Tox
@@ -27,14 +23,18 @@
fi
##
-# Install RVM
-
+# Setup RVM
if [[ "${KOKORO_INSTALL_RVM:-}" == "yes" ]] ; then
- curl -sSL https://rvm.io/mpapis.asc | gpg --import -
- curl -sSL https://rvm.io/pkuczynski.asc | gpg --import -
+ git config --global --add safe.directory $HOMEBREW_PREFIX/Library/Taps/homebrew/homebrew-cask
+ 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
- # 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
+# "Install" valgrind if it doesn't exist
+##
+if [ ! -x "$(command -v valgrind)" ]; then
+ echo "#! /bin/bash" > valgrind
+ chmod ug+x valgrind
+ sudo mv valgrind /usr/local/bin/valgrind
fi
diff --git a/kokoro/release/ruby/macos/build_artifacts.sh b/kokoro/release/ruby/macos/build_artifacts.sh
index c68b63c..a109d45 100755
--- a/kokoro/release/ruby/macos/build_artifacts.sh
+++ b/kokoro/release/ruby/macos/build_artifacts.sh
@@ -12,8 +12,5 @@
# ruby environment
bash kokoro/release/ruby/macos/ruby/ruby_build_environment.sh
-gem install rubygems-update
-update_rubygems
-
# build artifacts
bash kokoro/release/ruby/macos/ruby/ruby_build.sh
diff --git a/kokoro/release/ruby/macos/ruby/ruby_build.sh b/kokoro/release/ruby/macos/ruby/ruby_build.sh
index 55773b2..bbfc631 100755
--- a/kokoro/release/ruby/macos/ruby/ruby_build.sh
+++ b/kokoro/release/ruby/macos/ruby/ruby_build.sh
@@ -3,7 +3,6 @@
set -ex
# Build protoc
-use_bazel.sh 5.1.1
bazel build //:protoc
export PROTOC=$PWD/bazel-bin/protoc
diff --git a/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh b/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh
index 2a9cb16..ae973fc 100755
--- a/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh
+++ b/kokoro/release/ruby/macos/ruby/ruby_build_environment.sh
@@ -4,13 +4,9 @@
set +ex # rvm script is very verbose and exits with errorcode
-curl -sSL https://rvm.io/mpapis.asc | gpg --import -
-curl -sSL https://rvm.io/pkuczynski.asc | gpg --import -
-
-# 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
+# Fix permissions
+sudo chown -R $(whoami) $HOME/.rvm/
+sudo chown -R $(whoami) /Library/Ruby/
source $HOME/.rvm/scripts/rvm
set -e # rvm commands are very verbose
diff --git a/kokoro/windows/bazel/build.bat b/kokoro/windows/bazel/build.bat
new file mode 100644
index 0000000..73eb0fd
--- /dev/null
+++ b/kokoro/windows/bazel/build.bat
@@ -0,0 +1,30 @@
+@rem enter repo root
+cd /d %~dp0\..\..\..
+
+call kokoro\windows\prepare_build_win64.bat || goto :error
+
+@rem Allow Bazel to create short paths.
+fsutil 8dot3name set 0
+
+@rem TODO(b/241475022) Use docker to guarantee better stability.
+
+@rem Reinstall Bazel due to corupt installation in kokoro.
+bazel version
+choco install bazel -y -i
+bazel version
+
+@rem Make paths as short as possible to avoid long path issues.
+set BAZEL_STARTUP=--output_user_root=C:/tmp --windows_enable_symlinks
+set BAZEL_FLAGS=--enable_runfiles --keep_going --test_output=streamed --verbose_failures
+
+@rem Build libraries first.
+bazel %BAZEL_STARTUP% build //:protoc //:protobuf //:protobuf_lite %BAZEL_FLAGS% || goto :error
+
+@rem Run C++ tests.
+bazel %BAZEL_STARTUP% test //src/... %BAZEL_FLAGS% || goto :error
+
+goto :EOF
+
+:error
+echo Failed!
+exit /b 1
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/bazel/continuous.cfg
similarity index 65%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/bazel/continuous.cfg
index 9a71745..5978a7a 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/bazel/continuous.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/bazel/build.bat"
timeout_mins: 1440
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/bazel/presubmit.cfg
similarity index 65%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/bazel/presubmit.cfg
index 9a71745..5978a7a 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/bazel/presubmit.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/bazel/build.bat"
timeout_mins: 1440
diff --git a/kokoro/windows/cmake/build.bat b/kokoro/windows/cmake/build.bat
new file mode 100644
index 0000000..29d012b
--- /dev/null
+++ b/kokoro/windows/cmake/build.bat
@@ -0,0 +1,32 @@
+@rem enter repo root
+cd /d %~dp0\..\..\..
+
+call kokoro\windows\prepare_build_win64.bat || goto :error
+
+@rem TODO(b/241475022) Use docker to guarantee better stability.
+@rem TODO(b/241484899) Run conformance tests in windows.
+
+md build -ea 0
+md %KOKORO_ARTIFACTS_DIR%\logs -ea 0
+
+cd build
+
+cmake .. ^
+ -G "Visual Studio 15 2017" -A x64 ^
+ -Dprotobuf_BUILD_CONFORMANCE=OFF ^
+ -Dprotobuf_WITH_ZLIB=OFF ^
+ -Dprotobuf_TEST_XML_OUTDIR=%KOKORO_ARTIFACTS_DIR%\logs\ || goto :error
+
+cmake --build . || goto :error
+
+ctest --verbose -C Debug || goto :error
+
+goto :success
+
+:error
+cd /d %~dp0\..\..\..
+echo Failed!
+exit /b 1
+
+:success
+cd ..
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/cmake/continuous.cfg
similarity index 65%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/cmake/continuous.cfg
index 9a71745..37e89e0 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/cmake/continuous.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake/build.bat"
timeout_mins: 1440
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/cmake/presubmit.cfg
similarity index 65%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/cmake/presubmit.cfg
index 9a71745..37e89e0 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/cmake/presubmit.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake/build.bat"
timeout_mins: 1440
diff --git a/kokoro/windows/cmake_install/build.bat b/kokoro/windows/cmake_install/build.bat
new file mode 100644
index 0000000..5392f9e
--- /dev/null
+++ b/kokoro/windows/cmake_install/build.bat
@@ -0,0 +1,45 @@
+@rem enter repo root
+cd /d %~dp0\..\..\..
+
+call kokoro\windows\prepare_build_win64.bat || goto :error
+
+@rem TODO(b/241475022) Use docker to guarantee better stability.
+@rem TODO(b/241484899) Run conformance tests in windows.
+
+md build -ea 0
+md %KOKORO_ARTIFACTS_DIR%\logs -ea 0
+
+cd build
+
+@rem First install protobuf from source.
+cmake .. ^
+ -G "Visual Studio 15 2017" -A x64 ^
+ -Dprotobuf_BUILD_CONFORMANCE=OFF ^
+ -Dprotobuf_WITH_ZLIB=OFF || goto :error
+
+cmake --build . --target install || goto :error
+
+@rem Next run tests forcing the use of our installation.
+
+rm -rf *
+
+cmake .. ^
+ -G "Visual Studio 15 2017" -A x64 ^
+ -Dprotobuf_REMOVE_INSTALLED_HEADERS=ON ^
+ -Dprotobuf_BUILD_PROTOBUF_BINARIES=OFF ^
+ -Dprotobuf_BUILD_CONFORMANCE=OFF ^
+ -Dprotobuf_TEST_XML_OUTDIR=%KOKORO_ARTIFACTS_DIR%\logs\ || goto :error
+
+cmake --build . --target ALL_BUILD || goto :error
+
+ctest --verbose -C Debug || goto :error
+
+goto :success
+
+:error
+cd /d %~dp0\..\..\..
+echo Failed!
+exit /b 1
+
+:success
+cd ..
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/cmake_install/continuous.cfg
similarity index 63%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/cmake_install/continuous.cfg
index 9a71745..2efc0dc 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/cmake_install/continuous.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake_install/build.bat"
timeout_mins: 1440
diff --git a/kokoro/macos/php7.3_mac/presubmit.cfg b/kokoro/windows/cmake_install/presubmit.cfg
similarity index 63%
rename from kokoro/macos/php7.3_mac/presubmit.cfg
rename to kokoro/windows/cmake_install/presubmit.cfg
index 9a71745..2efc0dc 100644
--- a/kokoro/macos/php7.3_mac/presubmit.cfg
+++ b/kokoro/windows/cmake_install/presubmit.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake_install/build.bat"
timeout_mins: 1440
diff --git a/kokoro/windows/cmake_nmake/build.bat b/kokoro/windows/cmake_nmake/build.bat
new file mode 100644
index 0000000..a080771
--- /dev/null
+++ b/kokoro/windows/cmake_nmake/build.bat
@@ -0,0 +1,34 @@
+@rem enter repo root
+cd /d %~dp0\..\..\..
+
+call kokoro\windows\prepare_build_win64.bat || goto :error
+
+@rem TODO(b/241475022) Use docker to guarantee better stability.
+@rem TODO(b/241484899) Run conformance tests in windows.
+
+md build -ea 0
+md %KOKORO_ARTIFACTS_DIR%\logs -ea 0
+
+cd build
+
+cmake .. ^
+ -G "NMake Makefiles" ^
+ -DCMAKE_C_COMPILER=cl.exe ^
+ -DCMAKE_CXX_COMPILER=cl.exe ^
+ -Dprotobuf_BUILD_CONFORMANCE=OFF ^
+ -Dprotobuf_WITH_ZLIB=OFF ^
+ -Dprotobuf_TEST_XML_OUTDIR=%KOKORO_ARTIFACTS_DIR%\logs\ || goto :error
+
+cmake --build . || goto :error
+
+ctest --verbose -C Debug || goto :error
+
+goto :success
+
+:error
+cd /d %~dp0\..\..\..
+echo Failed!
+exit /b 1
+
+:success
+cd ..
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/cmake_nmake/continuous.cfg
similarity index 63%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/cmake_nmake/continuous.cfg
index 9a71745..3c279fe 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/cmake_nmake/continuous.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake_nmake/build.bat"
timeout_mins: 1440
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/cmake_nmake/presubmit.cfg
similarity index 63%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/cmake_nmake/presubmit.cfg
index 9a71745..3c279fe 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/cmake_nmake/presubmit.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake_nmake/build.bat"
timeout_mins: 1440
diff --git a/kokoro/windows/cmake_shared/build.bat b/kokoro/windows/cmake_shared/build.bat
new file mode 100644
index 0000000..bcd5244
--- /dev/null
+++ b/kokoro/windows/cmake_shared/build.bat
@@ -0,0 +1,33 @@
+@rem enter repo root
+cd /d %~dp0\..\..\..
+
+call kokoro\windows\prepare_build_win64.bat || goto :error
+
+@rem TODO(b/241475022) Use docker to guarantee better stability.
+@rem TODO(b/241484899) Run conformance tests in windows.
+
+md build -ea 0
+md %KOKORO_ARTIFACTS_DIR%\logs -ea 0
+
+cd build
+
+cmake .. ^
+ -G "Visual Studio 15 2017" -A x64 ^
+ -Dprotobuf_BUILD_SHARED_LIBS=ON ^
+ -Dprotobuf_BUILD_CONFORMANCE=OFF ^
+ -Dprotobuf_WITH_ZLIB=OFF ^
+ -Dprotobuf_TEST_XML_OUTDIR=%KOKORO_ARTIFACTS_DIR%\logs\ || goto :error
+
+cmake --build . || goto :error
+
+ctest --verbose -C Debug || goto :error
+
+goto :success
+
+:error
+cd /d %~dp0\..\..\..
+echo Failed!
+exit /b 1
+
+:success
+cd ..
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/cmake_shared/continuous.cfg
similarity index 63%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/cmake_shared/continuous.cfg
index 9a71745..0ba52e2 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/cmake_shared/continuous.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake_shared/build.bat"
timeout_mins: 1440
diff --git a/kokoro/macos/php7.3_mac/continuous.cfg b/kokoro/windows/cmake_shared/presubmit.cfg
similarity index 63%
copy from kokoro/macos/php7.3_mac/continuous.cfg
copy to kokoro/windows/cmake_shared/presubmit.cfg
index 9a71745..0ba52e2 100644
--- a/kokoro/macos/php7.3_mac/continuous.cfg
+++ b/kokoro/windows/cmake_shared/presubmit.cfg
@@ -1,5 +1,5 @@
# Config file for running tests in Kokoro
# Location of the build script in repository
-build_file: "protobuf/kokoro/macos/php7.3_mac/build.sh"
+build_file: "protobuf/kokoro/windows/cmake_shared/build.bat"
timeout_mins: 1440
diff --git a/kokoro/windows/prepare_build_win64.bat b/kokoro/windows/prepare_build_win64.bat
new file mode 100644
index 0000000..d8eb2a2
--- /dev/null
+++ b/kokoro/windows/prepare_build_win64.bat
@@ -0,0 +1,16 @@
+@rem Update Chocolatey
+choco upgrade -y --no-progress chocolatey
+choco install -y --no-progress --pre cmake
+
+@rem Enable long paths.
+Powershell.exe -Command "New-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem -Name LongPathsEnabled -Value 1 -PropertyType DWORD -Force"
+
+@rem Update git submodules.
+git submodule update --init --recursive
+
+@rem Select Visual Studio 2017.
+call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64
+
+@rem Convert Windows line breaks to Unix line breaks
+@rem This allows text-matching tests to pass
+find . -type f -print0 | xargs -0 d2u
diff --git a/objectivec/GPBApi.pbobjc.h b/objectivec/GPBApi.pbobjc.h
index 871e908..1848aa6 100644
--- a/objectivec/GPBApi.pbobjc.h
+++ b/objectivec/GPBApi.pbobjc.h
@@ -74,12 +74,12 @@
/** The methods of this interface, in unspecified order. */
@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;
/** Any metadata attached to the interface. */
@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;
/**
@@ -115,7 +115,7 @@
/** Included interfaces. See [Mixin][]. */
@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;
/** The source syntax of the service. */
@@ -169,7 +169,7 @@
/** Any metadata attached to the method. */
@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;
/** The source syntax of this method. */
diff --git a/objectivec/GPBFieldMask.pbobjc.h b/objectivec/GPBFieldMask.pbobjc.h
index c4667b4..b7eccff 100644
--- a/objectivec/GPBFieldMask.pbobjc.h
+++ b/objectivec/GPBFieldMask.pbobjc.h
@@ -247,7 +247,7 @@
/** The set of field mask paths. */
@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;
@end
diff --git a/objectivec/GPBStruct.pbobjc.h b/objectivec/GPBStruct.pbobjc.h
index dd6ab28..ff4eefd 100644
--- a/objectivec/GPBStruct.pbobjc.h
+++ b/objectivec/GPBStruct.pbobjc.h
@@ -87,7 +87,7 @@
/** Unordered map of dynamically typed values. */
@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;
@end
@@ -178,7 +178,7 @@
/** Repeated field of dynamically typed values. */
@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;
@end
diff --git a/objectivec/GPBType.pbobjc.h b/objectivec/GPBType.pbobjc.h
index b023050..969219f 100644
--- a/objectivec/GPBType.pbobjc.h
+++ b/objectivec/GPBType.pbobjc.h
@@ -195,17 +195,17 @@
/** The list of fields. */
@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;
/** The list of types appearing in `oneof` definitions in this type. */
@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;
/** The protocol buffer options. */
@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;
/** The source context. */
@@ -279,7 +279,7 @@
/** The protocol buffer options. */
@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;
/** The field JSON name. */
@@ -334,12 +334,12 @@
/** Enum value definitions. */
@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;
/** Protocol buffer options. */
@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;
/** The source context. */
@@ -385,7 +385,7 @@
/** Protocol buffer options. */
@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;
@end
diff --git a/objectivec/GPBUtilities.m b/objectivec/GPBUtilities.m
index b72d0e2..cd0910c 100644
--- a/objectivec/GPBUtilities.m
+++ b/objectivec/GPBUtilities.m
@@ -2248,7 +2248,7 @@
NSCAssert([[self descriptor] oneofWithName:oneof.name] == oneof,
@"OneofDescriptor %@ doesn't appear to be for %@ messages.",
oneof.name, [self class]);
- GPBFieldDescriptor *firstField = oneof->fields_[0];
+ GPBFieldDescriptor *firstField __unused = oneof->fields_[0];
NSCAssert(firstField->description_->hasIndex == oneofHasIndex,
@"Internal error, oneofHasIndex (%d) doesn't match (%d).",
firstField->description_->hasIndex, oneofHasIndex);
diff --git a/objectivec/generate_well_known_types.sh b/objectivec/generate_well_known_types.sh
index 1b9de6e..e6c9b04 100755
--- a/objectivec/generate_well_known_types.sh
+++ b/objectivec/generate_well_known_types.sh
@@ -73,6 +73,7 @@
if ! diff "${ObjCDir}/GPB${OBJC_NAME}${EXT}" "${TMP_DIR}/${DIR}/${OBJC_NAME}${EXT}" > /dev/null 2>&1 ; then
if [[ "${CHECK_ONLY}" == 1 ]] ; then
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
fi
diff --git a/php/BUILD.bazel b/php/BUILD.bazel
index 3460959..dfb0976 100644
--- a/php/BUILD.bazel
+++ b/php/BUILD.bazel
@@ -12,7 +12,9 @@
"src/Google/Protobuf/**/*.php",
"tests/*.php",
"tests/*.sh",
+ "tests/generated_previous/**/*.php",
"tests/proto/**/*.proto",
+ "tests/proto_previous/*.proto",
]) + [
"BUILD.bazel",
"README.md",
diff --git a/php/composer.json b/php/composer.json
index 756704f..436142f 100644
--- a/php/composer.json
+++ b/php/composer.json
@@ -20,7 +20,10 @@
"autoload-dev": {
"psr-4": {
"": "tmp"
- }
+ },
+ "classmap": [
+ "tests/generated_previous"
+ ]
},
"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",
diff --git a/php/ext/google/protobuf/def.c b/php/ext/google/protobuf/def.c
index dfb96f2..8c3266e 100644
--- a/php/ext/google/protobuf/def.c
+++ b/php/ext/google/protobuf/def.c
@@ -162,7 +162,7 @@
ZVAL_NULL(val);
} else {
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_class_entry *ce = zend_lookup_class(str); // May autoload the class.
@@ -457,6 +457,44 @@
}
/*
+ * FieldDescriptor::getContainingOneof()
+ *
+ * Returns the OneofDescriptor for this field, or null if it is not inside
+ * a oneof.
+ */
+PHP_METHOD(FieldDescriptor, getContainingOneof) {
+ FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+ const upb_OneofDef *o = upb_FieldDef_ContainingOneof(intern->fielddef);
+ zval ret;
+
+ if (!o) {
+ RETURN_NULL();
+ }
+
+ OneofDescriptor_FromOneofDef(&ret, o);
+ RETURN_COPY_VALUE(&ret);
+}
+
+/*
+ * FieldDescriptor::getRealContainingOneof()
+ *
+ * Returns the non-synthetic OneofDescriptor for this field, or null if it is
+ * not inside a oneof.
+ */
+PHP_METHOD(FieldDescriptor, getRealContainingOneof) {
+ FieldDescriptor *intern = (FieldDescriptor*)Z_OBJ_P(getThis());
+ const upb_OneofDef *o = upb_FieldDef_RealContainingOneof(intern->fielddef);
+ zval ret;
+
+ if (!o) {
+ RETURN_NULL();
+ }
+
+ OneofDescriptor_FromOneofDef(&ret, o);
+ RETURN_COPY_VALUE(&ret);
+}
+
+/*
* FieldDescriptor::getMessageType()
*
* Returns the Descriptor for this field, which must be a message.
@@ -482,6 +520,8 @@
PHP_ME(FieldDescriptor, getType, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, isMap, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, getEnumType, arginfo_void, ZEND_ACC_PUBLIC)
+ PHP_ME(FieldDescriptor, getContainingOneof, arginfo_void, ZEND_ACC_PUBLIC)
+ PHP_ME(FieldDescriptor, getRealContainingOneof, arginfo_void, ZEND_ACC_PUBLIC)
PHP_ME(FieldDescriptor, getMessageType, arginfo_void, ZEND_ACC_PUBLIC)
ZEND_FE_END
};
@@ -499,19 +539,24 @@
}
static zend_class_entry *Descriptor_GetGeneratedClass(const upb_MessageDef *m) {
- char *classname =
- GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m));
- zend_string *str = zend_string_init(classname, strlen(classname), 0);
- zend_class_entry *ce = zend_lookup_class(str); // May autoload the class.
+ for (int i = 0; i < 2; ++i) {
+ char *classname =
+ GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m), (bool)i);
+ 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) {
- zend_error(E_ERROR, "Couldn't load generated class %s", classname);
+ if (ce) {
+ return ce;
+ }
}
- free(classname);
- return ce;
+ char *classname =
+ GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m), false);
+ zend_error(E_ERROR, "Couldn't load generated class %s", classname);
+ return NULL;
}
void Descriptor_FromMessageDef(zval *val, const upb_MessageDef *m) {
diff --git a/php/ext/google/protobuf/names.c b/php/ext/google/protobuf/names.c
index 5d7b68a..e359d01 100644
--- a/php/ext/google/protobuf/names.c
+++ b/php/ext/google/protobuf/names.c
@@ -82,12 +82,15 @@
"global", "goto", "insteadof", "interface", "isset",
"list", "match", "namespace", "new", "object",
"or", "parent", "print", "private", "protected",
- "public", "require", "require_once", "return", "self",
- "static", "switch", "throw", "trait", "try",
- "unset", "use", "var", "while", "xor",
- "yield", "int", "float", "bool", "string",
- "true", "false", "null", "void", "iterable",
- NULL};
+ "public", "readonly", "require", "require_once", "return",
+ "self", "static", "switch", "throw", "trait",
+ "try", "unset", "use", "var", "while",
+ "xor", "yield", "int", "float", "bool",
+ "string", "true", "false", "null", "void",
+ "iterable", NULL};
+
+const char *const kPreviouslyUnreservedNames[] = {
+ "readonly", NULL};
bool is_reserved_name(const char* name) {
int i;
@@ -99,6 +102,15 @@
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) {
if (ch >= 'A' && ch <= 'Z') {
return ch - ('A' - 'a');
@@ -115,17 +127,22 @@
}
}
-static bool is_reserved(const char *segment, int length) {
- bool result;
- char* lower = calloc(1, length + 1);
- memcpy(lower, segment, length);
- int i = 0;
- while(lower[i]) {
- lower[i] = nolocale_tolower(lower[i]);
- i++;
+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]);
}
- lower[length] = 0;
+ return lower;
+}
+
+static bool is_reserved(const char *segment, int length, bool previous) {
+ bool result;
+ char* lower = strdup_nolocale_lower(segment, length);
result = is_reserved_name(lower);
+ if (result && previous && is_previously_unreserved_name(lower)) {
+ result = false;
+ }
free(lower);
return result;
}
@@ -133,11 +150,12 @@
static void fill_prefix(const char *segment, int length,
const char *prefix_given,
const char *package_name,
- stringsink *classname) {
+ stringsink *classname,
+ bool previous) {
if (prefix_given != NULL && strcmp(prefix_given, "") != 0) {
stringsink_string(classname, prefix_given, strlen(prefix_given));
} else {
- if (is_reserved(segment, length)) {
+ if (is_reserved(segment, length, previous)) {
if (package_name != NULL &&
strcmp("google.protobuf", package_name) == 0) {
stringsink_string(classname, "GPB", 3);
@@ -160,7 +178,7 @@
}
static void fill_namespace(const char *package, const char *php_namespace,
- stringsink *classname) {
+ stringsink *classname, bool previous) {
if (php_namespace != NULL) {
if (strlen(php_namespace) != 0) {
stringsink_string(classname, php_namespace, strlen(php_namespace));
@@ -174,7 +192,7 @@
while (j < package_len && package[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);
stringsink_string(classname, "\\", 1);
i = j + 1;
@@ -185,7 +203,8 @@
static void fill_classname(const char *fullname,
const char *package,
const char *prefix,
- stringsink *classname) {
+ stringsink *classname,
+ bool previous) {
int classname_start = 0;
if (package != NULL) {
size_t package_len = strlen(package);
@@ -199,7 +218,7 @@
while (j < fullname_len && fullname[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);
if (j != fullname_len) {
stringsink_string(classname, "\\", 1);
@@ -215,7 +234,7 @@
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
// bytes allocated, one for '.', one for trailing 0, and 3 for 'GPB' if
// given message is google.protobuf.Empty.
@@ -234,8 +253,8 @@
stringsink namesink;
stringsink_init(&namesink);
- fill_namespace(package, php_namespace, &namesink);
- fill_classname(fullname, package, prefix, &namesink);
+ fill_namespace(package, php_namespace, &namesink, previous);
+ fill_classname(fullname, package, prefix, &namesink, previous);
stringsink_string(&namesink, "\0", 1);
ret = strdup(namesink.ptr);
stringsink_uninit(&namesink);
@@ -243,3 +262,26 @@
free(prefix);
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;
+}
diff --git a/php/ext/google/protobuf/names.h b/php/ext/google/protobuf/names.h
index 86af799..cc42dc8 100644
--- a/php/ext/google/protobuf/names.h
+++ b/php/ext/google/protobuf/names.h
@@ -35,6 +35,7 @@
// Translates a protobuf symbol name (eg. foo.bar.Baz) into a PHP class name
// (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_
diff --git a/php/ext/google/protobuf/package.xml b/php/ext/google/protobuf/package.xml
index bf3d90e..c291714 100644
--- a/php/ext/google/protobuf/package.xml
+++ b/php/ext/google/protobuf/package.xml
@@ -5,16 +5,16 @@
<summary>Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data.</summary>
<description>https://developers.google.com/protocol-buffers/</description>
<lead>
- <name>Bo Yang</name>
- <user>stanleycheung</user>
- <email>protobuf-opensource@google.com</email>
+ <name>Protobuf Team</name>
+ <user>protobufpackages</user>
+ <email>protobuf-packages@google.com</email>
<active>yes</active>
</lead>
- <date>2022-06-23</date>
- <time>12:12:44</time>
+ <date>2022-07-25</date>
+ <time>13:21:48</time>
<version>
- <release>3.21.2</release>
- <api>3.21.2</api>
+ <release>3.21.4</release>
+ <api>3.21.4</api>
</version>
<stability>
<release>stable</release>
@@ -1358,5 +1358,35 @@
<notes>
</notes>
</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>
+ <release>
+ <version>
+ <release>3.21.4</release>
+ <api>3.21.4</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <date>2022-07-25</date>
+ <time>13:21:48</time>
+ <license uri="https://opensource.org/licenses/BSD-3-Clause">BSD-3-Clause</license>
+ <notes>
+ </notes>
+ </release>
</changelog>
</package>
diff --git a/php/ext/google/protobuf/protobuf.c b/php/ext/google/protobuf/protobuf.c
index c786b6e..a5aba23 100644
--- a/php/ext/google/protobuf/protobuf.c
+++ b/php/ext/google/protobuf/protobuf.c
@@ -242,13 +242,19 @@
// -----------------------------------------------------------------------------
void NameMap_AddMessage(const upb_MessageDef *m) {
- char *k = GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m));
- zend_hash_str_add_ptr(&PROTOBUF_G(name_msg_cache), k, strlen(k), (void*)m);
- free(k);
+ for (int i = 0; i < 2; ++i) {
+ char *k = GetPhpClassname(upb_MessageDef_File(m), upb_MessageDef_FullName(m), (bool)i);
+ 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) {
- 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);
free(k);
}
diff --git a/php/ext/google/protobuf/protobuf.h b/php/ext/google/protobuf/protobuf.h
index c5dac32..e63e9e8 100644
--- a/php/ext/google/protobuf/protobuf.h
+++ b/php/ext/google/protobuf/protobuf.h
@@ -127,7 +127,7 @@
ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()
-#define PHP_PROTOBUF_VERSION "3.21.2"
+#define PHP_PROTOBUF_VERSION "3.21.4"
// ptr -> PHP object cache. This is a weak map that caches lazily-created
// wrapper objects around upb types:
diff --git a/php/src/Google/Protobuf/Internal/Descriptor.php b/php/src/Google/Protobuf/Internal/Descriptor.php
index a7f80d5..51a34d6 100644
--- a/php/src/Google/Protobuf/Internal/Descriptor.php
+++ b/php/src/Google/Protobuf/Internal/Descriptor.php
@@ -45,6 +45,7 @@
private $enum_type = [];
private $klass;
private $legacy_klass;
+ private $previous_klass;
private $options;
private $oneof_decl = [];
@@ -162,6 +163,16 @@
return $this->legacy_klass;
}
+ public function setPreviouslyUnreservedClass($klass)
+ {
+ $this->previous_klass = $klass;
+ }
+
+ public function getPreviouslyUnreservedClass()
+ {
+ return $this->previous_klass;
+ }
+
public function setOptions($options)
{
$this->options = $options;
@@ -179,6 +190,7 @@
$message_name_without_package = "";
$classname = "";
$legacy_classname = "";
+ $previous_classname = "";
$fullname = "";
GPBUtil::getFullClassName(
$proto,
@@ -187,10 +199,12 @@
$message_name_without_package,
$classname,
$legacy_classname,
- $fullname);
+ $fullname,
+ $previous_classname);
$desc->setFullName($fullname);
$desc->setClass($classname);
$desc->setLegacyClass($legacy_classname);
+ $desc->setPreviouslyUnreservedClass($previous_classname);
$desc->setOptions($proto->getOptions());
foreach ($proto->getField() as $field_proto) {
diff --git a/php/src/Google/Protobuf/Internal/DescriptorPool.php b/php/src/Google/Protobuf/Internal/DescriptorPool.php
index 1468a02..1be00e2 100644
--- a/php/src/Google/Protobuf/Internal/DescriptorPool.php
+++ b/php/src/Google/Protobuf/Internal/DescriptorPool.php
@@ -96,6 +96,7 @@
$descriptor->getClass();
$this->class_to_desc[$descriptor->getClass()] = $descriptor;
$this->class_to_desc[$descriptor->getLegacyClass()] = $descriptor;
+ $this->class_to_desc[$descriptor->getPreviouslyUnreservedClass()] = $descriptor;
foreach ($descriptor->getNestedType() as $nested_type) {
$this->addDescriptor($nested_type);
}
diff --git a/php/src/Google/Protobuf/Internal/EnumDescriptor.php b/php/src/Google/Protobuf/Internal/EnumDescriptor.php
index 7af4f84..383f53b 100644
--- a/php/src/Google/Protobuf/Internal/EnumDescriptor.php
+++ b/php/src/Google/Protobuf/Internal/EnumDescriptor.php
@@ -101,7 +101,8 @@
$enum_name_without_package,
$classname,
$legacy_classname,
- $fullname);
+ $fullname,
+ $unused_previous_classname);
$desc->setFullName($fullname);
$desc->setClass($classname);
$desc->setLegacyClass($legacy_classname);
diff --git a/php/src/Google/Protobuf/Internal/GPBUtil.php b/php/src/Google/Protobuf/Internal/GPBUtil.php
index 4b15283..ffea900 100644
--- a/php/src/Google/Protobuf/Internal/GPBUtil.php
+++ b/php/src/Google/Protobuf/Internal/GPBUtil.php
@@ -285,11 +285,12 @@
"include"=>0, "include_once"=>0, "instanceof"=>0, "insteadof"=>0,
"interface"=>0, "isset"=>0, "list"=>0, "match"=>0, "namespace"=>0,
"new"=>0, "or"=>0, "parent"=>0, "print"=>0, "private"=>0,
- "protected"=>0,"public"=>0, "require"=>0, "require_once"=>0,
- "return"=>0, "self"=>0, "static"=>0, "switch"=>0, "throw"=>0,
- "trait"=>0, "try"=>0,"unset"=>0, "use"=>0, "var"=>0, "while"=>0,
- "xor"=>0, "yield"=>0, "int"=>0, "float"=>0, "bool"=>0, "string"=>0,
- "true"=>0, "false"=>0, "null"=>0, "void"=>0, "iterable"=>0
+ "protected"=>0,"public"=>0, "readonly" => 0,"require"=>0,
+ "require_once"=>0,"return"=>0, "self"=>0, "static"=>0, "switch"=>0,
+ "throw"=>0,"trait"=>0, "try"=>0,"unset"=>0, "use"=>0, "var"=>0,
+ "while"=>0,"xor"=>0, "yield"=>0, "int"=>0, "float"=>0, "bool"=>0,
+ "string"=>0,"true"=>0, "false"=>0, "null"=>0, "void"=>0,
+ "iterable"=>0
);
if (array_key_exists(strtolower($classname), $reserved_words)) {
@@ -303,6 +304,27 @@
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(
$name,
$file_proto)
@@ -322,6 +344,17 @@
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(
$proto,
$containing,
@@ -329,7 +362,8 @@
&$message_name_without_package,
&$classname,
&$legacy_classname,
- &$fullname)
+ &$fullname,
+ &$previous_classname)
{
// Full name needs to start with '.'.
$message_name_without_package = $proto->getName();
@@ -350,6 +384,9 @@
$legacy_class_name_without_package =
static::getLegacyClassNameWithoutPackage(
$message_name_without_package, $file_proto);
+ $previous_class_name_without_package =
+ static::getPreviouslyUnreservedClassNameWithoutPackage(
+ $message_name_without_package, $file_proto);
$option = $file_proto->getOptions();
if (!is_null($option) && $option->hasPhpNamespace()) {
@@ -358,10 +395,13 @@
$classname = $namespace . "\\" . $class_name_without_package;
$legacy_classname =
$namespace . "\\" . $legacy_class_name_without_package;
+ $previous_classname =
+ $namespace . "\\" . $previous_class_name_without_package;
return;
} else {
$classname = $class_name_without_package;
$legacy_classname = $legacy_class_name_without_package;
+ $previous_classname = $previous_class_name_without_package;
return;
}
}
@@ -369,6 +409,7 @@
if ($package === "") {
$classname = $class_name_without_package;
$legacy_classname = $legacy_class_name_without_package;
+ $previous_classname = $previous_class_name_without_package;
} else {
$parts = array_map('ucwords', explode('.', $package));
foreach ($parts as $i => $part) {
@@ -381,6 +422,11 @@
$legacy_classname =
implode('\\', array_map('ucwords', explode('.', $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;
}
}
diff --git a/php/tests/DescriptorsTest.php b/php/tests/DescriptorsTest.php
index 4f17a49..336d979 100644
--- a/php/tests/DescriptorsTest.php
+++ b/php/tests/DescriptorsTest.php
@@ -83,10 +83,10 @@
$this->assertSame($class, $desc->getClass());
$this->assertInstanceOf('\Google\Protobuf\FieldDescriptor', $desc->getField(0));
- $this->assertSame(7, $desc->getFieldCount());
+ $this->assertSame(8, $desc->getFieldCount());
$this->assertInstanceOf('\Google\Protobuf\OneofDescriptor', $desc->getOneofDecl(0));
- $this->assertSame(1, $desc->getOneofDeclCount());
+ $this->assertSame(2, $desc->getOneofDeclCount());
}
public function testDescriptorForIncludedMessage()
@@ -180,6 +180,7 @@
$this->assertSame(self::GPBTYPE_MESSAGE, $fieldDesc->getType());
$this->assertInstanceOf('\Google\Protobuf\Descriptor', $fieldDesc->getMessageType());
$this->assertFalse($fieldDesc->isMap());
+ $this->assertNull($fieldDesc->getContainingOneof());
// Oneof int field
// Tested further in testOneofDescriptor()
@@ -189,6 +190,21 @@
$this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
$this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
$this->assertFalse($fieldDesc->isMap());
+ $this->assertSame($fieldDesc->getContainingOneof(), $fieldDesc->getRealContainingOneof());
+
+ $oneofDesc = $fieldDesc->getContainingOneof();
+ $this->assertSame('my_oneof', $oneofDesc->getName());
+
+ // Proto3 optional it field.
+ // Tested further in testOneofDescriptor()
+ $fieldDesc = $fieldDescMap[52];
+ $this->assertSame('proto3_optional_int32', $fieldDesc->getName());
+ $this->assertSame(52, $fieldDesc->getNumber());
+ $this->assertSame(self::GPBLABEL_OPTIONAL, $fieldDesc->getLabel());
+ $this->assertSame(self::GPBTYPE_INT32, $fieldDesc->getType());
+ $this->assertFalse($fieldDesc->isMap());
+ $this->assertNull($fieldDesc->getRealContainingOneof());
+ $this->assertNotNull($fieldDesc->getContainingOneof());
// Map int-enum field
$fieldDesc = $fieldDescMap[71];
diff --git a/php/tests/GeneratedClassTest.php b/php/tests/GeneratedClassTest.php
index 8a89973..b42649d 100644
--- a/php/tests/GeneratedClassTest.php
+++ b/php/tests/GeneratedClassTest.php
@@ -334,6 +334,19 @@
$this->legacyEnum(new TestLegacyMessage\NestedEnum);
}
+ public function testLegacyReadOnlyMessage()
+ {
+ $this->assertTrue(class_exists('\Upper\READONLY'));
+ $this->assertTrue(class_exists('\Lower\readonly'));
+ $this->assertTrue(class_exists('\Php\Test\TestNamespace\PBEmpty\ReadOnly'));
+ }
+
+ public function testLegacyReadOnlyEnum()
+ {
+ $this->assertTrue(class_exists('\Upper_enum\READONLY'));
+ $this->assertTrue(class_exists('\Lower_enum\readonly'));
+ }
+
private function legacyEnum(TestLegacyMessage_NestedEnum $enum)
{
// If we made it here without a PHP Fatal error, the typehint worked
@@ -943,6 +956,7 @@
$m = new \Lower\PBprivate();
$m = new \Lower\PBprotected();
$m = new \Lower\PBpublic();
+ $m = new \Lower\PBreadonly();
$m = new \Lower\PBrequire();
$m = new \Lower\PBrequire_once();
$m = new \Lower\PBreturn();
@@ -1023,6 +1037,7 @@
$m = new \Upper\PBPRIVATE();
$m = new \Upper\PBPROTECTED();
$m = new \Upper\PBPUBLIC();
+ $m = new \Upper\PBREADONLY();
$m = new \Upper\PBREQUIRE();
$m = new \Upper\PBREQUIRE_ONCE();
$m = new \Upper\PBRETURN();
@@ -1104,6 +1119,7 @@
$m = new \Lower_enum\PBprotected();
$m = new \Lower_enum\PBpublic();
$m = new \Lower_enum\PBrequire();
+ $m = new \Lower_enum\PBreadonly();
$m = new \Lower_enum\PBrequire_once();
$m = new \Lower_enum\PBreturn();
$m = new \Lower_enum\PBself();
@@ -1183,6 +1199,7 @@
$m = new \Upper_enum\PBPRIVATE();
$m = new \Upper_enum\PBPROTECTED();
$m = new \Upper_enum\PBPUBLIC();
+ $m = new \Upper_enum\PBREADONLY();
$m = new \Upper_enum\PBREQUIRE();
$m = new \Upper_enum\PBREQUIRE_ONCE();
$m = new \Upper_enum\PBRETURN();
@@ -1287,6 +1304,7 @@
$m = \Lower_enum_value\NotAllowed::iterable;
$m = \Lower_enum_value\NotAllowed::parent;
$m = \Lower_enum_value\NotAllowed::self;
+ $m = \Lower_enum_value\NotAllowed::readonly;
$m = \Upper_enum_value\NotAllowed::PBABSTRACT;
$m = \Upper_enum_value\NotAllowed::PBAND;
@@ -1367,6 +1385,7 @@
$m = \Upper_enum_value\NotAllowed::ITERABLE;
$m = \Upper_enum_value\NotAllowed::PARENT;
$m = \Upper_enum_value\NotAllowed::SELF;
+ $m = \Upper_enum_value\NotAllowed::READONLY;
$this->assertTrue(true);
}
diff --git a/php/tests/PreviouslyGeneratedClassTest.php b/php/tests/PreviouslyGeneratedClassTest.php
new file mode 100644
index 0000000..077c84f
--- /dev/null
+++ b/php/tests/PreviouslyGeneratedClassTest.php
@@ -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);
+ }
+}
diff --git a/php/tests/generated_previous/GPBMetadata/ProtoPrevious/TestPreviouslyUnreservedMessage.php b/php/tests/generated_previous/GPBMetadata/ProtoPrevious/TestPreviouslyUnreservedMessage.php
new file mode 100644
index 0000000..d2120f9
--- /dev/null
+++ b/php/tests/generated_previous/GPBMetadata/ProtoPrevious/TestPreviouslyUnreservedMessage.php
@@ -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;
+ }
+}
+
diff --git a/php/tests/generated_previous/Previous/readonly.php b/php/tests/generated_previous/Previous/readonly.php
new file mode 100644
index 0000000..013f293
--- /dev/null
+++ b/php/tests/generated_previous/Previous/readonly.php
@@ -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);
+ }
+
+}
+
diff --git a/php/tests/proto/test_descriptors.proto b/php/tests/proto/test_descriptors.proto
index d42aec7..b0956c6 100644
--- a/php/tests/proto/test_descriptors.proto
+++ b/php/tests/proto/test_descriptors.proto
@@ -14,6 +14,7 @@
oneof my_oneof {
int32 oneof_int32 = 51;
}
+ optional int32 proto3_optional_int32 = 52;
map<int32, EnumSub> map_int32_enum = 71;
diff --git a/php/tests/proto/test_php_namespace.proto b/php/tests/proto/test_php_namespace.proto
index 61085bf..eb4a523 100644
--- a/php/tests/proto/test_php_namespace.proto
+++ b/php/tests/proto/test_php_namespace.proto
@@ -27,5 +27,7 @@
enum NestedEnum {
ZERO = 0;
};
+ // Test previously unreserved name
+ message ReadOnly {}
}
}
diff --git a/php/tests/proto/test_reserved_enum_lower.proto b/php/tests/proto/test_reserved_enum_lower.proto
index f8557d2..1f96ac6 100644
--- a/php/tests/proto/test_reserved_enum_lower.proto
+++ b/php/tests/proto/test_reserved_enum_lower.proto
@@ -57,6 +57,7 @@
enum private { ZERO52 = 0; }
enum protected { ZERO53 = 0; }
enum public { ZERO54 = 0; }
+enum readonly { ZERO80 = 0; }
enum require { ZERO55 = 0; }
enum require_once { ZERO56 = 0; }
enum return { ZERO57 = 0; }
diff --git a/php/tests/proto/test_reserved_enum_upper.proto b/php/tests/proto/test_reserved_enum_upper.proto
index 8d382ab..c5e7e99 100644
--- a/php/tests/proto/test_reserved_enum_upper.proto
+++ b/php/tests/proto/test_reserved_enum_upper.proto
@@ -57,6 +57,7 @@
enum PRIVATE { ZERO52 = 0; }
enum PROTECTED { ZERO53 = 0; }
enum PUBLIC { ZERO54 = 0; }
+enum READONLY { ZERO80 = 0; }
enum REQUIRE { ZERO55 = 0; }
enum REQUIRE_ONCE { ZERO56 = 0; }
enum RETURN { ZERO57 = 0; }
diff --git a/php/tests/proto/test_reserved_enum_value_lower.proto b/php/tests/proto/test_reserved_enum_value_lower.proto
index ca5a7c7..86c6877 100644
--- a/php/tests/proto/test_reserved_enum_value_lower.proto
+++ b/php/tests/proto/test_reserved_enum_value_lower.proto
@@ -58,6 +58,7 @@
private = 51;
protected = 52;
public = 53;
+ readonly = 79;
require = 54;
require_once = 55;
return = 56;
diff --git a/php/tests/proto/test_reserved_enum_value_upper.proto b/php/tests/proto/test_reserved_enum_value_upper.proto
index 6b4040d..ac0beda 100644
--- a/php/tests/proto/test_reserved_enum_value_upper.proto
+++ b/php/tests/proto/test_reserved_enum_value_upper.proto
@@ -58,6 +58,7 @@
PRIVATE = 51;
PROTECTED = 52;
PUBLIC = 53;
+ READONLY = 79;
REQUIRE = 54;
REQUIRE_ONCE = 55;
RETURN = 56;
diff --git a/php/tests/proto/test_reserved_message_lower.proto b/php/tests/proto/test_reserved_message_lower.proto
index 2390a87..551ed7a 100644
--- a/php/tests/proto/test_reserved_message_lower.proto
+++ b/php/tests/proto/test_reserved_message_lower.proto
@@ -57,6 +57,7 @@
message private {}
message protected {}
message public {}
+message readonly {}
message require {}
message require_once {}
message return {}
diff --git a/php/tests/proto/test_reserved_message_upper.proto b/php/tests/proto/test_reserved_message_upper.proto
index 9f55330..96995c9 100644
--- a/php/tests/proto/test_reserved_message_upper.proto
+++ b/php/tests/proto/test_reserved_message_upper.proto
@@ -57,6 +57,7 @@
message PRIVATE {}
message PROTECTED {}
message PUBLIC {}
+message READONLY {}
message REQUIRE {}
message REQUIRE_ONCE {}
message RETURN {}
diff --git a/php/tests/proto_previous/test_previously_unreserved_message.proto b/php/tests/proto_previous/test_previously_unreserved_message.proto
new file mode 100644
index 0000000..1b4b0b7
--- /dev/null
+++ b/php/tests/proto_previous/test_previously_unreserved_message.proto
@@ -0,0 +1,5 @@
+syntax = "proto3";
+
+package previous;
+
+message readonly {}
\ No newline at end of file
diff --git a/protobuf_version.bzl b/protobuf_version.bzl
index b6c35f3..edac503 100644
--- a/protobuf_version.bzl
+++ b/protobuf_version.bzl
@@ -1,3 +1,3 @@
-PROTOC_VERSION = '21.2'
-PROTOBUF_JAVA_VERSION = '3.21.2'
-PROTOBUF_PYTHON_VERSION = '4.21.2'
+PROTOC_VERSION = '21.4'
+PROTOBUF_JAVA_VERSION = '3.21.4'
+PROTOBUF_PYTHON_VERSION = '4.21.4'
diff --git a/protoc-artifacts/pom.xml b/protoc-artifacts/pom.xml
index bce8121..d816174 100644
--- a/protoc-artifacts/pom.xml
+++ b/protoc-artifacts/pom.xml
@@ -4,11 +4,11 @@
<parent>
<groupId>com.google</groupId>
<artifactId>google</artifactId>
- <version>1</version>
+ <version>5</version>
</parent>
<groupId>com.google.protobuf</groupId>
<artifactId>protoc</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
<packaging>pom</packaging>
<name>Protobuf Compiler</name>
<description>
diff --git a/python/google/protobuf/__init__.py b/python/google/protobuf/__init__.py
index 2c3e06c..b01d870 100644
--- a/python/google/protobuf/__init__.py
+++ b/python/google/protobuf/__init__.py
@@ -30,4 +30,4 @@
# Copyright 2007 Google Inc. All Rights Reserved.
-__version__ = '4.21.2'
+__version__ = '4.21.4'
diff --git a/python/google/protobuf/descriptor.py b/python/google/protobuf/descriptor.py
index fa7a5b9..997f6db 100644
--- a/python/google/protobuf/descriptor.py
+++ b/python/google/protobuf/descriptor.py
@@ -1021,13 +1021,7 @@
# FileDescriptor() is called from various places, not only from generated
# files, to register dynamic proto files and messages.
# pylint: disable=g-explicit-bool-comparison
- if serialized_pb == b'':
- # Cpp generated code must be linked in if serialized_pb is ''
- try:
- return _message.default_pool.FindFileByName(name)
- except KeyError:
- raise RuntimeError('Please link in cpp generated lib for %s' % (name))
- elif serialized_pb:
+ if serialized_pb:
return _message.default_pool.AddSerializedFile(serialized_pb)
else:
return super(FileDescriptor, cls).__new__(cls)
diff --git a/python/google/protobuf/internal/message_test.py b/python/google/protobuf/internal/message_test.py
index 624dbdb..0a6c0b1 100644
--- a/python/google/protobuf/internal/message_test.py
+++ b/python/google/protobuf/internal/message_test.py
@@ -1768,6 +1768,19 @@
with self.assertRaises(TypeError):
123 in msg.map_string_string
+ def testScalarMapComparison(self):
+ msg1 = map_unittest_pb2.TestMap()
+ msg2 = map_unittest_pb2.TestMap()
+
+ self.assertEqual(msg1.map_int32_int32, msg2.map_int32_int32)
+
+ def testMessageMapComparison(self):
+ msg1 = map_unittest_pb2.TestMap()
+ msg2 = map_unittest_pb2.TestMap()
+
+ self.assertEqual(msg1.map_int32_foreign_message,
+ msg2.map_int32_foreign_message)
+
def testMapGet(self):
# Need to test that get() properly returns the default, even though the dict
# has defaultdict-like semantics.
diff --git a/python/google/protobuf/message.py b/python/google/protobuf/message.py
index 76c6802..0fe6a4f 100644
--- a/python/google/protobuf/message.py
+++ b/python/google/protobuf/message.py
@@ -74,7 +74,8 @@
__slots__ = []
- #: The :class:`google.protobuf.descriptor.Descriptor` for this message type.
+ #: The :class:`google.protobuf.Descriptor`
+ # for this message type.
DESCRIPTOR = None
def __deepcopy__(self, memo=None):
diff --git a/python/release.sh b/python/release.sh
index 15a70db..87fcf8c 100755
--- a/python/release.sh
+++ b/python/release.sh
@@ -19,11 +19,24 @@
chmod +x test-venv/bin/protoc
source test-venv/bin/activate
- pip install -i ${PYPI} protobuf==${VERSION} --no-cache-dir
+ (pip install -i ${PYPI} protobuf==${VERSION} --no-cache-dir) || (retry_pip_install ${PYPI} ${VERSION})
deactivate
rm -fr test-venv
}
+function retry_pip_install() {
+ local PYPI=$1
+ local VERSION=$2
+
+ read -p "pip install failed, possibly due to delay between upload and availability on pip. Retry? [y/n]" -r
+ echo
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
+ exit 1
+ fi
+
+ (pip install -i ${PYPI} protobuf==${VERSION} --no-cache-dir) || (retry_pip_install ${PYPI} ${VERSION})
+}
+
[ $# -lt 1 ] && {
echo "Usage: $0 VERSION ["
@@ -86,13 +99,16 @@
python3 setup.py sdist
twine upload --skip-existing -r testpypi -u protobuf-wheel-test dist/*
-# Test locally with different python versions.
+# Sleep to allow time for distribution to be available on pip.
+sleep 5m
+
+# Test locally.
run_install_test ${TESTING_VERSION} python3 https://test.pypi.org/simple
# Deploy egg/wheel packages to testing PyPI and test again.
python3 setup.py clean build bdist_wheel
twine upload --skip-existing -r testpypi -u protobuf-wheel-test dist/*
-
+sleep 5m
run_install_test ${TESTING_VERSION} python3 https://test.pypi.org/simple
echo "All install tests have passed using testing PyPI."
diff --git a/ruby/README.md b/ruby/README.md
index 42a1ffa..be8d6bc 100644
--- a/ruby/README.md
+++ b/ruby/README.md
@@ -9,7 +9,7 @@
Installation from Gem
---------------------
-In Gemfile (Please check a version of Protocol Buffers you needed [RubyGems](https://rubygems.org/gems/google-protobuf)):
+In Gemfile (Please check which version of Protocol Buffers you need: [RubyGems](https://rubygems.org/gems/google-protobuf)):
gem 'google-protobuf'
diff --git a/ruby/google-protobuf.gemspec b/ruby/google-protobuf.gemspec
index e7aa377..e43665d 100644
--- a/ruby/google-protobuf.gemspec
+++ b/ruby/google-protobuf.gemspec
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = "google-protobuf"
- s.version = "3.21.2"
+ s.version = "3.21.4"
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.summary = "Protocol Buffers"
diff --git a/ruby/pom.xml b/ruby/pom.xml
index f1bdbf1..4264172 100644
--- a/ruby/pom.xml
+++ b/ruby/pom.xml
@@ -9,7 +9,7 @@
<groupId>com.google.protobuf.jruby</groupId>
<artifactId>protobuf-jruby</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
<name>Protocol Buffer JRuby native extension</name>
<description>
Protocol Buffers are a way of encoding structured data in an efficient yet
@@ -76,7 +76,7 @@
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
- <version>3.21.2</version>
+ <version>3.21.4</version>
</dependency>
<dependency>
<groupId>org.jruby</groupId>
diff --git a/src/Makefile.am b/src/Makefile.am
index 9875b1c..b78c92d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,7 +18,7 @@
PTHREAD_DEF =
endif
-PROTOBUF_VERSION = 32:2:0
+PROTOBUF_VERSION = 3.21.4
if GCC
# Turn on all warnings except for sign comparison (we ignore sign comparison
@@ -189,7 +189,7 @@
lib_LTLIBRARIES = libprotobuf-lite.la libprotobuf.la libprotoc.la
libprotobuf_lite_la_LIBADD = $(PTHREAD_LIBS) $(LIBATOMIC_LIBS)
-libprotobuf_lite_la_LDFLAGS = -version-info $(PROTOBUF_VERSION) -export-dynamic -no-undefined
+libprotobuf_lite_la_LDFLAGS = -release $(PROTOBUF_VERSION) -export-dynamic -no-undefined
if HAVE_LD_VERSION_SCRIPT
libprotobuf_lite_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libprotobuf-lite.map
EXTRA_libprotobuf_lite_la_DEPENDENCIES = libprotobuf-lite.map
@@ -236,7 +236,7 @@
google/protobuf/wire_format_lite.cc
libprotobuf_la_LIBADD = $(PTHREAD_LIBS) $(LIBATOMIC_LIBS)
-libprotobuf_la_LDFLAGS = -version-info $(PROTOBUF_VERSION) -export-dynamic -no-undefined
+libprotobuf_la_LDFLAGS = -release $(PROTOBUF_VERSION) -export-dynamic -no-undefined
if HAVE_LD_VERSION_SCRIPT
libprotobuf_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libprotobuf.map
EXTRA_libprotobuf_la_DEPENDENCIES = libprotobuf.map
@@ -322,7 +322,7 @@
nodist_libprotobuf_la_SOURCES = $(nodist_libprotobuf_lite_la_SOURCES)
libprotoc_la_LIBADD = $(PTHREAD_LIBS) libprotobuf.la
-libprotoc_la_LDFLAGS = -version-info $(PROTOBUF_VERSION) -export-dynamic -no-undefined
+libprotoc_la_LDFLAGS = -release $(PROTOBUF_VERSION) -export-dynamic -no-undefined
if HAVE_LD_VERSION_SCRIPT
libprotoc_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libprotoc.map
EXTRA_libprotoc_la_DEPENDENCIES = libprotoc.map
@@ -774,6 +774,7 @@
google/protobuf/compiler/csharp/csharp_generator_unittest.cc \
google/protobuf/compiler/importer_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/mock_code_generator.cc \
google/protobuf/compiler/mock_code_generator.h \
diff --git a/src/file_lists.cmake b/src/file_lists.cmake
index 32f10b2..67092cc 100644
--- a/src/file_lists.cmake
+++ b/src/file_lists.cmake
@@ -771,6 +771,7 @@
${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/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/objectivec/objectivec_helpers_unittest.cc
${protobuf_SOURCE_DIR}/src/google/protobuf/compiler/parser_unittest.cc
diff --git a/src/google/protobuf/any.pb.h b/src/google/protobuf/any.pb.h
index 2368446..0a9ee26 100644
--- a/src/google/protobuf/any.pb.h
+++ b/src/google/protobuf/any.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/api.pb.h b/src/google/protobuf/api.pb.h
index ac80560..4903294 100644
--- a/src/google/protobuf/api.pb.h
+++ b/src/google/protobuf/api.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/arena_unittest.cc b/src/google/protobuf/arena_unittest.cc
index 0987c99..1466376 100644
--- a/src/google/protobuf/arena_unittest.cc
+++ b/src/google/protobuf/arena_unittest.cc
@@ -1419,7 +1419,7 @@
ASSERT_GT(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) {
diff --git a/src/google/protobuf/arenastring.cc b/src/google/protobuf/arenastring.cc
index 03b1438..8d7e6ff 100644
--- a/src/google/protobuf/arenastring.cc
+++ b/src/google/protobuf/arenastring.cc
@@ -50,7 +50,8 @@
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 guaranteed by `new std::string` depends on both:
// - new align = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / max_align_t
@@ -64,8 +65,8 @@
#endif
constexpr size_t kStringAlign = alignof(std::string);
-static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 8, "");
-static_assert(alignof(ExplicitlyConstructedArenaString) >= 8, "");
+static_assert((kStringAlign > kNewAlign ? kStringAlign : kNewAlign) >= 4, "");
+static_assert(alignof(ExplicitlyConstructedArenaString) >= 4, "");
} // namespace
diff --git a/src/google/protobuf/arenaz_sampler.cc b/src/google/protobuf/arenaz_sampler.cc
index 6ad3a36..ef67518 100644
--- a/src/google/protobuf/arenaz_sampler.cc
+++ b/src/google/protobuf/arenaz_sampler.cc
@@ -33,6 +33,7 @@
#include <atomic>
#include <cstdint>
#include <limits>
+#include <utility>
// Must be included last.
@@ -56,23 +57,34 @@
PROTOBUF_CONSTINIT std::atomic<bool> g_arenaz_enabled{true};
PROTOBUF_CONSTINIT std::atomic<int32_t> g_arenaz_sample_parameter{1 << 10};
+PROTOBUF_CONSTINIT std::atomic<ThreadSafeArenazConfigListener>
+ g_arenaz_config_listener{nullptr};
PROTOBUF_THREAD_LOCAL absl::profiling_internal::ExponentialBiased
g_exponential_biased_generator;
+void TriggerThreadSafeArenazConfigListener() {
+ auto* listener = g_arenaz_config_listener.load(std::memory_order_acquire);
+ if (listener != nullptr) listener();
+}
+
} // namespace
PROTOBUF_THREAD_LOCAL SamplingState global_sampling_state = {
- .next_sample = int64_t{1} << 10, .sample_stride = int64_t{1} << 10};
+ /*next_sample=*/0, /*sample_stride=*/0};
ThreadSafeArenaStats::ThreadSafeArenaStats() { PrepareForSampling(0); }
ThreadSafeArenaStats::~ThreadSafeArenaStats() = default;
-void ThreadSafeArenaStats::PrepareForSampling(int64_t stride) {
+void ThreadSafeArenaStats::BlockStats::PrepareForSampling() {
num_allocations.store(0, std::memory_order_relaxed);
- bytes_used.store(0, std::memory_order_relaxed);
bytes_allocated.store(0, std::memory_order_relaxed);
+ bytes_used.store(0, std::memory_order_relaxed);
bytes_wasted.store(0, std::memory_order_relaxed);
- max_bytes_allocated.store(0, std::memory_order_relaxed);
+}
+
+void ThreadSafeArenaStats::PrepareForSampling(int64_t stride) {
+ for (auto& blockstats : block_histogram) blockstats.PrepareForSampling();
+ max_block_size.store(0, std::memory_order_relaxed);
thread_ids.store(0, std::memory_order_relaxed);
weight = stride;
// The inliner makes hardcoded skip_count difficult (especially when combined
@@ -81,12 +93,44 @@
depth = absl::GetStackTrace(stack, kMaxStackDepth, /* skip_count= */ 0);
}
+size_t ThreadSafeArenaStats::FindBin(size_t bytes) {
+ if (bytes <= kMaxSizeForBinZero) return 0;
+ if (bytes <= kMaxSizeForPenultimateBin) {
+ // absl::bit_width() returns one plus the base-2 logarithm of x, with any
+ // fractional part discarded.
+ return absl::bit_width(absl::bit_ceil(bytes)) - kLogMaxSizeForBinZero - 1;
+ }
+ return kBlockHistogramBins - 1;
+}
+
+std::pair<size_t, size_t> ThreadSafeArenaStats::MinMaxBlockSizeForBin(
+ size_t bin) {
+ ABSL_ASSERT(bin < kBlockHistogramBins);
+ if (bin == 0) return {1, kMaxSizeForBinZero};
+ if (bin < kBlockHistogramBins - 1) {
+ return {(1 << (kLogMaxSizeForBinZero + bin - 1)) + 1,
+ 1 << (kLogMaxSizeForBinZero + bin)};
+ }
+ return {kMaxSizeForPenultimateBin + 1, std::numeric_limits<size_t>::max()};
+}
+
void RecordAllocateSlow(ThreadSafeArenaStats* info, size_t used,
size_t allocated, size_t wasted) {
- info->num_allocations.fetch_add(1, 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_wasted.fetch_add(wasted, std::memory_order_relaxed);
+ // Update the allocated bytes for the current block.
+ ThreadSafeArenaStats::BlockStats& curr =
+ info->block_histogram[ThreadSafeArenaStats::FindBin(allocated)];
+ curr.bytes_allocated.fetch_add(allocated, std::memory_order_relaxed);
+ curr.num_allocations.fetch_add(1, std::memory_order_relaxed);
+
+ // Update the used and wasted bytes for the previous block.
+ ThreadSafeArenaStats::BlockStats& prev =
+ info->block_histogram[ThreadSafeArenaStats::FindBin(used + wasted)];
+ prev.bytes_used.fetch_add(used, std::memory_order_relaxed);
+ prev.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);
info->thread_ids.fetch_or(tid, std::memory_order_relaxed);
}
@@ -115,11 +159,29 @@
return GlobalThreadSafeArenazSampler().Register(old_stride);
}
+void SetThreadSafeArenazConfigListener(ThreadSafeArenazConfigListener l) {
+ g_arenaz_config_listener.store(l, std::memory_order_release);
+}
+
+bool IsThreadSafeArenazEnabled() {
+ return g_arenaz_enabled.load(std::memory_order_acquire);
+}
+
void SetThreadSafeArenazEnabled(bool enabled) {
+ SetThreadSafeArenazEnabledInternal(enabled);
+ TriggerThreadSafeArenazConfigListener();
+}
+
+void SetThreadSafeArenazEnabledInternal(bool enabled) {
g_arenaz_enabled.store(enabled, std::memory_order_release);
}
void SetThreadSafeArenazSampleParameter(int32_t rate) {
+ SetThreadSafeArenazSampleParameterInternal(rate);
+ TriggerThreadSafeArenazConfigListener();
+}
+
+void SetThreadSafeArenazSampleParameterInternal(int32_t rate) {
if (rate > 0) {
g_arenaz_sample_parameter.store(rate, std::memory_order_release);
} else {
@@ -133,6 +195,11 @@
}
void SetThreadSafeArenazMaxSamples(int32_t max) {
+ SetThreadSafeArenazMaxSamplesInternal(max);
+ TriggerThreadSafeArenazConfigListener();
+}
+
+void SetThreadSafeArenazMaxSamplesInternal(int32_t max) {
if (max > 0) {
GlobalThreadSafeArenazSampler().SetMaxSamples(max);
} else {
@@ -141,6 +208,10 @@
}
}
+size_t ThreadSafeArenazMaxSamples() {
+ return GlobalThreadSafeArenazSampler().GetMaxSamples();
+}
+
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {
if (next_sample >= 0) {
global_sampling_state.next_sample = next_sample;
@@ -157,10 +228,16 @@
return nullptr;
}
+void SetThreadSafeArenazConfigListener(ThreadSafeArenazConfigListener) {}
void SetThreadSafeArenazEnabled(bool enabled) {}
+void SetThreadSafeArenazEnabledInternal(bool enabled) {}
+bool IsThreadSafeArenazEnabled() { return false; }
void SetThreadSafeArenazSampleParameter(int32_t rate) {}
+void SetThreadSafeArenazSampleParameterInternal(int32_t rate) {}
int32_t ThreadSafeArenazSampleParameter() { return 0; }
void SetThreadSafeArenazMaxSamples(int32_t max) {}
+void SetThreadSafeArenazMaxSamplesInternal(int32_t max) {}
+size_t ThreadSafeArenazMaxSamples() { return 0; }
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample) {}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
diff --git a/src/google/protobuf/arenaz_sampler.h b/src/google/protobuf/arenaz_sampler.h
index 4a04c6a..c608bcd 100644
--- a/src/google/protobuf/arenaz_sampler.h
+++ b/src/google/protobuf/arenaz_sampler.h
@@ -31,9 +31,11 @@
#ifndef GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
#define GOOGLE_PROTOBUF_SRC_GOOGLE_PROTOBUF_ARENAZ_SAMPLER_H__
+#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
+#include <utility>
// Must be included last.
@@ -65,12 +67,30 @@
// These fields are mutated by the various Record* APIs and need to be
// thread-safe.
- std::atomic<int> num_allocations;
- std::atomic<size_t> bytes_used;
- std::atomic<size_t> bytes_allocated;
- std::atomic<size_t> bytes_wasted;
- // Records the largest size an arena ever had.
- std::atomic<size_t> max_bytes_allocated;
+ struct BlockStats {
+ std::atomic<int> num_allocations;
+ std::atomic<size_t> bytes_allocated;
+ std::atomic<size_t> bytes_used;
+ std::atomic<size_t> bytes_wasted;
+
+ void PrepareForSampling();
+ };
+
+ // block_histogram is a kBlockHistogramBins sized histogram. The zeroth bin
+ // stores info about blocks of size \in [1, 1 << kLogMaxSizeForBinZero]. Bin
+ // i, where i > 0, stores info for blocks of size \in (max_size_bin (i-1),
+ // 1 << (kLogMaxSizeForBinZero + i)]. The final bin stores info about blocks
+ // of size \in [kMaxSizeForPenultimateBin + 1,
+ // std::numeric_limits<size_t>::max()].
+ static constexpr size_t kBlockHistogramBins = 15;
+ static constexpr size_t kLogMaxSizeForBinZero = 7;
+ static constexpr size_t kMaxSizeForBinZero = (1 << kLogMaxSizeForBinZero);
+ static constexpr size_t kMaxSizeForPenultimateBin =
+ 1 << (kLogMaxSizeForBinZero + kBlockHistogramBins - 2);
+ std::array<BlockStats, kBlockHistogramBins> block_histogram;
+
+ // Records the largest block allocated for the arena.
+ std::atomic<size_t> max_block_size;
// 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
// bit mixing for thread-ids; `% 64` would only grab the low bits and might
@@ -90,6 +110,13 @@
if (PROTOBUF_PREDICT_TRUE(info == nullptr)) return;
RecordAllocateSlow(info, used, allocated, wasted);
}
+
+ // Returns the bin for the provided size.
+ static size_t FindBin(size_t bytes);
+
+ // Returns the min and max bytes that can be stored in the histogram for
+ // blocks in the provided bin.
+ static std::pair<size_t, size_t> MinMaxBlockSizeForBin(size_t bin);
};
struct SamplingState {
@@ -199,17 +226,29 @@
// Returns a global Sampler.
ThreadSafeArenazSampler& GlobalThreadSafeArenazSampler();
+using ThreadSafeArenazConfigListener = void (*)();
+void SetThreadSafeArenazConfigListener(ThreadSafeArenazConfigListener l);
+
// Enables or disables sampling for thread safe arenas.
void SetThreadSafeArenazEnabled(bool enabled);
+void SetThreadSafeArenazEnabledInternal(bool enabled);
+
+// Returns true if sampling is on, false otherwise.
+bool IsThreadSafeArenazEnabled();
// Sets the rate at which thread safe arena will be sampled.
void SetThreadSafeArenazSampleParameter(int32_t rate);
+void SetThreadSafeArenazSampleParameterInternal(int32_t rate);
// Returns the rate at which thread safe arena will be sampled.
int32_t ThreadSafeArenazSampleParameter();
// Sets a soft max for the number of samples that will be kept.
void SetThreadSafeArenazMaxSamples(int32_t max);
+void SetThreadSafeArenazMaxSamplesInternal(int32_t max);
+
+// Returns the max number of samples that will be kept.
+size_t ThreadSafeArenazMaxSamples();
// Sets the current value for when arenas should be next sampled.
void SetThreadSafeArenazGlobalNextSample(int64_t next_sample);
diff --git a/src/google/protobuf/arenaz_sampler_test.cc b/src/google/protobuf/arenaz_sampler_test.cc
index 7bac100..67570e8 100644
--- a/src/google/protobuf/arenaz_sampler_test.cc
+++ b/src/google/protobuf/arenaz_sampler_test.cc
@@ -30,6 +30,8 @@
#include <google/protobuf/arenaz_sampler.h>
+#include <atomic>
+#include <limits>
#include <memory>
#include <random>
#include <utility>
@@ -37,7 +39,6 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include <google/protobuf/stubs/strutil.h>
// Must be included last.
@@ -57,10 +58,17 @@
return h->info_;
}
};
+
std::vector<size_t> GetBytesAllocated(ThreadSafeArenazSampler* s) {
std::vector<size_t> res;
s->Iterate([&](const ThreadSafeArenaStats& info) {
- res.push_back(info.bytes_allocated.load(std::memory_order_acquire));
+ for (const auto& block_stats : info.block_histogram) {
+ size_t bytes_allocated =
+ block_stats.bytes_allocated.load(std::memory_order_acquire);
+ if (bytes_allocated != 0) {
+ res.push_back(bytes_allocated);
+ }
+ }
});
return res;
}
@@ -69,7 +77,8 @@
int64_t stride) {
auto* info = s->Register(stride);
assert(info != nullptr);
- info->bytes_allocated.store(size);
+ info->block_histogram[0].bytes_allocated.store(size,
+ std::memory_order_relaxed);
return info;
}
@@ -85,46 +94,115 @@
MutexLock l(&info.init_mu);
info.PrepareForSampling(kTestStride);
- EXPECT_EQ(info.num_allocations.load(), 0);
- EXPECT_EQ(info.bytes_used.load(), 0);
- EXPECT_EQ(info.bytes_allocated.load(), 0);
- EXPECT_EQ(info.bytes_wasted.load(), 0);
- EXPECT_EQ(info.max_bytes_allocated.load(), 0);
+ for (const auto& block_stats : info.block_histogram) {
+ EXPECT_EQ(block_stats.num_allocations.load(std::memory_order_relaxed), 0);
+ EXPECT_EQ(block_stats.bytes_used.load(std::memory_order_relaxed), 0);
+ EXPECT_EQ(block_stats.bytes_allocated.load(std::memory_order_relaxed), 0);
+ EXPECT_EQ(block_stats.bytes_wasted.load(std::memory_order_relaxed), 0);
+ }
+ EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 0);
EXPECT_EQ(info.weight, kTestStride);
- info.num_allocations.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_wasted.store(1, std::memory_order_relaxed);
- info.max_bytes_allocated.store(1, std::memory_order_relaxed);
+ for (auto& block_stats : info.block_histogram) {
+ block_stats.num_allocations.store(1, std::memory_order_relaxed);
+ block_stats.bytes_used.store(1, std::memory_order_relaxed);
+ block_stats.bytes_allocated.store(1, std::memory_order_relaxed);
+ block_stats.bytes_wasted.store(1, std::memory_order_relaxed);
+ }
+ info.max_block_size.store(1, std::memory_order_relaxed);
info.PrepareForSampling(2 * kTestStride);
- EXPECT_EQ(info.num_allocations.load(), 0);
- EXPECT_EQ(info.bytes_used.load(), 0);
- EXPECT_EQ(info.bytes_allocated.load(), 0);
- EXPECT_EQ(info.bytes_wasted.load(), 0);
- EXPECT_EQ(info.max_bytes_allocated.load(), 0);
+ for (auto& block_stats : info.block_histogram) {
+ EXPECT_EQ(block_stats.num_allocations.load(std::memory_order_relaxed), 0);
+ EXPECT_EQ(block_stats.bytes_used.load(std::memory_order_relaxed), 0);
+ EXPECT_EQ(block_stats.bytes_allocated.load(std::memory_order_relaxed), 0);
+ EXPECT_EQ(block_stats.bytes_wasted.load(std::memory_order_relaxed), 0);
+ }
+ EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 0);
EXPECT_EQ(info.weight, 2 * kTestStride);
}
+TEST(ThreadSafeArenaStatsTest, FindBin) {
+ size_t current_bin = 0;
+ size_t bytes = 1;
+ while (current_bin < ThreadSafeArenaStats::kBlockHistogramBins - 1) {
+ size_t next_bin = ThreadSafeArenaStats::FindBin(bytes);
+ if (next_bin != current_bin) {
+ // Test the bins increase linearly.
+ EXPECT_EQ(next_bin, current_bin + 1);
+ // Test the bins change only at values of the form 2^k + 1.
+ EXPECT_EQ(absl::popcount(bytes - 1), 1);
+ current_bin = next_bin;
+ }
+ ++bytes;
+ }
+}
+
+TEST(ThreadSafeArenaStatsTest, MinMaxBlockSizeForBin) {
+ std::pair<size_t, size_t> current_limits =
+ ThreadSafeArenaStats::MinMaxBlockSizeForBin(0);
+ EXPECT_EQ(current_limits.first, 1);
+ EXPECT_LT(current_limits.first, current_limits.second);
+ for (size_t i = 1; i < ThreadSafeArenaStats::kBlockHistogramBins; ++i) {
+ std::pair<size_t, size_t> next_limits =
+ ThreadSafeArenaStats::MinMaxBlockSizeForBin(i);
+ EXPECT_LT(next_limits.first, next_limits.second);
+ // Test the limits do not have gaps.
+ EXPECT_EQ(next_limits.first, current_limits.second + 1);
+ if (i != ThreadSafeArenaStats::kBlockHistogramBins - 1) {
+ EXPECT_EQ(next_limits.second, 2 * current_limits.second);
+ }
+ current_limits = next_limits;
+ }
+ // Test the limits cover the entire range possible.
+ EXPECT_EQ(current_limits.second, std::numeric_limits<size_t>::max());
+}
+
TEST(ThreadSafeArenaStatsTest, RecordAllocateSlow) {
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.num_allocations.load(), 1);
- EXPECT_EQ(info.bytes_used.load(), 100);
- EXPECT_EQ(info.bytes_allocated.load(), 128);
- EXPECT_EQ(info.bytes_wasted.load(), 0);
- EXPECT_EQ(info.max_bytes_allocated.load(), 0);
+ RecordAllocateSlow(&info, /*requested=*/0, /*allocated=*/128, /*wasted=*/0);
+ EXPECT_EQ(
+ info.block_histogram[0].num_allocations.load(std::memory_order_relaxed),
+ 1);
+ EXPECT_EQ(info.block_histogram[0].bytes_used.load(std::memory_order_relaxed),
+ 0);
+ EXPECT_EQ(
+ info.block_histogram[0].bytes_allocated.load(std::memory_order_relaxed),
+ 128);
+ EXPECT_EQ(
+ info.block_histogram[0].bytes_wasted.load(std::memory_order_relaxed), 0);
+ EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 128);
RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256,
/*wasted=*/28);
- EXPECT_EQ(info.num_allocations.load(), 2);
- EXPECT_EQ(info.bytes_used.load(), 200);
- EXPECT_EQ(info.bytes_allocated.load(), 384);
- EXPECT_EQ(info.bytes_wasted.load(), 28);
- EXPECT_EQ(info.max_bytes_allocated.load(), 0);
+ EXPECT_EQ(info.block_histogram[0].bytes_used.load(std::memory_order_relaxed),
+ 100);
+ EXPECT_EQ(
+ info.block_histogram[0].bytes_wasted.load(std::memory_order_relaxed), 28);
+ EXPECT_EQ(
+ info.block_histogram[1].num_allocations.load(std::memory_order_relaxed),
+ 1);
+ EXPECT_EQ(
+ info.block_histogram[1].bytes_allocated.load(std::memory_order_relaxed),
+ 256);
+ EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 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(std::memory_order_relaxed), 128);
+ RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/256,
+ /*wasted=*/28);
+ EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256);
+ RecordAllocateSlow(&info, /*requested=*/100, /*allocated=*/128,
+ /*wasted=*/28);
+ EXPECT_EQ(info.max_block_size.load(std::memory_order_relaxed), 256);
}
TEST(ThreadSafeArenazSamplerTest, SamplingCorrectness) {
@@ -197,12 +275,15 @@
constexpr int64_t kTestStride = 17;
ThreadSafeArenaStatsHandle h(sampler.Register(kTestStride));
auto* info = ThreadSafeArenaStatsHandlePeer::GetInfo(&h);
- info->bytes_allocated.store(0x12345678, std::memory_order_relaxed);
+ info->block_histogram[0].bytes_allocated.store(0x12345678,
+ std::memory_order_relaxed);
bool found = false;
sampler.Iterate([&](const ThreadSafeArenaStats& h) {
if (&h == info) {
- EXPECT_EQ(h.bytes_allocated.load(), 0x12345678);
+ EXPECT_EQ(
+ h.block_histogram[0].bytes_allocated.load(std::memory_order_relaxed),
+ 0x12345678);
EXPECT_EQ(h.weight, kTestStride);
found = true;
}
@@ -215,7 +296,8 @@
if (&h == info) {
// this will only happen if some other thread has resurrected the info
// the old handle was using.
- if (h.bytes_allocated.load() == 0x12345678) {
+ if (h.block_histogram[0].bytes_allocated.load(
+ std::memory_order_relaxed) == 0x12345678) {
found = true;
}
}
@@ -231,7 +313,7 @@
auto* info2 = Register(&sampler, 2, kTestStride);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 2));
- info1->bytes_allocated.store(3);
+ info1->block_histogram[0].bytes_allocated.store(3, std::memory_order_relaxed);
EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(3, 2));
sampler.Unregister(info1);
@@ -243,18 +325,18 @@
std::vector<ThreadSafeArenaStats*> infos;
constexpr int64_t kTestStride = 200;
for (size_t i = 0; i < 3; ++i) {
- infos.push_back(Register(&sampler, i, kTestStride));
+ infos.push_back(Register(&sampler, i + 1, kTestStride));
}
- EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 1, 2));
+ EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 2, 3));
sampler.Unregister(infos[1]);
- EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2));
+ EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3));
infos.push_back(Register(&sampler, 3, kTestStride));
infos.push_back(Register(&sampler, 4, kTestStride));
- EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2, 3, 4));
+ EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3, 3, 4));
sampler.Unregister(infos[3]);
- EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(0, 2, 4));
+ EXPECT_THAT(GetBytesAllocated(&sampler), UnorderedElementsAre(1, 3, 4));
sampler.Unregister(infos[0]);
sampler.Unregister(infos[2]);
@@ -360,6 +442,7 @@
SetThreadSafeArenazEnabled(true);
// Setting 1 as the parameter value means one in every two arenas would be
// sampled, on average.
+ int32_t oldparam = ThreadSafeArenazSampleParameter();
SetThreadSafeArenazSampleParameter(1);
SetThreadSafeArenazGlobalNextSample(0);
auto& sampler = GlobalThreadSafeArenazSampler();
@@ -387,6 +470,95 @@
}
}
EXPECT_GT(count, 0);
+ SetThreadSafeArenazSampleParameter(oldparam);
+}
+
+class SampleFirstArenaThread : public Thread {
+ protected:
+ void Run() override {
+ google::protobuf::Arena arena;
+ google::protobuf::ArenaSafeUniquePtr<
+ protobuf_test_messages::proto2::TestAllTypesProto2>
+ message = google::protobuf::MakeArenaSafeUnique<
+ protobuf_test_messages::proto2::TestAllTypesProto2>(&arena);
+ GOOGLE_CHECK(message != nullptr);
+ arena_created_.Notify();
+ samples_counted_.WaitForNotification();
+ }
+
+ public:
+ explicit SampleFirstArenaThread(const thread::Options& options)
+ : Thread(options, "SampleFirstArenaThread") {}
+
+ absl::Notification arena_created_;
+ absl::Notification samples_counted_;
+};
+
+// Test that the first arena created on a thread may and may not be chosen for
+// sampling.
+TEST(ThreadSafeArenazSamplerTest, SampleFirstArena) {
+ SetThreadSafeArenazEnabled(true);
+ auto& sampler = GlobalThreadSafeArenazSampler();
+
+ enum class SampleResult {
+ kSampled,
+ kUnsampled,
+ kSpoiled,
+ };
+
+ auto count_samples = [&]() {
+ int count = 0;
+ sampler.Iterate([&](const ThreadSafeArenaStats& h) { ++count; });
+ return count;
+ };
+
+ auto run_sample_experiment = [&]() {
+ int before = count_samples();
+ thread::Options options;
+ options.set_joinable(true);
+ SampleFirstArenaThread t(options);
+ t.Start();
+ t.arena_created_.WaitForNotification();
+ int during = count_samples();
+ t.samples_counted_.Notify();
+ t.Join();
+ int after = count_samples();
+
+ // If we didn't get back where we were, some other thread may have
+ // created an arena and produced an invalid experiment run.
+ if (before != after) return SampleResult::kSpoiled;
+
+ switch (during - before) {
+ case 1:
+ return SampleResult::kSampled;
+ case 0:
+ return SampleResult::kUnsampled;
+ default:
+ return SampleResult::kSpoiled;
+ }
+ };
+
+ constexpr int kTrials = 10000;
+ bool sampled = false;
+ bool unsampled = false;
+ for (int i = 0; i < kTrials; ++i) {
+ switch (run_sample_experiment()) {
+ case SampleResult::kSampled:
+ sampled = true;
+ break;
+ case SampleResult::kUnsampled:
+ unsampled = true;
+ break;
+ default:
+ break;
+ }
+
+ // This is the success criteria for the entire test. At some point
+ // we sampled the first arena and at some point we did not.
+ if (sampled && unsampled) return;
+ }
+ EXPECT_TRUE(sampled);
+ EXPECT_TRUE(unsampled);
}
#endif // defined(PROTOBUF_ARENAZ_SAMPLE)
diff --git a/src/google/protobuf/compiler/BUILD.bazel b/src/google/protobuf/compiler/BUILD.bazel
index 419e8f5..6e5f8da 100644
--- a/src/google/protobuf/compiler/BUILD.bazel
+++ b/src/google/protobuf/compiler/BUILD.bazel
@@ -195,7 +195,8 @@
"-Wno-deprecated-declarations",
],
}) + [
- "-DGOOGLE_PROTOBUF_TEST_PLUGIN_PATH=\\\"src/google/protobuf/compiler/test_plugin\\\"",
+ # Note: This only works on Windows with symlinks and runfiles enabled.
+ "-DGOOGLE_PROTOBUF_TEST_PLUGIN_PATH=\\\"$(rootpath :test_plugin)\\\"",
],
data = [
":test_plugin",
diff --git a/src/google/protobuf/compiler/command_line_interface.cc b/src/google/protobuf/compiler/command_line_interface.cc
index 0a0c7e3..f5cc9a9 100644
--- a/src/google/protobuf/compiler/command_line_interface.cc
+++ b/src/google/protobuf/compiler/command_line_interface.cc
@@ -339,9 +339,12 @@
void AddErrorOrWarning(const std::string& filename, int line, int column,
const std::string& message, const std::string& type,
std::ostream& out) {
- // Print full path when running under MSVS
std::string dfile;
- if (format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
+ if (
+#ifndef PROTOBUF_OPENSOURCE
+ // Print full path when running under MSVS
+ format_ == CommandLineInterface::ERROR_FORMAT_MSVS &&
+#endif // !PROTOBUF_OPENSOURCE
tree_ != nullptr && tree_->VirtualFileToDiskFile(filename, &dfile)) {
out << dfile;
} else {
@@ -398,7 +401,6 @@
// Get name of all output files.
void GetOutputFilenames(std::vector<std::string>* output_filenames);
-
// implements GeneratorContext --------------------------------------
io::ZeroCopyOutputStream* Open(const std::string& filename) override;
io::ZeroCopyOutputStream* OpenForAppend(const std::string& filename) override;
@@ -963,6 +965,7 @@
int CommandLineInterface::Run(int argc, const char* const argv[]) {
Clear();
+
switch (ParseArguments(argc, argv)) {
case PARSE_ARGUMENT_DONE_AND_EXIT:
return 0;
@@ -1076,7 +1079,6 @@
}
}
- // Write all output to disk.
for (const auto& pair : output_directories) {
const std::string& location = pair.first;
GeneratorContextImpl* directory = pair.second.get();
@@ -1151,7 +1153,6 @@
// Do not add a default case.
}
}
-
return 0;
}
@@ -2123,7 +2124,8 @@
<< codegen_name
<< " hasn't been updated to support optional fields in "
"proto3. Please ask the owner of this code generator to "
- "support proto3 optional.";
+ "support proto3 optional."
+ << std::endl;
return false;
}
}
diff --git a/src/google/protobuf/compiler/command_line_interface_unittest.cc b/src/google/protobuf/compiler/command_line_interface_unittest.cc
index 7bde1b4..eb969f5 100644
--- a/src/google/protobuf/compiler/command_line_interface_unittest.cc
+++ b/src/google/protobuf/compiler/command_line_interface_unittest.cc
@@ -357,9 +357,9 @@
}
#endif
- if (plugin_path.empty()) {
+ if (plugin_path.empty() || !FileExists(plugin_path)) {
GOOGLE_LOG(ERROR)
- << "Plugin executable not found. Plugin tests are likely to fail.";
+ << "Plugin tests are likely to fail. Plugin executable not found at: " << plugin_path;
} else {
args.push_back("--plugin=prefix-gen-plug=" + plugin_path);
}
diff --git a/src/google/protobuf/compiler/cpp/field.cc b/src/google/protobuf/compiler/cpp/field.cc
index cf4f14e..90d2084 100644
--- a/src/google/protobuf/compiler/cpp/field.cc
+++ b/src/google/protobuf/compiler/cpp/field.cc
@@ -330,7 +330,6 @@
}
}
-
void SetCommonOneofFieldVariables(
const FieldDescriptor* descriptor,
std::map<std::string, std::string>* variables) {
diff --git a/src/google/protobuf/compiler/cpp/field.h b/src/google/protobuf/compiler/cpp/field.h
index 3903e79..3fcbda3 100644
--- a/src/google/protobuf/compiler/cpp/field.h
+++ b/src/google/protobuf/compiler/cpp/field.h
@@ -208,7 +208,6 @@
virtual bool IsInlined() const { return false; }
-
virtual ArenaDtorNeeds NeedsArenaDestructor() const {
return ArenaDtorNeeds::kNone;
}
diff --git a/src/google/protobuf/compiler/cpp/file.cc b/src/google/protobuf/compiler/cpp/file.cc
index 838e0ab..502d8c0 100644
--- a/src/google/protobuf/compiler/cpp/file.cc
+++ b/src/google/protobuf/compiler/cpp/file.cc
@@ -495,12 +495,10 @@
generator->GenerateInitDefaultSplitInstance(printer);
format(
"} {}\n"
- " ~$1$() {}\n"
" union {\n"
- " $2$ _instance;\n"
+ " $1$ _instance;\n"
" };\n"
"};\n",
- DefaultInstanceType(generator->descriptor_, options_, /*split=*/true),
StrCat(generator->classname_, "::Impl_::Split"));
// NO_DESTROY is not necessary for correctness. The empty destructor is
// enough. However, the empty destructor fails to be elided in some
@@ -508,7 +506,7 @@
// there just to improve performance and binary size in these builds.
format(
"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),
DefaultInstanceName(generator->descriptor_, options_, /*split=*/true));
}
@@ -999,7 +997,7 @@
const Descriptor* class_desc = p.second;
format(
"struct $1$;\n"
- "$dllexport_decl $extern $1$ $2$;\n",
+ "$dllexport_decl $extern const $1$ $2$;\n",
DefaultInstanceType(class_desc, options, /*split=*/true),
DefaultInstanceName(class_desc, options, /*split=*/true));
}
diff --git a/src/google/protobuf/compiler/cpp/helpers.cc b/src/google/protobuf/compiler/cpp/helpers.cc
index 4939aa5..d182c86 100644
--- a/src/google/protobuf/compiler/cpp/helpers.cc
+++ b/src/google/protobuf/compiler/cpp/helpers.cc
@@ -176,7 +176,6 @@
#endif // !PROTOBUF_FUTURE_BREAKING_CHANGES
};
-
static std::unordered_set<std::string>* MakeKeywordsMap() {
auto* result = new std::unordered_set<std::string>();
for (const auto keyword : kKeywordList) {
@@ -525,7 +524,6 @@
return result;
}
-
std::string FieldMemberName(const FieldDescriptor* field, bool split) {
StringPiece prefix =
IsMapEntryMessage(field->containing_type()) ? "" : "_impl_.";
@@ -876,8 +874,6 @@
bool IsProfileDriven(const Options& options) {
return options.access_info_map != nullptr;
}
-
-
bool IsStringInlined(const FieldDescriptor* descriptor,
const Options& options) {
(void)descriptor;
diff --git a/src/google/protobuf/compiler/cpp/message.cc b/src/google/protobuf/compiler/cpp/message.cc
index 3ca5315..50b86f7 100644
--- a/src/google/protobuf/compiler/cpp/message.cc
+++ b/src/google/protobuf/compiler/cpp/message.cc
@@ -828,7 +828,6 @@
// Generate type-specific accessor declarations.
field_generators_.get(field).GenerateAccessorDeclarations(printer);
-
format("\n");
}
@@ -1238,41 +1237,41 @@
Formatter::SaveState saver(&format);
format.AddMap(vars);
- // Generate has_$name$() or $name$_size().
- if (field->is_repeated()) {
- if (IsFieldStripped(field, options_)) {
- format(
- "inline int $classname$::$name$_size() const { "
- "__builtin_trap(); }\n");
- } else {
- 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);
+
+ // Generate has_$name$() or $name$_size().
+ if (field->is_repeated()) {
+ if (IsFieldStripped(field, options_)) {
+ format(
+ "inline int $classname$::$name$_size() const { "
+ "__builtin_trap(); }\n");
} else {
- // Singular field.
- GenerateSingularFieldHasBits(field, format);
+ 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)) {
GenerateFieldClear(field, true, format);
}
-
// Generate type-specific accessors.
if (!IsFieldStripped(field, options_)) {
field_generators_.get(field).GenerateInlineAccessorDefinitions(printer);
@@ -1760,7 +1759,7 @@
format(
"private:\n"
"inline bool IsSplitMessageDefault() const {\n"
- " return $split$ == reinterpret_cast<Impl_::Split*>(&$1$);\n"
+ " return $split$ == reinterpret_cast<const Impl_::Split*>(&$1$);\n"
"}\n"
"PROTOBUF_NOINLINE void PrepareSplitMessageForWrite();\n"
"public:\n",
@@ -1928,6 +1927,8 @@
" typedef void InternalArenaConstructable_;\n"
" typedef void DestructorSkippable_;\n"
"};\n"
+ "static_assert(std::is_trivially_copy_constructible<Split>::value);\n"
+ "static_assert(std::is_trivially_destructible<Split>::value);\n"
"Split* _split_;\n");
}
@@ -2204,11 +2205,12 @@
" if (IsSplitMessageDefault()) {\n"
" void* chunk = "
"::PROTOBUF_NAMESPACE_ID::internal::CreateSplitMessageGeneric("
- "GetArenaForAllocation(), &$1$, sizeof(Impl_::Split));\n"
+ "GetArenaForAllocation(), &$1$, sizeof(Impl_::Split), this, &$2$);\n"
" $split$ = reinterpret_cast<Impl_::Split*>(chunk);\n"
" }\n"
"}\n",
- DefaultInstanceName(descriptor_, options_, /*split=*/true));
+ DefaultInstanceName(descriptor_, options_, /*split=*/true),
+ DefaultInstanceName(descriptor_, options_, /*split=*/false));
}
GenerateVerify(printer);
@@ -2421,8 +2423,15 @@
}
if (ShouldSplit(descriptor_, options_)) {
put_sep();
- format("decltype($split$){reinterpret_cast<Impl_::Split*>(&$1$)}",
- DefaultInstanceName(descriptor_, options_, /*split=*/true));
+ // We can't assign the default split to this->split without the const_cast
+ // 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_)) {
put_sep();
@@ -2681,7 +2690,7 @@
}
if (ShouldSplit(descriptor_, options_)) {
put_sep();
- format("/*decltype($split$)*/&$1$._instance",
+ format("/*decltype($split$)*/const_cast<Impl_::Split*>(&$1$._instance)",
DefaultInstanceName(descriptor_, options_, /*split=*/true));
}
@@ -2866,8 +2875,10 @@
}
if (ShouldSplit(descriptor_, options_)) {
put_sep();
- format("decltype($split$){reinterpret_cast<Impl_::Split*>(&$1$)}",
- DefaultInstanceName(descriptor_, options_, /*split=*/true));
+ format(
+ "decltype($split$){const_cast<Impl_::Split*>"
+ "(reinterpret_cast<const Impl_::Split*>(&$1$))}",
+ DefaultInstanceName(descriptor_, options_, /*split=*/true));
}
for (auto oneof : OneOfRange(descriptor_)) {
put_sep();
diff --git a/src/google/protobuf/compiler/cpp/options.h b/src/google/protobuf/compiler/cpp/options.h
index 88d4b30..7aab669 100644
--- a/src/google/protobuf/compiler/cpp/options.h
+++ b/src/google/protobuf/compiler/cpp/options.h
@@ -85,7 +85,7 @@
bool profile_driven_inline_string = true;
bool message_owned_arena_trial = false;
bool force_split = false;
- bool profile_driven_split = false;
+ bool profile_driven_split = true;
#ifdef PROTOBUF_STABLE_EXPERIMENTS
bool force_eagerly_verified_lazy = true;
bool force_inline_string = true;
diff --git a/src/google/protobuf/compiler/cpp/parse_function_generator.cc b/src/google/protobuf/compiler/cpp/parse_function_generator.cc
index 8045f48..30b0fd4 100644
--- a/src/google/protobuf/compiler/cpp/parse_function_generator.cc
+++ b/src/google/protobuf/compiler/cpp/parse_function_generator.cc
@@ -83,7 +83,8 @@
return 2;
}
-void PopulateFastFieldEntry(const TailCallTableInfo::FieldEntryInfo& entry,
+void PopulateFastFieldEntry(const Descriptor* descriptor,
+ const TailCallTableInfo::FieldEntryInfo& entry,
const Options& options,
TailCallTableInfo::FastFieldInfo& info);
@@ -158,6 +159,7 @@
}
std::vector<TailCallTableInfo::FastFieldInfo> SplitFastFieldsForSize(
+ const Descriptor* descriptor,
const std::vector<TailCallTableInfo::FieldEntryInfo>& field_entries,
int table_size_log2, const Options& options,
MessageSCCAnalyzer* scc_analyzer) {
@@ -200,7 +202,7 @@
GOOGLE_CHECK(info.func_name.empty()) << info.func_name;
info.field = field;
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
// bit (tailcall parsing uses a uint64_t for hasbits, but only stores 32).
info.hasbit_idx = HasHasbit(field) ? entry.hasbit_idx : 63;
@@ -412,8 +414,8 @@
int num_fast_fields = -1;
for (int try_size_log2 : {0, 1, 2, 3, 4, 5}) {
size_t try_size = 1 << try_size_log2;
- auto split_fields = SplitFastFieldsForSize(field_entries, try_size_log2,
- options, scc_analyzer);
+ auto split_fields = SplitFastFieldsForSize(
+ descriptor, field_entries, try_size_log2, options, scc_analyzer);
GOOGLE_CHECK_EQ(split_fields.size(), try_size);
int try_num_fast_fields = 0;
for (const auto& info : split_fields) {
@@ -1667,11 +1669,12 @@
namespace {
-void PopulateFastFieldEntry(const TailCallTableInfo::FieldEntryInfo& entry,
+void PopulateFastFieldEntry(const Descriptor* descriptor,
+ const TailCallTableInfo::FieldEntryInfo& entry,
const Options& options,
TailCallTableInfo::FastFieldInfo& info) {
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);
switch (field->type()) {
@@ -1784,7 +1787,36 @@
// Append the tag length. Fast parsing only handles 1- or 2-byte tags.
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;
}
diff --git a/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc b/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc
index e21eff1..56b920d 100644
--- a/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_generator_unittest.cc
@@ -74,6 +74,22 @@
EXPECT_FALSE(IsDescriptorOptionMessage(DescriptorProto::descriptor()));
}
+TEST(CSharpIdentifiers, UnderscoresToCamelCase) {
+ EXPECT_EQ("FooBar", UnderscoresToCamelCase("Foo_Bar", true));
+ EXPECT_EQ("fooBar", UnderscoresToCamelCase("FooBar", false));
+ EXPECT_EQ("foo123", UnderscoresToCamelCase("foo_123", false));
+ // remove leading underscores
+ EXPECT_EQ("Foo123", UnderscoresToCamelCase("_Foo_123", true));
+ // this one has slight unexpected output as it capitalises the first
+ // letter after consuming the underscores, but this was the existing
+ // behaviour so I have not changed it
+ EXPECT_EQ("FooBar", UnderscoresToCamelCase("___fooBar", false));
+ // leave a leading underscore for identifiers that would otherwise
+ // be invalid because they would start with a digit
+ EXPECT_EQ("_123Foo", UnderscoresToCamelCase("_123_foo", true));
+ EXPECT_EQ("_123Foo", UnderscoresToCamelCase("___123_foo", true));
+}
+
} // namespace
} // namespace csharp
} // namespace compiler
diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.cc b/src/google/protobuf/compiler/csharp/csharp_helpers.cc
index 42d9527..1b58b95 100644
--- a/src/google/protobuf/compiler/csharp/csharp_helpers.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_helpers.cc
@@ -144,6 +144,7 @@
bool cap_next_letter,
bool preserve_period) {
std::string result;
+
// Note: I distrust ctype.h due to locales.
for (int i = 0; i < input.size(); i++) {
if ('a' <= input[i] && input[i] <= 'z') {
@@ -177,6 +178,23 @@
if (input.size() > 0 && input[input.size() - 1] == '#') {
result += '_';
}
+
+ // https://github.com/protocolbuffers/protobuf/issues/8101
+ // To avoid generating invalid identifiers - if the input string
+ // starts with _<digit> (or multiple underscores then digit) then
+ // we need to preserve the underscore as an identifier cannot start
+ // with a digit.
+ // This check is being done after the loop rather than before
+ // to handle the case where there are multiple underscores before the
+ // first digit. We let them all be consumed so we can see if we would
+ // start with a digit.
+ // Note: not preserving leading underscores for all otherwise valid identifiers
+ // so as to not break anything that relies on the existing behaviour
+ if (result.size() > 0 && ('0' <= result[0] && result[0] <= '9')
+ && input.size() > 0 && input[0] == '_')
+ {
+ result.insert(0, 1, '_');
+ }
return result;
}
@@ -379,15 +397,30 @@
}
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
std::string property_name = UnderscoresToPascalCase(GetFieldName(descriptor));
- // Avoid either our own type name or reserved names. Note that not all names
- // are reserved - a field called to_string, write_to etc would still cause a problem.
+ // Avoid either our own type name or reserved names.
// 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()
- || property_name == "Types"
- || property_name == "Descriptor") {
+ || reserved_member_names.find(property_name) != reserved_member_names.end()) {
property_name += "_";
}
return property_name;
diff --git a/src/google/protobuf/compiler/csharp/csharp_helpers.h b/src/google/protobuf/compiler/csharp/csharp_helpers.h
index 836bd5d..1d07359 100644
--- a/src/google/protobuf/compiler/csharp/csharp_helpers.h
+++ b/src/google/protobuf/compiler/csharp/csharp_helpers.h
@@ -91,7 +91,9 @@
int GetFixedSize(FieldDescriptor::Type type);
-std::string UnderscoresToCamelCase(const std::string& input,
+// Note that we wouldn't normally want to export this (we're not expecting
+// it to be used outside libprotoc itself) but this exposes it for testing.
+std::string PROTOC_EXPORT UnderscoresToCamelCase(const std::string& input,
bool cap_next_letter,
bool preserve_period);
diff --git a/src/google/protobuf/compiler/csharp/csharp_map_field.cc b/src/google/protobuf/compiler/csharp/csharp_map_field.cc
index a13b995..9efd3d5 100644
--- a/src/google/protobuf/compiler/csharp/csharp_map_field.cc
+++ b/src/google/protobuf/compiler/csharp/csharp_map_field.cc
@@ -90,7 +90,7 @@
void MapFieldGenerator::GenerateMergingCode(io::Printer* printer) {
printer->Print(
variables_,
- "$name$_.Add(other.$name$_);\n");
+ "$name$_.MergeFrom(other.$name$_);\n");
}
void MapFieldGenerator::GenerateParsingCode(io::Printer* printer) {
diff --git a/src/google/protobuf/compiler/java/enum_field.cc b/src/google/protobuf/compiler/java/enum_field.cc
index b82d147..aa3a51f 100644
--- a/src/google/protobuf/compiler/java/enum_field.cc
+++ b/src/google/protobuf/compiler/java/enum_field.cc
@@ -281,6 +281,18 @@
" $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\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,
/* builder */ false, /* kdoc */ true);
printer->Print(variables_,
diff --git a/src/google/protobuf/compiler/java/enum_field_lite.cc b/src/google/protobuf/compiler/java/enum_field_lite.cc
index caa215b..a256824 100644
--- a/src/google/protobuf/compiler/java/enum_field_lite.cc
+++ b/src/google/protobuf/compiler/java/enum_field_lite.cc
@@ -296,6 +296,18 @@
" $kt_dsl_builder$.${$set$capitalized_name$$}$(value)\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,
/* builder */ false, /* kdoc */ true);
printer->Print(variables_,
diff --git a/src/google/protobuf/compiler/java/file.cc b/src/google/protobuf/compiler/java/file.cc
index bd3647d..c466518 100644
--- a/src/google/protobuf/compiler/java/file.cc
+++ b/src/google/protobuf/compiler/java/file.cc
@@ -704,10 +704,13 @@
options_.annotate_code ? &annotation_collector : nullptr);
printer.Print(
- "//Generated by the protocol buffer compiler. DO NOT EDIT!\n"
+ "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
"// source: $filename$\n"
"\n",
"filename", descriptor->file()->name());
+ printer.Print(
+ "// Generated files should ignore deprecation warnings\n"
+ "@file:Suppress(\"DEPRECATION\")");
if (!java_package_.empty()) {
printer.Print(
"package $package$;\n"
diff --git a/src/google/protobuf/compiler/java/generator.h b/src/google/protobuf/compiler/java/generator.h
index bbc7170..39d2445 100644
--- a/src/google/protobuf/compiler/java/generator.h
+++ b/src/google/protobuf/compiler/java/generator.h
@@ -63,7 +63,12 @@
uint64_t GetSupportedFeatures() const override;
+ void set_opensource_runtime(bool opensource) {
+ opensource_runtime_ = opensource;
+ }
+
private:
+ bool opensource_runtime_ = true;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(JavaGenerator);
};
diff --git a/src/google/protobuf/compiler/java/message_serialization.h b/src/google/protobuf/compiler/java/message_serialization.h
index 6145392..15dc515 100644
--- a/src/google/protobuf/compiler/java/message_serialization.h
+++ b/src/google/protobuf/compiler/java/message_serialization.h
@@ -32,6 +32,7 @@
#define GOOGLE_PROTOBUF_COMPILER_JAVA_MESSAGE_SERIALIZATION_H__
#include <algorithm>
+#include <cstddef>
#include <vector>
#include <google/protobuf/io/printer.h>
@@ -66,20 +67,31 @@
std::sort(sorted_extensions.begin(), sorted_extensions.end(),
ExtensionRangeOrdering());
+ std::size_t range_idx = 0;
+
// Merge the fields and the extension ranges, both sorted by field number.
- for (int i = 0, j = 0;
- i < descriptor->field_count() || j < sorted_extensions.size();) {
- if (i == descriptor->field_count()) {
- GenerateSerializeExtensionRange(printer, sorted_extensions[j++]);
- } else if (j == sorted_extensions.size()) {
- field_generators.get(sorted_fields[i++])
- .GenerateSerializationCode(printer);
- } else if (sorted_fields[i]->number() < sorted_extensions[j]->start) {
- field_generators.get(sorted_fields[i++])
- .GenerateSerializationCode(printer);
- } else {
- GenerateSerializeExtensionRange(printer, sorted_extensions[j++]);
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ const FieldDescriptor* field = sorted_fields[i];
+
+ // Collapse all extension ranges up until the next field. This leads to
+ // shorter and more efficient codegen for messages containing a large
+ // number of extension ranges without fields in between them.
+ const Descriptor::ExtensionRange* range = nullptr;
+ while (range_idx < sorted_extensions.size() &&
+ sorted_extensions[range_idx]->end <= field->number()) {
+ range = sorted_extensions[range_idx++];
}
+
+ 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());
}
}
diff --git a/src/google/protobuf/compiler/java/message_serialization_unittest.cc b/src/google/protobuf/compiler/java/message_serialization_unittest.cc
new file mode 100644
index 0000000..b2e1fca
--- /dev/null
+++ b/src/google/protobuf/compiler/java/message_serialization_unittest.cc
@@ -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
diff --git a/src/google/protobuf/compiler/java/message_serialization_unittest.proto b/src/google/protobuf/compiler/java/message_serialization_unittest.proto
new file mode 100644
index 0000000..9cfdf42
--- /dev/null
+++ b/src/google/protobuf/compiler/java/message_serialization_unittest.proto
@@ -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;
+}
diff --git a/src/google/protobuf/compiler/main.cc b/src/google/protobuf/compiler/main.cc
index 3960946..15b6591 100644
--- a/src/google/protobuf/compiler/main.cc
+++ b/src/google/protobuf/compiler/main.cc
@@ -66,6 +66,10 @@
cli.RegisterGenerator("--java_out", "--java_opt", &java_generator,
"Generate Java source file.");
+#ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE
+ java_generator.set_opensource_runtime(true);
+#endif
+
// Proto2 Kotlin
java::KotlinGenerator kt_generator;
cli.RegisterGenerator("--kotlin_out", "--kotlin_opt", &kt_generator,
@@ -76,6 +80,11 @@
python::Generator py_generator;
cli.RegisterGenerator("--python_out", "--python_opt", &py_generator,
"Generate Python source file.");
+
+#ifdef GOOGLE_PROTOBUF_RUNTIME_INCLUDE_BASE
+ py_generator.set_opensource_runtime(true);
+#endif
+
// Python pyi
python::PyiGenerator pyi_generator;
cli.RegisterGenerator("--pyi_out", &pyi_generator,
diff --git a/src/google/protobuf/compiler/php/php_generator.cc b/src/google/protobuf/compiler/php/php_generator.cc
index 7c9a3e3..b238703 100644
--- a/src/google/protobuf/compiler/php/php_generator.cc
+++ b/src/google/protobuf/compiler/php/php_generator.cc
@@ -48,29 +48,29 @@
const std::string kDescriptorDirName = "Google/Protobuf/Internal";
const std::string kDescriptorPackageName = "Google\\Protobuf\\Internal";
const char* const kReservedNames[] = {
- "abstract", "and", "array", "as", "break",
- "callable", "case", "catch", "class", "clone",
- "const", "continue", "declare", "default", "die",
- "do", "echo", "else", "elseif", "empty",
- "enddeclare", "endfor", "endforeach", "endif", "endswitch",
- "endwhile", "eval", "exit", "extends", "final",
- "finally", "fn", "for", "foreach", "function",
- "global", "goto", "if", "implements", "include",
- "include_once", "instanceof", "insteadof", "interface", "isset",
- "list", "match", "namespace", "new", "or",
- "parent", "print", "private", "protected", "public",
- "require", "require_once", "return", "self", "static",
- "switch", "throw", "trait", "try", "unset",
- "use", "var", "while", "xor", "yield",
- "int", "float", "bool", "string", "true",
- "false", "null", "void", "iterable"};
+ "abstract", "and", "array", "as", "break",
+ "callable", "case", "catch", "class", "clone",
+ "const", "continue", "declare", "default", "die",
+ "do", "echo", "else", "elseif", "empty",
+ "enddeclare", "endfor", "endforeach", "endif", "endswitch",
+ "endwhile", "eval", "exit", "extends", "final",
+ "finally", "fn", "for", "foreach", "function",
+ "global", "goto", "if", "implements", "include",
+ "include_once", "instanceof", "insteadof", "interface", "isset",
+ "list", "match", "namespace", "new", "or",
+ "parent", "print", "private", "protected", "public",
+ "readonly", "require", "require_once", "return", "self",
+ "static", "switch", "throw", "trait", "try",
+ "unset", "use", "var", "while", "xor",
+ "yield", "int", "float", "bool", "string",
+ "true", "false", "null", "void", "iterable"};
const char* const kValidConstantNames[] = {
"int", "float", "bool", "string", "true",
"false", "null", "void", "iterable", "parent",
- "self"
+ "self", "readonly"
};
-const int kReservedNamesSize = 79;
-const int kValidConstantNamesSize = 11;
+const int kReservedNamesSize = 80;
+const int kValidConstantNamesSize = 12;
const int kFieldSetter = 1;
const int kFieldGetter = 2;
const int kFieldProperty = 3;
@@ -407,6 +407,34 @@
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(std::string php_namespace,
+ const DescriptorType* desc) {
+ if (!php_namespace.empty()) {
+ for (int i = 0; i < php_namespace.size(); i++) {
+ if (php_namespace[i] == '\\') {
+ php_namespace[i] = '/';
+ }
+ }
+ return php_namespace + "/" + desc->name() + ".php";
+ }
+
+ return desc->name() + ".php";
+}
+
std::string GeneratedServiceFileName(const ServiceDescriptor* service,
const Options& options) {
std::string result = FullClassName(service, options) + "Interface";
@@ -1252,6 +1280,82 @@
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 fullname = FullClassName(desc, options);
+ std::string php_namespace;
+ std::string classname;
+ int lastindex = fullname.find_last_of("\\");
+
+ if (lastindex != std::string::npos) {
+ php_namespace = fullname.substr(0, lastindex);
+ classname = fullname.substr(lastindex + 1);
+ } else {
+ php_namespace = "";
+ classname = fullname;
+ }
+
+ std::string filename = LegacyReadOnlyGeneratedClassFileName(php_namespace, desc);
+ std::unique_ptr<io::ZeroCopyOutputStream> output(
+ generator_context->Open(filename));
+ io::Printer printer(output.get(), '^');
+
+ GenerateHead(file, &printer);
+
+ if (!php_namespace.empty()) {
+ printer.Print(
+ "namespace ^name^;\n\n",
+ "name", php_namespace);
+ }
+
+ printer.Print("class_exists(^new^::class); // autoload the new class, which "
+ "will also create an alias to the deprecated class\n",
+ "new", classname);
+ 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", classname);
+}
+
void GenerateEnumFile(const FileDescriptor* file, const EnumDescriptor* en,
const Options& options,
GeneratorContext* generator_context) {
@@ -1372,6 +1476,19 @@
"new", fullname,
"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,
@@ -1487,6 +1604,19 @@
"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.
for (int i = 0; i < message->nested_type_count(); i++) {
GenerateMessageFile(file, message->nested_type(i), options,
diff --git a/src/google/protobuf/compiler/plugin.pb.h b/src/google/protobuf/compiler/plugin.pb.h
index b73c9de..51152e6 100644
--- a/src/google/protobuf/compiler/plugin.pb.h
+++ b/src/google/protobuf/compiler/plugin.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/compiler/python/generator.cc b/src/google/protobuf/compiler/python/generator.cc
index d8d6d74..70b0804 100644
--- a/src/google/protobuf/compiler/python/generator.cc
+++ b/src/google/protobuf/compiler/python/generator.cc
@@ -217,15 +217,12 @@
GeneratorContext* context, std::string* error) const {
// -----------------------------------------------------------------
// parse generator options
- bool cpp_generated_lib_linked = false;
std::vector<std::pair<std::string, std::string> > options;
ParseGeneratorParameter(parameter, &options);
for (int i = 0; i < options.size(); i++) {
- if (options[i].first == "cpp_generated_lib_linked") {
- cpp_generated_lib_linked = true;
- } else if (options[i].first == "pyi_out") {
+ if (options[i].first == "pyi_out") {
python::PyiGenerator pyi_generator;
if (!pyi_generator.Generate(file, "", context, error)) {
return false;
@@ -247,10 +244,6 @@
file_ = file;
std::string filename = GetFileName(file, ".py");
- pure_python_workable_ = !cpp_generated_lib_linked;
- if (HasPrefixString(file->name(), "google/protobuf/")) {
- pure_python_workable_ = true;
- }
FileDescriptorProto fdp;
file_->CopyTo(&fdp);
@@ -263,55 +256,49 @@
printer_ = &printer;
PrintTopBoilerplate(printer_, file_, GeneratingDescriptorProto());
- if (pure_python_workable_) {
- PrintImports();
- }
+ PrintImports();
PrintFileDescriptor();
- if (pure_python_workable_) {
- if (GeneratingDescriptorProto()) {
- printer_->Print("if _descriptor._USE_C_DESCRIPTORS == False:\n");
- printer_->Indent();
- // Create enums before message descriptors
- PrintAllNestedEnumsInFile();
- PrintMessageDescriptors();
- FixForeignFieldsInDescriptors();
- printer_->Outdent();
- printer_->Print("else:\n");
- printer_->Indent();
- }
- // Find the message descriptors first and then use the message
- // descriptor to find enums.
- printer_->Print(
- "_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())\n");
- if (GeneratingDescriptorProto()) {
- printer_->Outdent();
- }
+ if (GeneratingDescriptorProto()) {
+ printer_->Print("if _descriptor._USE_C_DESCRIPTORS == False:\n");
+ printer_->Indent();
+ // Create enums before message descriptors
+ PrintAllNestedEnumsInFile();
+ PrintMessageDescriptors();
+ FixForeignFieldsInDescriptors();
+ printer_->Outdent();
+ printer_->Print("else:\n");
+ printer_->Indent();
+ }
+ // Find the message descriptors first and then use the message
+ // descriptor to find enums.
+ printer_->Print(
+ "_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals())\n");
+ if (GeneratingDescriptorProto()) {
+ printer_->Outdent();
}
std::string module_name = ModuleName(file->name());
printer_->Print(
"_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, '$module_name$', "
"globals())\n",
"module_name", module_name);
- if (pure_python_workable_) {
- printer.Print("if _descriptor._USE_C_DESCRIPTORS == False:\n");
- printer_->Indent();
+ printer.Print("if _descriptor._USE_C_DESCRIPTORS == False:\n");
+ printer_->Indent();
- // We have to fix up the extensions after the message classes themselves,
- // since they need to call static RegisterExtension() methods on these
- // classes.
- FixForeignFieldsInExtensions();
- // Descriptor options may have custom extensions. These custom options
- // can only be successfully parsed after we register corresponding
- // extensions. Therefore we parse all options again here to recognize
- // custom options that may be unknown when we define the descriptors.
- // This does not apply to services because they are not used by extensions.
- FixAllDescriptorOptions();
+ // We have to fix up the extensions after the message classes themselves,
+ // since they need to call static RegisterExtension() methods on these
+ // classes.
+ FixForeignFieldsInExtensions();
+ // Descriptor options may have custom extensions. These custom options
+ // can only be successfully parsed after we register corresponding
+ // extensions. Therefore we parse all options again here to recognize
+ // custom options that may be unknown when we define the descriptors.
+ // This does not apply to services because they are not used by extensions.
+ FixAllDescriptorOptions();
- // Set serialized_start and serialized_end.
- SetSerializedPbInterval();
+ // Set serialized_start and serialized_end.
+ SetSerializedPbInterval();
- printer_->Outdent();
- }
+ printer_->Outdent();
if (HasGenericServices(file)) {
printer_->Print(
"_builder.BuildServices(DESCRIPTOR, '$module_name$', globals())\n",
@@ -389,30 +376,26 @@
" create_key=_descriptor._internal_create_key,\n";
printer_->Print(m, file_descriptor_template);
printer_->Indent();
- if (pure_python_workable_) {
- printer_->Print("serialized_pb=b'$value$'\n", "value",
- strings::CHexEscape(file_descriptor_serialized_));
- if (file_->dependency_count() != 0) {
- printer_->Print(",\ndependencies=[");
- for (int i = 0; i < file_->dependency_count(); ++i) {
- std::string module_alias = ModuleAlias(file_->dependency(i)->name());
- printer_->Print("$module_alias$.DESCRIPTOR,", "module_alias",
- module_alias);
- }
- printer_->Print("]");
+ printer_->Print("serialized_pb=b'$value$'\n", "value",
+ strings::CHexEscape(file_descriptor_serialized_));
+ if (file_->dependency_count() != 0) {
+ printer_->Print(",\ndependencies=[");
+ for (int i = 0; i < file_->dependency_count(); ++i) {
+ std::string module_alias = ModuleAlias(file_->dependency(i)->name());
+ printer_->Print("$module_alias$.DESCRIPTOR,", "module_alias",
+ module_alias);
}
- if (file_->public_dependency_count() > 0) {
- printer_->Print(",\npublic_dependencies=[");
- for (int i = 0; i < file_->public_dependency_count(); ++i) {
- std::string module_alias =
- ModuleAlias(file_->public_dependency(i)->name());
- printer_->Print("$module_alias$.DESCRIPTOR,", "module_alias",
- module_alias);
- }
- printer_->Print("]");
+ printer_->Print("]");
+ }
+ if (file_->public_dependency_count() > 0) {
+ printer_->Print(",\npublic_dependencies=[");
+ for (int i = 0; i < file_->public_dependency_count(); ++i) {
+ std::string module_alias =
+ ModuleAlias(file_->public_dependency(i)->name());
+ printer_->Print("$module_alias$.DESCRIPTOR,", "module_alias",
+ module_alias);
}
- } else {
- printer_->Print("serialized_pb=''\n");
+ printer_->Print("]");
}
// TODO(falk): Also print options and fix the message_type, enum_type,
@@ -467,11 +450,9 @@
printer_->Indent();
printer_->Indent();
- if (pure_python_workable_) {
- for (int i = 0; i < enum_descriptor.value_count(); ++i) {
- PrintEnumValueDescriptor(*enum_descriptor.value(i));
- printer_->Print(",\n");
- }
+ for (int i = 0; i < enum_descriptor.value_count(); ++i) {
+ PrintEnumValueDescriptor(*enum_descriptor.value(i));
+ printer_->Print(",\n");
}
printer_->Outdent();
@@ -482,10 +463,8 @@
EnumDescriptorProto edp;
printer_->Outdent();
printer_->Print(")\n");
- if (pure_python_workable_) {
- printer_->Print("_sym_db.RegisterEnumDescriptor($name$)\n", "name",
- module_level_descriptor_name);
- }
+ printer_->Print("_sym_db.RegisterEnumDescriptor($name$)\n", "name",
+ module_level_descriptor_name);
printer_->Print("\n");
}
@@ -535,10 +514,6 @@
void Generator::PrintDescriptorKeyAndModuleName(
const ServiceDescriptor& descriptor) const {
std::string name = ModuleLevelServiceDescriptorName(descriptor);
- if (!pure_python_workable_) {
- name = "_descriptor.ServiceDescriptor(full_name='" +
- descriptor.full_name() + "')";
- }
printer_->Print("$descriptor_key$ = $descriptor_name$,\n", "descriptor_key",
kDescriptorKey, "descriptor_name", name);
std::string module_name = ModuleName(file_->name());
@@ -728,12 +703,7 @@
PrintNestedMessages(message_descriptor, qualified_name, to_register);
std::map<std::string, std::string> m;
m["descriptor_key"] = kDescriptorKey;
- if (pure_python_workable_) {
- m["descriptor_name"] = ModuleLevelDescriptorName(message_descriptor);
- } else {
- m["descriptor_name"] = "_descriptor.Descriptor(full_name='" +
- message_descriptor.full_name() + "')";
- }
+ m["descriptor_name"] = ModuleLevelDescriptorName(message_descriptor);
printer_->Print(m, "'$descriptor_key$' : $descriptor_name$,\n");
std::string module_name = ModuleName(file_->name());
printer_->Print("'__module__' : '$module_name$'\n", "module_name",
diff --git a/src/google/protobuf/compiler/python/generator.h b/src/google/protobuf/compiler/python/generator.h
index f1fecbc..46b0ad1 100644
--- a/src/google/protobuf/compiler/python/generator.h
+++ b/src/google/protobuf/compiler/python/generator.h
@@ -76,6 +76,10 @@
uint64_t GetSupportedFeatures() const override;
+ void set_opensource_runtime(bool opensource) {
+ opensource_runtime_ = opensource;
+ }
+
private:
void PrintImports() const;
void PrintFileDescriptor() const;
@@ -170,7 +174,8 @@
mutable const FileDescriptor* file_; // Set in Generate(). Under mutex_.
mutable std::string file_descriptor_serialized_;
mutable io::Printer* printer_; // Set in Generate(). Under mutex_.
- mutable bool pure_python_workable_;
+
+ bool opensource_runtime_ = true;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Generator);
};
diff --git a/src/google/protobuf/compiler/python/pyi_generator.cc b/src/google/protobuf/compiler/python/pyi_generator.cc
index 1ccc9a2..e954fd3 100644
--- a/src/google/protobuf/compiler/python/pyi_generator.cc
+++ b/src/google/protobuf/compiler/python/pyi_generator.cc
@@ -44,39 +44,22 @@
namespace compiler {
namespace python {
-template <typename DescriptorT>
-struct SortByName {
- bool operator()(const DescriptorT* l, const DescriptorT* r) const {
- return l->name() < r->name();
- }
-};
-
PyiGenerator::PyiGenerator() : file_(nullptr) {}
PyiGenerator::~PyiGenerator() {}
-void PyiGenerator::PrintItemMap(
- const std::map<std::string, std::string>& item_map) const {
- for (const auto& entry : item_map) {
- printer_->Print("$key$: $value$\n", "key", entry.first, "value",
- entry.second);
- }
-}
-
template <typename DescriptorT>
-std::string PyiGenerator::ModuleLevelName(
- const DescriptorT& descriptor,
- const std::map<std::string, std::string>& import_map) const {
+std::string PyiGenerator::ModuleLevelName(const DescriptorT& descriptor) const {
std::string name = NamePrefixedWithNestedTypes(descriptor, ".");
if (descriptor.file() != file_) {
std::string module_alias;
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::vector<std::string> tokens = Split(module_name, ".");
module_alias = "_" + tokens.back();
} else {
- module_alias = import_map.at(filename);
+ module_alias = import_map_.at(filename);
}
name = module_alias + "." + name;
}
@@ -156,7 +139,6 @@
void PyiGenerator::PrintImportForDescriptor(
const FileDescriptor& desc,
- std::map<std::string, std::string>* import_map,
std::set<std::string>* seen_aliases) const {
const std::string& filename = desc.name();
std::string module_name = StrippedModuleName(filename);
@@ -176,21 +158,19 @@
}
printer_->Print("$statement$ as $alias$\n", "statement",
import_statement, "alias", alias);
- (*import_map)[filename] = alias;
+ import_map_[filename] = alias;
seen_aliases->insert(alias);
}
-void PyiGenerator::PrintImports(
- std::map<std::string, std::string>* item_map,
- std::map<std::string, std::string>* import_map) const {
+void PyiGenerator::PrintImports() const {
// Prints imported dependent _pb2 files.
std::set<std::string> seen_aliases;
for (int i = 0; i < file_->dependency_count(); ++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) {
PrintImportForDescriptor(
- *dep->public_dependency(j), import_map, &seen_aliases);
+ *dep->public_dependency(j), &seen_aliases);
}
}
@@ -254,7 +234,7 @@
if (import_modules.has_union) {
printer_->Print(", Union as _Union");
}
- printer_->Print("\n\n");
+ printer_->Print("\n");
// Public imports
for (int i = 0; i < file_->public_dependency_count(); ++i) {
@@ -272,17 +252,8 @@
module_name, "enum_class",
public_dep->enum_type(i)->name());
}
- // Enum values for public imports
- for (int i = 0; i < public_dep->enum_type_count(); ++i) {
- const EnumDescriptor* enum_descriptor = public_dep->enum_type(i);
- for (int j = 0; j < enum_descriptor->value_count(); ++j) {
- (*item_map)[enum_descriptor->value(j)->name()] =
- ModuleLevelName(*enum_descriptor, *import_map);
- }
- }
- // Top level extensions for public imports
- AddExtensions(*public_dep, item_map);
}
+printer_->Print("\n");
}
void PyiGenerator::PrintEnum(const EnumDescriptor& enum_descriptor) const {
@@ -293,20 +264,18 @@
"enum_name", enum_name);
}
-// Adds enum value to item map which will be ordered and printed later.
-void PyiGenerator::AddEnumValue(
- const EnumDescriptor& enum_descriptor,
- std::map<std::string, std::string>* item_map,
- const std::map<std::string, std::string>& import_map) const {
+void PyiGenerator::PrintEnumValues(
+ const EnumDescriptor& enum_descriptor) const {
// 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) {
const EnumValueDescriptor* value_descriptor = enum_descriptor.value(j);
- (*item_map)[value_descriptor->name()] = module_enum_name;
+ printer_->Print("$name$: $module_enum_name$\n",
+ "name", value_descriptor->name(),
+ "module_enum_name", module_enum_name);
}
}
-// Prints top level enums
void PyiGenerator::PrintTopLevelEnums() const {
for (int i = 0; i < file_->enum_type_count(); ++i) {
printer_->Print("\n");
@@ -314,25 +283,22 @@
}
}
-// Add top level extensions to item_map which will be ordered and
-// printed later.
template <typename DescriptorT>
-void PyiGenerator::AddExtensions(
- const DescriptorT& descriptor,
- std::map<std::string, std::string>* item_map) const {
+void PyiGenerator::PrintExtensions(const DescriptorT& descriptor) const {
for (int i = 0; i < descriptor.extension_count(); ++i) {
const FieldDescriptor* extension_field = descriptor.extension(i);
std::string constant_name = extension_field->name() + "_FIELD_NUMBER";
ToUpper(&constant_name);
- (*item_map)[constant_name] = "_ClassVar[int]";
- (*item_map)[extension_field->name()] = "_descriptor.FieldDescriptor";
+ printer_->Print("$constant_name$: _ClassVar[int]\n",
+ "constant_name", constant_name);
+ printer_->Print("$name$: _descriptor.FieldDescriptor\n",
+ "name", extension_field->name());
}
}
// Returns the string format of a field's cpp_type
std::string PyiGenerator::GetFieldType(
- const FieldDescriptor& field_des, const Descriptor& containing_des,
- const std::map<std::string, std::string>& import_map) const {
+ const FieldDescriptor& field_des, const Descriptor& containing_des) const {
switch (field_des.cpp_type()) {
case FieldDescriptor::CPPTYPE_INT32:
case FieldDescriptor::CPPTYPE_UINT32:
@@ -345,7 +311,7 @@
case FieldDescriptor::CPPTYPE_BOOL:
return "bool";
case FieldDescriptor::CPPTYPE_ENUM:
- return ModuleLevelName(*field_des.enum_type(), import_map);
+ return ModuleLevelName(*field_des.enum_type());
case FieldDescriptor::CPPTYPE_STRING:
if (field_des.type() == FieldDescriptor::TYPE_STRING) {
return "str";
@@ -356,7 +322,7 @@
// 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
// 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 &&
name == containing_des.name())) {
std::string module = ModuleName(field_des.file()->name());
@@ -371,8 +337,7 @@
}
void PyiGenerator::PrintMessage(
- const Descriptor& message_descriptor, bool is_nested,
- const std::map<std::string, std::string>& import_map) const {
+ const Descriptor& message_descriptor, bool is_nested) const {
if (!is_nested) {
printer_->Print("\n");
}
@@ -390,17 +355,11 @@
printer_->Indent();
printer_->Indent();
- std::vector<const FieldDescriptor*> fields;
- fields.reserve(message_descriptor.field_count());
- for (int i = 0; i < message_descriptor.field_count(); ++i) {
- fields.push_back(message_descriptor.field(i));
- }
- std::sort(fields.begin(), fields.end(), SortByName<FieldDescriptor>());
-
// Prints slots
printer_->Print("__slots__ = [", "class_name", class_name);
bool first_item = true;
- for (const auto& field_des : fields) {
+ for (int i = 0; i < message_descriptor.field_count(); ++i) {
+ const FieldDescriptor* field_des = message_descriptor.field(i);
if (IsPythonKeyword(field_des->name())) {
continue;
}
@@ -413,48 +372,34 @@
}
printer_->Print("]\n");
- std::map<std::string, std::string> item_map;
// Prints Extensions for extendable messages
if (message_descriptor.extension_range_count() > 0) {
- item_map["Extensions"] = "_python_message._ExtensionDict";
+ printer_->Print("Extensions: _python_message._ExtensionDict\n");
}
// Prints nested enums
- std::vector<const EnumDescriptor*> nested_enums;
- nested_enums.reserve(message_descriptor.enum_type_count());
for (int i = 0; i < message_descriptor.enum_type_count(); ++i) {
- nested_enums.push_back(message_descriptor.enum_type(i));
- }
- std::sort(nested_enums.begin(), nested_enums.end(),
- SortByName<EnumDescriptor>());
-
- for (const auto& entry : nested_enums) {
- PrintEnum(*entry);
- // Adds enum value to item_map which will be ordered and printed later
- AddEnumValue(*entry, &item_map, import_map);
+ PrintEnum(*message_descriptor.enum_type(i));
+ PrintEnumValues(*message_descriptor.enum_type(i));
}
// Prints nested messages
- std::vector<const Descriptor*> nested_messages;
- nested_messages.reserve(message_descriptor.nested_type_count());
for (int i = 0; i < message_descriptor.nested_type_count(); ++i) {
- nested_messages.push_back(message_descriptor.nested_type(i));
- }
- std::sort(nested_messages.begin(), nested_messages.end(),
- SortByName<Descriptor>());
-
- for (const auto& entry : nested_messages) {
- PrintMessage(*entry, true, import_map);
+ PrintMessage(*message_descriptor.nested_type(i), true);
}
- // Adds extensions to item_map which will be ordered and printed later
- AddExtensions(message_descriptor, &item_map);
+ PrintExtensions(message_descriptor);
- // Adds field number and field descriptor to item_map
+ // Prints field number
for (int i = 0; i < message_descriptor.field_count(); ++i) {
const FieldDescriptor& field_des = *message_descriptor.field(i);
- item_map[ToUpper(field_des.name()) + "_FIELD_NUMBER"] =
- "_ClassVar[int]";
+ printer_->Print(
+ "$field_number_name$: _ClassVar[int]\n", "field_number_name",
+ ToUpper(field_des.name()) + "_FIELD_NUMBER");
+ }
+ // Prints field name and type
+ for (int i = 0; i < message_descriptor.field_count(); ++i) {
+ const FieldDescriptor& field_des = *message_descriptor.field(i);
if (IsPythonKeyword(field_des.name())) {
continue;
}
@@ -465,27 +410,25 @@
field_type = (value_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
? "_containers.MessageMap["
: "_containers.ScalarMap[");
- field_type += GetFieldType(*key_des, message_descriptor, import_map);
+ field_type += GetFieldType(*key_des, message_descriptor);
field_type += ", ";
- field_type += GetFieldType(*value_des, message_descriptor, import_map);
+ field_type += GetFieldType(*value_des, message_descriptor);
} else {
if (field_des.is_repeated()) {
field_type = (field_des.cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
? "_containers.RepeatedCompositeFieldContainer["
: "_containers.RepeatedScalarFieldContainer[");
}
- field_type += GetFieldType(field_des, message_descriptor, import_map);
+ field_type += GetFieldType(field_des, message_descriptor);
}
if (field_des.is_repeated()) {
field_type += "]";
}
- item_map[field_des.name()] = field_type;
+ printer_->Print("$name$: $type$\n",
+ "name", field_des.name(), "type", field_type);
}
- // Prints all items in item_map
- PrintItemMap(item_map);
-
// Prints __init__
printer_->Print("def __init__(self");
bool has_key_words = false;
@@ -513,9 +456,9 @@
const Descriptor* map_entry = field_des->message_type();
printer_->Print(
"_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",
- GetFieldType(*map_entry->field(1), message_descriptor, import_map));
+ GetFieldType(*map_entry->field(1), message_descriptor));
} else {
if (field_des->is_repeated()) {
printer_->Print("_Iterable[");
@@ -523,15 +466,15 @@
if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
printer_->Print(
"_Union[$type_name$, _Mapping]", "type_name",
- GetFieldType(*field_des, message_descriptor, import_map));
+ GetFieldType(*field_des, message_descriptor));
} else {
if (field_des->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
printer_->Print("_Union[$type_name$, str]", "type_name",
- ModuleLevelName(*field_des->enum_type(), import_map));
+ ModuleLevelName(*field_des->enum_type()));
} else {
printer_->Print(
"$type_name$", "type_name",
- GetFieldType(*field_des, message_descriptor, import_map));
+ GetFieldType(*field_des, message_descriptor));
}
}
if (field_des->is_repeated()) {
@@ -553,36 +496,21 @@
printer_->Outdent();
}
-void PyiGenerator::PrintMessages(
- const std::map<std::string, std::string>& import_map) const {
+void PyiGenerator::PrintMessages() const {
// Deterministically order the descriptors.
- std::vector<const Descriptor*> messages;
- messages.reserve(file_->message_type_count());
for (int i = 0; i < file_->message_type_count(); ++i) {
- messages.push_back(file_->message_type(i));
- }
- std::sort(messages.begin(), messages.end(), SortByName<Descriptor>());
-
- for (const auto& entry : messages) {
- PrintMessage(*entry, false, import_map);
+ PrintMessage(*file_->message_type(i), false);
}
}
void PyiGenerator::PrintServices() const {
- std::vector<const ServiceDescriptor*> services;
- services.reserve(file_->service_count());
- for (int i = 0; i < file_->service_count(); ++i) {
- services.push_back(file_->service(i));
- }
- std::sort(services.begin(), services.end(), SortByName<ServiceDescriptor>());
-
// Prints $Service$ and $Service$_Stub classes
- for (const auto& entry : services) {
+ for (int i = 0; i < file_->service_count(); ++i) {
printer_->Print("\n");
printer_->Print(
"class $service_name$(_service.service): ...\n\n"
"class $service_name$_Stub($service_name$): ...\n",
- "service_name", entry->name());
+ "service_name", file_->service(i)->name());
}
}
@@ -591,6 +519,7 @@
GeneratorContext* context,
std::string* error) const {
MutexLock lock(&mutex_);
+ import_map_.clear();
// Calculate file name.
file_ = file;
std::string filename =
@@ -601,29 +530,28 @@
io::Printer printer(output.get(), '$');
printer_ = &printer;
- // item map will store "DESCRIPTOR", top level extensions, top level enum
- // values. The items will be sorted and printed later.
- std::map<std::string, std::string> item_map;
+ PrintImports();
+ printer_->Print("DESCRIPTOR: _descriptor.FileDescriptor\n");
- // Adds "DESCRIPTOR" into item_map.
- item_map["DESCRIPTOR"] = "_descriptor.FileDescriptor";
-
- // import_map will be a mapping from filename to module alias, e.g.
- // "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.
- for (int i = 0; i < file_->enum_type_count(); ++i) {
- AddEnumValue(*file_->enum_type(i), &item_map, import_map);
+ // Prints extensions and enums from imports.
+ for (int i = 0; i < file_->public_dependency_count(); ++i) {
+ const FileDescriptor* public_dep = file_->public_dependency(i);
+ PrintExtensions(*public_dep);
+ for (int i = 0; i < public_dep->enum_type_count(); ++i) {
+ const EnumDescriptor* enum_descriptor = public_dep->enum_type(i);
+ PrintEnumValues(*enum_descriptor);
+ }
}
- // Adds top level extensions to item_map.
- AddExtensions(*file_, &item_map);
- // Prints item map
- PrintItemMap(item_map);
- PrintMessages(import_map);
PrintTopLevelEnums();
+ // Prints top level enum values
+ for (int i = 0; i < file_->enum_type_count(); ++i) {
+ PrintEnumValues(*file_->enum_type(i));
+ }
+ // Prints top level Extensions
+ PrintExtensions(*file_);
+ PrintMessages();
+
if (HasGenericServices(file)) {
PrintServices();
}
diff --git a/src/google/protobuf/compiler/python/pyi_generator.h b/src/google/protobuf/compiler/python/pyi_generator.h
index 9611ed4..40741e1 100644
--- a/src/google/protobuf/compiler/python/pyi_generator.h
+++ b/src/google/protobuf/compiler/python/pyi_generator.h
@@ -76,37 +76,29 @@
private:
void PrintImportForDescriptor(const FileDescriptor& desc,
- std::map<std::string, std::string>* import_map,
std::set<std::string>* seen_aliases) const;
- void PrintImports(std::map<std::string, std::string>* item_map,
- std::map<std::string, std::string>* import_map) const;
- void PrintEnum(const EnumDescriptor& enum_descriptor) const;
- void AddEnumValue(const EnumDescriptor& enum_descriptor,
- std::map<std::string, std::string>* item_map,
- const std::map<std::string, std::string>& import_map) const;
+ void PrintImports() const;
void PrintTopLevelEnums() const;
+ void PrintEnum(const EnumDescriptor& enum_descriptor) const;
+ void PrintEnumValues(const EnumDescriptor& enum_descriptor) const;
template <typename DescriptorT>
- void AddExtensions(const DescriptorT& descriptor,
- std::map<std::string, std::string>* item_map) const;
- void PrintMessages(
- const std::map<std::string, std::string>& import_map) const;
- void PrintMessage(const Descriptor& message_descriptor, bool is_nested,
- const std::map<std::string, std::string>& import_map) const;
+ void PrintExtensions(const DescriptorT& descriptor) const;
+ void PrintMessages() const;
+ void PrintMessage(const Descriptor& message_descriptor, bool is_nested) const;
void PrintServices() const;
- void PrintItemMap(const std::map<std::string, std::string>& item_map) const;
std::string GetFieldType(
- const FieldDescriptor& field_des, const Descriptor& containing_des,
- const std::map<std::string, std::string>& import_map) const;
+ const FieldDescriptor& field_des, const Descriptor& containing_des) const;
template <typename DescriptorT>
- std::string ModuleLevelName(
- const DescriptorT& descriptor,
- const std::map<std::string, std::string>& import_map) const;
+ std::string ModuleLevelName(const DescriptorT& descriptor) const;
// Very coarse-grained lock to ensure that Generate() is reentrant.
- // Guards file_ and printer_.
+ // Guards file_, printer_, and import_map_.
mutable Mutex mutex_;
mutable const FileDescriptor* file_; // 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);
};
diff --git a/src/google/protobuf/descriptor.h b/src/google/protobuf/descriptor.h
index 685e923..beb3e64 100644
--- a/src/google/protobuf/descriptor.h
+++ b/src/google/protobuf/descriptor.h
@@ -1577,7 +1577,11 @@
const FileOptions& options() const;
// Syntax of this file.
- enum Syntax {
+ enum Syntax
+#ifndef SWIG
+ : int
+#endif // !SWIG
+ {
SYNTAX_UNKNOWN = 0,
SYNTAX_PROTO2 = 2,
SYNTAX_PROTO3 = 3,
diff --git a/src/google/protobuf/descriptor.pb.h b/src/google/protobuf/descriptor.pb.h
index f6f5da5..439db9f 100644
--- a/src/google/protobuf/descriptor.pb.h
+++ b/src/google/protobuf/descriptor.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/duration.pb.h b/src/google/protobuf/duration.pb.h
index 87729c4..1e4a3e1 100644
--- a/src/google/protobuf/duration.pb.h
+++ b/src/google/protobuf/duration.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/empty.pb.h b/src/google/protobuf/empty.pb.h
index fdc398c..c5f528b 100644
--- a/src/google/protobuf/empty.pb.h
+++ b/src/google/protobuf/empty.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/field_mask.pb.h b/src/google/protobuf/field_mask.pb.h
index fd75b88..01ecfac 100644
--- a/src/google/protobuf/field_mask.pb.h
+++ b/src/google/protobuf/field_mask.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/generated_message_reflection.cc b/src/google/protobuf/generated_message_reflection.cc
index 0353b74..70014b2 100644
--- a/src/google/protobuf/generated_message_reflection.cc
+++ b/src/google/protobuf/generated_message_reflection.cc
@@ -2514,6 +2514,7 @@
}
void Reflection::PrepareSplitMessageForWrite(Message* message) const {
+ GOOGLE_DCHECK_NE(message, schema_.default_instance_);
void** split = MutableSplitField(message);
const void* default_split = GetSplitField(schema_.default_instance_);
if (*split == default_split) {
diff --git a/src/google/protobuf/generated_message_tctable_impl.h b/src/google/protobuf/generated_message_tctable_impl.h
index 67349b4..3fad18c 100644
--- a/src/google/protobuf/generated_message_tctable_impl.h
+++ b/src/google/protobuf/generated_message_tctable_impl.h
@@ -38,7 +38,6 @@
#include <google/protobuf/port.h>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/generated_message_tctable_decl.h>
-#include <google/protobuf/message_lite.h>
#include <google/protobuf/metadata_lite.h>
#include <google/protobuf/parse_context.h>
#include <google/protobuf/wire_format_lite.h>
@@ -257,16 +256,6 @@
// clang-format on
} // 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
template <size_t align>
void AlignFail(uintptr_t address) {
@@ -349,6 +338,28 @@
static const char* FastZ64P1(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):
// E: closed enum (N.B.: open enums use V32, above)
// r: enum range v: enum validator (_IsValid function)
@@ -600,6 +611,135 @@
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 protobuf
} // namespace google
diff --git a/src/google/protobuf/generated_message_tctable_lite.cc b/src/google/protobuf/generated_message_tctable_lite.cc
index aef780f..f9fb6cc 100644
--- a/src/google/protobuf/generated_message_tctable_lite.cc
+++ b/src/google/protobuf/generated_message_tctable_lite.cc
@@ -30,6 +30,7 @@
#include <cstdint>
#include <numeric>
+#include <type_traits>
#include <google/protobuf/extension_set.h>
#include <google/protobuf/generated_message_tctable_decl.h>
@@ -84,56 +85,13 @@
// TODO(b/64614992): remove this asm
asm("" : "+r"(table));
#endif
- ptr = TagDispatch(msg, ptr, ctx, table - 1, 0, {});
+ ptr = TagDispatch(msg, ptr, ctx, {}, table - 1, 0);
if (ptr == nullptr) break;
if (ctx->LastTag() != 1) break; // Ended on terminating tag
}
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.
static uint32_t FastDecodeTag(uint8_t coded_tag) {
return coded_tag;
@@ -875,8 +833,31 @@
}
const char* TcParser::FastV8S1(PROTOBUF_TC_PARAM_DECL) {
- PROTOBUF_MUSTTAIL return SingularVarint<bool, uint8_t>(
- PROTOBUF_TC_PARAM_PASS);
+ // Special case for a varint bool field with a tag of 1 byte:
+ // 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) {
PROTOBUF_MUSTTAIL return SingularVarint<bool, uint16_t>(
diff --git a/src/google/protobuf/io/printer.h b/src/google/protobuf/io/printer.h
index e30bfa6..aa507cb 100644
--- a/src/google/protobuf/io/printer.h
+++ b/src/google/protobuf/io/printer.h
@@ -251,7 +251,8 @@
template <typename... Args>
void Print(const char* text, const Args&... args) {
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
@@ -299,18 +300,13 @@
void Annotate(const char* begin_varname, const char* end_varname,
const std::string& file_path, const std::vector<int>& path);
- // Base case
- void PrintInternal(std::map<std::string, std::string>* vars,
- const char* text) {
- Print(*vars, text);
- }
+ void FillMap(std::map<std::string, std::string>* vars) {}
template <typename... Args>
- void PrintInternal(std::map<std::string, std::string>* vars, const char* text,
- const char* key, const std::string& value,
- const Args&... args) {
+ void FillMap(std::map<std::string, std::string>* vars, const std::string& key,
+ const std::string& value, const Args&... args) {
(*vars)[key] = value;
- PrintInternal(vars, text, args...);
+ FillMap(vars, args...);
}
// Copy size worth of bytes from data to buffer_.
diff --git a/src/google/protobuf/io/tokenizer.cc b/src/google/protobuf/io/tokenizer.cc
index f9e0776..9614d01 100644
--- a/src/google/protobuf/io/tokenizer.cc
+++ b/src/google/protobuf/io/tokenizer.cc
@@ -406,7 +406,7 @@
case '\n': {
if (!allow_multiline_strings_) {
- AddError("String literals cannot cross line boundaries.");
+ AddError("Multiline strings are not allowed. Did you miss a \"?.");
return;
}
NextChar();
diff --git a/src/google/protobuf/io/tokenizer_unittest.cc b/src/google/protobuf/io/tokenizer_unittest.cc
index 16ba940..6233d6a 100644
--- a/src/google/protobuf/io/tokenizer_unittest.cc
+++ b/src/google/protobuf/io/tokenizer_unittest.cc
@@ -1067,7 +1067,8 @@
{"'\\X' foo", true, "0:2: Invalid escape sequence in string literal.\n"},
{"'\\x' foo", true, "0:3: Expected hex digits for escape sequence.\n"},
{"'foo", false, "0:4: Unexpected end of string.\n"},
- {"'bar\nfoo", true, "0:4: String literals cannot cross line boundaries.\n"},
+ {"'bar\nfoo", true,
+ "0:4: Multiline strings are not allowed. Did you miss a \"?.\n"},
{"'\\u01' foo", true,
"0:5: Expected four hex digits for \\u escape sequence.\n"},
{"'\\u01' foo", true,
diff --git a/src/google/protobuf/map.h b/src/google/protobuf/map.h
index d0aa010..a3f79f1 100644
--- a/src/google/protobuf/map.h
+++ b/src/google/protobuf/map.h
@@ -333,6 +333,11 @@
} // namespace internal
+#ifdef PROTOBUF_FUTURE_MAP_PAIR_UPGRADE
+// This is the class for Map's internal value_type.
+template <typename Key, typename T>
+using MapPair = std::pair<const Key, T>;
+#else
// This is the class for Map's internal value_type. Instead of using
// std::pair as value_type, we use this class which provides us more control of
// its process of construction and destruction.
@@ -363,6 +368,7 @@
friend class Arena;
friend class Map<Key, T>;
};
+#endif
// Map is an associative container type used to store protobuf map
// fields. Each Map instance may or may not use a different hash function, a
diff --git a/src/google/protobuf/map_test.cc b/src/google/protobuf/map_test.cc
index f7c024c..fd89524 100644
--- a/src/google/protobuf/map_test.cc
+++ b/src/google/protobuf/map_test.cc
@@ -84,3 +84,5 @@
} // namespace internal
} // namespace protobuf
} // namespace google
+
+#include <google/protobuf/port_undef.inc>
diff --git a/src/google/protobuf/map_test.inc b/src/google/protobuf/map_test.inc
index 041ac82..1d55dd5 100644
--- a/src/google/protobuf/map_test.inc
+++ b/src/google/protobuf/map_test.inc
@@ -810,6 +810,8 @@
m, UnorderedElementsAre(Pair(1, "one"), Pair(2, "two"), Pair(42, "aaa")));
}
+#ifndef PROTOBUF_FUTURE_MAP_PAIR_UPGRADE
+
TEST_F(MapImplTest, EmplaceKeyOnly) {
using ::testing::Pair;
using ::testing::UnorderedElementsAre;
@@ -824,6 +826,43 @@
EXPECT_THAT(m, UnorderedElementsAre(Pair(1, ""), Pair(42, "")));
}
+#else
+
+TEST_F(MapImplTest, ValueTypeNoImplicitConversion) {
+ using vt = typename Map<const char*, int>::value_type;
+
+ EXPECT_FALSE((std::is_convertible<
+ vt, std::pair<std::string, std::vector<std::string>>>::value));
+}
+
+enum class ConstructorType {
+ kDefault,
+ kCopy,
+ kMove,
+};
+
+struct ConstructorTag {
+ ConstructorTag() : invoked_constructor(ConstructorType::kDefault) {}
+ ConstructorTag(const ConstructorTag&)
+ : invoked_constructor(ConstructorType::kCopy) {}
+ ConstructorTag(ConstructorTag&&)
+ : invoked_constructor(ConstructorType::kMove) {}
+
+ ConstructorType invoked_constructor;
+};
+
+TEST_F(MapImplTest, ValueTypeHasMoveConstructor) {
+ using vt = typename Map<ConstructorTag, ConstructorTag>::value_type;
+ ConstructorTag l, r;
+
+ vt pair(l, std::move(r));
+
+ EXPECT_EQ(pair.first.invoked_constructor, ConstructorType::kCopy);
+ EXPECT_EQ(pair.second.invoked_constructor, ConstructorType::kMove);
+}
+
+#endif // !PROTOBUF_FUTURE_MAP_PAIR_UPGRADE
+
struct CountedInstance {
CountedInstance() { ++num_created; }
CountedInstance(const CountedInstance&) : CountedInstance() {}
diff --git a/src/google/protobuf/message.cc b/src/google/protobuf/message.cc
index 724a622..5052b1c 100644
--- a/src/google/protobuf/message.cc
+++ b/src/google/protobuf/message.cc
@@ -214,8 +214,10 @@
}
namespace internal {
-void* CreateSplitMessageGeneric(Arena* arena, void* default_split,
- size_t size) {
+void* CreateSplitMessageGeneric(Arena* arena, const void* default_split,
+ size_t size, const void* message,
+ const void* default_message) {
+ GOOGLE_DCHECK_NE(message, default_message);
void* split =
(arena == nullptr) ? ::operator new(size) : arena->AllocateAligned(size);
memcpy(split, default_split, size);
diff --git a/src/google/protobuf/message.h b/src/google/protobuf/message.h
index 7042a13..0d69a6b 100644
--- a/src/google/protobuf/message.h
+++ b/src/google/protobuf/message.h
@@ -411,7 +411,9 @@
namespace internal {
// Creates and returns an allocation for a split message.
-void* CreateSplitMessageGeneric(Arena* arena, void* default_split, size_t size);
+void* CreateSplitMessageGeneric(Arena* arena, const void* default_split,
+ size_t size, const void* message,
+ const void* default_message);
// Forward-declare interfaces used to implement RepeatedFieldRef.
// These are protobuf internals that users shouldn't care about.
@@ -483,6 +485,11 @@
return internal::ToIntSize(SpaceUsedLong(message));
}
+ // Returns true if the given message is a default message instance.
+ bool IsDefaultInstance(const Message& message) const {
+ return schema_.IsDefaultInstance(message);
+ }
+
// Check if the given non-repeated field is set.
bool HasField(const Message& message, const FieldDescriptor* field) const;
diff --git a/src/google/protobuf/message_unittest.inc b/src/google/protobuf/message_unittest.inc
index 44e3b31..c7c29e6 100644
--- a/src/google/protobuf/message_unittest.inc
+++ b/src/google/protobuf/message_unittest.inc
@@ -1174,11 +1174,12 @@
std::signbit(out_message.optional_double()));
}
-std::string EncodeEnumValue(int number, int value, int non_canonical_bytes) {
- uint8_t buf[100];
- uint8_t* p = buf;
-
- p = internal::WireFormatLite::WriteEnumToArray(number, value, p);
+// Adds `non_canonical_bytes` bytes to the varint representation at the tail of
+// the buffer.
+// `buf` points to the start of the buffer, `p` points to one-past-the-end.
+// Returns the new one-past-the-end pointer.
+uint8_t* AddNonCanonicalBytes(const uint8_t* buf, uint8_t* p,
+ int non_canonical_bytes) {
// varint can have a max of 10 bytes.
while (non_canonical_bytes-- > 0 && p - buf < 10) {
// Add a dummy byte at the end.
@@ -1186,7 +1187,15 @@
p[0] = 0;
++p;
}
+ return p;
+}
+std::string EncodeEnumValue(int number, int value, int non_canonical_bytes) {
+ uint8_t buf[100];
+ uint8_t* p = buf;
+
+ p = internal::WireFormatLite::WriteEnumToArray(number, value, p);
+ p = AddNonCanonicalBytes(buf, p, non_canonical_bytes);
return std::string(buf, p);
}
@@ -1201,6 +1210,16 @@
const auto other_field = EncodeOtherField();
+ // Encode a boolean field for many different cases and verify that it can be
+ // parsed as expected.
+ // There are:
+ // - optional/repeated/packed fields
+ // - field tags that encode in 1/2/3 bytes
+ // - canonical and non-canonical encodings of the varint
+ // - last vs not last field
+ // - label combinations to trigger different parsers: sequential, small
+ // sequential, non-validated.
+
constexpr int kInvalidValue = 0x900913;
auto* ref = obj.GetReflection();
auto* descriptor = obj.descriptor();
@@ -1226,6 +1245,8 @@
auto encoded = EncodeEnumValue(field->number(), value_desc->number(),
non_canonical_bytes);
if (use_tail_field) {
+ // Make sure that fields after this one can be parsed too. ie test
+ // that the "next" jump is correct too.
encoded += other_field;
}
@@ -1263,5 +1284,72 @@
}
}
+std::string EncodeBoolValue(int number, bool value, int non_canonical_bytes) {
+ uint8_t buf[100];
+ uint8_t* p = buf;
+
+ p = internal::WireFormatLite::WriteBoolToArray(number, value, p);
+ p = AddNonCanonicalBytes(buf, p, non_canonical_bytes);
+ return std::string(buf, p);
+}
+
+TEST(MESSAGE_TEST_NAME, TestBoolParsers) {
+ UNITTEST::BoolParseTester obj;
+
+ const auto other_field = EncodeOtherField();
+
+ // Encode a boolean field for many different cases and verify that it can be
+ // parsed as expected.
+ // There are:
+ // - optional/repeated/packed fields
+ // - field tags that encode in 1/2/3 bytes
+ // - canonical and non-canonical encodings of the varint
+ // - last vs not last field
+
+ auto* ref = obj.GetReflection();
+ auto* descriptor = obj.descriptor();
+ for (bool use_tail_field : {false, true}) {
+ SCOPED_TRACE(use_tail_field);
+ for (int non_canonical_bytes = 0; non_canonical_bytes < 10;
+ ++non_canonical_bytes) {
+ SCOPED_TRACE(non_canonical_bytes);
+ for (int i = 0; i < descriptor->field_count(); ++i) {
+ const auto* field = descriptor->field(i);
+ if (field->name() == "other_field") continue;
+ SCOPED_TRACE(field->full_name());
+ for (bool value : {false, true}) {
+ SCOPED_TRACE(value);
+ auto encoded =
+ EncodeBoolValue(field->number(), value, non_canonical_bytes);
+ if (use_tail_field) {
+ // Make sure that fields after this one can be parsed too. ie test
+ // that the "next" jump is correct too.
+ encoded += other_field;
+ }
+
+ EXPECT_TRUE(obj.ParseFromString(encoded));
+ if (field->is_repeated()) {
+ ASSERT_EQ(ref->FieldSize(obj, field), 1);
+ EXPECT_EQ(ref->GetRepeatedBool(obj, field, 0), value);
+ } else {
+ EXPECT_TRUE(ref->HasField(obj, field));
+ EXPECT_EQ(ref->GetBool(obj, field), value);
+ }
+ auto& unknown = ref->GetUnknownFields(obj);
+ ASSERT_EQ(unknown.field_count(), 0);
+ }
+ }
+ }
+ }
+}
+
+TEST(MESSAGE_TEST_NAME, IsDefaultInstance) {
+ UNITTEST::TestAllTypes msg;
+ const auto& default_msg = UNITTEST::TestAllTypes::default_instance();
+ const auto* r = msg.GetReflection();
+ EXPECT_TRUE(r->IsDefaultInstance(default_msg));
+ EXPECT_FALSE(r->IsDefaultInstance(msg));
+}
+
} // namespace protobuf
} // namespace google
diff --git a/src/google/protobuf/parse_context.h b/src/google/protobuf/parse_context.h
index a7baf4d..c72e19f 100644
--- a/src/google/protobuf/parse_context.h
+++ b/src/google/protobuf/parse_context.h
@@ -617,6 +617,7 @@
PROTOBUF_NODISCARD inline PROTOBUF_ALWAYS_INLINE uint64_t
RotRight7AndReplaceLowByte(uint64_t res, const char& byte) {
+ // TODO(b/239808098): remove the inline assembly
#if defined(__x86_64__) && defined(__GNUC__)
// This will only use one register for `res`.
// `byte` comes as a reference to allow the compiler to generate code like:
diff --git a/src/google/protobuf/port_def.inc b/src/google/protobuf/port_def.inc
index 67afdff..7d067fd 100644
--- a/src/google/protobuf/port_def.inc
+++ b/src/google/protobuf/port_def.inc
@@ -181,11 +181,15 @@
// Future versions of protobuf will include breaking changes to some APIs.
// This macro can be set to enable these API changes ahead of time, so that
// user code can be updated before upgrading versions of protobuf.
-// PROTOBUF_FUTURE_FINAL is used on classes that are historically not marked as
-// final, but that may be marked final in future (breaking) releases.
#ifdef PROTOBUF_FUTURE_BREAKING_CHANGES
-// Used on classes that are historically not marked as final.
+
+// Used to upgrade google::protobuf::MapPair<K, V> to std::pair<const K, V>.
+// Owner: mordberg@
+#define PROTOBUF_FUTURE_MAP_PAIR_UPGRADE 1
+
+// Used on classes that are historically not marked as final, but that may be
+// marked final in future (breaking) releases.
// Owner: kfm@
#define PROTOBUF_FUTURE_FINAL final
@@ -208,7 +212,7 @@
#ifdef PROTOBUF_VERSION
#error PROTOBUF_VERSION was previously defined
#endif
-#define PROTOBUF_VERSION 3021002
+#define PROTOBUF_VERSION 3021004
#ifdef PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC
#error PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC was previously defined
@@ -678,7 +682,8 @@
#error PROTOBUF_CONSTINIT was previously defined
#endif
-#if defined(_MSC_VER)
+// Lexan sets both MSV_VER and clang, so handle it with the clang path.
+#if defined(_MSC_VER) && !defined(__clang__)
// MSVC 17 currently seems to raise an error about constant-initialized pointers.
# if PROTOBUF_MSC_VER_MIN(1930)
# define PROTOBUF_CONSTINIT
@@ -694,7 +699,7 @@
// uses Clang 12.0.5.
# elif !defined(__CYGWIN__) && \
__has_cpp_attribute(clang::require_constant_initialization) && \
- ((defined(__APPLE__) && PROTOBUF_CLANG_MIN(13, 0)) || \
+ ((defined(__APPLE__) && PROTOBUF_CLANG_MIN(13, 0)) || \
(!defined(__APPLE__) && PROTOBUF_CLANG_MIN(12, 0)))
# define PROTOBUF_CONSTINIT [[clang::require_constant_initialization]]
# define PROTOBUF_CONSTEXPR constexpr
@@ -827,11 +832,22 @@
#define PROTOBUF_TAIL_CALL_TABLE_PARSER_ENABLED 1
#endif
-#define PROTOBUF_TC_PARAM_DECL \
- ::google::protobuf::MessageLite *msg, const char *ptr, \
- ::google::protobuf::internal::ParseContext *ctx, \
- const ::google::protobuf::internal::TcParseTableBase *table, \
- uint64_t hasbits, ::google::protobuf::internal::TcFieldData data
+// 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.
+// Arguments `msg`, `ptr` and `ctx` are the 1st/2nd/3rd argument to match the
+// signature of ParseLoop.
+//
+// Note for x86_64: `data` must be the third or fourth argument for performance
+// reasons. In order to efficiently read the second byte of `data` we need it to
+// be passed in RDX or RCX.
+#define PROTOBUF_TC_PARAM_DECL \
+ ::google::protobuf::MessageLite *msg, const char *ptr, \
+ ::google::protobuf::internal::ParseContext *ctx, \
+ ::google::protobuf::internal::TcFieldData data, \
+ const ::google::protobuf::internal::TcParseTableBase *table, uint64_t hasbits
+// PROTOBUF_TC_PARAM_PASS passes values to match PROTOBUF_TC_PARAM_DECL.
+#define PROTOBUF_TC_PARAM_PASS msg, ptr, ctx, data, table, hasbits
#ifdef PROTOBUF_UNUSED
#error PROTOBUF_UNUSED was previously defined
@@ -953,6 +969,21 @@
#pragma warning(disable: 4125)
#endif
+#if PROTOBUF_ENABLE_DEBUG_LOGGING_MAY_LEAK_PII
+#define PROTOBUF_DEBUG true
+#else
+#define PROTOBUF_DEBUG false
+#endif
+
+// This `for` allows us to condition the `GOOGLE_LOG` on the define above, so that
+// code can write `PROTOBUF_DLOG(INFO) << ...;` and have it turned off when
+// debug logging is off.
+//
+// This is a `for`, not and `if`, to avoid it accidentally chaining with an
+// `else` below it.
+#define PROTOBUF_DLOG(x) \
+ for (bool b = PROTOBUF_DEBUG; b; b = false) GOOGLE_LOG(x)
+
// We don't want code outside port_def doing complex testing, so
// remove our portable condition test macros to nudge folks away from
// using it themselves.
diff --git a/src/google/protobuf/port_undef.inc b/src/google/protobuf/port_undef.inc
index bb36d7c..23eb789 100644
--- a/src/google/protobuf/port_undef.inc
+++ b/src/google/protobuf/port_undef.inc
@@ -108,9 +108,12 @@
#undef PROTOBUF_LOCKS_EXCLUDED
#undef PROTOBUF_NO_THREAD_SAFETY_ANALYSIS
#undef PROTOBUF_GUARDED_BY
+#undef PROTOBUF_DEBUG
+#undef PROTOBUF_DLOG
#ifdef PROTOBUF_FUTURE_BREAKING_CHANGES
#undef PROTOBUF_FUTURE_BREAKING_CHANGES
+#undef PROTOBUF_FUTURE_MAP_PAIR_UPGRADE
#undef PROTOBUF_FUTURE_REMOVE_DEFAULT_FIELD_COMPARATOR
#undef PROTOBUF_FUTURE_REMOVE_CLEARED_API
#endif
diff --git a/src/google/protobuf/repeated_field.h b/src/google/protobuf/repeated_field.h
index e504274..a7c1275 100644
--- a/src/google/protobuf/repeated_field.h
+++ b/src/google/protobuf/repeated_field.h
@@ -94,43 +94,30 @@
constexpr int kRepeatedFieldUpperClampLimit =
(std::numeric_limits<int>::max() / 2) + 1;
-// Swaps two blocks of memory of size sizeof(T).
-template <typename T>
-inline void SwapBlock(char* p, char* q) {
- T tmp;
- memcpy(&tmp, p, sizeof(T));
- memcpy(p, q, sizeof(T));
- memcpy(q, &tmp, sizeof(T));
-}
-
// Swaps two blocks of memory of size kSize:
-// template <int kSize> void memswap(char* p, char* q);
-template <int kSize>
-inline typename std::enable_if<(kSize == 0), void>::type memswap(char*, char*) {
-}
-
-#define PROTO_MEMSWAP_DEF_SIZE(reg_type, max_size) \
- template <int kSize> \
- typename std::enable_if<(kSize >= sizeof(reg_type) && kSize < (max_size)), \
- void>::type \
- memswap(char* p, char* q) { \
- SwapBlock<reg_type>(p, q); \
- memswap<kSize - sizeof(reg_type)>(p + sizeof(reg_type), \
- q + sizeof(reg_type)); \
- }
-
-PROTO_MEMSWAP_DEF_SIZE(uint8_t, 2)
-PROTO_MEMSWAP_DEF_SIZE(uint16_t, 4)
-PROTO_MEMSWAP_DEF_SIZE(uint32_t, 8)
-
-#ifdef __SIZEOF_INT128__
-PROTO_MEMSWAP_DEF_SIZE(uint64_t, 16)
-PROTO_MEMSWAP_DEF_SIZE(__uint128_t, (1u << 31))
+template <size_t kSize>
+void memswap(char* a, char* b) {
+#if __SIZEOF_INT128__
+ using Buffer = __uint128_t;
#else
-PROTO_MEMSWAP_DEF_SIZE(uint64_t, (1u << 31))
+ using Buffer = uint64_t;
#endif
-#undef PROTO_MEMSWAP_DEF_SIZE
+ constexpr size_t kBlockSize = sizeof(Buffer);
+ Buffer buf;
+ for (size_t i = 0; i < kSize / kBlockSize; ++i) {
+ memcpy(&buf, a, kBlockSize);
+ memcpy(a, b, kBlockSize);
+ memcpy(b, &buf, kBlockSize);
+ a += kBlockSize;
+ b += kBlockSize;
+ }
+
+ // Swap the leftover bytes, could be zero.
+ memcpy(&buf, a, kSize % kBlockSize);
+ memcpy(a, b, kSize % kBlockSize);
+ memcpy(b, &buf, kSize % kBlockSize);
+}
template <typename Element>
class RepeatedIterator;
@@ -233,12 +220,6 @@
// copies data between each other.
void Swap(RepeatedField* other);
- // Swaps entire contents with "other". Should be called only if the caller can
- // guarantee that both repeated fields are on the same arena or are on the
- // heap. Swapping between different arenas is disallowed and caught by a
- // GOOGLE_DCHECK (see API docs for details).
- void UnsafeArenaSwap(RepeatedField* other);
-
// Swaps two elements.
void SwapElements(int index1, int index2);
@@ -321,6 +302,12 @@
: rep()->arena;
}
+ // Swaps entire contents with "other". Should be called only if the caller can
+ // guarantee that both repeated fields are on the same arena or are on the
+ // heap. Swapping between different arenas is disallowed and caught by a
+ // GOOGLE_DCHECK (see API docs for details).
+ void UnsafeArenaSwap(RepeatedField* other);
+
static constexpr int kInitialSize = 0;
// A note on the representation here (see also comment below for
// RepeatedPtrFieldBase's struct Rep):
diff --git a/src/google/protobuf/repeated_field_unittest.cc b/src/google/protobuf/repeated_field_unittest.cc
index c521147..2cb6bab 100644
--- a/src/google/protobuf/repeated_field_unittest.cc
+++ b/src/google/protobuf/repeated_field_unittest.cc
@@ -305,6 +305,60 @@
}
}
+template <int kSize>
+void TestMemswap() {
+ SCOPED_TRACE(kSize);
+
+ const auto a_char = [](int i) -> char { return (i % ('z' - 'a')) + 'a'; };
+ const auto b_char = [](int i) -> char { return (i % ('Z' - 'A')) + 'A'; };
+ std::string a, b;
+ for (int i = 0; i < kSize; ++i) {
+ a += a_char(i);
+ b += b_char(i);
+ }
+ // We will not swap these.
+ a += "+";
+ b += "-";
+
+ std::string expected_a = b, expected_b = a;
+ expected_a.back() = '+';
+ expected_b.back() = '-';
+
+ internal::memswap<kSize>(&a[0], &b[0]);
+
+ // ODR use the functions in a way that forces the linker to keep them. That
+ // way we can see their generated code.
+ volatile auto odr_use_for_asm_dump = &internal::memswap<kSize>;
+ (void)odr_use_for_asm_dump;
+
+ EXPECT_EQ(expected_a, a);
+ EXPECT_EQ(expected_b, b);
+}
+
+TEST(Memswap, VerifyWithSmallAndLargeSizes) {
+ // Arbitrary sizes
+ TestMemswap<0>();
+ TestMemswap<1>();
+ TestMemswap<10>();
+ TestMemswap<100>();
+ TestMemswap<1000>();
+ TestMemswap<10000>();
+ TestMemswap<100000>();
+ TestMemswap<1000000>();
+
+ // Pointer aligned sizes
+ TestMemswap<sizeof(void*) * 1>();
+ TestMemswap<sizeof(void*) * 7>();
+ TestMemswap<sizeof(void*) * 17>();
+ TestMemswap<sizeof(void*) * 27>();
+
+ // Test also just the block size and no leftover.
+ TestMemswap<64 * 1>();
+ TestMemswap<64 * 2>();
+ TestMemswap<64 * 3>();
+ TestMemswap<64 * 4>();
+}
+
// Determines how much space was reserved by the given field by adding elements
// to it until it re-allocates its space.
static int ReservedSpace(RepeatedField<int>* field) {
diff --git a/src/google/protobuf/repeated_ptr_field.h b/src/google/protobuf/repeated_ptr_field.h
index acdbee3..8a9c9e6 100644
--- a/src/google/protobuf/repeated_ptr_field.h
+++ b/src/google/protobuf/repeated_ptr_field.h
@@ -41,8 +41,6 @@
//
// This header covers RepeatedPtrField.
-// IWYU pragma: private, include "net/proto2/public/repeated_field.h"
-
#ifndef GOOGLE_PROTOBUF_REPEATED_PTR_FIELD_H__
#define GOOGLE_PROTOBUF_REPEATED_PTR_FIELD_H__
diff --git a/src/google/protobuf/source_context.pb.h b/src/google/protobuf/source_context.pb.h
index fb2e29f..899cfb0 100644
--- a/src/google/protobuf/source_context.pb.h
+++ b/src/google/protobuf/source_context.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/struct.pb.h b/src/google/protobuf/struct.pb.h
index 7a0805d..154ea45 100644
--- a/src/google/protobuf/struct.pb.h
+++ b/src/google/protobuf/struct.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/stubs/common.h b/src/google/protobuf/stubs/common.h
index 9d90305..c4d2636 100644
--- a/src/google/protobuf/stubs/common.h
+++ b/src/google/protobuf/stubs/common.h
@@ -82,7 +82,7 @@
// The current version, represented as a single integer to make comparison
// easier: major * 10^6 + minor * 10^3 + micro
-#define GOOGLE_PROTOBUF_VERSION 3021002
+#define GOOGLE_PROTOBUF_VERSION 3021004
// A suffix string for alpha, beta or rc releases. Empty for stable releases.
#define GOOGLE_PROTOBUF_VERSION_SUFFIX ""
diff --git a/src/google/protobuf/timestamp.pb.h b/src/google/protobuf/timestamp.pb.h
index 3b5a469..771216e 100644
--- a/src/google/protobuf/timestamp.pb.h
+++ b/src/google/protobuf/timestamp.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/type.pb.h b/src/google/protobuf/type.pb.h
index bcf1a44..2555602 100644
--- a/src/google/protobuf/type.pb.h
+++ b/src/google/protobuf/type.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/src/google/protobuf/unittest.proto b/src/google/protobuf/unittest.proto
index 2ac8f17..de78f9a 100644
--- a/src/google/protobuf/unittest.proto
+++ b/src/google/protobuf/unittest.proto
@@ -346,6 +346,16 @@
optional bytes oneof_bytes_extension = 114;
}
+message TestMixedFieldsAndExtensions {
+ optional int32 a = 1;
+ repeated fixed32 b = 3;
+ extensions 2, 4;
+ extend TestMixedFieldsAndExtensions {
+ optional int32 c = 2;
+ repeated fixed32 d = 4;
+ }
+}
+
message TestGroup {
optional group OptionalGroup = 16 {
optional int32 a = 17;
@@ -1527,3 +1537,20 @@
optional int32 other_field = 99;
};
+// This message contains different kind of bool fields to exercise the different
+// parsers in table-drived.
+message BoolParseTester {
+ optional bool optional_bool_lowfield = 1;
+ optional bool optional_bool_midfield = 1001;
+ optional bool optional_bool_hifield = 1000001;
+ repeated bool repeated_bool_lowfield = 2;
+ repeated bool repeated_bool_midfield = 1002;
+ repeated bool repeated_bool_hifield = 1000002;
+ repeated bool packed_bool_lowfield = 3 [packed = true];
+ repeated bool packed_bool_midfield = 1003 [packed = true];
+ repeated bool packed_bool_hifield = 1000003 [packed = true];
+
+ // An arbitrary field we can append to to break the runs of repeated fields.
+ optional int32 other_field = 99;
+};
+
diff --git a/src/google/protobuf/util/BUILD.bazel b/src/google/protobuf/util/BUILD.bazel
index 2d09db8..f9de867 100644
--- a/src/google/protobuf/util/BUILD.bazel
+++ b/src/google/protobuf/util/BUILD.bazel
@@ -69,9 +69,12 @@
cc_test(
name = "message_differencer_unittest",
srcs = ["message_differencer_unittest.cc"],
- copts = COPTS + [
- "-Wno-deprecated-declarations",
- ],
+ copts = COPTS + select({
+ "//build_defs:config_msvc": [],
+ "//conditions:default": [
+ "-Wno-deprecated-declarations",
+ ],
+ }),
deps = [
":differencer",
":message_differencer_unittest_cc_proto",
diff --git a/src/google/protobuf/util/json_format_proto3.proto b/src/google/protobuf/util/json_format_proto3.proto
index f9c5199..4df5eb9 100644
--- a/src/google/protobuf/util/json_format_proto3.proto
+++ b/src/google/protobuf/util/json_format_proto3.proto
@@ -189,6 +189,14 @@
int32 value = 1 [json_name = "@value"];
}
+message TestEvilJson {
+ int32 regular_value = 1 [json_name = "regular_name"];
+ int32 script = 2 [json_name = "</script>"];
+ int32 quotes = 3 [json_name = "unbalanced\"quotes"];
+ int32 script_and_quotes = 4
+ [json_name = "\"<script>alert('hello!);</script>"];
+}
+
message TestExtensions {
.protobuf_unittest.TestAllExtensions extensions = 1;
}
diff --git a/src/google/protobuf/util/json_util.h b/src/google/protobuf/util/json_util.h
index 8fbab5a..6a5a448 100644
--- a/src/google/protobuf/util/json_util.h
+++ b/src/google/protobuf/util/json_util.h
@@ -33,6 +33,7 @@
#ifndef GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
#define GOOGLE_PROTOBUF_UTIL_JSON_UTIL_H__
+
#include <google/protobuf/stubs/bytestream.h>
#include <google/protobuf/stubs/status.h>
#include <google/protobuf/stubs/strutil.h>
diff --git a/src/google/protobuf/util/json_util_test.cc b/src/google/protobuf/util/json_util_test.cc
index 9cb3597..99272d7 100644
--- a/src/google/protobuf/util/json_util_test.cc
+++ b/src/google/protobuf/util/json_util_test.cc
@@ -42,6 +42,7 @@
#include <google/protobuf/struct.pb.h>
#include <google/protobuf/timestamp.pb.h>
#include <google/protobuf/wrappers.pb.h>
+#include <google/protobuf/unittest.pb.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <google/protobuf/stubs/status.h>
@@ -63,6 +64,11 @@
// Must be included last.
#include <google/protobuf/port_def.inc>
+bool IsJson2() {
+ // Pay no attention to the person behind the curtain.
+ return false;
+}
+
namespace google {
namespace protobuf {
namespace util {
@@ -75,6 +81,7 @@
using ::proto3::TestWrapper;
using ::proto_util_converter::testing::MapIn;
using ::testing::ElementsAre;
+using ::testing::Not;
using ::testing::SizeIs;
// TODO(b/234474291): Use the gtest versions once that's available in OSS.
@@ -155,7 +162,7 @@
util::StatusOr<Proto> ToProto(StringPiece json,
JsonParseOptions options = {}) {
Proto proto;
- RETURN_IF_ERROR(JsonStringToMessage(json, &proto, options));
+ RETURN_IF_ERROR(ToProto(proto, json, options));
return proto;
}
@@ -270,13 +277,43 @@
R"("defaultString":"hello","defaultBytes":"d29ybGQ=","defaultNestedEnum":"BAR",)"
R"("defaultForeignEnum":"FOREIGN_BAR","defaultImportEnum":"IMPORT_BAR",)"
R"("defaultStringPiece":"abc","defaultCord":"123"})"));
+
+ // The ESF parser actually gets this wrong, and serializes floats whose
+ // default value is non-finite as 0. We make sure to reproduce this bug.
+ if (IsJson2()) {
+ EXPECT_THAT(
+ ToJson(protobuf_unittest::TestExtremeDefaultValues(), options),
+ IsOkAndHolds(
+ R"({"escapedBytes":"XDAwMFwwMDFcMDA3XDAxMFwwMTRcblxyXHRcMDEzXFxcJ1wiXDM3Ng==")"
+ R"(,"largeUint32":4294967295,"largeUint64":"18446744073709551615",)"
+ R"("smallInt32":-2147483647,"smallInt64":"-9223372036854775807",)"
+ R"("utf8String":"ሴ","zeroFloat":0,"oneFloat":1,"smallFloat":1.5,)"
+ R"("negativeOneFloat":-1,"negativeFloat":-1.5,"largeFloat":2e+08,)"
+ R"("smallNegativeFloat":-8e-28,"infDouble":0,"negInfDouble":0,)"
+ R"("nanDouble":0,"infFloat":0,"negInfFloat":0,"nanFloat":0,)"
+ R"("cppTrigraph":"? ? ?? ?? ??? ??/ ??-","reallySmallInt32":-2147483648)"
+ R"(,"reallySmallInt64":"-9223372036854775808","stringWithZero":"hel\u0000lo")"
+ R"(,"bytesWithZero":"d29yXDAwMGxk","stringPieceWithZero":"ab\u0000c")"
+ R"(,"cordWithZero":"12\u00003","replacementString":"${unknown}"})"));
+ } else {
+ EXPECT_THAT(
+ ToJson(protobuf_unittest::TestExtremeDefaultValues(), options),
+ IsOkAndHolds(
+ R"({"escapedBytes":"XDAwMFwwMDFcMDA3XDAxMFwwMTRcblxyXHRcMDEzXFxcJ1wiXDM3Ng==")"
+ R"(,"largeUint32":4294967295,"largeUint64":"18446744073709551615",)"
+ R"("smallInt32":-2147483647,"smallInt64":"-9223372036854775807")"
+ R"(,"reallySmallInt32":-2147483648,"reallySmallInt64":"-9223372036854775808",)"
+ R"("utf8String":"ሴ","zeroFloat":0,"oneFloat":1,"smallFloat":1.5,)"
+ R"("negativeOneFloat":-1,"negativeFloat":-1.5,"largeFloat":2e+08,)"
+ R"("smallNegativeFloat":-8e-28,"infDouble":0,"negInfDouble":0)"
+ R"(,"nanDouble":0,"infFloat":0,"negInfFloat":0,"nanFloat":0)"
+ R"(,"cppTrigraph":"? ? ?? ?? ??? ??/ ??-","stringWithZero":"hel\u0000lo")"
+ R"(,"bytesWithZero":"d29yXDAwMGxk","stringPieceWithZero":"ab\u0000c")"
+ R"(,"cordWithZero":"12\u00003","replacementString":"${unknown}"})"));
+ }
}
TEST_P(JsonTest, TestPreserveProtoFieldNames) {
- if (GetParam() == Codec::kResolver) {
- GTEST_SKIP();
- }
-
TestMessage m;
m.mutable_message_value();
@@ -286,6 +323,22 @@
}
+TEST_P(JsonTest, Camels) {
+ protobuf_unittest::TestCamelCaseFieldNames m;
+ m.set_stringfield("sTRINGfIELD");
+
+ EXPECT_THAT(ToJson(m), IsOkAndHolds(R"({"StringField":"sTRINGfIELD"})"));
+}
+
+TEST_P(JsonTest, EvilString) {
+ auto m = ToProto<TestMessage>(R"json(
+ {"string_value": ")json"
+ "\n\r\b\f\1\2\3"
+ "\"}");
+ ASSERT_OK(m);
+ EXPECT_EQ(m->string_value(), "\n\r\b\f\1\2\3");
+}
+
TEST_P(JsonTest, TestAlwaysPrintEnumsAsInts) {
TestMessage orig;
orig.set_enum_value(proto3::BAR);
@@ -378,6 +431,7 @@
"repeatedEnumValue": [1, "FOO"],
"repeatedMessageValue": [
{"value": 40},
+ {},
{"value": 96}
]
}
@@ -406,9 +460,10 @@
EXPECT_THAT(m->repeated_string_value(), ElementsAre("foo", "bar ", ""));
EXPECT_THAT(m->repeated_enum_value(), ElementsAre(proto3::BAR, proto3::FOO));
- ASSERT_THAT(m->repeated_message_value(), SizeIs(2));
+ ASSERT_THAT(m->repeated_message_value(), SizeIs(3));
EXPECT_EQ(m->repeated_message_value(0).value(), 40);
- EXPECT_EQ(m->repeated_message_value(1).value(), 96);
+ EXPECT_EQ(m->repeated_message_value(1).value(), 0);
+ EXPECT_EQ(m->repeated_message_value(2).value(), 96);
EXPECT_THAT(
ToJson(*m),
@@ -419,7 +474,7 @@
R"("messageValue":{"value":2048},"repeatedBoolValue":[true],"repeatedInt32Value":[0,-42])"
R"(,"repeatedUint64Value":["1","2"],"repeatedDoubleValue":[1.5,-2],)"
R"("repeatedStringValue":["foo","bar ",""],"repeatedEnumValue":["BAR","FOO"],)"
- R"("repeatedMessageValue":[{"value":40},{"value":96}]})"));
+ R"("repeatedMessageValue":[{"value":40},{},{"value":96}]})"));
}
TEST_P(JsonTest, CurseOfAtob) {
@@ -434,6 +489,15 @@
false, true));
}
+TEST_P(JsonTest, FloatPrecision) {
+ google::protobuf::Value v;
+ v.mutable_list_value()->add_values()->set_number_value(0.9900000095367432);
+ v.mutable_list_value()->add_values()->set_number_value(0.8799999952316284);
+
+ EXPECT_THAT(ToJson(v),
+ IsOkAndHolds("[0.99000000953674316,0.87999999523162842]"));
+}
+
TEST_P(JsonTest, ParseLegacySingleRepeatedField) {
auto m = ToProto<TestMessage>(R"json({
"repeatedInt32Value": 1997,
@@ -721,6 +785,20 @@
}
)json"),
StatusIs(util::StatusCode::kInvalidArgument));
+
+ TestAny m2;
+ m2.mutable_value();
+ EXPECT_THAT(ToJson(m2), IsOkAndHolds(R"({"value":{}})"));
+ m2.mutable_value()->set_value("garbage");
+ // The ESF parser does not return InvalidArgument for this error.
+ EXPECT_THAT(ToJson(m2), Not(StatusIs(util::StatusCode::kOk)));
+
+ m2.Clear();
+ m2.mutable_value()->set_type_url("type.googleapis.com/proto3.TestMessage");
+ EXPECT_THAT(
+ ToJson(m2),
+ IsOkAndHolds(
+ R"({"value":{"@type":"type.googleapis.com/proto3.TestMessage"}})"));
}
TEST_P(JsonTest, TestFlatList) {
@@ -731,6 +809,24 @@
)json");
ASSERT_OK(m);
EXPECT_THAT(m->repeated_int32_value(), ElementsAre(5, 6));
+
+ // The above flatteing behavior is suppressed for google::protobuf::ListValue.
+ auto m2 = ToProto<google::protobuf::Value>(R"json(
+ {
+ "repeatedInt32Value": [[[5]], [6]]
+ }
+ )json");
+ ASSERT_OK(m2);
+ auto fields = m2->struct_value().fields();
+ auto list = fields["repeatedInt32Value"].list_value();
+ EXPECT_EQ(list.values(0)
+ .list_value()
+ .values(0)
+ .list_value()
+ .values(0)
+ .number_value(),
+ 5);
+ EXPECT_EQ(list.values(1).list_value().values(0).number_value(), 6);
}
TEST_P(JsonTest, ParseWrappers) {
@@ -893,6 +989,37 @@
EXPECT_EQ(m.enum_value(), proto3::BAR);
}
+// This functionality is not correctly implemented by the ESF parser, so
+// the test is only turned on when testing json2.
+TEST_P(JsonTest, Extensions) {
+ if (GetParam() == Codec::kResolver || !IsJson2()) {
+ GTEST_SKIP();
+ }
+
+ auto m = ToProto<protobuf_unittest::TestMixedFieldsAndExtensions>(R"json({
+ "[protobuf_unittest.TestMixedFieldsAndExtensions.c]": 42,
+ "a": 5,
+ "b": [1, 2, 3],
+ "[protobuf_unittest.TestMixedFieldsAndExtensions.d]": [1, 1, 2, 3, 5, 8, 13]
+ })json");
+ ASSERT_OK(m);
+ EXPECT_EQ(m->a(), 5);
+ EXPECT_THAT(m->b(), ElementsAre(1, 2, 3));
+ EXPECT_EQ(m->GetExtension(protobuf_unittest::TestMixedFieldsAndExtensions::c),
+ 42);
+ EXPECT_THAT(
+ m->GetRepeatedExtension(protobuf_unittest::TestMixedFieldsAndExtensions::d),
+ ElementsAre(1, 1, 2, 3, 5, 8, 13));
+
+ EXPECT_THAT(
+ ToJson(*m),
+ IsOkAndHolds(
+ R"({"a":5,)"
+ R"("[protobuf_unittest.TestMixedFieldsAndExtensions.c]":42,)"
+ R"("b":[1,2,3],)"
+ R"("[protobuf_unittest.TestMixedFieldsAndExtensions.d]":[1,1,2,3,5,8,13]})"));
+}
+
// Parsing does NOT work like MergeFrom: existing repeated field values are
// clobbered, not appended to.
TEST_P(JsonTest, TestOverwriteRepeated) {
@@ -1065,13 +1192,68 @@
ASSERT_THAT(m2->repeated_value(), SizeIs(1));
EXPECT_TRUE(m2->repeated_value(0).has_null_value());
+
+ m2->Clear();
+ m2->mutable_repeated_value(); // Materialize an empty singular Value.
+ m2->add_repeated_value();
+ m2->add_repeated_value()->set_string_value("solitude");
+ m2->add_repeated_value();
+ EXPECT_THAT(ToJson(*m2), IsOkAndHolds(R"({"repeatedValue":["solitude"]})"));
}
-TEST_P(JsonTest, DISABLED_HtmlEscape) {
+TEST_P(JsonTest, ListList) {
+ auto m = ToProto<proto3::TestListValue>(R"json({
+ "repeated_value": [["ayy", "lmao"]]
+ })json");
+ ASSERT_OK(m);
+
+ EXPECT_EQ(m->repeated_value(0).values(0).string_value(), "ayy");
+ EXPECT_EQ(m->repeated_value(0).values(1).string_value(), "lmao");
+
+ m = ToProto<proto3::TestListValue>(R"json({
+ "repeated_value": [{
+ "values": ["ayy", "lmao"]
+ }]
+ })json");
+ ASSERT_OK(m);
+
+ EXPECT_EQ(m->repeated_value(0).values(0).string_value(), "ayy");
+ EXPECT_EQ(m->repeated_value(0).values(1).string_value(), "lmao");
+}
+
+TEST_P(JsonTest, HtmlEscape) {
TestMessage m;
m.set_string_value("</script>");
EXPECT_THAT(ToJson(m),
- IsOkAndHolds("{\"stringValue\":\"\\u003c/script\\u003e\"}"));
+ IsOkAndHolds(R"({"stringValue":"\u003c/script\u003e"})"));
+
+ proto3::TestEvilJson m2;
+ JsonPrintOptions opts;
+ opts.always_print_primitive_fields = true;
+ EXPECT_THAT(
+ ToJson(m2, opts),
+ IsOkAndHolds(
+ R"({"regular_name":0,"\u003c/script\u003e":0,)"
+ R"("unbalanced\"quotes":0,)"
+ R"("\"\u003cscript\u003ealert('hello!);\u003c/script\u003e":0})"));
+}
+
+TEST_P(JsonTest, FieldOrder) {
+ // $ protoscope -s <<< "3: 3 22: 2 1: 1 22: 2"
+ std::string out;
+ util::Status s = BinaryToJsonString(
+ resolver_.get(), "type.googleapis.com/proto3.TestMessage",
+ "\x18\x03\xb0\x01\x02\x08\x01\xb0\x01\x02", &out);
+ ASSERT_OK(s);
+ if (IsJson2()) {
+ EXPECT_EQ(
+ out,
+ R"({"boolValue":true,"int64Value":"3","repeatedInt32Value":[2,2]})");
+ } else {
+ EXPECT_EQ(
+ out,
+ R"({"int64Value":"3","repeatedInt32Value":[2],"boolValue":true,"repeatedInt32Value":[2]})");
+ }
}
} // namespace
diff --git a/src/google/protobuf/util/message_differencer.h b/src/google/protobuf/util/message_differencer.h
index 4df3152..c8291c1 100644
--- a/src/google/protobuf/util/message_differencer.h
+++ b/src/google/protobuf/util/message_differencer.h
@@ -242,17 +242,17 @@
// Reports that a field has been added into Message2.
virtual void ReportAdded(const Message& message1, const Message& message2,
- const std::vector<SpecificField>& field_path) = 0;
+ const std::vector<SpecificField>& field_path) {}
// Reports that a field has been deleted from Message1.
virtual void ReportDeleted(
const Message& message1, const Message& message2,
- const std::vector<SpecificField>& field_path) = 0;
+ const std::vector<SpecificField>& field_path) {}
// Reports that the value of a field has been modified.
virtual void ReportModified(
const Message& message1, const Message& message2,
- const std::vector<SpecificField>& field_path) = 0;
+ const std::vector<SpecificField>& field_path) {}
// Reports that a repeated field has been moved to another location. This
// only applies when using TreatAsSet or TreatAsMap() -- see below. Also
diff --git a/src/google/protobuf/wire_format_lite.h b/src/google/protobuf/wire_format_lite.h
index a7e64bf..8f38f7c 100644
--- a/src/google/protobuf/wire_format_lite.h
+++ b/src/google/protobuf/wire_format_lite.h
@@ -53,7 +53,6 @@
#include <google/protobuf/message_lite.h>
#include <google/protobuf/repeated_field.h>
-// Do UTF-8 validation on string type in Debug build only
#ifndef NDEBUG
#define GOOGLE_PROTOBUF_UTF8_VALIDATION_ENABLED
#endif
diff --git a/src/google/protobuf/wrappers.pb.h b/src/google/protobuf/wrappers.pb.h
index 72304a5..f629e5e 100644
--- a/src/google/protobuf/wrappers.pb.h
+++ b/src/google/protobuf/wrappers.pb.h
@@ -13,7 +13,7 @@
#error incompatible with your Protocol Buffer headers. Please update
#error your headers.
#endif
-#if 3021002 < PROTOBUF_MIN_PROTOC_VERSION
+#if 3021004 < PROTOBUF_MIN_PROTOC_VERSION
#error This file was generated by an older version of protoc which is
#error incompatible with your Protocol Buffer headers. Please
#error regenerate this file with a newer version of protoc.
diff --git a/tests.sh b/tests.sh
index f2b481c..6508674 100755
--- a/tests.sh
+++ b/tests.sh
@@ -45,44 +45,6 @@
PPROF_PATH=/usr/bin/google-pprof HEAPCHECK=strict ./protobuf-test
}
-build_cpp_distcheck() {
- grep -q -- "-Og" src/Makefile.am &&
- echo "The -Og flag is incompatible with Clang versions older than 4.0." &&
- exit 1
-
- # Initialize any submodules.
- git submodule update --init --recursive
- ./autogen.sh
- ./configure
- make dist
-
- # List all files that should be included in the distribution package.
- git ls-files | grep "^\(java\|python\|objectivec\|csharp\|ruby\|php\|cmake\|examples\|src/google/protobuf/.*\.proto\)" |\
- grep -v ".gitignore" | grep -v "java/lite/proguard.pgcfg" |\
- grep -v "python/compatibility_tests" | grep -v "python/docs" | grep -v "python/.repo-metadata.json" |\
- grep -v "python/protobuf_distutils" | grep -v "csharp/compatibility_tests" > dist.lst
- # Unzip the dist tar file.
- DIST=`ls *.tar.gz`
- tar -xf $DIST
- cd ${DIST//.tar.gz}
- # Check if every file exists in the dist tar file.
- FILES_MISSING=""
- for FILE in $(<../dist.lst); do
- [ -f "$FILE" ] || {
- echo "$FILE is not found!"
- FILES_MISSING="$FILE $FILES_MISSING"
- }
- done
- cd ..
- if [ ! -z "$FILES_MISSING" ]; then
- echo "Missing files in EXTRA_DIST: $FILES_MISSING"
- exit 1
- fi
-
- # Do the regular dist-check for C++.
- make distcheck -j$(nproc)
-}
-
build_dist_install() {
# Create a symlink pointing to python2 and put it at the beginning of $PATH.
# This is necessary because the googletest build system involves a Python
@@ -490,47 +452,8 @@
test_php_c
}
-build_php7.0_mac() {
+build_php_mac() {
internal_build_cpp
- # Install PHP
- curl -s https://php-osx.liip.ch/install.sh | bash -s 7.0
- PHP_FOLDER=`find /usr/local -type d -name "php5-7.0*"` # The folder name may change upon time
- test ! -z "$PHP_FOLDER"
- export PATH="$PHP_FOLDER/bin:$PATH"
-
- # Install Composer
- wget https://getcomposer.org/download/2.0.13/composer.phar --progress=dot:mega -O /usr/local/bin/composer
- chmod a+x /usr/local/bin/composer
-
- # Install valgrind
- echo "#! /bin/bash" > valgrind
- chmod ug+x valgrind
- sudo mv valgrind /usr/local/bin/valgrind
-
- # Test
- test_php_c
-}
-
-build_php7.3_mac() {
- internal_build_cpp
- # Install PHP
- # We can't test PHP 7.4 with these binaries yet:
- # https://github.com/liip/php-osx/issues/276
- curl -s https://php-osx.liip.ch/install.sh | bash -s 7.3
- PHP_FOLDER=`find /usr/local -type d -name "php5-7.3*"` # The folder name may change upon time
- test ! -z "$PHP_FOLDER"
- export PATH="$PHP_FOLDER/bin:$PATH"
-
- # Install Composer
- wget https://getcomposer.org/download/2.0.13/composer.phar --progress=dot:mega -O /usr/local/bin/composer
- chmod a+x /usr/local/bin/composer
-
- # Install valgrind
- echo "#! /bin/bash" > valgrind
- chmod ug+x valgrind
- sudo mv valgrind /usr/local/bin/valgrind
-
- # Test
test_php_c
}
@@ -578,7 +501,6 @@
if [ "$#" -ne 1 ]; then
echo "
Usage: $0 { cpp |
- cpp_distcheck |
csharp |
java_jdk7 |
java_oracle7 |
diff --git a/version.json b/version.json
index e50d633..4244127 100644
--- a/version.json
+++ b/version.json
@@ -1,17 +1,17 @@
{
"main": {
- "protoc_version": "21-dev",
+ "protoc_version": "22-dev",
"lts": false,
- "date": "2022-04-22",
+ "date": "2022-07-21",
"languages": {
- "cpp": "3.21-dev",
- "csharp": "3.21-dev",
- "java": "3.21-dev",
- "javascript": "3.21-dev",
- "objectivec": "3.21-dev",
- "php": "3.21-dev",
- "python": "4.21-dev",
- "ruby": "3.21-dev"
+ "cpp": "3.22-dev",
+ "csharp": "3.22-dev",
+ "java": "3.22-dev",
+ "javascript": "3.22-dev",
+ "objectivec": "3.22-dev",
+ "php": "3.22-dev",
+ "python": "4.22-dev",
+ "ruby": "3.22-dev"
}
}
}