diff options
Diffstat (limited to 'src/google/protobuf/compiler/cpp')
25 files changed, 1815 insertions, 1209 deletions
diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 3b4b97e6..8adee0f5 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -197,6 +197,8 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { vars["nested_name"] = descriptor_->name(); vars["classname"] = classname_; vars["constexpr"] = options_.proto_h ? "constexpr " : ""; + vars["{"] = ""; + vars["}"] = ""; printer->Print(vars, "typedef $classname$ $nested_name$;\n"); for (int j = 0; j < descriptor_->value_count(); j++) { @@ -204,22 +206,27 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { vars["deprecated_attr"] = descriptor_->value(j)->options().deprecated() ? "GOOGLE_PROTOBUF_DEPRECATED_ATTR " : ""; printer->Print(vars, - "$deprecated_attr$static $constexpr$const $nested_name$ $tag$ =\n" + "$deprecated_attr$static $constexpr$const $nested_name$ ${$$tag$$}$ =\n" " $classname$_$tag$;\n"); + printer->Annotate("{", "}", descriptor_->value(j)); } printer->Print(vars, "static inline bool $nested_name$_IsValid(int value) {\n" " return $classname$_IsValid(value);\n" "}\n" - "static const $nested_name$ $nested_name$_MIN =\n" - " $classname$_$nested_name$_MIN;\n" - "static const $nested_name$ $nested_name$_MAX =\n" + "static const $nested_name$ ${$$nested_name$_MIN$}$ =\n" + " $classname$_$nested_name$_MIN;\n"); + printer->Annotate("{", "}", descriptor_); + printer->Print(vars, + "static const $nested_name$ ${$$nested_name$_MAX$}$ =\n" " $classname$_$nested_name$_MAX;\n"); + printer->Annotate("{", "}", descriptor_); if (generate_array_size_) { printer->Print(vars, - "static const int $nested_name$_ARRAYSIZE =\n" + "static const int ${$$nested_name$_ARRAYSIZE$}$ =\n" " $classname$_$nested_name$_ARRAYSIZE;\n"); + printer->Annotate("{", "}", descriptor_); } if (HasDescriptorMethods(descriptor_->file(), options_)) { @@ -242,27 +249,10 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { } } -void EnumGenerator::GenerateDescriptorInitializer(io::Printer* printer) { - std::map<string, string> vars; - vars["index"] = SimpleItoa(descriptor_->index()); - vars["index_in_metadata"] = SimpleItoa(index_in_metadata_); - - if (descriptor_->containing_type() == NULL) { - printer->Print(vars, - "file_level_enum_descriptors[$index_in_metadata$] = " - "file->enum_type($index$);\n"); - } else { - vars["parent"] = ClassName(descriptor_->containing_type(), false); - printer->Print(vars, - "file_level_enum_descriptors[$index_in_metadata$] = " - "$parent$_descriptor->enum_type($index$);\n"); - } -} - -void EnumGenerator::GenerateMethods(io::Printer* printer) { +void EnumGenerator::GenerateMethods(int idx, io::Printer* printer) { std::map<string, string> vars; vars["classname"] = classname_; - vars["index_in_metadata"] = SimpleItoa(index_in_metadata_); + vars["index_in_metadata"] = SimpleItoa(idx); vars["constexpr"] = options_.proto_h ? "constexpr " : ""; vars["file_namespace"] = FileLevelNamespace(descriptor_->file()->name()); diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.h b/src/google/protobuf/compiler/cpp/cpp_enum.h index 0b568c57..0d2488a9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum.h @@ -86,13 +86,10 @@ class EnumGenerator { // Source file stuff. - // Generate code that initializes the global variable storing the enum's - // descriptor. - void GenerateDescriptorInitializer(io::Printer* printer); - // Generate non-inline methods related to the enum, such as IsValidValue(). - // Goes in the .cc file. - void GenerateMethods(io::Printer* printer); + // Goes in the .cc file. EnumDescriptors are stored in an array, idx is + // the index in this array that corresponds with this enum. + void GenerateMethods(int idx, io::Printer* printer); private: const EnumDescriptor* descriptor_; @@ -101,8 +98,6 @@ class EnumGenerator { // whether to generate the *_ARRAYSIZE constant. const bool generate_array_size_; - int index_in_metadata_; - friend class FileGenerator; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(EnumGenerator); }; diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index 08a635fa..008490ed 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -232,9 +232,8 @@ GenerateSwappingCode(io::Printer* printer) const { void EnumOneofFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { - printer->Print( - variables_, - "_$classname$_default_instance_.$name$_ = $default$;\n"); + printer->Print(variables_, + "$ns$::_$classname$_default_instance_.$name$_ = $default$;\n"); } // =================================================================== diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.cc b/src/google/protobuf/compiler/cpp/cpp_extension.cc index 6b1673b2..0a4e0bb9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_extension.cc +++ b/src/google/protobuf/compiler/cpp/cpp_extension.cc @@ -167,46 +167,6 @@ void ExtensionGenerator::GenerateDefinition(io::Printer* printer) { " $name$($constant_name$, $default$);\n"); } -void ExtensionGenerator::GenerateRegistration(io::Printer* printer) { - std::map<string, string> vars; - vars["extendee" ] = ExtendeeClassName(descriptor_); - vars["number" ] = SimpleItoa(descriptor_->number()); - vars["field_type" ] = SimpleItoa(static_cast<int>(descriptor_->type())); - vars["is_repeated"] = descriptor_->is_repeated() ? "true" : "false"; - vars["is_packed" ] = (descriptor_->is_repeated() && - descriptor_->options().packed()) - ? "true" : "false"; - - switch (descriptor_->cpp_type()) { - case FieldDescriptor::CPPTYPE_ENUM: - printer->Print( - vars, - "::google::protobuf::internal::ExtensionSet::RegisterEnumExtension(\n" - " $extendee$::internal_default_instance(),\n" - " $number$, $field_type$, $is_repeated$, $is_packed$,\n"); - printer->Print( - " &$type$_IsValid);\n", - "type", ClassName(descriptor_->enum_type(), true)); - break; - case FieldDescriptor::CPPTYPE_MESSAGE: - printer->Print( - vars, - "::google::protobuf::internal::ExtensionSet::RegisterMessageExtension(\n" - " $extendee$::internal_default_instance(),\n" - " $number$, $field_type$, $is_repeated$, $is_packed$,\n"); - printer->Print(" $type$::internal_default_instance());\n", "type", - ClassName(descriptor_->message_type(), true)); - break; - default: - printer->Print( - vars, - "::google::protobuf::internal::ExtensionSet::RegisterExtension(\n" - " $extendee$::internal_default_instance(),\n" - " $number$, $field_type$, $is_repeated$, $is_packed$);\n"); - break; - } -} - } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.h b/src/google/protobuf/compiler/cpp/cpp_extension.h index 1c1caf1f..30236d71 100644 --- a/src/google/protobuf/compiler/cpp/cpp_extension.h +++ b/src/google/protobuf/compiler/cpp/cpp_extension.h @@ -67,9 +67,6 @@ class ExtensionGenerator { // Source file stuff. void GenerateDefinition(io::Printer* printer); - // Generate code to register the extension. - void GenerateRegistration(io::Printer* printer); - private: const FieldDescriptor* descriptor_; string type_traits_; diff --git a/src/google/protobuf/compiler/cpp/cpp_field.cc b/src/google/protobuf/compiler/cpp/cpp_field.cc index dce9617c..f8e11855 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_field.cc @@ -61,6 +61,7 @@ using internal::WireFormat; void SetCommonFieldVariables(const FieldDescriptor* descriptor, std::map<string, string>* variables, const Options& options) { + (*variables)["ns"] = Namespace(descriptor); (*variables)["name"] = FieldName(descriptor); (*variables)["index"] = SimpleItoa(descriptor->index()); (*variables)["number"] = SimpleItoa(descriptor->number()); diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index a066a6a7..0e74f215 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -55,70 +55,24 @@ namespace google { namespace protobuf { namespace compiler { namespace cpp { -namespace { -// The list of names that are defined as macros on some platforms. We need to -// #undef them for the generated code to compile. -const char* kMacroNames[] = {"major", "minor"}; - -bool IsMacroName(const string& name) { - // Just do a linear search as the number of elements is very small. - for (int i = 0; i < GOOGLE_ARRAYSIZE(kMacroNames); ++i) { - if (name == kMacroNames[i]) return true; - } - return false; -} - -void CollectMacroNames(const Descriptor* message, std::vector<string>* names) { - for (int i = 0; i < message->field_count(); ++i) { - const FieldDescriptor* field = message->field(i); - if (IsMacroName(field->name())) { - names->push_back(field->name()); - } - } - for (int i = 0; i < message->nested_type_count(); ++i) { - CollectMacroNames(message->nested_type(i), names); - } -} - -void CollectMacroNames(const FileDescriptor* file, std::vector<string>* names) { - // 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() != "google/protobuf/compiler/plugin.proto") { - return; - } - for (int i = 0; i < file->message_type_count(); ++i) { - CollectMacroNames(file->message_type(i), names); - } -} - - -} // namespace - -// =================================================================== FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) : file_(file), options_(options), scc_analyzer_(options), - message_generators_owner_( - new google::protobuf::scoped_ptr<MessageGenerator>[file->message_type_count()]), enum_generators_owner_( new google::protobuf::scoped_ptr<EnumGenerator>[file->enum_type_count()]), service_generators_owner_( new google::protobuf::scoped_ptr<ServiceGenerator>[file->service_count()]), extension_generators_owner_( new google::protobuf::scoped_ptr<ExtensionGenerator>[file->extension_count()]) { - - for (int i = 0; i < file->message_type_count(); i++) { - message_generators_owner_[i].reset( - new MessageGenerator(file->message_type(i), options, &scc_analyzer_)); - message_generators_owner_[i]->Flatten(&message_generators_); - } - - for (int i = 0; i < message_generators_.size(); i++) { - message_generators_[i]->AddGenerators(&enum_generators_, - &extension_generators_); + std::vector<const Descriptor*> msgs = FlattenMessagesInFile(file); + for (int i = 0; i < msgs.size(); i++) { + // Deleted in destructor + MessageGenerator* msg_gen = + new MessageGenerator(msgs[i], i, options, &scc_analyzer_); + message_generators_.push_back(msg_gen); + msg_gen->AddGenerators(&enum_generators_, &extension_generators_); } for (int i = 0; i < file->enum_type_count(); i++) { @@ -126,9 +80,6 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) new EnumGenerator(file->enum_type(i), options)); enum_generators_.push_back(enum_generators_owner_[i].get()); } - for (int i = 0; i < enum_generators_.size(); i++) { - enum_generators_[i]->index_in_metadata_ = i; - } for (int i = 0; i < file->service_count(); i++) { service_generators_owner_[i].reset( @@ -147,14 +98,36 @@ FileGenerator::FileGenerator(const FileDescriptor* file, const Options& options) extension_generators_.push_back(extension_generators_owner_[i].get()); } + package_parts_ = Split(file_->package(), ".", true); } -FileGenerator::~FileGenerator() {} +FileGenerator::~FileGenerator() { + for (int i = 0; i < message_generators_.size(); i++) { + delete message_generators_[i]; + } +} void FileGenerator::GenerateMacroUndefs(io::Printer* printer) { + // 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() != "google/protobuf/compiler/plugin.proto") { + return; + } std::vector<string> names_to_undef; - CollectMacroNames(file_, &names_to_undef); + std::vector<const FieldDescriptor*> fields; + ListAllFields(file_, &fields); + for (int i = 0; i < fields.size(); i++) { + const string& name = fields[i]->name(); + static const char* kMacroNames[] = {"major", "minor"}; + for (int i = 0; i < GOOGLE_ARRAYSIZE(kMacroNames); ++i) { + if (name == kMacroNames[i]) { + names_to_undef.push_back(name); + break; + } + } + } for (int i = 0; i < names_to_undef.size(); ++i) { printer->Print( "#ifdef $name$\n" @@ -170,43 +143,41 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { GenerateMacroUndefs(printer); - GenerateForwardDeclarations(printer); - - // Open namespace. - GenerateNamespaceOpeners(printer); - GenerateGlobalStateFunctionDeclarations(printer); - printer->Print("\n"); + GenerateForwardDeclarations(printer); - GenerateEnumDefinitions(printer); + { + NamespaceOpener ns(Namespace(file_), printer); - printer->Print(kThickSeparator); - printer->Print("\n"); + printer->Print("\n"); - GenerateMessageDefinitions(printer); + GenerateEnumDefinitions(printer); - printer->Print("\n"); - printer->Print(kThickSeparator); - printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); - GenerateServiceDefinitions(printer); + GenerateMessageDefinitions(printer); - GenerateExtensionIdentifiers(printer); + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); - printer->Print("\n"); - printer->Print(kThickSeparator); - printer->Print("\n"); + GenerateServiceDefinitions(printer); - GenerateInlineFunctionDefinitions(printer); + GenerateExtensionIdentifiers(printer); - printer->Print( - "\n" - "// @@protoc_insertion_point(namespace_scope)\n" - "\n"); + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + + GenerateInlineFunctionDefinitions(printer); - // Close up namespace. - GenerateNamespaceClosers(printer); + printer->Print( + "\n" + "// @@protoc_insertion_point(namespace_scope)\n" + "\n"); + } // We need to specialize some templates in the ::google::protobuf namespace: GenerateProto2NamespaceEnumSpecializations(printer); @@ -269,15 +240,12 @@ void FileGenerator::GeneratePBHeader(io::Printer* printer, // TODO(gerbens) remove this. printer->Print( "// @@protoc_insertion_point(includes)\n"); - - // Open namespace. - GenerateNamespaceOpeners(printer); - printer->Print( - "\n" - "// @@protoc_insertion_point(namespace_scope)\n"); - // Close up namespace. - GenerateNamespaceClosers(printer); - + { + NamespaceOpener ns(Namespace(file_), printer); + printer->Print( + "\n" + "// @@protoc_insertion_point(namespace_scope)\n"); + } printer->Print( "\n" "// @@protoc_insertion_point(global_scope)\n" @@ -287,7 +255,7 @@ void FileGenerator::GeneratePBHeader(io::Printer* printer, GenerateBottomHeaderGuard(printer, filename_identifier); } -void FileGenerator::GenerateSource(io::Printer* printer) { +void FileGenerator::GenerateSourceIncludes(io::Printer* printer) { const bool use_system_include = IsWellKnownMessage(file_); string header = StripProto(file_->name()) + (options_.proto_h ? ".proto.h" : ".pb.h"); @@ -295,9 +263,6 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" "// source: $filename$\n" "\n" - // The generated code calls accessors that might be deprecated. We don't - // want the compiler to warn in generated code. - "#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION\n" "#include $left$$header$$right$\n" "\n" "#include <algorithm>\n" // for swap() @@ -340,101 +305,129 @@ void FileGenerator::GenerateSource(io::Printer* printer) { printer->Print( "// @@protoc_insertion_point(includes)\n"); +} + +void FileGenerator::GenerateSourceDefaultInstance(int idx, + io::Printer* printer) { + printer->Print( + "class $classname$DefaultTypeInternal {\n" + " public:\n" + " ::google::protobuf::internal::ExplicitlyConstructed<$classname$>\n" + " _instance;\n", + "classname", message_generators_[idx]->classname_); + printer->Indent(); + message_generators_[idx]->GenerateExtraDefaultFields(printer); + printer->Outdent(); + printer->Print("} _$classname$_default_instance_;\n", "classname", + message_generators_[idx]->classname_); +} - GenerateNamespaceOpeners(printer); +namespace { - for (int i = 0; i < message_generators_.size(); i++) { - string parent; - if (IsMapEntryMessage(message_generators_[i]->descriptor_)) { - parent = ClassName(message_generators_[i]->descriptor_->containing_type(), - false) + - "::"; +// Generates weak symbol declarations for types that are to be considered weakly +// referenced. +void GenerateWeakDeclarations( + const FileDescriptor* file, const Options& options, + SCCAnalyzer* scc_analyzer, + io::Printer* printer) { + std::vector<const FieldDescriptor*> fields; + ListAllFields(file, &fields); + + // To ensure determinism and minimize the number of namespace statements, + // we output the forward declarations sorted on namespace and type / function + // name. + std::set<std::pair<string, string> > messages; + std::set<std::pair<string, string> > inits; + for (int i = 0; i < fields.size(); ++i) { + const FieldDescriptor* field = fields[i]; + bool is_weak = IsImplicitWeakField(field, options); + if (is_weak) { + const Descriptor* msg = field->message_type(); + string flns = FileLevelNamespace(msg); + string repr = ClassName(scc_analyzer->GetSCC(msg)->GetRepresentative()); + inits.insert(std::make_pair(flns, "InitDefaults" + repr)); + inits.insert(std::make_pair(flns, "AddDescriptors")); + messages.insert(std::make_pair(Namespace(msg), ClassName(msg))); } - printer->Print( - "class $classname$DefaultTypeInternal {\n" - "public:\n" - " ::google::protobuf::internal::ExplicitlyConstructed<$parent$$classname$>\n" - " _instance;\n", - "parent", parent, "classname", message_generators_[i]->classname_); - printer->Indent(); - message_generators_[i]->GenerateExtraDefaultFields(printer); - printer->Outdent(); - printer->Print( - "} _$classname$_default_instance_;\n", - "classname", message_generators_[i]->classname_); } - for (int i = 0; i < enum_generators_.size(); i++) { - enum_generators_[i]->index_in_metadata_ = i; + if (messages.empty()) { + return; } - if (HasGenericServices(file_, options_)) { - for (int i = 0; i < service_generators_.size(); i++) { - service_generators_[i]->index_in_metadata_ = i; - } + + printer->Print("\n"); + NamespaceOpener ns(printer); + for (std::set<std::pair<string, string> >::const_iterator it = + messages.begin(); + it != messages.end(); ++it) { + ns.ChangeTo(it->first); + printer->Print( + "extern __attribute__((weak)) $classname$DefaultTypeInternal " + "_$classname$_default_instance_;\n", + "classname", it->second); + } + for (std::set<std::pair<string, string> >::const_iterator it = inits.begin(); + it != inits.end(); ++it) { + ns.ChangeTo(it->first); + printer->Print("void $name$() __attribute__((weak));\n", + "name", it->second); } +} - printer->Print( - "\n" - "namespace $file_namespace$ {\n" - "\n", - "file_namespace", FileLevelNamespace(file_->name())); +} // namespace - if (HasDescriptorMethods(file_, options_)) { - printer->Print( - "\n" - "namespace {\n" - "\n"); +void FileGenerator::GenerateSourceForMessage(int idx, io::Printer* printer) { + GenerateSourceIncludes(printer); + GenerateWeakDeclarations(file_, options_, &scc_analyzer_, printer); - if (!message_generators_.empty()) { - printer->Print("::google::protobuf::Metadata file_level_metadata[$size$];\n", - "size", SimpleItoa(message_generators_.size())); - } - if (!enum_generators_.empty()) { - printer->Print( - "const ::google::protobuf::EnumDescriptor* " - "file_level_enum_descriptors[$size$];\n", - "size", SimpleItoa(enum_generators_.size())); - } - if (HasGenericServices(file_, options_) && file_->service_count() > 0) { - printer->Print( - "const ::google::protobuf::ServiceDescriptor* " - "file_level_service_descriptors[$size$];\n", - "size", SimpleItoa(file_->service_count())); - } + { // package namespace + NamespaceOpener ns(Namespace(file_), printer); + + // Define default instances + GenerateSourceDefaultInstance(idx, printer); + + // Generate classes. + printer->Print("\n"); + message_generators_[idx]->GenerateClassMethods(printer); printer->Print( - "\n" - "} // namespace\n" - "\n"); - } + "\n" + "// @@protoc_insertion_point(namespace_scope)\n"); + } // end package namespace - // Define our externally-visible BuildDescriptors() function. (For the lite - // library, all this does is initialize default instances.) - GenerateBuildDescriptors(printer); + if (IsSCCRepresentative(message_generators_[idx]->descriptor_)) { + NamespaceOpener ns(FileLevelNamespace(file_), printer); + GenerateInitForSCC(GetSCC(message_generators_[idx]->descriptor_), printer); + } printer->Print( "\n" - "} // namespace $file_namespace$\n" - "\n", - "file_namespace", FileLevelNamespace(file_->name())); + "// @@protoc_insertion_point(global_scope)\n"); +} + +void FileGenerator::GenerateGlobalSource(io::Printer* printer) { + GenerateSourceIncludes(printer); + GenerateWeakDeclarations(file_, options_, &scc_analyzer_, printer); + + // TODO(gerbens) Generate tables here + + // 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_)) { + NamespaceOpener ns(FileLevelNamespace(file_), printer); + GenerateReflectionInitializationCode(printer); + } + + NamespaceOpener ns(Namespace(file_), printer); // Generate enums. for (int i = 0; i < enum_generators_.size(); i++) { - enum_generators_[i]->GenerateMethods(printer); + enum_generators_[i]->GenerateMethods(i, printer); } - // Generate classes. - for (int i = 0; i < message_generators_.size(); i++) { - printer->Print("\n"); - printer->Print(kThickSeparator); - printer->Print("\n"); - message_generators_[i]->GenerateClassMethods(printer); - - printer->Print("#if PROTOBUF_INLINE_NOT_IN_HEADERS\n"); - // Generate class inline methods. - message_generators_[i]->GenerateInlineMethods(printer, - /* is_inline = */ false); - printer->Print("#endif // PROTOBUF_INLINE_NOT_IN_HEADERS\n"); + // Define extensions. + for (int i = 0; i < extension_generators_.size(); i++) { + extension_generators_[i]->GenerateDefinition(printer); } if (HasGenericServices(file_, options_)) { @@ -446,18 +439,76 @@ void FileGenerator::GenerateSource(io::Printer* printer) { service_generators_[i]->GenerateImplementation(printer); } } +} - // Define extensions. - for (int i = 0; i < extension_generators_.size(); i++) { - extension_generators_[i]->GenerateDefinition(printer); +void FileGenerator::GenerateSource(io::Printer* printer) { + GenerateSourceIncludes(printer); + GenerateWeakDeclarations(file_, options_, &scc_analyzer_, printer); + + { + NamespaceOpener ns(Namespace(file_), printer); + + // Define default instances + for (int i = 0; i < message_generators_.size(); i++) { + GenerateSourceDefaultInstance(i, printer); + if (UsingImplicitWeakFields(file_, options_)) { + printer->Print("void $classname$_Reference() {}\n", "classname", + message_generators_[i]->classname_); + } + } } - printer->Print( - "\n" - "// @@protoc_insertion_point(namespace_scope)\n"); + { + NamespaceOpener ns(FileLevelNamespace(file_), printer); + // Define the initialization code to initialize the default instances. + // This code doesn't use a global constructor. + GenerateInitializationCode(printer); - GenerateNamespaceClosers(printer); + // 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(printer); + } + } + + + { + NamespaceOpener ns(Namespace(file_), printer); + + // Actually implement the protos + + // Generate enums. + for (int i = 0; i < enum_generators_.size(); i++) { + enum_generators_[i]->GenerateMethods(i, printer); + } + // Generate classes. + for (int i = 0; i < message_generators_.size(); i++) { + printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + message_generators_[i]->GenerateClassMethods(printer); + } + + if (HasGenericServices(file_, options_)) { + // Generate services. + for (int i = 0; i < service_generators_.size(); i++) { + if (i == 0) printer->Print("\n"); + printer->Print(kThickSeparator); + printer->Print("\n"); + service_generators_[i]->GenerateImplementation(printer); + } + } + + // Define extensions. + for (int i = 0; i < extension_generators_.size(); i++) { + extension_generators_[i]->GenerateDefinition(printer); + } + + printer->Print( + "\n" + "// @@protoc_insertion_point(namespace_scope)\n"); + } printer->Print( "\n" "// @@protoc_insertion_point(global_scope)\n"); @@ -512,6 +563,10 @@ class FileGenerator::ForwardDeclarations { options.dllexport_decl.empty() ? "" : options.dllexport_decl + " ", "classname", it->first); + if (options.lite_implicit_weak_fields) { + printer->Print("void $classname$_Reference();\n", + "classname", it->first); + } } for (std::map<string, ForwardDeclarations *>::const_iterator it = namespaces_.begin(), @@ -532,13 +587,11 @@ class FileGenerator::ForwardDeclarations { std::map<string, const EnumDescriptor*> enums_; }; -void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { +void FileGenerator::GenerateReflectionInitializationCode(io::Printer* printer) { // AddDescriptors() is a file-level procedure which adds the encoded // FileDescriptorProto for this .proto file to the global DescriptorPool for // generated files (DescriptorPool::generated_pool()). It ordinarily runs at - // static initialization time, but is not used at all in LITE_RUNTIME mode - // except when extensions are used. This procedure also constructs default - // instances and registers extensions. + // static initialization time, but is not used at all in LITE_RUNTIME mode. // // Its sibling, AssignDescriptors(), actually pulls the compiled // FileDescriptor from the DescriptorPool and uses it to populate all of @@ -547,172 +600,83 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { // anyone calls descriptor() or GetReflection() on one of the types defined // in the file. - // In optimize_for = LITE_RUNTIME mode, we don't generate AssignDescriptors() - // and we only use AddDescriptors() to allocate default instances. - - // TODO(ckennelly): Gate this with the same options flag to enable - // table-driven parsing. - - printer->Print("PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::ParseTableField\n" - " const TableStruct::entries[] " - "GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); - printer->Indent(); - - std::vector<size_t> entries; - size_t count = 0; - for (int i = 0; i < message_generators_.size(); i++) { - size_t value = message_generators_[i]->GenerateParseOffsets(printer); - entries.push_back(value); - count += value; - } - - // We need these arrays to exist, and MSVC does not like empty arrays. - if (count == 0) { - printer->Print("{0, 0, 0, ::google::protobuf::internal::kInvalidMask, 0, 0},\n"); - } - - printer->Outdent(); - printer->Print( - "};\n" - "\n" - "PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::AuxillaryParseTableField\n" - " const TableStruct::aux[] " - "GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); - printer->Indent(); - - std::vector<size_t> aux_entries; - count = 0; - for (int i = 0; i < message_generators_.size(); i++) { - size_t value = message_generators_[i]->GenerateParseAuxTable(printer); - aux_entries.push_back(value); - count += value; - } - - if (count == 0) { - printer->Print("::google::protobuf::internal::AuxillaryParseTableField(),\n"); + if (!message_generators_.empty()) { + printer->Print("::google::protobuf::Metadata file_level_metadata[$size$];\n", "size", + SimpleItoa(message_generators_.size())); } - - printer->Outdent(); - printer->Print( - "};\n" - "PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::ParseTable const\n" - " TableStruct::schema[] " - "GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); - printer->Indent(); - - size_t offset = 0; - size_t aux_offset = 0; - for (int i = 0; i < message_generators_.size(); i++) { - message_generators_[i]->GenerateParseTable(printer, offset, aux_offset); - offset += entries[i]; - aux_offset += aux_entries[i]; + if (!enum_generators_.empty()) { + printer->Print( + "const ::google::protobuf::EnumDescriptor* " + "file_level_enum_descriptors[$size$];\n", + "size", SimpleItoa(enum_generators_.size())); } - - if (message_generators_.empty()) { - printer->Print("{ NULL, NULL, 0, -1, -1, -1, -1, NULL, false },\n"); + if (HasGenericServices(file_, options_) && file_->service_count() > 0) { + printer->Print( + "const ::google::protobuf::ServiceDescriptor* " + "file_level_service_descriptors[$size$];\n", + "size", SimpleItoa(file_->service_count())); } - printer->Outdent(); - printer->Print( - "};\n" - "\n"); - - if (!message_generators_.empty() && options_.table_driven_serialization) { + if (!message_generators_.empty()) { printer->Print( - "const ::google::protobuf::internal::FieldMetadata TableStruct::field_metadata[] " - "= {\n"); + "\n" + "const ::google::protobuf::uint32 TableStruct::offsets[] " + "GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); printer->Indent(); - std::vector<int> field_metadata_offsets; - int idx = 0; + std::vector<std::pair<size_t, size_t> > pairs; + pairs.reserve(message_generators_.size()); for (int i = 0; i < message_generators_.size(); i++) { - field_metadata_offsets.push_back(idx); - idx += message_generators_[i]->GenerateFieldMetadata(printer); + pairs.push_back(message_generators_[i]->GenerateOffsets(printer)); } - field_metadata_offsets.push_back(idx); printer->Outdent(); printer->Print( "};\n" - "const ::google::protobuf::internal::SerializationTable " - "TableStruct::serialization_table[] = {\n"); + "static const ::google::protobuf::internal::MigrationSchema schemas[] " + "GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); + printer->Indent(); + { + int offset = 0; + for (int i = 0; i < message_generators_.size(); i++) { + message_generators_[i]->GenerateSchema(printer, offset, + pairs[i].second); + offset += pairs[i].first; + } + } + printer->Outdent(); + printer->Print( + "};\n" + "\nstatic " + "::google::protobuf::Message const * const file_default_instances[] = {\n"); printer->Indent(); - // We rely on the order we layout the tables to match the order we - // calculate them with FlattenMessagesInFile, so we check here that - // these match exactly. - std::vector<const Descriptor*> calculated_order = - FlattenMessagesInFile(file_); - GOOGLE_CHECK_EQ(calculated_order.size(), message_generators_.size()); for (int i = 0; i < message_generators_.size(); i++) { - GOOGLE_CHECK_EQ(calculated_order[i], message_generators_[i]->descriptor_); + const Descriptor* descriptor = message_generators_[i]->descriptor_; printer->Print( - "{$num_fields$, TableStruct::field_metadata + $index$},\n", - "classname", message_generators_[i]->classname_, "num_fields", - SimpleItoa(field_metadata_offsets[i + 1] - field_metadata_offsets[i]), - "index", SimpleItoa(field_metadata_offsets[i])); + "reinterpret_cast<const " + "::google::protobuf::Message*>(&$ns$::_$classname$_default_instance_),\n", + "classname", ClassName(descriptor), "ns", Namespace(descriptor)); } printer->Outdent(); printer->Print( "};\n" "\n"); + } else { + // we still need these symbols to exist + printer->Print( + // MSVC doesn't like empty arrays, so we add a dummy. + "const ::google::protobuf::uint32 TableStruct::offsets[1] = {};\n" + "static const ::google::protobuf::internal::MigrationSchema* schemas = NULL;\n" + "static const ::google::protobuf::Message* const* " + "file_default_instances = NULL;\n" + "\n"); } - if (HasDescriptorMethods(file_, options_)) { - if (!message_generators_.empty()) { - printer->Print("const ::google::protobuf::uint32 TableStruct::offsets[] " - "GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); - printer->Indent(); - std::vector<std::pair<size_t, size_t> > pairs; - for (int i = 0; i < message_generators_.size(); i++) { - pairs.push_back(message_generators_[i]->GenerateOffsets(printer)); - } - printer->Outdent(); - printer->Print( - "};\n" - "static const ::google::protobuf::internal::MigrationSchema schemas[] " - "GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); - printer->Indent(); - { - int offset = 0; - for (int i = 0; i < message_generators_.size(); i++) { - message_generators_[i]->GenerateSchema(printer, offset, - pairs[i].second); - offset += pairs[i].first; - } - } - printer->Outdent(); - printer->Print( - "};\n" - "\nstatic " - "::google::protobuf::Message const * const file_default_instances[] = {\n"); - printer->Indent(); - for (int i = 0; i < message_generators_.size(); i++) { - const Descriptor* descriptor = message_generators_[i]->descriptor_; - printer->Print( - "reinterpret_cast<const " - "::google::protobuf::Message*>(&_$classname$_default_instance_),\n", - "classname", ClassName(descriptor, false)); - } - printer->Outdent(); - printer->Print( - "};\n" - "\n"); - } else { - // we still need these symbols to exist - printer->Print( - // MSVC doesn't like empty arrays, so we add a dummy. - "const ::google::protobuf::uint32 TableStruct::offsets[1] = {};\n" - "static const ::google::protobuf::internal::MigrationSchema* schemas = NULL;\n" - "static const ::google::protobuf::Message* const* " - "file_default_instances = NULL;\n"); - } - // --------------------------------------------------------------- + // --------------------------------------------------------------- - // protobuf_AssignDescriptorsOnce(): The first time it is called, calls - // AssignDescriptors(). All later times, waits for the first call to - // complete and then returns. - string message_factory = "NULL"; + // protobuf_AssignDescriptorsOnce(): The first time it is called, calls + // AssignDescriptors(). All later times, waits for the first call to + // complete and then returns. + string message_factory = "NULL"; printer->Print( - "namespace {\n" - "\n" "void protobuf_AssignDescriptors() {\n" // Make sure the file has found its way into the pool. If a descriptor // is requested *during* static init then AddDescriptors() may not have @@ -733,20 +697,6 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { ? "file_level_service_descriptors" : "NULL", "factory", message_factory); - // TODO(gerbens) have the compiler include the schemas for map types - // so that this can go away, and we can potentially use table driven - // serialization for map types as well. - for (int i = 0; i < message_generators_.size(); i++) { - if (!IsMapEntryMessage(message_generators_[i]->descriptor_)) continue; - printer->Print( - "file_level_metadata[$index$].reflection = " - "$parent$::$classname$::CreateReflection(file_level_metadata[$index$]" - ".descriptor, _$classname$_default_instance_._instance.get_mutable());\n", - "index", SimpleItoa(i), "parent", - ClassName(message_generators_[i]->descriptor_->containing_type(), - false), - "classname", ClassName(message_generators_[i]->descriptor_, false)); - } printer->Print( "}\n" "\n" @@ -769,7 +719,8 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { // protobuf_AssignDescriptorsOnce, because that is called from all the // GetMetadata member methods. printer->Print( - "void protobuf_RegisterTypes(const ::std::string&) GOOGLE_ATTRIBUTE_COLD;\n" + "void protobuf_RegisterTypes(const ::std::string&) " + "GOOGLE_PROTOBUF_ATTRIBUTE_COLD;\n" "void protobuf_RegisterTypes(const ::std::string&) {\n" " protobuf_AssignDescriptorsOnce();\n"); printer->Indent(); @@ -783,63 +734,15 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { printer->Outdent(); printer->Print( - "}\n" - "\n" - "} // namespace\n"); - } - - // Now generate the InitDefaultsImpl() function. - printer->Print( - "void TableStruct::InitDefaultsImpl() {\n" - " GOOGLE_PROTOBUF_VERIFY_VERSION;\n\n" - // Force initialization of primitive values we depend on. - " ::google::protobuf::internal::InitProtobufDefaults();\n"); - - printer->Indent(); - - // Call the InitDefaults() methods for all of our dependencies, to make - // sure they get added first. - for (int i = 0; i < file_->dependency_count(); i++) { - const FileDescriptor* dependency = file_->dependency(i); - // Print the namespace prefix for the dependency. - string file_namespace = QualifiedFileLevelSymbol( - dependency->package(), FileLevelNamespace(dependency->name())); - // Call its AddDescriptors function. - printer->Print("$file_namespace$::InitDefaults();\n", "file_namespace", - file_namespace); - } - - // Allocate and initialize default instances. This can't be done lazily - // since default instances are returned by simple accessors and are used with - // extensions. Speaking of which, we also register extensions at this time. - for (int i = 0; i < message_generators_.size(); i++) { - message_generators_[i]->GenerateDefaultInstanceAllocator(printer); - } - for (int i = 0; i < extension_generators_.size(); i++) { - extension_generators_[i]->GenerateRegistration(printer); - } - for (int i = 0; i < message_generators_.size(); i++) { - message_generators_[i]->GenerateDefaultInstanceInitializer(printer); - } - printer->Outdent(); - printer->Print( - "}\n" - "\n" - "void InitDefaults() {\n" - " static GOOGLE_PROTOBUF_DECLARE_ONCE(once);\n" - " ::google::protobuf::GoogleOnceInit(&once, &TableStruct::InitDefaultsImpl);\n" - "}\n"); - - // ----------------------------------------------------------------- + "}\n" + "\n"); - // Now generate the AddDescriptors() function. - printer->Print( - "namespace {\n" - "void AddDescriptorsImpl() {\n" - " InitDefaults();\n"); + // Now generate the AddDescriptors() function. + printer->Print( + "void AddDescriptorsImpl() {\n" + " InitDefaults();\n"); + printer->Indent(); - printer->Indent(); - if (HasDescriptorMethods(file_, options_)) { // Embed the descriptor. We simply serialize the entire // FileDescriptorProto // and embed it as a string literal, which is parsed and built into real @@ -850,10 +753,11 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { file_proto.SerializeToString(&file_data); printer->Print("static const char descriptor[] " - "GOOGLE_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); + "GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) " + "= {\n"); printer->Indent(); - if (file_data.size() > 66535) { + if (file_data.size() > 65535) { // Workaround for MSVC: "Error C1091: compiler limit: string exceeds 65535 // bytes in length". Declare a static array of characters rather than use // a string literal. Only write 25 bytes per line. @@ -887,31 +791,27 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { "::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(\n" " \"$filename$\", &protobuf_RegisterTypes);\n", "filename", file_->name()); - } // Call the AddDescriptors() methods for all of our dependencies, to make // sure they get added first. for (int i = 0; i < file_->dependency_count(); i++) { const FileDescriptor* dependency = file_->dependency(i); // Print the namespace prefix for the dependency. - string file_namespace = QualifiedFileLevelSymbol( - dependency->package(), FileLevelNamespace(dependency->name())); + string file_namespace = FileLevelNamespace(dependency); // Call its AddDescriptors function. - printer->Print("$file_namespace$::AddDescriptors();\n", "file_namespace", + printer->Print("::$file_namespace$::AddDescriptors();\n", "file_namespace", file_namespace); } printer->Outdent(); printer->Print( "}\n" - "} // anonymous namespace\n" "\n" "void AddDescriptors() {\n" " static GOOGLE_PROTOBUF_DECLARE_ONCE(once);\n" " ::google::protobuf::GoogleOnceInit(&once, &AddDescriptorsImpl);\n" "}\n"); - if (StaticInitializersForced(file_, options_)) { printer->Print( "// Force AddDescriptors() to be called at dynamic initialization " "time.\n" @@ -920,24 +820,213 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { " AddDescriptors();\n" " }\n" "} static_descriptor_initializer;\n"); - } } -void FileGenerator::GenerateNamespaceOpeners(io::Printer* printer) { - if (package_parts_.size() > 0) printer->Print("\n"); +void FileGenerator::GenerateInitForSCC(const SCC* scc, io::Printer* printer) { + const string scc_name = ClassName(scc->GetRepresentative()); + printer->Print( + "void InitDefaults$scc_name$Impl() {\n" + " GOOGLE_PROTOBUF_VERIFY_VERSION;\n\n" + // Force initialization of primitive values we depend on. + " ::google::protobuf::internal::InitProtobufDefaults();\n", + "scc_name", scc_name); - for (int i = 0; i < package_parts_.size(); i++) { - printer->Print("namespace $part$ {\n", - "part", package_parts_[i]); + printer->Indent(); + + // Call the InitDefaults() methods for all of our dependencies, to make + // sure they get added first. + for (int i = 0; i < scc->children.size(); i++) { + const SCC* child_scc = scc->children[i]; + const FileDescriptor* dependency = child_scc->GetRepresentative()->file(); + // Print the namespace prefix for the dependency. + string file_namespace = FileLevelNamespace(dependency); + std::map<string, string> variables; + variables["file_namespace"] = file_namespace; + variables["scc_name"] = ClassName(child_scc->GetRepresentative(), false); + bool using_weak_fields = UsingImplicitWeakFields(file_, options_); + if (using_weak_fields) { + // We're building for lite with implicit weak fields, so we need to handle + // the possibility that this InitDefaults function is not linked into the + // binary. Some of these might actually be guaranteed to be non-null since + // we might have a strong reference to the dependency (via a required + // field, for example), but it's simplest to just assume that any of them + // could be null. + printer->Print( + variables, + "if (&$file_namespace$::InitDefaults$scc_name$ != NULL) {\n" + " $file_namespace$::InitDefaults$scc_name$();\n" + "}\n"); + } else { + printer->Print(variables, + "$file_namespace$::InitDefaults$scc_name$();\n"); + } + } + + // First construct all the necessary default instances. + for (int i = 0; i < message_generators_.size(); i++) { + if (scc_analyzer_.GetSCC(message_generators_[i]->descriptor_) != scc) { + continue; + } + // TODO(gerbens) This requires this function to be friend. Remove + // the need for this. + message_generators_[i]->GenerateFieldDefaultInstances(printer); + printer->Print( + "{\n" + " void* ptr = &$ns$::_$classname$_default_instance_;\n" + " new (ptr) $ns$::$classname$();\n", + "ns", Namespace(message_generators_[i]->descriptor_), + "classname", ClassName(message_generators_[i]->descriptor_)); + if (!IsMapEntryMessage(message_generators_[i]->descriptor_)) { + printer->Print( + " ::google::protobuf::internal::OnShutdownDestroyMessage(ptr);\n"); + } + printer->Print("}\n"); } + + // TODO(gerbens) make default instances be the same as normal instances. + // Default instances differ from normal instances because they have cross + // linked message fields. + for (int i = 0; i < message_generators_.size(); i++) { + if (scc_analyzer_.GetSCC(message_generators_[i]->descriptor_) != scc) { + continue; + } + printer->Print("$classname$::InitAsDefaultInstance();\n", "classname", + QualifiedClassName(message_generators_[i]->descriptor_)); + } + printer->Outdent(); + printer->Print("}\n\n"); + printer->Print( + "void InitDefaults$scc_name$() {\n" + " static GOOGLE_PROTOBUF_DECLARE_ONCE(once);\n" + " ::google::protobuf::GoogleOnceInit(&once, " + "&InitDefaults$scc_name$Impl);\n" + "}\n\n", + "scc_name", scc_name); } -void FileGenerator::GenerateNamespaceClosers(io::Printer* printer) { - if (package_parts_.size() > 0) printer->Print("\n"); +void FileGenerator::GenerateInitializationCode(io::Printer* printer) { + // Messages depend on the existence of a default instance, which has to + // initialized properly. The default instances are allocated in the data + // segment, but we can't quite allocate the type directly. The destructors + // cannot run at program exit as this could lead to segfaults in a threaded + // environment. Hence these instances must be inplace constructed at first + // use. + + if (options_.table_driven_parsing) { + // TODO(ckennelly): Gate this with the same options flag to enable + // table-driven parsing. + printer->Print( + "PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::ParseTableField\n" + " const TableStruct::entries[] " + "GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); + printer->Indent(); - for (int i = package_parts_.size() - 1; i >= 0; i--) { - printer->Print("} // namespace $part$\n", - "part", package_parts_[i]); + std::vector<size_t> entries; + size_t count = 0; + for (int i = 0; i < message_generators_.size(); i++) { + size_t value = message_generators_[i]->GenerateParseOffsets(printer); + entries.push_back(value); + count += value; + } + + // We need these arrays to exist, and MSVC does not like empty arrays. + if (count == 0) { + printer->Print("{0, 0, 0, ::google::protobuf::internal::kInvalidMask, 0, 0},\n"); + } + + printer->Outdent(); + printer->Print( + "};\n" + "\n" + "PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::AuxillaryParseTableField\n" + " const TableStruct::aux[] " + "GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); + printer->Indent(); + + std::vector<size_t> aux_entries; + count = 0; + for (int i = 0; i < message_generators_.size(); i++) { + size_t value = message_generators_[i]->GenerateParseAuxTable(printer); + aux_entries.push_back(value); + count += value; + } + + if (count == 0) { + printer->Print("::google::protobuf::internal::AuxillaryParseTableField(),\n"); + } + + printer->Outdent(); + printer->Print( + "};\n" + "PROTOBUF_CONSTEXPR_VAR ::google::protobuf::internal::ParseTable const\n" + " TableStruct::schema[] " + "GOOGLE_PROTOBUF_ATTRIBUTE_SECTION_VARIABLE(protodesc_cold) = {\n"); + printer->Indent(); + + size_t offset = 0; + size_t aux_offset = 0; + for (int i = 0; i < message_generators_.size(); i++) { + message_generators_[i]->GenerateParseTable(printer, offset, aux_offset); + offset += entries[i]; + aux_offset += aux_entries[i]; + } + + if (message_generators_.empty()) { + printer->Print("{ NULL, NULL, 0, -1, -1, false },\n"); + } + + printer->Outdent(); + printer->Print( + "};\n" + "\n"); + } + + if (!message_generators_.empty() && options_.table_driven_serialization) { + printer->Print( + "const ::google::protobuf::internal::FieldMetadata TableStruct::field_metadata[] " + "= {\n"); + printer->Indent(); + std::vector<int> field_metadata_offsets; + int idx = 0; + for (int i = 0; i < message_generators_.size(); i++) { + field_metadata_offsets.push_back(idx); + idx += message_generators_[i]->GenerateFieldMetadata(printer); + } + field_metadata_offsets.push_back(idx); + printer->Outdent(); + printer->Print( + "};\n" + "const ::google::protobuf::internal::SerializationTable " + "TableStruct::serialization_table[] = {\n"); + printer->Indent(); + // We rely on the order we layout the tables to match the order we + // calculate them with FlattenMessagesInFile, so we check here that + // these match exactly. + std::vector<const Descriptor*> calculated_order = + FlattenMessagesInFile(file_); + GOOGLE_CHECK_EQ(calculated_order.size(), message_generators_.size()); + for (int i = 0; i < message_generators_.size(); i++) { + GOOGLE_CHECK_EQ(calculated_order[i], message_generators_[i]->descriptor_); + printer->Print( + "{$num_fields$, TableStruct::field_metadata + $index$},\n", + "classname", message_generators_[i]->classname_, "num_fields", + SimpleItoa(field_metadata_offsets[i + 1] - field_metadata_offsets[i]), + "index", SimpleItoa(field_metadata_offsets[i])); + } + printer->Outdent(); + printer->Print( + "};\n" + "\n"); + } + + // ----------------------------------------------------------------- + // All functionality that need private access. + + // Now generate the InitDefaults for each SCC. + for (int i = 0; i < message_generators_.size(); i++) { + if (IsSCCRepresentative(message_generators_[i]->descriptor_)) { + GenerateInitForSCC(GetSCC(message_generators_[i]->descriptor_), printer); + } } } @@ -985,6 +1074,9 @@ void FileGenerator::GenerateBottomHeaderGuard( } void FileGenerator::GenerateLibraryIncludes(io::Printer* printer) { + if (UsingImplicitWeakFields(file_, options_)) { + printer->Print("#include <google/protobuf/implicit_weak_message.h>\n"); + } printer->Print( "#include <google/protobuf/stubs/common.h>\n" @@ -1114,31 +1206,56 @@ void FileGenerator::GenerateDependencyIncludes(io::Printer* printer) { void FileGenerator::GenerateGlobalStateFunctionDeclarations( io::Printer* printer) { - // Forward-declare the AddDescriptors, AssignDescriptors - // functions, so that we can declare them to be friends of each class. +// Forward-declare the AddDescriptors, InitDefaults because these are called +// by .pb.cc files depending on this file. printer->Print( "\n" "namespace $file_namespace$ {\n" - "// Internal implementation detail -- do not call these.\n" + "// Internal implementation detail -- do not use these members.\n" "struct $dllexport_decl$TableStruct {\n" + // These tables describe how to serialize and parse messages. Used + // for table driven code. " static const ::google::protobuf::internal::ParseTableField entries[];\n" " static const ::google::protobuf::internal::AuxillaryParseTableField aux[];\n" - " static const ::google::protobuf::internal::ParseTable schema[];\n" - " static const ::google::protobuf::uint32 offsets[];\n" + " static const ::google::protobuf::internal::ParseTable schema[$num$];\n" " static const ::google::protobuf::internal::FieldMetadata field_metadata[];\n" " static const ::google::protobuf::internal::SerializationTable " "serialization_table[];\n" - // The following function(s) need to be able to access private members of - // the messages defined in the file. So we make them static members. - // This is the internal implementation of InitDefaults. It should only - // be called by InitDefaults which makes sure it will be called only once. - " static void InitDefaultsImpl();\n" - "};\n" - "void $dllexport_decl$AddDescriptors();\n" - "void $dllexport_decl$InitDefaults();\n" - "} // namespace $file_namespace$\n", - "file_namespace", FileLevelNamespace(file_->name()), "dllexport_decl", + " static const ::google::protobuf::uint32 offsets[];\n" + "};\n", + "file_namespace", FileLevelNamespace(file_), "dllexport_decl", + options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " ", + "num", SimpleItoa(std::max(size_t(1), message_generators_.size()))); + if (HasDescriptorMethods(file_, options_)) { + printer->Print( + "void $dllexport_decl$AddDescriptors();\n", "dllexport_decl", + options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); + } + for (int i = 0; i < message_generators_.size(); i++) { + if (!IsSCCRepresentative(message_generators_[i]->descriptor_)) continue; + string scc_name = ClassName(message_generators_[i]->descriptor_); + // TODO(gerbens) Remove the Impl from header. This is solely because + // it currently still needs to be a friend of the protos. + printer->Print( + "void $dllexport_decl$InitDefaults$scc_name$Impl();\n" + "void $dllexport_decl$InitDefaults$scc_name$();\n", + "scc_name", scc_name, "dllexport_decl", + options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); + } + // TODO(gerbens) This is for proto1 interoperability. Remove when proto1 + // is gone. + printer->Print( + "inline void $dllexport_decl$InitDefaults() {\n", "dllexport_decl", options_.dllexport_decl.empty() ? "" : options_.dllexport_decl + " "); + for (int i = 0; i < message_generators_.size(); i++) { + if (!IsSCCRepresentative(message_generators_[i]->descriptor_)) continue; + string scc_name = ClassName(message_generators_[i]->descriptor_); + printer->Print(" InitDefaults$scc_name$();\n", "scc_name", scc_name); + } + printer->Print("}\n"); + printer->Print( + "} // namespace $file_namespace$\n", + "file_namespace", FileLevelNamespace(file_)); } void FileGenerator::GenerateMessageDefinitions(io::Printer* printer) { @@ -1187,41 +1304,6 @@ void FileGenerator::GenerateExtensionIdentifiers(io::Printer* printer) { } void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* printer) { - // An aside about inline functions in .proto.h mode: - // - // The PROTOBUF_INLINE_NOT_IN_HEADERS symbol controls conditionally - // moving much of the inline functions to the .pb.cc file, which can be a - // significant performance benefit for compilation time, at the expense - // of non-inline function calls. - // - // However, in .proto.h mode, the definition of the internal dependent - // base class must remain in the header, and can never be out-lined. The - // dependent base class also needs access to has-bit manipuation - // functions, so the has-bit functions must be unconditionally inlined in - // proto_h mode. - // - // This gives us three flavors of functions: - // - // 1. Functions on the message not used by the internal dependent base - // class: in .proto.h mode, only some functions are defined on the - // message class; others are defined on the dependent base class. - // These are guarded and can be out-lined. These are generated by - // GenerateInlineMethods, and include has_* bit functions in - // non-proto_h mode. - // - // 2. Functions on the internal dependent base class: these functions - // are dependent on a template parameter, so they always need to - // remain in the header. - // - // 3. Functions on the message that are used by the dependent base: the - // dependent base class down casts itself to the message - // implementation class to access these functions (the has_* bit - // manipulation functions). Unlike #1, these functions must - // unconditionally remain in the header. These are emitted by - // GenerateDependentInlineMethods, even though they are not actually - // dependent. - - printer->Print("#if !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); // TODO(gerbens) remove pragmas when gcc is no longer used. Current version // of gcc fires a bogus error when compiled with strict-aliasing. printer->Print( @@ -1242,7 +1324,6 @@ void FileGenerator::GenerateInlineFunctionDefinitions(io::Printer* printer) { "#ifdef __GNUC__\n" " #pragma GCC diagnostic pop\n" "#endif // __GNUC__\n"); - printer->Print("#endif // !PROTOBUF_INLINE_NOT_IN_HEADERS\n"); for (int i = 0; i < message_generators_.size(); i++) { if (i > 0) { diff --git a/src/google/protobuf/compiler/cpp/cpp_file.h b/src/google/protobuf/compiler/cpp/cpp_file.h index e10fe2f3..7e61cbad 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.h +++ b/src/google/protobuf/compiler/cpp/cpp_file.h @@ -35,10 +35,12 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ #define GOOGLE_PROTOBUF_COMPILER_CPP_FILE_H__ +#include <algorithm> #include <memory> #ifndef _SHARED_PTR_H #include <google/protobuf/stubs/shared_ptr.h> #endif +#include <set> #include <string> #include <vector> #include <google/protobuf/stubs/common.h> @@ -82,16 +84,21 @@ class FileGenerator { const string& info_path); void GenerateSource(io::Printer* printer); + int NumMessages() const { return message_generators_.size(); } + // Similar to GenerateSource but generates only one message + void GenerateSourceForMessage(int idx, io::Printer* printer); + void GenerateGlobalSource(io::Printer* printer); + private: // Internal type used by GenerateForwardDeclarations (defined in file.cc). class ForwardDeclarations; - // Generate the BuildDescriptors() procedure, which builds all descriptors - // for types defined in the file. - void GenerateBuildDescriptors(io::Printer* printer); + void GenerateSourceIncludes(io::Printer* printer); + void GenerateSourceDefaultInstance(int idx, io::Printer* printer); - void GenerateNamespaceOpeners(io::Printer* printer); - void GenerateNamespaceClosers(io::Printer* printer); + void GenerateInitForSCC(const SCC* scc, io::Printer* printer); + void GenerateInitializationCode(io::Printer* printer); + void GenerateReflectionInitializationCode(io::Printer* printer); // For other imports, generates their forward-declarations. void GenerateForwardDeclarations(io::Printer* printer); @@ -143,11 +150,23 @@ class FileGenerator { // a breaking change so we prefer the #undef approach. void GenerateMacroUndefs(io::Printer* printer); + bool IsSCCRepresentative(const Descriptor* d) { + return GetSCCRepresentative(d) == d; + } + const Descriptor* GetSCCRepresentative(const Descriptor* d) { + return GetSCC(d)->GetRepresentative(); + } + const SCC* GetSCC(const Descriptor* d) { + return scc_analyzer_.GetSCC(d); + } + + const FileDescriptor* file_; const Options options_; SCCAnalyzer scc_analyzer_; + // Contains the post-order walk of all the messages (and child messages) in // this file. If you need a pre-order walk just reverse iterate. std::vector<MessageGenerator*> message_generators_; @@ -155,10 +174,8 @@ class FileGenerator { std::vector<ServiceGenerator*> service_generators_; std::vector<ExtensionGenerator*> extension_generators_; - // These members are just for owning (and thus proper deleting). Some of the - // message_ and enum_generators above are owned by child messages. - google::protobuf::scoped_array<google::protobuf::scoped_ptr<MessageGenerator> > - message_generators_owner_; + // These members are just for owning (and thus proper deleting). + // Nested (enum/extension)_generators are owned by child messages. google::protobuf::scoped_array<google::protobuf::scoped_ptr<EnumGenerator> > enum_generators_owner_; google::protobuf::scoped_array<google::protobuf::scoped_ptr<ServiceGenerator> > service_generators_owner_; diff --git a/src/google/protobuf/compiler/cpp/cpp_generator.cc b/src/google/protobuf/compiler/cpp/cpp_generator.cc index 68abd0ef..e01e5dca 100644 --- a/src/google/protobuf/compiler/cpp/cpp_generator.cc +++ b/src/google/protobuf/compiler/cpp/cpp_generator.cc @@ -46,6 +46,7 @@ #include <google/protobuf/io/printer.h> #include <google/protobuf/io/zero_copy_stream.h> #include <google/protobuf/descriptor.pb.h> +#include <google/protobuf/stubs/strutil.h> namespace google { namespace protobuf { @@ -84,7 +85,7 @@ bool CppGenerator::Generate(const FileDescriptor* file, // __declspec(dllimport) depending on what is being compiled. // Options file_options; - + bool split_source = false; for (int i = 0; i < options.size(); i++) { if (options[i].first == "dllexport_decl") { file_options.dllexport_decl = options[i].second; @@ -98,10 +99,14 @@ bool CppGenerator::Generate(const FileDescriptor* file, file_options.annotation_guard_name = options[i].second; } else if (options[i].first == "lite") { file_options.enforce_lite = true; + } else if (options[i].first == "lite_implicit_weak_fields") { + file_options.lite_implicit_weak_fields = true; } else if (options[i].first == "table_driven_parsing") { file_options.table_driven_parsing = true; } else if (options[i].first == "table_driven_serialization") { file_options.table_driven_serialization = true; + } else if (options[i].first == "split_source") { + split_source = true; } else { *error = "Unknown generator option: " + options[i].first; return false; @@ -135,14 +140,13 @@ bool CppGenerator::Generate(const FileDescriptor* file, } } - basename.append(".pb"); { google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( - generator_context->Open(basename + ".h")); + generator_context->Open(basename + ".pb.h")); GeneratedCodeInfo annotations; io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector( &annotations); - string info_path = basename + ".h.meta"; + string info_path = basename + ".pb.h.meta"; io::Printer printer(output.get(), '$', file_options.annotate_headers ? &annotation_collector : NULL); @@ -156,9 +160,24 @@ bool CppGenerator::Generate(const FileDescriptor* file, } // Generate cc file. - { + if (split_source) { + { + // This is the global .cc file, containing enum/services/tables/reflection + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( + generator_context->Open(basename + ".pb.cc")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateGlobalSource(&printer); + } + for (int i = 0; i < file_generator.NumMessages(); i++) { + // TODO(gerbens) Agree on naming scheme. + google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( + generator_context->Open(basename + "." + SimpleItoa(i) + ".cc")); + io::Printer printer(output.get(), '$'); + file_generator.GenerateSourceForMessage(i, &printer); + } + } else { google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( - generator_context->Open(basename + ".cc")); + generator_context->Open(basename + ".pb.cc")); io::Printer printer(output.get(), '$'); file_generator.GenerateSource(&printer); } diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index 00959796..4aa77d06 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -32,10 +32,11 @@ // Based on original Protocol Buffers design by // Sanjay Ghemawat, Jeff Dean, and others. +#include <google/protobuf/stubs/hash.h> #include <limits> #include <map> +#include <queue> #include <vector> -#include <google/protobuf/stubs/hash.h> #include <google/protobuf/stubs/logging.h> #include <google/protobuf/stubs/common.h> @@ -105,6 +106,30 @@ bool HasExtension(const Descriptor* descriptor) { return false; } +// Encode [0..63] as 'A'-'Z', 'a'-'z', '0'-'9', '_' +char Base63Char(int value) { + GOOGLE_CHECK_GE(value, 0); + if (value < 26) return 'A' + value; + value -= 26; + if (value < 26) return 'a' + value; + value -= 26; + if (value < 10) return '0' + value; + GOOGLE_CHECK_EQ(value, 10); + return '_'; +} + +// Given a c identifier has 63 legal characters we can't implement base64 +// encoding. So we return the k least significant "digits" in base 63. +template <typename I> +string Base63(I n, int k) { + string res; + while (k-- > 0) { + res += Base63Char(static_cast<int>(n % 63)); + n /= 63; + } + return res; +} + } // namespace string UnderscoresToCamelCase(const string& input, bool cap_next_letter) { @@ -137,44 +162,63 @@ const char kThickSeparator[] = const char kThinSeparator[] = "// -------------------------------------------------------------------\n"; -string ClassName(const Descriptor* descriptor, bool qualified) { - - // Find "outer", the descriptor of the top-level message in which - // "descriptor" is embedded. - const Descriptor* outer = descriptor; - while (outer->containing_type() != NULL) outer = outer->containing_type(); - - const string& outer_name = outer->full_name(); - string inner_name = descriptor->full_name().substr(outer_name.size()); - - if (qualified) { - return "::" + DotsToColons(outer_name) + DotsToUnderscores(inner_name); - } else { - return outer->name() + DotsToUnderscores(inner_name); +bool CanInitializeByZeroing(const FieldDescriptor* field) { + if (field->is_repeated() || field->is_extension()) return false; + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_ENUM: + return field->default_value_enum()->number() == 0; + case FieldDescriptor::CPPTYPE_INT32: + return field->default_value_int32() == 0; + case FieldDescriptor::CPPTYPE_INT64: + return field->default_value_int64() == 0; + case FieldDescriptor::CPPTYPE_UINT32: + return field->default_value_uint32() == 0; + case FieldDescriptor::CPPTYPE_UINT64: + return field->default_value_uint64() == 0; + case FieldDescriptor::CPPTYPE_FLOAT: + return field->default_value_float() == 0; + case FieldDescriptor::CPPTYPE_DOUBLE: + return field->default_value_double() == 0; + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() == false; + default: + return false; } } -string ClassName(const EnumDescriptor* enum_descriptor, bool qualified) { +string ClassName(const Descriptor* descriptor) { + const Descriptor* parent = descriptor->containing_type(); + string res; + if (parent) res += ClassName(parent) + "_"; + res += descriptor->name(); + if (IsMapEntryMessage(descriptor)) res += "_DoNotUse"; + return res; +} + +string ClassName(const EnumDescriptor* enum_descriptor) { if (enum_descriptor->containing_type() == NULL) { - if (qualified) { - return "::" + DotsToColons(enum_descriptor->full_name()); - } else { - return enum_descriptor->name(); - } + return enum_descriptor->name(); } else { - string result = ClassName(enum_descriptor->containing_type(), qualified); - result += '_'; - result += enum_descriptor->name(); - return result; + return ClassName(enum_descriptor->containing_type()) + "_" + + enum_descriptor->name(); } } +string Namespace(const string& package) { + if (package.empty()) return ""; + return "::" + DotsToColons(package); +} + string DefaultInstanceName(const Descriptor* descriptor) { string prefix = descriptor->file()->package().empty() ? "" : "::"; return prefix + DotsToColons(descriptor->file()->package()) + "::_" + ClassName(descriptor, false) + "_default_instance_"; } +string ReferenceFunctionName(const Descriptor* descriptor) { + return QualifiedClassName(descriptor) + "_Reference"; +} + string DependentBaseClassTemplateName(const Descriptor* descriptor) { return ClassName(descriptor, false) + "_InternalBase"; } @@ -210,6 +254,30 @@ string EnumValueName(const EnumValueDescriptor* enum_value) { return result; } +int EstimateAlignmentSize(const FieldDescriptor* field) { + if (field == NULL) return 0; + if (field->is_repeated()) return 8; + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_BOOL: + return 1; + + case FieldDescriptor::CPPTYPE_INT32: + case FieldDescriptor::CPPTYPE_UINT32: + case FieldDescriptor::CPPTYPE_ENUM: + case FieldDescriptor::CPPTYPE_FLOAT: + return 4; + + case FieldDescriptor::CPPTYPE_INT64: + case FieldDescriptor::CPPTYPE_UINT64: + case FieldDescriptor::CPPTYPE_DOUBLE: + case FieldDescriptor::CPPTYPE_STRING: + case FieldDescriptor::CPPTYPE_MESSAGE: + return 8; + } + GOOGLE_LOG(FATAL) << "Can't get here."; + return -1; // Make compiler happy. +} + string FieldConstantName(const FieldDescriptor *field) { string field_name = UnderscoresToCamelCase(field->name(), true); string result = "k" + field_name + "FieldNumber"; @@ -484,19 +552,6 @@ string SafeFunctionName(const Descriptor* descriptor, return function_name; } -bool StaticInitializersForced(const FileDescriptor* file, - const Options& options) { - if (HasDescriptorMethods(file, options) || file->extension_count() > 0) { - return true; - } - for (int i = 0; i < file->message_type_count(); ++i) { - if (HasExtension(file->message_type(i))) { - return true; - } - } - return false; -} - static bool HasMapFields(const Descriptor* descriptor) { for (int i = 0; i < descriptor->field_count(); ++i) { @@ -672,13 +727,11 @@ void Flatten(const Descriptor* descriptor, } // namespace -std::vector<const Descriptor*> FlattenMessagesInFile( - const FileDescriptor* file) { - std::vector<const Descriptor*> result; +void FlattenMessagesInFile(const FileDescriptor* file, + std::vector<const Descriptor*>* result) { for (int i = 0; i < file->message_type_count(); i++) { - Flatten(file->message_type(i), &result); + Flatten(file->message_type(i), result); } - return result; } bool HasWeakFields(const Descriptor* descriptor) { @@ -689,6 +742,27 @@ bool HasWeakFields(const FileDescriptor* file) { return false; } +bool UsingImplicitWeakFields(const FileDescriptor* file, + const Options& options) { + return options.lite_implicit_weak_fields && + GetOptimizeFor(file, options) == FileOptions::LITE_RUNTIME; +} + + +bool IsImplicitWeakField(const FieldDescriptor* field, const Options& options) { + return UsingImplicitWeakFields(field->file(), options) && + field->type() == FieldDescriptor::TYPE_MESSAGE && + !field->is_required() && !field->is_repeated() && !field->is_map() && + field->containing_oneof() == NULL && + field->message_type()->file() != field->file(); +} + +struct CompareDescriptors { + bool operator()(const Descriptor* a, const Descriptor* b) { + return a->full_name() < b->full_name(); + } +}; + SCCAnalyzer::NodeData SCCAnalyzer::DFS(const Descriptor* descriptor) { // Must not have visited already. GOOGLE_DCHECK_EQ(cache_.count(descriptor), 0); @@ -728,10 +802,33 @@ SCCAnalyzer::NodeData SCCAnalyzer::DFS(const Descriptor* descriptor) { if (scc_desc == descriptor) break; } + + // The order of descriptors is random and depends how this SCC was + // discovered. In-order to ensure maximum stability we sort it by name. + std::sort(scc->descriptors.begin(), scc->descriptors.end(), + CompareDescriptors()); + AddChildren(scc); } return result; } +void SCCAnalyzer::AddChildren(SCC* scc) { + std::set<const SCC*> seen; + for (int i = 0; i < scc->descriptors.size(); i++) { + const Descriptor* descriptor = scc->descriptors[i]; + for (int j = 0; j < descriptor->field_count(); j++) { + const Descriptor* child_msg = descriptor->field(j)->message_type(); + if (child_msg) { + const SCC* child = GetSCC(child_msg); + if (child == scc) continue; + if (seen.insert(child).second) { + scc->children.push_back(child); + } + } + } + } +} + MessageAnalysis SCCAnalyzer::GetSCCAnalysis(const SCC* scc) { if (analysis_cache_.count(scc)) return analysis_cache_[scc]; MessageAnalysis result = MessageAnalysis(); @@ -784,6 +881,46 @@ MessageAnalysis SCCAnalyzer::GetSCCAnalysis(const SCC* scc) { return analysis_cache_[scc] = result; } +void ListAllFields(const Descriptor* d, + std::vector<const FieldDescriptor*>* fields) { + // Collect sub messages + for (int i = 0; i < d->nested_type_count(); i++) { + ListAllFields(d->nested_type(i), fields); + } + // Collect message level extensions. + for (int i = 0; i < d->extension_count(); i++) { + fields->push_back(d->extension(i)); + } + // Add types of fields necessary + for (int i = 0; i < d->field_count(); i++) { + fields->push_back(d->field(i)); + } +} + +void ListAllFields(const FileDescriptor* d, + std::vector<const FieldDescriptor*>* fields) { + // Collect file level message. + for (int i = 0; i < d->message_type_count(); i++) { + ListAllFields(d->message_type(i), fields); + } + // Collect message level extensions. + for (int i = 0; i < d->extension_count(); i++) { + fields->push_back(d->extension(i)); + } +} + +void ListAllTypesForServices(const FileDescriptor* fd, + std::vector<const Descriptor*>* types) { + for (int i = 0; i < fd->service_count(); i++) { + const ServiceDescriptor* sd = fd->service(i); + for (int j = 0; j < sd->method_count(); j++) { + const MethodDescriptor* method = sd->method(j); + types->push_back(method->input_type()); + types->push_back(method->output_type()); + } + } +} + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index 6ae68591..550438dd 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -38,16 +38,13 @@ #include <map> #include <string> #include <google/protobuf/compiler/cpp/cpp_options.h> +#include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/descriptor.h> +#include <google/protobuf/stubs/strutil.h> namespace google { namespace protobuf { - -namespace io { -class Printer; -} - namespace compiler { namespace cpp { @@ -56,6 +53,31 @@ namespace cpp { extern const char kThickSeparator[]; extern const char kThinSeparator[]; +// Name space of the proto file. This namespace is such that the string +// "<namespace>::some_name" is the correct fully qualified namespace. +// This means if the package is empty the namespace is "", and otherwise +// the namespace is "::foo::bar::...::baz" without trailing semi-colons. +string Namespace(const string& package); +inline string Namespace(const FileDescriptor* d) { + return Namespace(d->package()); +} +template <typename Desc> +string Namespace(const Desc* d) { + return Namespace(d->file()); +} + +// Returns true if it's safe to reset "field" to zero. +bool CanInitializeByZeroing(const FieldDescriptor* field); + +string ClassName(const Descriptor* descriptor); +string ClassName(const EnumDescriptor* enum_descriptor); +template <typename Desc> +string QualifiedClassName(const Desc* d) { + return Namespace(d) + "::" + ClassName(d); +} + +// DEPRECATED just use ClassName or QualifiedClassName, a boolean is very +// unreadable at the callsite. // Returns the non-nested type name for the given type. If "qualified" is // true, prefix the type with the full namespace. For example, if you had: // package foo.bar; @@ -64,12 +86,22 @@ extern const char kThinSeparator[]; // ::foo::bar::Baz_Qux // While the non-qualified version would be: // Baz_Qux -string ClassName(const Descriptor* descriptor, bool qualified); -string ClassName(const EnumDescriptor* enum_descriptor, bool qualified); +inline string ClassName(const Descriptor* descriptor, bool qualified) { + return qualified ? QualifiedClassName(descriptor) : ClassName(descriptor); +} + +inline string ClassName(const EnumDescriptor* descriptor, bool qualified) { + return qualified ? QualifiedClassName(descriptor) : ClassName(descriptor); +} // Fully qualified name of the default_instance of this message. string DefaultInstanceName(const Descriptor* descriptor); +// Returns the name of a no-op function that we can call to introduce a linker +// dependency on the given message type. This is used to implement implicit weak +// fields. +string ReferenceFunctionName(const Descriptor* descriptor); + // Name of the CRTP class template (for use with proto_h). // This is a class name, like "ProtoName_InternalBase". string DependentBaseClassTemplateName(const Descriptor* descriptor); @@ -92,6 +124,12 @@ string FieldName(const FieldDescriptor* field); // Get the sanitized name that should be used for the given enum in C++ code. string EnumValueName(const EnumValueDescriptor* enum_value); +// Returns an estimate of the compiler's alignment for the field. This +// can't guarantee to be correct because the generated code could be compiled on +// different systems with different alignment rules. The estimates below assume +// 64-bit pointers. +int EstimateAlignmentSize(const FieldDescriptor* field); + // Get the unqualified name that should be used for a field's field // number constant. string FieldConstantName(const FieldDescriptor *field); @@ -150,6 +188,12 @@ string FilenameIdentifier(const string& filename); // For each .proto file generates a unique namespace. In this namespace global // definitions are put to prevent collisions. string FileLevelNamespace(const string& filename); +inline string FileLevelNamespace(const FileDescriptor* file) { + return FileLevelNamespace(file->name()); +} +inline string FileLevelNamespace(const Descriptor* d) { + return FileLevelNamespace(d->file()); +} // Return the qualified C++ name for a file level symbol. string QualifiedFileLevelSymbol(const string& package, const string& name); @@ -225,10 +269,6 @@ inline bool HasFastArraySerialization(const FileDescriptor* file, return GetOptimizeFor(file, options) == FileOptions::SPEED; } -// Returns whether we have to generate code with static initializers. -bool StaticInitializersForced(const FileDescriptor* file, - const Options& options); - inline bool IsMapEntryMessage(const Descriptor* descriptor) { return descriptor->options().map_entry(); @@ -289,12 +329,25 @@ inline ::google::protobuf::FileOptions_OptimizeMode GetOptimizeFor( } // This orders the messages in a .pb.cc as it's outputted by file.cc -std::vector<const Descriptor*> FlattenMessagesInFile( - const FileDescriptor* file); +void FlattenMessagesInFile(const FileDescriptor* file, + std::vector<const Descriptor*>* result); +inline std::vector<const Descriptor*> FlattenMessagesInFile( + const FileDescriptor* file) { + std::vector<const Descriptor*> result; + FlattenMessagesInFile(file, &result); + return result; +} bool HasWeakFields(const Descriptor* desc); bool HasWeakFields(const FileDescriptor* desc); +// Indicates whether we should use implicit weak fields for this file. +bool UsingImplicitWeakFields(const FileDescriptor* file, + const Options& options); + +// Indicates whether to treat this field as implicitly weak. +bool IsImplicitWeakField(const FieldDescriptor* field, const Options& options); + // Returns true if the "required" restriction check should be ignored for the // given field. inline static bool ShouldIgnoreRequiredFieldCheck(const FieldDescriptor* field, @@ -302,8 +355,46 @@ inline static bool ShouldIgnoreRequiredFieldCheck(const FieldDescriptor* field, return false; } +class LIBPROTOC_EXPORT NamespaceOpener { + public: + explicit NamespaceOpener(io::Printer* printer) : printer_(printer) {} + NamespaceOpener(const string& name, io::Printer* printer) + : printer_(printer) { + ChangeTo(name); + } + ~NamespaceOpener() { ChangeTo(""); } + + void ChangeTo(const string& name) { + std::vector<string> new_stack_ = + Split(name, "::", true); + int len = std::min(name_stack_.size(), new_stack_.size()); + int common_idx = 0; + while (common_idx < len) { + if (name_stack_[common_idx] != new_stack_[common_idx]) break; + common_idx++; + } + for (int i = name_stack_.size() - 1; i >= common_idx; i--) { + printer_->Print("} // namespace $ns$\n", "ns", name_stack_[i]); + } + name_stack_.swap(new_stack_); + for (int i = common_idx; i < name_stack_.size(); i++) { + printer_->Print("namespace $ns$ {\n", "ns", name_stack_[i]); + } + } + + private: + io::Printer* printer_; + std::vector<string> name_stack_; +}; + +// Description of each strongly connected component. Note that the order +// of both the descriptors in this SCC and the order of children is +// deterministic. struct SCC { std::vector<const Descriptor*> descriptors; + std::vector<const SCC*> children; + + const Descriptor* GetRepresentative() const { return descriptors[0]; } }; struct MessageAnalysis { @@ -357,8 +448,16 @@ class LIBPROTOC_EXPORT SCCAnalyzer { // Tarjan's Strongly Connected Components algo NodeData DFS(const Descriptor* descriptor); + + // Add the SCC's that are children of this SCC to its children. + void AddChildren(SCC* scc); }; +void ListAllFields(const FileDescriptor* d, + std::vector<const FieldDescriptor*>* fields); +void ListAllTypesForServices(const FileDescriptor* fd, + std::vector<const Descriptor*>* types); + } // namespace cpp } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/cpp/cpp_map_field.cc b/src/google/protobuf/compiler/cpp/cpp_map_field.cc index da33d29b..d06a1d39 100644 --- a/src/google/protobuf/compiler/cpp/cpp_map_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_map_field.cc @@ -114,50 +114,13 @@ MapFieldGenerator::~MapFieldGenerator() {} void MapFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { - if (HasDescriptorMethods(descriptor_->file(), options_)) { - printer->Print( - variables_, - "public:\n" - "class $map_classname$ : public " - "::google::protobuf::internal::MapEntry<$map_classname$, \n" - " $key_cpp$, $val_cpp$,\n" - " $key_wire_type$,\n" - " $val_wire_type$,\n" - " $default_enum_value$ > {\n" - "public:\n" - " typedef ::google::protobuf::internal::MapEntry<$map_classname$, \n" - " $key_cpp$, $val_cpp$,\n" - " $key_wire_type$,\n" - " $val_wire_type$,\n" - " $default_enum_value$ > SuperType;\n" - " $map_classname$();\n" - " $map_classname$(::google::protobuf::Arena* arena);\n" - " void MergeFrom(const ::google::protobuf::Message& other) PROTOBUF_FINAL;\n" - " void MergeFrom(const $map_classname$& other);\n" - " static const Message* internal_default_instance() { return " - "reinterpret_cast<const " - "Message*>(&_$map_classname$_default_instance_); }\n" - " ::google::protobuf::Metadata GetMetadata() const;\n" - "};\n"); - } else { - printer->Print(variables_, - "public:\n" - "typedef ::google::protobuf::internal::MapEntryLite<\n" - " $key_cpp$, $val_cpp$,\n" - " $key_wire_type$,\n" - " $val_wire_type$,\n" - " $default_enum_value$ >\n" - " $map_classname$;\n"); - } printer->Print(variables_, - "private:\n" "::google::protobuf::internal::MapField$lite$<\n" " $map_classname$,\n" " $key_cpp$, $val_cpp$,\n" " $key_wire_type$,\n" " $val_wire_type$,\n" - " $default_enum_value$ > $name$_;\n" - "private:\n"); + " $default_enum_value$ > $name$_;\n"); } void MapFieldGenerator:: @@ -262,7 +225,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { printer->Print(variables_, " unknown_fields_stream.WriteVarint32($tag$u);\n" " unknown_fields_stream.WriteVarint32(\n" - " static_cast<google::protobuf::uint32>(data.size()));\n" + " static_cast< ::google::protobuf::uint32>(data.size()));\n" " unknown_fields_stream.WriteString(data);\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 63ebb3c5..cf9c1233 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -48,6 +48,7 @@ #include <google/protobuf/compiler/cpp/cpp_extension.h> #include <google/protobuf/compiler/cpp/cpp_field.h> #include <google/protobuf/compiler/cpp/cpp_helpers.h> +#include <google/protobuf/compiler/cpp/cpp_padding_optimizer.h> #include <google/protobuf/io/coded_stream.h> #include <google/protobuf/io/printer.h> #include <google/protobuf/descriptor.pb.h> @@ -109,106 +110,6 @@ struct ExtensionRangeSorter { } }; -// This returns an estimate of the compiler's alignment for the field. This -// can't guarantee to be correct because the generated code could be compiled on -// different systems with different alignment rules. The estimates below assume -// 64-bit pointers. -int EstimateAlignmentSize(const FieldDescriptor* field) { - if (field == NULL) return 0; - if (field->is_repeated()) return 8; - switch (field->cpp_type()) { - case FieldDescriptor::CPPTYPE_BOOL: - return 1; - - case FieldDescriptor::CPPTYPE_INT32: - case FieldDescriptor::CPPTYPE_UINT32: - case FieldDescriptor::CPPTYPE_ENUM: - case FieldDescriptor::CPPTYPE_FLOAT: - return 4; - - case FieldDescriptor::CPPTYPE_INT64: - case FieldDescriptor::CPPTYPE_UINT64: - case FieldDescriptor::CPPTYPE_DOUBLE: - case FieldDescriptor::CPPTYPE_STRING: - case FieldDescriptor::CPPTYPE_MESSAGE: - return 8; - } - GOOGLE_LOG(FATAL) << "Can't get here."; - return -1; // Make compiler happy. -} - -// FieldGroup is just a helper for OptimizePadding below. It holds a vector of -// fields that are grouped together because they have compatible alignment, and -// a preferred location in the final field ordering. -class FieldGroup { - public: - FieldGroup() - : preferred_location_(0) {} - - // A group with a single field. - FieldGroup(float preferred_location, const FieldDescriptor* field) - : preferred_location_(preferred_location), - fields_(1, field) {} - - // Append the fields in 'other' to this group. - void Append(const FieldGroup& other) { - if (other.fields_.empty()) { - return; - } - // Preferred location is the average among all the fields, so we weight by - // the number of fields on each FieldGroup object. - preferred_location_ = - (preferred_location_ * fields_.size() + - (other.preferred_location_ * other.fields_.size())) / - (fields_.size() + other.fields_.size()); - fields_.insert(fields_.end(), other.fields_.begin(), other.fields_.end()); - } - - void SetPreferredLocation(float location) { preferred_location_ = location; } - const std::vector<const FieldDescriptor*>& fields() const { return fields_; } - - // FieldGroup objects sort by their preferred location. - bool operator<(const FieldGroup& other) const { - return preferred_location_ < other.preferred_location_; - } - - private: - // "preferred_location_" is an estimate of where this group should go in the - // final list of fields. We compute this by taking the average index of each - // field in this group in the original ordering of fields. This is very - // approximate, but should put this group close to where its member fields - // originally went. - float preferred_location_; - std::vector<const FieldDescriptor*> fields_; - // We rely on the default copy constructor and operator= so this type can be - // used in a vector. -}; - -// Helper for the code that emits the Clear() method. -bool CanInitializeByZeroing(const FieldDescriptor* field) { - if (field->is_repeated() || field->is_extension()) return false; - switch (field->cpp_type()) { - case internal::WireFormatLite::CPPTYPE_ENUM: - return field->default_value_enum()->number() == 0; - case internal::WireFormatLite::CPPTYPE_INT32: - return field->default_value_int32() == 0; - case internal::WireFormatLite::CPPTYPE_INT64: - return field->default_value_int64() == 0; - case internal::WireFormatLite::CPPTYPE_UINT32: - return field->default_value_uint32() == 0; - case internal::WireFormatLite::CPPTYPE_UINT64: - return field->default_value_uint64() == 0; - case internal::WireFormatLite::CPPTYPE_FLOAT: - return field->default_value_float() == 0; - case internal::WireFormatLite::CPPTYPE_DOUBLE: - return field->default_value_double() == 0; - case internal::WireFormatLite::CPPTYPE_BOOL: - return field->default_value_bool() == false; - default: - return false; - } -} - bool IsPOD(const FieldDescriptor* field) { if (field->is_repeated() || field->is_extension()) return false; switch (field->cpp_type()) { @@ -242,133 +143,6 @@ bool CanConstructByZeroing(const FieldDescriptor* field, return ret; } -// Reorder 'fields' so that if the fields are output into a c++ class in the new -// order, fields of similiar family (see below) are together and within each -// family, alignment padding is minimized. -// -// We try to do this while keeping each field as close as possible to its field -// number order so that we don't reduce cache locality much for function that -// access each field in order. Originally, OptimizePadding used declaration -// order for its decisions, but generated code minus the serializer/parsers uses -// the output of OptimizePadding as well (stored in -// MessageGenerator::optimized_order_). Since the serializers use field number -// order, we use that as a tie-breaker. -// -// TODO(ckennelly): If/when we have profiles available for the compiler, use -// those rather than respect declaration order. -// -// We classify each field into a particular "family" of fields, that we perform -// the same operation on in our generated functions. -// -// REPEATED is placed first, as the C++ compiler automatically initializes -// these fields in layout order. -// -// STRING is grouped next, as our Clear/SharedCtor/SharedDtor walks it and -// calls ArenaStringPtr::Destroy on each. -// -// -// MESSAGE is grouped next, as our Clear/SharedDtor code walks it and calls -// delete on each. We initialize these fields with a NULL pointer (see -// MessageFieldGenerator::GenerateConstructorCode), which allows them to be -// memset. -// -// ZERO_INITIALIZABLE is memset in Clear/SharedCtor -// -// OTHER these fields are initialized one-by-one. -void OptimizePadding(std::vector<const FieldDescriptor*>* fields, - const Options& options) { - // The sorted numeric order of Family determines the declaration order in the - // memory layout. - enum Family { - REPEATED = 0, - STRING = 1, - MESSAGE = 3, - ZERO_INITIALIZABLE = 4, - OTHER = 5, - kMaxFamily - }; - - // First divide fields into those that align to 1 byte, 4 bytes or 8 bytes. - std::vector<FieldGroup> aligned_to_1[kMaxFamily]; - std::vector<FieldGroup> aligned_to_4[kMaxFamily]; - std::vector<FieldGroup> aligned_to_8[kMaxFamily]; - for (int i = 0; i < fields->size(); ++i) { - const FieldDescriptor* field = (*fields)[i]; - - Family f = OTHER; - if (field->is_repeated()) { - f = REPEATED; - } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { - f = STRING; - } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - f = MESSAGE; - - } else if (CanInitializeByZeroing(field)) { - f = ZERO_INITIALIZABLE; - } - - const int j = field->number(); - switch (EstimateAlignmentSize(field)) { - case 1: aligned_to_1[f].push_back(FieldGroup(j, field)); break; - case 4: aligned_to_4[f].push_back(FieldGroup(j, field)); break; - case 8: aligned_to_8[f].push_back(FieldGroup(j, field)); break; - default: - GOOGLE_LOG(FATAL) << "Unknown alignment size."; - } - } - - // For each family, group fields to optimize padding. - for (int f = 0; f < kMaxFamily; f++) { - // Now group fields aligned to 1 byte into sets of 4, and treat those like a - // single field aligned to 4 bytes. - for (int i = 0; i < aligned_to_1[f].size(); i += 4) { - FieldGroup field_group; - for (int j = i; j < aligned_to_1[f].size() && j < i + 4; ++j) { - field_group.Append(aligned_to_1[f][j]); - } - aligned_to_4[f].push_back(field_group); - } - // Sort by preferred location to keep fields as close to their field number - // order as possible. Using stable_sort ensures that the output is - // consistent across runs. - std::stable_sort(aligned_to_4[f].begin(), aligned_to_4[f].end()); - - // Now group fields aligned to 4 bytes (or the 4-field groups created above) - // into pairs, and treat those like a single field aligned to 8 bytes. - for (int i = 0; i < aligned_to_4[f].size(); i += 2) { - FieldGroup field_group; - for (int j = i; j < aligned_to_4[f].size() && j < i + 2; ++j) { - field_group.Append(aligned_to_4[f][j]); - } - if (i == aligned_to_4[f].size() - 1) { - if (f == OTHER) { - // Move incomplete 4-byte block to the beginning. This is done to - // pair with the (possible) leftover blocks from the - // ZERO_INITIALIZABLE family. - field_group.SetPreferredLocation(-1); - } else { - // Move incomplete 4-byte block to the end. - field_group.SetPreferredLocation(fields->size() + 1); - } - } - aligned_to_8[f].push_back(field_group); - } - // Sort by preferred location. - std::stable_sort(aligned_to_8[f].begin(), aligned_to_8[f].end()); - } - - // Now pull out all the FieldDescriptors in order. - fields->clear(); - for (int f = 0; f < kMaxFamily; ++f) { - for (int i = 0; i < aligned_to_8[f].size(); ++i) { - fields->insert(fields->end(), - aligned_to_8[f][i].fields().begin(), - aligned_to_8[f][i].fields().end()); - } - } -} - - // Emits an if-statement with a condition that evaluates to true if |field| is // considered non-default (will be sent over the wire), for message types // without true field presence. Should only be called if @@ -423,25 +197,31 @@ bool HasHasMethod(const FieldDescriptor* field) { void CollectMapInfo(const Descriptor* descriptor, std::map<string, string>* variables) { GOOGLE_CHECK(IsMapEntryMessage(descriptor)); + std::map<string, string>& vars = *variables; const FieldDescriptor* key = descriptor->FindFieldByName("key"); const FieldDescriptor* val = descriptor->FindFieldByName("value"); - (*variables)["key"] = PrimitiveTypeName(key->cpp_type()); + vars["key_cpp"] = PrimitiveTypeName(key->cpp_type()); switch (val->cpp_type()) { case FieldDescriptor::CPPTYPE_MESSAGE: - (*variables)["val"] = FieldMessageTypeName(val); + vars["val_cpp"] = FieldMessageTypeName(val); break; case FieldDescriptor::CPPTYPE_ENUM: - (*variables)["val"] = ClassName(val->enum_type(), true); + vars["val_cpp"] = ClassName(val->enum_type(), true); break; default: - (*variables)["val"] = PrimitiveTypeName(val->cpp_type()); - } - (*variables)["key_wire_type"] = - "::google::protobuf::internal::WireFormatLite::TYPE_" + - ToUpper(DeclaredTypeMethodName(key->type())); - (*variables)["val_wire_type"] = - "::google::protobuf::internal::WireFormatLite::TYPE_" + - ToUpper(DeclaredTypeMethodName(val->type())); + vars["val_cpp"] = PrimitiveTypeName(val->cpp_type()); + } + vars["key_wire_type"] = "::google::protobuf::internal::WireFormatLite::TYPE_" + + ToUpper(DeclaredTypeMethodName(key->type())); + vars["val_wire_type"] = "::google::protobuf::internal::WireFormatLite::TYPE_" + + ToUpper(DeclaredTypeMethodName(val->type())); + if (descriptor->file()->syntax() != FileDescriptor::SYNTAX_PROTO3 && + val->type() == FieldDescriptor::TYPE_ENUM) { + const EnumValueDescriptor* default_value = val->default_value_enum(); + vars["default_enum_value"] = Int32ToString(default_value->number()); + } else { + vars["default_enum_value"] = "0"; + } } // Does the given field have a private (internal helper only) has_$name$() @@ -531,21 +311,22 @@ void SetUnknkownFieldsVariable(const Descriptor* descriptor, // =================================================================== MessageGenerator::MessageGenerator(const Descriptor* descriptor, + int index_in_file_messages, const Options& options, SCCAnalyzer* scc_analyzer) : descriptor_(descriptor), + index_in_file_messages_(index_in_file_messages), classname_(ClassName(descriptor, false)), options_(options), field_generators_(descriptor, options), max_has_bit_index_(0), - nested_generators_(new google::protobuf::scoped_ptr< - MessageGenerator>[descriptor->nested_type_count()]), enum_generators_( new google::protobuf::scoped_ptr<EnumGenerator>[descriptor->enum_type_count()]), extension_generators_(new google::protobuf::scoped_ptr< ExtensionGenerator>[descriptor->extension_count()]), use_dependent_base_(false), num_weak_fields_(0), + message_layout_helper_(new PaddingOptimizer()), scc_analyzer_(scc_analyzer) { // Compute optimized field order to be used for layout and initialization // purposes. @@ -557,7 +338,8 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, optimized_order_.push_back(field); } } - OptimizePadding(&optimized_order_, options_); + + message_layout_helper_->OptimizeLayout(&optimized_order_, options_); if (HasFieldPresence(descriptor_->file())) { // We use -1 as a sentinel. @@ -573,11 +355,6 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, } } - for (int i = 0; i < descriptor->nested_type_count(); i++) { - nested_generators_[i].reset(new MessageGenerator(descriptor->nested_type(i), - options, scc_analyzer)); - } - for (int i = 0; i < descriptor->enum_type_count(); i++) { enum_generators_[i].reset( new EnumGenerator(descriptor->enum_type(i), options)); @@ -603,6 +380,9 @@ MessageGenerator::MessageGenerator(const Descriptor* descriptor, } table_driven_ = TableDrivenParsingEnabled(descriptor_, options_); + + scc_name_ = + ClassName(scc_analyzer_->GetSCC(descriptor_)->GetRepresentative(), false); } MessageGenerator::~MessageGenerator() {} @@ -620,14 +400,6 @@ size_t MessageGenerator::HasBitsSize() const { return sizeof_has_bits; } -void MessageGenerator::Flatten(std::vector<MessageGenerator*>* list) { - for (int i = 0; i < descriptor_->nested_type_count(); i++) { - nested_generators_[i]->Flatten(list); - } - index_in_file_messages_ = list->size(); - list->push_back(this); -} - void MessageGenerator::AddGenerators( std::vector<EnumGenerator*>* enum_generators, std::vector<ExtensionGenerator*>* extension_generators) { @@ -1010,8 +782,8 @@ GenerateFieldAccessorDefinitions(io::Printer* printer, bool is_inline) { } // Generate type-specific accessors. - field_generators_.get(field).GenerateInlineAccessorDefinitions(printer, - is_inline); + field_generators_.get(field).GenerateInlineAccessorDefinitions( + printer, /* is_inline = */ true); printer->Print("\n"); } @@ -1056,7 +828,42 @@ GenerateDependentBaseClassDefinition(io::Printer* printer) { void MessageGenerator:: GenerateClassDefinition(io::Printer* printer) { - if (IsMapEntryMessage(descriptor_)) return; + if (IsMapEntryMessage(descriptor_)) { + std::map<string, string> vars; + vars["classname"] = classname_; + CollectMapInfo(descriptor_, &vars); + vars["lite"] = + HasDescriptorMethods(descriptor_->file(), options_) ? "" : "Lite"; + printer->Print( + vars, + "class $classname$ : public " + "::google::protobuf::internal::MapEntry$lite$<$classname$, \n" + " $key_cpp$, $val_cpp$,\n" + " $key_wire_type$,\n" + " $val_wire_type$,\n" + " $default_enum_value$ > {\n" + "public:\n" + " typedef ::google::protobuf::internal::MapEntry$lite$<$classname$, \n" + " $key_cpp$, $val_cpp$,\n" + " $key_wire_type$,\n" + " $val_wire_type$,\n" + " $default_enum_value$ > SuperType;\n" + " $classname$();\n" + " $classname$(::google::protobuf::Arena* arena);\n" + " void MergeFrom(const $classname$& other);\n" + " static const $classname$* internal_default_instance() { return " + "reinterpret_cast<const " + "$classname$*>(&_$classname$_default_instance_); }\n"); + if (HasDescriptorMethods(descriptor_->file(), options_)) { + printer->Print( + " void MergeFrom(const ::google::protobuf::Message& other) PROTOBUF_FINAL;\n" + " ::google::protobuf::Metadata GetMetadata() const;\n" + "};\n"); + } else { + printer->Print("};\n"); + } + return; + } if (use_dependent_base_) { GenerateDependentBaseClassDefinition(printer); printer->Print("\n"); @@ -1109,26 +916,23 @@ GenerateClassDefinition(io::Printer* printer) { "\n"); } - // Generate move constructor and move assignment operator for types other than - // Any. - if (!IsAnyMessage(descriptor_)) { - printer->Print(vars, - "#if LANG_CXX11\n" - "$classname$($classname$&& from) noexcept\n" - " : $classname$() {\n" - " *this = ::std::move(from);\n" - "}\n" - "\n" - "inline $classname$& operator=($classname$&& from) noexcept {\n" - " if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {\n" - " if (this != &from) InternalSwap(&from);\n" - " } else {\n" - " CopyFrom(from);\n" - " }\n" - " return *this;\n" - "}\n" - "#endif\n"); - } + // Generate move constructor and move assignment operator. + printer->Print(vars, + "#if LANG_CXX11\n" + "$classname$($classname$&& from) noexcept\n" + " : $classname$() {\n" + " *this = ::std::move(from);\n" + "}\n" + "\n" + "inline $classname$& operator=($classname$&& from) noexcept {\n" + " if (GetArenaNoVirtual() == from.GetArenaNoVirtual()) {\n" + " if (this != &from) InternalSwap(&from);\n" + " } else {\n" + " CopyFrom(from);\n" + " }\n" + " return *this;\n" + "}\n" + "#endif\n"); SetUnknkownFieldsVariable(descriptor_, options_, &vars); if (PublicUnknownFieldsAccessors(descriptor_)) { @@ -1199,6 +1003,7 @@ GenerateClassDefinition(io::Printer* printer) { vars["message_index"] = SimpleItoa(index_in_file_messages_); printer->Print( vars, + "static void InitAsDefaultInstance(); // FOR INTERNAL USE ONLY\n" "static inline const $classname$* internal_default_instance() {\n" " return reinterpret_cast<const $classname$*>(\n" " &_$classname$_default_instance_);\n" @@ -1358,6 +1163,8 @@ GenerateClassDefinition(io::Printer* printer) { printer->Print("typedef $nested_full_name$ $nested_name$;\n", "nested_name", nested_type->name(), "nested_full_name", ClassName(nested_type, false)); + printer->Annotate("nested_full_name", nested_type); + printer->Annotate("nested_name", nested_type); } } @@ -1548,14 +1355,13 @@ GenerateClassDefinition(io::Printer* printer) { // The TableStruct struct needs access to the private parts, in order to // construct the offsets of all members. - // - // Some InitDefault and Shutdown are defined as static member functions of - // TableStruct such that they are also allowed to access private members. + // TODO(gerbens) Remove the friend for InitDefaults. printer->Print( - "friend struct $file_namespace$::TableStruct;\n", + "friend struct ::$file_namespace$::TableStruct;\n" + "friend void ::$file_namespace$::InitDefaults$scc_name$Impl();\n", // Vars. - "file_namespace", - FileLevelNamespace(descriptor_->file()->name())); + "scc_name", scc_name_, "file_namespace", + FileLevelNamespace(descriptor_)); printer->Outdent(); printer->Print("};"); @@ -1577,7 +1383,7 @@ GenerateDependentInlineMethods(io::Printer* printer) { void MessageGenerator:: GenerateInlineMethods(io::Printer* printer, bool is_inline) { if (IsMapEntryMessage(descriptor_)) return; - GenerateFieldAccessorDefinitions(printer, is_inline); + GenerateFieldAccessorDefinitions(printer, /* is_inline = */ true); // Generate oneof_case() functions. for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { @@ -1634,7 +1440,8 @@ bool MessageGenerator::GenerateParseTable(io::Printer* printer, size_t offset, std::map<string, string> vars; - vars["classname"] = classname_; + vars["classname"] = ClassName(descriptor_); + vars["classtype"] = QualifiedClassName(descriptor_); vars["offset"] = SimpleItoa(offset); vars["aux_offset"] = SimpleItoa(aux_offset); @@ -1661,48 +1468,34 @@ bool MessageGenerator::GenerateParseTable(io::Printer* printer, size_t offset, printer->Print(vars, "-1,\n"); } else { printer->Print(vars, - "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" - " $classname$, _has_bits_),\n"); + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" + " $classtype$, _has_bits_),\n"); } if (descriptor_->oneof_decl_count() > 0) { printer->Print(vars, "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" - " $classname$, _oneof_case_),\n"); + " $classtype$, _oneof_case_),\n"); } else { printer->Print("-1, // no _oneof_case_\n"); } if (descriptor_->extension_range_count() > 0) { printer->Print(vars, - "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " - "_extensions_),\n"); + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classtype$, " + "_extensions_),\n"); } else { printer->Print("-1, // no _extensions_\n"); } // TODO(ckennelly): Consolidate this with the calculation for // AuxillaryParseTableField. - std::vector<string> package_parts; - - const Descriptor* outer = descriptor_; - while (outer->containing_type() != NULL) { - outer = outer->containing_type(); - } - - package_parts = Split( - outer->full_name(), ".", true); - // outer->full_name() contains the class itself. Remove it as it is - // used in the name of the default instance variable. - GOOGLE_DCHECK_NE(package_parts.size(), 0); - package_parts.back().clear(); - - vars["ns"] = Join(package_parts, "::"); + vars["ns"] = Namespace(descriptor_); printer->Print(vars, - "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" - " $classname$, _internal_metadata_),\n" - "&::$ns$_$classname$_default_instance_,\n"); + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(\n" + " $classtype$, _internal_metadata_),\n" + "&$ns$::_$classname$_default_instance_,\n"); if (UseUnknownFieldSet(descriptor_->file(), options_)) { printer->Print(vars, "true,\n"); @@ -1717,15 +1510,14 @@ bool MessageGenerator::GenerateParseTable(io::Printer* printer, size_t offset, void MessageGenerator::GenerateSchema(io::Printer* printer, int offset, int has_offset) { - if (IsMapEntryMessage(descriptor_)) return; - std::map<string, string> vars; - vars["classname"] = classname_; + vars["classname"] = QualifiedClassName(descriptor_); vars["offset"] = SimpleItoa(offset); - vars["has_bits_offsets"] = HasFieldPresence(descriptor_->file()) - ? SimpleItoa(offset + has_offset) - : "-1"; + vars["has_bits_offsets"] = + HasFieldPresence(descriptor_->file()) || IsMapEntryMessage(descriptor_) + ? SimpleItoa(offset + has_offset) + : "-1"; printer->Print(vars, "{ $offset$, $has_bits_offsets$, sizeof($classname$)},\n"); @@ -1804,6 +1596,8 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { return 0; } + string full_classname = QualifiedClassName(descriptor_); + std::vector<const FieldDescriptor*> sorted = SortFieldsByNumber(descriptor_); if (IsMapEntryMessage(descriptor_)) { for (int i = 0; i < 2; i++) { @@ -1812,9 +1606,7 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { field->number(), WireFormat::WireTypeForFieldType(field->type())); std::map<string, string> vars; - vars["classname"] = classname_; - vars["parent_classname"] = - ClassName(descriptor_->containing_type(), false); + vars["classname"] = QualifiedClassName(descriptor_); vars["field_name"] = FieldName(field); vars["tag"] = SimpleItoa(tag); vars["hasbit"] = SimpleItoa(i); @@ -1824,23 +1616,18 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { GOOGLE_CHECK(!IsMapEntryMessage(field->message_type())); { vars["ptr"] = - QualifiedFileLevelSymbol( - field->message_type()->file()->package(), - FileLevelNamespace(field->message_type()->file()->name())) + + "::" + FileLevelNamespace(field->message_type()) + "::TableStruct::serialization_table + " + SimpleItoa(FindMessageIndexInFile(field->message_type())); } } - vars["extra"] = HasDescriptorMethods(descriptor_->file(), options_) - ? "::SuperType" - : ""; printer->Print(vars, "{GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" - "::google::protobuf::internal::MapEntryHelper<$parent_classname$::$" - "classname$$extra$>, $field_name$_), $tag$," + "::google::protobuf::internal::MapEntryHelper<$classname$::" + "SuperType>, $field_name$_), $tag$," "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(" - "::google::protobuf::internal::MapEntryHelper<$parent_classname$::$" - "classname$$extra$>, _has_bits_) * 8 + $hasbit$, $type$, " + "::google::protobuf::internal::MapEntryHelper<$classname$::" + "SuperType>, _has_bits_) * 8 + $hasbit$, $type$, " "$ptr$},\n"); } return 2; @@ -1848,7 +1635,7 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { printer->Print( "{GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " "_cached_size_), 0, 0, 0, NULL},\n", - "classname", classname_); + "classname", full_classname); std::vector<const Descriptor::ExtensionRange*> sorted_extensions; for (int i = 0; i < descriptor_->extension_range_count(); ++i) { sorted_extensions.push_back(descriptor_->extension_range(i)); @@ -1868,7 +1655,7 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { "::google::protobuf::internal::FieldMetadata::kSpecial, " "reinterpret_cast<const " "void*>(::google::protobuf::internal::ExtensionSerializer)},\n", - "classname", classname_, "start", SimpleItoa(range->start), "end", + "classname", full_classname, "start", SimpleItoa(range->start), "end", SimpleItoa(range->end)); } if (i == sorted.size()) break; @@ -1886,14 +1673,14 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { classfieldname = field->containing_oneof()->name(); } std::map<string, string> vars; - vars["classname"] = classname_; + vars["classname"] = full_classname; vars["field_name"] = classfieldname; vars["tag"] = SimpleItoa(tag); vars["ptr"] = "NULL"; if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { if (IsMapEntryMessage(field->message_type())) { vars["idx"] = SimpleItoa(FindMessageIndexInFile(field->message_type())); - vars["fieldclassname"] = ClassName(field->message_type(), false); + vars["fieldclassname"] = QualifiedClassName(field->message_type()); printer->Print(vars, "{GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($" "classname$, $field_name$_), $tag$, $idx$, " @@ -1901,15 +1688,13 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { "reinterpret_cast<const void*>(static_cast< " "::google::protobuf::internal::SpecialSerializer>(" "::google::protobuf::internal::MapFieldSerializer< " - "::google::protobuf::internal::MapEntryToMapField<$classname$::$" - "fieldclassname$>::MapFieldType, " + "::google::protobuf::internal::MapEntryToMapField<" + "$fieldclassname$>::MapFieldType, " "TableStruct::serialization_table>))},\n"); continue; } else { vars["ptr"] = - QualifiedFileLevelSymbol( - field->message_type()->file()->package(), - FileLevelNamespace(field->message_type()->file()->name())) + + "::" + FileLevelNamespace(field->message_type()) + "::TableStruct::serialization_table + " + SimpleItoa(FindMessageIndexInFile(field->message_type())); } @@ -1960,40 +1745,20 @@ int MessageGenerator::GenerateFieldMetadata(io::Printer* printer) { "_internal_metadata_), 0, ~0u, " "::google::protobuf::internal::FieldMetadata::kSpecial, reinterpret_cast<const " "void*>($serializer$)},\n", - "classname", classname_, "serializer", serializer); + "classname", full_classname, "serializer", serializer); return num_field_metadata; } -void MessageGenerator:: -GenerateDefaultInstanceAllocator(io::Printer* printer) { - // Construct the default instances of all fields, as they will be used - // when creating the default instance of the entire message. +void MessageGenerator::GenerateFieldDefaultInstances(io::Printer* printer) { + // Construct the default instances for all fields that need one. for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_.get(descriptor_->field(i)) .GenerateDefaultInstanceAllocator(printer); } - - // Construct the default instance. We can't call InitAsDefaultInstance() yet - // because we need to make sure all default instances that this one might - // depend on are constructed first. - printer->Print("_$classname$_default_instance_._instance.DefaultConstruct();\n" - "::google::protobuf::internal::OnShutdownDestroyMessage(\n" - " &_$classname$_default_instance_);", - "classname", classname_); } void MessageGenerator:: GenerateDefaultInstanceInitializer(io::Printer* printer) { - if (IsMapEntryMessage(descriptor_)) { - printer->Print( - "_$classname$_default_instance_._instance.get_mutable()->set_default_instance(_$" - "classname$_default_instance_._instance.get_mutable());\n" - "_$classname$_default_instance_._instance.get_mutable()->InitAsDefaultInstance();" - "\n", - "classname", classname_); - return; - } - // The default instance needs all of its embedded message pointers // cross-linked to other default instances. We can't do this initialization // in the constructor because some other default instances may not have been @@ -2016,10 +1781,11 @@ GenerateDefaultInstanceInitializer(io::Printer* printer) { } name += FieldName(field); printer->Print( - "$name$_ = const_cast< $type$*>(\n" + "$ns$::$name$_ = const_cast< $type$*>(\n" " $type$::internal_default_instance());\n", // Vars. - "name", name, "type", FieldMessageTypeName(field)); + "name", name, "type", FieldMessageTypeName(field), "ns", + Namespace(descriptor_)); } else if (field->containing_oneof() && HasDescriptorMethods(descriptor_->file(), options_)) { field_generators_.get(descriptor_->field(i)) @@ -2031,31 +1797,41 @@ GenerateDefaultInstanceInitializer(io::Printer* printer) { void MessageGenerator:: GenerateClassMethods(io::Printer* printer) { if (IsMapEntryMessage(descriptor_)) { + printer->Print( + "$classname$::$classname$() {}\n" + "$classname$::$classname$(::google::protobuf::Arena* arena) : " + "SuperType(arena) {}\n" + "void $classname$::MergeFrom(const $classname$& other) {\n" + " MergeFromInternal(other);\n" + "}\n", + "classname", classname_); if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( - "$parent$::$classname$::$classname$() {}\n" - "$parent$::$classname$::$classname$(::google::protobuf::Arena* arena) : " - "SuperType(arena) {}\n" - "::google::protobuf::Metadata $parent$::$classname$::GetMetadata() const {\n" - " $file_namespace$::protobuf_AssignDescriptorsOnce();\n" - " return $file_namespace$::file_level_metadata[$index$];\n" + "::google::protobuf::Metadata $classname$::GetMetadata() const {\n" + " ::$file_namespace$::protobuf_AssignDescriptorsOnce();\n" + " return ::$file_namespace$::file_level_metadata[$index$];\n" "}\n" - "void $parent$::$classname$::MergeFrom(\n" + "void $classname$::MergeFrom(\n" " const ::google::protobuf::Message& other) {\n" " ::google::protobuf::Message::MergeFrom(other);\n" "}\n" - "void $parent$::$classname$::MergeFrom(const $classname$& other) {\n" - " MergeFromInternal(other);\n" - "}\n" "\n", - "file_namespace", FileLevelNamespace(descriptor_->file()->name()), - "parent", ClassName(descriptor_->containing_type(), false), + "file_namespace", FileLevelNamespace(descriptor_), "classname", classname_, "index", SimpleItoa(index_in_file_messages_)); } return; } + // TODO(gerbens) Remove this function. With a little bit of cleanup and + // refactoring this is superfluous. + printer->Print("void $classname$::InitAsDefaultInstance() {\n", "classname", + classname_); + printer->Indent(); + GenerateDefaultInstanceInitializer(printer); + printer->Outdent(); + printer->Print("}\n"); + if (IsAnyMessage(descriptor_)) { printer->Print( "void $classname$::PackFrom(const ::google::protobuf::Message& message) {\n" @@ -2134,23 +1910,24 @@ GenerateClassMethods(io::Printer* printer) { if (options_.table_driven_serialization) { printer->Print( - "const void* $classname$::InternalGetTable() const {\n" - " return $file_namespace$::TableStruct::serialization_table + $index$;\n" - "}\n" - "\n", - "classname", classname_, "index", SimpleItoa(index_in_file_messages_), - "file_namespace", FileLevelNamespace(descriptor_->file()->name())); + "const void* $classname$::InternalGetTable() const {\n" + " return ::$file_namespace$::TableStruct::serialization_table + " + "$index$;\n" + "}\n" + "\n", + "classname", classname_, "index", SimpleItoa(index_in_file_messages_), + "file_namespace", FileLevelNamespace(descriptor_)); } if (HasDescriptorMethods(descriptor_->file(), options_)) { printer->Print( "::google::protobuf::Metadata $classname$::GetMetadata() const {\n" " $file_namespace$::protobuf_AssignDescriptorsOnce();\n" - " return " + " return ::" "$file_namespace$::file_level_metadata[kIndexInFileMessages];\n" "}\n" "\n", "classname", classname_, "file_namespace", - FileLevelNamespace(descriptor_->file()->name())); + FileLevelNamespace(descriptor_)); } else { printer->Print( "::std::string $classname$::GetTypeName() const {\n" @@ -2224,7 +2001,7 @@ size_t MessageGenerator::GenerateParseOffsets(io::Printer* printer) { WireFormat::TagSize(field->number(), field->type()); std::map<string, string> vars; - vars["classname"] = classname_; + vars["classname"] = QualifiedClassName(descriptor_); if (field->containing_oneof() != NULL) { vars["name"] = field->containing_oneof()->name(); vars["presence"] = SimpleItoa(field->containing_oneof()->index()); @@ -2280,23 +2057,8 @@ size_t MessageGenerator::GenerateParseAuxTable(io::Printer* printer) { last_field_number++; break; case FieldDescriptor::CPPTYPE_MESSAGE: { - std::vector<string> package_parts; - - const Descriptor* outer = field->message_type(); - while (outer->containing_type() != NULL) { - outer = outer->containing_type(); - } - - package_parts = Split( - outer->full_name(), ".", true); - // outer->full_name() contains the class itself. Remove it as it is - // used in the name of the default instance variable. - GOOGLE_DCHECK_NE(package_parts.size(), 0); - package_parts.back().clear(); - if (field->is_map()) { - vars["classname"] = ClassName(field->containing_type(), false) + - "::" + ClassName(field->message_type(), false); + vars["classname"] = QualifiedClassName(field->message_type()); printer->Print(vars, "{::google::protobuf::internal::AuxillaryParseTableField::map_" "aux{&::google::protobuf::internal::ParseMap<$classname$>}},\n"); @@ -2305,13 +2067,15 @@ size_t MessageGenerator::GenerateParseAuxTable(io::Printer* printer) { } else { vars["classname"] = ClassName(field->message_type(), false); } - vars["ns"] = Join(package_parts, "::"); + vars["ns"] = Namespace(field->message_type()); vars["type"] = FieldMessageTypeName(field); - vars["file_namespace"] = FileLevelNamespace(outer->file()->name()); + vars["file_namespace"] = + FileLevelNamespace(field->message_type()); - printer->Print(vars, + printer->Print( + vars, "{::google::protobuf::internal::AuxillaryParseTableField::message_aux{\n" - " &::$ns$_$classname$_default_instance_,\n"); + " &$ns$::_$classname$_default_instance_,\n"); bool dont_emit_table = !TableDrivenParsingEnabled(field->message_type(), options_); @@ -2320,8 +2084,8 @@ size_t MessageGenerator::GenerateParseAuxTable(io::Printer* printer) { printer->Print(" NULL,\n"); } else { printer->Print(vars, - " ::$ns$$file_namespace$::TableStruct::schema +\n" - " ::$ns$$classname$::kIndexInFileMessages,\n"); + " ::$file_namespace$::TableStruct::schema +\n" + " $ns$::$classname$::kIndexInFileMessages,\n"); } printer->Print("}},\n"); @@ -2333,8 +2097,9 @@ size_t MessageGenerator::GenerateParseAuxTable(io::Printer* printer) { case FieldOptions::STRING: vars["default"] = field->default_value_string().empty() - ? "&::google::protobuf::internal::fixed_address_empty_string" - : "&" + classname_ + "::_default_" + FieldName(field) + "_"; + ? "&::google::protobuf::internal::fixed_address_empty_string" + : "&" + Namespace(field) + " ::" + classname_ + + "::_default_" + FieldName(field) + "_"; break; case FieldOptions::CORD: case FieldOptions::STRING_PIECE: @@ -2360,11 +2125,11 @@ size_t MessageGenerator::GenerateParseAuxTable(io::Printer* printer) { std::pair<size_t, size_t> MessageGenerator::GenerateOffsets( io::Printer* printer) { - if (IsMapEntryMessage(descriptor_)) return std::make_pair(0, 0); std::map<string, string> variables; - variables["classname"] = classname_; + string full_classname = QualifiedClassName(descriptor_); + variables["classname"] = full_classname; - if (HasFieldPresence(descriptor_->file())) { + if (HasFieldPresence(descriptor_->file()) || IsMapEntryMessage(descriptor_)) { printer->Print( variables, "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " @@ -2405,27 +2170,29 @@ std::pair<size_t, size_t> MessageGenerator::GenerateOffsets( for (int i = 0; i < descriptor_->field_count(); i++) { const FieldDescriptor* field = descriptor_->field(i); if (field->containing_oneof() || field->options().weak()) { - printer->Print( - "offsetof($classname$DefaultTypeInternal, $name$_),\n", - "classname", classname_, "name", FieldName(field)); + printer->Print("offsetof($classname$DefaultTypeInternal, $name$_),\n", + "classname", full_classname, "name", FieldName(field)); } else { printer->Print( "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, " - "$name$_),\n", - "classname", classname_, - "name", FieldName(field)); + "$name$_),\n", + "classname", full_classname, "name", FieldName(field)); } } for (int i = 0; i < descriptor_->oneof_decl_count(); i++) { const OneofDescriptor* oneof = descriptor_->oneof_decl(i); printer->Print( - "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, $name$_),\n", - "classname", classname_, - "name", oneof->name()); + "GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET($classname$, $name$_),\n", + "classname", full_classname, "name", oneof->name()); } - if (HasFieldPresence(descriptor_->file())) { + if (IsMapEntryMessage(descriptor_)) { + entries += 2; + printer->Print( + "0,\n" + "1,\n"); + } else if (HasFieldPresence(descriptor_->file())) { entries += has_bit_indices_.size(); for (int i = 0; i < has_bit_indices_.size(); i++) { const string index = has_bit_indices_[i] >= 0 ? @@ -2700,27 +2467,28 @@ GenerateStructors(io::Printer* printer) { "$classname$::$classname$()\n" " : $initializer$ {\n" " if (GOOGLE_PREDICT_TRUE(this != internal_default_instance())) {\n" - " $file_namespace$::InitDefaults();\n" + " ::$file_namespace$::InitDefaults$scc_name$();\n" " }\n" " SharedCtor();\n" " // @@protoc_insertion_point(constructor:$full_name$)\n" "}\n", "classname", classname_, "full_name", descriptor_->full_name(), - "initializer", initializer_null, "file_namespace", - FileLevelNamespace(descriptor_->file()->name())); + "scc_name", scc_name_, "initializer", initializer_null, "file_namespace", + FileLevelNamespace(descriptor_)); if (SupportsArenas(descriptor_)) { printer->Print( "$classname$::$classname$(::google::protobuf::Arena* arena)\n" " : $initializer$ {\n" - " $file_namespace$::InitDefaults();\n" + " ::$file_namespace$::InitDefaults$scc_name$();\n" " SharedCtor();\n" " RegisterArenaDtor(arena);\n" " // @@protoc_insertion_point(arena_constructor:$full_name$)\n" "}\n", "initializer", initializer_with_arena, "classname", classname_, "superclass", superclass, "full_name", descriptor_->full_name(), - "file_namespace", FileLevelNamespace(descriptor_->file()->name())); + "scc_name", scc_name_, "file_namespace", + FileLevelNamespace(descriptor_)); } // Generate the copy constructor. @@ -2865,23 +2633,23 @@ GenerateStructors(io::Printer* printer) { !descriptor_->options().no_standard_descriptor_accessor()) { printer->Print( "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" - " $file_namespace$::protobuf_AssignDescriptorsOnce();\n" - " return " + " ::$file_namespace$::protobuf_AssignDescriptorsOnce();\n" + " return ::" "$file_namespace$::file_level_metadata[kIndexInFileMessages]." "descriptor;\n" "}\n" "\n", "classname", classname_, "file_namespace", - FileLevelNamespace(descriptor_->file()->name())); + FileLevelNamespace(descriptor_)); } printer->Print( "const $classname$& $classname$::default_instance() {\n" - " $file_namespace$::InitDefaults();\n" + " ::$file_namespace$::InitDefaults$scc_name$();\n" " return *internal_default_instance();\n" "}\n\n", - "classname", classname_, "file_namespace", - FileLevelNamespace(descriptor_->file()->name())); + "classname", classname_, "scc_name", scc_name_, "file_namespace", + FileLevelNamespace(descriptor_)); if (SupportsArenas(descriptor_)) { printer->Print( @@ -2930,6 +2698,9 @@ bool MessageGenerator::MaybeGenerateOptionalFieldCondition( void MessageGenerator:: GenerateClear(io::Printer* printer) { + // Performance tuning parameters + const int kMaxUnconditionalPrimitiveBytesClear = 4; + printer->Print( "void $classname$::Clear() {\n" "// @@protoc_insertion_point(message_clear_start:$full_name$)\n", @@ -2951,6 +2722,17 @@ GenerateClear(io::Printer* printer) { } int last_i = -1; + int unconditional_budget = kMaxUnconditionalPrimitiveBytesClear; + for (int i = 0; i < optimized_order_.size(); i++) { + const FieldDescriptor* field = optimized_order_[i]; + + if (!CanInitializeByZeroing(field)) { + continue; + } + + unconditional_budget -= EstimateAlignmentSize(field); + } + for (int i = 0; i < optimized_order_.size(); ) { // Detect infinite loops. GOOGLE_CHECK_NE(i, last_i); @@ -2998,7 +2780,8 @@ GenerateClear(io::Printer* printer) { if (last_chunk == -1) { last_chunk = chunk; last_chunk_start = i; - } else if (chunk != last_chunk) { + } else if ((memset_run_start == -1 || unconditional_budget < 0) && + chunk != last_chunk) { // Emit the fields for this chunk so far. break; } @@ -3012,6 +2795,11 @@ GenerateClear(io::Printer* printer) { last_chunk_mask |= static_cast<uint32>(1) << (index % 32); } + if (memset_run_start != memset_run_end && unconditional_budget >= 0) { + // Flush the memset fields. + goto flush; + } + // Step 4: Non-repeated, non-zero initializable fields. for (; i < optimized_order_.size(); i++) { const FieldDescriptor* field = optimized_order_[i]; @@ -3037,6 +2825,8 @@ GenerateClear(io::Printer* printer) { last_chunk_mask |= static_cast<uint32>(1) << (index % 32); } +flush: + if (last_chunk != -1) { GOOGLE_DCHECK_NE(-1, last_chunk_start); GOOGLE_DCHECK_NE(-1, last_chunk_end); @@ -3044,7 +2834,10 @@ GenerateClear(io::Printer* printer) { const int count = popcnt(last_chunk_mask); const bool have_outer_if = HasFieldPresence(descriptor_->file()) && - (last_chunk_start != last_chunk_end); + (last_chunk_start != last_chunk_end) && + (memset_run_start != last_chunk_start || + memset_run_end != last_chunk_end || + unconditional_budget < 0); if (have_outer_if) { // Check (up to) 8 has_bits at a time if we have more than one field in @@ -3613,12 +3406,11 @@ GenerateMergeFromCodedStream(io::Printer* printer) { printer->Print( "return ::google::protobuf::internal::MergePartialFromCodedStream$lite$(\n" " this,\n" - " $file_namespace$::TableStruct::schema[\n" + " ::$file_namespace$::TableStruct::schema[\n" " $classname$::kIndexInFileMessages],\n" " input);\n", - "classname", classname_, - "file_namespace", FileLevelNamespace(descriptor_->file()->name()), - "lite", lite); + "classname", classname_, "file_namespace", + FileLevelNamespace(descriptor_), "lite", lite); printer->Outdent(); @@ -3631,17 +3423,14 @@ GenerateMergeFromCodedStream(io::Printer* printer) { " ::google::protobuf::uint32 tag;\n"); if (!UseUnknownFieldSet(descriptor_->file(), options_)) { - // Use LazyStringOutputString to avoid initializing unknown fields string - // unless it is actually needed. For the same reason, disable eager refresh - // on the CodedOutputStream. printer->Print( - " ::google::protobuf::io::LazyStringOutputStream unknown_fields_string(\n" - " ::google::protobuf::NewPermanentCallback(&_internal_metadata_,\n" - " &::google::protobuf::internal::InternalMetadataWithArenaLite::\n" - " mutable_unknown_fields));\n" - " ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n" - " &unknown_fields_string, false);\n", - "classname", classname_); + " ::google::protobuf::internal::LiteUnknownFieldSetter unknown_fields_setter(\n" + " &_internal_metadata_);\n" + " ::google::protobuf::io::StringOutputStream unknown_fields_output(\n" + " unknown_fields_setter.buffer());\n" + " ::google::protobuf::io::CodedOutputStream unknown_fields_stream(\n" + " &unknown_fields_output, false);\n", + "classname", classname_); } printer->Print( diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index 352069eb..cf64f483 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -43,6 +43,7 @@ #include <string> #include <google/protobuf/compiler/cpp/cpp_field.h> #include <google/protobuf/compiler/cpp/cpp_helpers.h> +#include <google/protobuf/compiler/cpp/cpp_message_layout_helper.h> #include <google/protobuf/compiler/cpp/cpp_options.h> namespace google { @@ -62,12 +63,10 @@ class ExtensionGenerator; // extension.h class MessageGenerator { public: // See generator.cc for the meaning of dllexport_decl. - MessageGenerator(const Descriptor* descriptor, const Options& options, - SCCAnalyzer* scc_analyzer); + MessageGenerator(const Descriptor* descriptor, int index_in_file_messages, + const Options& options, SCCAnalyzer* scc_analyzer); ~MessageGenerator(); - // Appends the pre-order walk of the nested generators to list. - void Flatten(std::vector<MessageGenerator*>* list); // Append the two types of nested generators to the corresponding vector. void AddGenerators(std::vector<EnumGenerator*>* enum_generators, std::vector<ExtensionGenerator*>* extension_generators); @@ -96,8 +95,8 @@ class MessageGenerator { // Generate extra fields void GenerateExtraDefaultFields(io::Printer* printer); - // Generates code that allocates the message's default instance. - void GenerateDefaultInstanceAllocator(io::Printer* printer); + // Generates code that creates default instances for fields. + void GenerateFieldDefaultInstances(io::Printer* printer); // Generates code that initializes the message's default instance. This // is separate from allocating because all default instances must be @@ -208,6 +207,7 @@ class MessageGenerator { std::vector<uint32> RequiredFieldsBitMask() const; const Descriptor* descriptor_; + int index_in_file_messages_; string classname_; Options options_; FieldGeneratorMap field_generators_; @@ -218,7 +218,6 @@ class MessageGenerator { std::vector<const FieldDescriptor *> optimized_order_; std::vector<int> has_bit_indices_; int max_has_bit_index_; - google::protobuf::scoped_array<google::protobuf::scoped_ptr<MessageGenerator> > nested_generators_; google::protobuf::scoped_array<google::protobuf::scoped_ptr<EnumGenerator> > enum_generators_; google::protobuf::scoped_array<google::protobuf::scoped_ptr<ExtensionGenerator> > extension_generators_; int num_required_fields_; @@ -227,9 +226,10 @@ class MessageGenerator { // table_driven_ indicates the generated message uses table-driven parsing. bool table_driven_; - int index_in_file_messages_; + google::protobuf::scoped_ptr<MessageLayoutHelper> message_layout_helper_; SCCAnalyzer* scc_analyzer_; + string scc_name_; friend class FileGenerator; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageGenerator); diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index da4c3950..5888f51a 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -45,13 +45,42 @@ namespace cpp { namespace { +// When we are generating code for implicit weak fields, we need to insert some +// additional casts. These functions return the casted expression if +// implicit_weak_field is true but otherwise return the original expression. +// Ordinarily a static_cast is enough to cast google::protobuf::MessageLite* to a class +// deriving from it, but we need a reinterpret_cast in cases where the generated +// message is forward-declared but its full definition is not visible. +string StaticCast(const string& type, const string& expression, + bool implicit_weak_field) { + if (implicit_weak_field) { + return "static_cast< " + type + " >(" + expression + ")"; + } else { + return expression; + } +} + +string ReinterpretCast(const string& type, const string& expression, + bool implicit_weak_field) { + if (implicit_weak_field) { + return "reinterpret_cast< " + type + " >(" + expression + ")"; + } else { + return expression; + } +} + void SetMessageVariables(const FieldDescriptor* descriptor, std::map<string, string>* variables, const Options& options) { SetCommonFieldVariables(descriptor, variables, options); (*variables)["type"] = FieldMessageTypeName(descriptor); + (*variables)["casted_member"] = + StaticCast((*variables)["type"] + "*", (*variables)["name"] + "_", + IsImplicitWeakField(descriptor, options)); (*variables)["type_default_instance"] = DefaultInstanceName(descriptor->message_type()); + (*variables)["type_reference_function"] = + ReferenceFunctionName(descriptor->message_type()); if (descriptor->options().weak() || !descriptor->containing_oneof()) { (*variables)["non_null_ptr_to_name"] = StrCat("this->", (*variables)["name"], "_"); @@ -85,7 +114,8 @@ MessageFieldGenerator::MessageFieldGenerator(const FieldDescriptor* descriptor, const Options& options) : FieldGenerator(options), descriptor_(descriptor), - dependent_field_(options.proto_h && IsFieldDependent(descriptor)) { + dependent_field_(options.proto_h && IsFieldDependent(descriptor)), + implicit_weak_field_(IsImplicitWeakField(descriptor, options)) { SetMessageVariables(descriptor, &variables_, options); } @@ -93,7 +123,11 @@ MessageFieldGenerator::~MessageFieldGenerator() {} void MessageFieldGenerator:: GeneratePrivateMembers(io::Printer* printer) const { - printer->Print(variables_, "$type$* $name$_;\n"); + if (implicit_weak_field_) { + printer->Print(variables_, "google::protobuf::MessageLite* $name$_;\n"); + } else { + printer->Print(variables_, "$type$* $name$_;\n"); + } } void MessageFieldGenerator:: @@ -108,7 +142,11 @@ GenerateDependentAccessorDeclarations(io::Printer* printer) const { if (!dependent_field_) { return; } - // Arena manipulation code is out-of-line in the derived message class. + // Arena manipulation code is out-of-line in the derived message class. The + // one exception is unsafe_arena_release_; this method has to be inline so + // that when the implicit weak field optimization is enabled, the method does + // not introduce a strong dependency on the submessage type unless the + // accessor actually gets called somewhere. printer->Print(variables_, "$deprecated_attr$$type$* ${$mutable_$name$$}$();\n"); printer->Annotate("{", "}", descriptor_); @@ -118,14 +156,22 @@ GenerateDependentAccessorDeclarations(io::Printer* printer) const { "$deprecated_attr$void ${$set_allocated_$name$$}$" "($type$* $name$);\n"); printer->Annotate("{", "}", descriptor_); + if (SupportsArenas(descriptor_)) { + printer->Print( + variables_, + "$deprecated_attr$$type$* ${$unsafe_arena_release_$name$$}$();\n"); + printer->Annotate("{", "}", descriptor_); + } } void MessageFieldGenerator:: GenerateAccessorDeclarations(io::Printer* printer) const { if (SupportsArenas(descriptor_)) { printer->Print(variables_, - "private:\n" - "void _slow_mutable_$name$();\n"); + "private:\n"); + if (!implicit_weak_field_) { + printer->Print(variables_, "void _slow_mutable_$name$();\n"); + } if (SupportsArenas(descriptor_->message_type())) { printer->Print(variables_, "void _slow_set_allocated_$name$(\n" @@ -135,6 +181,16 @@ GenerateAccessorDeclarations(io::Printer* printer) const { "$type$* _slow_$release_name$();\n" "public:\n"); } + if (implicit_weak_field_) { + // These private accessors are used by MergeFrom and + // MergePartialFromCodedStream, and their purpose is to provide access to + // the field without creating a strong dependency on the message type. + printer->Print(variables_, + "private:\n" + "const google::protobuf::MessageLite& _internal_$name$() const;\n" + "google::protobuf::MessageLite* _internal_mutable_$name$();\n" + "public:\n"); + } GenerateGetterDeclaration(printer); if (!dependent_field_) { printer->Print(variables_, @@ -146,12 +202,14 @@ GenerateAccessorDeclarations(io::Printer* printer) const { "$deprecated_attr$void ${$set_allocated_$name$$}$" "($type$* $name$);\n"); printer->Annotate("{", "}", descriptor_); + if (SupportsArenas(descriptor_)) { + printer->Print( + variables_, + "$deprecated_attr$$type$* ${$unsafe_arena_release_$name$$}$();\n"); + printer->Annotate("{", "}", descriptor_); + } } if (SupportsArenas(descriptor_)) { - printer->Print( - variables_, - "$deprecated_attr$$type$* ${$unsafe_arena_release_$name$$}$();\n"); - printer->Annotate("{", "}", descriptor_); printer->Print(variables_, "$deprecated_attr$void " "${$unsafe_arena_set_allocated_$name$$}$(\n" @@ -162,9 +220,39 @@ GenerateAccessorDeclarations(io::Printer* printer) const { void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( io::Printer* printer) const { - if (SupportsArenas(descriptor_)) { + if (implicit_weak_field_) { printer->Print(variables_, - "void $classname$::_slow_mutable_$name$() {\n"); + "const google::protobuf::MessageLite& $classname$::_internal_$name$() const {\n" + " if ($name$_ != NULL) {\n" + " return *$name$_;\n" + " } else if (&$type_default_instance$ != NULL) {\n" + " return *reinterpret_cast<const google::protobuf::MessageLite*>(\n" + " &$type_default_instance$);\n" + " } else {\n" + " return *reinterpret_cast<const google::protobuf::MessageLite*>(\n" + " &::google::protobuf::internal::implicit_weak_message_default_instance);\n" + " }\n" + "}\n"); + } + if (SupportsArenas(descriptor_)) { + if (implicit_weak_field_) { + printer->Print(variables_, + "google::protobuf::MessageLite* $classname$::_internal_mutable_$name$() {\n" + " $set_hasbit$\n" + " if ($name$_ == NULL) {\n" + " if (&$type_default_instance$ == NULL) {\n" + " $name$_ = ::google::protobuf::Arena::CreateMessage<\n" + " ::google::protobuf::internal::ImplicitWeakMessage>(\n" + " GetArenaNoVirtual());\n" + " } else {\n" + " $name$_ = reinterpret_cast<const google::protobuf::MessageLite*>(\n" + " &$type_default_instance$)->New(GetArenaNoVirtual());\n" + " }\n" + " }\n" + " return $name$_;\n"); + } else { + printer->Print(variables_, + "void $classname$::_slow_mutable_$name$() {\n"); if (SupportsArenas(descriptor_->message_type())) { printer->Print(variables_, " $name$_ = ::google::protobuf::Arena::CreateMessage< $type$ >(\n" @@ -174,23 +262,27 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( " $name$_ = ::google::protobuf::Arena::Create< $type$ >(\n" " GetArenaNoVirtual());\n"); } + } printer->Print(variables_, "}\n" "$type$* $classname$::_slow_$release_name$() {\n" " if ($name$_ == NULL) {\n" " return NULL;\n" - " } else {\n" - " $type$* temp = new $type$(*$name$_);\n" - " $name$_ = NULL;\n" - " return temp;\n" + " } else {\n"); + if (implicit_weak_field_) { + printer->Print(variables_, + " google::protobuf::MessageLite* temp = $name$_->New();\n" + " temp->CheckTypeAndMergeFrom(*$name$_);\n"); + } else { + printer->Print(variables_, + " $type$* temp = new $type$(*$name$_);\n"); + } + printer->Print(variables_, " $name$_ = NULL;\n"); + printer->Print( + " return $result$;\n", "result", + StaticCast(variables_.at("type") + "*", "temp", implicit_weak_field_)); + printer->Print(variables_, " }\n" - "}\n" - "$type$* $classname$::unsafe_arena_release_$name$() {\n" - " // @@protoc_insertion_point(field_unsafe_arena_release:$full_name$)\n" - " $clear_hasbit$\n" - " $type$* temp = $name$_;\n" - " $name$_ = NULL;\n" - " return temp;\n" "}\n"); if (SupportsArenas(descriptor_->message_type())) { // NOTE: the same logic is mirrored in weak_message_field.cc. Any @@ -202,12 +294,23 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( " ::google::protobuf::Arena::GetArena(*$name$) == NULL) {\n" " message_arena->Own(*$name$);\n" " } else if (message_arena !=\n" - " ::google::protobuf::Arena::GetArena(*$name$)) {\n" - " $type$* new_$name$ = \n" - " ::google::protobuf::Arena::CreateMessage< $type$ >(\n" - " message_arena);\n" - " new_$name$->CopyFrom(**$name$);\n" - " *$name$ = new_$name$;\n" + " ::google::protobuf::Arena::GetArena(*$name$)) {\n"); + if (implicit_weak_field_) { + printer->Print(variables_, + " google::protobuf::MessageLite* new_$name$ =\n" + " reinterpret_cast<const google::protobuf::MessageLite*>(\n" + " &$type_default_instance$)->New(GetArenaNoVirtual());\n" + " new_$name$->CheckTypeAndMergeFrom(**$name$);\n" + " *$name$ = static_cast< $type$* >(new_$name$);\n"); + } else { + printer->Print(variables_, + " $type$* new_$name$ =\n" + " ::google::protobuf::Arena::CreateMessage< $type$ >(\n" + " message_arena);\n" + " new_$name$->CopyFrom(**$name$);\n" + " *$name$ = new_$name$;\n"); + } + printer->Print(variables_, " }\n" "}\n"); } @@ -228,6 +331,20 @@ void MessageFieldGenerator::GenerateNonInlineAccessorDefinitions( " // @@protoc_insertion_point(field_unsafe_arena_set_allocated" ":$full_name$)\n" "}\n"); + } else if (implicit_weak_field_) { + printer->Print(variables_, + "google::protobuf::MessageLite* $classname$::_internal_mutable_$name$() {\n" + " $set_hasbit$\n" + " if ($name$_ == NULL) {\n" + " if (&$type_default_instance$ == NULL) {\n" + " $name$_ = new ::google::protobuf::internal::ImplicitWeakMessage;\n" + " } else {\n" + " $name$_ = reinterpret_cast<const google::protobuf::MessageLite*>(\n" + " &$type_default_instance$)->New();\n" + " }\n" + " }\n" + " return $name$_;\n" + "}\n"); } } @@ -243,6 +360,10 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { variables["dependent_classname"] = DependentBaseClassTemplateName(descriptor_->containing_type()) + "<T>"; variables["this_message"] = DependentBaseDownCast(); + variables["casted_reference"] = + ReinterpretCast(variables["dependent_typename"] + "*&", + variables["this_message"] + variables["name"] + "_", + implicit_weak_field_); if (!variables["set_hasbit"].empty()) { variables["set_hasbit"] = variables["this_message"] + variables["set_hasbit"]; @@ -255,19 +376,39 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { if (SupportsArenas(descriptor_)) { printer->Print(variables, "template <class T>\n" - "inline $type$* $dependent_classname$::mutable_$name$() {\n" + "inline $type$* $dependent_classname$::mutable_$name$() {\n"); + if (implicit_weak_field_) { + printer->Print(variables, " $type_reference_function$();\n"); + } + printer->Print(variables, " $set_hasbit$\n" - " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" - " if ($name$_ == NULL) {\n" - " $this_message$_slow_mutable_$name$();\n" + " $dependent_typename$*& $name$_ = $casted_reference$;\n" + " if ($name$_ == NULL) {\n"); + if (implicit_weak_field_) { + if (SupportsArenas(descriptor_->message_type())) { + printer->Print(variables, + " $name$_ = reinterpret_cast<$dependent_typename$*>(\n" + " reinterpret_cast<const google::protobuf::MessageLite*>(\n" + " &$type_default_instance$)->New(\n" + " $this_message$GetArenaNoVirtual()));\n"); + } + } else { + printer->Print(variables, + " $this_message$_slow_mutable_$name$();\n"); + } + printer->Print(variables, " }\n" " // @@protoc_insertion_point(field_mutable:$full_name$)\n" " return $name$_;\n" "}\n" "template <class T>\n" "inline $type$* $dependent_classname$::$release_name$() {\n" - " // @@protoc_insertion_point(field_release:$full_name$)\n" - " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n"); + if (implicit_weak_field_) { + printer->Print(variables, " $type_reference_function$();\n"); + } + printer->Print(variables, + " $dependent_typename$*& $name$_ = $casted_reference$;\n" " $clear_hasbit$\n" " if ($this_message$GetArenaNoVirtual() != NULL) {\n" " return $this_message$_slow_$release_name$();\n" @@ -281,7 +422,7 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { "inline void $dependent_classname$::" "set_allocated_$name$($type$* $name$) {\n" " ::google::protobuf::Arena* message_arena = $this_message$GetArenaNoVirtual();\n" - " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" + " $dependent_typename$*& $name$_ = $casted_reference$;\n" " if (message_arena == NULL) {\n" " delete $name$_;\n" " }\n" @@ -311,13 +452,27 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { " }\n" // TODO(dlj): move insertion points to message class. " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n" + "template <class T>\n" + "inline $type$* $dependent_classname$::unsafe_arena_release_$name$() {\n" + " // @@protoc_insertion_point(" + "field_unsafe_arena_release:$full_name$)\n"); + if (implicit_weak_field_) { + printer->Print(variables, " $type_reference_function$();\n"); + } + printer->Print(variables, + " $clear_hasbit$\n" + " $dependent_typename$*& $name$_ = $casted_reference$;\n" + " $dependent_typename$* temp = $name$_;\n" + " $name$_ = NULL;\n" + " return temp;\n" "}\n"); } else { printer->Print(variables, "template <class T>\n" "inline $type$* $dependent_classname$::mutable_$name$() {\n" " $set_hasbit$\n" - " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" + " $dependent_typename$*& $name$_ = $casted_reference$;\n" " if ($name$_ == NULL) {\n" " $name$_ = new $dependent_typename$;\n" " }\n" @@ -326,9 +481,13 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { "}\n" "template <class T>\n" "inline $type$* $dependent_classname$::$release_name$() {\n" - " // @@protoc_insertion_point(field_release:$full_name$)\n" + " // @@protoc_insertion_point(field_release:$full_name$)\n"); + if (implicit_weak_field_) { + printer->Print(variables, " $type_reference_function$();\n"); + } + printer->Print(variables, " $clear_hasbit$\n" - " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" + " $dependent_typename$*& $name$_ = $casted_reference$;\n" " $dependent_typename$* temp = $name$_;\n" " $name$_ = NULL;\n" " return temp;\n" @@ -336,7 +495,7 @@ GenerateDependentInlineAccessorDefinitions(io::Printer* printer) const { "template <class T>\n" "inline void $dependent_classname$::" "set_allocated_$name$($type$* $name$) {\n" - " $dependent_typename$*& $name$_ = $this_message$$name$_;\n" + " $dependent_typename$*& $name$_ = $casted_reference$;\n" " delete $name$_;\n"); if (SupportsArenas(descriptor_->message_type())) { @@ -366,9 +525,16 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, bool is_inline) const { std::map<string, string> variables(variables_); variables["inline"] = is_inline ? "inline " : ""; + variables["const_member"] = ReinterpretCast( + "const " + variables["type"] + "*", variables["name"] + "_", + implicit_weak_field_); printer->Print(variables, - "$inline$const $type$& $classname$::$name$() const {\n" - " const $type$* p = $name$_;\n" + "$inline$const $type$& $classname$::$name$() const {\n"); + if (implicit_weak_field_) { + printer->Print(variables, " $type_reference_function$();\n"); + } + printer->Print(variables, + " const $type$* p = $const_member$;\n" " // @@protoc_insertion_point(field_get:$full_name$)\n" " return p != NULL ? *p : *reinterpret_cast<const $type$*>(\n" " &$type_default_instance$);\n" @@ -381,11 +547,18 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, "$inline$" "$type$* $classname$::mutable_$name$() {\n" " $set_hasbit$\n" - " if ($name$_ == NULL) {\n" - " _slow_mutable_$name$();\n" + " if ($name$_ == NULL) {\n"); + if (implicit_weak_field_) { + printer->Print(variables, + " _internal_mutable_$name$();\n"); + } else { + printer->Print(variables, + " _slow_mutable_$name$();\n"); + } + printer->Print(variables, " }\n" " // @@protoc_insertion_point(field_mutable:$full_name$)\n" - " return $name$_;\n" + " return $casted_member$;\n" "}\n" "$inline$" "$type$* $classname$::$release_name$() {\n" @@ -394,7 +567,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " if (GetArenaNoVirtual() != NULL) {\n" " return _slow_$release_name$();\n" " } else {\n" - " $type$* temp = $name$_;\n" + " $type$* temp = $casted_member$;\n" " $name$_ = NULL;\n" " return temp;\n" " }\n" @@ -429,6 +602,19 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " $clear_hasbit$\n" " }\n" " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" + "}\n" + "$inline$" + "$type$* $classname$::unsafe_arena_release_$name$() {\n" + " // @@protoc_insertion_point(" + "field_unsafe_arena_release:$full_name$)\n"); + if (implicit_weak_field_) { + printer->Print(variables, " $type_reference_function$();\n"); + } + printer->Print(variables, + " $clear_hasbit$\n" + " $type$* temp = $casted_member$;\n" + " $name$_ = NULL;\n" + " return temp;\n" "}\n"); } else { printer->Print(variables, @@ -439,13 +625,13 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " $name$_ = new $type$;\n" " }\n" " // @@protoc_insertion_point(field_mutable:$full_name$)\n" - " return $name$_;\n" + " return $casted_member$;\n" "}\n" "$inline$" "$type$* $classname$::$release_name$() {\n" " // @@protoc_insertion_point(field_release:$full_name$)\n" " $clear_hasbit$\n" - " $type$* temp = $name$_;\n" + " $type$* temp = $casted_member$;\n" " $name$_ = NULL;\n" " return temp;\n" "}\n" @@ -485,6 +671,9 @@ GenerateClearingCode(io::Printer* printer) const { "if ($this_message$GetArenaNoVirtual() == NULL && " "$this_message$$name$_ != NULL) delete $this_message$$name$_;\n" "$this_message$$name$_ = NULL;\n"); + } else if (implicit_weak_field_) { + printer->Print(variables, + "if ($this_message$$name$_ != NULL) $this_message$$name$_->Clear();\n"); } else { printer->Print(variables, "if ($this_message$$name$_ != NULL) $this_message$$name$_->" @@ -502,6 +691,10 @@ GenerateMessageClearingCode(io::Printer* printer) const { " delete $name$_;\n" "}\n" "$name$_ = NULL;\n"); + } else if (implicit_weak_field_) { + printer->Print(variables_, + "GOOGLE_DCHECK($name$_ != NULL);\n" + "$name$_->Clear();\n"); } else { printer->Print(variables_, "GOOGLE_DCHECK($name$_ != NULL);\n" @@ -511,8 +704,14 @@ GenerateMessageClearingCode(io::Printer* printer) const { void MessageFieldGenerator:: GenerateMergingCode(io::Printer* printer) const { - printer->Print(variables_, - "mutable_$name$()->$type$::MergeFrom(from.$name$());\n"); + if (implicit_weak_field_) { + printer->Print(variables_, + "_internal_mutable_$name$()->CheckTypeAndMergeFrom(\n" + " from._internal_$name$());\n"); + } else { + printer->Print(variables_, + "mutable_$name$()->$type$::MergeFrom(from.$name$());\n"); + } } void MessageFieldGenerator:: @@ -557,17 +756,24 @@ GenerateCopyConstructorCode(io::Printer* printer) const { // wasn't copied, so both of these methods allocate the submessage on the // heap. - printer->Print(variables_, - "if (from.has_$name$()) {\n" - " $name$_ = new $type$(*from.$name$_);\n" - "} else {\n" - " $name$_ = NULL;\n" - "}\n"); + string new_expression = (implicit_weak_field_ ? "from.$name$_->New()" + : "new $type$(*from.$name$_)"); + string output = + "if (from.has_$name$()) {\n" + " $name$_ = " + new_expression + ";\n" + "} else {\n" + " $name$_ = NULL;\n" + "}\n"; + printer->Print(variables_, output.c_str()); } void MessageFieldGenerator:: GenerateMergeFromCodedStream(io::Printer* printer) const { - if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { + if (implicit_weak_field_) { + printer->Print(variables_, + "DO_(::google::protobuf::internal::WireFormatLite::ReadMessage(\n" + " input, _internal_mutable_$name$()));\n"); + } else if (descriptor_->type() == FieldDescriptor::TYPE_MESSAGE) { printer->Print(variables_, "DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(\n" " input, mutable_$name$()));\n"); @@ -595,9 +801,11 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { void MessageFieldGenerator:: GenerateByteSize(io::Printer* printer) const { - printer->Print(variables_, + std::map<string, string> variables = variables_; + variables["no_virtual"] = (implicit_weak_field_ ? "" : "NoVirtual"); + printer->Print(variables, "total_size += $tag_size$ +\n" - " ::google::protobuf::internal::WireFormatLite::$declared_type$SizeNoVirtual(\n" + " ::google::protobuf::internal::WireFormatLite::$declared_type$Size$no_virtual$(\n" " *$non_null_ptr_to_name$);\n"); } @@ -771,13 +979,15 @@ void MessageOneofFieldGenerator::InternalGenerateInlineAccessorDefinitions( " }\n" " // @@protoc_insertion_point(field_set_allocated:$full_name$)\n" "}\n" - "$inline$ $type$* $classname$::unsafe_arena_release_$name$() {\n" + "$tmpl$" + "$inline$" + "$type$* $dependent_classname$::unsafe_arena_release_$name$() {\n" " // @@protoc_insertion_point(field_unsafe_arena_release" ":$full_name$)\n" - " if (has_$name$()) {\n" - " clear_has_$oneof_name$();\n" - " $type$* temp = $oneof_prefix$$name$_;\n" - " $oneof_prefix$$name$_ = NULL;\n" + " if ($this_message$has_$name$()) {\n" + " $this_message$clear_has_$oneof_name$();\n" + " $dependent_typename$* temp = $this_message$$oneof_prefix$$name$_;\n" + " $this_message$$oneof_prefix$$name$_ = NULL;\n" " return temp;\n" " } else {\n" " return NULL;\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index cd9737f0..14698992 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -44,6 +44,8 @@ namespace protobuf { namespace compiler { namespace cpp { +bool IsImplicitWeakField(const FieldDescriptor* field, const Options& options); + class MessageFieldGenerator : public FieldGenerator { public: MessageFieldGenerator(const FieldDescriptor* descriptor, @@ -78,6 +80,7 @@ class MessageFieldGenerator : public FieldGenerator { const FieldDescriptor* descriptor_; const bool dependent_field_; + const bool implicit_weak_field_; std::map<string, string> variables_; private: diff --git a/src/google/protobuf/compiler/cpp/cpp_message_layout_helper.h b/src/google/protobuf/compiler/cpp/cpp_message_layout_helper.h new file mode 100644 index 00000000..d502a6f0 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_message_layout_helper.h @@ -0,0 +1,61 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: seongkim@google.com (Seong Beom Kim) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_LAYOUT_HELPER_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_LAYOUT_HELPER_H__ + +#include <google/protobuf/compiler/cpp/cpp_options.h> +#include <google/protobuf/descriptor.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// Provides an abstract interface to optimize message layout +// by rearranging the fields of a message. +class MessageLayoutHelper { + public: + virtual ~MessageLayoutHelper() {} + + virtual void OptimizeLayout(std::vector<const FieldDescriptor*>* fields, + const Options& options) = 0; +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_MESSAGE_LAYOUT_HELPER_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_options.h b/src/google/protobuf/compiler/cpp/cpp_options.h index 04338083..4a29ad0e 100644 --- a/src/google/protobuf/compiler/cpp/cpp_options.h +++ b/src/google/protobuf/compiler/cpp/cpp_options.h @@ -39,6 +39,8 @@ namespace google { namespace protobuf { namespace compiler { +class AccessInfoMap; + namespace cpp { // Generator options (see generator.cc for a description of each): @@ -50,7 +52,9 @@ struct Options { annotate_headers(false), enforce_lite(false), table_driven_parsing(false), - table_driven_serialization(false) {} + table_driven_serialization(false), + lite_implicit_weak_fields(false), + access_info_map(NULL) {} string dllexport_decl; bool safe_boundary_check; @@ -60,8 +64,10 @@ struct Options { bool enforce_lite; bool table_driven_parsing; bool table_driven_serialization; + bool lite_implicit_weak_fields; string annotation_pragma_name; string annotation_guard_name; + const AccessInfoMap* access_info_map; }; } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc b/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc new file mode 100644 index 00000000..e9303865 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc @@ -0,0 +1,220 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include <google/protobuf/compiler/cpp/cpp_padding_optimizer.h> + +#include <google/protobuf/compiler/cpp/cpp_helpers.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +namespace { + +// FieldGroup is just a helper for PaddingOptimizer below. It holds a vector of +// fields that are grouped together because they have compatible alignment, and +// a preferred location in the final field ordering. +class FieldGroup { + public: + FieldGroup() : preferred_location_(0) {} + + // A group with a single field. + FieldGroup(float preferred_location, const FieldDescriptor* field) + : preferred_location_(preferred_location), fields_(1, field) {} + + // Append the fields in 'other' to this group. + void Append(const FieldGroup& other) { + if (other.fields_.empty()) { + return; + } + // Preferred location is the average among all the fields, so we weight by + // the number of fields on each FieldGroup object. + preferred_location_ = (preferred_location_ * fields_.size() + + (other.preferred_location_ * other.fields_.size())) / + (fields_.size() + other.fields_.size()); + fields_.insert(fields_.end(), other.fields_.begin(), other.fields_.end()); + } + + void SetPreferredLocation(float location) { preferred_location_ = location; } + const std::vector<const FieldDescriptor*>& fields() const { return fields_; } + + // FieldGroup objects sort by their preferred location. + bool operator<(const FieldGroup& other) const { + return preferred_location_ < other.preferred_location_; + } + + private: + // "preferred_location_" is an estimate of where this group should go in the + // final list of fields. We compute this by taking the average index of each + // field in this group in the original ordering of fields. This is very + // approximate, but should put this group close to where its member fields + // originally went. + float preferred_location_; + std::vector<const FieldDescriptor*> fields_; + // We rely on the default copy constructor and operator= so this type can be + // used in a vector. +}; + +} // namespace + +// Reorder 'fields' so that if the fields are output into a c++ class in the new +// order, fields of similar family (see below) are together and within each +// family, alignment padding is minimized. +// +// We try to do this while keeping each field as close as possible to its field +// number order so that we don't reduce cache locality much for function that +// access each field in order. Originally, OptimizePadding used declaration +// order for its decisions, but generated code minus the serializer/parsers uses +// the output of OptimizePadding as well (stored in +// MessageGenerator::optimized_order_). Since the serializers use field number +// order, we use that as a tie-breaker. +// +// We classify each field into a particular "family" of fields, that we perform +// the same operation on in our generated functions. +// +// REPEATED is placed first, as the C++ compiler automatically initializes +// these fields in layout order. +// +// STRING is grouped next, as our Clear/SharedCtor/SharedDtor walks it and +// calls ArenaStringPtr::Destroy on each. +// +// +// MESSAGE is grouped next, as our Clear/SharedDtor code walks it and calls +// delete on each. We initialize these fields with a NULL pointer (see +// MessageFieldGenerator::GenerateConstructorCode), which allows them to be +// memset. +// +// ZERO_INITIALIZABLE is memset in Clear/SharedCtor +// +// OTHER these fields are initialized one-by-one. +void PaddingOptimizer::OptimizeLayout( + std::vector<const FieldDescriptor*>* fields, const Options& options) { + // The sorted numeric order of Family determines the declaration order in the + // memory layout. + enum Family { + REPEATED = 0, + STRING = 1, + MESSAGE = 3, + ZERO_INITIALIZABLE = 4, + OTHER = 5, + kMaxFamily + }; + + // First divide fields into those that align to 1 byte, 4 bytes or 8 bytes. + std::vector<FieldGroup> aligned_to_1[kMaxFamily]; + std::vector<FieldGroup> aligned_to_4[kMaxFamily]; + std::vector<FieldGroup> aligned_to_8[kMaxFamily]; + for (int i = 0; i < fields->size(); ++i) { + const FieldDescriptor* field = (*fields)[i]; + + Family f = OTHER; + if (field->is_repeated()) { + f = REPEATED; + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + f = STRING; + } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + f = MESSAGE; + + } else if (CanInitializeByZeroing(field)) { + f = ZERO_INITIALIZABLE; + } + + const int j = field->number(); + switch (EstimateAlignmentSize(field)) { + case 1: + aligned_to_1[f].push_back(FieldGroup(j, field)); + break; + case 4: + aligned_to_4[f].push_back(FieldGroup(j, field)); + break; + case 8: + aligned_to_8[f].push_back(FieldGroup(j, field)); + break; + default: + GOOGLE_LOG(FATAL) << "Unknown alignment size " << EstimateAlignmentSize(field) + << "for a field " << field->full_name() << "."; + } + } + + // For each family, group fields to optimize padding. + for (int f = 0; f < kMaxFamily; f++) { + // Now group fields aligned to 1 byte into sets of 4, and treat those like a + // single field aligned to 4 bytes. + for (int i = 0; i < aligned_to_1[f].size(); i += 4) { + FieldGroup field_group; + for (int j = i; j < aligned_to_1[f].size() && j < i + 4; ++j) { + field_group.Append(aligned_to_1[f][j]); + } + aligned_to_4[f].push_back(field_group); + } + // Sort by preferred location to keep fields as close to their field number + // order as possible. Using stable_sort ensures that the output is + // consistent across runs. + std::stable_sort(aligned_to_4[f].begin(), aligned_to_4[f].end()); + + // Now group fields aligned to 4 bytes (or the 4-field groups created above) + // into pairs, and treat those like a single field aligned to 8 bytes. + for (int i = 0; i < aligned_to_4[f].size(); i += 2) { + FieldGroup field_group; + for (int j = i; j < aligned_to_4[f].size() && j < i + 2; ++j) { + field_group.Append(aligned_to_4[f][j]); + } + if (i == aligned_to_4[f].size() - 1) { + if (f == OTHER) { + // Move incomplete 4-byte block to the beginning. This is done to + // pair with the (possible) leftover blocks from the + // ZERO_INITIALIZABLE family. + field_group.SetPreferredLocation(-1); + } else { + // Move incomplete 4-byte block to the end. + field_group.SetPreferredLocation(fields->size() + 1); + } + } + aligned_to_8[f].push_back(field_group); + } + // Sort by preferred location. + std::stable_sort(aligned_to_8[f].begin(), aligned_to_8[f].end()); + } + + // Now pull out all the FieldDescriptors in order. + fields->clear(); + for (int f = 0; f < kMaxFamily; ++f) { + for (int i = 0; i < aligned_to_8[f].size(); ++i) { + fields->insert(fields->end(), aligned_to_8[f][i].fields().begin(), + aligned_to_8[f][i].fields().end()); + } + } +} + +} // namespace cpp +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.h b/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.h new file mode 100644 index 00000000..9641ba40 --- /dev/null +++ b/src/google/protobuf/compiler/cpp/cpp_padding_optimizer.h @@ -0,0 +1,64 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: seongkim@google.com (Seong Beom Kim) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. + +#ifndef GOOGLE_PROTOBUF_COMPILER_CPP_PADDING_OPTIMIZER_H__ +#define GOOGLE_PROTOBUF_COMPILER_CPP_PADDING_OPTIMIZER_H__ + +#include <google/protobuf/compiler/cpp/cpp_message_layout_helper.h> + +namespace google { +namespace protobuf { +namespace compiler { +namespace cpp { + +// Rearranges the fields of a message to minimize padding. +// Fields are grouped by the type and the size. +// For example, grouping four boolean fields and one int32 +// field results in zero padding overhead. See OptimizeLayout's +// comment for details. +class PaddingOptimizer : public MessageLayoutHelper { + public: + PaddingOptimizer() {} + ~PaddingOptimizer() {} + + void OptimizeLayout(std::vector<const FieldDescriptor*>* fields, + const Options& options); +}; + +} // namespace cpp +} // namespace compiler +} // namespace protobuf + +} // namespace google +#endif // GOOGLE_PROTOBUF_COMPILER_CPP_PADDING_OPTIMIZER_H__ diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index b05fcc4e..67cfc405 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -245,9 +245,8 @@ GenerateSwappingCode(io::Printer* printer) const { void PrimitiveOneofFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { - printer->Print( - variables_, - "_$classname$_default_instance_.$name$_ = $default$;\n"); + printer->Print(variables_, + "$ns$::_$classname$_default_instance_.$name$_ = $default$;\n"); } void PrimitiveOneofFieldGenerator:: @@ -433,7 +432,7 @@ GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { " ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED,\n" " target);\n" " target = ::google::protobuf::io::CodedOutputStream::WriteVarint32ToArray(\n" - " static_cast< ::google::protobuf::uint32>(\n" + " static_cast< ::google::protobuf::int32>(\n" " _$name$_cached_byte_size_), target);\n" " target = ::google::protobuf::internal::WireFormatLite::\n" " Write$declared_type$NoTagToArray(this->$name$_, target);\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index fec13b6d..8e675751 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -58,8 +58,8 @@ void SetStringVariables(const FieldDescriptor* descriptor, (*variables)["default_variable"] = descriptor->default_value_string().empty() ? "&::google::protobuf::internal::GetEmptyStringAlreadyInited()" - : "&" + (*variables)["classname"] + "::" + default_variable_string + - ".get()"; + : "&" + Namespace(descriptor) + "::" + (*variables)["classname"] + + "::" + default_variable_string + ".get()"; (*variables)["pointer_type"] = descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; (*variables)["null_check"] = "GOOGLE_DCHECK(value != NULL);\n"; @@ -71,6 +71,9 @@ void SetStringVariables(const FieldDescriptor* descriptor, (*variables)["full_name"] = descriptor->full_name(); (*variables)["string_piece"] = "::std::string"; + + (*variables)["lite"] = + HasDescriptorMethods(descriptor->file(), options) ? "" : "Lite"; } } // namespace @@ -79,7 +82,8 @@ void SetStringVariables(const FieldDescriptor* descriptor, StringFieldGenerator::StringFieldGenerator(const FieldDescriptor* descriptor, const Options& options) - : FieldGenerator(options), descriptor_(descriptor) { + : FieldGenerator(options), descriptor_(descriptor), + lite_(!HasDescriptorMethods(descriptor->file(), options)) { SetStringVariables(descriptor, &variables_, options); } @@ -207,13 +211,13 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, "}\n" "$inline$void $classname$::set_$name$(const ::std::string& value) {\n" " $set_hasbit$\n" - " $name$_.Set($default_variable$, value, GetArenaNoVirtual());\n" + " $name$_.Set$lite$($default_variable$, value, GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n" "#if LANG_CXX11\n" "$inline$void $classname$::set_$name$(::std::string&& value) {\n" " $set_hasbit$\n" - " $name$_.Set(\n" + " $name$_.Set$lite$(\n" " $default_variable$, ::std::move(value), GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_rvalue:$full_name$)\n" "}\n" @@ -221,7 +225,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, "$inline$void $classname$::set_$name$(const char* value) {\n" " $null_check$" " $set_hasbit$\n" - " $name$_.Set($default_variable$, $string_piece$(value),\n" + " $name$_.Set$lite$($default_variable$, $string_piece$(value),\n" " GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_char:$full_name$)\n" "}\n" @@ -229,7 +233,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, "void $classname$::set_$name$(const $pointer_type$* value,\n" " size_t size) {\n" " $set_hasbit$\n" - " $name$_.Set($default_variable$, $string_piece$(\n" + " $name$_.Set$lite$($default_variable$, $string_piece$(\n" " reinterpret_cast<const char*>(value), size), " "GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" @@ -453,7 +457,7 @@ GenerateCopyConstructorCode(io::Printer* printer) const { if (SupportsArenas(descriptor_) || descriptor_->containing_oneof() != NULL) { // TODO(gpike): improve this printer->Print(variables_, - "$name$_.Set($default_variable$, from.$name$(),\n" + "$name$_.Set$lite$($default_variable$, from.$name$(),\n" " GetArenaNoVirtual());\n"); } else { printer->Print(variables_, @@ -480,13 +484,14 @@ GenerateDestructorCode(io::Printer* printer) const { void StringFieldGenerator:: GenerateDefaultInstanceAllocator(io::Printer* printer) const { if (!descriptor_->default_value_string().empty()) { - printer->Print(variables_, - "$classname$::$default_variable_name$.DefaultConstruct();\n" - "*$classname$::$default_variable_name$.get_mutable() = " - "::std::string($default$, $default_length$);\n" - "::google::protobuf::internal::OnShutdownDestroyString(\n" - " $classname$::$default_variable_name$.get_mutable());\n" - ); + printer->Print( + variables_, + "$ns$::$classname$::$default_variable_name$.DefaultConstruct();\n" + "*$ns$::$classname$::$default_variable_name$.get_mutable() = " + "::std::string($default$, $default_length$);\n" + "::google::protobuf::internal::OnShutdownDestroyString(\n" + " $ns$::$classname$::$default_variable_name$.get_mutable());\n" + ); } } @@ -572,7 +577,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " set_has_$name$();\n" " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" " }\n" - " $oneof_prefix$$name$_.Set($default_variable$, value,\n" + " $oneof_prefix$$name$_.Set$lite$($default_variable$, value,\n" " GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set:$full_name$)\n" "}\n" @@ -584,7 +589,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " set_has_$name$();\n" " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" " }\n" - " $oneof_prefix$$name$_.Set(\n" + " $oneof_prefix$$name$_.Set$lite$(\n" " $default_variable$, ::std::move(value), GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_rvalue:$full_name$)\n" "}\n" @@ -596,7 +601,7 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " set_has_$name$();\n" " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" " }\n" - " $oneof_prefix$$name$_.Set($default_variable$,\n" + " $oneof_prefix$$name$_.Set$lite$($default_variable$,\n" " $string_piece$(value), GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_char:$full_name$)\n" "}\n" @@ -608,7 +613,8 @@ GenerateInlineAccessorDefinitions(io::Printer* printer, " set_has_$name$();\n" " $oneof_prefix$$name$_.UnsafeSetDefault($default_variable$);\n" " }\n" - " $oneof_prefix$$name$_.Set($default_variable$, $string_piece$(\n" + " $oneof_prefix$$name$_.Set$lite$(\n" + " $default_variable$, $string_piece$(\n" " reinterpret_cast<const char*>(value), size),\n" " GetArenaNoVirtual());\n" " // @@protoc_insertion_point(field_set_pointer:$full_name$)\n" @@ -806,7 +812,7 @@ void StringOneofFieldGenerator:: GenerateConstructorCode(io::Printer* printer) const { printer->Print( variables_, - "_$classname$_default_instance_.$name$_.UnsafeSetDefault(\n" + "$ns$::_$classname$_default_instance_.$name$_.UnsafeSetDefault(\n" " $default_variable$);\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h index 531252b0..933f3c6b 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -73,6 +73,7 @@ class StringFieldGenerator : public FieldGenerator { protected: const FieldDescriptor* descriptor_; std::map<string, string> variables_; + const bool lite_; private: GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(StringFieldGenerator); diff --git a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto index 4e25b2ea..7fe98759 100644 --- a/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto +++ b/src/google/protobuf/compiler/cpp/cpp_test_bad_identifiers.proto @@ -151,6 +151,10 @@ enum ConflictingEnum { // NO_PROTO3 message DummyMessage {} +// Message names that could conflict. +message Shutdown {} +message TableStruct {} + service TestConflictingMethodNames { rpc Closure(DummyMessage) returns (DummyMessage); } diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index fdde771b..61cc32a4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -744,21 +744,6 @@ TEST(GeneratedMessageTest, NonEmptyMergeFrom) { TestUtil::ExpectAllFieldsSet(message1); } -#if !defined(PROTOBUF_TEST_NO_DESCRIPTORS) || \ - !defined(GOOGLE_PROTOBUF_NO_RTTI) -#ifdef PROTOBUF_HAS_DEATH_TEST -#ifndef NDEBUG - -TEST(GeneratedMessageTest, MergeFromSelf) { - unittest::TestAllTypes message; - EXPECT_DEATH(message.MergeFrom(message), "pb[.]cc.*Check failed:"); - EXPECT_DEATH(message.MergeFrom(implicit_cast<const Message&>(message)), - "pb[.]cc.*Check failed:"); -} - -#endif // NDEBUG -#endif // PROTOBUF_HAS_DEATH_TEST -#endif // !PROTOBUF_TEST_NO_DESCRIPTORS || !GOOGLE_PROTOBUF_NO_RTTI // Test the generated SerializeWithCachedSizesToArray(), TEST(GeneratedMessageTest, SerializationToArray) { |