Automated rollback of commit cdce70d4e5.

PiperOrigin-RevId: 830414655
This commit is contained in:
Protobuf Team Bot 2025-11-10 06:29:04 -08:00 committed by Copybara-Service
parent c657b59bf6
commit efacad9581
29 changed files with 177 additions and 975 deletions

View file

@ -30,8 +30,6 @@ jobs:
config:
- { name: "Fastbuild" }
- { name: "Optimized", flags: "-c opt", continuous-only: true }
- { name: "GCC Optimized", flags: "-c opt --force_pic --java_runtime_version=remotejdk_11 --copt=\"-Wno-error=maybe-uninitialized\"", image: "us-docker.pkg.dev/protobuf-build/containers/test/linux/gcc:8.0.1-12.2-12e21b8dda91028bc14212a3ab582c7c4d149fac" }
- { name: "GCC Static", flags: "-c opt --dynamic_mode=off --java_runtime_version=remotejdk_11 --copt=\"-Wno-error=maybe-uninitialized\"", image: "us-docker.pkg.dev/protobuf-build/containers/test/linux/gcc:8.0.1-12.2-12e21b8dda91028bc14212a3ab582c7c4d149fac", continuous-only: true }
- { name: "ASAN", flags: "--config=asan -c dbg", exclude-targets: "-//benchmarks:benchmark -//python/...", runner: ubuntu-22-4core }
- { name: "UBSAN", flags: "--config=ubsan -c dbg", exclude-targets: "-//benchmarks:benchmark -//python/... -//lua/...", continuous-only: true }
- { name: "32-bit", flags: "--copt=-m32 --linkopt=-m32", exclude-targets: "-//benchmarks:benchmark -//python/..." }
@ -51,12 +49,33 @@ jobs:
if: ${{ !matrix.config.continuous-only || inputs.continuous-run }}
uses: protocolbuffers/protobuf-ci/bazel-docker@v5
with:
image: ${{ matrix.config.image || 'us-docker.pkg.dev/protobuf-build/containers/test/linux/sanitize:8.0.1-12e21b8dda91028bc14212a3ab582c7c4d149fac' }}
image: us-docker.pkg.dev/protobuf-build/containers/test/linux/sanitize:${{ matrix.config.bazel_version || '8.0.1' }}-12e21b8dda91028bc14212a3ab582c7c4d149fac
credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
bazel-cache: upb-bazel
bazel: test //bazel/... //benchmarks/... //lua/... //python/... //upb/... //upb_generator/... ${{ matrix.config.flags }}
exclude-targets: ${{ matrix.config.exclude-targets }}
linux-gcc:
strategy:
fail-fast: false # Don't cancel all jobs if one fails.
name: GCC Optimized
runs-on: ubuntu-latest
steps:
- name: Checkout pending changes
uses: protocolbuffers/protobuf-ci/checkout@v5
with:
ref: ${{ inputs.safe-checkout }}
- name: Run tests
uses: protocolbuffers/protobuf-ci/bazel-docker@v5
with:
image: "us-docker.pkg.dev/protobuf-build/containers/test/linux/gcc:8.0.1-12.2-12e21b8dda91028bc14212a3ab582c7c4d149fac"
credentials: ${{ secrets.GAR_SERVICE_ACCOUNT }}
bazel-cache: "upb-bazel-gcc"
bazel: >-
test -c opt
--copt="-Wno-error=maybe-uninitialized" --java_runtime_version=remotejdk_11
//bazel/... //benchmarks/... //lua/... //python/... //upb/... //upb_generator/...
windows:
strategy:
fail-fast: false # Don't cancel all jobs if one fails.

View file

@ -185,14 +185,12 @@ upb/message/value.h
upb/mini_descriptor/build_enum.h
upb/mini_descriptor/decode.h
upb/mini_descriptor/link.h
upb/mini_table/compat.h
upb/mini_table/debug_string.h
upb/mini_table/enum.h
upb/mini_table/extension.h
upb/mini_table/extension_registry.h
upb/mini_table/field.h
upb/mini_table/file.h
upb/mini_table/generated_registry.h
upb/mini_table/message.h
upb/mini_table/sub.h
upb/port/atomic.h

View file

@ -249,7 +249,6 @@ cc_dist_library(
"//upb/json",
"//upb/message:compare",
"//upb/message:copy",
"//upb/mini_table:compat",
"//upb/mini_table:debug_string",
"//upb/text",
"//upb/text:debug",

View file

@ -98,7 +98,6 @@ cc_library(
"//upb/message:internal",
"//upb/mini_descriptor",
"//upb/mini_table",
"//upb/mini_table:internal",
"//upb/wire",
] + select({
":fasttable_enabled_setting": [
@ -309,13 +308,9 @@ filegroup(
filegroup(
name = "test_srcs",
srcs = [
"//upb/hash:test_srcs",
"//upb/json:test_srcs",
"//upb/lex:test_srcs",
"//upb/mem:test_srcs",
"//upb/message:test_srcs",
"//upb/mini_descriptor:test_srcs",
"//upb/mini_table:test_srcs",
"//upb/test:test_srcs",
"//upb/util:test_srcs",
"//upb/wire:test_srcs",

View file

@ -36,7 +36,6 @@
#include "upb/mini_table/extension_registry.h"
#include "upb/mini_table/field.h"
#include "upb/mini_table/file.h"
#include "upb/mini_table/internal/generated_registry.h"
#include "upb/mini_table/message.h"
#include "upb/mini_table/sub.h"
#include "upb/wire/decode.h"

View file

@ -65,5 +65,5 @@ filegroup(
"**/*test.cc",
],
),
visibility = ["//upb:__pkg__"],
visibility = ["//pkg:__pkg__"],
)

View file

@ -72,5 +72,5 @@ filegroup(
"**/*test.cc",
],
),
visibility = ["//upb:__pkg__"],
visibility = ["//pkg:__pkg__"],
)

View file

@ -105,5 +105,5 @@ filegroup(
"**/*test.cc",
],
),
visibility = ["//upb:__pkg__"],
visibility = ["//pkg:__pkg__"],
)

View file

@ -28,7 +28,6 @@ cc_library(
name = "mini_table",
srcs = [
"extension_registry.c",
"generated_registry.c",
"message.c",
],
hdrs = [
@ -37,7 +36,6 @@ cc_library(
"extension_registry.h",
"field.h",
"file.h",
"generated_registry.h",
"message.h",
"sub.h",
],
@ -86,7 +84,6 @@ cc_library(
"internal/extension.h",
"internal/field.h",
"internal/file.h",
"internal/generated_registry.h",
"internal/message.h",
"internal/size_log2.h",
"internal/sub.h",
@ -95,6 +92,7 @@ cc_library(
visibility = ["//visibility:public"],
deps = [
"//upb/base",
"//upb/hash",
"//upb/mem",
"//upb/message:types",
"//upb/port",
@ -133,21 +131,6 @@ cc_test(
],
)
cc_test(
name = "generated_registry_test",
srcs = ["generated_registry_test.cc"],
deps = [
":mini_table",
"//src/google/protobuf:descriptor_upb_minitable_proto",
"//upb/test:custom_options_upb_minitable_proto",
"//upb/test:editions_test_upb_minitable_proto",
"//upb/test:test_multiple_files_upb_minitable_proto",
"@abseil-cpp//absl/synchronization",
"@googletest//:gtest",
"@googletest//:gtest_main",
],
)
proto_library(
name = "message_benchmark_proto",
testonly = 1,

View file

@ -15,8 +15,6 @@
#include "upb/hash/str_table.h"
#include "upb/mem/arena.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/generated_registry.h"
#include "upb/mini_table/internal/generated_registry.h"
#include "upb/mini_table/message.h"
// Must be last.
@ -91,46 +89,25 @@ failure:
return status;
}
const UPB_PRIVATE(upb_GeneratedExtensionListEntry) *
UPB_PRIVATE(upb_generated_extension_list) = NULL;
#ifdef UPB_LINKARR_DECLARE
UPB_LINKARR_DECLARE(upb_AllExts, const upb_MiniTableExtension);
bool upb_ExtensionRegistry_AddAllLinkedExtensions(upb_ExtensionRegistry* r) {
const UPB_PRIVATE(upb_GeneratedExtensionListEntry)* entry =
UPB_PRIVATE(upb_generated_extension_list);
while (entry != NULL) {
// Comparing pointers to different objects is undefined behavior, so we
// convert them to uintptr_t and compare their values.
uintptr_t begin = (uintptr_t)entry->start;
uintptr_t end = (uintptr_t)entry->stop;
uintptr_t current = begin;
while (current < end) {
const upb_MiniTableExtension* ext =
(const upb_MiniTableExtension*)current;
// Sentinels and padding introduced by the linker can result in zeroed
// entries, so simply skip them.
if (upb_MiniTableExtension_Number(ext) == 0) {
// MSVC introduces padding that might not be sized exactly the same as
// upb_MiniTableExtension, so we can't iterate by sizeof. This is a
// valid thing for any linker to do, so it's safer to just always do it.
current += UPB_ALIGN_OF(upb_MiniTableExtension);
continue;
}
if (upb_ExtensionRegistry_Add(r, ext) !=
kUpb_ExtensionRegistryStatus_Ok) {
const upb_MiniTableExtension* start = UPB_LINKARR_START(upb_AllExts);
const upb_MiniTableExtension* stop = UPB_LINKARR_STOP(upb_AllExts);
for (const upb_MiniTableExtension* p = start; p < stop; p++) {
// Windows can introduce zero padding, so we have to skip zeroes.
if (upb_MiniTableExtension_Number(p) != 0) {
if (upb_ExtensionRegistry_Add(r, p) != kUpb_ExtensionRegistryStatus_Ok) {
return false;
}
current += sizeof(upb_MiniTableExtension);
}
entry = entry->next;
}
return true;
}
const upb_ExtensionRegistry* upb_ExtensionRegistry_GetGenerated(
const upb_GeneratedRegistryRef* genreg) {
return genreg != NULL ? genreg->registry : NULL;
}
#endif // UPB_LINKARR_DECLARE
const upb_MiniTableExtension* upb_ExtensionRegistry_Lookup(
const upb_ExtensionRegistry* r, const upb_MiniTable* t, uint32_t num) {

View file

@ -13,7 +13,6 @@
#include "upb/mem/arena.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/generated_registry.h"
#include "upb/mini_table/message.h"
// Must be last.
@ -80,6 +79,8 @@ UPB_API upb_ExtensionRegistryStatus upb_ExtensionRegistry_Add(
upb_ExtensionRegistryStatus upb_ExtensionRegistry_AddArray(
upb_ExtensionRegistry* r, const upb_MiniTableExtension** e, size_t count);
#ifdef UPB_LINKARR_DECLARE
// Adds all extensions linked into the binary into the registry. The set of
// linked extensions is assembled by the linker using linker arrays. This
// will likely not work properly if the extensions are split across multiple
@ -93,14 +94,7 @@ upb_ExtensionRegistryStatus upb_ExtensionRegistry_AddArray(
UPB_API bool upb_ExtensionRegistry_AddAllLinkedExtensions(
upb_ExtensionRegistry* r);
// Returns the extension registry contained by a reference to the generated
// registry. The reference must be held for the lifetime of the registry.
//
// TODO This should actually be moved to generated_registry.h, but
// can't because of the current location of
// upb_ExtensionRegistry_AddAllLinkedExtensions.
UPB_API const upb_ExtensionRegistry* upb_ExtensionRegistry_GetGenerated(
const upb_GeneratedRegistryRef* genreg);
#endif // UPB_LINKARR_DECLARE
// Looks up the extension (if any) defined for message type |t| and field
// number |num|. Returns the extension if found, otherwise NULL.

View file

@ -1,140 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "upb/mini_table/generated_registry.h"
#include <stdint.h>
#include "upb/mem/alloc.h"
#include "upb/mem/arena.h"
#include "upb/mini_table/extension_registry.h"
#include "upb/mini_table/internal/generated_registry.h" // IWYU pragma: keep
#include "upb/port/atomic.h"
// Must be last.
#include "upb/port/def.inc"
#if UPB_TSAN
#include <sched.h>
#endif // UPB_TSAN
typedef struct upb_GeneratedRegistry {
UPB_ATOMIC(upb_GeneratedRegistryRef*) ref;
UPB_ATOMIC(int32_t) ref_count;
} upb_GeneratedRegistry;
static upb_GeneratedRegistry* _upb_generated_registry(void) {
static upb_GeneratedRegistry r = {NULL, 0};
return &r;
}
// Constructs a new GeneratedRegistryRef, adding all linked extensions to the
// registry or returning NULL on failure.
static upb_GeneratedRegistryRef* _upb_GeneratedRegistry_New(void) {
upb_Arena* arena = NULL;
upb_ExtensionRegistry* extreg = NULL;
upb_GeneratedRegistryRef* ref = upb_gmalloc(sizeof(upb_GeneratedRegistryRef));
if (ref == NULL) goto err;
arena = upb_Arena_New();
if (arena == NULL) goto err;
extreg = upb_ExtensionRegistry_New(arena);
if (extreg == NULL) goto err;
ref->arena = arena;
ref->registry = extreg;
if (!upb_ExtensionRegistry_AddAllLinkedExtensions(extreg)) goto err;
return ref;
err:
if (arena != NULL) upb_Arena_Free(arena);
if (ref != NULL) upb_gfree(ref);
return NULL;
}
const upb_GeneratedRegistryRef* upb_GeneratedRegistry_Load(void) {
upb_GeneratedRegistry* registry = _upb_generated_registry();
// Loop until we successfully acquire a reference. This loop should only
// kick in under extremely high contention, and it should be guaranteed to
// succeed.
while (true) {
int32_t count = upb_Atomic_Load(&registry->ref_count, memory_order_acquire);
// Try to increment the refcount, but only if it's not zero.
while (count > 0) {
if (upb_Atomic_CompareExchangeStrong(&registry->ref_count, &count,
count + 1, memory_order_acquire,
memory_order_relaxed)) {
// Successfully incremented. We can now safely load and return the
// pointer.
const upb_GeneratedRegistryRef* ref =
upb_Atomic_Load(&registry->ref, memory_order_acquire);
UPB_ASSERT(ref != NULL);
return ref;
}
// CAS failed, `count` was updated. Loop will retry.
}
// If we're here, the count was 0. Time for the slow path.
// Double-check that the pointer is NULL before trying to create.
upb_GeneratedRegistryRef* ref =
upb_Atomic_Load(&registry->ref, memory_order_acquire);
if (ref == NULL) {
// Pointer is NULL, try to create and publish a new registry.
upb_GeneratedRegistryRef* new_ref = _upb_GeneratedRegistry_New();
if (new_ref == NULL) return NULL; // OOM
// Try to CAS the pointer from NULL to our new_ref.
if (upb_Atomic_CompareExchangeStrong(&registry->ref, &ref, new_ref,
memory_order_release,
memory_order_acquire)) {
// We won the race. Set the ref count to 1.
upb_Atomic_Store(&registry->ref_count, 1, memory_order_release);
return new_ref;
} else {
// We lost the race. `ref` now holds the pointer from the winning
// thread. Clean up our unused one and loop to try again to get a
// reference.
upb_Arena_Free(new_ref->arena);
upb_gfree(new_ref);
}
}
// If we are here, either we lost the CAS race, or the pointer was already
// non-NULL. In either case, we loop to the top and try to increment the
// refcount of the existing object.
#if UPB_TSAN
// Yield to give other threads a chance to increment the refcount. This is
// especially an issue for TSAN builds, which are prone to locking up from
// the thread with the upb_Atomic_Store call above getting starved.
sched_yield();
#endif // UPB_TSAN
}
}
void upb_GeneratedRegistry_Release(const upb_GeneratedRegistryRef* r) {
if (r == NULL) return;
upb_GeneratedRegistry* registry = _upb_generated_registry();
int ref_count = upb_Atomic_Sub(&registry->ref_count, 1, memory_order_acq_rel);
UPB_ASSERT(registry->ref_count >= 0);
// A ref_count of 1 means that we decremented the refcount to 0.
if (ref_count == 1) {
upb_GeneratedRegistryRef* ref =
upb_Atomic_Exchange(&registry->ref, NULL, memory_order_acq_rel);
if (ref != NULL) {
// This is the last reference and we won any potential race to store NULL,
// so we need to clean up.
upb_Arena_Free(ref->arena);
upb_gfree(ref);
}
}
}

View file

@ -1,56 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef UPB_MINI_TABLE_GENERATED_REGISTRY_H_
#define UPB_MINI_TABLE_GENERATED_REGISTRY_H_
// Must be last.
#include "upb/port/def.inc"
#ifdef __cplusplus
extern "C" {
#endif
/* Generated registry: a global singleton that gathers all extensions linked
* into the binary.
*
* This singleton is thread-safe and lock-free, implemented using atomics. The
* registry is lazily initialized the first time it is loaded. When all
* references are released, the registry will be destroyed. New loads
* afterwards will simply reload the same registry as needed.
*
* The extension minitables are registered in gencode using linker arrays. Each
* .proto file produces a weak, hidden, constructor function that adds all
* visible extensions from the array into the registry. In each binary, only
* one copy of the constructor will actually be preserved by the linker, and
* that copy will add all of the extensions for the entire binary. All of these
* are added to a global linked list of minitables pre-main, which are then used
* to construct this singleton as needed.
*/
typedef struct upb_GeneratedRegistryRef upb_GeneratedRegistryRef;
// Loads the generated registry, returning a reference to it. The reference
// must be held for the lifetime of any ExtensionRegistry obtained from it.
//
// Returns NULL on failure.
UPB_API const upb_GeneratedRegistryRef* upb_GeneratedRegistry_Load(void);
// Releases a reference to the generated registry. This may destroy the
// registry if there are no other references to it.
//
// NULL is a valid argument and is simply ignored for easier error handling in
// callers.
UPB_API void upb_GeneratedRegistry_Release(const upb_GeneratedRegistryRef* r);
#ifdef __cplusplus
} /* extern "C" */
#endif
#include "upb/port/undef.inc"
#endif // UPB_MINI_TABLE_GENERATED_REGISTRY_H_

View file

@ -1,186 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "upb/mini_table/generated_registry.h"
#include <array>
#include <thread> // NOLINT
#include <vector>
#include "google/protobuf/descriptor.upb_minitable.h"
#include <gtest/gtest.h>
#include "absl/synchronization/barrier.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/extension_registry.h"
#include "upb/test/custom_options.upb_minitable.h"
#include "upb/test/editions_test.upb_minitable.h"
#include "upb/test/test_multiple_files.upb_minitable.h"
#include "upb/test/test_multiple_files2.upb_minitable.h"
namespace {
class GeneratedRegistryTest : public ::testing::Test {
protected:
void SetUp() override {
ref_ = upb_GeneratedRegistry_Load();
ASSERT_NE(ref_, nullptr);
reg_ = upb_ExtensionRegistry_GetGenerated(ref_);
ASSERT_NE(reg_, nullptr);
}
void TearDown() override { upb_GeneratedRegistry_Release(ref_); }
const upb_GeneratedRegistryRef* ref_ = nullptr;
const upb_ExtensionRegistry* reg_ = nullptr;
};
TEST_F(GeneratedRegistryTest, LocalExtensionExists) {
// Test an arbitrary extension defined in the same .proto file that we expect
// to be linked in.
// Force linkage and sanity check extension numbers.
ASSERT_EQ(upb_MiniTableExtension_Number(&upb_test_2023_ext_ext), 100);
EXPECT_NE(upb_ExtensionRegistry_Lookup(
reg_, &upb__test_02023__EditionsMessage_msg_init, 100),
nullptr);
}
TEST_F(GeneratedRegistryTest, ForeignExtensionExists) {
// Test an arbitrary extension defined by an import that we expect to be
// linked in.
// Force linkage and sanity check extension numbers.
ASSERT_EQ(upb_MiniTableExtension_Number(&upb_message_opt_ext), 7739036);
EXPECT_NE(upb_ExtensionRegistry_Lookup(reg_, &google__protobuf__MessageOptions_msg_init,
7739036),
nullptr);
}
TEST_F(GeneratedRegistryTest, UnlinkedExtensionDoesNotExist) {
// Test an arbitrary extension defined by an option import that we do not
// expect to be linked in.
// Corresponds to upb.message_opt_unlinked in custom_options_unlinked.proto.
EXPECT_EQ(upb_ExtensionRegistry_Lookup(reg_, &google__protobuf__MessageOptions_msg_init,
7739037),
nullptr);
}
TEST_F(GeneratedRegistryTest, MultipleFilesExtensionExists) {
// Test that multiple .proto files from the same proto_library can be linked
// in and registered.
// Force linkage and sanity check extension numbers.
ASSERT_EQ(upb_MiniTableExtension_Number(&upb_multiple_files_ext1_ext), 100);
ASSERT_EQ(upb_MiniTableExtension_Number(&upb_multiple_files_ext2_ext), 100);
EXPECT_NE(upb_ExtensionRegistry_Lookup(
reg_, &upb__MultipleFilesMessage1_msg_init, 100),
nullptr);
EXPECT_NE(upb_ExtensionRegistry_Lookup(
reg_, &upb__MultipleFilesMessage2_msg_init, 100),
nullptr);
}
TEST_F(GeneratedRegistryTest, ReleaseOnError) {
upb_GeneratedRegistry_Release(nullptr);
}
// On 32-bit systems, the stack size is smaller, so we can't run too many
// concurrent threads without overflowing the stack.
constexpr int kRaceIterations = sizeof(void*) < 8 ? 100 : 2000;
TEST(GeneratedRegistryRaceTest, Load) {
absl::Barrier barrier(kRaceIterations);
std::vector<std::thread> threads;
std::array<const upb_GeneratedRegistryRef*, kRaceIterations> refs;
for (int i = 0; i < kRaceIterations; ++i) {
threads.push_back(std::thread([&, i]() {
barrier.Block();
const upb_GeneratedRegistryRef* ref = upb_GeneratedRegistry_Load();
EXPECT_NE(upb_ExtensionRegistry_GetGenerated(ref), nullptr);
refs[i] = ref;
}));
}
for (auto& t : threads) t.join();
for (int i = 0; i < kRaceIterations; ++i) {
EXPECT_NE(refs[i], nullptr);
upb_GeneratedRegistry_Release(refs[i]);
}
}
TEST(GeneratedRegistryRaceTest, Release) {
absl::Barrier barrier(kRaceIterations);
std::vector<std::thread> threads;
std::array<const upb_GeneratedRegistryRef*, kRaceIterations> refs;
for (int i = 0; i < kRaceIterations; ++i) {
refs[i] = upb_GeneratedRegistry_Load();
ASSERT_NE(refs[i], nullptr);
}
for (int i = 0; i < kRaceIterations; ++i) {
threads.push_back(std::thread([&, i]() {
EXPECT_NE(upb_ExtensionRegistry_GetGenerated(refs[i]), nullptr);
barrier.Block();
upb_GeneratedRegistry_Release(refs[i]);
}));
}
for (auto& t : threads) t.join();
}
TEST(GeneratedRegistryRaceTest, LoadRelease) {
absl::Barrier barrier(kRaceIterations);
std::vector<std::thread> threads;
for (int i = 0; i < kRaceIterations; ++i) {
threads.push_back(std::thread([&]() {
barrier.Block();
const upb_GeneratedRegistryRef* ref = upb_GeneratedRegistry_Load();
EXPECT_NE(upb_ExtensionRegistry_GetGenerated(ref), nullptr);
upb_GeneratedRegistry_Release(ref);
}));
}
for (auto& t : threads) t.join();
}
TEST(GeneratedRegistryRaceTest, ReleaseLastAndLoadMultiple) {
for (int i = 0; i < kRaceIterations; ++i) {
// Load one reference. The registry is now alive with ref_count == 1.
const upb_GeneratedRegistryRef* ref = upb_GeneratedRegistry_Load();
ASSERT_NE(ref, nullptr);
constexpr int kNumLoaders = 4;
absl::Barrier barrier(kNumLoaders + 1);
std::vector<std::thread> threads;
threads.push_back(std::thread([&]() {
barrier.Block();
// This release should bring the ref_count to 0 and trigger cleanup.
upb_GeneratedRegistry_Release(ref);
}));
for (int j = 0; j < kNumLoaders; ++j) {
threads.push_back(std::thread([&]() {
barrier.Block();
const upb_GeneratedRegistryRef* ref2 = upb_GeneratedRegistry_Load();
EXPECT_NE(upb_ExtensionRegistry_GetGenerated(ref2), nullptr);
upb_GeneratedRegistry_Release(ref2);
}));
}
for (auto& t : threads) t.join();
}
}
} // namespace

View file

@ -81,7 +81,7 @@ UPB_API_INLINE bool upb_MiniTableExtension_SetSubEnum(
return true;
}
UPB_API_INLINE const struct upb_MiniTableField* upb_MiniTableExtension_ToField(
UPB_API_INLINE const upb_MiniTableField* upb_MiniTableExtension_ToField(
const struct upb_MiniTableExtension* e) {
return &e->UPB_PRIVATE(field);
}

View file

@ -1,37 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#ifndef UPB_MINI_TABLE_INTERNAL_GENERATED_EXTENSION_REGISTRY_H_
#define UPB_MINI_TABLE_INTERNAL_GENERATED_EXTENSION_REGISTRY_H_
#include "upb/mini_table/internal/extension.h"
// Must be last.
#include "upb/port/def.inc"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct UPB_PRIVATE(upb_GeneratedExtensionListEntry) {
const struct upb_MiniTableExtension* start;
const struct upb_MiniTableExtension* stop;
const struct UPB_PRIVATE(upb_GeneratedExtensionListEntry) * next;
} UPB_PRIVATE(upb_GeneratedExtensionListEntry);
struct upb_GeneratedRegistryRef {
struct upb_Arena* arena;
const struct upb_ExtensionRegistry* registry;
};
#ifdef __cplusplus
} /* extern "C" */
#endif
#include "upb/port/undef.inc"
#endif /* UPB_MINI_TABLE_INTERNAL_GENERATED_EXTENSION_REGISTRY_H_ */

View file

@ -31,10 +31,6 @@
failure_order) \
atomic_compare_exchange_weak_explicit(addr, expected, desired, \
success_order, failure_order)
#define upb_Atomic_Add(addr, val, order) \
atomic_fetch_add_explicit(addr, val, order)
#define upb_Atomic_Sub(addr, val, order) \
atomic_fetch_sub_explicit(addr, val, order)
#elif defined(UPB_USE_MSC_ATOMICS)
#include <intrin.h>
@ -43,41 +39,49 @@
#define upb_Atomic_Init(addr, val) (*(addr) = val)
#pragma intrinsic(_InterlockedExchange)
static int32_t upb_Atomic_LoadMsc32(int32_t volatile* addr) {
// Compare exchange with an unlikely value reduces the risk of a spurious
// (but harmless) store
return _InterlockedCompareExchange(addr, 0xDEADC0DE, 0xDEADC0DE);
}
#pragma intrinsic(_InterlockedCompareExchange)
static bool upb_Atomic_CompareExchangeMscP32(int32_t volatile* addr,
int32_t* expected,
int32_t desired) {
int32_t expect_val = *expected;
int32_t actual_val = _InterlockedCompareExchange(addr, desired, expect_val);
if (expect_val != actual_val) {
*expected = actual_val;
return false;
}
return true;
}
#if defined(_WIN64)
// MSVC, without C11 atomics, does not have any way in pure C to force
// load-acquire store-release behavior, so we hack it with exchanges.
#pragma intrinsic(_InterlockedExchange64)
#define upb_Atomic_Store(addr, val, order) \
(void)_InterlockedExchange64((uint64_t volatile *)addr, (uint64_t)val)
#pragma intrinsic(_InterlockedCompareExchange64)
static uintptr_t upb_Atomic_LoadMsc64(uint64_t volatile* addr) {
static uintptr_t upb_Atomic_LoadMsc(uint64_t volatile *addr) {
// Compare exchange with an unlikely value reduces the risk of a spurious
// (but harmless) store
return _InterlockedCompareExchange64(addr, 0xDEADC0DEBAADF00D,
0xDEADC0DEBAADF00D);
}
// If _Generic is available, use it to avoid emitting a "'uintptr_t' differs in
// levels of indirection from 'void *'" or -Wint-conversion compiler warning.
#if __STDC_VERSION__ >= 201112L
#define upb_Atomic_Load(addr, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t) *: upb_Atomic_LoadMsc( \
(uint64_t volatile *)(addr)), \
default: (void *)upb_Atomic_LoadMsc((uint64_t volatile *)(addr)))
#define upb_Atomic_Exchange(addr, val, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t) *: _InterlockedExchange64( \
(uint64_t volatile *)(addr), (uint64_t)val), \
default: (void *)_InterlockedExchange64((uint64_t volatile *)addr, \
(uint64_t)val))
#else
// Compare exchange with an unlikely value reduces the risk of a spurious
// (but harmless) store
#define upb_Atomic_Load(addr, order) \
(void *)upb_Atomic_LoadMsc((uint64_t volatile *)(addr))
#define upb_Atomic_Exchange(addr, val, order) \
(void *)_InterlockedExchange64((uint64_t volatile *)addr, (uint64_t)val)
#endif
#pragma intrinsic(_InterlockedCompareExchange64)
static bool upb_Atomic_CompareExchangeMscP64(uint64_t volatile* addr,
uint64_t* expected,
uint64_t desired) {
static bool upb_Atomic_CompareExchangeMscP(uint64_t volatile *addr,
uint64_t *expected,
uint64_t desired) {
uint64_t expect_val = *expected;
uint64_t actual_val =
_InterlockedCompareExchange64(addr, desired, expect_val);
@ -88,173 +92,72 @@ static bool upb_Atomic_CompareExchangeMscP64(uint64_t volatile* addr,
return true;
}
#pragma intrinsic(_InterlockedExchange64)
// If _Generic is available, use it to avoid emitting a "'uintptr_t' differs in
// levels of indirection from 'void *'" or -Wint-conversion compiler warning.
#if __STDC_VERSION__ >= 201112L
#define upb_Atomic_Store(addr, val, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t)*: (void)_InterlockedExchange64( \
(uint64_t volatile*)(addr), (uint64_t)val), \
UPB_ATOMIC(int32_t)*: (void)_InterlockedExchange( \
(int32_t volatile*)(addr), (int32_t)val), \
default: (void)_InterlockedExchange64((uint64_t volatile*)addr, \
(uint64_t)val))
#define upb_Atomic_Load(addr, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t)*: upb_Atomic_LoadMsc64( \
(uint64_t volatile*)(addr)), \
UPB_ATOMIC(int32_t)*: upb_Atomic_LoadMsc32((int32_t volatile*)(addr)), \
default: (void*)upb_Atomic_LoadMsc64((uint64_t volatile*)(addr)))
#define upb_Atomic_Exchange(addr, val, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t)*: _InterlockedExchange64( \
(uint64_t volatile*)(addr), (uint64_t)val), \
UPB_ATOMIC(int32_t)*: _InterlockedExchange((int32_t volatile*)(addr), \
(int32_t)val), \
default: (void*)_InterlockedExchange64((uint64_t volatile*)addr, \
(uint64_t)val))
#define upb_Atomic_CompareExchangeStrong(addr, expected, desired, \
success_order, failure_order) \
_Generic(addr, \
UPB_ATOMIC(int32_t)*: upb_Atomic_CompareExchangeMscP32( \
(int32_t volatile*)(addr), (int32_t*)expected, \
(int32_t)desired), \
default: upb_Atomic_CompareExchangeMscP64((uint64_t volatile*)(addr), \
(uint64_t*)expected, \
(uint64_t)desired))
#define upb_Atomic_CompareExchangeWeak(addr, expected, desired, success_order, \
failure_order) \
_Generic(addr, \
UPB_ATOMIC(int32_t)*: upb_Atomic_CompareExchangeMscP32( \
(int32_t volatile*)(addr), (int32_t*)expected, \
(int32_t)desired), \
default: upb_Atomic_CompareExchangeMscP64((uint64_t volatile*)(addr), \
(uint64_t*)expected, \
(uint64_t)desired))
#else
UPB_INLINE void _upb_Atomic_StoreP(void* addr, uint64_t val, size_t size) {
if (size == sizeof(int32_t)) {
(void)_InterlockedExchange((int32_t volatile*)addr, (int32_t)val);
} else {
(void)_InterlockedExchange64((uint64_t volatile*)addr, val);
}
}
#define upb_Atomic_Store(addr, val, order) \
_upb_Atomic_StoreP(addr, val, sizeof(*addr))
UPB_INLINE int64_t _upb_Atomic_LoadP(void* addr, size_t size) {
if (size == sizeof(int32_t)) {
return (int64_t)upb_Atomic_LoadMsc32((int32_t volatile*)addr);
} else {
return upb_Atomic_LoadMsc64((uint64_t volatile*)addr);
}
}
#define upb_Atomic_Load(addr, order) _upb_Atomic_LoadP(addr, sizeof(*addr))
UPB_INLINE int64_t _upb_Atomic_ExchangeP(void* addr, uint64_t val,
size_t size) {
if (size == sizeof(int32_t)) {
return (int64_t)_InterlockedExchange((int32_t volatile*)addr, (int32_t)val);
} else {
return (int64_t)_InterlockedExchange64((uint64_t volatile*)addr, val);
}
}
#define upb_Atomic_Exchange(addr, val, order) \
_upb_Atomic_ExchangeP(addr, val, sizeof(*addr))
UPB_INLINE bool _upb_Atomic_CompareExchangeMscP(void* addr, void* expected,
uint64_t desired, size_t size) {
if (size == sizeof(int32_t)) {
return upb_Atomic_CompareExchangeMscP32(
(int32_t volatile*)addr, (int32_t*)expected, (int32_t)desired);
} else {
return upb_Atomic_CompareExchangeMscP64((uint64_t volatile*)addr,
(uint64_t*)expected, desired);
}
}
#define upb_Atomic_CompareExchangeStrong(addr, expected, desired, \
success_order, failure_order) \
_upb_Atomic_CompareExchangeMscP(addr, expected, desired, sizeof(*addr))
upb_Atomic_CompareExchangeMscP((uint64_t volatile *)addr, \
(uint64_t *)expected, (uint64_t)desired)
#define upb_Atomic_CompareExchangeWeak(addr, expected, desired, success_order, \
failure_order) \
_upb_Atomic_CompareExchangeMscP(addr, expected, desired, sizeof(*addr))
#endif
upb_Atomic_CompareExchangeMscP((uint64_t volatile *)addr, \
(uint64_t *)expected, (uint64_t)desired)
#else // 32 bit pointers
#pragma intrinsic(_InterlockedExchange)
#define upb_Atomic_Store(addr, val, order) \
(void)_InterlockedExchange((uint32_t volatile*)addr, (uint32_t)val)
(void)_InterlockedExchange((uint32_t volatile *)addr, (uint32_t)val)
#pragma intrinsic(_InterlockedCompareExchange)
static uintptr_t upb_Atomic_LoadMsc(uint32_t volatile *addr) {
// Compare exchange with an unlikely value reduces the risk of a spurious
// (but harmless) store
return _InterlockedCompareExchange(addr, 0xDEADC0DE, 0xDEADC0DE);
}
// If _Generic is available, use it to avoid emitting 'uintptr_t' differs in
// levels of indirection from 'void *'
#if __STDC_VERSION__ >= 201112L
#define upb_Atomic_Load(addr, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t)*: (uintptr_t)upb_Atomic_LoadMsc32( \
(uint32_t volatile*)(addr)), \
UPB_ATOMIC(int32_t)*: upb_Atomic_LoadMsc32((int32_t volatile*)(addr)), \
default: (void*)upb_Atomic_LoadMsc32((uint32_t volatile*)(addr)))
#define upb_Atomic_Load(addr, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t) *: upb_Atomic_LoadMsc( \
(uint32_t volatile *)(addr)), \
default: (void *)upb_Atomic_LoadMsc((uint32_t volatile *)(addr)))
#define upb_Atomic_Exchange(addr, val, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t)*: _InterlockedExchange((uint32_t volatile*)(addr), \
(uint32_t)val), \
default: (void*)_InterlockedExchange((uint32_t volatile*)addr, \
(uint32_t)val))
#define upb_Atomic_Exchange(addr, val, order) \
_Generic(addr, \
UPB_ATOMIC(uintptr_t) *: _InterlockedExchange( \
(uint32_t volatile *)(addr), (uint32_t)val), \
default: (void *)_InterlockedExchange64((uint32_t volatile *)addr, \
(uint32_t)val))
#else
#define upb_Atomic_Load(addr, order) \
(void*)upb_Atomic_LoadMsc32((uint32_t volatile*)(addr))
(void *)upb_Atomic_LoadMsc((uint32_t volatile *)(addr))
#define upb_Atomic_Exchange(addr, val, order) \
(void*)_InterlockedExchange((uint32_t volatile*)addr, (uint32_t)val)
(void *)_InterlockedExchange((uint32_t volatile *)addr, (uint32_t)val)
#endif
#pragma intrinsic(_InterlockedCompareExchange)
static bool upb_Atomic_CompareExchangeMscP(uint32_t volatile *addr,
uint32_t *expected,
uint32_t desired) {
uint32_t expect_val = *expected;
uint32_t actual_val = _InterlockedCompareExchange(addr, desired, expect_val);
if (expect_val != actual_val) {
*expected = actual_val;
return false;
}
return true;
}
#define upb_Atomic_CompareExchangeStrong(addr, expected, desired, \
success_order, failure_order) \
upb_Atomic_CompareExchangeMscP32((uint32_t volatile*)addr, \
(uint32_t*)expected, (uint32_t)desired)
upb_Atomic_CompareExchangeMscP((uint32_t volatile *)addr, \
(uint32_t *)expected, (uint32_t)desired)
#define upb_Atomic_CompareExchangeWeak(addr, expected, desired, success_order, \
failure_order) \
upb_Atomic_CompareExchangeMscP32((uint32_t volatile*)addr, \
(uint32_t*)expected, (uint32_t)desired)
#endif
#pragma intrinsic(_InterlockedExchangeAdd)
#pragma intrinsic(_InterlockedExchangeAdd64)
// If _Generic is available, use it to switch between 32 and 64 bit types.
#if __STDC_VERSION__ >= 201112L
#define upb_Atomic_Add(addr, val, order) \
_Generic(addr, \
UPB_ATOMIC(int64_t)*: _InterlockedExchangeAdd64(addr, (int64_t)val), \
UPB_ATOMIC(int32_t)*: _InterlockedExchangeAdd(addr, (int32_t)val))
#define upb_Atomic_Sub(addr, val, order) \
_Generic(addr, \
UPB_ATOMIC(int64_t)*: _InterlockedExchangeAdd64(addr, -(int64_t)val), \
UPB_ATOMIC(int32_t)*: _InterlockedExchangeAdd(addr, -(int32_t)val))
#else
#define upb_Atomic_Add(addr, val, order) \
sizeof(*addr) == sizeof(int32_t) \
? _InterlockedExchangeAdd((uint32_t volatile*)addr, (int32_t)val) \
: _InterlockedExchangeAdd64((uint64_t volatile*)addr, (int64_t)val)
#define upb_Atomic_Sub(addr, val, order) \
sizeof(*addr) == sizeof(int32_t) \
? _InterlockedExchangeAdd((uint32_t volatile*)addr, -(int32_t)val) \
: _InterlockedExchangeAdd64((uint64_t volatile*)addr, -(int64_t)val)
upb_Atomic_CompareExchangeMscP((uint32_t volatile *)addr, \
(uint32_t *)expected, (uint32_t)desired)
#endif
#else // No atomics
@ -300,9 +203,6 @@ UPB_INLINE bool _upb_NonAtomic_CompareExchangeStrongP(void* addr,
failure_order) \
upb_Atomic_CompareExchangeStrong(addr, expected, desired, 0, 0)
#define upb_Atomic_Add(addr, val, order) (*addr += val)
#define upb_Atomic_Sub(addr, val, order) (*addr -= val)
#endif
#include "upb/port/undef.inc"

View file

@ -89,7 +89,7 @@ Error, UINTPTR_MAX is undefined
/* If we always read/write as a consistent type to each address, this shouldn't
* violate aliasing.
*/
#define UPB_PTR_AT(msg, ofs, type) ((type*)((char*)(msg) + (ofs)))
#define UPB_PTR_AT(msg, ofs, type) ((type *)((char *)(msg) + (ofs)))
// A flexible array member may have lower alignment requirements than the struct
// overall - in that case, it can overlap with the trailing padding of the rest
@ -496,12 +496,6 @@ Error, UINTPTR_MAX is undefined
#define UPB_NO_SANITIZE_ADDRESS
#endif
#if defined(__GNUC__) && (defined(__clang__) || UPB_GNUC_MIN(11, 0))
#define UPB_RETAIN __attribute__((retain))
#else
#define UPB_RETAIN
#endif
// Linker arrays combine elements from multiple translation units into a single
// array that can be iterated over at runtime.
//
@ -526,20 +520,14 @@ Error, UINTPTR_MAX is undefined
// }
// }
#define UPB_LINKARR_ATTR
#define UPB_LINKARR_SENTINEL UPB_RETAIN __attribute__((weak, used))
#if defined(__ELF__) || defined(__wasm__)
#define UPB_LINKARR_APPEND(name) \
__attribute__(( \
section("linkarr_" #name))) UPB_LINKARR_ATTR UPB_NO_SANITIZE_ADDRESS
__attribute__((section("linkarr_" #name))) UPB_NO_SANITIZE_ADDRESS
#define UPB_LINKARR_DECLARE(name, type) \
extern type __start_linkarr_##name; \
extern type __stop_linkarr_##name; \
UPB_LINKARR_APPEND(name) \
UPB_LINKARR_SENTINEL type UPB_linkarr_internal_empty_##name[1]
UPB_LINKARR_APPEND(name) type UPB_linkarr_internal_empty_##name[1]
#define UPB_LINKARR_START(name) (&__start_linkarr_##name)
#define UPB_LINKARR_STOP(name) (&__stop_linkarr_##name)
@ -547,85 +535,43 @@ Error, UINTPTR_MAX is undefined
/* As described in: https://stackoverflow.com/a/22366882 */
#define UPB_LINKARR_APPEND(name) \
__attribute__(( \
section("__DATA,__la_" #name))) UPB_LINKARR_ATTR UPB_NO_SANITIZE_ADDRESS
__attribute__((section("__DATA,__la_" #name))) UPB_NO_SANITIZE_ADDRESS
#define UPB_LINKARR_DECLARE(name, type) \
extern type __start_linkarr_##name __asm( \
"section$start$__DATA$__la_" #name); \
extern type __stop_linkarr_##name __asm( \
"section$end$__DATA$" \
"__la_" #name); \
UPB_LINKARR_APPEND(name) \
UPB_LINKARR_SENTINEL type UPB_linkarr_internal_empty_##name[1]
UPB_LINKARR_APPEND(name) type UPB_linkarr_internal_empty_##name[1]
#define UPB_LINKARR_START(name) (&__start_linkarr_##name)
#define UPB_LINKARR_STOP(name) (&__stop_linkarr_##name)
#elif defined(_MSC_VER)
#elif defined(_MSC_VER) && defined(__clang__)
/* See:
* https://devblogs.microsoft.com/oldnewthing/20181107-00/?p=100155
* https://devblogs.microsoft.com/oldnewthing/20181108-00/?p=100165
* https://devblogs.microsoft.com/oldnewthing/20181109-00/?p=100175 */
#define UPB_STRINGIFY_INTERNAL(x) #x
#define UPB_STRINGIFY(x) UPB_STRINGIFY_INTERNAL(x)
#define UPB_CONCAT(a, b, c) a##b##c
#define UPB_LINKARR_NAME(name, index) \
UPB_STRINGIFY(UPB_CONCAT(la_, name, index))
#define UPB_LINKARR_APPEND(name) \
__pragma(section(UPB_LINKARR_NAME(name, $j), read)) \
__declspec(allocate(UPB_LINKARR_NAME(name, $j)))
// clang-format off
#define UPB_LINKARR_DECLARE(name, type) \
__pragma(message(UPB_LINKARR_NAME(name, $j))) \
__pragma(section(UPB_LINKARR_NAME(name, $a), read)) \
__pragma(section(UPB_LINKARR_NAME(name, $z), read)) \
__declspec(allocate(UPB_LINKARR_NAME(name, $a)), selectany) \
type __start_linkarr_##name; \
__declspec(allocate(UPB_LINKARR_NAME(name, $z)), selectany) \
type __stop_linkarr_##name; \
UPB_LINKARR_APPEND(name) \
__declspec(selectany) type UPB_linkarr_internal_empty_##name[1] = {0}
// clang-format on
// Usage of __attribute__ here probably means this is Clang-specific, and would
// not work on MSVC.
#define UPB_LINKARR_APPEND(name) \
__declspec(allocate("la_" #name "$j")) UPB_NO_SANITIZE_ADDRESS
#define UPB_LINKARR_DECLARE(name, type) \
__declspec(allocate("la_" #name "$a")) type __start_linkarr_##name; \
__declspec(allocate("la_" #name "$z")) type __stop_linkarr_##name; \
UPB_LINKARR_APPEND(name) type UPB_linkarr_internal_empty_##name[1] = {0}
#define UPB_LINKARR_START(name) (&__start_linkarr_##name)
#define UPB_LINKARR_STOP(name) (&__stop_linkarr_##name)
#else
// Linker arrays are not supported on this platform. Make macros no-ops.
// Linker arrays are not supported on this platform. Make appends a no-op but
// don't define the other macros.
#define UPB_LINKARR_APPEND(name)
#define UPB_LINKARR_DECLARE(name, type)
#define UPB_LINKARR_START(name) (NULL)
#define UPB_LINKARR_STOP(name) (NULL)
#endif
#if defined(__ELF__) || defined(__wasm__) || defined(__MACH__)
#define UPB_CONSTRUCTOR(name) \
__attribute__((weak, visibility("hidden"), constructor)) void UPB_PRIVATE( \
name)(void)
#elif defined(_MSC_VER)
/*
* See: https://stackoverflow.com/questions/1113409
*
* The /include pragma suggested in the link above doesn't work in our case
* because it requires globally unique names. We need a different solution
* to prevent optimizers from removing the constructor. Our solution is to
* create a dummy exported weak symbol that prevent this stripping.
*/
#pragma section(".CRT$XCU", long, read)
#define UPB_CONSTRUCTOR(name) \
static void __cdecl UPB_PRIVATE(name)(void); \
__declspec(allocate(".CRT$XCU"), selectany) void( \
__cdecl * UPB_PRIVATE(name##_))(void) = UPB_PRIVATE(name); \
__declspec(selectany, dllexport) void* UPB_PRIVATE(name##_force_linkage) = \
&UPB_PRIVATE(name##_); \
static void __cdecl UPB_PRIVATE(name)(void)
#else
// No constructor support, nothing we can do except not break builds.
#define UPB_CONSTRUCTOR(name) static void UPB_PRIVATE(name)(void)
#endif
// Future versions of upb 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.

View file

@ -149,8 +149,8 @@ cc_library(
],
deps = [
":internal",
"//upb/mem",
"//upb/mini_descriptor",
"//upb/mini_table",
],
)

View file

@ -7,35 +7,25 @@
#include "upb/reflection/internal/def_pool.h"
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "upb/base/status.h"
#include "upb/base/string_view.h"
#include "upb/hash/common.h"
#include "upb/hash/int_table.h"
#include "upb/hash/str_table.h"
#include "upb/mem/alloc.h"
#include "upb/mem/arena.h"
#include "upb/mini_descriptor/decode.h"
#include "upb/mini_table/extension.h"
#include "upb/mini_table/extension_registry.h"
#include "upb/mini_table/file.h"
#include "upb/mini_table/generated_registry.h"
#include "upb/mini_table/message.h"
#include "upb/reflection/def.h"
#include "upb/reflection/def_type.h"
#include "upb/reflection/file_def.h"
#include "upb/reflection/internal/def_builder.h"
#include "upb/reflection/internal/enum_def.h"
#include "upb/reflection/internal/enum_value_def.h"
#include "upb/reflection/internal/field_def.h"
#include "upb/reflection/internal/file_def.h"
#include "upb/reflection/internal/message_def.h"
#include "upb/reflection/internal/service_def.h"
#include "upb/reflection/internal/upb_edition_defaults.h"
// Must be last.
#include "upb/port/def.inc"
#include "upb/wire/decode.h"
struct upb_DefPool {
upb_Arena* arena;
@ -43,7 +33,6 @@ struct upb_DefPool {
upb_strtable files; // file_name -> (upb_FileDef*)
upb_inttable exts; // (upb_MiniTableExtension*) -> (upb_FieldDef*)
upb_ExtensionRegistry* extreg;
const upb_GeneratedRegistryRef* generated_extreg;
const UPB_DESC(FeatureSetDefaults) * feature_set_defaults;
upb_MiniTablePlatform platform;
void* scratch_data;
@ -52,7 +41,6 @@ struct upb_DefPool {
};
void upb_DefPool_Free(upb_DefPool* s) {
upb_GeneratedRegistry_Release(s->generated_extreg);
upb_Arena_Free(s->arena);
upb_gfree(s->scratch_data);
upb_gfree(s);
@ -78,9 +66,6 @@ upb_DefPool* upb_DefPool_New(void) {
s->extreg = upb_ExtensionRegistry_New(s->arena);
if (!s->extreg) goto err;
s->generated_extreg = upb_GeneratedRegistry_Load();
if (!s->generated_extreg) goto err;
s->platform = kUpb_MiniTablePlatform_Native;
upb_Status status;
@ -542,8 +527,3 @@ const upb_FieldDef** upb_DefPool_GetAllExtensions(const upb_DefPool* s,
bool _upb_DefPool_LoadDefInit(upb_DefPool* s, const _upb_DefPool_Init* init) {
return _upb_DefPool_LoadDefInitEx(s, init, false);
}
const upb_ExtensionRegistry* _upb_DefPool_GeneratedExtensionRegistry(
const upb_DefPool* s) {
return upb_ExtensionRegistry_GetGenerated(s->generated_extreg);
}

View file

@ -29,18 +29,17 @@
// We want to copy the options verbatim into the destination options proto.
// We use serialize+parse as our deep copy.
#define UPB_DEF_SET_OPTIONS(target, desc_type, options_type, proto) \
if (UPB_DESC(desc_type##_has_options)(proto)) { \
size_t size; \
char* pb = UPB_DESC(options_type##_serialize)( \
UPB_DESC(desc_type##_options)(proto), ctx->tmp_arena, &size); \
if (!pb) _upb_DefBuilder_OomErr(ctx); \
target = UPB_DESC(options_type##_parse_ex)( \
pb, size, _upb_DefPool_GeneratedExtensionRegistry(ctx->symtab), 0, \
_upb_DefBuilder_Arena(ctx)); \
if (!target) _upb_DefBuilder_OomErr(ctx); \
} else { \
target = (const UPB_DESC(options_type)*)kUpbDefOptDefault; \
#define UPB_DEF_SET_OPTIONS(target, desc_type, options_type, proto) \
if (UPB_DESC(desc_type##_has_options)(proto)) { \
size_t size; \
char* pb = UPB_DESC(options_type##_serialize)( \
UPB_DESC(desc_type##_options)(proto), ctx->tmp_arena, &size); \
if (!pb) _upb_DefBuilder_OomErr(ctx); \
target = \
UPB_DESC(options_type##_parse)(pb, size, _upb_DefBuilder_Arena(ctx)); \
if (!target) _upb_DefBuilder_OomErr(ctx); \
} else { \
target = (const UPB_DESC(options_type)*)kUpbDefOptDefault; \
}
#ifdef __cplusplus

View file

@ -9,8 +9,7 @@
#define UPB_REFLECTION_DEF_POOL_INTERNAL_H_
#include "upb/mini_descriptor/decode.h"
#include "upb/mini_table/extension_registry.h"
#include "upb/reflection/def.h"
#include "upb/reflection/def_pool.h"
// Must be last.
#include "upb/port/def.inc"
@ -49,9 +48,6 @@ bool _upb_DefPool_LoadDefInit(upb_DefPool* s, const _upb_DefPool_Init* init);
bool _upb_DefPool_LoadDefInitEx(upb_DefPool* s, const _upb_DefPool_Init* init,
bool rebuild_minitable);
const upb_ExtensionRegistry* _upb_DefPool_GeneratedExtensionRegistry(
const upb_DefPool* s);
#ifdef __cplusplus
} /* extern "C" */
#endif

View file

@ -94,7 +94,6 @@ proto_library(
":custom_options_proto",
":custom_options_unlinked_proto",
],
deps = ["//:descriptor_proto"],
)
upb_c_proto_library(
@ -103,41 +102,12 @@ upb_c_proto_library(
deps = [":editions_test_proto"],
)
upb_minitable_proto_library(
name = "editions_test_upb_minitable_proto",
testonly = 1,
visibility = ["//upb:__subpackages__"],
deps = [":editions_test_proto"],
)
upb_proto_reflection_library(
name = "editions_test_upb_proto_reflection",
testonly = 1,
deps = [":editions_test_proto"],
)
proto_library(
name = "test_multiple_files_proto",
testonly = 1,
srcs = [
"test_multiple_files.proto",
"test_multiple_files2.proto",
],
)
upb_c_proto_library(
name = "test_multiple_files_upb_c_proto",
testonly = 1,
deps = [":test_multiple_files_proto"],
)
upb_minitable_proto_library(
name = "test_multiple_files_upb_minitable_proto",
testonly = 1,
visibility = ["//upb:__subpackages__"],
deps = [":test_multiple_files_proto"],
)
proto_library(
name = "test_cpp_proto",
srcs = ["test_cpp.proto"],
@ -227,6 +197,8 @@ cc_test(
copts = UPB_DEFAULT_CPPOPTS,
deps = [
":custom_options_upb_c_proto",
":custom_options_upb_minitable_proto",
":custom_options_upb_proto_reflection",
":editions_test_upb_c_proto",
":editions_test_upb_proto_reflection",
"//upb/base",
@ -367,7 +339,6 @@ upb_c_proto_library(
upb_minitable_proto_library(
name = "custom_options_upb_minitable_proto",
testonly = 1,
visibility = ["//upb:__subpackages__"],
deps = [":custom_options_proto"],
)
@ -427,9 +398,6 @@ filegroup(
[
"**/*.proto",
],
exclude = [
"custom_options_unlinked.proto",
],
),
visibility = ["//upb:__pkg__"],
)

View file

@ -7,18 +7,14 @@
#include <stdint.h>
#include <string>
#include <gtest/gtest.h>
#include "upb/base/descriptor_constants.h"
#include "upb/base/string_view.h"
#include "upb/base/upcast.h"
#include "upb/mem/arena.hpp"
#include "upb/message/message.h"
#include "upb/port/def.inc"
#include "upb/reflection/def.hpp"
#include "upb/reflection/descriptor_bootstrap.h"
#include "upb/test/custom_options.upb.h"
#include "upb/test/editions_test.upb.h"
#include "upb/test/editions_test.upbdefs.h"
@ -73,25 +69,7 @@ TEST(EditionsTest, ImportOptionUnlinked) {
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
const google_protobuf_MessageOptions* options = md.options();
upb_StringView data;
uintptr_t iter = kUpb_Message_UnknownBegin;
ASSERT_TRUE(upb_Message_NextUnknown(UPB_UPCAST(options), &data, &iter));
EXPECT_EQ(std::string(data.data, data.size),
// 7739037: 9
"\xE8\xE9\xC2\x1D\011");
EXPECT_FALSE(upb_Message_NextUnknown(UPB_UPCAST(options), &data, &iter));
}
TEST(EditionsTest, ImportOptionLinked) {
// Test that linked option dependencies don't show up in unknown fields. This
// also actually *uses* the linked options to guarantee linkage and make the
// previous test pass.
upb::Arena arena;
upb::DefPool defpool;
upb::MessageDefPtr md(upb_test_2023_EditionsMessage_getmsgdef(defpool.ptr()));
const google_protobuf_MessageOptions* options = md.options();
EXPECT_EQ(upb_message_opt(options), 87);
EXPECT_TRUE(upb_Message_HasUnknown(UPB_UPCAST(options)));
}
TEST(EditionsTest, ConstructProto) {

View file

@ -9,8 +9,6 @@ edition = "2024";
package upb.test_2023;
import "google/protobuf/descriptor.proto";
import option "upb/test/custom_options.proto";
import option "upb/test/custom_options_unlinked.proto";
@ -27,8 +25,6 @@ message EditionsMessage {
EditionsEnum enum_field = 5;
repeated int32 unpacked_field = 6
[features.repeated_field_encoding = EXPANDED];
extensions 100 to 199;
}
enum EditionsEnum {
@ -36,7 +32,3 @@ enum EditionsEnum {
ONE = 1;
}
extend EditionsMessage {
int32 ext = 100;
}

View file

@ -1,24 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// This test is meant to handle the case where two .proto files are build in the
// same proto_library. With dynamic linkage, they will end up in the same
// shared library.
edition = "2024";
package upb;
message MultipleFilesMessage1 {
int32 field1 = 1;
extensions 100 to 199;
}
extend MultipleFilesMessage1 {
int32 multiple_files_ext1 = 100;
}

View file

@ -1,24 +0,0 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2025 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
// This test is meant to handle the case where two .proto files are build in the
// same proto_library. With dynamic linkage, they will end up in the same
// shared library.
edition = "2024";
package upb;
message MultipleFilesMessage2 {
int32 field1 = 1;
extensions 100 to 199;
}
extend MultipleFilesMessage2 {
int32 multiple_files_ext2 = 100;
}

View file

@ -16,7 +16,6 @@
#include "upb/reflection/def.h"
#include "upb/reflection/enum_reserved_range.h"
#include "upb/reflection/extension_range.h"
#include "upb/reflection/internal/def_pool.h"
#include "upb/reflection/internal/field_def.h"
#include "upb/reflection/internal/file_def.h"
#include "upb/reflection/message.h"
@ -35,16 +34,16 @@ typedef struct {
// We want to copy the options verbatim into the destination options proto.
// We use serialize+parse as our deep copy.
#define SET_OPTIONS(proto, desc_type, options_type, src, extreg) \
{ \
size_t size; \
/* MEM: could use a temporary arena here instead. */ \
char* pb = google_protobuf_##options_type##_serialize(src, ctx->arena, &size); \
CHK_OOM(pb); \
google_protobuf_##options_type* dst = \
google_protobuf_##options_type##_parse_ex(pb, size, extreg, 0, ctx->arena); \
CHK_OOM(dst); \
google_protobuf_##desc_type##_set_options(proto, dst); \
#define SET_OPTIONS(proto, desc_type, options_type, src) \
{ \
size_t size; \
/* MEM: could use a temporary arena here instead. */ \
char* pb = google_protobuf_##options_type##_serialize(src, ctx->arena, &size); \
CHK_OOM(pb); \
google_protobuf_##options_type* dst = \
google_protobuf_##options_type##_parse(pb, size, ctx->arena); \
CHK_OOM(dst); \
google_protobuf_##desc_type##_set_options(proto, dst); \
}
static upb_StringView strviewdup2(upb_ToProto_Context* ctx,
@ -273,9 +272,7 @@ static google_protobuf_FieldDescriptorProto* fielddef_toproto(upb_ToProto_Contex
if (upb_FieldDef_HasOptions(f)) {
SET_OPTIONS(proto, FieldDescriptorProto, FieldOptions,
upb_FieldDef_Options(f),
_upb_DefPool_GeneratedExtensionRegistry(
upb_FileDef_Pool(upb_FieldDef_File(f))));
upb_FieldDef_Options(f));
}
return proto;
@ -292,9 +289,7 @@ static google_protobuf_OneofDescriptorProto* oneofdef_toproto(upb_ToProto_Contex
if (upb_OneofDef_HasOptions(o)) {
SET_OPTIONS(proto, OneofDescriptorProto, OneofOptions,
upb_OneofDef_Options(o),
_upb_DefPool_GeneratedExtensionRegistry(upb_FileDef_Pool(
upb_MessageDef_File(upb_OneofDef_ContainingType(o)))));
upb_OneofDef_Options(o));
}
return proto;
@ -312,9 +307,7 @@ static google_protobuf_EnumValueDescriptorProto* enumvaldef_toproto(
if (upb_EnumValueDef_HasOptions(e)) {
SET_OPTIONS(proto, EnumValueDescriptorProto, EnumValueOptions,
upb_EnumValueDef_Options(e),
_upb_DefPool_GeneratedExtensionRegistry(upb_FileDef_Pool(
upb_EnumDef_File(upb_EnumValueDef_Enum(e)))));
upb_EnumValueDef_Options(e));
}
return proto;
@ -352,9 +345,8 @@ static google_protobuf_EnumDescriptorProto* enumdef_toproto(upb_ToProto_Context*
}
if (upb_EnumDef_HasOptions(e)) {
SET_OPTIONS(proto, EnumDescriptorProto, EnumOptions, upb_EnumDef_Options(e),
_upb_DefPool_GeneratedExtensionRegistry(
upb_FileDef_Pool(upb_EnumDef_File(e))));
SET_OPTIONS(proto, EnumDescriptorProto, EnumOptions,
upb_EnumDef_Options(e));
}
UPB_DESC(SymbolVisibility) visibility = upb_EnumDef_Visibility(e);
@ -366,8 +358,7 @@ static google_protobuf_EnumDescriptorProto* enumdef_toproto(upb_ToProto_Context*
}
static google_protobuf_DescriptorProto_ExtensionRange* extrange_toproto(
upb_ToProto_Context* ctx, const upb_MessageDef* m,
const upb_ExtensionRange* e) {
upb_ToProto_Context* ctx, const upb_ExtensionRange* e) {
google_protobuf_DescriptorProto_ExtensionRange* proto =
google_protobuf_DescriptorProto_ExtensionRange_new(ctx->arena);
CHK_OOM(proto);
@ -379,9 +370,7 @@ static google_protobuf_DescriptorProto_ExtensionRange* extrange_toproto(
if (upb_ExtensionRange_HasOptions(e)) {
SET_OPTIONS(proto, DescriptorProto_ExtensionRange, ExtensionRangeOptions,
upb_ExtensionRange_Options(e),
_upb_DefPool_GeneratedExtensionRegistry(
upb_FileDef_Pool(upb_MessageDef_File(m))));
upb_ExtensionRange_Options(e));
}
return proto;
@ -438,8 +427,7 @@ static google_protobuf_DescriptorProto* msgdef_toproto(upb_ToProto_Context* ctx,
google_protobuf_DescriptorProto_ExtensionRange** ext_ranges =
google_protobuf_DescriptorProto_resize_extension_range(proto, n, ctx->arena);
for (int i = 0; i < n; i++) {
ext_ranges[i] =
extrange_toproto(ctx, m, upb_MessageDef_ExtensionRange(m, i));
ext_ranges[i] = extrange_toproto(ctx, upb_MessageDef_ExtensionRange(m, i));
}
n = upb_MessageDef_ReservedRangeCount(m);
@ -458,9 +446,7 @@ static google_protobuf_DescriptorProto* msgdef_toproto(upb_ToProto_Context* ctx,
if (upb_MessageDef_HasOptions(m)) {
SET_OPTIONS(proto, DescriptorProto, MessageOptions,
upb_MessageDef_Options(m),
_upb_DefPool_GeneratedExtensionRegistry(
upb_FileDef_Pool(upb_MessageDef_File(m))));
upb_MessageDef_Options(m));
}
UPB_DESC(SymbolVisibility) visibility = upb_MessageDef_Visibility(m);
@ -496,10 +482,8 @@ static google_protobuf_MethodDescriptorProto* methoddef_toproto(upb_ToProto_Cont
}
if (upb_MethodDef_HasOptions(m)) {
SET_OPTIONS(
proto, MethodDescriptorProto, MethodOptions, upb_MethodDef_Options(m),
_upb_DefPool_GeneratedExtensionRegistry(
upb_FileDef_Pool(upb_ServiceDef_File(upb_MethodDef_Service(m)))));
SET_OPTIONS(proto, MethodDescriptorProto, MethodOptions,
upb_MethodDef_Options(m));
}
return proto;
@ -523,9 +507,7 @@ static google_protobuf_ServiceDescriptorProto* servicedef_toproto(
if (upb_ServiceDef_HasOptions(s)) {
SET_OPTIONS(proto, ServiceDescriptorProto, ServiceOptions,
upb_ServiceDef_Options(s),
_upb_DefPool_GeneratedExtensionRegistry(
upb_FileDef_Pool(upb_ServiceDef_File(s))));
upb_ServiceDef_Options(s));
}
return proto;
@ -607,8 +589,8 @@ static google_protobuf_FileDescriptorProto* filedef_toproto(upb_ToProto_Context*
}
if (upb_FileDef_HasOptions(f)) {
SET_OPTIONS(proto, FileDescriptorProto, FileOptions, upb_FileDef_Options(f),
_upb_DefPool_GeneratedExtensionRegistry(upb_FileDef_Pool(f)));
SET_OPTIONS(proto, FileDescriptorProto, FileOptions,
upb_FileDef_Options(f));
}
return proto;

View file

@ -243,27 +243,6 @@ void WriteExtension(const DefPoolPair& pools, upb::FieldDefPtr ext,
output("\n};\n");
}
void RegisterExtensions(Output& output) {
output("UPB_LINKARR_DECLARE(upb_AllExts, const upb_MiniTableExtension);\n");
output("UPB_CONSTRUCTOR(upb_GeneratedRegistry_Constructor) {\n");
// TODO Although we define this function as weak and only one
// copy will ever exist in any binary, every instance will get registered as a
// separate constructor call. To avoid duplicate registrations, we use a
// static variable to ensure that the function is only executed once.
output(" static bool finished = false;\n");
output(" if (finished) return;\n");
output(" finished = true;\n");
output(" static UPB_PRIVATE(upb_GeneratedExtensionListEntry) entry = {\n");
output(" UPB_LINKARR_START(upb_AllExts),\n");
output(" UPB_LINKARR_STOP(upb_AllExts),\n");
output(" NULL\n");
output(" };\n");
output(" UPB_ASSERT(entry.next == NULL);\n");
output(" entry.next = UPB_PRIVATE(upb_generated_extension_list);\n");
output(" UPB_PRIVATE(upb_generated_extension_list) = &entry;\n");
output("}\n");
}
} // namespace
void WriteMiniTableHeader(const DefPoolPair& pools, upb::FileDefPtr file,
@ -361,10 +340,6 @@ void WriteMiniTableSourceIncludes(upb::FileDefPtr file,
output(
"extern const struct upb_MiniTable "
"UPB_PRIVATE(_kUpb_MiniTable_StaticallyTreeShaken);\n");
output(
"extern const UPB_PRIVATE(upb_GeneratedExtensionListEntry)* "
"UPB_PRIVATE(upb_generated_extension_list);\n");
}
void WriteMiniTableSource(const DefPoolPair& pools, upb::FileDefPtr file,
@ -437,10 +412,6 @@ void WriteMiniTableSource(const DefPoolPair& pools, upb::FileDefPtr file,
"\n");
}
if (!extensions.empty()) {
RegisterExtensions(output);
}
output("const upb_MiniTableFile $0 = {\n", FileVarName(file));
output(" $0,\n", messages.empty() ? "NULL" : kMessagesInit);
output(" $0,\n", enums.empty() ? "NULL" : kEnumsInit);
@ -486,19 +457,12 @@ void WriteMiniTableMultipleSources(
context->Open(MultipleSourceFilename(file, e.full_name(), &i)));
ABSL_CHECK(stream->WriteCord(absl::Cord(output.output())));
}
if (!extensions.empty()) {
// All extensions can be written to a single file because none of the
// symbols are retain, and the only weak symbols exist for deduping. It's
// most efficient to write them all together, especially with
// upb_RegisterExtensionList getting called once per weak definition.
for (const auto ext : extensions) {
Output output;
WriteMiniTableSourceIncludes(file, options, output);
for (const auto ext : extensions) {
WriteExtension(pools, ext, output);
}
RegisterExtensions(output);
WriteExtension(pools, ext, output);
auto stream = absl::WrapUnique(
context->Open(MultipleSourceFilename(file, "extensions", &i)));
context->Open(MultipleSourceFilename(file, ext.full_name(), &i)));
ABSL_CHECK(stream->WriteCord(absl::Cord(output.output())));
}
}