Use function pointers instead of instance defaults for MSVC. The instance defaults are not constant initialized and are not ready to use during registration.

PiperOrigin-RevId: 529471986
pull/12640/head
Protobuf Team Bot 2 years ago committed by Copybara-Service
parent 5223f56dba
commit bc94b4b8d0
  1. 10
      src/google/protobuf/compiler/cpp/parse_function_generator.cc
  2. 38
      src/google/protobuf/generated_message_tctable_decl.h
  3. 4
      src/google/protobuf/generated_message_tctable_impl.h
  4. 51
      src/google/protobuf/generated_message_tctable_lite.cc
  5. 18
      src/google/protobuf/message.cc

@ -72,6 +72,12 @@ bool HasWeakFields(const Descriptor* descriptor) {
}
bool UseDirectTcParserTable(const FieldDescriptor* field,
const Options& options) {
if (options.opensource_runtime) {
// We can only use direct tables when the default instances are constant
// initialized. In OSS we support some compilers that do not provide this
// requirement, so disable the direct use.
return false;
}
if (field->cpp_type() != field->CPPTYPE_MESSAGE) return false;
auto* m = field->message_type();
return !m->options().message_set_wire_format() &&
@ -533,7 +539,9 @@ void ParseFunctionGenerator::GenerateTailCallTable(Formatter& format) {
format("{_fl::Offset{sizeof($classname$::Impl_::Split)}},\n");
break;
case TailCallTableInfo::kSubMessage:
format("{::_pbi::FieldAuxDefaultMessage{}, &$1$},\n",
format("{::_pbi::FieldAuxMessageCreator<$1$>{}, &$2$},\n",
QualifiedClassName(aux_entry.field->message_type(),
options_),
QualifiedDefaultInstanceName(
aux_entry.field->message_type(), options_));
break;

@ -165,6 +165,30 @@ struct Offset {
struct FieldAuxDefaultMessage {};
template <typename T>
struct FieldAuxMessageCreator {};
template <typename T>
MessageLite* NewImpl(Arena* arena) {
return Arena::CreateMessage<T>(arena);
}
#if defined(_MSC_VER)
// In some versions of MSVC the globals are not properly constant initialized
// unless they are marked `constexpr`, which the default instances are not.
// In that case, we can't use them during parse because we need to be able to
// parse during dynamic initialization.
template <typename = void>
constexpr std::false_type CanUseDefaultInstanceForNew() {
return {};
}
#else
template <typename = void>
constexpr std::true_type CanUseDefaultInstanceForNew() {
return {};
}
#endif
// Small type card used by mini parse to handle map entries.
// Map key/values are very limited, so we can encode the whole thing in a single
// byte.
@ -411,13 +435,24 @@ struct alignas(uint64_t) TcParseTableBase {
: enum_range{range_start, range_length} {}
constexpr FieldAux(const MessageLite* msg) : message_default_p(msg) {}
constexpr FieldAux(FieldAuxDefaultMessage, const void* msg)
: message_default_p(msg) {
ABSL_ASSERT(CanUseDefaultInstanceForNew());
}
template <typename T,
std::enable_if_t<CanUseDefaultInstanceForNew<T>(), int> = 0>
constexpr FieldAux(FieldAuxMessageCreator<T>, const void* msg)
: message_default_p(msg) {}
template <typename T,
std::enable_if_t<!CanUseDefaultInstanceForNew<T>(), int> = 0>
constexpr FieldAux(FieldAuxMessageCreator<T>, const void*)
: creator(&NewImpl<T>) {}
constexpr FieldAux(const TcParseTableBase* table) : table(table) {}
constexpr FieldAux(MapAuxInfo map_info) : map_info(map_info) {}
constexpr FieldAux(void (*create_in_arena)(Arena*, void*))
: create_in_arena(create_in_arena) {}
constexpr FieldAux(LazyEagerVerifyFnType verify_func)
: verify_func(verify_func) {}
bool (*enum_validator)(int);
struct {
int16_t start; // minimum enum number (if it fits)
@ -425,15 +460,18 @@ struct alignas(uint64_t) TcParseTableBase {
} enum_range;
uint32_t offset;
const void* message_default_p;
MessageLite* (*creator)(Arena* arena);
const TcParseTableBase* table;
MapAuxInfo map_info;
void (*create_in_arena)(Arena*, void*);
LazyEagerVerifyFnType verify_func;
const MessageLite* message_default() const {
ABSL_DCHECK(CanUseDefaultInstanceForNew());
return static_cast<const MessageLite*>(message_default_p);
}
const MessageLite* message_default_weak() const {
ABSL_DCHECK(CanUseDefaultInstanceForNew());
return *static_cast<const MessageLite* const*>(message_default_p);
}
};

@ -811,6 +811,10 @@ class PROTOBUF_EXPORT TcParser final {
const char* ptr, Arena* arena, SerialArena* serial_arena,
ParseContext* ctx, RepeatedPtrField<std::string>& field);
static MessageLite* NewMessage(Arena* arena, TcParseTableBase::FieldAux aux);
static MessageLite* AddMessage(RepeatedPtrFieldBase& field,
TcParseTableBase::FieldAux aux);
static void AddUnknownEnum(MessageLite* msg, const TcParseTableBase* table,
uint32_t tag, int32_t enum_value);

@ -425,6 +425,7 @@ inline PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularParseMessageAuxImpl(
if (aux_is_table) {
const auto* inner_table = table->field_aux(data.aux_idx())->table;
if (field == nullptr) {
ABSL_DCHECK(CanUseDefaultInstanceForNew());
field = inner_table->default_instance->New(msg->GetArenaForAllocation());
}
if (group_coding) {
@ -434,9 +435,8 @@ inline PROTOBUF_ALWAYS_INLINE const char* TcParser::SingularParseMessageAuxImpl(
return ctx->ParseMessage<TcParser>(field, ptr, inner_table);
} else {
if (field == nullptr) {
const MessageLite* default_instance =
table->field_aux(data.aux_idx())->message_default();
field = default_instance->New(msg->GetArenaForAllocation());
field = NewMessage(msg->GetArenaForAllocation(),
*table->field_aux(data.aux_idx()));
}
if (group_coding) {
return ctx->ParseGroup(field, ptr, FastDecodeTag(saved_tag));
@ -510,9 +510,10 @@ inline PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedParseMessageAuxImpl(
auto& field = RefAt<RepeatedPtrFieldBase>(msg, data.offset());
do {
ptr += sizeof(TagType);
MessageLite* submsg = field.Add<GenericTypeHandler<MessageLite>>(
aux_is_table ? aux.table->default_instance : aux.message_default());
if (aux_is_table) {
ABSL_DCHECK(CanUseDefaultInstanceForNew());
MessageLite* submsg = field.Add<GenericTypeHandler<MessageLite>>(
aux.table->default_instance);
if (group_coding) {
ptr = ctx->ParseGroup<TcParser>(submsg, ptr,
FastDecodeTag(expected_tag), aux.table);
@ -520,6 +521,7 @@ inline PROTOBUF_ALWAYS_INLINE const char* TcParser::RepeatedParseMessageAuxImpl(
ptr = ctx->ParseMessage<TcParser>(submsg, ptr, aux.table);
}
} else {
MessageLite* submsg = AddMessage(field, aux);
if (group_coding) {
ptr = ctx->ParseGroup(submsg, ptr, FastDecodeTag(expected_tag));
} else {
@ -1741,6 +1743,7 @@ void* TcParser::MaybeGetSplitBase(MessageLite* msg, const bool is_split,
void* out = msg;
if (is_split) {
const uint32_t split_offset = GetSplitOffset(table);
ABSL_DCHECK(CanUseDefaultInstanceForNew());
void* default_split =
TcParser::RefAt<void*>(table->default_instance, split_offset);
void*& split = TcParser::RefAt<void*>(msg, split_offset);
@ -2110,6 +2113,27 @@ bool TcParser::MpVerifyUtf8(const absl::Cord& wire_bytes,
}
}
PROTOBUF_ALWAYS_INLINE MessageLite* TcParser::AddMessage(
RepeatedPtrFieldBase& field, TcParseTableBase::FieldAux aux) {
if (CanUseDefaultInstanceForNew()) {
return field.Add<GenericTypeHandler<MessageLite>>(aux.message_default());
} else {
MessageLite* value =
field.AddFromCleared<GenericTypeHandler<MessageLite>>();
if (value == nullptr) {
value = aux.creator(field.GetArena());
field.AddAllocated<GenericTypeHandler<MessageLite>>(value);
}
return value;
}
}
PROTOBUF_ALWAYS_INLINE MessageLite* TcParser::NewMessage(
Arena* arena, TcParseTableBase::FieldAux aux) {
return CanUseDefaultInstanceForNew() ? aux.message_default()->New(arena)
: aux.creator(arena);
}
template <bool is_split>
PROTOBUF_NOINLINE const char* TcParser::MpString(PROTOBUF_TC_PARAM_DECL) {
const auto& entry = RefAt<FieldEntry>(table, data.entry_offset());
@ -2310,6 +2334,7 @@ PROTOBUF_NOINLINE const char* TcParser::MpMessage(PROTOBUF_TC_PARAM_DECL) {
if ((type_card & field_layout::kTvMask) == field_layout::kTvTable) {
auto* inner_table = table->field_aux(&entry)->table;
if (need_init || field == nullptr) {
ABSL_DCHECK(CanUseDefaultInstanceForNew());
field = inner_table->default_instance->New(msg->GetArenaForAllocation());
}
if (is_group) {
@ -2318,15 +2343,15 @@ PROTOBUF_NOINLINE const char* TcParser::MpMessage(PROTOBUF_TC_PARAM_DECL) {
return ctx->ParseMessage<TcParser>(field, ptr, inner_table);
} else {
if (need_init || field == nullptr) {
const MessageLite* def;
if ((type_card & field_layout::kTvMask) == field_layout::kTvDefault) {
def = table->field_aux(&entry)->message_default();
field =
NewMessage(msg->GetArenaForAllocation(), *table->field_aux(&entry));
} else {
ABSL_DCHECK_EQ(type_card & field_layout::kTvMask,
+field_layout::kTvWeakPtr);
def = table->field_aux(&entry)->message_default_weak();
field = table->field_aux(&entry)->message_default_weak()->New(
msg->GetArenaForAllocation());
}
field = def->New(msg->GetArenaForAllocation());
}
if (is_group) {
return ctx->ParseGroup(field, ptr, decoded_tag);
@ -2375,15 +2400,15 @@ const char* TcParser::MpRepeatedMessage(PROTOBUF_TC_PARAM_DECL) {
}
return ctx->ParseMessage<TcParser>(value, ptr, inner_table);
} else {
const MessageLite* def;
MessageLite* value;
if ((type_card & field_layout::kTvMask) == field_layout::kTvDefault) {
def = aux.message_default();
value = AddMessage(field, aux);
} else {
ABSL_DCHECK_EQ(type_card & field_layout::kTvMask,
+field_layout::kTvWeakPtr);
def = aux.message_default_weak();
value = field.Add<GenericTypeHandler<MessageLite>>(
aux.message_default_weak());
}
MessageLite* value = field.Add<GenericTypeHandler<MessageLite>>(def);
if (is_group) {
return ctx->ParseGroup(value, ptr, decoded_tag);
}

@ -162,14 +162,20 @@ void Message::DiscardUnknownFields() {
const char* Message::_InternalParse(const char* ptr,
internal::ParseContext* ctx) {
#if defined(PROTOBUF_USE_TABLE_PARSER_ON_REFLECTION)
auto meta = GetMetadata();
ptr = internal::TcParser::ParseLoop(this, ptr, ctx,
meta.reflection->GetTcParseTable());
return ptr;
// The reflection based TDP builder will use default instances. If we can't do
// it disable the whole thing and fallback to the reflection parser.
constexpr bool kUseTcParser = internal::CanUseDefaultInstanceForNew();
#else
return WireFormat::_InternalParse(this, ptr, ctx);
constexpr bool kUseTcParser = false;
#endif
if (kUseTcParser) {
auto meta = GetMetadata();
ptr = internal::TcParser::ParseLoop(this, ptr, ctx,
meta.reflection->GetTcParseTable());
return ptr;
}
return WireFormat::_InternalParse(this, ptr, ctx);
}
uint8_t* Message::_InternalSerialize(uint8_t* target,

Loading…
Cancel
Save