• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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