// Protocol Buffers - Google's data interchange format // Copyright 2022 Google Inc. 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 // // Defines ClassData, the structure that contains type information about // messages. Used to implement reflection, parsing, dynamic casting, and other // runtime features. // // This file intentionally does not depend on protobuf_lite. #ifndef GOOGLE_PROTOBUF_CLASS_DATA_H__ #define GOOGLE_PROTOBUF_CLASS_DATA_H__ #include #include #include "absl/log/absl_check.h" #include "google/protobuf/port.h" // Must be included last. #include "google/protobuf/port_def.inc" namespace google { namespace protobuf { class Arena; class Descriptor; class MessageLite; class Reflection; namespace io { class EpsCopyOutputStream; } // namespace io namespace internal { struct ClassDataFull; struct DescriptorTable; class LazyField; struct TcParseTableBase; class MessageCreator { public: using Func = void* (*)(const void*, void*, Arena*); // Use -1/0/1 to be able to use <0, ==0, >0 enum Tag : int8_t { kFunc = -1, kZeroInit = 0, kMemcpy = 1, }; constexpr MessageCreator() : allocation_size_(), tag_(), alignment_(), func_(nullptr) {} static constexpr MessageCreator ZeroInit(uint32_t allocation_size, uint8_t alignment) { MessageCreator out; out.allocation_size_ = allocation_size; out.tag_ = kZeroInit; out.alignment_ = alignment; return out; } static constexpr MessageCreator CopyInit(uint32_t allocation_size, uint8_t alignment) { MessageCreator out; out.allocation_size_ = allocation_size; out.tag_ = kMemcpy; out.alignment_ = alignment; return out; } constexpr MessageCreator(Func func, uint32_t allocation_size, uint8_t alignment) : allocation_size_(allocation_size), tag_(kFunc), alignment_(alignment), func_(func) {} // Template for testing. template MessageLite* PlacementNew(const MessageLite* prototype_for_func, const MessageLite* prototype_for_copy, void* mem, Arena* arena) const; // Make this a template to avoid depending on arena.h. template void* AllocateMessage(Arena* arena) const { if (arena != nullptr) { return arena->AllocateAligned(allocation_size_); } else { return Allocate(allocation_size_); } } Tag tag() const { return tag_; } uint32_t allocation_size() const { return allocation_size_; } uint8_t alignment() const { return alignment_; } private: uint32_t allocation_size_; Tag tag_; uint8_t alignment_; Func func_; }; // Note: The order of arguments in the functions is chosen so that it has // the same ABI as the member function that calls them. Eg the `this` // pointer becomes the first argument in the free function. // // Future work: // We could save more data by omitting any optional pointer that would // otherwise be null. We can have some metadata in ClassData telling us if we // have them and their offset. struct PROTOBUF_EXPORT ClassData { #ifndef PROTOBUF_MESSAGE_GLOBALS const MessageLite* prototype; const internal::TcParseTableBase* tc_table; #endif // PROTOBUF_MESSAGE_GLOBALS bool (*is_initialized)(const MessageLite&); void (*merge_to_from)(MessageLite& to, const MessageLite& from_msg); internal::MessageCreator message_creator; #if defined(PROTOBUF_CUSTOM_VTABLE) void (*destroy_message)(MessageLite& msg); void (MessageLite::*clear)(); size_t (*byte_size_long)(const MessageLite&); uint8_t* (*serialize)(const MessageLite& msg, uint8_t* ptr, io::EpsCopyOutputStream* stream); #endif // PROTOBUF_CUSTOM_VTABLE // Offset of the CachedSize member. uint32_t cached_size_offset; // LITE objects (ie !descriptor_methods) collocate their name as a // char[] just beyond the ClassData. bool is_lite; bool is_dynamic = false; // In normal mode we have the small constructor to avoid the cost in // codegen. #if !defined(PROTOBUF_CUSTOM_VTABLE) constexpr ClassData(const MessageLite* prototype, const internal::TcParseTableBase* tc_table, bool (*is_initialized)(const MessageLite&), void (*merge_to_from)(MessageLite& to, const MessageLite& from_msg), internal::MessageCreator message_creator, uint32_t cached_size_offset, bool is_lite) : #ifndef PROTOBUF_MESSAGE_GLOBALS prototype(prototype), tc_table(tc_table), #endif // PROTOBUF_MESSAGE_GLOBALS is_initialized(is_initialized), merge_to_from(merge_to_from), message_creator(message_creator), cached_size_offset(cached_size_offset), is_lite(is_lite) { } #endif // !PROTOBUF_CUSTOM_VTABLE // But we always provide the full constructor even in normal mode to make // helper code simpler. constexpr ClassData( const MessageLite* prototype, const internal::TcParseTableBase* tc_table, bool (*is_initialized)(const MessageLite&), void (*merge_to_from)(MessageLite& to, const MessageLite& from_msg), internal::MessageCreator message_creator, [[maybe_unused]] void (*destroy_message)(MessageLite& msg), // [[maybe_unused]] void (MessageLite::*clear)(), [[maybe_unused]] size_t (*byte_size_long)(const MessageLite&), [[maybe_unused]] uint8_t* (*serialize)(const MessageLite& msg, uint8_t* ptr, io::EpsCopyOutputStream* stream), uint32_t cached_size_offset, bool is_lite) : #ifndef PROTOBUF_MESSAGE_GLOBALS prototype(prototype), tc_table(tc_table), #endif // PROTOBUF_MESSAGE_GLOBALS is_initialized(is_initialized), merge_to_from(merge_to_from), message_creator(message_creator), #if defined(PROTOBUF_CUSTOM_VTABLE) destroy_message(destroy_message), clear(clear), byte_size_long(byte_size_long), serialize(serialize), #endif // PROTOBUF_CUSTOM_VTABLE cached_size_offset(cached_size_offset), is_lite(is_lite) { } const ClassDataFull& full() const; const TcParseTableBase* GetTcParseTable() const; #ifndef PROTOBUF_MESSAGE_GLOBALS const MessageLite* default_instance() const { return prototype; } #else const MessageLite* default_instance() const; #endif // PROTOBUF_MESSAGE_GLOBALS // Defined in message_lite.h. MessageLite* New(Arena* arena) const; // Defined in message_lite.h. MessageLite* PlacementNew(void* mem, Arena* arena) const; uint32_t allocation_size() const { return message_creator.allocation_size(); } uint8_t alignment() const { return message_creator.alignment(); } }; #ifndef PROTOBUF_MESSAGE_GLOBALS struct ClassDataLite : ClassData { constexpr ClassDataLite(ClassData base, const char* type_name) : ClassData(base), type_name_ptr(type_name) {} const char* type_name() const { return type_name_ptr; } const char* type_name_ptr; constexpr const ClassData* base() const { return this; } }; #else using ClassDataLite = ClassDataFull; #endif // PROTOBUF_MESSAGE_GLOBALS // We use a secondary vtable for descriptor based methods. This way ClassData // does not grow with the number of descriptor methods. This avoids extra // costs in MessageLite. struct PROTOBUF_EXPORT DescriptorMethods { absl::string_view (*get_type_name)(const ClassData* data); std::string (*initialization_error_string)(const MessageLite&); const internal::TcParseTableBase* (*get_tc_table)(const ClassData*); size_t (*space_used_long)(const MessageLite&); std::string (*debug_string)(const MessageLite&); void (*verify_lazy_field_consistency)(const LazyField&); }; // ClassData* can and should be placed on read-only section to maximize sharing. // However, ClassDataFull has mutable fields for lazy initialization of // reflection related data. To keep the lazy initialization and to move the // ClassDataFull to the read-only section we use a secondary table. Extra // indirection should be tolerable considering that reflection isn't performance // critical. struct PROTOBUF_EXPORT ReflectionData { constexpr ReflectionData(const DescriptorMethods* descriptor_methods, const internal::DescriptorTable* descriptor_table, void (*get_metadata_tracker)()) : reflection(nullptr), descriptor(nullptr), descriptor_table(descriptor_table), descriptor_methods(descriptor_methods), get_metadata_tracker(get_metadata_tracker) {} // Accesses are protected by the once_flag in `descriptor_table`. When the // table is null these are populated from the beginning and need to // protection. const Reflection* reflection; const Descriptor* descriptor; // Codegen types will provide a DescriptorTable to do lazy // registration/initialization of the reflection objects. // Other types, like DynamicMessage, keep the table as null but eagerly // populate `reflection`/`descriptor` fields. const internal::DescriptorTable* descriptor_table; const DescriptorMethods* descriptor_methods; // When an access tracker is installed, this function notifies the tracker // that GetMetadata was called. void (*get_metadata_tracker)(); }; #ifndef PROTOBUF_MESSAGE_GLOBALS struct PROTOBUF_EXPORT ClassDataFull : ClassData { constexpr ClassDataFull(ClassData base, const DescriptorMethods* descriptor_methods, const internal::DescriptorTable* descriptor_table, void (*get_metadata_tracker)()) : ClassData(base), reflection_ptr(nullptr), descriptor_ptr(nullptr), descriptor_table_ptr(descriptor_table), descriptor_methods_ptr(descriptor_methods), get_metadata_tracker_func(get_metadata_tracker) {} constexpr const ClassData* base() const { return this; } // Accessors for reflection related data. const Reflection* reflection() const { return reflection_ptr; } const Descriptor* descriptor() const { return descriptor_ptr; } void set_reflection(const Reflection* reflection) const { reflection_ptr = reflection; } void set_descriptor(const Descriptor* descriptor) const { descriptor_ptr = descriptor; } const internal::DescriptorTable* descriptor_table() const { return descriptor_table_ptr; } const DescriptorMethods* descriptor_methods() const { return descriptor_methods_ptr; } bool has_get_metadata_tracker() const { return get_metadata_tracker_func != nullptr; } void get_metadata_tracker() const { get_metadata_tracker_func(); } // Accesses are protected by the once_flag in `descriptor_table`. When the // table is null these are populated from the beginning and need to // protection. mutable const Reflection* reflection_ptr; mutable const Descriptor* descriptor_ptr; // Codegen types will provide a DescriptorTable to do lazy // registration/initialization of the reflection objects. // Other types, like DynamicMessage, keep the table as null but eagerly // populate `reflection`/`descriptor` fields. const internal::DescriptorTable* descriptor_table_ptr; const DescriptorMethods* descriptor_methods_ptr; // When an access tracker is installed, this function notifies the tracker // that GetMetadata was called. void (*get_metadata_tracker_func)(); }; #else // TODO b/474609573 - Rename this type to reflect that is's unified to // ClassDataLite as well. struct PROTOBUF_EXPORT ClassDataFull : ClassData { constexpr ClassDataFull(ClassData base, ReflectionData* reflection_data) : ClassData(base), aux_data{.reflection_data = reflection_data} { ABSL_DCHECK(!is_lite); } constexpr ClassDataFull(ClassData base, const char* type_name) : ClassData(base), aux_data{.type_name = type_name} { ABSL_DCHECK(is_lite); } constexpr const ClassData* base() const { return this; } // Accessors for reflection related data (ClassDataFull only). const Reflection* reflection() const { return reflection_data()->reflection; } const Descriptor* descriptor() const { return reflection_data()->descriptor; } void set_reflection(const Reflection* reflection) const { reflection_data()->reflection = reflection; } void set_descriptor(const Descriptor* descriptor) const { reflection_data()->descriptor = descriptor; } const internal::DescriptorTable* descriptor_table() const { return reflection_data()->descriptor_table; } const DescriptorMethods* descriptor_methods() const { return reflection_data()->descriptor_methods; } bool has_get_metadata_tracker() const { return reflection_data()->get_metadata_tracker != nullptr; } void get_metadata_tracker() const { reflection_data()->get_metadata_tracker(); } ReflectionData* reflection_data() const { ABSL_DCHECK(!is_lite); return aux_data.reflection_data; } // Accessors for type name (ClassDataLite only). const char* type_name() const { ABSL_DCHECK(is_lite); return aux_data.type_name; } union ReflectionDataOrTypeName { ReflectionData* reflection_data; const char* type_name; } aux_data; }; #endif // PROTOBUF_MESSAGE_GLOBALS inline const ClassDataFull& ClassData::full() const { ABSL_DCHECK(!is_lite); return *static_cast(this); } } // namespace internal } // namespace protobuf } // namespace google #include "google/protobuf/port_undef.inc" #endif // GOOGLE_PROTOBUF_CLASS_DATA_H__