blob: afba7ef906a1a3f87bf2e996bbb36e35074c2682 [file] [log] [blame]
// 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.
// Author: kenton@google.com (Kenton Varda)
// Based on original Protocol Buffers design by
// Sanjay Ghemawat, Jeff Dean, and others.
#include "google/protobuf/compiler/cpp/file.h"
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "google/protobuf/compiler/scc.h"
#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/escaping.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "google/protobuf/compiler/cpp/enum.h"
#include "google/protobuf/compiler/cpp/extension.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/message.h"
#include "google/protobuf/compiler/cpp/names.h"
#include "google/protobuf/compiler/cpp/service.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"
// Must be last.
#include "google/protobuf/port_def.inc"
namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
absl::flat_hash_map<absl::string_view, std::string> FileVars(
const FileDescriptor* file, const Options& options) {
return {
{"filename", file->name()},
{"package_ns", Namespace(file, options)},
{"tablename", UniqueName("TableStruct", file, options)},
{"desc_table", DescriptorTableName(file, options)},
{"dllexport_decl", options.dllexport_decl},
{"file_level_metadata", UniqueName("file_level_metadata", file, options)},
{"file_level_enum_descriptors",
UniqueName("file_level_enum_descriptors", file, options)},
{"file_level_service_descriptors",
UniqueName("file_level_service_descriptors", file, options)},
};
}
// TODO(b/203101078): remove pragmas that suppresses uninitialized warnings when
// clang bug is fixed.
void MuteWuninitialized(io::Printer* p) {
p->Emit(R"(
#if defined(__llvm__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized"
#endif // __llvm__
)");
}
void UnmuteWuninitialized(io::Printer* p) {
p->Emit(R"(
#if defined(__llvm__)
#pragma clang diagnostic pop
#endif // __llvm__
)");
}
} // namespace
FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options)
: file_(file), options_(options), scc_analyzer_(options) {
std::vector<const Descriptor*> msgs = FlattenMessagesInFile(file);
for (int i = 0; i < msgs.size(); ++i) {
message_generators_.push_back(std::make_unique<MessageGenerator>(
msgs[i], variables_, i, options, &scc_analyzer_));
message_generators_.back()->AddGenerators(&enum_generators_,
&extension_generators_);
}
for (int i = 0; i < file->enum_type_count(); ++i) {
enum_generators_.push_back(
std::make_unique<EnumGenerator>(file->enum_type(i), options));
}
for (int i = 0; i < file->service_count(); ++i) {
service_generators_.push_back(std::make_unique<ServiceGenerator>(
file->service(i), variables_, options));
}
if (HasGenericServices(file_, options_)) {
for (int i = 0; i < service_generators_.size(); ++i) {
service_generators_[i]->index_in_metadata_ = i;
}
}
for (int i = 0; i < file->extension_count(); ++i) {
extension_generators_.push_back(std::make_unique<ExtensionGenerator>(
file->extension(i), options, &scc_analyzer_));
}
for (int i = 0; i < file->weak_dependency_count(); ++i) {
weak_deps_.insert(file->weak_dependency(i));
}
}
void FileGenerator::GenerateFile(io::Printer* p, GeneratedFileType file_type,
std::function<void()> cb) {
auto v = p->WithVars(FileVars(file_, options_));
auto guard = IncludeGuard(file_, file_type, options_);
p->Emit({{"cb", cb}, {"guard", guard}}, R"(
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: $filename$
#ifndef $guard$
#define $guard$
#include <limits>
#include <string>
#include <type_traits>
$cb$;
#endif // $guard$
)");
}
void FileGenerator::GenerateMacroUndefs(io::Printer* p) {
// Only do this for protobuf's own types. There are some google3 protos using
// macros as field names and the generated code compiles after the macro
// expansion. Undefing these macros actually breaks such code.
if (file_->name() != "net/proto2/compiler/proto/plugin.proto" &&
file_->name() != "google/protobuf/compiler/plugin.proto") {
return;
}
std::vector<const FieldDescriptor*> fields;
ListAllFields(file_, &fields);
absl::flat_hash_set<absl::string_view> all_fields;
for (const FieldDescriptor* field : fields) {
all_fields.insert(field->name());
}
for (absl::string_view name : {"major", "minor"}) {
if (!all_fields.contains(name)) {
continue;
}
p->Emit({{"name", std::string(name)}}, R"(
#ifdef $name$
#undef $name$
#endif // $name$
)");
}
}
void FileGenerator::GenerateSharedHeaderCode(io::Printer* p) {
p->Emit(
{
{"port_def",
[&] { IncludeFile("net/proto2/public/port_def.inc", p); }},
{"port_undef",
[&] { IncludeFile("net/proto2/public/port_undef.inc", p); }},
{"dllexport_macro", FileDllExport(file_, options_)},
{"undefs", [&] { GenerateMacroUndefs(p); }},
{"global_state_decls",
[&] { GenerateGlobalStateFunctionDeclarations(p); }},
{"fwd_decls", [&] { GenerateForwardDeclarations(p); }},
{"proto2_ns_enums",
[&] { GenerateProto2NamespaceEnumSpecializations(p); }},
{"main_decls",
[&] {
NamespaceOpener ns(Namespace(file_, options_), p);
p->Emit(
{
{"enums", [&] { GenerateEnumDefinitions(p); }},
{"messages", [&] { GenerateMessageDefinitions(p); }},
{"services", [&] { GenerateServiceDefinitions(p); }},
{"extensions", [&] { GenerateExtensionIdentifiers(p); }},
{"inline_fns",
[&] { GenerateInlineFunctionDefinitions(p); }},
},
R"(
$enums$
$hrule_thick$
$messages$
$hrule_thick$
$services$
$extensions$
$hrule_thick$
$inline_fns$
// @@protoc_insertion_point(namespace_scope)
)");
}},
},
R"(
// Must be included last.
$port_def$
#define $dllexport_macro$$ dllexport_decl$
$undefs$
PROTOBUF_NAMESPACE_OPEN
namespace internal {
class AnyMetadata;
} // namespace internal
PROTOBUF_NAMESPACE_CLOSE
$global_state_decls$;
$fwd_decls$
$main_decls$
$proto2_ns_enums$
// @@protoc_insertion_point(global_scope)
$port_undef$
)");
}
void FileGenerator::GenerateProtoHeader(io::Printer* p,
absl::string_view info_path) {
if (!options_.proto_h) {
return;
}
GenerateFile(p, GeneratedFileType::kProtoH, [&] {
if (!options_.opensource_runtime) {
p->Emit(R"(
#ifdef SWIG
#error "Do not SWIG-wrap protobufs."
#endif // SWIG
)");
}
if (IsBootstrapProto(options_, file_)) {
p->Emit({{"name", StripProto(file_->name())}}, R"cc(
// IWYU pragma: private, include "$name$.proto.h"
)cc");
}
p->Emit(
{
{"library_includes", [&] { GenerateLibraryIncludes(p); }},
{"proto_includes",
[&] {
for (int i = 0; i < file_->public_dependency_count(); ++i) {
const FileDescriptor* dep = file_->public_dependency(i);
p->Emit({{"name", StripProto(dep->name())}}, R"(
#include "$name$.proto.h"
)");
}
}},
{"metadata_pragma", [&] { GenerateMetadataPragma(p, info_path); }},
{"header_main", [&] { GenerateSharedHeaderCode(p); }},
},
R"cc(
$library_includes$;
$proto_includes$;
// @@protoc_insertion_point(includes)
$metadata_pragma$;
$header_main$;
)cc");
});
}
void FileGenerator::GeneratePBHeader(io::Printer* p,
absl::string_view info_path) {
GenerateFile(p, GeneratedFileType::kPbH, [&] {
p->Emit(
{
{"library_includes",
[&] {
if (options_.proto_h) {
std::string target_basename = StripProto(file_->name());
if (!options_.opensource_runtime) {
GetBootstrapBasename(options_, target_basename,
&target_basename);
}
p->Emit({{"name", target_basename}}, R"(
#include "$name$.proto.h" // IWYU pragma: export
)");
} else {
GenerateLibraryIncludes(p);
}
}},
{"proto_includes",
[&] {
if (options_.transitive_pb_h) {
GenerateDependencyIncludes(p);
}
}},
{"metadata_pragma", [&] { GenerateMetadataPragma(p, info_path); }},
{"header_main",
[&] {
if (!options_.proto_h) {
GenerateSharedHeaderCode(p);
return;
}
{
NamespaceOpener ns(Namespace(file_, options_), p);
p->Emit(R"cc(
// @@protoc_insertion_point(namespace_scope)
)cc");
}
p->Emit(R"cc(
// @@protoc_insertion_point(global_scope)
)cc");
}},
},
R"cc(
$library_includes$;
$proto_includes$;
// @@protoc_insertion_point(includes)
$metadata_pragma$;
$header_main$;
)cc");
});
}
void FileGenerator::DoIncludeFile(absl::string_view google3_name,
bool do_export, io::Printer* p) {
constexpr absl::string_view prefix = "net/proto2/";
GOOGLE_CHECK(absl::StartsWith(google3_name, prefix)) << google3_name;
auto v = p->WithVars(
{{"export_suffix", do_export ? "// IWYU pragma: export" : ""}});
if (options_.opensource_runtime) {
absl::ConsumePrefix(&google3_name, prefix);
absl::ConsumePrefix(&google3_name, "internal/");
absl::ConsumePrefix(&google3_name, "proto/");
absl::ConsumePrefix(&google3_name, "public/");
std::string path;
if (absl::ConsumePrefix(&google3_name, "io/public/")) {
path = absl::StrCat("io/", google3_name);
} else {
path = std::string(google3_name);
}
if (options_.runtime_include_base.empty()) {
p->Emit({{"path", path}}, R"(
#include "google/protobuf/$path$"$ export_suffix$
)");
} else {
p->Emit({{"base", options_.runtime_include_base}, {"path", path}},
R"(
#include "$base$google/protobuf/$path$"$ export_suffix$
)");
}
} else {
std::string path(google3_name);
// The bootstrapped proto generated code needs to use the
// third_party/protobuf header paths to avoid circular dependencies.
if (options_.bootstrap) {
constexpr absl::string_view bootstrap_prefix = "net/proto2/public";
if (absl::ConsumePrefix(&google3_name, bootstrap_prefix)) {
path = absl::StrCat("third_party/protobuf", google3_name);
}
}
p->Emit({{"path", path}}, R"(
#include "$path$"$ export_suffix$
)");
}
}
std::string FileGenerator::CreateHeaderInclude(absl::string_view basename,
const FileDescriptor* file) {
if (options_.opensource_runtime && IsWellKnownMessage(file) &&
!options_.runtime_include_base.empty()) {
return absl::StrCat("\"", options_.runtime_include_base, basename, "\"");
}
return absl::StrCat("\"", basename, "\"");
}
void FileGenerator::GenerateSourceIncludes(io::Printer* p) {
std::string target_basename = StripProto(file_->name());
if (!options_.opensource_runtime) {
GetBootstrapBasename(options_, target_basename, &target_basename);
}
absl::StrAppend(&target_basename, options_.proto_h ? ".proto.h" : ".pb.h");
p->Emit({{"h_include", CreateHeaderInclude(target_basename, file_)}},
R"(
// Generated by the protocol buffer compiler. DO NOT EDIT!
// source: $filename$
#include $h_include$
#include <algorithm>
)");
IncludeFile("net/proto2/io/public/coded_stream.h", p);
// TODO(gerbens) This is to include parse_context.h, we need a better way
IncludeFile("net/proto2/public/extension_set.h", p);
IncludeFile("net/proto2/public/wire_format_lite.h", p);
// Unknown fields implementation in lite mode uses StringOutputStream
if (!UseUnknownFieldSet(file_, options_) && !message_generators_.empty()) {
IncludeFile("net/proto2/io/public/zero_copy_stream_impl_lite.h", p);
}
if (HasDescriptorMethods(file_, options_)) {
IncludeFile("net/proto2/public/descriptor.h", p);
IncludeFile("net/proto2/public/generated_message_reflection.h", p);
IncludeFile("net/proto2/public/reflection_ops.h", p);
IncludeFile("net/proto2/public/wire_format.h", p);
}
if (HasGeneratedMethods(file_, options_) &&
options_.tctable_mode != Options::kTCTableNever) {
IncludeFile("net/proto2/public/generated_message_tctable_impl.h", p);
}
if (options_.proto_h) {
// Use the smaller .proto.h files.
for (int i = 0; i < file_->dependency_count(); ++i) {
const FileDescriptor* dep = file_->dependency(i);
if (!options_.opensource_runtime &&
IsDepWeak(dep)) { // Do not import weak deps.
continue;
}
std::string basename = StripProto(dep->name());
if (IsBootstrapProto(options_, file_)) {
GetBootstrapBasename(options_, basename, &basename);
}
p->Emit({{"name", basename}}, R"(
#include "$name$.proto.h"
)");
}
}
if (HasCordFields(file_, options_)) {
p->Emit(R"(
#include "third_party/absl/strings/internal/string_constant.h"
)");
}
p->Emit(R"cc(
// @@protoc_insertion_point(includes)
// Must be included last.
)cc");
IncludeFile("net/proto2/public/port_def.inc", p);
}
void FileGenerator::GenerateSourcePrelude(io::Printer* p) {
// For MSVC builds, we use #pragma init_seg to move the initialization of our
// libraries to happen before the user code.
// This worksaround the fact that MSVC does not do constant initializers when
// required by the standard.
p->Emit(R"cc(
PROTOBUF_PRAGMA_INIT_SEG
namespace _pb = ::$proto_ns$;
namespace _pbi = ::$proto_ns$::internal;
)cc");
if (HasGeneratedMethods(file_, options_) &&
options_.tctable_mode != Options::kTCTableNever) {
p->Emit(R"cc(
namespace _fl = ::$proto_ns$::internal::field_layout;
)cc");
}
}
void FileGenerator::GenerateSourceDefaultInstance(int idx, io::Printer* p) {
MessageGenerator* generator = message_generators_[idx].get();
// Generate the split instance first because it's needed in the constexpr
// constructor.
if (ShouldSplit(generator->descriptor(), options_)) {
// Use a union to disable the destructor of the _instance member.
// We can constant initialize, but the object will still have a non-trivial
// destructor that we need to elide.
//
// NO_DESTROY is not necessary for correctness. The empty destructor is
// enough. However, the empty destructor fails to be elided in some
// configurations (like non-opt or with certain sanitizers). NO_DESTROY is
// there just to improve performance and binary size in these builds.
p->Emit(
{
{"type", DefaultInstanceType(generator->descriptor(), options_,
/*split=*/true)},
{"name", DefaultInstanceName(generator->descriptor(), options_,
/*split=*/true)},
{"default",
[&] { generator->GenerateInitDefaultSplitInstance(p); }},
{"class", absl::StrCat(ClassName(generator->descriptor()),
"::Impl_::Split")},
},
R"cc(
struct $type$ {
PROTOBUF_CONSTEXPR $type$() : _instance{$default$} {}
union {
$class$ _instance;
};
};
PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 const $type$ $name$;
)cc");
}
generator->GenerateConstexprConstructor(p);
p->Emit(
{
{"type", DefaultInstanceType(generator->descriptor(), options_)},
{"name", DefaultInstanceName(generator->descriptor(), options_)},
{"default", [&] { generator->GenerateInitDefaultSplitInstance(p); }},
{"class", ClassName(generator->descriptor())},
},
R"cc(
struct $type$ {
PROTOBUF_CONSTEXPR $type$() : _instance(::_pbi::ConstantInitialized{}) {}
~$type$() {}
union {
$class$ _instance;
};
};
PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT
PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 $type$ $name$;
)cc");
for (int i = 0; i < generator->descriptor()->field_count(); ++i) {
const FieldDescriptor* field = generator->descriptor()->field(i);
if (!IsStringInlined(field, options_)) {
continue;
}
// Force the initialization of the inlined string in the default instance.
p->Emit(
{
{"class", ClassName(generator->descriptor())},
{"field", FieldName(field)},
{"default", DefaultInstanceName(generator->descriptor(), options_)},
{"member", FieldMemberName(field, ShouldSplit(field, options_))},
},
R"cc(
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 std::true_type
$class$::Impl_::_init_inline_$field$_ =
($default$._instance.$member$.Init(), std::true_type{});
)cc");
}
if (options_.lite_implicit_weak_fields) {
p->Emit(
{
{"ptr", DefaultInstancePtr(generator->descriptor(), options_)},
{"name", DefaultInstanceName(generator->descriptor(), options_)},
},
R"cc(
PROTOBUF_CONSTINIT const void* $ptr$ = &$name$;
)cc");
}
}
// A list of things defined in one .pb.cc file that we need to reference from
// another .pb.cc file.
struct FileGenerator::CrossFileReferences {
// When we forward-declare things, we want to create a sorted order so our
// output is deterministic and minimizes namespace changes.
struct DescCompare {
template <typename T>
bool operator()(const T* const& a, const T* const& b) const {
return a->full_name() < b->full_name();
}
bool operator()(const FileDescriptor* const& a,
const FileDescriptor* const& b) const {
return a->name() < b->name();
}
};
// Populated if we are referencing from messages or files.
absl::btree_set<const Descriptor*, DescCompare> weak_default_instances;
// Only if we are referencing from files.
absl::btree_set<const FileDescriptor*, DescCompare> strong_reflection_files;
absl::btree_set<const FileDescriptor*, DescCompare> weak_reflection_files;
};
void FileGenerator::GetCrossFileReferencesForField(const FieldDescriptor* field,
CrossFileReferences* refs) {
const Descriptor* msg = field->message_type();
if (msg == nullptr) {
return;
}
if (IsImplicitWeakField(field, options_, &scc_analyzer_) ||
IsWeak(field, options_)) {
refs->weak_default_instances.insert(msg);
}
}
void FileGenerator::GetCrossFileReferencesForFile(const FileDescriptor* file,
CrossFileReferences* refs) {
ForEachField(file, [this, refs](const FieldDescriptor* field) {
GetCrossFileReferencesForField(field, refs);
});
if (!HasDescriptorMethods(file, options_)) {
return;
}
for (int i = 0; i < file->dependency_count(); ++i) {
const FileDescriptor* dep = file->dependency(i);
if (IsDepWeak(dep)) {
refs->weak_reflection_files.insert(dep);
} else {
refs->strong_reflection_files.insert(dep);
}
}
}
// Generates references to variables defined in other files.
void FileGenerator::GenerateInternalForwardDeclarations(
const CrossFileReferences& refs, io::Printer* p) {
{
NamespaceOpener ns(p);
for (auto instance : refs.weak_default_instances) {
ns.ChangeTo(Namespace(instance, options_));
if (options_.lite_implicit_weak_fields) {
p->Emit({{"ptr", DefaultInstancePtr(instance, options_)}}, R"cc(
PROTOBUF_CONSTINIT __attribute__((weak)) const void* $ptr$ =
&::_pbi::implicit_weak_message_default_instance;
)cc");
} else {
p->Emit({{"type", DefaultInstanceType(instance, options_)},
{"name", DefaultInstanceName(instance, options_)}},
R"cc(
extern __attribute__((weak)) $type$ $name$;
)cc");
}
}
}
for (auto file : refs.weak_reflection_files) {
p->Emit({{"table", DescriptorTableName(file, options_)}}, R"cc(
extern __attribute__((weak)) const ::_pbi::DescriptorTable $table$;
)cc");
}
}
void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* p) {
auto v = p->WithVars(FileVars(file_, options_));
GenerateSourceIncludes(p);
GenerateSourcePrelude(p);
if (IsAnyMessage(file_, options_)) {
MuteWuninitialized(p);
}
CrossFileReferences refs;
ForEachField(message_generators_[idx]->descriptor(),
[this, &refs](const FieldDescriptor* field) {
GetCrossFileReferencesForField(field, &refs);
});
GenerateInternalForwardDeclarations(refs, p);
{
NamespaceOpener ns(Namespace(file_, options_), p);
p->Emit(
{
{"defaults", [&] { GenerateSourceDefaultInstance(idx, p); }},
{"class_methods",
[&] { message_generators_[idx]->GenerateClassMethods(p); }},
},
R"cc(
$defaults$;
$class_methods$;
// @@protoc_insertion_point(namespace_scope)
)cc");
}
{
NamespaceOpener proto_ns(ProtobufNamespace(options_), p);
message_generators_[idx]->GenerateSourceInProto2Namespace(p);
}
if (IsAnyMessage(file_, options_)) {
UnmuteWuninitialized(p);
}
p->Emit(R"cc(
// @@protoc_insertion_point(global_scope)
)cc");
}
void FileGenerator::GenerateSourceForExtension(int idx, io::Printer* p) {
auto v = p->WithVars(FileVars(file_, options_));
GenerateSourceIncludes(p);
GenerateSourcePrelude(p);
NamespaceOpener ns(Namespace(file_, options_), p);
extension_generators_[idx]->GenerateDefinition(p);
}
void FileGenerator::GenerateGlobalSource(io::Printer* p) {
auto v = p->WithVars(FileVars(file_, options_));
GenerateSourceIncludes(p);
GenerateSourcePrelude(p);
{
// Define the code to initialize reflection. This code uses a global
// constructor to register reflection data with the runtime pre-main.
if (HasDescriptorMethods(file_, options_)) {
GenerateReflectionInitializationCode(p);
}
}
NamespaceOpener ns(Namespace(file_, options_), p);
for (int i = 0; i < enum_generators_.size(); ++i) {
enum_generators_[i]->GenerateMethods(i, p);
}
}
void FileGenerator::GenerateSource(io::Printer* p) {
auto v = p->WithVars(FileVars(file_, options_));
GenerateSourceIncludes(p);
GenerateSourcePrelude(p);
CrossFileReferences refs;
GetCrossFileReferencesForFile(file_, &refs);
GenerateInternalForwardDeclarations(refs, p);
if (IsAnyMessage(file_, options_)) {
MuteWuninitialized(p);
}
{
NamespaceOpener ns(Namespace(file_, options_), p);
for (int i = 0; i < message_generators_.size(); ++i) {
GenerateSourceDefaultInstance(i, p);
}
}
{
if (HasDescriptorMethods(file_, options_)) {
// Define the code to initialize reflection. This code uses a global
// constructor to register reflection data with the runtime pre-main.
GenerateReflectionInitializationCode(p);
}
}
{
NamespaceOpener ns(Namespace(file_, options_), p);
// Actually implement the protos
// Generate enums.
for (int i = 0; i < enum_generators_.size(); ++i) {
enum_generators_[i]->GenerateMethods(i, p);
}
// Generate classes.
for (int i = 0; i < message_generators_.size(); ++i) {
p->Emit(R"(
$hrule_thick$
)");
message_generators_[i]->GenerateClassMethods(p);
}
if (HasGenericServices(file_, options_)) {
// Generate services.
for (int i = 0; i < service_generators_.size(); ++i) {
p->Emit(R"(
$hrule_thick$
)");
service_generators_[i]->GenerateImplementation(p);
}
}
// Define extensions.
for (int i = 0; i < extension_generators_.size(); ++i) {
extension_generators_[i]->GenerateDefinition(p);
}
p->Emit(R"cc(
// @@protoc_insertion_point(namespace_scope)
)cc");
}
{
NamespaceOpener proto_ns(ProtobufNamespace(options_), p);
for (int i = 0; i < message_generators_.size(); ++i) {
message_generators_[i]->GenerateSourceInProto2Namespace(p);
}
}
p->Emit(R"cc(
// @@protoc_insertion_point(global_scope)
)cc");
if (IsAnyMessage(file_, options_)) {
UnmuteWuninitialized(p);
}
IncludeFile("net/proto2/public/port_undef.inc", p);
}
void FileGenerator::GenerateReflectionInitializationCode(io::Printer* p) {
if (!message_generators_.empty()) {
p->Emit({{"len", message_generators_.size()}}, R"cc(
static ::_pb::Metadata $file_level_metadata$[$len$];
)cc");
}
if (!enum_generators_.empty()) {
p->Emit({{"len", enum_generators_.size()}}, R"cc(
static const ::_pb::EnumDescriptor* $file_level_enum_descriptors$[$len$];
)cc");
} else {
p->Emit(R"cc(
static constexpr const ::_pb::EnumDescriptor**
$file_level_enum_descriptors$ = nullptr;
)cc");
}
if (HasGenericServices(file_, options_) && file_->service_count() > 0) {
p->Emit({{"len", file_->service_count()}}, R"cc(
static const ::_pb::ServiceDescriptor*
$file_level_service_descriptors$[$len$];
)cc");
} else {
p->Emit(R"cc(
static constexpr const ::_pb::ServiceDescriptor**
$file_level_service_descriptors$ = nullptr;
)cc");
}
if (!message_generators_.empty()) {
std::vector<std::pair<size_t, size_t>> offsets;
offsets.reserve(message_generators_.size());
p->Emit(
{
{"offsets",
[&] {
for (int i = 0; i < message_generators_.size(); ++i) {
offsets.push_back(message_generators_[i]->GenerateOffsets(p));
}
}},
{"schemas",
[&] {
int offset = 0;
for (int i = 0; i < message_generators_.size(); ++i) {
message_generators_[i]->GenerateSchema(p, offset,
offsets[i].second);
offset += offsets[i].first;
}
}},
{"defaults",
[&] {
for (auto& gen : message_generators_) {
p->Emit(
{
{"ns", Namespace(gen->descriptor(), options_)},
{"class", ClassName(gen->descriptor())},
},
R"cc(
&$ns$::_$class$_default_instance_._instance,
)cc");
}
}},
},
R"cc(
const ::uint32_t $tablename$::offsets[] PROTOBUF_SECTION_VARIABLE(
protodesc_cold) = {
$offsets$,
};
static const ::_pbi::MigrationSchema
schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
$schemas$,
};
static const ::_pb::Message* const file_default_instances[] = {
$defaults$,
};
)cc");
} else {
// Ee still need these symbols to exist.
//
// MSVC doesn't like empty arrays, so we add a dummy.
p->Emit(R"cc(
const ::uint32_t $tablename$::offsets[1] = {};
static constexpr ::_pbi::MigrationSchema* schemas = nullptr;
static constexpr ::_pb::Message* const* file_default_instances = nullptr;
)cc");
}
// ---------------------------------------------------------------
// Embed the descriptor. We simply serialize the entire
// FileDescriptorProto/ and embed it as a string literal, which is parsed and
// built into real descriptors at initialization time.
FileDescriptorProto file_proto;
file_->CopyTo(&file_proto);
std::string file_data;
file_proto.SerializeToString(&file_data);
auto desc_name = UniqueName("descriptor_table_protodef", file_, options_);
p->Emit(
{{"desc_name", desc_name},
{"encoded_file_proto",
[&] {
absl::string_view data = file_data;
if (data.size() <= 65535) {
static constexpr size_t kBytesPerLine = 40;
while (!data.empty()) {
auto to_write = std::min(kBytesPerLine, data.size());
auto chunk = data.substr(0, to_write);
data = data.substr(to_write);
p->Emit({{"text", EscapeTrigraphs(absl::CEscape(chunk))}}, R"cc(
"$text$"
)cc");
}
return;
}
// Workaround for MSVC: "Error C1091: compiler limit: string exceeds
// 65535 bytes in length". Declare a static array of chars rather than
// use a string literal. Only write 25 bytes per line.
static constexpr size_t kBytesPerLine = 25;
while (!data.empty()) {
auto to_write = std::min(kBytesPerLine, data.size());
auto chunk = data.substr(0, to_write);
data = data.substr(to_write);
std::string line;
for (char c : chunk) {
absl::StrAppend(&line, "'",
absl::CEscape(absl::string_view(&c, 1)), "', ");
}
p->Emit({{"line", line}}, R"cc(
$line$
)cc");
}
}}},
R"cc(
const char $desc_name$[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
$encoded_file_proto$,
};
)cc");
CrossFileReferences refs;
GetCrossFileReferencesForFile(file_, &refs);
size_t num_deps =
refs.strong_reflection_files.size() + refs.weak_reflection_files.size();
// Build array of DescriptorTable deps.
if (num_deps > 0) {
p->Emit(
{
{"len", num_deps},
{"deps",
[&] {
for (auto dep : refs.strong_reflection_files) {
p->Emit({{"name", DescriptorTableName(dep, options_)}}, R"cc(
&::$name$,
)cc");
}
for (auto dep : refs.weak_reflection_files) {
p->Emit({{"name", DescriptorTableName(dep, options_)}}, R"cc(
&::$name$,
)cc");
}
}},
},
R"cc(
static const ::_pbi::DescriptorTable* const $desc_table$_deps[$len$] =
{
$deps$,
};
)cc");
}
// The DescriptorTable itself.
// Should be "bool eager = NeedsEagerDescriptorAssignment(file_, options_);"
// however this might cause a tsan failure in superroot b/148382879,
// so disable for now.
bool eager = false;
p->Emit(
{
{"eager", eager ? "true" : "false"},
{"file_proto_len", file_data.size()},
{"proto_name", desc_name},
{"deps_ptr", num_deps == 0
? "nullptr"
: absl::StrCat(p->LookupVar("desc_table"), "_deps")},
{"num_deps", num_deps},
{"num_msgs", message_generators_.size()},
{"msgs_ptr", message_generators_.empty()
? "nullptr"
: std::string(p->LookupVar("file_level_metadata"))},
},
R"cc(
static ::absl::once_flag $desc_table$_once;
const ::_pbi::DescriptorTable $desc_table$ = {
false,
$eager$,
$file_proto_len$,
$proto_name$,
"$filename$",
&$desc_table$_once,
$deps_ptr$,
$num_deps$,
$num_msgs$,
schemas,
file_default_instances,
$tablename$::offsets,
$msgs_ptr$,
$file_level_enum_descriptors$,
$file_level_service_descriptors$,
};
// This function exists to be marked as weak.
// It can significantly speed up compilation by breaking up LLVM's SCC
// in the .pb.cc translation units. Large translation units see a
// reduction of more than 35% of walltime for optimized builds. Without
// the weak attribute all the messages in the file, including all the
// vtables and everything they use become part of the same SCC through
// a cycle like:
// GetMetadata -> descriptor table -> default instances ->
// vtables -> GetMetadata
// By adding a weak function here we break the connection from the
// individual vtables back into the descriptor table.
PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* $desc_table$_getter() {
return &$desc_table$;
}
)cc");
// For descriptor.proto we want to avoid doing any dynamic initialization,
// because in some situations that would otherwise pull in a lot of
// unnecessary code that can't be stripped by --gc-sections. Descriptor
// initialization will still be performed lazily when it's needed.
if (file_->name() == "net/proto2/proto/descriptor.proto") {
return;
}
p->Emit({{"dummy", UniqueName("dynamic_init_dummy", file_, options_)}}, R"cc(
// Force running AddDescriptors() at dynamic initialization time.
PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
static ::_pbi::AddDescriptorsRunner $dummy$(&$desc_table$);
)cc");
}
class FileGenerator::ForwardDeclarations {
public:
void AddMessage(const Descriptor* d) { classes_.emplace(ClassName(d), d); }
void AddEnum(const EnumDescriptor* d) { enums_.emplace(ClassName(d), d); }
void AddSplit(const Descriptor* d) { splits_.emplace(ClassName(d), d); }
void Print(io::Printer* p, const Options& options) const {
for (const auto& e : enums_) {
p->Emit({{"enum", e.first, e.second}}, R"cc(
enum $enum$ : int;
bool $enum$_IsValid(int value);
)cc");
}
for (const auto& c : classes_) {
const Descriptor* desc = c.second;
p->Emit(
{
{"class", c.first, desc},
{"default_type", DefaultInstanceType(desc, options)},
{"default_name", DefaultInstanceName(desc, options)},
},
R"cc(
class $class$;
struct $default_type$;
$dllexport_decl $extern $default_type$ $default_name$;
)cc");
}
for (const auto& s : splits_) {
const Descriptor* desc = s.second;
p->Emit(
{
{"default_type",
DefaultInstanceType(desc, options, /*split=*/true)},
{"default_name",
DefaultInstanceName(desc, options, /*split=*/true)},
},
R"cc(
struct $default_type$;
$dllexport_decl $extern const $default_type$ $default_name$;
)cc");
}
}
void PrintTopLevelDecl(io::Printer* p, const Options& options) const {
for (const auto& c : classes_) {
p->Emit({{"class", QualifiedClassName(c.second, options)}}, R"cc(
template <>
$dllexport_decl $$class$* Arena::CreateMaybeMessage<$class$>(Arena*);
)cc");
}
}
private:
absl::btree_map<std::string, const Descriptor*> classes_;
absl::btree_map<std::string, const EnumDescriptor*> enums_;
absl::btree_map<std::string, const Descriptor*> splits_;
};
static void PublicImportDFS(
const FileDescriptor* fd,
absl::flat_hash_set<const FileDescriptor*>& fd_set) {
for (int i = 0; i < fd->public_dependency_count(); ++i) {
const FileDescriptor* dep = fd->public_dependency(i);
if (fd_set.insert(dep).second) {
PublicImportDFS(dep, fd_set);
}
}
}
void FileGenerator::GenerateForwardDeclarations(io::Printer* p) {
std::vector<const Descriptor*> classes;
FlattenMessagesInFile(file_, &classes); // All messages need forward decls.
std::vector<const EnumDescriptor*> enums;
if (options_.proto_h) { // proto.h needs extra forward declarations.
// All classes / enums referred to as field members
std::vector<const FieldDescriptor*> fields;
ListAllFields(file_, &fields);
for (const auto* field : fields) {
classes.push_back(field->containing_type());
classes.push_back(field->message_type());
enums.push_back(field->enum_type());
}
ListAllTypesForServices(file_, &classes);
}
// Calculate the set of files whose definitions we get through include.
// No need to forward declare types that are defined in these.
absl::flat_hash_set<const FileDescriptor*> public_set;
PublicImportDFS(file_, public_set);
absl::btree_map<std::string, ForwardDeclarations> decls;
for (const auto* d : classes) {
if (d != nullptr && !public_set.count(d->file()))
decls[Namespace(d, options_)].AddMessage(d);
}
for (const auto* e : enums) {
if (e != nullptr && !public_set.count(e->file()))
decls[Namespace(e, options_)].AddEnum(e);
}
for (const auto& mg : message_generators_) {
const Descriptor* d = mg->descriptor();
if (d != nullptr && public_set.count(d->file()) == 0u &&
ShouldSplit(mg->descriptor(), options_))
decls[Namespace(d, options_)].AddSplit(d);
}
NamespaceOpener ns(p);
for (const auto& decl : decls) {
ns.ChangeTo(decl.first);
decl.second.Print(p, options_);
}
ns.ChangeTo("PROTOBUF_NAMESPACE_ID");
for (const auto& decl : decls) {
decl.second.PrintTopLevelDecl(p, options_);
}
}
void FileGenerator::GenerateLibraryIncludes(io::Printer* p) {
if (UsingImplicitWeakFields(file_, options_)) {
IncludeFile("net/proto2/public/implicit_weak_message.h", p);
}
if (HasWeakFields(file_, options_)) {
GOOGLE_CHECK(!options_.opensource_runtime);
IncludeFile("net/proto2/public/weak_field_map.h", p);
}
if (HasLazyFields(file_, options_, &scc_analyzer_)) {
GOOGLE_CHECK(!options_.opensource_runtime);
IncludeFile("net/proto2/public/lazy_field.h", p);
}
if (ShouldVerify(file_, options_, &scc_analyzer_)) {
IncludeFile("net/proto2/public/wire_format_verify.h", p);
}
if (options_.opensource_runtime) {
// Verify the protobuf library header version is compatible with the protoc
// version before going any further.
IncludeFile("net/proto2/public/port_def.inc", p);
p->Emit(
{
{"min_version", PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC},
{"version", PROTOBUF_VERSION},
},
R"(
#if PROTOBUF_VERSION < $min_version$
#error "This file was generated by a newer version of protoc which is"
#error "incompatible with your Protocol Buffer headers. Please update"
#error "your headers."
#endif // PROTOBUF_VERSION
#if $version$ < 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."
#endif // PROTOBUF_MIN_PROTOC_VERSION
)");
IncludeFile("net/proto2/public/port_undef.inc", p);
}
// OK, it's now safe to #include other files.
IncludeFile("net/proto2/io/public/coded_stream.h", p);
IncludeFile("net/proto2/public/arena.h", p);
IncludeFile("net/proto2/public/arenastring.h", p);
if ((options_.force_inline_string || options_.profile_driven_inline_string) &&
!options_.opensource_runtime) {
IncludeFile("net/proto2/public/inlined_string_field.h", p);
}
if (HasSimpleBaseClasses(file_, options_)) {
IncludeFile("net/proto2/public/generated_message_bases.h", p);
}
if (HasGeneratedMethods(file_, options_) &&
options_.tctable_mode != Options::kTCTableNever) {
IncludeFile("net/proto2/public/generated_message_tctable_decl.h", p);
}
IncludeFile("net/proto2/public/generated_message_util.h", p);
IncludeFile("net/proto2/public/metadata_lite.h", p);
if (HasDescriptorMethods(file_, options_)) {
IncludeFile("net/proto2/public/generated_message_reflection.h", p);
}
if (!message_generators_.empty()) {
if (HasDescriptorMethods(file_, options_)) {
IncludeFile("net/proto2/public/message.h", p);
} else {
IncludeFile("net/proto2/public/message_lite.h", p);
}
}
if (options_.opensource_runtime) {
// Open-source relies on unconditional includes of these.
IncludeFileAndExport("net/proto2/public/repeated_field.h", p);
IncludeFileAndExport("net/proto2/public/extension_set.h", p);
} else {
// Google3 includes these files only when they are necessary.
if (HasExtensionsOrExtendableMessage(file_)) {
IncludeFileAndExport("net/proto2/public/extension_set.h", p);
}
if (HasRepeatedFields(file_)) {
IncludeFileAndExport("net/proto2/public/repeated_field.h", p);
}
if (HasStringPieceFields(file_, options_)) {
IncludeFile("net/proto2/public/string_piece_field_support.h", p);
}
if (HasCordFields(file_, options_)) {
p->Emit(R"(
#include "third_party/absl/strings/cord.h"
)");
}
}
if (HasMapFields(file_)) {
IncludeFileAndExport("net/proto2/public/map.h", p);
if (HasDescriptorMethods(file_, options_)) {
IncludeFile("net/proto2/public/map_entry.h", p);
IncludeFile("net/proto2/public/map_field_inl.h", p);
} else {
IncludeFile("net/proto2/public/map_entry_lite.h", p);
IncludeFile("net/proto2/public/map_field_lite.h", p);
}
}
if (HasEnumDefinitions(file_)) {
if (HasDescriptorMethods(file_, options_)) {
IncludeFile("net/proto2/public/generated_enum_reflection.h", p);
} else {
IncludeFile("net/proto2/public/generated_enum_util.h", p);
}
}
if (HasGenericServices(file_, options_)) {
IncludeFile("net/proto2/public/service.h", p);
}
if (UseUnknownFieldSet(file_, options_) && !message_generators_.empty()) {
IncludeFile("net/proto2/public/unknown_field_set.h", p);
}
}
void FileGenerator::GenerateMetadataPragma(io::Printer* p,
absl::string_view info_path) {
if (info_path.empty() || options_.annotation_pragma_name.empty() ||
options_.annotation_guard_name.empty()) {
return;
}
p->Emit(
{
{"guard", options_.annotation_guard_name},
{"pragma", options_.annotation_pragma_name},
{"info_path", std::string(info_path)},
},
R"(
#ifdef $guard$
#pragma $pragma$ "$info_path$"
#endif // $guard$
)");
}
void FileGenerator::GenerateDependencyIncludes(io::Printer* p) {
for (int i = 0; i < file_->dependency_count(); ++i) {
const FileDescriptor* dep = file_->dependency(i);
// Do not import weak deps.
if (IsDepWeak(dep)) {
continue;
}
std::string basename = StripProto(dep->name());
if (IsBootstrapProto(options_, file_)) {
GetBootstrapBasename(options_, basename, &basename);
}
p->Emit(
{{"name", CreateHeaderInclude(absl::StrCat(basename, ".pb.h"), dep)}},
R"(
#include $name$
)");
}
}
void FileGenerator::GenerateGlobalStateFunctionDeclarations(io::Printer* p) {
// Forward-declare the DescriptorTable because this is referenced by .pb.cc
// files depending on this file.
//
// The TableStruct is also outputted in weak_message_field.cc, because the
// weak fields must refer to table struct but cannot include the header.
// Also it annotates extra weak attributes.
// TODO(gerbens) make sure this situation is handled better.
p->Emit(R"cc(
// Internal implementation detail -- do not use these members.
struct $dllexport_decl $$tablename$ {
static const ::uint32_t offsets[];
};
)cc");
if (HasDescriptorMethods(file_, options_)) {
p->Emit(R"cc(
$dllexport_decl $extern const ::$proto_ns$::internal::DescriptorTable
$desc_table$;
)cc");
}
}
void FileGenerator::GenerateMessageDefinitions(io::Printer* p) {
for (int i = 0; i < message_generators_.size(); ++i) {
p->Emit(R"cc(
$hrule_thin$
)cc");
message_generators_[i]->GenerateClassDefinition(p);
}
}
void FileGenerator::GenerateEnumDefinitions(io::Printer* p) {
for (int i = 0; i < enum_generators_.size(); ++i) {
enum_generators_[i]->GenerateDefinition(p);
}
}
void FileGenerator::GenerateServiceDefinitions(io::Printer* p) {
if (!HasGenericServices(file_, options_)) {
return;
}
for (int i = 0; i < service_generators_.size(); ++i) {
p->Emit(R"cc(
$hrule_thin$
)cc");
service_generators_[i]->GenerateDeclarations(p);
}
p->Emit(R"cc(
$hrule_thick$
)cc");
}
void FileGenerator::GenerateExtensionIdentifiers(io::Printer* p) {
// Declare extension identifiers. These are in global scope and so only
// the global scope extensions.
for (auto& extension_generator : extension_generators_) {
if (extension_generator->IsScoped()) {
continue;
}
extension_generator->GenerateDeclaration(p);
}
}
void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* p) {
// TODO(gerbens) remove pragmas when gcc is no longer used. Current version
// of gcc fires a bogus error when compiled with strict-aliasing.
p->Emit(R"(
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstrict-aliasing"
#endif // __GNUC__
)");
for (int i = 0; i < message_generators_.size(); ++i) {
p->Emit(R"cc(
$hrule_thin$
)cc");
message_generators_[i]->GenerateInlineMethods(p);
}
p->Emit(R"(
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif // __GNUC__
)");
}
void FileGenerator::GenerateProto2NamespaceEnumSpecializations(io::Printer* p) {
// Emit GetEnumDescriptor specializations into google::protobuf namespace.
if (!HasEnumDefinitions(file_)) {
return;
}
p->PrintRaw("\n");
NamespaceOpener ns(ProtobufNamespace(options_), p);
p->PrintRaw("\n");
for (auto& gen : enum_generators_) {
gen->GenerateGetEnumDescriptorSpecializations(p);
}
p->PrintRaw("\n");
}
} // namespace cpp
} // namespace compiler
} // namespace protobuf
} // namespace google