diff options
author | kenton@google.com <kenton@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2009-04-25 02:53:47 +0000 |
---|---|---|
committer | kenton@google.com <kenton@google.com@630680e5-0e50-0410-840e-4b1c322b438d> | 2009-04-25 02:53:47 +0000 |
commit | d37d46dfbcedadeb439ad0367f8afcf8867dca43 (patch) | |
tree | b896df229f7c671637924c156d5a759ba50a3190 /src/google/protobuf/compiler | |
parent | 709ea28f3264aa5632e5577a4080671173fc6166 (diff) | |
download | protobuf-d37d46dfbcedadeb439ad0367f8afcf8867dca43.tar.gz protobuf-d37d46dfbcedadeb439ad0367f8afcf8867dca43.tar.bz2 protobuf-d37d46dfbcedadeb439ad0367f8afcf8867dca43.zip |
Integrate recent changes from Google-internal code tree. See CHANGES.txt
for details.
Diffstat (limited to 'src/google/protobuf/compiler')
28 files changed, 1281 insertions, 492 deletions
diff --git a/src/google/protobuf/compiler/cpp/cpp_enum.cc b/src/google/protobuf/compiler/cpp/cpp_enum.cc index 77e50332..875cbef8 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum.cc @@ -100,6 +100,19 @@ void EnumGenerator::GenerateDefinition(io::Printer* printer) { "const $classname$ $prefix$$short_name$_MIN = $prefix$$min_name$;\n" "const $classname$ $prefix$$short_name$_MAX = $prefix$$max_name$;\n" "\n"); + + // The _Name and _Parse methods + printer->Print(vars, + "inline const ::std::string& $classname$_Name($classname$ value) {\n" + " return ::google::protobuf::internal::NameOfEnum(\n" + " $classname$_descriptor(), value);\n" + "}\n"); + printer->Print(vars, + "inline bool $classname$_Parse(\n" + " const ::std::string& name, $classname$* value) {\n" + " return ::google::protobuf::internal::ParseNamedEnum<$classname$>(\n" + " $classname$_descriptor(), name, value);\n" + "}\n"); } void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { @@ -122,6 +135,13 @@ void EnumGenerator::GenerateSymbolImports(io::Printer* printer) { "static inline bool $nested_name$_IsValid(int value) {\n" " return $classname$_IsValid(value);\n" "}\n" + "static inline const ::std::string& $nested_name$_Name($nested_name$ value) {\n" + " return $classname$_Name(value);\n" + "}\n" + "static inline bool $nested_name$_Parse(const ::std::string& name,\n" + " $nested_name$* value) {\n" + " return $classname$_Parse(name, value);\n" + "}\n" "static const $nested_name$ $nested_name$_MIN =\n" " $classname$_$nested_name$_MIN;\n" "static const $nested_name$ $nested_name$_MAX =\n" @@ -147,12 +167,10 @@ void EnumGenerator::GenerateDescriptorInitializer( void EnumGenerator::GenerateMethods(io::Printer* printer) { map<string, string> vars; vars["classname"] = classname_; - vars["builddescriptorsname"] = - GlobalBuildDescriptorsName(descriptor_->file()->name()); printer->Print(vars, "const ::google::protobuf::EnumDescriptor* $classname$_descriptor() {\n" - " if ($classname$_descriptor_ == NULL) $builddescriptorsname$();\n" + " protobuf_AssignDescriptorsOnce();\n" " return $classname$_descriptor_;\n" "}\n" "bool $classname$_IsValid(int value) {\n" diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc index a78bf887..b90eb372 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.cc @@ -116,8 +116,8 @@ GenerateSwappingCode(io::Printer* printer) const { } void EnumFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { - printer->Print(variables_, ",\n$name$_($default$)"); +GenerateConstructorCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); } void EnumFieldGenerator:: @@ -128,15 +128,22 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "if ($type$_IsValid(value)) {\n" " set_$name$(static_cast< $type$ >(value));\n" "} else {\n" - " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" + " mutable_unknown_fields()->AddVarint($number$, value);\n" "}\n"); } void EnumFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::WriteEnum(" - "$number$, this->$name$(), output));\n"); + "::google::protobuf::internal::WireFormat::WriteEnum(" + "$number$, this->$name$(), output);\n"); +} + +void EnumFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + printer->Print(variables_, + "target = ::google::protobuf::internal::WireFormat::WriteEnumToArray(" + "$number$, this->$name$(), target);\n"); } void EnumFieldGenerator:: @@ -217,12 +224,8 @@ GenerateSwappingCode(io::Printer* printer) const { } void RepeatedEnumFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { - printer->Print(variables_, ",\n$name$_()"); - if (descriptor_->options().packed() && - descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { - printer->Print(variables_, ",\n_$name$_cached_byte_size_()"); - } +GenerateConstructorCode(io::Printer* printer) const { + // Not needed for repeated fields. } void RepeatedEnumFieldGenerator:: @@ -248,7 +251,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { "if ($type$_IsValid(value)) {\n" " add_$name$(static_cast< $type$ >(value));\n" "} else {\n" - " mutable_unknown_fields()->AddField($number$)->add_varint(value);\n" + " mutable_unknown_fields()->AddVarint($number$, value);\n" "}\n"); } } @@ -259,22 +262,51 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" - " DO_(::google::protobuf::internal::WireFormat::WriteTag(" - "$number$, ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED," - "output));\n" - " DO_(output->WriteVarint32(_$name$_cached_byte_size_));\n" + " ::google::protobuf::internal::WireFormat::WriteTag(" + "$number$, " + "::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED, " + "output);\n" + " output->WriteVarint32(_$name$_cached_byte_size_);\n" + "}\n"); + } + printer->Print(variables_, + "for (int i = 0; i < this->$name$_size(); i++) {\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + " ::google::protobuf::internal::WireFormat::WriteEnumNoTag(" + "this->$name$(i), output);\n"); + } else { + printer->Print(variables_, + " ::google::protobuf::internal::WireFormat::WriteEnum(" + "$number$, this->$name$(i), output);\n"); + } + printer->Print("}\n"); +} + +void RepeatedEnumFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + if (descriptor_->options().packed()) { + // Write the tag and the size. + printer->Print(variables_, + "if (this->$name$_size() > 0) {\n" + " target = ::google::protobuf::internal::WireFormat::WriteTagToArray(" + "$number$, " + "::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED, " + "target);\n" + " target = ::google::protobuf::io::CodedOutputStream::WriteVarint32ToArray(" + "_$name$_cached_byte_size_, target);\n" "}\n"); } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); if (descriptor_->options().packed()) { printer->Print(variables_, - " DO_(::google::protobuf::internal::WireFormat::WriteEnumNoTag(" - "this->$name$(i), output));\n"); + " target = ::google::protobuf::internal::WireFormat::WriteEnumNoTagToArray(" + "this->$name$(i), target);\n"); } else { printer->Print(variables_, - " DO_(::google::protobuf::internal::WireFormat::WriteEnum(" - "$number$, this->$name$(i), output));\n"); + " target = ::google::protobuf::internal::WireFormat::WriteEnumToArray(" + "$number$, this->$name$(i), target);\n"); } printer->Print("}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_enum_field.h b/src/google/protobuf/compiler/cpp/cpp_enum_field.h index f67b7ac0..20dd57bb 100644 --- a/src/google/protobuf/compiler/cpp/cpp_enum_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_enum_field.h @@ -56,9 +56,10 @@ class EnumFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: @@ -80,9 +81,10 @@ class RepeatedEnumFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.cc b/src/google/protobuf/compiler/cpp/cpp_extension.cc index 3f212b93..61ebda73 100644 --- a/src/google/protobuf/compiler/cpp/cpp_extension.cc +++ b/src/google/protobuf/compiler/cpp/cpp_extension.cc @@ -36,6 +36,7 @@ #include <google/protobuf/compiler/cpp/cpp_helpers.h> #include <google/protobuf/stubs/strutil.h> #include <google/protobuf/io/printer.h> +#include <google/protobuf/descriptor.pb.h> namespace google { namespace protobuf { @@ -55,7 +56,9 @@ ExtensionGenerator::ExtensionGenerator(const FieldDescriptor* descriptor, case FieldDescriptor::CPPTYPE_ENUM: type_traits_.append("EnumTypeTraits< "); type_traits_.append(ClassName(descriptor_->enum_type(), true)); - type_traits_.append(" >"); + type_traits_.append(", "); + type_traits_.append(ClassName(descriptor_->enum_type(), true)); + type_traits_.append("_IsValid>"); break; case FieldDescriptor::CPPTYPE_STRING: type_traits_.append("StringTypeTraits"); @@ -81,6 +84,8 @@ void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) { vars["number" ] = SimpleItoa(descriptor_->number()); vars["type_traits" ] = type_traits_; vars["name" ] = descriptor_->name(); + vars["field_type" ] = SimpleItoa(static_cast<int>(descriptor_->type())); + vars["packed" ] = descriptor_->options().packed() ? "true" : "false"; vars["constant_name"] = FieldConstantName(descriptor_); // If this is a class member, it needs to be declared "static". Otherwise, @@ -95,19 +100,39 @@ void ExtensionGenerator::GenerateDeclaration(io::Printer* printer) { printer->Print(vars, "static const int $constant_name$ = $number$;\n" "$qualifier$ ::google::protobuf::internal::ExtensionIdentifier< $extendee$,\n" - " ::google::protobuf::internal::$type_traits$ > $name$;\n"); + " ::google::protobuf::internal::$type_traits$, $field_type$, $packed$ >\n" + " $name$;\n" + ); } void ExtensionGenerator::GenerateDefinition(io::Printer* printer) { + // If this is a class member, it needs to be declared in its class scope. + string scope = (descriptor_->extension_scope() == NULL) ? "" : + ClassName(descriptor_->extension_scope(), false) + "::"; + string name = scope + descriptor_->name(); + map<string, string> vars; vars["extendee" ] = ClassName(descriptor_->containing_type(), true); vars["type_traits" ] = type_traits_; - vars["name" ] = descriptor_->name(); + vars["name" ] = name; vars["constant_name"] = FieldConstantName(descriptor_); - - // If this is a class member, it needs to be declared in its class scope. - vars["scope"] = (descriptor_->extension_scope() == NULL) ? "" : - ClassName(descriptor_->extension_scope(), false) + "::"; + vars["default" ] = DefaultValue(descriptor_); + vars["field_type" ] = SimpleItoa(static_cast<int>(descriptor_->type())); + vars["packed" ] = descriptor_->options().packed() ? "true" : "false"; + vars["scope" ] = scope; + + if (descriptor_->cpp_type() == FieldDescriptor::CPPTYPE_STRING) { + // We need to declare a global string which will contain the default value. + // We cannot declare it at class scope because that would require exposing + // it in the header which would be annoying for other reasons. So we + // replace :: with _ in the name and declare it as a global. + string global_name = StringReplace(name, "::", "_", true); + vars["global_name"] = global_name; + printer->Print(vars, + "const ::std::string $global_name$_default($default$);\n"); + // Update the default to refer to the string global. + vars["default"] = global_name + "_default"; + } // Likewise, class members need to declare the field constant variable. if (descriptor_->extension_scope() != NULL) { @@ -119,8 +144,46 @@ void ExtensionGenerator::GenerateDefinition(io::Printer* printer) { printer->Print(vars, "::google::protobuf::internal::ExtensionIdentifier< $extendee$,\n" - " ::google::protobuf::internal::$type_traits$ > $scope$$name$(" - "$constant_name$);\n"); + " ::google::protobuf::internal::$type_traits$, $field_type$, $packed$ >\n" + " $name$($constant_name$, $default$);\n"); +} + +void ExtensionGenerator::GenerateRegistration(io::Printer* printer) { + map<string, string> vars; + vars["extendee" ] = ClassName(descriptor_->containing_type(), true); + 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$::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$::default_instance(),\n" + " $number$, $field_type$, $is_repeated$, $is_packed$,\n"); + printer->Print( + " &$type$::default_instance());\n", + "type", ClassName(descriptor_->message_type(), true)); + break; + default: + printer->Print(vars, + "::google::protobuf::internal::ExtensionSet::RegisterExtension(\n" + " &$extendee$::default_instance(),\n" + " $number$, $field_type$, $is_repeated$, $is_packed$);\n"); + break; + } } } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_extension.h b/src/google/protobuf/compiler/cpp/cpp_extension.h index a9e58c18..3068b091 100644 --- a/src/google/protobuf/compiler/cpp/cpp_extension.h +++ b/src/google/protobuf/compiler/cpp/cpp_extension.h @@ -66,6 +66,9 @@ 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.h b/src/google/protobuf/compiler/cpp/cpp_field.h index e5f8258f..7e7c7f83 100644 --- a/src/google/protobuf/compiler/cpp/cpp_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_field.h @@ -94,16 +94,13 @@ class FieldGenerator { // message.cc under the GenerateSwap method. virtual void GenerateSwappingCode(io::Printer* printer) const = 0; - // Generate any initializers needed for the private members declared by - // GeneratePrivateMembers(). These go into the message class's - // constructor's initializer list. For each initializer, this method - // must print the comma and newline separating it from the *previous* - // initializer, not the *next* initailizer. That is, print a ",\n" first, - // e.g.: - // printer->Print(",\n$name$_($default$)"); - virtual void GenerateInitializer(io::Printer* printer) const = 0; - - // Generate any code that needs to go in the class's destructor. + // Generate initialization code for private members declared by + // GeneratePrivateMembers(). These go into the message class's SharedCtor() + // method, invoked by each of the generated constructors. + virtual void GenerateConstructorCode(io::Printer* printer) const = 0; + + // Generate any code that needs to go in the class's SharedDtor() method, + // invoked by the destructor. // Most field types don't need this, so the default implementation is empty. virtual void GenerateDestructorCode(io::Printer* printer) const {} @@ -115,6 +112,12 @@ class FieldGenerator { // message's SerializeWithCachedSizes() method. virtual void GenerateSerializeWithCachedSizes(io::Printer* printer) const = 0; + // Generate lines to serialize this field directly to the array "target", + // which are placed within the message's SerializeWithCachedSizesToArray() + // method. This must also advance "target" past the written bytes. + virtual void GenerateSerializeWithCachedSizesToArray( + io::Printer* printer) const = 0; + // Generate lines to compute the serialized size of this field, which // are placed in the message's ByteSize() method. virtual void GenerateByteSize(io::Printer* printer) const = 0; diff --git a/src/google/protobuf/compiler/cpp/cpp_file.cc b/src/google/protobuf/compiler/cpp/cpp_file.cc index 6487979f..dcc48552 100644 --- a/src/google/protobuf/compiler/cpp/cpp_file.cc +++ b/src/google/protobuf/compiler/cpp/cpp_file.cc @@ -143,17 +143,20 @@ void FileGenerator::GenerateHeader(io::Printer* printer) { // Open namespace. GenerateNamespaceOpeners(printer); - // Forward-declare the AssignGlobalDescriptors function, so that we can - // declare it to be a friend of each class. + // Forward-declare the AddDescriptors and AssignDescriptors functions, so + // that we can declare them to be friends of each class. printer->Print( "\n" "// Internal implementation detail -- do not call these.\n" - "void $dllexport_decl$ $builddescriptorsname$();\n" - "void $builddescriptorsname$_AssignGlobalDescriptors(\n" - " ::google::protobuf::FileDescriptor* file);\n" - "\n", - "builddescriptorsname", GlobalBuildDescriptorsName(file_->name()), + "void $dllexport_decl$ $adddescriptorsname$();\n", + "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), "dllexport_decl", dllexport_decl_); + printer->Print( + // Note that we don't put dllexport_decl on this because it is only called + // by the .pb.cc file in which it is defined. + "void $assigndescriptorsname$();\n" + "\n", + "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name())); // Generate forward declarations of classes. for (int i = 0; i < file_->message_type_count(); i++) { @@ -232,6 +235,7 @@ void FileGenerator::GenerateSource(io::Printer* printer) { "// Generated by the protocol buffer compiler. DO NOT EDIT!\n" "\n" "#include \"$basename$.pb.h\"\n" + "#include <google/protobuf/stubs/once.h>\n" "#include <google/protobuf/descriptor.h>\n" "#include <google/protobuf/io/coded_stream.h>\n" "#include <google/protobuf/reflection_ops.h>\n" @@ -296,23 +300,46 @@ void FileGenerator::GenerateSource(io::Printer* printer) { } void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { - // BuildDescriptors() is a file-level procedure which initializes all of - // the Descriptor objects for this file. It runs the first time one of the - // descriptors is accessed. This will always be at static initialization - // time, because every message has a statically-initialized default instance, - // and the constructor for a message class accesses its descriptor. See the - // constructor and the descriptor() method of message classes. + // 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 always runs + // at static initialization time, so all files will be registered before + // main() starts. This procedure also constructs default instances and + // registers extensions. // - // We also construct the reflection object for each class inside - // BuildDescriptors(). + // Its sibling, AssignDescriptors(), actually pulls the compiled + // FileDescriptor from the DescriptorPool and uses it to populate all of + // the global variables which store pointers to the descriptor objects. + // It also constructs the reflection objects. It is called the first time + // anyone calls descriptor() or GetReflection() on one of the types defined + // in the file. - // First we generate a method to assign the global descriptors. printer->Print( "\n" - "void $builddescriptorsname$_AssignGlobalDescriptors(" - "const ::google::protobuf::FileDescriptor* file) {\n", - "builddescriptorsname", GlobalBuildDescriptorsName(file_->name())); + "void $assigndescriptorsname$() {\n", + "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name())); printer->Indent(); + + // Make sure the file has found its way into the pool. If a descriptor + // is requested *during* static init then AddDescriptors() may not have + // been called yet, so we call it manually. Note that it's fine if + // AddDescriptors() is called multiple times. + printer->Print( + "$adddescriptorsname$();\n", + "adddescriptorsname", GlobalAddDescriptorsName(file_->name())); + + // Get the file's descriptor from the pool. + printer->Print( + "const ::google::protobuf::FileDescriptor* file =\n" + " ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(\n" + " \"$filename$\");\n" + // Note that this GOOGLE_CHECK is necessary to prevent a warning about "file" + // being unused when compiling an empty .proto file. + "GOOGLE_CHECK(file != NULL);\n", + "filename", file_->name()); + + // Go through all the stuff defined in this file and generated code to + // assign the global descriptor pointers based on the file descriptor. for (int i = 0; i < file_->message_type_count(); i++) { message_generators_[i]->GenerateDescriptorInitializer(printer, i); } @@ -322,29 +349,63 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { for (int i = 0; i < file_->service_count(); i++) { service_generators_[i]->GenerateDescriptorInitializer(printer, i); } + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + // ----------------------------------------------------------------- + + // protobuf_AssignDescriptorsOnce(): The first time it is called, calls + // AssignDescriptors(). All later times, waits for the first call to + // complete and then returns. + printer->Print( + "namespace {\n" + "\n" + "GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);\n" + "inline void protobuf_AssignDescriptorsOnce() {\n" + " ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,\n" + " &$assigndescriptorsname$);\n" + "}\n" + "\n", + "assigndescriptorsname", GlobalAssignDescriptorsName(file_->name())); + + // protobuf_RegisterTypes(): Calls + // MessageFactory::InternalRegisterGeneratedType() for each message type. + printer->Print( + "void protobuf_RegisterTypes() {\n" + " protobuf_AssignDescriptorsOnce();\n"); + printer->Indent(); + for (int i = 0; i < file_->message_type_count(); i++) { - message_generators_[i]->GenerateDefaultInstanceInitializer(printer); + message_generators_[i]->GenerateTypeRegistrations(printer); } printer->Outdent(); printer->Print( - "}\n"); + "}\n" + "\n" + "} // namespace\n"); + // ----------------------------------------------------------------- + + // Now generate the AddDescriptors() function. printer->Print( "\n" - "void $builddescriptorsname$() {\n" + "void $adddescriptorsname$() {\n" + // We don't need any special synchronization here because this code is + // called at static init time before any threads exist. " static bool already_here = false;\n" " if (already_here) return;\n" " already_here = true;\n" " GOOGLE_PROTOBUF_VERIFY_VERSION;\n" - " ::google::protobuf::DescriptorPool* pool =\n" - " ::google::protobuf::DescriptorPool::internal_generated_pool();\n" "\n", - "builddescriptorsname", GlobalBuildDescriptorsName(file_->name())); + "adddescriptorsname", GlobalAddDescriptorsName(file_->name())); printer->Indent(); - // Call the BuildDescriptors() methods for all of our dependencies, to make - // sure they get initialized first. + // 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. @@ -355,10 +416,10 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { printer->Print("$name$::", "name", dependency_package_parts[i]); } - // Call its BuildDescriptors function. + // Call its AddDescriptors function. printer->Print( "$name$();\n", - "name", GlobalBuildDescriptorsName(dependency->name())); + "name", GlobalAddDescriptorsName(dependency->name())); } // Embed the descriptor. We simply serialize the entire FileDescriptorProto @@ -370,7 +431,7 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { file_proto.SerializeToString(&file_data); printer->Print( - "pool->InternalBuildGeneratedFile("); + "::google::protobuf::DescriptorPool::InternalAddGeneratedFile("); // Only write 40 bytes per line. static const int kBytesPerLine = 40; @@ -379,24 +440,41 @@ void FileGenerator::GenerateBuildDescriptors(io::Printer* printer) { "data", CEscape(file_data.substr(i, kBytesPerLine))); } printer->Print( - ", $size$,\n" - "&$builddescriptorsname$_AssignGlobalDescriptors);\n", - "size", SimpleItoa(file_data.size()), - "builddescriptorsname", GlobalBuildDescriptorsName(file_->name())); + ", $size$);\n", + "size", SimpleItoa(file_data.size())); + + // Call MessageFactory::InternalRegisterGeneratedFile(). + printer->Print( + "::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(\n" + " \"$filename$\", &protobuf_RegisterTypes);\n", + "filename", file_->name()); + + // 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 < file_->message_type_count(); i++) { + message_generators_[i]->GenerateDefaultInstanceAllocator(printer); + } + for (int i = 0; i < file_->extension_count(); i++) { + extension_generators_[i]->GenerateRegistration(printer); + } + for (int i = 0; i < file_->message_type_count(); i++) { + message_generators_[i]->GenerateDefaultInstanceInitializer(printer); + } printer->Outdent(); printer->Print( "}\n" "\n" - "// Force BuildDescriptors() to be called at static initialization time.\n" + "// Force AddDescriptors() to be called at static initialization time.\n" "struct StaticDescriptorInitializer_$filename$ {\n" " StaticDescriptorInitializer_$filename$() {\n" - " $builddescriptorsname$();\n" + " $adddescriptorsname$();\n" " }\n" "} static_descriptor_initializer_$filename$_;\n" "\n", - "builddescriptorsname", GlobalBuildDescriptorsName(file_->name()), + "adddescriptorsname", GlobalAddDescriptorsName(file_->name()), "filename", FilenameIdentifier(file_->name())); } diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.cc b/src/google/protobuf/compiler/cpp/cpp_helpers.cc index d536bea4..214daff9 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.cc +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.cc @@ -38,6 +38,7 @@ #include <google/protobuf/compiler/cpp/cpp_helpers.h> #include <google/protobuf/stubs/common.h> #include <google/protobuf/stubs/strutil.h> +#include <google/protobuf/stubs/substitute.h> namespace google { namespace protobuf { @@ -213,6 +214,41 @@ const char* DeclaredTypeMethodName(FieldDescriptor::Type type) { return ""; } +string DefaultValue(const FieldDescriptor* field) { + switch (field->cpp_type()) { + case FieldDescriptor::CPPTYPE_INT32: + return SimpleItoa(field->default_value_int32()); + case FieldDescriptor::CPPTYPE_UINT32: + return SimpleItoa(field->default_value_uint32()) + "u"; + case FieldDescriptor::CPPTYPE_INT64: + return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")"; + case FieldDescriptor::CPPTYPE_UINT64: + return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")"; + case FieldDescriptor::CPPTYPE_DOUBLE: + return SimpleDtoa(field->default_value_double()); + case FieldDescriptor::CPPTYPE_FLOAT: + return SimpleFtoa(field->default_value_float()); + case FieldDescriptor::CPPTYPE_BOOL: + return field->default_value_bool() ? "true" : "false"; + case FieldDescriptor::CPPTYPE_ENUM: + // Lazy: Generate a static_cast because we don't have a helper function + // that constructs the full name of an enum value. + return strings::Substitute( + "static_cast< $0 >($1)", + ClassName(field->enum_type(), true), + field->default_value_enum()->number()); + case FieldDescriptor::CPPTYPE_STRING: + return "\"" + CEscape(field->default_value_string()) + "\""; + case FieldDescriptor::CPPTYPE_MESSAGE: + return ClassName(field->message_type(), true) + "::default_instance()"; + } + // Can't actually get here; make compiler happy. (We could add a default + // case above but then we wouldn't get the nice compiler warning when a + // new type is added.) + GOOGLE_LOG(FATAL) << "Can't get here."; + return ""; +} + // Convert a file name into a valid identifier. string FilenameIdentifier(const string& filename) { string result; @@ -230,9 +266,14 @@ string FilenameIdentifier(const string& filename) { return result; } -// Return the name of the BuildDescriptors() function for a given file. -string GlobalBuildDescriptorsName(const string& filename) { - return "protobuf_BuildDesc_" + FilenameIdentifier(filename); +// Return the name of the AddDescriptors() function for a given file. +string GlobalAddDescriptorsName(const string& filename) { + return "protobuf_AddDesc_" + FilenameIdentifier(filename); +} + +// Return the name of the AssignDescriptors() function for a given file. +string GlobalAssignDescriptorsName(const string& filename) { + return "protobuf_AssignDesc_" + FilenameIdentifier(filename); } } // namespace cpp diff --git a/src/google/protobuf/compiler/cpp/cpp_helpers.h b/src/google/protobuf/compiler/cpp/cpp_helpers.h index 30c6e7d0..2260a934 100644 --- a/src/google/protobuf/compiler/cpp/cpp_helpers.h +++ b/src/google/protobuf/compiler/cpp/cpp_helpers.h @@ -90,11 +90,17 @@ const char* PrimitiveTypeName(FieldDescriptor::CppType type); // methods of WireFormat. For example, TYPE_INT32 becomes "Int32". const char* DeclaredTypeMethodName(FieldDescriptor::Type type); +// Get code that evaluates to the field's default value. +string DefaultValue(const FieldDescriptor* field); + // Convert a file name into a valid identifier. string FilenameIdentifier(const string& filename); -// Return the name of the BuildDescriptors() function for a given file. -string GlobalBuildDescriptorsName(const string& filename); +// Return the name of the AddDescriptors() function for a given file. +string GlobalAddDescriptorsName(const string& filename); + +// Return the name of the AssignDescriptors() function for a given file. +string GlobalAssignDescriptorsName(const string& filename); } // namespace cpp } // namespace compiler diff --git a/src/google/protobuf/compiler/cpp/cpp_message.cc b/src/google/protobuf/compiler/cpp/cpp_message.cc index 2ec49234..44546bd0 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message.cc @@ -223,104 +223,10 @@ GenerateFieldAccessorDeclarations(io::Printer* printer) { } if (descriptor_->extension_range_count() > 0) { - // Generate accessors for extensions. - - // Normally I'd generate prototypes here and generate the actual - // definitions of these methods in GenerateFieldAccessorDefinitions, but - // the prototypes for these silly methods are so absurdly complicated that - // it meant way too much repitition. - // - // We use "_proto_TypeTraits" as a type name below because "TypeTraits" - // causes problems if the class has a nested message or enum type with that - // name and "_TypeTraits" is technically reserved for the C++ library since - // it starts with an underscore followed by a capital letter. + // Generate accessors for extensions. We just call a macro located in + // extension_set.h since the accessors about 80 lines of static code. printer->Print( - // Has, Size, Clear - "template <typename _proto_TypeTraits>\n" - "inline bool HasExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id) const {\n" - " return _extensions_.Has(id.number());\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline void ClearExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id) {\n" - " _extensions_.ClearExtension(id.number());\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline int ExtensionSize(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id) const {\n" - " return _extensions_.ExtensionSize(id.number());\n" - "}\n" - "\n" - - // Singular accessors - "template <typename _proto_TypeTraits>\n" - "inline typename _proto_TypeTraits::ConstType GetExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id) const {\n" - " return _proto_TypeTraits::Get(id.number(), _extensions_);\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline typename _proto_TypeTraits::MutableType MutableExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id) {\n" - " return _proto_TypeTraits::Mutable(id.number(), &_extensions_);\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline void SetExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id,\n" - " typename _proto_TypeTraits::ConstType value) {\n" - " _proto_TypeTraits::Set(id.number(), value, &_extensions_);\n" - "}\n" - "\n" - - // Repeated accessors - "template <typename _proto_TypeTraits>\n" - "inline typename _proto_TypeTraits::ConstType GetExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id,\n" - " int index) const {\n" - " return _proto_TypeTraits::Get(id.number(), _extensions_, index);\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline typename _proto_TypeTraits::MutableType MutableExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id,\n" - " int index) {\n" - " return _proto_TypeTraits::Mutable(id.number(),index,&_extensions_);\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline void SetExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id,\n" - " int index, typename _proto_TypeTraits::ConstType value) {\n" - " _proto_TypeTraits::Set(id.number(), index, value, &_extensions_);\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline typename _proto_TypeTraits::MutableType AddExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id) {\n" - " return _proto_TypeTraits::Add(id.number(), &_extensions_);\n" - "}\n" - "\n" - "template <typename _proto_TypeTraits>\n" - "inline void AddExtension(\n" - " const ::google::protobuf::internal::ExtensionIdentifier<\n" - " $classname$, _proto_TypeTraits>& id,\n" - " typename _proto_TypeTraits::ConstType value) {\n" - " _proto_TypeTraits::Add(id.number(), value, &_extensions_);\n" - "}\n", + "GOOGLE_PROTOBUF_EXTENSION_ACCESSORS($classname$)\n", "classname", classname_); } } @@ -391,8 +297,6 @@ GenerateClassDefinition(io::Printer* printer) { } else { vars["dllexport"] = dllexport_decl_ + " "; } - vars["builddescriptorsname"] = - GlobalBuildDescriptorsName(descriptor_->file()->name()); printer->Print(vars, "class $dllexport$$classname$ : public ::google::protobuf::Message {\n" @@ -433,18 +337,30 @@ GenerateClassDefinition(io::Printer* printer) { "void CopyFrom(const $classname$& from);\n" "void MergeFrom(const $classname$& from);\n" "void Clear();\n" - "bool IsInitialized() const;\n" - "int ByteSize() const;\n" - "\n" - "bool MergePartialFromCodedStream(\n" - " ::google::protobuf::io::CodedInputStream* input);\n" - "bool SerializeWithCachedSizes(\n" - " ::google::protobuf::io::CodedOutputStream* output) const;\n"); + "bool IsInitialized() const;\n"); + + if (!descriptor_->options().message_set_wire_format()) { + // For message_set_wire_format, we don't generate parsing or + // serialization code even if optimize_for = SPEED, since MessageSet + // encoding is somewhat more complicated than normal extension encoding + // and we'd like to avoid having to implement it in multiple places. + // WireFormat's implementation is probably good enough. + printer->Print(vars, + "\n" + "int ByteSize() const;\n" + "bool MergePartialFromCodedStream(\n" + " ::google::protobuf::io::CodedInputStream* input);\n" + "void SerializeWithCachedSizes(\n" + " ::google::protobuf::io::CodedOutputStream* output) const;\n" + "::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;\n"); + } } printer->Print(vars, "int GetCachedSize() const { return _cached_size_; }\n" "private:\n" + "void SharedCtor();\n" + "void SharedDtor();\n" "void SetCachedSize(int size) const { _cached_size_ = size; }\n" "public:\n" "\n" @@ -505,11 +421,17 @@ GenerateClassDefinition(io::Printer* printer) { .GeneratePrivateMembers(printer); } - // Generate offsets and _has_bits_ boilerplate. - printer->Print(vars, - "friend void $builddescriptorsname$_AssignGlobalDescriptors(\n" - " const ::google::protobuf::FileDescriptor* file);\n"); + // Declare AddDescriptors() and BuildDescriptors() as friends so that they + // can assign private static variables like default_instance_ and reflection_. + printer->Print( + "friend void $adddescriptorsname$();\n" + "friend void $assigndescriptorsname$();\n", + "adddescriptorsname", + GlobalAddDescriptorsName(descriptor_->file()->name()), + "assigndescriptorsname", + GlobalAssignDescriptorsName(descriptor_->file()->name())); + // Generate offsets and _has_bits_ boilerplate. if (descriptor_->field_count() > 0) { printer->Print(vars, "::google::protobuf::uint32 _has_bits_[($field_count$ + 31) / 32];\n"); @@ -592,12 +514,6 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { "$parent$_descriptor_->nested_type($index$);\n"); } - // 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(vars, - "$classname$::default_instance_ = new $classname$();\n"); - // Generate the offsets. GenerateOffsets(printer); @@ -622,6 +538,7 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { } printer->Print(vars, " ::google::protobuf::DescriptorPool::generated_pool(),\n" + " ::google::protobuf::MessageFactory::generated_factory(),\n" " sizeof($classname$));\n"); // Handle nested types. @@ -632,11 +549,35 @@ GenerateDescriptorInitializer(io::Printer* printer, int index) { for (int i = 0; i < descriptor_->enum_type_count(); i++) { enum_generators_[i]->GenerateDescriptorInitializer(printer, i); } +} +void MessageGenerator:: +GenerateTypeRegistrations(io::Printer* printer) { // Register this message type with the message factory. - printer->Print(vars, + printer->Print( "::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(\n" - " $classname$_descriptor_, $classname$::default_instance_);\n"); + " $classname$_descriptor_, &$classname$::default_instance());\n", + "classname", classname_); + + // Handle nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateTypeRegistrations(printer); + } +} + +void MessageGenerator:: +GenerateDefaultInstanceAllocator(io::Printer* 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_ = new $classname$();\n", + "classname", classname_); + + // Handle nested types. + for (int i = 0; i < descriptor_->nested_type_count(); i++) { + nested_generators_[i]->GenerateDefaultInstanceAllocator(printer); + } } void MessageGenerator:: @@ -645,6 +586,11 @@ GenerateDefaultInstanceInitializer(io::Printer* printer) { "$classname$::default_instance_->InitAsDefaultInstance();\n", "classname", classname_); + // Register extensions. + for (int i = 0; i < descriptor_->extension_count(); i++) { + extension_generators_[i]->GenerateRegistration(printer); + } + // Handle nested types. for (int i = 0; i < descriptor_->nested_type_count(); i++) { nested_generators_[i]->GenerateDefaultInstanceInitializer(printer); @@ -695,14 +641,24 @@ GenerateClassMethods(io::Printer* printer) { GenerateClear(printer); printer->Print("\n"); - GenerateMergeFromCodedStream(printer); - printer->Print("\n"); + if (!descriptor_->options().message_set_wire_format()) { + // For message_set_wire_format, we don't generate parsing or + // serialization code even if optimize_for = SPEED, since MessageSet + // encoding is somewhat more complicated than normal extension encoding + // and we'd like to avoid having to implement it in multiple places. + // WireFormat's implementation is probably good enough. + GenerateMergeFromCodedStream(printer); + printer->Print("\n"); - GenerateSerializeWithCachedSizes(printer); - printer->Print("\n"); + GenerateSerializeWithCachedSizes(printer); + printer->Print("\n"); - GenerateByteSize(printer); - printer->Print("\n"); + GenerateSerializeWithCachedSizesToArray(printer); + printer->Print("\n"); + + GenerateByteSize(printer); + printer->Print("\n"); + } GenerateMergeFrom(printer); printer->Print("\n"); @@ -723,12 +679,10 @@ GenerateClassMethods(io::Printer* printer) { "}\n" "\n" "const ::google::protobuf::Reflection* $classname$::GetReflection() const {\n" - " if ($classname$_reflection_ == NULL) $builddescriptorsname$();\n" + " protobuf_AssignDescriptorsOnce();\n" " return $classname$_reflection_;\n" "}\n", - "classname", classname_, - "builddescriptorsname", - GlobalBuildDescriptorsName(descriptor_->file()->name())); + "classname", classname_); } void MessageGenerator:: @@ -757,28 +711,68 @@ GenerateInitializerList(io::Printer* printer) { printer->Indent(); printer->Print( - "::google::protobuf::Message(),\n"); + "::google::protobuf::Message()"); - if (descriptor_->extension_range_count() > 0) { - printer->Print( - "_extensions_(&$classname$_descriptor_,\n" - " ::google::protobuf::DescriptorPool::generated_pool(),\n" - " ::google::protobuf::MessageFactory::generated_factory()),\n", - "classname", classname_); - } + printer->Outdent(); + printer->Outdent(); +} + +void MessageGenerator:: +GenerateSharedConstructorCode(io::Printer* printer) { + printer->Print( + "void $classname$::SharedCtor() {\n", + "classname", classname_); + printer->Indent(); printer->Print( - "_unknown_fields_(),\n" - "_cached_size_(0)"); + "_cached_size_ = 0;\n"); - // Write the initializers for each field. for (int i = 0; i < descriptor_->field_count(); i++) { field_generators_.get(descriptor_->field(i)) - .GenerateInitializer(printer); + .GenerateConstructorCode(printer); } + printer->Print( + "::memset(_has_bits_, 0, sizeof(_has_bits_));\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void MessageGenerator:: +GenerateSharedDestructorCode(io::Printer* printer) { + printer->Print( + "void $classname$::SharedDtor() {\n", + "classname", classname_); + printer->Indent(); + // Write the destructors for each field. + for (int i = 0; i < descriptor_->field_count(); i++) { + field_generators_.get(descriptor_->field(i)) + .GenerateDestructorCode(printer); + } + + printer->Print( + "if (this != default_instance_) {\n"); + + // We need to delete all embedded messages. + // TODO(kenton): If we make unset messages point at default instances + // instead of NULL, then it would make sense to move this code into + // MessageFieldGenerator::GenerateDestructorCode(). + for (int i = 0; i < descriptor_->field_count(); i++) { + const FieldDescriptor* field = descriptor_->field(i); + + if (!field->is_repeated() && + field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { + printer->Print(" delete $name$_;\n", + "name", FieldName(field)); + } + } + printer->Outdent(); + printer->Print( + " }\n" + "}\n" + "\n"); } void MessageGenerator:: @@ -790,7 +784,7 @@ GenerateStructors(io::Printer* printer) { "classname", classname_); GenerateInitializerList(printer); printer->Print(" {\n" - " ::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + " SharedCtor();\n" "}\n"); printer->Print( @@ -826,54 +820,33 @@ GenerateStructors(io::Printer* printer) { "classname", classname_); GenerateInitializerList(printer); printer->Print(" {\n" - " ::memset(_has_bits_, 0, sizeof(_has_bits_));\n" + " SharedCtor();\n" " MergeFrom(from);\n" "}\n" "\n"); + // Generate the shared constructor code. + GenerateSharedConstructorCode(printer); + // Generate the destructor. printer->Print( - "$classname$::~$classname$() {\n", + "$classname$::~$classname$() {\n" + " SharedDtor();\n" + "}\n" + "\n", "classname", classname_); - printer->Indent(); - - // Write the destructors for each field. - for (int i = 0; i < descriptor_->field_count(); i++) { - field_generators_.get(descriptor_->field(i)) - .GenerateDestructorCode(printer); - } + // Generate the shared destructor code. + GenerateSharedDestructorCode(printer); printer->Print( - "if (this != default_instance_) {\n"); - - // We need to delete all embedded messages. - // TODO(kenton): If we make unset messages point at default instances - // instead of NULL, then it would make sense to move this code into - // MessageFieldGenerator::GenerateDestructorCode(). - for (int i = 0; i < descriptor_->field_count(); i++) { - const FieldDescriptor* field = descriptor_->field(i); - - if (!field->is_repeated() && - field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { - printer->Print(" delete $name$_;\n", - "name", FieldName(field)); - } - } - - printer->Outdent(); - - printer->Print( - " }\n" - "}\n" - "\n" "const ::google::protobuf::Descriptor* $classname$::descriptor() {\n" - " if ($classname$_descriptor_ == NULL) $builddescriptorsname$();\n" + " protobuf_AssignDescriptorsOnce();\n" " return $classname$_descriptor_;\n" "}\n" "\n" "const $classname$& $classname$::default_instance() {\n" - " if (default_instance_ == NULL) $builddescriptorsname$();\n" + " if (default_instance_ == NULL) $adddescriptorsname$();" " return *default_instance_;\n" "}\n" "\n" @@ -883,8 +856,8 @@ GenerateStructors(io::Printer* printer) { " return new $classname$;\n" "}\n", "classname", classname_, - "builddescriptorsname", - GlobalBuildDescriptorsName(descriptor_->file()->name())); + "adddescriptorsname", + GlobalAddDescriptorsName(descriptor_->file()->name())); } void MessageGenerator:: @@ -1127,24 +1100,6 @@ GenerateCopyFrom(io::Printer* printer) { void MessageGenerator:: GenerateMergeFromCodedStream(io::Printer* printer) { - if (descriptor_->options().message_set_wire_format()) { - // For message_set_wire_format, we don't generate a parser, for two - // reasons: - // - WireFormat already needs to special-case this, and we'd like to - // avoid having multiple implementations of MessageSet wire format - // lying around the code base. - // - All fields are extensions, and extension parsing falls back to - // reflection anyway, so it wouldn't be any faster. - printer->Print( - "bool $classname$::MergePartialFromCodedStream(\n" - " ::google::protobuf::io::CodedInputStream* input) {\n" - " return ::google::protobuf::internal::WireFormat::ParseAndMergePartial(\n" - " input, this);\n" - "}\n", - "classname", classname_); - return; - } - printer->Print( "bool $classname$::MergePartialFromCodedStream(\n" " ::google::protobuf::io::CodedInputStream* input) {\n" @@ -1267,7 +1222,8 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } } printer->Print(") {\n" - " DO_(_extensions_.ParseField(tag, input, this));\n" + " DO_(_extensions_.ParseField(tag, input, default_instance_,\n" + " mutable_unknown_fields()));\n" " continue;\n" "}\n"); } @@ -1295,7 +1251,7 @@ GenerateMergeFromCodedStream(io::Printer* printer) { } void MessageGenerator::GenerateSerializeOneField( - io::Printer* printer, const FieldDescriptor* field) { + io::Printer* printer, const FieldDescriptor* field, bool to_array) { PrintFieldComment(printer, field); if (!field->is_repeated()) { @@ -1305,7 +1261,12 @@ void MessageGenerator::GenerateSerializeOneField( printer->Indent(); } - field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); + if (to_array) { + field_generators_.get(field).GenerateSerializeWithCachedSizesToArray( + printer); + } else { + field_generators_.get(field).GenerateSerializeWithCachedSizes(printer); + } if (!field->is_repeated()) { printer->Outdent(); @@ -1315,25 +1276,66 @@ void MessageGenerator::GenerateSerializeOneField( } void MessageGenerator::GenerateSerializeOneExtensionRange( - io::Printer* printer, const Descriptor::ExtensionRange* range) { + io::Printer* printer, const Descriptor::ExtensionRange* range, + bool to_array) { map<string, string> vars; vars["start"] = SimpleItoa(range->start); vars["end"] = SimpleItoa(range->end); printer->Print(vars, - "// Extension range [$start$, $end$)\n" - "DO_(_extensions_.SerializeWithCachedSizes(\n" - " $start$, $end$, *this, output));\n\n"); + "// Extension range [$start$, $end$)\n"); + if (to_array) { + printer->Print(vars, + "target = _extensions_.SerializeWithCachedSizesToArray(\n" + " $start$, $end$, target);\n\n"); + } else { + printer->Print(vars, + "_extensions_.SerializeWithCachedSizes(\n" + " $start$, $end$, output);\n\n"); + } } void MessageGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) { printer->Print( - "bool $classname$::SerializeWithCachedSizes(\n" - " ::google::protobuf::io::CodedOutputStream* output) const {\n" - "#define DO_(EXPRESSION) if (!(EXPRESSION)) return false\n", + "void $classname$::SerializeWithCachedSizes(\n" + " ::google::protobuf::io::CodedOutputStream* output) const {\n", + "classname", classname_); + printer->Indent(); + + printer->Print( + "::google::protobuf::uint8* raw_buffer = " + "output->GetDirectBufferForNBytesAndAdvance(_cached_size_);\n" + "if (raw_buffer != NULL) {\n" + " $classname$::SerializeWithCachedSizesToArray(raw_buffer);\n" + " return;\n" + "}\n" + "\n", + "classname", classname_); + GenerateSerializeWithCachedSizesBody(printer, false); + + printer->Outdent(); + printer->Print( + "}\n"); +} + +void MessageGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) { + printer->Print( + "::google::protobuf::uint8* $classname$::SerializeWithCachedSizesToArray(\n" + " ::google::protobuf::uint8* target) const {\n", "classname", classname_); printer->Indent(); + GenerateSerializeWithCachedSizesBody(printer, true); + + printer->Outdent(); + printer->Print( + " return target;\n" + "}\n"); +} + +void MessageGenerator:: +GenerateSerializeWithCachedSizesBody(io::Printer* printer, bool to_array) { scoped_array<const FieldDescriptor*> ordered_fields( SortFieldsByNumber(descriptor_)); @@ -1350,35 +1352,35 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) { i < descriptor_->field_count() || j < sorted_extensions.size(); ) { if (i == descriptor_->field_count()) { - GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + GenerateSerializeOneExtensionRange(printer, + sorted_extensions[j++], + to_array); } else if (j == sorted_extensions.size()) { - GenerateSerializeOneField(printer, ordered_fields[i++]); + GenerateSerializeOneField(printer, ordered_fields[i++], to_array); } else if (ordered_fields[i]->number() < sorted_extensions[j]->start) { - GenerateSerializeOneField(printer, ordered_fields[i++]); + GenerateSerializeOneField(printer, ordered_fields[i++], to_array); } else { - GenerateSerializeOneExtensionRange(printer, sorted_extensions[j++]); + GenerateSerializeOneExtensionRange(printer, + sorted_extensions[j++], + to_array); } } printer->Print("if (!unknown_fields().empty()) {\n"); printer->Indent(); - if (descriptor_->options().message_set_wire_format()) { + if (to_array) { printer->Print( - "DO_(::google::protobuf::internal::WireFormat::SerializeUnknownMessageSetItems(\n" - " unknown_fields(), output));\n"); + "target = " + "::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(\n" + " unknown_fields(), target);\n"); } else { printer->Print( - "DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" - " unknown_fields(), output));\n"); + "::google::protobuf::internal::WireFormat::SerializeUnknownFields(\n" + " unknown_fields(), output);\n"); } printer->Outdent(); - printer->Print( - "}\n" - "return true;\n"); - printer->Outdent(); printer->Print( - "#undef DO_\n" "}\n"); } @@ -1449,23 +1451,16 @@ GenerateByteSize(io::Printer* printer) { if (descriptor_->extension_range_count() > 0) { printer->Print( - "total_size += _extensions_.ByteSize(*this);\n" + "total_size += _extensions_.ByteSize();\n" "\n"); } printer->Print("if (!unknown_fields().empty()) {\n"); printer->Indent(); - if (descriptor_->options().message_set_wire_format()) { - printer->Print( - "total_size +=\n" - " ::google::protobuf::internal::WireFormat::ComputeUnknownMessageSetItemsSize(\n" - " unknown_fields());\n"); - } else { - printer->Print( - "total_size +=\n" - " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" - " unknown_fields());\n"); - } + printer->Print( + "total_size +=\n" + " ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(\n" + " unknown_fields());\n"); printer->Outdent(); printer->Print("}\n"); diff --git a/src/google/protobuf/compiler/cpp/cpp_message.h b/src/google/protobuf/compiler/cpp/cpp_message.h index d6669a34..31aa1c4c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message.h +++ b/src/google/protobuf/compiler/cpp/cpp_message.h @@ -86,7 +86,16 @@ class MessageGenerator { // descriptor. void GenerateDescriptorInitializer(io::Printer* printer, int index); - // Generates code that initializes the message's default instance. + // Generate code that calls MessageFactory::InternalRegisterGeneratedMessage() + // for all types. + void GenerateTypeRegistrations(io::Printer* printer); + + // Generates code that allocates the message's default instance. + void GenerateDefaultInstanceAllocator(io::Printer* printer); + + // Generates code that initializes the message's default instance. This + // is separate from allocating because all default instances must be + // allocated before any can be initialized. void GenerateDefaultInstanceInitializer(io::Printer* printer); // Generate all non-inline methods for this class. @@ -103,6 +112,15 @@ class MessageGenerator { // Generate constructors and destructor. void GenerateStructors(io::Printer* printer); + // The compiler typically generates multiple copies of each constructor and + // destructor: http://gcc.gnu.org/bugs.html#nonbugs_cxx + // Placing common code in a separate method reduces the generated code size. + // + // Generate the shared constructor code. + void GenerateSharedConstructorCode(io::Printer* printer); + // Generate the shared destructor code. + void GenerateSharedDestructorCode(io::Printer* printer); + // Generate the member initializer list for the constructors. The member // initializer list is shared between the default constructor and the copy // constructor. @@ -112,6 +130,9 @@ class MessageGenerator { void GenerateClear(io::Printer* printer); void GenerateMergeFromCodedStream(io::Printer* printer); void GenerateSerializeWithCachedSizes(io::Printer* printer); + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer); + void GenerateSerializeWithCachedSizesBody(io::Printer* printer, + bool to_array); void GenerateByteSize(io::Printer* printer); void GenerateMergeFrom(io::Printer* printer); void GenerateCopyFrom(io::Printer* printer); @@ -120,9 +141,11 @@ class MessageGenerator { // Helpers for GenerateSerializeWithCachedSizes(). void GenerateSerializeOneField(io::Printer* printer, - const FieldDescriptor* field); + const FieldDescriptor* field, + bool unbounded); void GenerateSerializeOneExtensionRange( - io::Printer* printer, const Descriptor::ExtensionRange* range); + io::Printer* printer, const Descriptor::ExtensionRange* range, + bool unbounded); const Descriptor* descriptor_; string classname_; diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.cc b/src/google/protobuf/compiler/cpp/cpp_message_field.cc index e2d2370b..2a7eb3f8 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.cc @@ -116,8 +116,8 @@ GenerateSwappingCode(io::Printer* printer) const { } void MessageFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { - printer->Print(variables_, ",\n$name$_(NULL)"); +GenerateConstructorCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = NULL;\n"); } void MessageFieldGenerator:: @@ -136,8 +136,16 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { void MessageFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" - "$number$, this->$name$(), output));\n"); + "::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" + "$number$, this->$name$(), output);\n"); +} + +void MessageFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + printer->Print(variables_, + "target = ::google::protobuf::internal::WireFormat::" + "Write$declared_type$NoVirtualToArray(" + "$number$, this->$name$(), target);\n"); } void MessageFieldGenerator:: @@ -212,8 +220,8 @@ GenerateSwappingCode(io::Printer* printer) const { } void RepeatedMessageFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { - printer->Print(variables_, ",\n$name$_()"); +GenerateConstructorCode(io::Printer* printer) const { + // Not needed for repeated fields. } void RepeatedMessageFieldGenerator:: @@ -233,8 +241,18 @@ void RepeatedMessageFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n" - " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" - "$number$, this->$name$(i), output));\n" + " ::google::protobuf::internal::WireFormat::Write$declared_type$NoVirtual(" + "$number$, this->$name$(i), output);\n" + "}\n"); +} + +void RepeatedMessageFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < this->$name$_size(); i++) {\n" + " target = ::google::protobuf::internal::WireFormat::" + "Write$declared_type$NoVirtualToArray(" + "$number$, this->$name$(i), target);\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_message_field.h b/src/google/protobuf/compiler/cpp/cpp_message_field.h index 7ce4c32b..f5147278 100644 --- a/src/google/protobuf/compiler/cpp/cpp_message_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_message_field.h @@ -56,9 +56,10 @@ class MessageFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: @@ -80,9 +81,10 @@ class RepeatedMessageFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc index 57244c5d..44d0b97c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.cc @@ -79,35 +79,6 @@ int FixedSize(FieldDescriptor::Type type) { return -1; } -string DefaultValue(const FieldDescriptor* field) { - switch (field->cpp_type()) { - case FieldDescriptor::CPPTYPE_INT32: - return SimpleItoa(field->default_value_int32()); - case FieldDescriptor::CPPTYPE_UINT32: - return SimpleItoa(field->default_value_uint32()) + "u"; - case FieldDescriptor::CPPTYPE_INT64: - return "GOOGLE_LONGLONG(" + SimpleItoa(field->default_value_int64()) + ")"; - case FieldDescriptor::CPPTYPE_UINT64: - return "GOOGLE_ULONGLONG(" + SimpleItoa(field->default_value_uint64())+ ")"; - case FieldDescriptor::CPPTYPE_DOUBLE: - return SimpleDtoa(field->default_value_double()); - case FieldDescriptor::CPPTYPE_FLOAT: - return SimpleFtoa(field->default_value_float()); - case FieldDescriptor::CPPTYPE_BOOL: - return field->default_value_bool() ? "true" : "false"; - - case FieldDescriptor::CPPTYPE_ENUM: - case FieldDescriptor::CPPTYPE_STRING: - case FieldDescriptor::CPPTYPE_MESSAGE: - GOOGLE_LOG(FATAL) << "Shouldn't get here."; - return ""; - } - // Can't actually get here; make compiler happy. (We could add a default - // case above but then we wouldn't get the nice compiler warning when a - // new type is added.) - return ""; -} - // TODO(kenton): Factor out a "SetCommonFieldVariables()" to get rid of // repeat code between this and the other field types. void SetPrimitiveVariables(const FieldDescriptor* descriptor, @@ -180,8 +151,8 @@ GenerateSwappingCode(io::Printer* printer) const { } void PrimitiveFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { - printer->Print(variables_, ",\n$name$_($default$)"); +GenerateConstructorCode(io::Printer* printer) const { + printer->Print(variables_, "$name$_ = $default$;\n"); } void PrimitiveFieldGenerator:: @@ -195,8 +166,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { void PrimitiveFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" - "$number$, this->$name$(), output));\n"); + "::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(), output);\n"); +} + +void PrimitiveFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + printer->Print(variables_, + "target = ::google::protobuf::internal::WireFormat::Write$declared_type$ToArray(" + "$number$, this->$name$(), target);\n"); } void PrimitiveFieldGenerator:: @@ -282,12 +260,8 @@ GenerateSwappingCode(io::Printer* printer) const { } void RepeatedPrimitiveFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { - printer->Print(variables_, ",\n$name$_()"); - if (descriptor_->options().packed() && - descriptor_->file()->options().optimize_for() == FileOptions::SPEED) { - printer->Print(variables_, ",\n_$name$_cached_byte_size_()"); - } +GenerateConstructorCode(io::Printer* printer) const { + // Not needed for repeated fields. } void RepeatedPrimitiveFieldGenerator:: @@ -324,22 +298,53 @@ GenerateSerializeWithCachedSizes(io::Printer* printer) const { // Write the tag and the size. printer->Print(variables_, "if (this->$name$_size() > 0) {\n" - " DO_(::google::protobuf::internal::WireFormat::WriteTag(" - "$number$, ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED," - "output));\n" - " DO_(output->WriteVarint32(_$name$_cached_byte_size_));\n" + " ::google::protobuf::internal::WireFormat::WriteTag(" + "$number$, " + "::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED, " + "output);\n" + " output->WriteVarint32(_$name$_cached_byte_size_);\n" + "}\n"); + } + printer->Print(variables_, + "for (int i = 0; i < this->$name$_size(); i++) {\n"); + if (descriptor_->options().packed()) { + printer->Print(variables_, + " ::google::protobuf::internal::WireFormat::Write$declared_type$NoTag(" + "this->$name$(i), output);\n"); + } else { + printer->Print(variables_, + " ::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output);\n"); + } + printer->Print("}\n"); +} + +void RepeatedPrimitiveFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + if (descriptor_->options().packed()) { + // Write the tag and the size. + printer->Print(variables_, + "if (this->$name$_size() > 0) {\n" + " target = ::google::protobuf::internal::WireFormat::WriteTagToArray(" + "$number$, " + "::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED, " + "target);\n" + " target = ::google::protobuf::io::CodedOutputStream::WriteVarint32ToArray(" + "_$name$_cached_byte_size_, target);\n" "}\n"); } printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n"); if (descriptor_->options().packed()) { printer->Print(variables_, - " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$NoTag(" - "this->$name$(i), output));\n"); + " target = ::google::protobuf::internal::WireFormat::" + "Write$declared_type$NoTagToArray(" + "this->$name$(i), target);\n"); } else { printer->Print(variables_, - " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" - "$number$, this->$name$(i), output));\n"); + " target = ::google::protobuf::internal::WireFormat::" + "Write$declared_type$ToArray(" + "$number$, this->$name$(i), target);\n"); } printer->Print("}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h index c7f7f310..6b96614c 100644 --- a/src/google/protobuf/compiler/cpp/cpp_primitive_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_primitive_field.h @@ -56,9 +56,10 @@ class PrimitiveFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: @@ -80,9 +81,10 @@ class RepeatedPrimitiveFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: diff --git a/src/google/protobuf/compiler/cpp/cpp_service.cc b/src/google/protobuf/compiler/cpp/cpp_service.cc index 59dbac1f..7689fa13 100644 --- a/src/google/protobuf/compiler/cpp/cpp_service.cc +++ b/src/google/protobuf/compiler/cpp/cpp_service.cc @@ -176,10 +176,12 @@ void ServiceGenerator::GenerateImplementation(io::Printer* printer) { "$classname$::~$classname$() {}\n" "\n" "const ::google::protobuf::ServiceDescriptor* $classname$::descriptor() {\n" + " protobuf_AssignDescriptorsOnce();\n" " return $classname$_descriptor_;\n" "}\n" "\n" "const ::google::protobuf::ServiceDescriptor* $classname$::GetDescriptor() {\n" + " protobuf_AssignDescriptorsOnce();\n" " return $classname$_descriptor_;\n" "}\n" "\n"); @@ -279,7 +281,7 @@ void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, printer->Print(vars_, " const ::google::protobuf::MethodDescriptor* method) const {\n" - " GOOGLE_DCHECK_EQ(method->service(), $classname$_descriptor_);\n" + " GOOGLE_DCHECK_EQ(method->service(), descriptor());\n" " switch(method->index()) {\n"); for (int i = 0; i < descriptor_->method_count(); i++) { @@ -320,7 +322,7 @@ void ServiceGenerator::GenerateStubMethods(io::Printer* printer) { " const $input_type$* request,\n" " $output_type$* response,\n" " ::google::protobuf::Closure* done) {\n" - " channel_->CallMethod($classname$_descriptor_->method($index$),\n" + " channel_->CallMethod(descriptor()->method($index$),\n" " controller, request, response, done);\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.cc b/src/google/protobuf/compiler/cpp/cpp_string_field.cc index 8e10e9b0..05858da4 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.cc +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.cc @@ -61,6 +61,8 @@ void SetStringVariables(const FieldDescriptor* descriptor, (*variables)["declared_type"] = DeclaredTypeMethodName(descriptor->type()); (*variables)["tag_size"] = SimpleItoa( WireFormat::TagSize(descriptor->number(), descriptor->type())); + (*variables)["pointer_type"] = + descriptor->type() == FieldDescriptor::TYPE_BYTES ? "void" : "char"; } } // namespace @@ -111,13 +113,8 @@ GenerateAccessorDeclarations(io::Printer* printer) const { printer->Print(variables_, "inline const ::std::string& $name$() const;\n" "inline void set_$name$(const ::std::string& value);\n" - "inline void set_$name$(const char* value);\n"); - if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { - printer->Print(variables_, - "inline void set_$name$(const void* value, size_t size);\n"); - } - - printer->Print(variables_, + "inline void set_$name$(const char* value);\n" + "inline void set_$name$(const $pointer_type$* value, size_t size);\n" "inline ::std::string* mutable_$name$();\n"); if (descriptor_->options().has_ctype()) { @@ -146,20 +143,15 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { " $name$_ = new ::std::string;\n" " }\n" " $name$_->assign(value);\n" - "}\n"); - - if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { - printer->Print(variables_, - "inline void $classname$::set_$name$(const void* value, size_t size) {\n" - " _set_bit($index$);\n" - " if ($name$_ == &_default_$name$_) {\n" - " $name$_ = new ::std::string;\n" - " }\n" - " $name$_->assign(reinterpret_cast<const char*>(value), size);\n" - "}\n"); - } - - printer->Print(variables_, + "}\n" + "inline " + "void $classname$::set_$name$(const $pointer_type$* value, size_t size) {\n" + " _set_bit($index$);\n" + " if ($name$_ == &_default_$name$_) {\n" + " $name$_ = new ::std::string;\n" + " }\n" + " $name$_->assign(reinterpret_cast<const char*>(value), size);\n" + "}\n" "inline ::std::string* $classname$::mutable_$name$() {\n" " _set_bit($index$);\n" " if ($name$_ == &_default_$name$_) {\n"); @@ -213,9 +205,9 @@ GenerateSwappingCode(io::Printer* printer) const { } void StringFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { +GenerateConstructorCode(io::Printer* printer) const { printer->Print(variables_, - ",\n$name$_(const_cast< ::std::string*>(&_default_$name$_))"); + "$name$_ = const_cast< ::std::string*>(&_default_$name$_);\n"); } void StringFieldGenerator:: @@ -236,8 +228,15 @@ GenerateMergeFromCodedStream(io::Printer* printer) const { void StringFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, - "DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" - "$number$, this->$name$(), output));\n"); + "::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(), output);\n"); +} + +void StringFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + printer->Print(variables_, + "target = ::google::protobuf::internal::WireFormat::Write$declared_type$ToArray(" + "$number$, this->$name$(), target);\n"); } void StringFieldGenerator:: @@ -281,15 +280,12 @@ GenerateAccessorDeclarations(io::Printer* printer) const { "inline ::std::string* mutable_$name$(int index);\n" "inline void set_$name$(int index, const ::std::string& value);\n" "inline void set_$name$(int index, const char* value);\n" + "inline " + "void set_$name$(int index, const $pointer_type$* value, size_t size);\n" "inline ::std::string* add_$name$();\n" "inline void add_$name$(const ::std::string& value);\n" - "inline void add_$name$(const char* value);\n"); - - if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { - printer->Print(variables_, - "inline void set_$name$(int index, const void* value, size_t size);\n" - "inline void add_$name$(const void* value, size_t size);\n"); - } + "inline void add_$name$(const char* value);\n" + "inline void add_$name$(const $pointer_type$* value, size_t size);\n"); if (descriptor_->options().has_ctype()) { printer->Outdent(); @@ -321,6 +317,12 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { "inline void $classname$::set_$name$(int index, const char* value) {\n" " $name$_.Mutable(index)->assign(value);\n" "}\n" + "inline void " + "$classname$::set_$name$" + "(int index, const $pointer_type$* value, size_t size) {\n" + " $name$_.Mutable(index)->assign(\n" + " reinterpret_cast<const char*>(value), size);\n" + "}\n" "inline ::std::string* $classname$::add_$name$() {\n" " return $name$_.Add();\n" "}\n" @@ -329,19 +331,11 @@ GenerateInlineAccessorDefinitions(io::Printer* printer) const { "}\n" "inline void $classname$::add_$name$(const char* value) {\n" " $name$_.Add()->assign(value);\n" + "}\n" + "inline void " + "$classname$::add_$name$(const $pointer_type$* value, size_t size) {\n" + " $name$_.Add()->assign(reinterpret_cast<const char*>(value), size);\n" "}\n"); - - if (descriptor_->type() == FieldDescriptor::TYPE_BYTES) { - printer->Print(variables_, - "inline void " - "$classname$::set_$name$(int index, const void* value, size_t size) {\n" - " $name$_.Mutable(index)->assign(\n" - " reinterpret_cast<const char*>(value), size);\n" - "}\n" - "inline void $classname$::add_$name$(const void* value, size_t size) {\n" - " $name$_.Add()->assign(reinterpret_cast<const char*>(value), size);\n" - "}\n"); - } } void RepeatedStringFieldGenerator:: @@ -360,8 +354,8 @@ GenerateSwappingCode(io::Printer* printer) const { } void RepeatedStringFieldGenerator:: -GenerateInitializer(io::Printer* printer) const { - printer->Print(variables_, ",\n$name$_()"); +GenerateConstructorCode(io::Printer* printer) const { + // Not needed for repeated fields. } void RepeatedStringFieldGenerator:: @@ -375,8 +369,18 @@ void RepeatedStringFieldGenerator:: GenerateSerializeWithCachedSizes(io::Printer* printer) const { printer->Print(variables_, "for (int i = 0; i < this->$name$_size(); i++) {\n" - " DO_(::google::protobuf::internal::WireFormat::Write$declared_type$(" - "$number$, this->$name$(i), output));\n" + " ::google::protobuf::internal::WireFormat::Write$declared_type$(" + "$number$, this->$name$(i), output);\n" + "}\n"); +} + +void RepeatedStringFieldGenerator:: +GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const { + printer->Print(variables_, + "for (int i = 0; i < this->$name$_size(); i++) {\n" + " target = ::google::protobuf::internal::WireFormat::" + "Write$declared_type$ToArray(" + "$number$, this->$name$(i), target);\n" "}\n"); } diff --git a/src/google/protobuf/compiler/cpp/cpp_string_field.h b/src/google/protobuf/compiler/cpp/cpp_string_field.h index 2244bd77..7f45107d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_string_field.h +++ b/src/google/protobuf/compiler/cpp/cpp_string_field.h @@ -57,10 +57,11 @@ class StringFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateDestructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: @@ -82,9 +83,10 @@ class RepeatedStringFieldGenerator : public FieldGenerator { void GenerateClearingCode(io::Printer* printer) const; void GenerateMergingCode(io::Printer* printer) const; void GenerateSwappingCode(io::Printer* printer) const; - void GenerateInitializer(io::Printer* printer) const; + void GenerateConstructorCode(io::Printer* printer) const; void GenerateMergeFromCodedStream(io::Printer* printer) const; void GenerateSerializeWithCachedSizes(io::Printer* printer) const; + void GenerateSerializeWithCachedSizesToArray(io::Printer* printer) const; void GenerateByteSize(io::Printer* printer) const; private: diff --git a/src/google/protobuf/compiler/cpp/cpp_unittest.cc b/src/google/protobuf/compiler/cpp/cpp_unittest.cc index c7e4ee3d..ea9cc32d 100644 --- a/src/google/protobuf/compiler/cpp/cpp_unittest.cc +++ b/src/google/protobuf/compiler/cpp/cpp_unittest.cc @@ -52,6 +52,8 @@ #include <google/protobuf/test_util.h> #include <google/protobuf/compiler/cpp/cpp_test_bad_identifiers.pb.h> #include <google/protobuf/compiler/importer.h> +#include <google/protobuf/io/coded_stream.h> +#include <google/protobuf/io/zero_copy_stream_impl.h> #include <google/protobuf/descriptor.h> #include <google/protobuf/descriptor.pb.h> #include <google/protobuf/dynamic_message.h> @@ -61,6 +63,7 @@ #include <google/protobuf/stubs/substitute.h> #include <google/protobuf/testing/googletest.h> #include <gtest/gtest.h> +#include <google/protobuf/stubs/stl_util-inl.h> namespace google { namespace protobuf { @@ -86,6 +89,8 @@ class MockErrorCollector : public MultiFileErrorCollector { } }; +#ifndef PROTOBUF_TEST_NO_DESCRIPTORS + // Test that generated code has proper descriptors: // Parse a descriptor directly (using google::protobuf::compiler::Importer) and // compare it to the one that was produced by generated code. @@ -115,6 +120,8 @@ TEST(GeneratedDescriptorTest, IdenticalDescriptors) { generated_decsriptor_proto.DebugString()); } +#endif // !PROTOBUF_TEST_NO_DESCRIPTORS + // =================================================================== TEST(GeneratedMessageTest, Defaults) { @@ -222,6 +229,22 @@ TEST(GeneratedMessageTest, ClearOneField) { TestUtil::ExpectAllFieldsSet(message); } +TEST(GeneratedMessageTest, StringCharStarLength) { + // Verify that we can use a char*,length to set one of the string fields. + unittest::TestAllTypes message; + message.set_optional_string("abcdef", 3); + EXPECT_EQ("abc", message.optional_string()); + + // Verify that we can use a char*,length to add to a repeated string field. + message.add_repeated_string("abcdef", 3); + EXPECT_EQ(1, message.repeated_string_size()); + EXPECT_EQ("abc", message.repeated_string(0)); + + // Verify that we can use a char*,length to set a repeated string field. + message.set_repeated_string(0, "wxyz", 2); + EXPECT_EQ("wx", message.repeated_string(0)); +} + TEST(GeneratedMessageTest, CopyFrom) { unittest::TestAllTypes message1, message2; @@ -346,6 +369,8 @@ TEST(GeneratedMessageTest, UpcastCopyFrom) { TestUtil::ExpectAllFieldsSet(message2); } +#ifndef PROTOBUF_TEST_NO_DESCRIPTORS + TEST(GeneratedMessageTest, DynamicMessageCopyFrom) { // Test copying from a DynamicMessage, which must fall back to using // reflection. @@ -366,6 +391,8 @@ TEST(GeneratedMessageTest, DynamicMessageCopyFrom) { TestUtil::ExpectAllFieldsSet(message2); } +#endif // !PROTOBUF_TEST_NO_DESCRIPTORS + TEST(GeneratedMessageTest, NonEmptyMergeFrom) { // Test merging with a non-empty message. Code is a modified form // of that found in google/protobuf/reflection_ops_unittest.cc. @@ -403,24 +430,75 @@ TEST(GeneratedMessageTest, MergeFromSelf) { #endif // GTEST_HAS_DEATH_TEST -TEST(GeneratedMessageTest, Serialization) { +// Test the generated SerializeWithCachedSizesToArray(), +TEST(GeneratedMessageTest, SerializationToArray) { unittest::TestAllTypes message1, message2; string data; - TestUtil::SetAllFields(&message1); - message1.SerializeToString(&data); + int size = message1.ByteSize(); + data.resize(size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&data)); + uint8* end = + message1.TestAllTypes::SerializeWithCachedSizesToArray(start); + EXPECT_EQ(size, end - start); EXPECT_TRUE(message2.ParseFromString(data)); TestUtil::ExpectAllFieldsSet(message2); +} +TEST(GeneratedMessageTest, PackedFieldsSerializationToArray) { unittest::TestPackedTypes packed_message1, packed_message2; string packed_data; TestUtil::SetPackedFields(&packed_message1); - packed_message1.SerializeToString(&packed_data); + int packed_size = packed_message1.ByteSize(); + packed_data.resize(packed_size); + uint8* start = reinterpret_cast<uint8*>(string_as_array(&packed_data)); + uint8* end = + packed_message1.TestPackedTypes::SerializeWithCachedSizesToArray(start); + EXPECT_EQ(packed_size, end - start); EXPECT_TRUE(packed_message2.ParseFromString(packed_data)); TestUtil::ExpectPackedFieldsSet(packed_message2); } +// Test the generated SerializeWithCachedSizes() by forcing the buffer to write +// one byte at a time. +TEST(GeneratedMessageTest, SerializationToStream) { + unittest::TestAllTypes message1, message2; + TestUtil::SetAllFields(&message1); + int size = message1.ByteSize(); + string data; + data.resize(size); + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + io::CodedOutputStream output_stream(&array_stream); + message1.TestAllTypes::SerializeWithCachedSizes(&output_stream); + EXPECT_FALSE(output_stream.HadError()); + EXPECT_EQ(size, output_stream.ByteCount()); + } + EXPECT_TRUE(message2.ParseFromString(data)); + TestUtil::ExpectAllFieldsSet(message2); + +} + +TEST(GeneratedMessageTest, PackedFieldsSerializationToStream) { + unittest::TestPackedTypes message1, message2; + TestUtil::SetPackedFields(&message1); + int size = message1.ByteSize(); + string data; + data.resize(size); + { + // Allow the output stream to buffer only one byte at a time. + io::ArrayOutputStream array_stream(string_as_array(&data), size, 1); + io::CodedOutputStream output_stream(&array_stream); + message1.TestPackedTypes::SerializeWithCachedSizes(&output_stream); + EXPECT_FALSE(output_stream.HadError()); + EXPECT_EQ(size, output_stream.ByteCount()); + } + EXPECT_TRUE(message2.ParseFromString(data)); + TestUtil::ExpectPackedFieldsSet(message2); +} + TEST(GeneratedMessageTest, Required) { // Test that IsInitialized() returns false if required fields are missing. @@ -547,6 +625,8 @@ TEST(GeneratedMessageTest, TestConflictingSymbolNames) { EXPECT_EQ(5, message.friend_()); } +#ifndef PROTOBUF_TEST_NO_DESCRIPTORS + TEST(GeneratedMessageTest, TestOptimizedForSize) { // We rely on the tests in reflection_ops_unittest and wire_format_unittest // to really test that reflection-based methods work. Here we are mostly @@ -614,6 +694,8 @@ TEST(GeneratedMessageTest, TestSpaceUsed) { message1.SpaceUsed()); } +#endif // !PROTOBUF_TEST_NO_DESCRIPTORS + // =================================================================== TEST(GeneratedEnumTest, EnumValuesAsSwitchCases) { @@ -682,8 +764,37 @@ TEST(GeneratedEnumTest, MinAndMax) { } } +#ifndef PROTOBUF_TEST_NO_DESCRIPTORS + +TEST(GeneratedEnumTest, Name) { + // "Names" in the presence of dup values are a bit arbitrary. + EXPECT_EQ("FOO1", unittest::TestEnumWithDupValue_Name(unittest::FOO1)); + EXPECT_EQ("FOO1", unittest::TestEnumWithDupValue_Name(unittest::FOO2)); + + EXPECT_EQ("SPARSE_A", unittest::TestSparseEnum_Name(unittest::SPARSE_A)); + EXPECT_EQ("SPARSE_B", unittest::TestSparseEnum_Name(unittest::SPARSE_B)); + EXPECT_EQ("SPARSE_C", unittest::TestSparseEnum_Name(unittest::SPARSE_C)); + EXPECT_EQ("SPARSE_D", unittest::TestSparseEnum_Name(unittest::SPARSE_D)); + EXPECT_EQ("SPARSE_E", unittest::TestSparseEnum_Name(unittest::SPARSE_E)); + EXPECT_EQ("SPARSE_F", unittest::TestSparseEnum_Name(unittest::SPARSE_F)); + EXPECT_EQ("SPARSE_G", unittest::TestSparseEnum_Name(unittest::SPARSE_G)); +} + +TEST(GeneratedEnumTest, Parse) { + unittest::TestEnumWithDupValue dup_value = unittest::FOO1; + EXPECT_TRUE(unittest::TestEnumWithDupValue_Parse("FOO1", &dup_value)); + EXPECT_EQ(unittest::FOO1, dup_value); + EXPECT_TRUE(unittest::TestEnumWithDupValue_Parse("FOO2", &dup_value)); + EXPECT_EQ(unittest::FOO2, dup_value); + EXPECT_FALSE(unittest::TestEnumWithDupValue_Parse("FOO", &dup_value)); +} + +#endif // PROTOBUF_TEST_NO_DESCRIPTORS + // =================================================================== +#ifndef PROTOBUF_TEST_NO_DESCRIPTORS + // Support code for testing services. class GeneratedServiceTest : public testing::Test { protected: @@ -977,6 +1088,27 @@ TEST_F(GeneratedServiceTest, NotImplemented) { EXPECT_TRUE(controller.called_); } +#endif // !PROTOBUF_TEST_NO_DESCRIPTORS + +// =================================================================== + +// This test must run last. It verifies that descriptors were or were not +// initialized depending on whether PROTOBUF_TEST_NO_DESCRIPTORS was defined. +// When this is defined, we skip all tests which are expected to trigger +// descriptor initialization. This verifies that everything else still works +// if descriptors are not initialized. +TEST(DescriptorInitializationTest, Initialized) { +#ifdef PROTOBUF_TEST_NO_DESCRIPTORS + bool should_have_descriptors = false; +#else + bool should_have_descriptors = true; +#endif + + EXPECT_EQ(should_have_descriptors, + DescriptorPool::generated_pool()->InternalIsFileLoaded( + "google/protobuf/unittest.proto")); +} + } // namespace cpp_unittest } // namespace cpp diff --git a/src/google/protobuf/compiler/importer.cc b/src/google/protobuf/compiler/importer.cc index 30e106e8..8a07dfc4 100644 --- a/src/google/protobuf/compiler/importer.cc +++ b/src/google/protobuf/compiler/importer.cc @@ -387,9 +387,22 @@ DiskSourceTree::DiskFileToVirtualFile( return SUCCESS; } +bool DiskSourceTree::VirtualFileToDiskFile(const string& virtual_file, + string* disk_file) { + scoped_ptr<io::ZeroCopyInputStream> stream(OpenVirtualFile(virtual_file, + disk_file)); + return stream != NULL; +} + io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) { - if (filename != CanonicalizePath(filename) || - ContainsParentReference(filename)) { + return OpenVirtualFile(filename, NULL); +} + +io::ZeroCopyInputStream* DiskSourceTree::OpenVirtualFile( + const string& virtual_file, + string* disk_file) { + if (virtual_file != CanonicalizePath(virtual_file) || + ContainsParentReference(virtual_file)) { // We do not allow importing of paths containing things like ".." or // consecutive slashes since the compiler expects files to be uniquely // identified by file name. @@ -397,16 +410,21 @@ io::ZeroCopyInputStream* DiskSourceTree::Open(const string& filename) { } for (int i = 0; i < mappings_.size(); i++) { - string disk_file; - if (ApplyMapping(filename, mappings_[i].virtual_path, - mappings_[i].disk_path, &disk_file)) { - io::ZeroCopyInputStream* stream = OpenDiskFile(disk_file); - if (stream != NULL) return stream; + string temp_disk_file; + if (ApplyMapping(virtual_file, mappings_[i].virtual_path, + mappings_[i].disk_path, &temp_disk_file)) { + io::ZeroCopyInputStream* stream = OpenDiskFile(temp_disk_file); + if (stream != NULL) { + if (disk_file != NULL) { + *disk_file = temp_disk_file; + } + return stream; + } if (errno == EACCES) { // The file exists but is not readable. // TODO(kenton): Find a way to report this more nicely. - GOOGLE_LOG(WARNING) << "Read access is denied for file: " << disk_file; + GOOGLE_LOG(WARNING) << "Read access is denied for file: " << temp_disk_file; return NULL; } } diff --git a/src/google/protobuf/compiler/importer.h b/src/google/protobuf/compiler/importer.h index e2177e1c..7a2efc29 100644 --- a/src/google/protobuf/compiler/importer.h +++ b/src/google/protobuf/compiler/importer.h @@ -267,6 +267,11 @@ class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { string* virtual_file, string* shadowing_disk_file); + // Given a virtual path, find the path to the file on disk. + // Return true and update disk_file with the on-disk path if the file exists. + // Return false and leave disk_file untouched if the file doesn't exist. + bool VirtualFileToDiskFile(const string& virtual_file, string* disk_file); + // implements SourceTree ------------------------------------------- io::ZeroCopyInputStream* Open(const string& filename); @@ -280,6 +285,11 @@ class LIBPROTOBUF_EXPORT DiskSourceTree : public SourceTree { }; vector<Mapping> mappings_; + // Like Open(), but returns the on-disk path in disk_file if disk_file is + // non-NULL and the file could be successfully opened. + io::ZeroCopyInputStream* OpenVirtualFile(const string& virtual_file, + string* disk_file); + // Like Open() but given the actual on-disk path. io::ZeroCopyInputStream* OpenDiskFile(const string& filename); diff --git a/src/google/protobuf/compiler/importer_unittest.cc b/src/google/protobuf/compiler/importer_unittest.cc index 51aeb90c..56fad56e 100644 --- a/src/google/protobuf/compiler/importer_unittest.cc +++ b/src/google/protobuf/compiler/importer_unittest.cc @@ -565,6 +565,34 @@ TEST_F(DiskSourceTreeTest, DiskFileToVirtualFileCanonicalization) { EXPECT_EQ("dir5/bar", virtual_file); } +TEST_F(DiskSourceTreeTest, VirtualFileToDiskFile) { + // Test VirtualFileToDiskFile. + + AddFile(dirnames_[0] + "/foo", "Hello World!"); + AddFile(dirnames_[1] + "/foo", "This file should be hidden."); + AddFile(dirnames_[1] + "/quux", "This file should not be hidden."); + source_tree_.MapPath("bar", dirnames_[0]); + source_tree_.MapPath("bar", dirnames_[1]); + + // Existent files, shadowed and non-shadowed case. + string disk_file; + EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/foo", &disk_file)); + EXPECT_EQ(dirnames_[0] + "/foo", disk_file); + EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/quux", &disk_file)); + EXPECT_EQ(dirnames_[1] + "/quux", disk_file); + + // Nonexistent file in existent directory and vice versa. + string not_touched = "not touched"; + EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("bar/baz", ¬_touched)); + EXPECT_EQ("not touched", not_touched); + EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", ¬_touched)); + EXPECT_EQ("not touched", not_touched); + + // Accept NULL as output parameter. + EXPECT_TRUE(source_tree_.VirtualFileToDiskFile("bar/foo", NULL)); + EXPECT_FALSE(source_tree_.VirtualFileToDiskFile("baz/foo", NULL)); +} + } // namespace } // namespace compiler diff --git a/src/google/protobuf/compiler/java/java_message.cc b/src/google/protobuf/compiler/java/java_message.cc index c2e0c115..c9cbd13f 100644 --- a/src/google/protobuf/compiler/java/java_message.cc +++ b/src/google/protobuf/compiler/java/java_message.cc @@ -466,6 +466,17 @@ GenerateParseFromMethods(io::Printer* printer) { " return newBuilder().mergeFrom(input, extensionRegistry)\n" " .buildParsed();\n" "}\n" + "public static $classname$ parseDelimitedFrom(java.io.InputStream input)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeDelimitedFrom(input).buildParsed();\n" + "}\n" + "public static $classname$ parseDelimitedFrom(\n" + " java.io.InputStream input,\n" + " com.google.protobuf.ExtensionRegistry extensionRegistry)\n" + " throws java.io.IOException {\n" + " return newBuilder().mergeDelimitedFrom(input, extensionRegistry)\n" + " .buildParsed();\n" + "}\n" "public static $classname$ parseFrom(\n" " com.google.protobuf.CodedInputStream input)\n" " throws java.io.IOException {\n" @@ -579,7 +590,8 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { printer->Print( "public $classname$ build() {\n" - " if (!isInitialized()) {\n" + // If result == null, we'll throw an appropriate exception later. + " if (result != null && !isInitialized()) {\n" " throw new com.google.protobuf.UninitializedMessageException(\n" " result);\n" " }\n" @@ -595,7 +607,11 @@ void MessageGenerator::GenerateCommonBuilderMethods(io::Printer* printer) { " return buildPartial();\n" "}\n" "\n" - "public $classname$ buildPartial() {\n", + "public $classname$ buildPartial() {\n" + " if (result == null) {\n" + " throw new IllegalStateException(\n" + " \"build() has already been called on this Builder.\");" + " }\n", "classname", ClassName(descriptor_)); printer->Indent(); diff --git a/src/google/protobuf/compiler/java/java_service.cc b/src/google/protobuf/compiler/java/java_service.cc index 5308dd20..5545bf7f 100644 --- a/src/google/protobuf/compiler/java/java_service.cc +++ b/src/google/protobuf/compiler/java/java_service.cc @@ -57,45 +57,111 @@ void ServiceGenerator::Generate(io::Printer* printer) { "classname", descriptor_->name()); printer->Indent(); - // Generate abstract method declarations. - for (int i = 0; i < descriptor_->method_count(); i++) { - const MethodDescriptor* method = descriptor_->method(i); - map<string, string> vars; - vars["name"] = UnderscoresToCamelCase(method); - vars["input"] = ClassName(method->input_type()); - vars["output"] = ClassName(method->output_type()); - printer->Print(vars, - "public abstract void $name$(\n" - " com.google.protobuf.RpcController controller,\n" - " $input$ request,\n" - " com.google.protobuf.RpcCallback<$output$> done);\n"); - } + printer->Print( + "protected $classname$() {}\n\n", + "classname", descriptor_->name()); + + GenerateInterface(printer); + + GenerateNewReflectiveServiceMethod(printer); + GenerateNewReflectiveBlockingServiceMethod(printer); + + GenerateAbstractMethods(printer); // Generate getDescriptor() and getDescriptorForType(). printer->Print( - "\n" "public static final\n" " com.google.protobuf.Descriptors.ServiceDescriptor\n" " getDescriptor() {\n" " return $file$.getDescriptor().getServices().get($index$);\n" - "}\n" - "public final com.google.protobuf.Descriptors.ServiceDescriptor\n" - " getDescriptorForType() {\n" - " return getDescriptor();\n" "}\n", "file", ClassName(descriptor_->file()), "index", SimpleItoa(descriptor_->index())); + GenerateGetDescriptorForType(printer); // Generate more stuff. GenerateCallMethod(printer); GenerateGetPrototype(REQUEST, printer); GenerateGetPrototype(RESPONSE, printer); GenerateStub(printer); + GenerateBlockingStub(printer); printer->Outdent(); printer->Print("}\n\n"); } +void ServiceGenerator::GenerateGetDescriptorForType(io::Printer* printer) { + printer->Print( + "public final com.google.protobuf.Descriptors.ServiceDescriptor\n" + " getDescriptorForType() {\n" + " return getDescriptor();\n" + "}\n"); +} + +void ServiceGenerator::GenerateInterface(io::Printer* printer) { + printer->Print("public interface Interface {\n"); + printer->Indent(); + GenerateAbstractMethods(printer); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void ServiceGenerator::GenerateNewReflectiveServiceMethod( + io::Printer* printer) { + printer->Print( + "public static com.google.protobuf.Service newReflectiveService(\n" + " final Interface impl) {\n" + " return new $classname$() {\n", + "classname", descriptor_->name()); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + printer->Print("@Override\n"); + GenerateMethodSignature(printer, method, IS_CONCRETE); + printer->Print( + " {\n" + " impl.$method$(controller, request, done);\n" + "}\n\n", + "method", UnderscoresToCamelCase(method)); + } + + printer->Outdent(); + printer->Print("};\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void ServiceGenerator::GenerateNewReflectiveBlockingServiceMethod( + io::Printer* printer) { + printer->Print( + "public static com.google.protobuf.BlockingService\n" + " newReflectiveBlockingService(final BlockingInterface impl) {\n" + " return new com.google.protobuf.BlockingService() {\n"); + printer->Indent(); + printer->Indent(); + + GenerateGetDescriptorForType(printer); + + GenerateCallBlockingMethod(printer); + GenerateGetPrototype(REQUEST, printer); + GenerateGetPrototype(RESPONSE, printer); + + printer->Outdent(); + printer->Print("};\n"); + printer->Outdent(); + printer->Print("}\n\n"); +} + +void ServiceGenerator::GenerateAbstractMethods(io::Printer* printer) { + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + GenerateMethodSignature(printer, method, IS_ABSTRACT); + printer->Print(";\n\n"); + } +} + void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { printer->Print( "\n" @@ -131,7 +197,49 @@ void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { printer->Print( "default:\n" - " throw new java.lang.RuntimeException(\"Can't get here.\");\n"); + " throw new java.lang.AssertionError(\"Can't get here.\");\n"); + + printer->Outdent(); + printer->Outdent(); + + printer->Print( + " }\n" + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateCallBlockingMethod(io::Printer* printer) { + printer->Print( + "\n" + "public final com.google.protobuf.Message callBlockingMethod(\n" + " com.google.protobuf.Descriptors.MethodDescriptor method,\n" + " com.google.protobuf.RpcController controller,\n" + " com.google.protobuf.Message request)\n" + " throws com.google.protobuf.ServiceException {\n" + " if (method.getService() != getDescriptor()) {\n" + " throw new java.lang.IllegalArgumentException(\n" + " \"Service.callBlockingMethod() given method descriptor for \" +\n" + " \"wrong service type.\");\n" + " }\n" + " switch(method.getIndex()) {\n"); + printer->Indent(); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + map<string, string> vars; + vars["index"] = SimpleItoa(i); + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "case $index$:\n" + " return impl.$method$(controller, ($input$)request);\n"); + } + + printer->Print( + "default:\n" + " throw new java.lang.AssertionError(\"Can't get here.\");\n"); printer->Outdent(); printer->Outdent(); @@ -144,6 +252,10 @@ void ServiceGenerator::GenerateCallMethod(io::Printer* printer) { void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, io::Printer* printer) { + /* + * TODO(cpovirk): The exception message says "Service.foo" when it may be + * "BlockingService.foo." Consider fixing. + */ printer->Print( "public final com.google.protobuf.Message\n" " get$request_or_response$Prototype(\n" @@ -171,7 +283,7 @@ void ServiceGenerator::GenerateGetPrototype(RequestOrResponse which, printer->Print( "default:\n" - " throw new java.lang.RuntimeException(\"Can't get here.\");\n"); + " throw new java.lang.AssertionError(\"Can't get here.\");\n"); printer->Outdent(); printer->Outdent(); @@ -189,7 +301,8 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) { " return new Stub(channel);\n" "}\n" "\n" - "public static final class Stub extends $classname$ {\n", + "public static final class Stub extends $classname$ implements Interface {" + "\n", "classname", ClassName(descriptor_)); printer->Indent(); @@ -206,33 +319,125 @@ void ServiceGenerator::GenerateStub(io::Printer* printer) { for (int i = 0; i < descriptor_->method_count(); i++) { const MethodDescriptor* method = descriptor_->method(i); + printer->Print("\n"); + GenerateMethodSignature(printer, method, IS_CONCRETE); + printer->Print(" {\n"); + printer->Indent(); + map<string, string> vars; vars["index"] = SimpleItoa(i); - vars["method"] = UnderscoresToCamelCase(method); - vars["input"] = ClassName(method->input_type()); vars["output"] = ClassName(method->output_type()); printer->Print(vars, - "\n" - "public void $method$(\n" - " com.google.protobuf.RpcController controller,\n" - " $input$ request,\n" - " com.google.protobuf.RpcCallback<$output$> done) {\n" - " channel.callMethod(\n" - " getDescriptor().getMethods().get($index$),\n" - " controller,\n" - " request,\n" - " $output$.getDefaultInstance(),\n" - " com.google.protobuf.RpcUtil.generalizeCallback(\n" - " done,\n" - " $output$.class,\n" - " $output$.getDefaultInstance()));\n" - "}\n"); + "channel.callMethod(\n" + " getDescriptor().getMethods().get($index$),\n" + " controller,\n" + " request,\n" + " $output$.getDefaultInstance(),\n" + " com.google.protobuf.RpcUtil.generalizeCallback(\n" + " done,\n" + " $output$.class,\n" + " $output$.getDefaultInstance()));\n"); + + printer->Outdent(); + printer->Print("}\n"); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); +} + +void ServiceGenerator::GenerateBlockingStub(io::Printer* printer) { + printer->Print( + "public static BlockingInterface newBlockingStub(\n" + " com.google.protobuf.BlockingRpcChannel channel) {\n" + " return new BlockingStub(channel);\n" + "}\n" + "\n"); + + printer->Print( + "public interface BlockingInterface {"); + printer->Indent(); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + GenerateBlockingMethodSignature(printer, method); + printer->Print(";\n"); + } + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); + + printer->Print( + "private static final class BlockingStub implements BlockingInterface {\n"); + printer->Indent(); + + printer->Print( + "private BlockingStub(com.google.protobuf.BlockingRpcChannel channel) {\n" + " this.channel = channel;\n" + "}\n" + "\n" + "private final com.google.protobuf.BlockingRpcChannel channel;\n"); + + for (int i = 0; i < descriptor_->method_count(); i++) { + const MethodDescriptor* method = descriptor_->method(i); + GenerateBlockingMethodSignature(printer, method); + printer->Print(" {\n"); + printer->Indent(); + + map<string, string> vars; + vars["index"] = SimpleItoa(i); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "return ($output$) channel.callBlockingMethod(\n" + " getDescriptor().getMethods().get($index$),\n" + " controller,\n" + " request,\n" + " $output$.getDefaultInstance());\n"); + + printer->Outdent(); + printer->Print( + "}\n" + "\n"); } printer->Outdent(); printer->Print("}\n"); } +void ServiceGenerator::GenerateMethodSignature(io::Printer* printer, + const MethodDescriptor* method, + IsAbstract is_abstract) { + map<string, string> vars; + vars["name"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + vars["abstract"] = (is_abstract == IS_ABSTRACT) ? "abstract" : ""; + printer->Print(vars, + "public $abstract$ void $name$(\n" + " com.google.protobuf.RpcController controller,\n" + " $input$ request,\n" + " com.google.protobuf.RpcCallback<$output$> done)"); +} + +void ServiceGenerator::GenerateBlockingMethodSignature( + io::Printer* printer, + const MethodDescriptor* method) { + map<string, string> vars; + vars["method"] = UnderscoresToCamelCase(method); + vars["input"] = ClassName(method->input_type()); + vars["output"] = ClassName(method->output_type()); + printer->Print(vars, + "\n" + "public $output$ $method$(\n" + " com.google.protobuf.RpcController controller,\n" + " $input$ request)\n" + " throws com.google.protobuf.ServiceException"); +} + } // namespace java } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/java/java_service.h b/src/google/protobuf/compiler/java/java_service.h index 83404174..e07eebf7 100644 --- a/src/google/protobuf/compiler/java/java_service.h +++ b/src/google/protobuf/compiler/java/java_service.h @@ -57,9 +57,28 @@ class ServiceGenerator { void Generate(io::Printer* printer); private: + + // Generate the getDescriptorForType() method. + void GenerateGetDescriptorForType(io::Printer* printer); + + // Generate a Java interface for the service. + void GenerateInterface(io::Printer* printer); + + // Generate newReflectiveService() method. + void GenerateNewReflectiveServiceMethod(io::Printer* printer); + + // Generate newReflectiveBlockingService() method. + void GenerateNewReflectiveBlockingServiceMethod(io::Printer* printer); + + // Generate abstract method declarations for all methods. + void GenerateAbstractMethods(io::Printer* printer); + // Generate the implementation of Service.callMethod(). void GenerateCallMethod(io::Printer* printer); + // Generate the implementation of BlockingService.callBlockingMethod(). + void GenerateCallBlockingMethod(io::Printer* printer); + // Generate the implementations of Service.get{Request,Response}Prototype(). enum RequestOrResponse { REQUEST, RESPONSE }; void GenerateGetPrototype(RequestOrResponse which, io::Printer* printer); @@ -67,6 +86,20 @@ class ServiceGenerator { // Generate a stub implementation of the service. void GenerateStub(io::Printer* printer); + // Generate a method signature, possibly abstract, without body or trailing + // semicolon. + enum IsAbstract { IS_ABSTRACT, IS_CONCRETE }; + void GenerateMethodSignature(io::Printer* printer, + const MethodDescriptor* method, + IsAbstract is_abstract); + + // Generate a blocking stub interface and implementation of the service. + void GenerateBlockingStub(io::Printer* printer); + + // Generate the method signature for one method of a blocking stub. + void GenerateBlockingMethodSignature(io::Printer* printer, + const MethodDescriptor* method); + const ServiceDescriptor* descriptor_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(ServiceGenerator); diff --git a/src/google/protobuf/compiler/parser.cc b/src/google/protobuf/compiler/parser.cc index e1c0d0d1..e9e01545 100644 --- a/src/google/protobuf/compiler/parser.cc +++ b/src/google/protobuf/compiler/parser.cc @@ -97,7 +97,8 @@ Parser::Parser() error_collector_(NULL), source_location_table_(NULL), had_errors_(false), - require_syntax_identifier_(false) { + require_syntax_identifier_(false), + stop_after_syntax_identifier_(false) { } Parser::~Parser() { @@ -309,10 +310,12 @@ bool Parser::Parse(io::Tokenizer* input, FileDescriptorProto* file) { // identifier. return false; } - } else { + } else if (!stop_after_syntax_identifier_) { syntax_identifier_ = "proto2"; } + if (stop_after_syntax_identifier_) return !had_errors_; + // Repeatedly parse statements until we reach the end of the file. while (!AtEnd()) { if (!ParseTopLevelStatement(file)) { @@ -341,7 +344,7 @@ bool Parser::ParseSyntaxIdentifier() { syntax_identifier_ = syntax; - if (syntax != "proto2") { + if (syntax != "proto2" && !stop_after_syntax_identifier_) { AddError(syntax_token.line, syntax_token.column, "Unrecognized syntax identifier \"" + syntax + "\". This parser " "only recognizes \"proto2\"."); diff --git a/src/google/protobuf/compiler/parser.h b/src/google/protobuf/compiler/parser.h index b670f740..72c96d04 100644 --- a/src/google/protobuf/compiler/parser.h +++ b/src/google/protobuf/compiler/parser.h @@ -90,7 +90,7 @@ class LIBPROTOBUF_EXPORT Parser { // Returns the identifier used in the "syntax = " declaration, if one was // seen during the last call to Parse(), or the empty string otherwise. - const string& GetSyntaxIndentifier() { return syntax_identifier_; } + const string& GetSyntaxIdentifier() { return syntax_identifier_; } // If set true, input files will be required to begin with a syntax // identifier. Otherwise, files may omit this. If a syntax identifier @@ -100,6 +100,18 @@ class LIBPROTOBUF_EXPORT Parser { require_syntax_identifier_ = value; } + // Call SetStopAfterSyntaxIdentifier(true) to tell the parser to stop + // parsing as soon as it has seen the syntax identifier, or lack thereof. + // This is useful for quickly identifying the syntax of the file without + // parsing the whole thing. If this is enabled, no error will be recorded + // if the syntax identifier is something other than "proto2" (since + // presumably the caller intends to deal with that), but other kinds of + // errors (e.g. parse errors) will still be reported. When this is enabled, + // you may pass a NULL FileDescriptorProto to Parse(). + void SetStopAfterSyntaxIdentifier(bool value) { + stop_after_syntax_identifier_ = value; + } + private: // ================================================================= // Error recovery helpers @@ -281,6 +293,7 @@ class LIBPROTOBUF_EXPORT Parser { SourceLocationTable* source_location_table_; bool had_errors_; bool require_syntax_identifier_; + bool stop_after_syntax_identifier_; string syntax_identifier_; GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Parser); diff --git a/src/google/protobuf/compiler/parser_unittest.cc b/src/google/protobuf/compiler/parser_unittest.cc index 2d48c5ae..c4f08e7f 100644 --- a/src/google/protobuf/compiler/parser_unittest.cc +++ b/src/google/protobuf/compiler/parser_unittest.cc @@ -176,6 +176,38 @@ class ParserTest : public testing::Test { // =================================================================== +TEST_F(ParserTest, StopAfterSyntaxIdentifier) { + SetupParser( + "// blah\n" + "syntax = \"foobar\";\n" + "this line will not be parsed\n"); + parser_->SetStopAfterSyntaxIdentifier(true); + EXPECT_TRUE(parser_->Parse(input_.get(), NULL)); + EXPECT_EQ("", error_collector_.text_); + EXPECT_EQ("foobar", parser_->GetSyntaxIdentifier()); +} + +TEST_F(ParserTest, StopAfterOmittedSyntaxIdentifier) { + SetupParser( + "// blah\n" + "this line will not be parsed\n"); + parser_->SetStopAfterSyntaxIdentifier(true); + EXPECT_TRUE(parser_->Parse(input_.get(), NULL)); + EXPECT_EQ("", error_collector_.text_); + EXPECT_EQ("", parser_->GetSyntaxIdentifier()); +} + +TEST_F(ParserTest, StopAfterSyntaxIdentifierWithErrors) { + SetupParser( + "// blah\n" + "syntax = error;\n"); + parser_->SetStopAfterSyntaxIdentifier(true); + EXPECT_FALSE(parser_->Parse(input_.get(), NULL)); + EXPECT_EQ("1:9: Expected syntax identifier.\n", error_collector_.text_); +} + +// =================================================================== + typedef ParserTest ParseMessageTest; TEST_F(ParseMessageTest, SimpleMessage) { @@ -201,7 +233,7 @@ TEST_F(ParseMessageTest, ImplicitSyntaxIdentifier) { " name: \"TestMessage\"" " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" "}"); - EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); + EXPECT_EQ("proto2", parser_->GetSyntaxIdentifier()); } TEST_F(ParseMessageTest, ExplicitSyntaxIdentifier) { @@ -215,7 +247,7 @@ TEST_F(ParseMessageTest, ExplicitSyntaxIdentifier) { " name: \"TestMessage\"" " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" "}"); - EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); + EXPECT_EQ("proto2", parser_->GetSyntaxIdentifier()); } TEST_F(ParseMessageTest, ExplicitRequiredSyntaxIdentifier) { @@ -230,7 +262,7 @@ TEST_F(ParseMessageTest, ExplicitRequiredSyntaxIdentifier) { " name: \"TestMessage\"" " field { name:\"foo\" label:LABEL_REQUIRED type:TYPE_INT32 number:1 }" "}"); - EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); + EXPECT_EQ("proto2", parser_->GetSyntaxIdentifier()); } TEST_F(ParseMessageTest, SimpleFields) { @@ -673,7 +705,7 @@ TEST_F(ParseErrorTest, MissingSyntaxIdentifier) { ExpectHasEarlyExitErrors( "message TestMessage {}", "0:0: File must begin with 'syntax = \"proto2\";'.\n"); - EXPECT_EQ("", parser_->GetSyntaxIndentifier()); + EXPECT_EQ("", parser_->GetSyntaxIdentifier()); } TEST_F(ParseErrorTest, UnknownSyntaxIdentifier) { @@ -681,14 +713,14 @@ TEST_F(ParseErrorTest, UnknownSyntaxIdentifier) { "syntax = \"no_such_syntax\";", "0:9: Unrecognized syntax identifier \"no_such_syntax\". This parser " "only recognizes \"proto2\".\n"); - EXPECT_EQ("no_such_syntax", parser_->GetSyntaxIndentifier()); + EXPECT_EQ("no_such_syntax", parser_->GetSyntaxIdentifier()); } TEST_F(ParseErrorTest, SimpleSyntaxError) { ExpectHasErrors( "message TestMessage @#$ { blah }", "0:20: Expected \"{\".\n"); - EXPECT_EQ("proto2", parser_->GetSyntaxIndentifier()); + EXPECT_EQ("proto2", parser_->GetSyntaxIdentifier()); } TEST_F(ParseErrorTest, ExpectedTopLevel) { |