1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 // Author: kenton@google.com (Kenton Varda)
9 // Based on original Protocol Buffers design by
10 // Sanjay Ghemawat, Jeff Dean, and others.
11
12 #include "google/protobuf/compiler/cpp/file.h"
13
14 #include <algorithm>
15 #include <cstddef>
16 #include <functional>
17 #include <memory>
18 #include <string>
19 #include <utility>
20 #include <vector>
21
22 #include "absl/container/btree_map.h"
23 #include "absl/container/btree_set.h"
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/container/flat_hash_set.h"
26 #include "absl/log/absl_check.h"
27 #include "absl/strings/escaping.h"
28 #include "absl/strings/match.h"
29 #include "absl/strings/str_cat.h"
30 #include "absl/strings/str_join.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/strings/strip.h"
33 #include "google/protobuf/compiler/code_generator.h"
34 #include "google/protobuf/compiler/cpp/enum.h"
35 #include "google/protobuf/compiler/cpp/extension.h"
36 #include "google/protobuf/compiler/cpp/helpers.h"
37 #include "google/protobuf/compiler/cpp/message.h"
38 #include "google/protobuf/compiler/cpp/names.h"
39 #include "google/protobuf/compiler/cpp/options.h"
40 #include "google/protobuf/compiler/cpp/service.h"
41 #include "google/protobuf/compiler/retention.h"
42 #include "google/protobuf/compiler/versions.h"
43 #include "google/protobuf/descriptor.h"
44 #include "google/protobuf/descriptor.pb.h"
45 #include "google/protobuf/dynamic_message.h"
46 #include "google/protobuf/io/printer.h"
47
48 // Must be last.
49 #include "google/protobuf/port_def.inc"
50
51 namespace google {
52 namespace protobuf {
53 namespace compiler {
54 namespace cpp {
55 namespace {
56 using Sub = ::google::protobuf::io::Printer::Sub;
57 using ::google::protobuf::internal::cpp::IsLazilyInitializedFile;
58
FileVars(const FileDescriptor * file,const Options & options)59 absl::flat_hash_map<absl::string_view, std::string> FileVars(
60 const FileDescriptor* file, const Options& options) {
61 return {
62 {"filename", std::string(file->name())},
63 {"package_ns", Namespace(file, options)},
64 {"tablename", UniqueName("TableStruct", file, options)},
65 {"desc_table", DescriptorTableName(file, options)},
66 {"dllexport_decl", options.dllexport_decl},
67 {"file_level_metadata", UniqueName("file_level_metadata", file, options)},
68 {"file_level_enum_descriptors",
69 UniqueName("file_level_enum_descriptors", file, options)},
70 {"file_level_service_descriptors",
71 UniqueName("file_level_service_descriptors", file, options)},
72 };
73 }
74
75 // TODO: remove pragmas that suppresses uninitialized warnings when
76 // clang bug is fixed.
MuteWuninitialized(io::Printer * p)77 void MuteWuninitialized(io::Printer* p) {
78 p->Emit(R"(
79 #if defined(__llvm__)
80 #pragma clang diagnostic push
81 #pragma clang diagnostic ignored "-Wuninitialized"
82 #endif // __llvm__
83 )");
84 }
85
UnmuteWuninitialized(io::Printer * p)86 void UnmuteWuninitialized(io::Printer* p) {
87 p->Emit(R"(
88 #if defined(__llvm__)
89 #pragma clang diagnostic pop
90 #endif // __llvm__
91 )");
92 }
93 } // namespace
94
ShouldSkipDependencyImports(const FileDescriptor * dep) const95 bool FileGenerator::ShouldSkipDependencyImports(
96 const FileDescriptor* dep) const {
97 // Do not import weak deps.
98 if (!options_.opensource_runtime && IsDepWeak(dep)) {
99 return true;
100 }
101
102 // Skip feature imports, which are a visible (but non-functional) deviation
103 // between editions and legacy syntax.
104 if (options_.strip_nonfunctional_codegen &&
105 IsKnownFeatureProto(dep->name())) {
106 return true;
107 }
108
109 return false;
110 }
111
FileGenerator(const FileDescriptor * file,const Options & options)112 FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options)
113 : file_(file), options_(options), scc_analyzer_(options) {
114 std::vector<const Descriptor*> msgs = FlattenMessagesInFile(file);
115 std::vector<const Descriptor*> msgs_topologically_ordered =
116 TopologicalSortMessagesInFile(file, scc_analyzer_);
117 ABSL_CHECK(msgs_topologically_ordered.size() == msgs.size())
118 << "Size mismatch";
119
120 for (size_t i = 0; i < msgs.size(); ++i) {
121 message_generators_.push_back(std::make_unique<MessageGenerator>(
122 msgs[i], variables_, i, options, &scc_analyzer_));
123 message_generators_.back()->AddGenerators(&enum_generators_,
124 &extension_generators_);
125 }
126 absl::flat_hash_map<const Descriptor*, int> msg_to_index;
127 for (size_t i = 0; i < msgs.size(); ++i) {
128 msg_to_index[msgs[i]] = i;
129 }
130 // Populate the topological order.
131 for (size_t i = 0; i < msgs.size(); ++i) {
132 auto it = msg_to_index.find(msgs_topologically_ordered[i]);
133 ABSL_DCHECK(it != msg_to_index.end())
134 << "Topological order has a message not present in the file!";
135 message_generators_topologically_ordered_.push_back(it->second);
136 }
137
138 for (int i = 0; i < file->enum_type_count(); ++i) {
139 enum_generators_.push_back(
140 std::make_unique<EnumGenerator>(file->enum_type(i), options));
141 }
142
143 for (int i = 0; i < file->service_count(); ++i) {
144 service_generators_.push_back(std::make_unique<ServiceGenerator>(
145 file->service(i), variables_, options));
146 }
147 if (HasGenericServices(file_, options_)) {
148 for (size_t i = 0; i < service_generators_.size(); ++i) {
149 service_generators_[i]->index_in_metadata_ = i;
150 }
151 }
152
153 for (int i = 0; i < file->extension_count(); ++i) {
154 extension_generators_.push_back(std::make_unique<ExtensionGenerator>(
155 file->extension(i), options, &scc_analyzer_));
156 }
157
158 for (int i = 0; i < file->weak_dependency_count(); ++i) {
159 weak_deps_.insert(file->weak_dependency(i));
160 }
161 }
162
GenerateFile(io::Printer * p,GeneratedFileType file_type,std::function<void ()> cb)163 void FileGenerator::GenerateFile(io::Printer* p, GeneratedFileType file_type,
164 std::function<void()> cb) {
165 auto v = p->WithVars(FileVars(file_, options_));
166 auto guard = IncludeGuard(file_, file_type, options_);
167 p->Print(
168 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
169 "// NO CHECKED-IN PROTOBUF "
170 "GENCODE\n"
171 "// source: $filename$\n");
172 if (options_.opensource_runtime) {
173 p->Print("// Protobuf C++ Version: $protobuf_cpp_version$\n",
174 "protobuf_cpp_version", PROTOBUF_CPP_VERSION_STRING);
175 }
176 p->Print("\n");
177 p->Emit({{"cb", cb}, {"guard", guard}}, R"(
178 #ifndef $guard$
179 #define $guard$
180
181 #include <limits>
182 #include <string>
183 #include <type_traits>
184 #include <utility>
185
186 $cb$;
187
188 #endif // $guard$
189 )");
190 }
191
GenerateMacroUndefs(io::Printer * p)192 void FileGenerator::GenerateMacroUndefs(io::Printer* p) {
193 // Only do this for protobuf's own types. There are some google3 protos using
194 // macros as field names and the generated code compiles after the macro
195 // expansion. Undefing these macros actually breaks such code.
196 if (file_->name() != "third_party/protobuf/compiler/plugin.proto" &&
197 file_->name() != "google/protobuf/compiler/plugin.proto") {
198 return;
199 }
200
201 std::vector<const FieldDescriptor*> fields;
202 ListAllFields(file_, &fields);
203
204 absl::flat_hash_set<absl::string_view> all_fields;
205 for (const FieldDescriptor* field : fields) {
206 all_fields.insert(field->name());
207 }
208
209 for (absl::string_view name : {"major", "minor"}) {
210 if (!all_fields.contains(name)) {
211 continue;
212 }
213
214 p->Emit({{"name", std::string(name)}}, R"(
215 #ifdef $name$
216 #undef $name$
217 #endif // $name$
218 )");
219 }
220 }
221
222
GenerateSharedHeaderCode(io::Printer * p)223 void FileGenerator::GenerateSharedHeaderCode(io::Printer* p) {
224 p->Emit(
225 {
226 {"port_def",
227 [&] { IncludeFile("third_party/protobuf/port_def.inc", p); }},
228 {"port_undef",
229 [&] { IncludeFile("third_party/protobuf/port_undef.inc", p); }},
230 {"dllexport_macro", FileDllExport(file_, options_)},
231 {"undefs", [&] { GenerateMacroUndefs(p); }},
232 {"global_state_decls",
233 [&] { GenerateGlobalStateFunctionDeclarations(p); }},
234 {"any_metadata",
235 [&] {
236 NamespaceOpener ns(ProtobufNamespace(options_), p);
237 p->Emit(R"cc(
238 namespace internal {
239 template <typename T>
240 ::absl::string_view GetAnyMessageName();
241 } // namespace internal
242 )cc");
243 }},
244 {"fwd_decls", [&] { GenerateForwardDeclarations(p); }},
245 {"proto2_ns_enums",
246 [&] { GenerateProto2NamespaceEnumSpecializations(p); }},
247 {"main_decls",
248 [&] {
249 NamespaceOpener ns(Namespace(file_, options_), p);
250 p->Emit(
251 {
252 {"enums", [&] { GenerateEnumDefinitions(p); }},
253 {"messages", [&] { GenerateMessageDefinitions(p); }},
254 {"services", [&] { GenerateServiceDefinitions(p); }},
255 {"extensions", [&] { GenerateExtensionIdentifiers(p); }},
256 {"inline_fns",
257 [&] { GenerateInlineFunctionDefinitions(p); }},
258 },
259 R"(
260 $enums$
261
262 $hrule_thick$
263
264 $messages$
265
266 $hrule_thick$
267
268 $services$
269
270 $extensions$
271
272 $hrule_thick$
273
274 $inline_fns$
275
276 // @@protoc_insertion_point(namespace_scope)
277 )");
278 }},
279 },
280 R"(
281 // Must be included last.
282 $port_def$
283
284 #define $dllexport_macro$$ dllexport_decl$
285 $undefs$
286
287 $any_metadata$;
288
289 $global_state_decls$;
290 $fwd_decls$
291
292 $main_decls$
293
294 $proto2_ns_enums$
295
296 // @@protoc_insertion_point(global_scope)
297
298 $port_undef$
299 )");
300 }
301
GenerateProtoHeader(io::Printer * p,absl::string_view info_path)302 void FileGenerator::GenerateProtoHeader(io::Printer* p,
303 absl::string_view info_path) {
304 if (!options_.proto_h) {
305 return;
306 }
307
308 GenerateFile(p, GeneratedFileType::kProtoH, [&] {
309 if (!options_.opensource_runtime) {
310 p->Emit(R"(
311 #ifdef SWIG
312 #error "Do not SWIG-wrap protobufs."
313 #endif // SWIG
314 )");
315 }
316 if (IsBootstrapProto(options_, file_)) {
317 p->Emit({{"name", StripProto(file_->name())}}, R"cc(
318 // IWYU pragma: private, include "$name$.proto.h"
319 )cc");
320 }
321
322 p->Emit(
323 {
324 {"library_includes", [&] { GenerateLibraryIncludes(p); }},
325 {"proto_includes",
326 [&] {
327 for (int i = 0; i < file_->public_dependency_count(); ++i) {
328 const FileDescriptor* dep = file_->public_dependency(i);
329 p->Emit({{"name", StripProto(dep->name())}}, R"(
330 #include "$name$.proto.h"
331 )");
332 }
333 }},
334 {"metadata_pragma", [&] { GenerateMetadataPragma(p, info_path); }},
335 {"header_main", [&] { GenerateSharedHeaderCode(p); }},
336 },
337 R"cc(
338 $library_includes$;
339 $proto_includes$;
340 // @@protoc_insertion_point(includes)
341
342 $metadata_pragma$;
343 $header_main$;
344 )cc");
345 });
346 }
347
GeneratePBHeader(io::Printer * p,absl::string_view info_path)348 void FileGenerator::GeneratePBHeader(io::Printer* p,
349 absl::string_view info_path) {
350 GenerateFile(p, GeneratedFileType::kPbH, [&] {
351 p->Emit(
352 {
353 {"library_includes",
354 [&] {
355 if (options_.proto_h) {
356 std::string target_basename = StripProto(file_->name());
357 if (!options_.opensource_runtime) {
358 GetBootstrapBasename(options_, target_basename,
359 &target_basename);
360 }
361 p->Emit({{"name", target_basename}}, R"(
362 #include "$name$.proto.h" // IWYU pragma: export
363 )");
364 } else {
365 GenerateLibraryIncludes(p);
366 }
367 }},
368 {"proto_includes",
369 [&] {
370 if (options_.transitive_pb_h) {
371 GenerateDependencyIncludes(p);
372 }
373 }},
374 {"metadata_pragma", [&] { GenerateMetadataPragma(p, info_path); }},
375 {"header_main",
376 [&] {
377 if (!options_.proto_h) {
378 GenerateSharedHeaderCode(p);
379 return;
380 }
381
382 {
383 NamespaceOpener ns(Namespace(file_, options_), p);
384 p->Emit(R"cc(
385
386 // @@protoc_insertion_point(namespace_scope)
387 )cc");
388 }
389 p->Emit(R"cc(
390
391 // @@protoc_insertion_point(global_scope)
392 )cc");
393 }},
394 },
395 R"cc(
396 $library_includes$;
397 $proto_includes$;
398 // @@protoc_insertion_point(includes)
399
400 $metadata_pragma$;
401 $header_main$;
402 )cc");
403 });
404 }
405
DoIncludeFile(absl::string_view google3_name,bool do_export,io::Printer * p)406 void FileGenerator::DoIncludeFile(absl::string_view google3_name,
407 bool do_export, io::Printer* p) {
408 constexpr absl::string_view prefix = "third_party/protobuf/";
409 ABSL_CHECK(absl::StartsWith(google3_name, prefix)) << google3_name;
410
411 auto v = p->WithVars(
412 {{"export_suffix", do_export ? "// IWYU pragma: export" : ""}});
413
414 if (options_.opensource_runtime) {
415 absl::ConsumePrefix(&google3_name, prefix);
416 absl::ConsumePrefix(&google3_name, "internal/");
417 absl::ConsumePrefix(&google3_name, "proto/");
418 absl::ConsumePrefix(&google3_name, "public/");
419
420 std::string path;
421 if (absl::ConsumePrefix(&google3_name, "io/public/")) {
422 path = absl::StrCat("io/", google3_name);
423 } else {
424 path = std::string(google3_name);
425 }
426
427 if (options_.runtime_include_base.empty()) {
428 p->Emit({{"path", path}}, R"(
429 #include "google/protobuf/$path$"$ export_suffix$
430 )");
431 } else {
432 p->Emit({{"base", options_.runtime_include_base}, {"path", path}},
433 R"(
434 #include "$base$google/protobuf/$path$"$ export_suffix$
435 )");
436 }
437 } else {
438 std::string path(google3_name);
439 // The bootstrapped proto generated code needs to use the
440 // third_party/protobuf header paths to avoid circular dependencies.
441 if (options_.bootstrap) {
442 constexpr absl::string_view bootstrap_prefix = "net/proto2/public";
443 if (absl::ConsumePrefix(&google3_name, bootstrap_prefix)) {
444 path = absl::StrCat("third_party/protobuf", google3_name);
445 }
446 }
447
448 p->Emit({{"path", path}}, R"(
449 #include "$path$"$ export_suffix$
450 )");
451 }
452 }
453
CreateHeaderInclude(absl::string_view basename,const FileDescriptor * file)454 std::string FileGenerator::CreateHeaderInclude(absl::string_view basename,
455 const FileDescriptor* file) {
456 if (options_.opensource_runtime && IsWellKnownMessage(file) &&
457 !options_.runtime_include_base.empty()) {
458 return absl::StrCat("\"", options_.runtime_include_base, basename, "\"");
459 }
460
461 return absl::StrCat("\"", basename, "\"");
462 }
463
GenerateSourceIncludes(io::Printer * p)464 void FileGenerator::GenerateSourceIncludes(io::Printer* p) {
465 std::string target_basename = StripProto(file_->name());
466 if (!options_.opensource_runtime) {
467 GetBootstrapBasename(options_, target_basename, &target_basename);
468 }
469
470 absl::StrAppend(&target_basename, options_.proto_h ? ".proto.h" : ".pb.h");
471 p->Print(
472 "// Generated by the protocol buffer compiler. DO NOT EDIT!\n"
473 "// NO CHECKED-IN PROTOBUF "
474 "GENCODE\n"
475 "// source: $filename$\n");
476 if (options_.opensource_runtime) {
477 p->Print("// Protobuf C++ Version: $protobuf_cpp_version$\n",
478 "protobuf_cpp_version", PROTOBUF_CPP_VERSION_STRING);
479 }
480 p->Print("\n");
481 p->Emit({{"h_include", CreateHeaderInclude(target_basename, file_)}},
482 R"(
483 #include $h_include$
484
485 #include <algorithm>
486 #include <type_traits>
487 )");
488
489 IncludeFile("third_party/protobuf/io/coded_stream.h", p);
490 IncludeFile("third_party/protobuf/generated_message_tctable_impl.h", p);
491 // TODO This is to include parse_context.h, we need a better way
492 IncludeFile("third_party/protobuf/extension_set.h", p);
493 IncludeFile("third_party/protobuf/generated_message_util.h", p);
494 IncludeFile("third_party/protobuf/wire_format_lite.h", p);
495
496 if (ShouldVerify(file_, options_, &scc_analyzer_)) {
497 IncludeFile("third_party/protobuf/wire_format_verify.h", p);
498 }
499
500 // Unknown fields implementation in lite mode uses StringOutputStream
501 if (!UseUnknownFieldSet(file_, options_) && !message_generators_.empty()) {
502 IncludeFile("third_party/protobuf/io/zero_copy_stream_impl_lite.h", p);
503 }
504
505 if (HasDescriptorMethods(file_, options_)) {
506 IncludeFile("third_party/protobuf/descriptor.h", p);
507 IncludeFile("third_party/protobuf/generated_message_reflection.h", p);
508 IncludeFile("third_party/protobuf/reflection_ops.h", p);
509 IncludeFile("third_party/protobuf/wire_format.h", p);
510 }
511
512 if (options_.proto_h) {
513 // Use the smaller .proto.h files.
514 for (int i = 0; i < file_->dependency_count(); ++i) {
515 const FileDescriptor* dep = file_->dependency(i);
516
517 if (ShouldSkipDependencyImports(dep)) continue;
518
519 std::string basename = StripProto(dep->name());
520 if (options_.bootstrap) {
521 GetBootstrapBasename(options_, basename, &basename);
522 }
523 p->Emit({{"name", basename}}, R"(
524 #include "$name$.proto.h"
525 )");
526 }
527 }
528
529 if (HasCordFields(file_, options_)) {
530 p->Emit(R"(
531 #include "absl/strings/internal/string_constant.h"
532 )");
533 }
534
535 p->Emit(R"cc(
536 // @@protoc_insertion_point(includes)
537
538 // Must be included last.
539 )cc");
540 IncludeFile("third_party/protobuf/port_def.inc", p);
541 }
542
GenerateSourcePrelude(io::Printer * p)543 void FileGenerator::GenerateSourcePrelude(io::Printer* p) {
544 // For MSVC builds, we use #pragma init_seg to move the initialization of our
545 // libraries to happen before the user code.
546 // This worksaround the fact that MSVC does not do constant initializers when
547 // required by the standard.
548 p->Emit(R"cc(
549 PROTOBUF_PRAGMA_INIT_SEG
550 namespace _pb = ::$proto_ns$;
551 namespace _pbi = ::$proto_ns$::internal;
552 namespace _fl = ::$proto_ns$::internal::field_layout;
553 )cc");
554 }
555
GenerateSourceDefaultInstance(int idx,io::Printer * p)556 void FileGenerator::GenerateSourceDefaultInstance(int idx, io::Printer* p) {
557 MessageGenerator* generator = message_generators_[idx].get();
558
559 if (!ShouldGenerateClass(generator->descriptor(), options_)) return;
560
561 // Generate the split instance first because it's needed in the constexpr
562 // constructor.
563 if (ShouldSplit(generator->descriptor(), options_)) {
564 // Use a union to disable the destructor of the _instance member.
565 // We can constant initialize, but the object will still have a non-trivial
566 // destructor that we need to elide.
567 //
568 // NO_DESTROY is not necessary for correctness. The empty destructor is
569 // enough. However, the empty destructor fails to be elided in some
570 // configurations (like non-opt or with certain sanitizers). NO_DESTROY is
571 // there just to improve performance and binary size in these builds.
572 p->Emit(
573 {
574 {"type", DefaultInstanceType(generator->descriptor(), options_,
575 /*split=*/true)},
576 {"name", DefaultInstanceName(generator->descriptor(), options_,
577 /*split=*/true)},
578 {"default",
579 [&] { generator->GenerateInitDefaultSplitInstance(p); }},
580 {"class", absl::StrCat(ClassName(generator->descriptor()),
581 "::Impl_::Split")},
582 },
583 R"cc(
584 struct $type$ {
585 PROTOBUF_CONSTEXPR $type$() : _instance{$default$} {}
586 union {
587 $class$ _instance;
588 };
589 };
590
591 PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT$ dllexport_decl$
592 PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 const $type$ $name$;
593 )cc");
594 }
595
596 generator->GenerateConstexprConstructor(p);
597
598 if (IsFileDescriptorProto(file_, options_)) {
599 p->Emit(
600 {
601 {"type", DefaultInstanceType(generator->descriptor(), options_)},
602 {"name", DefaultInstanceName(generator->descriptor(), options_)},
603 {"class", ClassName(generator->descriptor())},
604 },
605 R"cc(
606 struct $type$ {
607 #if defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
608 constexpr $type$() : _instance(::_pbi::ConstantInitialized{}) {}
609 #else // defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
610 $type$() {}
611 void Init() { ::new (&_instance) $class$(); };
612 #endif // defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
613 ~$type$() {}
614 union {
615 $class$ _instance;
616 };
617 };
618
619 PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT$ dllexport_decl$
620 PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 $type$ $name$;
621 )cc");
622 } else if (UsingImplicitWeakDescriptor(file_, options_)) {
623 p->Emit(
624 {
625 {"index", generator->index_in_file_messages()},
626 {"type", DefaultInstanceType(generator->descriptor(), options_)},
627 {"name", DefaultInstanceName(generator->descriptor(), options_)},
628 {"class", ClassName(generator->descriptor())},
629 {"section", WeakDefaultInstanceSection(
630 generator->descriptor(),
631 generator->index_in_file_messages(), options_)},
632 },
633 R"cc(
634 struct $type$ {
635 PROTOBUF_CONSTEXPR $type$() : _instance(::_pbi::ConstantInitialized{}) {}
636 ~$type$() {}
637 //~ _instance must be the first member.
638 union {
639 $class$ _instance;
640 };
641 ::_pbi::WeakDescriptorDefaultTail tail = {
642 file_default_instances + $index$, sizeof($type$)};
643 };
644
645 PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT$ dllexport_decl$
646 PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 $type$ $name$
647 __attribute__((section("$section$")));
648 )cc");
649 } else {
650 p->Emit(
651 {
652 {"type", DefaultInstanceType(generator->descriptor(), options_)},
653 {"name", DefaultInstanceName(generator->descriptor(), options_)},
654 {"class", ClassName(generator->descriptor())},
655 },
656 R"cc(
657 struct $type$ {
658 PROTOBUF_CONSTEXPR $type$() : _instance(::_pbi::ConstantInitialized{}) {}
659 ~$type$() {}
660 union {
661 $class$ _instance;
662 };
663 };
664
665 PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT$ dllexport_decl$
666 PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 $type$ $name$;
667 )cc");
668 }
669
670 for (int i = 0; i < generator->descriptor()->field_count(); ++i) {
671 const FieldDescriptor* field = generator->descriptor()->field(i);
672 if (!IsStringInlined(field, options_)) {
673 continue;
674 }
675
676 // Force the initialization of the inlined string in the default instance.
677 p->Emit(
678 {
679 {"class", ClassName(generator->descriptor())},
680 {"field", FieldName(field)},
681 {"default", DefaultInstanceName(generator->descriptor(), options_)},
682 {"member", FieldMemberName(field, ShouldSplit(field, options_))},
683 },
684 R"cc(
685 PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 std::true_type
686 $class$::Impl_::_init_inline_$field$_ =
687 ($default$._instance.$member$.Init(), std::true_type{});
688 )cc");
689 }
690
691 if (options_.lite_implicit_weak_fields) {
692 p->Emit(
693 {
694 {"ptr", DefaultInstancePtr(generator->descriptor(), options_)},
695 {"name", DefaultInstanceName(generator->descriptor(), options_)},
696 },
697 R"cc(
698 PROTOBUF_CONSTINIT const void* $ptr$ = &$name$;
699 )cc");
700 }
701 }
702
703 // A list of things defined in one .pb.cc file that we need to reference from
704 // another .pb.cc file.
705 struct FileGenerator::CrossFileReferences {
706 // When we forward-declare things, we want to create a sorted order so our
707 // output is deterministic and minimizes namespace changes.
708 struct DescCompare {
709 template <typename T>
operator ()google::protobuf::compiler::cpp::FileGenerator::CrossFileReferences::DescCompare710 bool operator()(const T* const& a, const T* const& b) const {
711 return a->full_name() < b->full_name();
712 }
713
operator ()google::protobuf::compiler::cpp::FileGenerator::CrossFileReferences::DescCompare714 bool operator()(const FileDescriptor* const& a,
715 const FileDescriptor* const& b) const {
716 return a->name() < b->name();
717 }
718 };
719
720 // Populated if we are referencing from messages or files.
721 absl::btree_set<const Descriptor*, DescCompare> weak_default_instances;
722
723 // Only if we are referencing from files.
724 absl::btree_set<const FileDescriptor*, DescCompare> strong_reflection_files;
725 absl::btree_set<const FileDescriptor*, DescCompare> weak_reflection_files;
726 };
727
GetCrossFileReferencesForField(const FieldDescriptor * field,CrossFileReferences * refs)728 void FileGenerator::GetCrossFileReferencesForField(const FieldDescriptor* field,
729 CrossFileReferences* refs) {
730 const Descriptor* msg = field->message_type();
731 if (msg == nullptr) {
732 return;
733 }
734
735 if (IsImplicitWeakField(field, options_, &scc_analyzer_) ||
736 IsWeak(field, options_)) {
737 refs->weak_default_instances.insert(msg);
738 }
739 }
740
GetCrossFileReferencesForFile(const FileDescriptor * file,CrossFileReferences * refs)741 void FileGenerator::GetCrossFileReferencesForFile(const FileDescriptor* file,
742 CrossFileReferences* refs) {
743 ForEachField(file, [this, refs](const FieldDescriptor* field) {
744 GetCrossFileReferencesForField(field, refs);
745 });
746
747 if (!HasDescriptorMethods(file, options_)) {
748 return;
749 }
750
751 for (int i = 0; i < file->dependency_count(); ++i) {
752 const FileDescriptor* dep = file->dependency(i);
753
754 if (!ShouldSkipDependencyImports(file->dependency(i))) {
755 refs->strong_reflection_files.insert(dep);
756 } else if (IsDepWeak(dep)) {
757 refs->weak_reflection_files.insert(dep);
758 }
759 }
760 }
761
762 // Generates references to variables defined in other files.
GenerateInternalForwardDeclarations(const CrossFileReferences & refs,io::Printer * p)763 void FileGenerator::GenerateInternalForwardDeclarations(
764 const CrossFileReferences& refs, io::Printer* p) {
765 {
766 NamespaceOpener ns(p);
767
768 for (auto instance : refs.weak_default_instances) {
769 ns.ChangeTo(Namespace(instance, options_));
770
771 if (options_.lite_implicit_weak_fields) {
772 p->Emit({{"ptr", DefaultInstancePtr(instance, options_)}}, R"cc(
773 PROTOBUF_CONSTINIT __attribute__((weak)) const void* $ptr$ =
774 &::_pbi::implicit_weak_message_default_instance;
775 )cc");
776 } else {
777 p->Emit({{"type", DefaultInstanceType(instance, options_)},
778 {"name", DefaultInstanceName(instance, options_)}},
779 R"cc(
780 extern __attribute__((weak)) $type$ $name$;
781 )cc");
782 }
783 }
784 }
785
786 for (auto file : refs.weak_reflection_files) {
787 p->Emit({{"table", DescriptorTableName(file, options_)}}, R"cc(
788 extern __attribute__((weak)) const ::_pbi::DescriptorTable $table$;
789 )cc");
790 }
791 }
792
GenerateSourceForMessage(int idx,io::Printer * p)793 void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* p) {
794 auto v = p->WithVars(FileVars(file_, options_));
795
796 GenerateSourceIncludes(p);
797 GenerateSourcePrelude(p);
798
799 if (IsAnyMessage(file_)) {
800 MuteWuninitialized(p);
801 }
802
803 CrossFileReferences refs;
804 ForEachField<false>(message_generators_[idx]->descriptor(),
805 [this, &refs](const FieldDescriptor* field) {
806 GetCrossFileReferencesForField(field, &refs);
807 });
808
809 GenerateInternalForwardDeclarations(refs, p);
810
811 {
812 NamespaceOpener ns(Namespace(file_, options_), p);
813 p->Emit(
814 {
815 {"defaults", [&] { GenerateSourceDefaultInstance(idx, p); }},
816 {"class_methods",
817 [&] { message_generators_[idx]->GenerateClassMethods(p); }},
818 },
819 R"cc(
820 $defaults$;
821
822 $class_methods$;
823
824 // @@protoc_insertion_point(namespace_scope)
825 )cc");
826 }
827
828 {
829 NamespaceOpener proto_ns(ProtobufNamespace(options_), p);
830 message_generators_[idx]->GenerateSourceInProto2Namespace(p);
831 }
832
833 if (IsAnyMessage(file_)) {
834 UnmuteWuninitialized(p);
835 }
836
837 p->Emit(R"cc(
838 // @@protoc_insertion_point(global_scope)
839 )cc");
840 }
841
GenerateStaticInitializer(io::Printer * p)842 void FileGenerator::GenerateStaticInitializer(io::Printer* p) {
843 int priority = 0;
844 for (auto& inits : static_initializers_) {
845 ++priority;
846 if (inits.empty()) continue;
847 p->Emit(
848 {{"priority", priority},
849 {"expr",
850 [&] {
851 for (auto& init : inits) {
852 init(p);
853 }
854 }}},
855 R"cc(
856 PROTOBUF_ATTRIBUTE_INIT_PRIORITY$priority$ static ::std::false_type
857 _static_init$priority$_ PROTOBUF_UNUSED =
858 ($expr$, ::std::false_type{});
859 )cc");
860 // Reset the vector because we might be generating many files.
861 inits.clear();
862 }
863 }
864
GenerateSourceForExtension(int idx,io::Printer * p)865 void FileGenerator::GenerateSourceForExtension(int idx, io::Printer* p) {
866 auto v = p->WithVars(FileVars(file_, options_));
867 GenerateSourceIncludes(p);
868 GenerateSourcePrelude(p);
869
870 NamespaceOpener ns(Namespace(file_, options_), p);
871 extension_generators_[idx]->GenerateDefinition(p);
872 for (auto priority : {kInitPriority101, kInitPriority102}) {
873 if (extension_generators_[idx]->WillGenerateRegistration(priority)) {
874 static_initializers_[priority].push_back([this, idx, priority](auto* p) {
875 extension_generators_[idx]->GenerateRegistration(p, priority);
876 });
877 }
878 }
879 GenerateStaticInitializer(p);
880 }
881
GenerateGlobalSource(io::Printer * p)882 void FileGenerator::GenerateGlobalSource(io::Printer* p) {
883 auto v = p->WithVars(FileVars(file_, options_));
884 GenerateSourceIncludes(p);
885 GenerateSourcePrelude(p);
886
887 {
888 // Define the code to initialize reflection. This code uses a global
889 // constructor to register reflection data with the runtime pre-main.
890 if (HasDescriptorMethods(file_, options_)) {
891 GenerateReflectionInitializationCode(p);
892 }
893 }
894
895 NamespaceOpener ns(Namespace(file_, options_), p);
896 for (size_t i = 0; i < enum_generators_.size(); ++i) {
897 enum_generators_[i]->GenerateMethods(i, p);
898 }
899 }
900
GenerateSource(io::Printer * p)901 void FileGenerator::GenerateSource(io::Printer* p) {
902 auto v = p->WithVars(FileVars(file_, options_));
903
904 GenerateSourceIncludes(p);
905 GenerateSourcePrelude(p);
906 CrossFileReferences refs;
907 GetCrossFileReferencesForFile(file_, &refs);
908 GenerateInternalForwardDeclarations(refs, p);
909
910 // When in weak descriptor mode, we generate the file_default_instances before
911 // the default instances.
912 if (UsingImplicitWeakDescriptor(file_, options_) &&
913 !message_generators_.empty()) {
914 p->Emit(
915 {
916 {"weak_defaults",
917 [&] {
918 for (auto& gen : message_generators_) {
919 p->Emit(
920 {
921 {"class", QualifiedClassName(gen->descriptor())},
922 {"section",
923 WeakDefaultInstanceSection(
924 gen->descriptor(), gen->index_in_file_messages(),
925 options_)},
926 },
927 R"cc(
928 extern const $class$ __start_$section$
929 __attribute__((weak));
930 )cc");
931 }
932 }},
933 {"defaults",
934 [&] {
935 for (auto& gen : message_generators_) {
936 p->Emit({{"section",
937 WeakDefaultInstanceSection(
938 gen->descriptor(), gen->index_in_file_messages(),
939 options_)}},
940 R"cc(
941 &__start_$section$,
942 )cc");
943 }
944 }},
945 },
946 R"cc(
947 $weak_defaults$;
948 static const ::_pb::Message* file_default_instances[] = {
949 $defaults$,
950 };
951 )cc");
952 }
953
954
955 if (IsAnyMessage(file_)) {
956 MuteWuninitialized(p);
957 }
958
959 {
960 NamespaceOpener ns(Namespace(file_, options_), p);
961 for (size_t i = 0; i < message_generators_.size(); ++i) {
962 GenerateSourceDefaultInstance(
963 message_generators_topologically_ordered_[i], p);
964 }
965 }
966
967 {
968 if (HasDescriptorMethods(file_, options_)) {
969 // Define the code to initialize reflection. This code uses a global
970 // constructor to register reflection data with the runtime pre-main.
971 GenerateReflectionInitializationCode(p);
972 }
973 }
974
975 {
976 NamespaceOpener ns(Namespace(file_, options_), p);
977
978 // Actually implement the protos
979
980 // Generate enums.
981 for (size_t i = 0; i < enum_generators_.size(); ++i) {
982 enum_generators_[i]->GenerateMethods(i, p);
983 }
984
985 // Generate classes.
986 for (size_t i = 0; i < message_generators_.size(); ++i) {
987 p->Emit(R"(
988 $hrule_thick$
989 )");
990 message_generators_[i]->GenerateClassMethods(p);
991 }
992
993 if (HasGenericServices(file_, options_)) {
994 // Generate services.
995 for (size_t i = 0; i < service_generators_.size(); ++i) {
996 p->Emit(R"(
997 $hrule_thick$
998 )");
999 service_generators_[i]->GenerateImplementation(p);
1000 }
1001 }
1002
1003 // Define extensions.
1004 const auto is_lazily_init = IsLazilyInitializedFile(file_->name());
1005 for (size_t i = 0; i < extension_generators_.size(); ++i) {
1006 extension_generators_[i]->GenerateDefinition(p);
1007 if (!is_lazily_init) {
1008 for (auto priority : {kInitPriority101, kInitPriority102}) {
1009 if (extension_generators_[i]->WillGenerateRegistration(priority)) {
1010 static_initializers_[priority].push_back(
1011 [this, i, priority](auto* p) {
1012 extension_generators_[i]->GenerateRegistration(p, priority);
1013 });
1014 }
1015 }
1016 }
1017 }
1018
1019 p->Emit(R"cc(
1020 // @@protoc_insertion_point(namespace_scope)
1021 )cc");
1022 }
1023
1024 {
1025 NamespaceOpener proto_ns(ProtobufNamespace(options_), p);
1026 for (size_t i = 0; i < message_generators_.size(); ++i) {
1027 message_generators_[i]->GenerateSourceInProto2Namespace(p);
1028 }
1029 }
1030
1031 p->Emit(R"cc(
1032 // @@protoc_insertion_point(global_scope)
1033 )cc");
1034
1035 if (IsAnyMessage(file_)) {
1036 UnmuteWuninitialized(p);
1037 }
1038
1039 GenerateStaticInitializer(p);
1040
1041 IncludeFile("third_party/protobuf/port_undef.inc", p);
1042 }
1043
GatherAllCustomOptionTypes(const FileDescriptor * file,absl::btree_map<absl::string_view,const Descriptor * > & out)1044 static void GatherAllCustomOptionTypes(
1045 const FileDescriptor* file,
1046 absl::btree_map<absl::string_view, const Descriptor*>& out) {
1047 const DescriptorPool* pool = file->pool();
1048 const Descriptor* fd_proto_descriptor = pool->FindMessageTypeByName(
1049 FileDescriptorProto::descriptor()->full_name());
1050 // Not all pools have descriptor.proto in them. In these cases there for sure
1051 // are no custom options.
1052 if (fd_proto_descriptor == nullptr) {
1053 return;
1054 }
1055
1056 // It's easier to inspect file as a proto, because we can use reflection on
1057 // the proto to iterate over all content.
1058 // However, we can't use the generated proto linked into the proto compiler
1059 // for this, since it doesn't know the extensions that are potentially present
1060 // the protos that are being compiled.
1061 // Use a dynamic one from the correct pool to parse them.
1062 DynamicMessageFactory factory(pool);
1063 std::unique_ptr<Message> fd_proto(
1064 factory.GetPrototype(fd_proto_descriptor)->New());
1065
1066 {
1067 FileDescriptorProto linkedin_fd_proto;
1068 file->CopyTo(&linkedin_fd_proto);
1069 ABSL_CHECK(
1070 fd_proto->ParseFromString(linkedin_fd_proto.SerializeAsString()));
1071 }
1072
1073 // Now find all the messages used, recursively.
1074 std::vector<const Message*> to_process = {fd_proto.get()};
1075 while (!to_process.empty()) {
1076 const Message& msg = *to_process.back();
1077 to_process.pop_back();
1078
1079 std::vector<const FieldDescriptor*> fields;
1080 const Reflection& reflection = *msg.GetReflection();
1081 reflection.ListFields(msg, &fields);
1082
1083 for (auto* field : fields) {
1084 if (field->is_extension()) {
1085 // Always add the extended.
1086 const Descriptor* desc = msg.GetDescriptor();
1087 out[desc->full_name()] = desc;
1088 }
1089
1090 // Add and recurse of the extendee if it is a message.
1091 const auto* field_msg = field->message_type();
1092 if (field_msg == nullptr) continue;
1093 if (field->is_extension()) {
1094 out[field_msg->full_name()] = field_msg;
1095 }
1096 if (field->is_repeated()) {
1097 for (int i = 0; i < reflection.FieldSize(msg, field); i++) {
1098 to_process.push_back(&reflection.GetRepeatedMessage(msg, field, i));
1099 }
1100 } else {
1101 to_process.push_back(&reflection.GetMessage(msg, field));
1102 }
1103 }
1104 }
1105 }
1106
1107 static std::vector<const Descriptor*>
GetMessagesToPinGloballyForWeakDescriptors(const FileDescriptor * file,const Options & options)1108 GetMessagesToPinGloballyForWeakDescriptors(const FileDescriptor* file,
1109 const Options& options) {
1110 // Sorted map to dedup and to make deterministic.
1111 absl::btree_map<absl::string_view, const Descriptor*> res;
1112
1113 // For simplicity we force pin request/response messages for all
1114 // services. The current implementation of services might not do
1115 // the pin itself, so it is simpler.
1116 // This is a place for improvement in the future.
1117 for (int i = 0; i < file->service_count(); ++i) {
1118 auto* service = file->service(i);
1119 for (int j = 0; j < service->method_count(); ++j) {
1120 auto* method = service->method(j);
1121 res[method->input_type()->full_name()] = method->input_type();
1122 res[method->output_type()->full_name()] = method->output_type();
1123 }
1124 }
1125
1126 // For correctness, we must ensure that all messages used as custom options in
1127 // the descriptor are pinned. Otherwise, we can't properly parse the
1128 // descriptor.
1129 GatherAllCustomOptionTypes(file, res);
1130
1131 std::vector<const Descriptor*> out;
1132 for (auto& p : res) {
1133 // We don't need to pin the bootstrap types. It is wasteful.
1134 if (IsBootstrapProto(options, p.second->file())) continue;
1135 out.push_back(p.second);
1136 }
1137 return out;
1138 }
1139
GenerateReflectionInitializationCode(io::Printer * p)1140 void FileGenerator::GenerateReflectionInitializationCode(io::Printer* p) {
1141 if (!enum_generators_.empty()) {
1142 p->Emit({{"len", enum_generators_.size()}}, R"cc(
1143 static const ::_pb::EnumDescriptor* $file_level_enum_descriptors$[$len$];
1144 )cc");
1145 } else {
1146 p->Emit(R"cc(
1147 static constexpr const ::_pb::EnumDescriptor**
1148 $file_level_enum_descriptors$ = nullptr;
1149 )cc");
1150 }
1151
1152 if (HasGenericServices(file_, options_) && file_->service_count() > 0) {
1153 p->Emit({{"len", file_->service_count()}}, R"cc(
1154 static const ::_pb::ServiceDescriptor*
1155 $file_level_service_descriptors$[$len$];
1156 )cc");
1157 } else {
1158 p->Emit(R"cc(
1159 static constexpr const ::_pb::ServiceDescriptor**
1160 $file_level_service_descriptors$ = nullptr;
1161 )cc");
1162 }
1163
1164 if (!message_generators_.empty()) {
1165 std::vector<std::pair<size_t, size_t>> offsets;
1166 offsets.reserve(message_generators_.size());
1167
1168 p->Emit(
1169 {
1170 {"offsets",
1171 [&] {
1172 for (size_t i = 0; i < message_generators_.size(); ++i) {
1173 offsets.push_back(message_generators_[i]->GenerateOffsets(p));
1174 }
1175 }},
1176 {"schemas",
1177 [&] {
1178 int offset = 0;
1179 for (size_t i = 0; i < message_generators_.size(); ++i) {
1180 message_generators_[i]->GenerateSchema(p, offset,
1181 offsets[i].second);
1182 offset += offsets[i].first;
1183 }
1184 }},
1185 },
1186 R"cc(
1187 const ::uint32_t
1188 $tablename$::offsets[] ABSL_ATTRIBUTE_SECTION_VARIABLE(
1189 protodesc_cold) = {
1190 $offsets$,
1191 };
1192
1193 static const ::_pbi::MigrationSchema
1194 schemas[] ABSL_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {
1195 $schemas$,
1196 };
1197 )cc");
1198 if (!UsingImplicitWeakDescriptor(file_, options_)) {
1199 p->Emit({{"defaults",
1200 [&] {
1201 for (auto& gen : message_generators_) {
1202 p->Emit(
1203 {
1204 {"ns", Namespace(gen->descriptor(), options_)},
1205 {"class", ClassName(gen->descriptor())},
1206 },
1207 R"cc(
1208 &$ns$::_$class$_default_instance_._instance,
1209 )cc");
1210 }
1211 }}},
1212 R"cc(
1213 static const ::_pb::Message* const file_default_instances[] = {
1214 $defaults$,
1215 };
1216 )cc");
1217 }
1218 } else {
1219 // Ee still need these symbols to exist.
1220 //
1221 // MSVC doesn't like empty arrays, so we add a dummy.
1222 p->Emit(R"cc(
1223 const ::uint32_t $tablename$::offsets[1] = {};
1224 static constexpr ::_pbi::MigrationSchema* schemas = nullptr;
1225 static constexpr ::_pb::Message* const* file_default_instances = nullptr;
1226 )cc");
1227 }
1228
1229 // ---------------------------------------------------------------
1230
1231 // Embed the descriptor. We simply serialize the entire
1232 // FileDescriptorProto/ and embed it as a string literal, which is parsed and
1233 // built into real descriptors at initialization time.
1234
1235 FileDescriptorProto file_proto = StripSourceRetentionOptions(*file_);
1236 std::string file_data;
1237 file_proto.SerializeToString(&file_data);
1238
1239 auto desc_name = UniqueName("descriptor_table_protodef", file_, options_);
1240 p->Emit(
1241 {{"desc_name", desc_name},
1242 {"encoded_file_proto",
1243 [&] {
1244 if (options_.strip_nonfunctional_codegen) {
1245 p->Emit(R"cc("")cc");
1246 return;
1247 }
1248
1249 absl::string_view data = file_data;
1250 if (data.size() <= 65535) {
1251 static constexpr size_t kBytesPerLine = 40;
1252 while (!data.empty()) {
1253 auto to_write = std::min(kBytesPerLine, data.size());
1254 auto chunk = data.substr(0, to_write);
1255 data = data.substr(to_write);
1256
1257 p->Emit({{"text", EscapeTrigraphs(absl::CEscape(chunk))}}, R"cc(
1258 "$text$"
1259 )cc");
1260 }
1261 return;
1262 }
1263
1264 // Workaround for MSVC: "Error C1091: compiler limit: string exceeds
1265 // 65535 bytes in length". Declare a static array of chars rather than
1266 // use a string literal. Only write 25 bytes per line.
1267 static constexpr size_t kBytesPerLine = 25;
1268 while (!data.empty()) {
1269 auto to_write = std::min(kBytesPerLine, data.size());
1270 auto chunk = data.substr(0, to_write);
1271 data = data.substr(to_write);
1272
1273 std::string line;
1274 for (char c : chunk) {
1275 absl::StrAppend(&line, "'",
1276 absl::CEscape(absl::string_view(&c, 1)), "', ");
1277 }
1278
1279 p->Emit({{"line", line}}, R"cc(
1280 $line$
1281 )cc");
1282 }
1283 }}},
1284 R"cc(
1285 const char $desc_name$[] ABSL_ATTRIBUTE_SECTION_VARIABLE(
1286 protodesc_cold) = {
1287 $encoded_file_proto$,
1288 };
1289 )cc");
1290
1291 CrossFileReferences refs;
1292 GetCrossFileReferencesForFile(file_, &refs);
1293 size_t num_deps =
1294 refs.strong_reflection_files.size() + refs.weak_reflection_files.size();
1295
1296 // Build array of DescriptorTable deps.
1297 if (num_deps > 0) {
1298 p->Emit(
1299 {
1300 {"len", num_deps},
1301 {"deps",
1302 [&] {
1303 for (auto dep : refs.strong_reflection_files) {
1304 p->Emit({{"name", DescriptorTableName(dep, options_)}}, R"cc(
1305 &::$name$,
1306 )cc");
1307 }
1308 for (auto dep : refs.weak_reflection_files) {
1309 p->Emit({{"name", DescriptorTableName(dep, options_)}}, R"cc(
1310 &::$name$,
1311 )cc");
1312 }
1313 }},
1314 },
1315 R"cc(
1316 static const ::_pbi::DescriptorTable* const $desc_table$_deps[$len$] =
1317 {
1318 $deps$,
1319 };
1320 )cc");
1321 }
1322
1323 // The DescriptorTable itself.
1324 // Should be "bool eager = NeedsEagerDescriptorAssignment(file_, options_);"
1325 // however this might cause a tsan failure in superroot b/148382879,
1326 // so disable for now.
1327 bool eager = false;
1328 p->Emit(
1329 {
1330 {"eager", eager ? "true" : "false"},
1331 {"file_proto_len",
1332 options_.strip_nonfunctional_codegen ? 0 : file_data.size()},
1333 {"proto_name", desc_name},
1334 {"deps_ptr", num_deps == 0
1335 ? "nullptr"
1336 : absl::StrCat(p->LookupVar("desc_table"), "_deps")},
1337 {"num_deps", num_deps},
1338 {"num_msgs", message_generators_.size()},
1339 },
1340 R"cc(
1341 static ::absl::once_flag $desc_table$_once;
1342 PROTOBUF_CONSTINIT const ::_pbi::DescriptorTable $desc_table$ = {
1343 false,
1344 $eager$,
1345 $file_proto_len$,
1346 $proto_name$,
1347 "$filename$",
1348 &$desc_table$_once,
1349 $deps_ptr$,
1350 $num_deps$,
1351 $num_msgs$,
1352 schemas,
1353 file_default_instances,
1354 $tablename$::offsets,
1355 $file_level_enum_descriptors$,
1356 $file_level_service_descriptors$,
1357 };
1358 )cc");
1359
1360 // For descriptor.proto and cpp_features.proto we want to avoid doing any
1361 // dynamic initialization, because in some situations that would otherwise
1362 // pull in a lot of unnecessary code that can't be stripped by --gc-sections.
1363 // Descriptor initialization will still be performed lazily when it's needed.
1364 if (!IsLazilyInitializedFile(file_->name())) {
1365 if (UsingImplicitWeakDescriptor(file_, options_)) {
1366 for (auto* pinned :
1367 GetMessagesToPinGloballyForWeakDescriptors(file_, options_)) {
1368 static_initializers_[kInitPriority102].push_back([this,
1369 pinned](auto* p) {
1370 p->Emit({{"pin", StrongReferenceToType(pinned, options_)}},
1371 R"cc(
1372 $pin$,
1373 )cc");
1374 });
1375 }
1376 }
1377 static_initializers_[kInitPriority102].push_back([](auto* p) {
1378 p->Emit(R"cc(
1379 ::_pbi::AddDescriptors(&$desc_table$),
1380 )cc");
1381 });
1382 }
1383
1384 // However, we must provide a way to force initialize the default instances
1385 // of FileDescriptorProto which will be used during registration of other
1386 // files.
1387 if (IsFileDescriptorProto(file_, options_)) {
1388 NamespaceOpener ns(p);
1389 ns.ChangeTo(absl::StrCat(ProtobufNamespace(options_), "::internal"));
1390 p->Emit(
1391 {{"dummy", UniqueName("dynamic_init_dummy", file_, options_)},
1392 {"initializers", absl::StrJoin(message_generators_, "\n",
1393 [&](std::string* out, const auto& gen) {
1394 absl::StrAppend(
1395 out,
1396 DefaultInstanceName(
1397 gen->descriptor(), options_),
1398 ".Init();");
1399 })}},
1400 R"cc(
1401 //~ Emit wants an indented line, so give it a comment to strip.
1402 #if !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
1403 PROTOBUF_EXPORT void InitializeFileDescriptorDefaultInstancesSlow() {
1404 $initializers$;
1405 }
1406 PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
1407 static std::true_type $dummy${
1408 (InitializeFileDescriptorDefaultInstances(), std::true_type{})};
1409 #endif // !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
1410 )cc");
1411 }
1412 }
1413
1414 class FileGenerator::ForwardDeclarations {
1415 public:
AddMessage(const Descriptor * d)1416 void AddMessage(const Descriptor* d) { classes_.emplace(ClassName(d), d); }
AddEnum(const EnumDescriptor * d)1417 void AddEnum(const EnumDescriptor* d) { enums_.emplace(ClassName(d), d); }
AddSplit(const Descriptor * d)1418 void AddSplit(const Descriptor* d) { splits_.emplace(ClassName(d), d); }
1419
Print(io::Printer * p,const Options & options) const1420 void Print(io::Printer* p, const Options& options) const {
1421 for (const auto& e : enums_) {
1422 p->Emit({Sub("enum", e.first).AnnotatedAs(e.second)}, R"cc(
1423 enum $enum$ : int;
1424 bool $enum$_IsValid(int value);
1425 )cc");
1426 }
1427
1428 for (const auto& c : classes_) {
1429 const Descriptor* desc = c.second;
1430 p->Emit(
1431 {
1432 Sub("class", c.first).AnnotatedAs(desc),
1433 {"default_type", DefaultInstanceType(desc, options)},
1434 {"default_name", DefaultInstanceName(desc, options)},
1435 },
1436 R"cc(
1437 class $class$;
1438 struct $default_type$;
1439 $dllexport_decl $extern $default_type$ $default_name$;
1440 )cc");
1441 }
1442
1443 for (const auto& s : splits_) {
1444 const Descriptor* desc = s.second;
1445 p->Emit(
1446 {
1447 {"default_type",
1448 DefaultInstanceType(desc, options, /*split=*/true)},
1449 {"default_name",
1450 DefaultInstanceName(desc, options, /*split=*/true)},
1451 },
1452 R"cc(
1453 struct $default_type$;
1454 $dllexport_decl $extern const $default_type$ $default_name$;
1455 )cc");
1456 }
1457 }
1458
PrintTopLevelDecl(io::Printer * p,const Options & options) const1459 void PrintTopLevelDecl(io::Printer* p, const Options& options) const {
1460 if (ShouldGenerateExternSpecializations(options)) {
1461 for (const auto& c : classes_) {
1462 if (!ShouldGenerateClass(c.second, options)) continue;
1463 auto vars = p->WithVars(
1464 {{"class", QualifiedClassName(c.second, options)},
1465 {"default_name", QualifiedDefaultInstanceName(c.second, options,
1466 /*split=*/false)}});
1467 // To reduce total linker input size in large binaries we make these
1468 // functions extern and define then in the pb.cc file. This avoids bloat
1469 // in callers by having duplicate definitions of the template.
1470 // However, it increases the size of the pb.cc translation units so it
1471 // is a tradeoff.
1472 p->Emit(R"cc(
1473 extern template void* Arena::DefaultConstruct<$class$>(Arena*);
1474 )cc");
1475 if (!IsMapEntryMessage(c.second)) {
1476 p->Emit(R"cc(
1477 extern template void* Arena::CopyConstruct<$class$>(Arena*,
1478 const void*);
1479 )cc");
1480 }
1481 // We can't make a constexpr pointer to the global if we have DLL
1482 // linkage so skip this.
1483 // The fallback traits are slower, but correct.
1484 if (options.dllexport_decl.empty()) {
1485 p->Emit(R"cc(
1486 template <>
1487 internal::GeneratedMessageTraitsT<decltype($default_name$),
1488 &$default_name$>
1489 internal::MessageTraitsImpl::value<$class$>;
1490 )cc");
1491 }
1492 }
1493 }
1494 }
1495
1496 private:
1497 absl::btree_map<std::string, const Descriptor*> classes_;
1498 absl::btree_map<std::string, const EnumDescriptor*> enums_;
1499 absl::btree_map<std::string, const Descriptor*> splits_;
1500 };
1501
PublicImportDFS(const FileDescriptor * fd,absl::flat_hash_set<const FileDescriptor * > & fd_set)1502 static void PublicImportDFS(
1503 const FileDescriptor* fd,
1504 absl::flat_hash_set<const FileDescriptor*>& fd_set) {
1505 for (int i = 0; i < fd->public_dependency_count(); ++i) {
1506 const FileDescriptor* dep = fd->public_dependency(i);
1507 if (fd_set.insert(dep).second) {
1508 PublicImportDFS(dep, fd_set);
1509 }
1510 }
1511 }
1512
GenerateForwardDeclarations(io::Printer * p)1513 void FileGenerator::GenerateForwardDeclarations(io::Printer* p) {
1514 std::vector<const Descriptor*> classes;
1515 FlattenMessagesInFile(file_, &classes); // All messages need forward decls.
1516
1517 std::vector<const EnumDescriptor*> enums;
1518 if (options_.proto_h) { // proto.h needs extra forward declarations.
1519 // All classes / enums referred to as field members
1520 std::vector<const FieldDescriptor*> fields;
1521 ListAllFields(file_, &fields);
1522 for (const auto* field : fields) {
1523 classes.push_back(field->containing_type());
1524 classes.push_back(field->message_type());
1525 enums.push_back(field->enum_type());
1526 }
1527
1528 ListAllTypesForServices(file_, &classes);
1529 }
1530
1531 // Calculate the set of files whose definitions we get through include.
1532 // No need to forward declare types that are defined in these.
1533 absl::flat_hash_set<const FileDescriptor*> public_set;
1534 PublicImportDFS(file_, public_set);
1535
1536 absl::btree_map<std::string, ForwardDeclarations> decls;
1537 for (const auto* d : classes) {
1538 if (d != nullptr && !public_set.contains(d->file()) &&
1539 ShouldGenerateClass(d, options_))
1540 decls[Namespace(d, options_)].AddMessage(d);
1541 }
1542 for (const auto* e : enums) {
1543 if (e != nullptr && !public_set.contains(e->file()))
1544 decls[Namespace(e, options_)].AddEnum(e);
1545 }
1546 for (const auto& mg : message_generators_) {
1547 const Descriptor* d = mg->descriptor();
1548 if (d != nullptr && public_set.count(d->file()) == 0u &&
1549 ShouldSplit(mg->descriptor(), options_))
1550 decls[Namespace(d, options_)].AddSplit(d);
1551 }
1552
1553 NamespaceOpener ns(p);
1554 for (const auto& decl : decls) {
1555 ns.ChangeTo(decl.first);
1556 decl.second.Print(p, options_);
1557 }
1558
1559 ns.ChangeTo(ProtobufNamespace(options_));
1560 for (const auto& decl : decls) {
1561 decl.second.PrintTopLevelDecl(p, options_);
1562 }
1563
1564 if (IsFileDescriptorProto(file_, options_)) {
1565 ns.ChangeTo(absl::StrCat(ProtobufNamespace(options_), "::internal"));
1566 p->Emit(R"cc(
1567 //~ Emit wants an indented line, so give it a comment to strip.
1568 #if !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
1569 PROTOBUF_EXPORT void InitializeFileDescriptorDefaultInstancesSlow();
1570 #endif // !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
1571 )cc");
1572 }
1573 }
1574
GenerateLibraryIncludes(io::Printer * p)1575 void FileGenerator::GenerateLibraryIncludes(io::Printer* p) {
1576 if (UsingImplicitWeakFields(file_, options_)) {
1577 IncludeFile("third_party/protobuf/implicit_weak_message.h", p);
1578 }
1579 if (HasWeakFields(file_, options_)) {
1580 ABSL_CHECK(!options_.opensource_runtime);
1581 IncludeFile("third_party/protobuf/weak_field_map.h", p);
1582 }
1583 if (HasLazyFields(file_, options_, &scc_analyzer_)) {
1584 ABSL_CHECK(!options_.opensource_runtime);
1585 IncludeFile("third_party/protobuf/lazy_field.h", p);
1586 }
1587 if (ShouldVerify(file_, options_, &scc_analyzer_)) {
1588 IncludeFile("third_party/protobuf/wire_format_verify.h", p);
1589 }
1590
1591 IncludeFile("third_party/protobuf/runtime_version.h", p);
1592 int version;
1593 if (options_.opensource_runtime) {
1594 const auto& v = GetProtobufCPPVersion(/*oss_runtime=*/true);
1595 version = v.major() * 1000000 + v.minor() * 1000 + v.patch();
1596 } else {
1597 version = GetProtobufCPPVersion(/*oss_runtime=*/false).minor();
1598 }
1599 p->Emit(
1600 {
1601 {"version", version},
1602
1603 // Downgrade to warnings if version mismatches for bootstrapped files,
1604 // so that release_compiler.h can build protoc_minimal successfully
1605 // and update stale files.
1606 {"err_level", options_.bootstrap ? "warning" : "error"},
1607 },
1608 R"(
1609 #if PROTOBUF_VERSION != $version$
1610 #$err_level$ "Protobuf C++ gencode is built with an incompatible version of"
1611 #$err_level$ "Protobuf C++ headers/runtime. See"
1612 #$err_level$ "https://protobuf.dev/support/cross-version-runtime-guarantee/#cpp"
1613 #endif
1614 )");
1615
1616 // OK, it's now safe to #include other files.
1617 IncludeFile("third_party/protobuf/io/coded_stream.h", p);
1618 IncludeFile("third_party/protobuf/arena.h", p);
1619 IncludeFile("third_party/protobuf/arenastring.h", p);
1620 if (IsStringInliningEnabled(options_)) {
1621 IncludeFile("third_party/protobuf/inlined_string_field.h", p);
1622 }
1623 if (HasSimpleBaseClasses(file_, options_)) {
1624 IncludeFile("third_party/protobuf/generated_message_bases.h", p);
1625 }
1626 if (HasGeneratedMethods(file_, options_)) {
1627 IncludeFile("third_party/protobuf/generated_message_tctable_decl.h", p);
1628 }
1629 IncludeFile("third_party/protobuf/generated_message_util.h", p);
1630 IncludeFile("third_party/protobuf/metadata_lite.h", p);
1631
1632 if (HasDescriptorMethods(file_, options_)) {
1633 IncludeFile("third_party/protobuf/generated_message_reflection.h", p);
1634 }
1635
1636 if (!message_generators_.empty()) {
1637 if (HasDescriptorMethods(file_, options_)) {
1638 IncludeFile("third_party/protobuf/message.h", p);
1639 }
1640 IncludeFile("third_party/protobuf/message_lite.h", p);
1641 }
1642 if (options_.opensource_runtime) {
1643 // Open-source relies on unconditional includes of these.
1644 IncludeFileAndExport("third_party/protobuf/repeated_field.h", p);
1645 IncludeFileAndExport("third_party/protobuf/extension_set.h", p);
1646 } else {
1647 // Google3 includes these files only when they are necessary.
1648 if (HasExtensionsOrExtendableMessage(file_)) {
1649 IncludeFileAndExport("third_party/protobuf/extension_set.h", p);
1650 }
1651 if (HasRepeatedFields(file_)) {
1652 IncludeFileAndExport("third_party/protobuf/repeated_field.h", p);
1653 }
1654 if (HasStringPieceFields(file_, options_)) {
1655 IncludeFile("third_party/protobuf/string_piece_field_support.h", p);
1656 }
1657 }
1658 if (HasCordFields(file_, options_)) {
1659 p->Emit(R"(
1660 #include "absl/strings/cord.h"
1661 )");
1662 }
1663 if (HasMapFields(file_)) {
1664 IncludeFileAndExport("third_party/protobuf/map.h", p);
1665 if (HasDescriptorMethods(file_, options_)) {
1666 IncludeFile("third_party/protobuf/map_entry.h", p);
1667 IncludeFile("third_party/protobuf/map_field_inl.h", p);
1668 } else {
1669 IncludeFile("third_party/protobuf/map_field_lite.h", p);
1670 }
1671 }
1672
1673 if (HasEnumDefinitions(file_)) {
1674 if (HasDescriptorMethods(file_, options_)) {
1675 IncludeFile("third_party/protobuf/generated_enum_reflection.h", p);
1676 } else {
1677 IncludeFile("third_party/protobuf/generated_enum_util.h", p);
1678 }
1679 }
1680
1681 if (HasGenericServices(file_, options_)) {
1682 IncludeFile("third_party/protobuf/service.h", p);
1683 }
1684
1685 if (UseUnknownFieldSet(file_, options_) && !message_generators_.empty()) {
1686 IncludeFile("third_party/protobuf/unknown_field_set.h", p);
1687 }
1688 }
1689
1690 void FileGenerator::GenerateMetadataPragma(io::Printer* p,
1691 absl::string_view info_path) {
1692 if (info_path.empty() || options_.annotation_pragma_name.empty() ||
1693 options_.annotation_guard_name.empty()) {
1694 return;
1695 }
1696
1697 p->Emit(
1698 {
1699 {"guard", options_.annotation_guard_name},
1700 {"pragma", options_.annotation_pragma_name},
1701 {"info_path", std::string(info_path)},
1702 },
1703 R"(
1704 #ifdef $guard$
1705 #pragma $pragma$ "$info_path$"
1706 #endif // $guard$
1707 )");
1708 }
1709
1710 void FileGenerator::GenerateDependencyIncludes(io::Printer* p) {
1711 for (int i = 0; i < file_->dependency_count(); ++i) {
1712 const FileDescriptor* dep = file_->dependency(i);
1713
1714 if (ShouldSkipDependencyImports(dep)) {
1715 continue;
1716 }
1717
1718 std::string basename = StripProto(dep->name());
1719 if (options_.bootstrap) {
1720 GetBootstrapBasename(options_, basename, &basename);
1721 }
1722
1723 p->Emit(
1724 {{"name", CreateHeaderInclude(absl::StrCat(basename, ".pb.h"), dep)}},
1725 R"(
1726 #include $name$
1727 )");
1728 }
1729 }
1730
1731 void FileGenerator::GenerateGlobalStateFunctionDeclarations(io::Printer* p) {
1732 // Forward-declare the DescriptorTable because this is referenced by .pb.cc
1733 // files depending on this file.
1734 //
1735 // The TableStruct is also outputted in weak_message_field.cc, because the
1736 // weak fields must refer to table struct but cannot include the header.
1737 // Also it annotates extra weak attributes.
1738 // TODO make sure this situation is handled better.
1739 p->Emit(R"cc(
1740 // Internal implementation detail -- do not use these members.
1741 struct $dllexport_decl $$tablename$ {
1742 static const ::uint32_t offsets[];
1743 };
1744 )cc");
1745
1746 if (HasDescriptorMethods(file_, options_)) {
1747 p->Emit(R"cc(
1748 $dllexport_decl $extern const ::$proto_ns$::internal::DescriptorTable
1749 $desc_table$;
1750 )cc");
1751 }
1752 }
1753
1754 void FileGenerator::GenerateMessageDefinitions(io::Printer* p) {
1755 for (size_t i = 0; i < message_generators_.size(); ++i) {
1756 p->Emit(R"cc(
1757 $hrule_thin$
1758 )cc");
1759 message_generators_[message_generators_topologically_ordered_[i]]
1760 ->GenerateClassDefinition(p);
1761 }
1762 }
1763
1764 void FileGenerator::GenerateEnumDefinitions(io::Printer* p) {
1765 for (size_t i = 0; i < enum_generators_.size(); ++i) {
1766 enum_generators_[i]->GenerateDefinition(p);
1767 }
1768 }
1769
1770 void FileGenerator::GenerateServiceDefinitions(io::Printer* p) {
1771 if (!HasGenericServices(file_, options_)) {
1772 return;
1773 }
1774
1775 for (size_t i = 0; i < service_generators_.size(); ++i) {
1776 p->Emit(R"cc(
1777 $hrule_thin$
1778 )cc");
1779 service_generators_[i]->GenerateDeclarations(p);
1780 }
1781
1782 p->Emit(R"cc(
1783 $hrule_thick$
1784 )cc");
1785 }
1786
1787 void FileGenerator::GenerateExtensionIdentifiers(io::Printer* p) {
1788 // Declare extension identifiers. These are in global scope and so only
1789 // the global scope extensions.
1790 for (auto& extension_generator : extension_generators_) {
1791 if (extension_generator->IsScoped()) {
1792 continue;
1793 }
1794 extension_generator->GenerateDeclaration(p);
1795 }
1796 }
1797
1798 void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* p) {
1799 // TODO remove pragmas when gcc is no longer used. Current version
1800 // of gcc fires a bogus error when compiled with strict-aliasing.
1801 p->Emit(R"(
1802 #ifdef __GNUC__
1803 #pragma GCC diagnostic push
1804 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
1805 #endif // __GNUC__
1806 )");
1807
1808 for (size_t i = 0; i < message_generators_.size(); ++i) {
1809 p->Emit(R"cc(
1810 $hrule_thin$
1811 )cc");
1812 message_generators_[i]->GenerateInlineMethods(p);
1813 }
1814
1815 p->Emit(R"(
1816 #ifdef __GNUC__
1817 #pragma GCC diagnostic pop
1818 #endif // __GNUC__
1819 )");
1820 }
1821
1822 void FileGenerator::GenerateProto2NamespaceEnumSpecializations(io::Printer* p) {
1823 // Emit GetEnumDescriptor specializations into google::protobuf namespace.
1824 if (!HasEnumDefinitions(file_)) {
1825 return;
1826 }
1827
1828 p->PrintRaw("\n");
1829 NamespaceOpener ns(ProtobufNamespace(options_), p);
1830 p->PrintRaw("\n");
1831 for (auto& gen : enum_generators_) {
1832 gen->GenerateGetEnumDescriptorSpecializations(p);
1833 }
1834 p->PrintRaw("\n");
1835 }
1836
1837 std::vector<const Descriptor*> FileGenerator::MessagesInTopologicalOrder()
1838 const {
1839 std::vector<const Descriptor*> descs;
1840 descs.reserve(message_generators_.size());
1841 for (size_t i = 0; i < message_generators_.size(); ++i) {
1842 descs.push_back(
1843 message_generators_[message_generators_topologically_ordered_[i]]
1844 ->descriptor());
1845 }
1846 return descs;
1847 }
1848 } // namespace cpp
1849 } // namespace compiler
1850 } // namespace protobuf
1851 } // namespace google
1852
1853 #include "google/protobuf/port_undef.inc"
1854