// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

// Author: kenton@google.com (Kenton Varda)
//  Based on original Protocol Buffers design by
//  Sanjay Ghemawat, Jeff Dean, and others.

#include "google/protobuf/compiler/cpp/file.h"

#include <algorithm>
#include <functional>
#include <memory>
#include <queue>
#include <string>
#include <utility>
#include <vector>

#include "absl/container/btree_map.h"
#include "absl/container/btree_set.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/log/absl_check.h"
#include "absl/strings/escaping.h"
#include "absl/strings/match.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "absl/strings/strip.h"
#include "google/protobuf/compiler/code_generator.h"
#include "google/protobuf/compiler/cpp/enum.h"
#include "google/protobuf/compiler/cpp/extension.h"
#include "google/protobuf/compiler/cpp/helpers.h"
#include "google/protobuf/compiler/cpp/message.h"
#include "google/protobuf/compiler/cpp/names.h"
#include "google/protobuf/compiler/cpp/service.h"
#include "google/protobuf/compiler/retention.h"
#include "google/protobuf/compiler/scc.h"
#include "google/protobuf/compiler/versions.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/io/printer.h"

// Must be last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {
namespace compiler {
namespace cpp {
namespace {
using Sub = ::google::protobuf::io::Printer::Sub;
using ::google::protobuf::internal::cpp::IsLazilyInitializedFile;

absl::flat_hash_map<absl::string_view, std::string> FileVars(
    const FileDescriptor* file, const Options& options) {
  return {
      {"filename", file->name()},
      {"package_ns", Namespace(file, options)},
      {"tablename", UniqueName("TableStruct", file, options)},
      {"desc_table", DescriptorTableName(file, options)},
      {"dllexport_decl", options.dllexport_decl},
      {"file_level_metadata", UniqueName("file_level_metadata", file, options)},
      {"file_level_enum_descriptors",
       UniqueName("file_level_enum_descriptors", file, options)},
      {"file_level_service_descriptors",
       UniqueName("file_level_service_descriptors", file, options)},
  };
}

// TODO: remove pragmas that suppresses uninitialized warnings when
// clang bug is fixed.
void MuteWuninitialized(io::Printer* p) {
  p->Emit(R"(
    #if defined(__llvm__)
    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wuninitialized"
    #endif  // __llvm__
  )");
}

void UnmuteWuninitialized(io::Printer* p) {
  p->Emit(R"(
    #if defined(__llvm__)
    #pragma clang diagnostic pop
    #endif  // __llvm__
  )");
}

// TopologicalSortMessagesInFile topologically sorts and returns a vector of
// proto descriptors defined in the file provided as input.  The underlying
// graph is defined using dependency relationship between protos.  For example,
// if proto A contains proto B as a member, then proto B would be ordered before
// proto A in a topological ordering, assuming there is no mutual dependence
// between the two protos.  The topological order is used to emit proto
// declarations so that a proto is declared after all the protos it is dependent
// on have been declared (again assuming no mutual dependence).  This is needed
// in cases where we may declare proto B as a member of proto A using an object,
// instead of a pointer.
//
// The proto dependencey graph can have cycles.  So instead of directly working
// with protos, we compute strong connected components (SCCs) composed of protos
// with mutual dependence.  The dependency graph on SCCs is a directed acyclic
// graph (DAG) and therefore a topological order can be computed for it i.e. an
// order where an SCC is ordered after all other SCCs it is dependent on have
// been ordered.
//
// The function below first constructs the SCC graph and then computes a
// deterministic topological order for the graph.
//
// For computing the SCC graph, we follow the following steps:
// 1. Collect the descriptors for the messages in the file.
// 2. Construct a map for descriptor to SCC mapping.
// 3. Construct a map for dependence between SCCs, referred to as
// child_to_parent_scc_map below.  This map constructed by running a BFS on the
// SCCs.
//
// For computing a deterministic topological order on the graph computed in step
// 3 above, we do the following:
// 1. Since the graph on SCCs is a DAG, therefore there will be at least one SCC
// that does not depend on other SCCs.  We first construct a list of all such
// SCCs.
// 2. Next we run a BFS starting with the list of SCCs computed in step 1.  For
// each SCC, we track the number of the SCC it is dependent on and the number of
// those SCC that have been ordered.  Once all the SCCs an SCC is dependent on
// have been ordered, this SCC is added to list of SCCs that are to be ordered
// next.
// 3. Within an SCC, the descriptors are ordered on the basis of the full_name()
// of the descriptors.
std::vector<const Descriptor*> TopologicalSortMessagesInFile(
    const FileDescriptor* file, MessageSCCAnalyzer& scc_analyzer) {
  // Collect the messages defined in this file.
  std::vector<const Descriptor*> messages_in_file = FlattenMessagesInFile(file);
  if (messages_in_file.empty()) return {};
  // Populate the map from the descriptor to the SCC to which the descriptor
  // belongs.
  absl::flat_hash_map<const Descriptor*, const SCC*> descriptor_to_scc_map;
  descriptor_to_scc_map.reserve(messages_in_file.size());
  for (const Descriptor* d : messages_in_file) {
    descriptor_to_scc_map.emplace(d, scc_analyzer.GetSCC(d));
  }
  ABSL_DCHECK(messages_in_file.size() == descriptor_to_scc_map.size())
      << "messages_in_file has duplicate messages!";
  // Each parent SCC has information about the child SCCs i.e. SCCs for fields
  // that are contained in the protos that belong to the parent SCC.  Use this
  // information to construct the inverse map from child SCC to parent SCC.
  absl::flat_hash_map<const SCC*, absl::flat_hash_set<const SCC*>>
      child_to_parent_scc_map;
  // For recording the number of edges from each SCC to other SCCs in the
  // forward map.
  absl::flat_hash_map<const SCC*, int> scc_to_outgoing_edges_map;
  std::queue<const SCC*> sccs_to_process;
  for (const auto& p : descriptor_to_scc_map) {
    sccs_to_process.push(p.second);
  }
  // Run a BFS to fill the two data structures: child_to_parent_scc_map and
  // scc_to_outgoing_edges_map.
  while (!sccs_to_process.empty()) {
    const SCC* scc = sccs_to_process.front();
    sccs_to_process.pop();
    auto& count = scc_to_outgoing_edges_map[scc];
    for (const auto& child : scc->children) {
      // Test whether this child has been seen thus far.  We do not know if the
      // children SCC vector contains unique children SCC.
      auto& parent_set = child_to_parent_scc_map[child];
      if (parent_set.empty()) {
        // Just added.
        sccs_to_process.push(child);
      }
      auto ret = parent_set.insert(scc);
      if (ret.second) {
        ++count;
      }
    }
  }
  std::vector<const SCC*> next_scc_q;
  // Find out the SCCs that do not have an outgoing edge i.e. the protos in this
  // SCC do not depend on protos other than the ones in this SCC.
  for (const auto& p : scc_to_outgoing_edges_map) {
    if (p.second == 0) {
      next_scc_q.push_back(p.first);
    }
  }
  ABSL_DCHECK(!next_scc_q.empty()) << "No independent components!";
  // Topologically sort the SCCs.
  // If an SCC no longer has an outgoing edge i.e. all the SCCs it depends on
  // have been ordered, then this SCC is now a candidate for ordering.
  std::vector<const Descriptor*> sorted_messages;
  while (!next_scc_q.empty()) {
    std::vector<const SCC*> current_scc_q;
    current_scc_q.swap(next_scc_q);
    // SCCs present in the current_scc_q are topologically equivalent to each
    // other.  Therefore they can be added to the output in any order.  We sort
    // these SCCs by the full_name() of the first descriptor that belongs to the
    // SCC.  This works well since the descriptors in each SCC are sorted by
    // full_name() and also that a descriptor can be part of only one SCC.
    std::sort(current_scc_q.begin(), current_scc_q.end(),
              [](const SCC* a, const SCC* b) {
                ABSL_DCHECK(!a->descriptors.empty()) << "No descriptors!";
                ABSL_DCHECK(!b->descriptors.empty()) << "No descriptors!";
                const Descriptor* ad = a->descriptors[0];
                const Descriptor* bd = b->descriptors[0];
                return ad->full_name() < bd->full_name();
              });
    while (!current_scc_q.empty()) {
      const SCC* scc = current_scc_q.back();
      current_scc_q.pop_back();
      // Messages in an SCC are already sorted on full_name().  So we can emit
      // them right away.
      for (const Descriptor* d : scc->descriptors) {
        // Only push messages that are defined in the file.
        if (descriptor_to_scc_map.contains(d)) {
          sorted_messages.push_back(d);
        }
      }
      // Find all the SCCs that are dependent on the current SCC.
      const auto& parents = child_to_parent_scc_map.find(scc);
      if (parents == child_to_parent_scc_map.end()) continue;
      for (const SCC* parent : parents->second) {
        auto it = scc_to_outgoing_edges_map.find(parent);
        ABSL_CHECK(it != scc_to_outgoing_edges_map.end());
        ABSL_CHECK(it->second > 0);
        // Reduce the dependency count for the SCC.  In case the dependency
        // count reaches 0, add the SCC to the list of SCCs to be ordered next.
        it->second--;
        if (it->second == 0) {
          next_scc_q.push_back(parent);
        }
      }
    }
  }
  for (const auto& p : scc_to_outgoing_edges_map) {
    ABSL_DCHECK(p.second == 0) << "SCC left behind!";
  }
  return sorted_messages;
}
}  // namespace

bool FileGenerator::ShouldSkipDependencyImports(
    const FileDescriptor* dep) const {
  // Do not import weak deps.
  if (!options_.opensource_runtime && IsDepWeak(dep)) {
    return true;
  }

  // Skip feature imports, which are a visible (but non-functional) deviation
  // between editions and legacy syntax.
  if (options_.strip_nonfunctional_codegen &&
      IsKnownFeatureProto(dep->name())) {
    return true;
  }

  return false;
}

FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options)
    : file_(file), options_(options), scc_analyzer_(options) {
  std::vector<const Descriptor*> msgs = FlattenMessagesInFile(file);
  std::vector<const Descriptor*> msgs_topologically_ordered =
      TopologicalSortMessagesInFile(file, scc_analyzer_);
  ABSL_CHECK(msgs_topologically_ordered.size() == msgs.size())
      << "Size mismatch";

  for (int i = 0; i < msgs.size(); ++i) {
    message_generators_.push_back(std::make_unique<MessageGenerator>(
        msgs[i], variables_, i, options, &scc_analyzer_));
    message_generators_.back()->AddGenerators(&enum_generators_,
                                              &extension_generators_);
  }
  absl::flat_hash_map<const Descriptor*, int> msg_to_index;
  for (size_t i = 0; i < msgs.size(); ++i) {
    msg_to_index[msgs[i]] = i;
  }
  // Populate the topological order.
  for (size_t i = 0; i < msgs.size(); ++i) {
    auto it = msg_to_index.find(msgs_topologically_ordered[i]);
    ABSL_DCHECK(it != msg_to_index.end())
        << "Topological order has a message not present in the file!";
    message_generators_topologically_ordered_.push_back(it->second);
  }

  for (int i = 0; i < file->enum_type_count(); ++i) {
    enum_generators_.push_back(
        std::make_unique<EnumGenerator>(file->enum_type(i), options));
  }

  for (int i = 0; i < file->service_count(); ++i) {
    service_generators_.push_back(std::make_unique<ServiceGenerator>(
        file->service(i), variables_, options));
  }
  if (HasGenericServices(file_, options_)) {
    for (size_t i = 0; i < service_generators_.size(); ++i) {
      service_generators_[i]->index_in_metadata_ = i;
    }
  }

  for (int i = 0; i < file->extension_count(); ++i) {
    extension_generators_.push_back(std::make_unique<ExtensionGenerator>(
        file->extension(i), options, &scc_analyzer_));
  }

  for (int i = 0; i < file->weak_dependency_count(); ++i) {
    weak_deps_.insert(file->weak_dependency(i));
  }
}

void FileGenerator::GenerateFile(io::Printer* p, GeneratedFileType file_type,
                                 std::function<void()> cb) {
  auto v = p->WithVars(FileVars(file_, options_));
  auto guard = IncludeGuard(file_, file_type, options_);
  p->Print(
      "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
      "// source: $filename$\n");
  if (options_.opensource_runtime) {
    p->Print("// Protobuf C++ Version: $protobuf_cpp_version$\n",
             "protobuf_cpp_version", PROTOBUF_CPP_VERSION_STRING);
  }
  p->Print("\n");
  p->Emit({{"cb", cb}, {"guard", guard}}, R"(
    #ifndef $guard$
    #define $guard$

    #include <limits>
    #include <string>
    #include <type_traits>
    #include <utility>

    $cb$;

    #endif  // $guard$
  )");
}

void FileGenerator::GenerateMacroUndefs(io::Printer* p) {
  // Only do this for protobuf's own types. There are some google3 protos using
  // macros as field names and the generated code compiles after the macro
  // expansion. Undefing these macros actually breaks such code.
  if (file_->name() != "third_party/protobuf/compiler/plugin.proto" &&
      file_->name() != "google/protobuf/compiler/plugin.proto") {
    return;
  }

  std::vector<const FieldDescriptor*> fields;
  ListAllFields(file_, &fields);

  absl::flat_hash_set<absl::string_view> all_fields;
  for (const FieldDescriptor* field : fields) {
    all_fields.insert(field->name());
  }

  for (absl::string_view name : {"major", "minor"}) {
    if (!all_fields.contains(name)) {
      continue;
    }

    p->Emit({{"name", std::string(name)}}, R"(
      #ifdef $name$
      #undef $name$
      #endif  // $name$
    )");
  }
}


void FileGenerator::GenerateSharedHeaderCode(io::Printer* p) {
  p->Emit(
      {
          {"port_def",
           [&] { IncludeFile("third_party/protobuf/port_def.inc", p); }},
          {"port_undef",
           [&] { IncludeFile("third_party/protobuf/port_undef.inc", p); }},
          {"dllexport_macro", FileDllExport(file_, options_)},
          {"undefs", [&] { GenerateMacroUndefs(p); }},
          {"global_state_decls",
           [&] { GenerateGlobalStateFunctionDeclarations(p); }},
          {"any_metadata",
           [&] {
             NamespaceOpener ns(ProtobufNamespace(options_), p);
             p->Emit(R"cc(
               namespace internal {
               class AnyMetadata;
               }  // namespace internal
             )cc");
           }},
          {"fwd_decls", [&] { GenerateForwardDeclarations(p); }},
          {"proto2_ns_enums",
           [&] { GenerateProto2NamespaceEnumSpecializations(p); }},
          {"main_decls",
           [&] {
             NamespaceOpener ns(Namespace(file_, options_), p);
             p->Emit(
                 {
                     {"enums", [&] { GenerateEnumDefinitions(p); }},
                     {"messages", [&] { GenerateMessageDefinitions(p); }},
                     {"services", [&] { GenerateServiceDefinitions(p); }},
                     {"extensions", [&] { GenerateExtensionIdentifiers(p); }},
                     {"inline_fns",
                      [&] { GenerateInlineFunctionDefinitions(p); }},
                 },
                 R"(
                   $enums$

                   $hrule_thick$

                   $messages$

                   $hrule_thick$

                   $services$

                   $extensions$

                   $hrule_thick$

                   $inline_fns$

                   // @@protoc_insertion_point(namespace_scope)
                 )");
           }},
      },
      R"(
          // Must be included last.
          $port_def$

          #define $dllexport_macro$$ dllexport_decl$
          $undefs$

          $any_metadata$;

          $global_state_decls$;
          $fwd_decls$

          $main_decls$

          $proto2_ns_enums$

          // @@protoc_insertion_point(global_scope)

          $port_undef$
      )");
}

void FileGenerator::GenerateProtoHeader(io::Printer* p,
                                        absl::string_view info_path) {
  if (!options_.proto_h) {
    return;
  }

  GenerateFile(p, GeneratedFileType::kProtoH, [&] {
    if (!options_.opensource_runtime) {
      p->Emit(R"(
          #ifdef SWIG
          #error "Do not SWIG-wrap protobufs."
          #endif  // SWIG
        )");
    }
    if (IsBootstrapProto(options_, file_)) {
      p->Emit({{"name", StripProto(file_->name())}}, R"cc(
        // IWYU pragma: private, include "$name$.proto.h"
      )cc");
    }

    p->Emit(
        {
            {"library_includes", [&] { GenerateLibraryIncludes(p); }},
            {"proto_includes",
             [&] {
               for (int i = 0; i < file_->public_dependency_count(); ++i) {
                 const FileDescriptor* dep = file_->public_dependency(i);
                 p->Emit({{"name", StripProto(dep->name())}}, R"(
                    #include "$name$.proto.h"
                 )");
               }
             }},
            {"metadata_pragma", [&] { GenerateMetadataPragma(p, info_path); }},
            {"header_main", [&] { GenerateSharedHeaderCode(p); }},
        },
        R"cc(
          $library_includes$;
          $proto_includes$;
          // @@protoc_insertion_point(includes)

          $metadata_pragma$;
          $header_main$;
        )cc");
  });
}

void FileGenerator::GeneratePBHeader(io::Printer* p,
                                     absl::string_view info_path) {
  GenerateFile(p, GeneratedFileType::kPbH, [&] {
    p->Emit(
        {
            {"library_includes",
             [&] {
               if (options_.proto_h) {
                 std::string target_basename = StripProto(file_->name());
                 if (!options_.opensource_runtime) {
                   GetBootstrapBasename(options_, target_basename,
                                        &target_basename);
                 }
                 p->Emit({{"name", target_basename}}, R"(
              #include "$name$.proto.h"  // IWYU pragma: export
              )");
               } else {
                 GenerateLibraryIncludes(p);
               }
             }},
            {"proto_includes",
             [&] {
               if (options_.transitive_pb_h) {
                 GenerateDependencyIncludes(p);
               }
             }},
            {"metadata_pragma", [&] { GenerateMetadataPragma(p, info_path); }},
            {"header_main",
             [&] {
               if (!options_.proto_h) {
                 GenerateSharedHeaderCode(p);
                 return;
               }

               {
                 NamespaceOpener ns(Namespace(file_, options_), p);
                 p->Emit(R"cc(

                   // @@protoc_insertion_point(namespace_scope)
                 )cc");
               }
               p->Emit(R"cc(

                 // @@protoc_insertion_point(global_scope)
               )cc");
             }},
        },
        R"cc(
          $library_includes$;
          $proto_includes$;
          // @@protoc_insertion_point(includes)

          $metadata_pragma$;
          $header_main$;
        )cc");
  });
}

void FileGenerator::DoIncludeFile(absl::string_view google3_name,
                                  bool do_export, io::Printer* p) {
  constexpr absl::string_view prefix = "third_party/protobuf/";
  ABSL_CHECK(absl::StartsWith(google3_name, prefix)) << google3_name;

  auto v = p->WithVars(
      {{"export_suffix", do_export ? "// IWYU pragma: export" : ""}});

  if (options_.opensource_runtime) {
    absl::ConsumePrefix(&google3_name, prefix);
    absl::ConsumePrefix(&google3_name, "internal/");
    absl::ConsumePrefix(&google3_name, "proto/");
    absl::ConsumePrefix(&google3_name, "public/");

    std::string path;
    if (absl::ConsumePrefix(&google3_name, "io/public/")) {
      path = absl::StrCat("io/", google3_name);
    } else {
      path = std::string(google3_name);
    }

    if (options_.runtime_include_base.empty()) {
      p->Emit({{"path", path}}, R"(
        #include "google/protobuf/$path$"$  export_suffix$
      )");
    } else {
      p->Emit({{"base", options_.runtime_include_base}, {"path", path}},
              R"(
        #include "$base$google/protobuf/$path$"$  export_suffix$
      )");
    }
  } else {
    std::string path(google3_name);
    // The bootstrapped proto generated code needs to use the
    // third_party/protobuf header paths to avoid circular dependencies.
    if (options_.bootstrap) {
      constexpr absl::string_view bootstrap_prefix = "net/proto2/public";
      if (absl::ConsumePrefix(&google3_name, bootstrap_prefix)) {
        path = absl::StrCat("third_party/protobuf", google3_name);
      }
    }

    p->Emit({{"path", path}}, R"(
      #include "$path$"$  export_suffix$
    )");
  }
}

std::string FileGenerator::CreateHeaderInclude(absl::string_view basename,
                                               const FileDescriptor* file) {
  if (options_.opensource_runtime && IsWellKnownMessage(file) &&
      !options_.runtime_include_base.empty()) {
    return absl::StrCat("\"", options_.runtime_include_base, basename, "\"");
  }

  return absl::StrCat("\"", basename, "\"");
}

void FileGenerator::GenerateSourceIncludes(io::Printer* p) {
  std::string target_basename = StripProto(file_->name());
  if (!options_.opensource_runtime) {
    GetBootstrapBasename(options_, target_basename, &target_basename);
  }

  absl::StrAppend(&target_basename, options_.proto_h ? ".proto.h" : ".pb.h");
  p->Emit({{"h_include", CreateHeaderInclude(target_basename, file_)}},
          R"(
        // Generated by the protocol buffer compiler.  DO NOT EDIT!
        // source: $filename$

        #include $h_include$

        #include <algorithm>
      )");

  IncludeFile("third_party/protobuf/io/coded_stream.h", p);
  // TODO This is to include parse_context.h, we need a better way
  IncludeFile("third_party/protobuf/extension_set.h", p);
  IncludeFile("third_party/protobuf/wire_format_lite.h", p);
  if (ShouldVerify(file_, options_, &scc_analyzer_)) {
    IncludeFile("third_party/protobuf/wire_format_verify.h", p);
  }

  // Unknown fields implementation in lite mode uses StringOutputStream
  if (!UseUnknownFieldSet(file_, options_) && !message_generators_.empty()) {
    IncludeFile("third_party/protobuf/io/zero_copy_stream_impl_lite.h", p);
  }

  if (HasDescriptorMethods(file_, options_)) {
    IncludeFile("third_party/protobuf/descriptor.h", p);
    IncludeFile("third_party/protobuf/generated_message_reflection.h", p);
    IncludeFile("third_party/protobuf/reflection_ops.h", p);
    IncludeFile("third_party/protobuf/wire_format.h", p);
  }

  if (HasGeneratedMethods(file_, options_)) {
    IncludeFile("third_party/protobuf/generated_message_tctable_impl.h", p);
  }

  if (options_.proto_h) {
    // Use the smaller .proto.h files.
    for (int i = 0; i < file_->dependency_count(); ++i) {
      const FileDescriptor* dep = file_->dependency(i);

      if (ShouldSkipDependencyImports(dep)) continue;

      std::string basename = StripProto(dep->name());
      if (options_.bootstrap) {
        GetBootstrapBasename(options_, basename, &basename);
      }
      p->Emit({{"name", basename}}, R"(
        #include "$name$.proto.h"
      )");
    }
  }

  if (HasCordFields(file_, options_)) {
    p->Emit(R"(
      #include "absl/strings/internal/string_constant.h"
    )");
  }

  p->Emit(R"cc(
    // @@protoc_insertion_point(includes)

    // Must be included last.
  )cc");
  IncludeFile("third_party/protobuf/port_def.inc", p);
}

void FileGenerator::GenerateSourcePrelude(io::Printer* p) {
  // For MSVC builds, we use #pragma init_seg to move the initialization of our
  // libraries to happen before the user code.
  // This worksaround the fact that MSVC does not do constant initializers when
  // required by the standard.
  p->Emit(R"cc(
    PROTOBUF_PRAGMA_INIT_SEG
    namespace _pb = ::$proto_ns$;
    namespace _pbi = ::$proto_ns$::internal;
  )cc");

  if (HasGeneratedMethods(file_, options_)) {
    p->Emit(R"cc(
      namespace _fl = ::$proto_ns$::internal::field_layout;
    )cc");
  }
}

void FileGenerator::GenerateSourceDefaultInstance(int idx, io::Printer* p) {
  MessageGenerator* generator = message_generators_[idx].get();

  if (!ShouldGenerateClass(generator->descriptor(), options_)) return;

  // Generate the split instance first because it's needed in the constexpr
  // constructor.
  if (ShouldSplit(generator->descriptor(), options_)) {
    // Use a union to disable the destructor of the _instance member.
    // We can constant initialize, but the object will still have a non-trivial
    // destructor that we need to elide.
    //
    // NO_DESTROY is not necessary for correctness. The empty destructor is
    // enough. However, the empty destructor fails to be elided in some
    // configurations (like non-opt or with certain sanitizers). NO_DESTROY is
    // there just to improve performance and binary size in these builds.
    p->Emit(
        {
            {"type", DefaultInstanceType(generator->descriptor(), options_,
                                         /*split=*/true)},
            {"name", DefaultInstanceName(generator->descriptor(), options_,
                                         /*split=*/true)},
            {"default",
             [&] { generator->GenerateInitDefaultSplitInstance(p); }},
            {"class", absl::StrCat(ClassName(generator->descriptor()),
                                   "::Impl_::Split")},
        },
        R"cc(
          struct $type$ {
            PROTOBUF_CONSTEXPR $type$() : _instance{$default$} {}
            union {
              $class$ _instance;
            };
          };

          PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT$ dllexport_decl$
              PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 const $type$ $name$;
        )cc");
  }

  generator->GenerateConstexprConstructor(p);

  if (IsFileDescriptorProto(file_, options_)) {
    p->Emit(
        {
            {"type", DefaultInstanceType(generator->descriptor(), options_)},
            {"name", DefaultInstanceName(generator->descriptor(), options_)},
            {"class", ClassName(generator->descriptor())},
        },
        R"cc(
          struct $type$ {
#if defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
            constexpr $type$() : _instance(::_pbi::ConstantInitialized{}) {}
#else   // defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
            $type$() {}
            void Init() { ::new (&_instance) $class$(); };
#endif  // defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
            ~$type$() {}
            union {
              $class$ _instance;
            };
          };

          PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT$ dllexport_decl$
              PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 $type$ $name$;
        )cc");
  } else {
    p->Emit(
        {
            {"type", DefaultInstanceType(generator->descriptor(), options_)},
            {"name", DefaultInstanceName(generator->descriptor(), options_)},
            {"class", ClassName(generator->descriptor())},
        },
        R"cc(
          struct $type$ {
            PROTOBUF_CONSTEXPR $type$() : _instance(::_pbi::ConstantInitialized{}) {}
            ~$type$() {}
            union {
              $class$ _instance;
            };
          };

          PROTOBUF_ATTRIBUTE_NO_DESTROY PROTOBUF_CONSTINIT$ dllexport_decl$
              PROTOBUF_ATTRIBUTE_INIT_PRIORITY1 $type$ $name$;
        )cc");
  }

  for (int i = 0; i < generator->descriptor()->field_count(); ++i) {
    const FieldDescriptor* field = generator->descriptor()->field(i);
    if (!IsStringInlined(field, options_)) {
      continue;
    }

    // Force the initialization of the inlined string in the default instance.
    p->Emit(
        {
            {"class", ClassName(generator->descriptor())},
            {"field", FieldName(field)},
            {"default", DefaultInstanceName(generator->descriptor(), options_)},
            {"member", FieldMemberName(field, ShouldSplit(field, options_))},
        },
        R"cc(
          PROTOBUF_ATTRIBUTE_INIT_PRIORITY2 std::true_type
              $class$::Impl_::_init_inline_$field$_ =
                  ($default$._instance.$member$.Init(), std::true_type{});
        )cc");
  }

  if (options_.lite_implicit_weak_fields) {
    p->Emit(
        {
            {"ptr", DefaultInstancePtr(generator->descriptor(), options_)},
            {"name", DefaultInstanceName(generator->descriptor(), options_)},
        },
        R"cc(
          PROTOBUF_CONSTINIT const void* $ptr$ = &$name$;
        )cc");
  }
}

// A list of things defined in one .pb.cc file that we need to reference from
// another .pb.cc file.
struct FileGenerator::CrossFileReferences {
  // When we forward-declare things, we want to create a sorted order so our
  // output is deterministic and minimizes namespace changes.
  struct DescCompare {
    template <typename T>
    bool operator()(const T* const& a, const T* const& b) const {
      return a->full_name() < b->full_name();
    }

    bool operator()(const FileDescriptor* const& a,
                    const FileDescriptor* const& b) const {
      return a->name() < b->name();
    }
  };

  // Populated if we are referencing from messages or files.
  absl::btree_set<const Descriptor*, DescCompare> weak_default_instances;

  // Only if we are referencing from files.
  absl::btree_set<const FileDescriptor*, DescCompare> strong_reflection_files;
  absl::btree_set<const FileDescriptor*, DescCompare> weak_reflection_files;
};

void FileGenerator::GetCrossFileReferencesForField(const FieldDescriptor* field,
                                                   CrossFileReferences* refs) {
  const Descriptor* msg = field->message_type();
  if (msg == nullptr) {
    return;
  }

  if (IsImplicitWeakField(field, options_, &scc_analyzer_) ||
      IsWeak(field, options_)) {
    refs->weak_default_instances.insert(msg);
  }
}

void FileGenerator::GetCrossFileReferencesForFile(const FileDescriptor* file,
                                                  CrossFileReferences* refs) {
  ForEachField(file, [this, refs](const FieldDescriptor* field) {
    GetCrossFileReferencesForField(field, refs);
  });

  if (!HasDescriptorMethods(file, options_)) {
    return;
  }

  for (int i = 0; i < file->dependency_count(); ++i) {
    const FileDescriptor* dep = file->dependency(i);

    if (!ShouldSkipDependencyImports(file->dependency(i))) {
      refs->strong_reflection_files.insert(dep);
    } else if (IsDepWeak(dep)) {
      refs->weak_reflection_files.insert(dep);
    }
  }
}

// Generates references to variables defined in other files.
void FileGenerator::GenerateInternalForwardDeclarations(
    const CrossFileReferences& refs, io::Printer* p) {
  {
    NamespaceOpener ns(p);
    for (auto instance : refs.weak_default_instances) {
      ns.ChangeTo(Namespace(instance, options_));

      if (options_.lite_implicit_weak_fields) {
        p->Emit({{"ptr", DefaultInstancePtr(instance, options_)}}, R"cc(
          PROTOBUF_CONSTINIT __attribute__((weak)) const void* $ptr$ =
              &::_pbi::implicit_weak_message_default_instance;
        )cc");
      } else {
        p->Emit({{"type", DefaultInstanceType(instance, options_)},
                 {"name", DefaultInstanceName(instance, options_)}},
                R"cc(
                  extern __attribute__((weak)) $type$ $name$;
                )cc");
      }
    }
  }

  for (auto file : refs.weak_reflection_files) {
    p->Emit({{"table", DescriptorTableName(file, options_)}}, R"cc(
      extern __attribute__((weak)) const ::_pbi::DescriptorTable $table$;
    )cc");
  }
}

void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* p) {
  auto v = p->WithVars(FileVars(file_, options_));

  GenerateSourceIncludes(p);
  GenerateSourcePrelude(p);

  if (IsAnyMessage(file_, options_)) {
    MuteWuninitialized(p);
  }

  CrossFileReferences refs;
  ForEachField(message_generators_[idx]->descriptor(),
               [this, &refs](const FieldDescriptor* field) {
                 GetCrossFileReferencesForField(field, &refs);
               });

  GenerateInternalForwardDeclarations(refs, p);

  {
    NamespaceOpener ns(Namespace(file_, options_), p);
    p->Emit(
        {
            {"defaults", [&] { GenerateSourceDefaultInstance(idx, p); }},
            {"class_methods",
             [&] { message_generators_[idx]->GenerateClassMethods(p); }},
        },
        R"cc(
          $defaults$;

          $class_methods$;

          // @@protoc_insertion_point(namespace_scope)
        )cc");
  }

  {
    NamespaceOpener proto_ns(ProtobufNamespace(options_), p);
    message_generators_[idx]->GenerateSourceInProto2Namespace(p);
  }

  if (IsAnyMessage(file_, options_)) {
    UnmuteWuninitialized(p);
  }

  p->Emit(R"cc(
    // @@protoc_insertion_point(global_scope)
  )cc");
}

void FileGenerator::GenerateSourceForExtension(int idx, io::Printer* p) {
  auto v = p->WithVars(FileVars(file_, options_));
  GenerateSourceIncludes(p);
  GenerateSourcePrelude(p);

  NamespaceOpener ns(Namespace(file_, options_), p);
  extension_generators_[idx]->GenerateDefinition(p);
}

void FileGenerator::GenerateGlobalSource(io::Printer* p) {
  auto v = p->WithVars(FileVars(file_, options_));
  GenerateSourceIncludes(p);
  GenerateSourcePrelude(p);

  {
    // Define the code to initialize reflection. This code uses a global
    // constructor to register reflection data with the runtime pre-main.
    if (HasDescriptorMethods(file_, options_)) {
      GenerateReflectionInitializationCode(p);
    }
  }

  NamespaceOpener ns(Namespace(file_, options_), p);
  for (int i = 0; i < enum_generators_.size(); ++i) {
    enum_generators_[i]->GenerateMethods(i, p);
  }
}

void FileGenerator::GenerateSource(io::Printer* p) {
  auto v = p->WithVars(FileVars(file_, options_));

  GenerateSourceIncludes(p);
  GenerateSourcePrelude(p);
  CrossFileReferences refs;
  GetCrossFileReferencesForFile(file_, &refs);
  GenerateInternalForwardDeclarations(refs, p);

  if (IsAnyMessage(file_, options_)) {
    MuteWuninitialized(p);
  }

  {
    NamespaceOpener ns(Namespace(file_, options_), p);
    for (int i = 0; i < message_generators_.size(); ++i) {
      GenerateSourceDefaultInstance(
          message_generators_topologically_ordered_[i], p);
    }
  }

  {
    if (HasDescriptorMethods(file_, options_)) {
      // Define the code to initialize reflection. This code uses a global
      // constructor to register reflection data with the runtime pre-main.
      GenerateReflectionInitializationCode(p);
    }
  }

  {
    NamespaceOpener ns(Namespace(file_, options_), p);

    // Actually implement the protos

    // Generate enums.
    for (int i = 0; i < enum_generators_.size(); ++i) {
      enum_generators_[i]->GenerateMethods(i, p);
    }

    // Generate classes.
    for (int i = 0; i < message_generators_.size(); ++i) {
      p->Emit(R"(
        $hrule_thick$
      )");
      message_generators_[i]->GenerateClassMethods(p);
    }

    if (HasGenericServices(file_, options_)) {
      // Generate services.
      for (int i = 0; i < service_generators_.size(); ++i) {
        p->Emit(R"(
          $hrule_thick$
        )");
        service_generators_[i]->GenerateImplementation(p);
      }
    }

    // Define extensions.
    for (int i = 0; i < extension_generators_.size(); ++i) {
      extension_generators_[i]->GenerateDefinition(p);
    }

    p->Emit(R"cc(
      // @@protoc_insertion_point(namespace_scope)
    )cc");
  }

  {
    NamespaceOpener proto_ns(ProtobufNamespace(options_), p);
    for (int i = 0; i < message_generators_.size(); ++i) {
      message_generators_[i]->GenerateSourceInProto2Namespace(p);
    }
  }

  p->Emit(R"cc(
    // @@protoc_insertion_point(global_scope)
  )cc");

  if (IsAnyMessage(file_, options_)) {
    UnmuteWuninitialized(p);
  }

  IncludeFile("third_party/protobuf/port_undef.inc", p);
}

void FileGenerator::GenerateReflectionInitializationCode(io::Printer* p) {
  if (!message_generators_.empty()) {
    p->Emit({{"len", message_generators_.size()}}, R"cc(
      static ::_pb::Metadata $file_level_metadata$[$len$];
    )cc");
  }

  if (!enum_generators_.empty()) {
    p->Emit({{"len", enum_generators_.size()}}, R"cc(
      static const ::_pb::EnumDescriptor* $file_level_enum_descriptors$[$len$];
    )cc");
  } else {
    p->Emit(R"cc(
      static constexpr const ::_pb::EnumDescriptor**
          $file_level_enum_descriptors$ = nullptr;
    )cc");
  }

  if (HasGenericServices(file_, options_) && file_->service_count() > 0) {
    p->Emit({{"len", file_->service_count()}}, R"cc(
      static const ::_pb::ServiceDescriptor*
          $file_level_service_descriptors$[$len$];
    )cc");
  } else {
    p->Emit(R"cc(
      static constexpr const ::_pb::ServiceDescriptor**
          $file_level_service_descriptors$ = nullptr;
    )cc");
  }

  if (!message_generators_.empty()) {
    std::vector<std::pair<size_t, size_t>> offsets;
    offsets.reserve(message_generators_.size());

    p->Emit(
        {
            {"offsets",
             [&] {
               for (int i = 0; i < message_generators_.size(); ++i) {
                 offsets.push_back(message_generators_[i]->GenerateOffsets(p));
               }
             }},
            {"schemas",
             [&] {
               int offset = 0;
               for (int i = 0; i < message_generators_.size(); ++i) {
                 message_generators_[i]->GenerateSchema(p, offset,
                                                        offsets[i].second);
                 offset += offsets[i].first;
               }
             }},
            {"defaults",
             [&] {
               for (auto& gen : message_generators_) {
                 p->Emit(
                     {
                         {"ns", Namespace(gen->descriptor(), options_)},
                         {"class", ClassName(gen->descriptor())},
                     },
                     R"cc(
                       &$ns$::_$class$_default_instance_._instance,
                     )cc");
               }
             }},
        },
        R"cc(
          const ::uint32_t $tablename$::offsets[] PROTOBUF_SECTION_VARIABLE(
              protodesc_cold) = {
              $offsets$,
          };

          static const ::_pbi::MigrationSchema
              schemas[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
                  $schemas$,
          };

          static const ::_pb::Message* const file_default_instances[] = {
              $defaults$,
          };
        )cc");
  } else {
    // Ee still need these symbols to exist.
    //
    // MSVC doesn't like empty arrays, so we add a dummy.
    p->Emit(R"cc(
      const ::uint32_t $tablename$::offsets[1] = {};
      static constexpr ::_pbi::MigrationSchema* schemas = nullptr;
      static constexpr ::_pb::Message* const* file_default_instances = nullptr;
    )cc");
  }

  // ---------------------------------------------------------------

  // Embed the descriptor.  We simply serialize the entire
  // FileDescriptorProto/ and embed it as a string literal, which is parsed and
  // built into real descriptors at initialization time.

  FileDescriptorProto file_proto = StripSourceRetentionOptions(*file_);
  std::string file_data;
  file_proto.SerializeToString(&file_data);

  auto desc_name = UniqueName("descriptor_table_protodef", file_, options_);
  p->Emit(
      {{"desc_name", desc_name},
       {"encoded_file_proto",
        [&] {
          if (options_.strip_nonfunctional_codegen) {
            p->Emit(R"cc("")cc");
            return;
          }

          absl::string_view data = file_data;
          if (data.size() <= 65535) {
            static constexpr size_t kBytesPerLine = 40;
            while (!data.empty()) {
              auto to_write = std::min(kBytesPerLine, data.size());
              auto chunk = data.substr(0, to_write);
              data = data.substr(to_write);

              p->Emit({{"text", EscapeTrigraphs(absl::CEscape(chunk))}}, R"cc(
                "$text$"
              )cc");
            }
            return;
          }

          // Workaround for MSVC: "Error C1091: compiler limit: string exceeds
          // 65535 bytes in length". Declare a static array of chars rather than
          // use a string literal. Only write 25 bytes per line.
          static constexpr size_t kBytesPerLine = 25;
          while (!data.empty()) {
            auto to_write = std::min(kBytesPerLine, data.size());
            auto chunk = data.substr(0, to_write);
            data = data.substr(to_write);

            std::string line;
            for (char c : chunk) {
              absl::StrAppend(&line, "'",
                              absl::CEscape(absl::string_view(&c, 1)), "', ");
            }

            p->Emit({{"line", line}}, R"cc(
              $line$
            )cc");
          }
        }}},
      R"cc(
        const char $desc_name$[] PROTOBUF_SECTION_VARIABLE(protodesc_cold) = {
            $encoded_file_proto$,
        };
      )cc");

  CrossFileReferences refs;
  GetCrossFileReferencesForFile(file_, &refs);
  size_t num_deps =
      refs.strong_reflection_files.size() + refs.weak_reflection_files.size();

  // Build array of DescriptorTable deps.
  if (num_deps > 0) {
    p->Emit(
        {
            {"len", num_deps},
            {"deps",
             [&] {
               for (auto dep : refs.strong_reflection_files) {
                 p->Emit({{"name", DescriptorTableName(dep, options_)}}, R"cc(
                   &::$name$,
                 )cc");
               }
               for (auto dep : refs.weak_reflection_files) {
                 p->Emit({{"name", DescriptorTableName(dep, options_)}}, R"cc(
                   &::$name$,
                 )cc");
               }
             }},
        },
        R"cc(
          static const ::_pbi::DescriptorTable* const $desc_table$_deps[$len$] =
              {
                  $deps$,
          };
        )cc");
  }

  // The DescriptorTable itself.
  // Should be "bool eager = NeedsEagerDescriptorAssignment(file_, options_);"
  // however this might cause a tsan failure in superroot b/148382879,
  // so disable for now.
  bool eager = false;
  p->Emit(
      {
          {"eager", eager ? "true" : "false"},
          {"file_proto_len",
           options_.strip_nonfunctional_codegen ? 0 : file_data.size()},
          {"proto_name", desc_name},
          {"deps_ptr", num_deps == 0
                           ? "nullptr"
                           : absl::StrCat(p->LookupVar("desc_table"), "_deps")},
          {"num_deps", num_deps},
          {"num_msgs", message_generators_.size()},
          {"msgs_ptr", message_generators_.empty()
                           ? "nullptr"
                           : std::string(p->LookupVar("file_level_metadata"))},
      },
      R"cc(
        static ::absl::once_flag $desc_table$_once;
        const ::_pbi::DescriptorTable $desc_table$ = {
            false,
            $eager$,
            $file_proto_len$,
            $proto_name$,
            "$filename$",
            &$desc_table$_once,
            $deps_ptr$,
            $num_deps$,
            $num_msgs$,
            schemas,
            file_default_instances,
            $tablename$::offsets,
            $msgs_ptr$,
            $file_level_enum_descriptors$,
            $file_level_service_descriptors$,
        };

        // This function exists to be marked as weak.
        // It can significantly speed up compilation by breaking up LLVM's SCC
        // in the .pb.cc translation units. Large translation units see a
        // reduction of more than 35% of walltime for optimized builds. Without
        // the weak attribute all the messages in the file, including all the
        // vtables and everything they use become part of the same SCC through
        // a cycle like:
        // GetMetadata -> descriptor table -> default instances ->
        //   vtables -> GetMetadata
        // By adding a weak function here we break the connection from the
        // individual vtables back into the descriptor table.
        PROTOBUF_ATTRIBUTE_WEAK const ::_pbi::DescriptorTable* $desc_table$_getter() {
          return &$desc_table$;
        }
      )cc");

  // For descriptor.proto and cpp_features.proto we want to avoid doing any
  // dynamic initialization, because in some situations that would otherwise
  // pull in a lot of unnecessary code that can't be stripped by --gc-sections.
  // Descriptor initialization will still be performed lazily when it's needed.
  if (!IsLazilyInitializedFile(file_->name())) {
    p->Emit({{"dummy", UniqueName("dynamic_init_dummy", file_, options_)}},
            R"cc(
              // Force running AddDescriptors() at dynamic initialization time.
              PROTOBUF_ATTRIBUTE_INIT_PRIORITY2
              static ::_pbi::AddDescriptorsRunner $dummy$(&$desc_table$);
            )cc");
  }

  // However, we must provide a way to force initialize the default instances
  // of FileDescriptorProto which will be used during registration of other
  // files.
  if (IsFileDescriptorProto(file_, options_)) {
    NamespaceOpener ns(p);
    ns.ChangeTo(absl::StrCat(ProtobufNamespace(options_), "::internal"));
    p->Emit(
        {{"dummy", UniqueName("dynamic_init_dummy", file_, options_)},
         {"initializers", absl::StrJoin(message_generators_, "\n",
                                        [&](std::string* out, const auto& gen) {
                                          absl::StrAppend(
                                              out,
                                              DefaultInstanceName(
                                                  gen->descriptor(), options_),
                                              ".Init();");
                                        })}},
        R"cc(
          //~ Emit wants an indented line, so give it a comment to strip.
#if !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
          PROTOBUF_EXPORT void InitializeFileDescriptorDefaultInstancesSlow() {
            $initializers$;
          }
          PROTOBUF_ATTRIBUTE_INIT_PRIORITY1
          static std::true_type $dummy${
              (InitializeFileDescriptorDefaultInstances(), std::true_type{})};
#endif  // !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
        )cc");
  }
}

class FileGenerator::ForwardDeclarations {
 public:
  void AddMessage(const Descriptor* d) { classes_.emplace(ClassName(d), d); }
  void AddEnum(const EnumDescriptor* d) { enums_.emplace(ClassName(d), d); }
  void AddSplit(const Descriptor* d) { splits_.emplace(ClassName(d), d); }

  void Print(io::Printer* p, const Options& options) const {
    for (const auto& e : enums_) {
      p->Emit({Sub("enum", e.first).AnnotatedAs(e.second)}, R"cc(
        enum $enum$ : int;
        bool $enum$_IsValid(int value);
      )cc");
    }

    for (const auto& c : classes_) {
      const Descriptor* desc = c.second;
      p->Emit(
          {
              Sub("class", c.first).AnnotatedAs(desc),
              {"default_type", DefaultInstanceType(desc, options)},
              {"default_name", DefaultInstanceName(desc, options)},
          },
          R"cc(
            class $class$;
            struct $default_type$;
            $dllexport_decl $extern $default_type$ $default_name$;
          )cc");
    }

    for (const auto& s : splits_) {
      const Descriptor* desc = s.second;
      p->Emit(
          {
              {"default_type",
               DefaultInstanceType(desc, options, /*split=*/true)},
              {"default_name",
               DefaultInstanceName(desc, options, /*split=*/true)},
          },
          R"cc(
            struct $default_type$;
            $dllexport_decl $extern const $default_type$ $default_name$;
          )cc");
    }
  }

  void PrintTopLevelDecl(io::Printer* p, const Options& options) const {
    if (ShouldGenerateExternSpecializations(options)) {
      for (const auto& c : classes_) {
        if (!ShouldGenerateClass(c.second, options)) continue;
        // To reduce total linker input size in large binaries we make these
        // functions extern and define then in the pb.cc file. This avoids bloat
        // in callers by having duplicate definitions of the template.
        // However, it increases the size of the pb.cc translation units so it
        // is a tradeoff.
        p->Emit({{"class", QualifiedClassName(c.second, options)}}, R"cc(
          template <>
          $dllexport_decl $$class$* Arena::CreateMaybeMessage<$class$>(Arena*);
        )cc");
#ifdef PROTOBUF_EXPLICIT_CONSTRUCTORS
        if (!IsMapEntryMessage(c.second)) {
          p->Emit({{"class", QualifiedClassName(c.second, options)}}, R"cc(
            template <>
            $dllexport_decl $$class$* Arena::CreateMaybeMessage<$class$>(
                Arena*, const $class$&);
          )cc");
        }
#endif  // PROTOBUF_EXPLICIT_CONSTRUCTORS
      }
    }
  }

 private:
  absl::btree_map<std::string, const Descriptor*> classes_;
  absl::btree_map<std::string, const EnumDescriptor*> enums_;
  absl::btree_map<std::string, const Descriptor*> splits_;
};

static void PublicImportDFS(
    const FileDescriptor* fd,
    absl::flat_hash_set<const FileDescriptor*>& fd_set) {
  for (int i = 0; i < fd->public_dependency_count(); ++i) {
    const FileDescriptor* dep = fd->public_dependency(i);
    if (fd_set.insert(dep).second) {
      PublicImportDFS(dep, fd_set);
    }
  }
}

void FileGenerator::GenerateForwardDeclarations(io::Printer* p) {
  std::vector<const Descriptor*> classes;
  FlattenMessagesInFile(file_, &classes);  // All messages need forward decls.

  std::vector<const EnumDescriptor*> enums;
  if (options_.proto_h) {  // proto.h needs extra forward declarations.
    // All classes / enums referred to as field members
    std::vector<const FieldDescriptor*> fields;
    ListAllFields(file_, &fields);
    for (const auto* field : fields) {
      classes.push_back(field->containing_type());
      classes.push_back(field->message_type());
      enums.push_back(field->enum_type());
    }

    ListAllTypesForServices(file_, &classes);
  }

  // Calculate the set of files whose definitions we get through include.
  // No need to forward declare types that are defined in these.
  absl::flat_hash_set<const FileDescriptor*> public_set;
  PublicImportDFS(file_, public_set);

  absl::btree_map<std::string, ForwardDeclarations> decls;
  for (const auto* d : classes) {
    if (d != nullptr && !public_set.contains(d->file()) &&
        ShouldGenerateClass(d, options_))
      decls[Namespace(d, options_)].AddMessage(d);
  }
  for (const auto* e : enums) {
    if (e != nullptr && !public_set.contains(e->file()))
      decls[Namespace(e, options_)].AddEnum(e);
  }
  for (const auto& mg : message_generators_) {
    const Descriptor* d = mg->descriptor();
    if (d != nullptr && public_set.count(d->file()) == 0u &&
        ShouldSplit(mg->descriptor(), options_))
      decls[Namespace(d, options_)].AddSplit(d);
  }

  NamespaceOpener ns(p);
  for (const auto& decl : decls) {
    ns.ChangeTo(decl.first);
    decl.second.Print(p, options_);
  }

  ns.ChangeTo(ProtobufNamespace(options_));
  for (const auto& decl : decls) {
    decl.second.PrintTopLevelDecl(p, options_);
  }

  if (IsFileDescriptorProto(file_, options_)) {
    ns.ChangeTo(absl::StrCat(ProtobufNamespace(options_), "::internal"));
    p->Emit(R"cc(
      //~ Emit wants an indented line, so give it a comment to strip.
#if !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
      PROTOBUF_EXPORT void InitializeFileDescriptorDefaultInstancesSlow();
#endif  // !defined(PROTOBUF_CONSTINIT_DEFAULT_INSTANCES)
    )cc");
  }
}

void FileGenerator::GenerateLibraryIncludes(io::Printer* p) {
  if (UsingImplicitWeakFields(file_, options_)) {
    IncludeFile("third_party/protobuf/implicit_weak_message.h", p);
  }
  if (HasWeakFields(file_, options_)) {
    ABSL_CHECK(!options_.opensource_runtime);
    IncludeFile("third_party/protobuf/weak_field_map.h", p);
  }
  if (HasLazyFields(file_, options_, &scc_analyzer_)) {
    ABSL_CHECK(!options_.opensource_runtime);
    IncludeFile("third_party/protobuf/lazy_field.h", p);
  }
  if (ShouldVerify(file_, options_, &scc_analyzer_)) {
    IncludeFile("third_party/protobuf/wire_format_verify.h", p);
  }

  if (options_.opensource_runtime) {
    // Verify the protobuf library header version is compatible with the protoc
    // version before going any further.
    IncludeFile("third_party/protobuf/port_def.inc", p);
    p->Emit(
        {
            {"min_version", PROTOBUF_MIN_HEADER_VERSION_FOR_PROTOC},
            {"version", PROTOBUF_VERSION},
        },
        R"(
        #if PROTOBUF_VERSION < $min_version$
        #error "This file was generated by a newer version of protoc which is"
        #error "incompatible with your Protocol Buffer headers. Please update"
        #error "your headers."
        #endif  // PROTOBUF_VERSION

        #if $version$ < PROTOBUF_MIN_PROTOC_VERSION
        #error "This file was generated by an older version of protoc which is"
        #error "incompatible with your Protocol Buffer headers. Please"
        #error "regenerate this file with a newer version of protoc."
        #endif  // PROTOBUF_MIN_PROTOC_VERSION
    )");
    IncludeFile("third_party/protobuf/port_undef.inc", p);
  }

  // OK, it's now safe to #include other files.
  IncludeFile("third_party/protobuf/io/coded_stream.h", p);
  IncludeFile("third_party/protobuf/arena.h", p);
  IncludeFile("third_party/protobuf/arenastring.h", p);
  if (IsStringInliningEnabled(options_)) {
    IncludeFile("third_party/protobuf/inlined_string_field.h", p);
  }
  if (HasSimpleBaseClasses(file_, options_)) {
    IncludeFile("third_party/protobuf/generated_message_bases.h", p);
  }
  if (HasGeneratedMethods(file_, options_)) {
    IncludeFile("third_party/protobuf/generated_message_tctable_decl.h", p);
  }
  IncludeFile("third_party/protobuf/generated_message_util.h", p);
  IncludeFile("third_party/protobuf/metadata_lite.h", p);

  if (HasDescriptorMethods(file_, options_)) {
    IncludeFile("third_party/protobuf/generated_message_reflection.h", p);
  }

  if (!message_generators_.empty()) {
    if (HasDescriptorMethods(file_, options_)) {
      IncludeFile("third_party/protobuf/message.h", p);
    } else {
      IncludeFile("third_party/protobuf/message_lite.h", p);
    }
  }
  if (options_.opensource_runtime) {
    // Open-source relies on unconditional includes of these.
    IncludeFileAndExport("third_party/protobuf/repeated_field.h", p);
    IncludeFileAndExport("third_party/protobuf/extension_set.h", p);
  } else {
    // Google3 includes these files only when they are necessary.
    if (HasExtensionsOrExtendableMessage(file_)) {
      IncludeFileAndExport("third_party/protobuf/extension_set.h", p);
    }
    if (HasRepeatedFields(file_)) {
      IncludeFileAndExport("third_party/protobuf/repeated_field.h", p);
    }
    if (HasStringPieceFields(file_, options_)) {
      IncludeFile("third_party/protobuf/string_piece_field_support.h", p);
    }
  }
  if (HasCordFields(file_, options_)) {
    p->Emit(R"(
      #include "absl/strings/cord.h"
      )");
  }
  if (HasMapFields(file_)) {
    IncludeFileAndExport("third_party/protobuf/map.h", p);
    if (HasDescriptorMethods(file_, options_)) {
      IncludeFile("third_party/protobuf/map_entry.h", p);
      IncludeFile("third_party/protobuf/map_field_inl.h", p);
    } else {
      IncludeFile("third_party/protobuf/map_field_lite.h", p);
    }
  }

  if (HasEnumDefinitions(file_)) {
    if (HasDescriptorMethods(file_, options_)) {
      IncludeFile("third_party/protobuf/generated_enum_reflection.h", p);
    } else {
      IncludeFile("third_party/protobuf/generated_enum_util.h", p);
    }
  }

  if (HasGenericServices(file_, options_)) {
    IncludeFile("third_party/protobuf/service.h", p);
  }

  if (UseUnknownFieldSet(file_, options_) && !message_generators_.empty()) {
    IncludeFile("third_party/protobuf/unknown_field_set.h", p);
  }
}

void FileGenerator::GenerateMetadataPragma(io::Printer* p,
                                           absl::string_view info_path) {
  if (info_path.empty() || options_.annotation_pragma_name.empty() ||
      options_.annotation_guard_name.empty()) {
    return;
  }

  p->Emit(
      {
          {"guard", options_.annotation_guard_name},
          {"pragma", options_.annotation_pragma_name},
          {"info_path", std::string(info_path)},
      },
      R"(
        #ifdef $guard$
        #pragma $pragma$ "$info_path$"
        #endif  // $guard$
      )");
}

void FileGenerator::GenerateDependencyIncludes(io::Printer* p) {
  for (int i = 0; i < file_->dependency_count(); ++i) {
    const FileDescriptor* dep = file_->dependency(i);

    if (ShouldSkipDependencyImports(dep)) {
      continue;
    }

    std::string basename = StripProto(dep->name());
    if (options_.bootstrap) {
      GetBootstrapBasename(options_, basename, &basename);
    }

    p->Emit(
        {{"name", CreateHeaderInclude(absl::StrCat(basename, ".pb.h"), dep)}},
        R"(
          #include $name$
        )");
  }
}

void FileGenerator::GenerateGlobalStateFunctionDeclarations(io::Printer* p) {
  // Forward-declare the DescriptorTable because this is referenced by .pb.cc
  // files depending on this file.
  //
  // The TableStruct is also outputted in weak_message_field.cc, because the
  // weak fields must refer to table struct but cannot include the header.
  // Also it annotates extra weak attributes.
  // TODO make sure this situation is handled better.
  p->Emit(R"cc(
    // Internal implementation detail -- do not use these members.
    struct $dllexport_decl $$tablename$ {
      static const ::uint32_t offsets[];
    };
  )cc");

  if (HasDescriptorMethods(file_, options_)) {
    p->Emit(R"cc(
      $dllexport_decl $extern const ::$proto_ns$::internal::DescriptorTable
          $desc_table$;
    )cc");
  }
}

void FileGenerator::GenerateMessageDefinitions(io::Printer* p) {
  for (int i = 0; i < message_generators_.size(); ++i) {
    p->Emit(R"cc(
      $hrule_thin$
    )cc");
    message_generators_[message_generators_topologically_ordered_[i]]
        ->GenerateClassDefinition(p);
  }
}

void FileGenerator::GenerateEnumDefinitions(io::Printer* p) {
  for (int i = 0; i < enum_generators_.size(); ++i) {
    enum_generators_[i]->GenerateDefinition(p);
  }
}

void FileGenerator::GenerateServiceDefinitions(io::Printer* p) {
  if (!HasGenericServices(file_, options_)) {
    return;
  }

  for (int i = 0; i < service_generators_.size(); ++i) {
    p->Emit(R"cc(
      $hrule_thin$
    )cc");
    service_generators_[i]->GenerateDeclarations(p);
  }

  p->Emit(R"cc(
    $hrule_thick$
  )cc");
}

void FileGenerator::GenerateExtensionIdentifiers(io::Printer* p) {
  // Declare extension identifiers. These are in global scope and so only
  // the global scope extensions.
  for (auto& extension_generator : extension_generators_) {
    if (extension_generator->IsScoped()) {
      continue;
    }
    extension_generator->GenerateDeclaration(p);
  }
}

void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* p) {
  // TODO remove pragmas when gcc is no longer used. Current version
  // of gcc fires a bogus error when compiled with strict-aliasing.
  p->Emit(R"(
      #ifdef __GNUC__
      #pragma GCC diagnostic push
      #pragma GCC diagnostic ignored "-Wstrict-aliasing"
      #endif  // __GNUC__
  )");

  for (int i = 0; i < message_generators_.size(); ++i) {
    p->Emit(R"cc(
      $hrule_thin$
    )cc");
    message_generators_[i]->GenerateInlineMethods(p);
  }

  p->Emit(R"(
      #ifdef __GNUC__
      #pragma GCC diagnostic pop
      #endif  // __GNUC__
  )");
}

void FileGenerator::GenerateProto2NamespaceEnumSpecializations(io::Printer* p) {
  // Emit GetEnumDescriptor specializations into google::protobuf namespace.
  if (!HasEnumDefinitions(file_)) {
    return;
  }

  p->PrintRaw("\n");
  NamespaceOpener ns(ProtobufNamespace(options_), p);
  p->PrintRaw("\n");
  for (auto& gen : enum_generators_) {
    gen->GenerateGetEnumDescriptorSpecializations(p);
  }
  p->PrintRaw("\n");
}

std::vector<const Descriptor*> FileGenerator::MessagesInTopologicalOrder()
    const {
  std::vector<const Descriptor*> descs;
  descs.reserve(message_generators_.size());
  for (int i = 0; i < message_generators_.size(); ++i) {
    descs.push_back(
        message_generators_[message_generators_topologically_ordered_[i]]
            ->descriptor());
  }
  return descs;
}
}  // namespace cpp
}  // namespace compiler
}  // namespace protobuf
}  // namespace google
